diff options
Diffstat (limited to 'tiger-compiler/src')
379 files changed, 22593 insertions, 0 deletions
diff --git a/tiger-compiler/src/assert/binder.cc b/tiger-compiler/src/assert/binder.cc new file mode 100644 index 0000000..04008bc --- /dev/null +++ b/tiger-compiler/src/assert/binder.cc @@ -0,0 +1,12 @@ +/** + ** \file assert/binder.cc + ** \brief Implementation of assert::Binder. + */ + +#include <assert/binder.hh> + +namespace assert { + + Binder::Binder() = default; + +} // namespace assert diff --git a/tiger-compiler/src/assert/binder.hh b/tiger-compiler/src/assert/binder.hh new file mode 100644 index 0000000..7ede6d7 --- /dev/null +++ b/tiger-compiler/src/assert/binder.hh @@ -0,0 +1,22 @@ +/** + ** \file assert/binder.hh + ** \brief Declaration of assert::Binder. + */ + +#pragma once + +#include <bind/binder.hh> + +namespace assert { + + class Binder + : public bind::Binder + { + public: + using super_class = bind::Binder; + + // Build a Binder + Binder(); + }; + +} // namespace assert diff --git a/tiger-compiler/src/assert/desugar-visitor.cc b/tiger-compiler/src/assert/desugar-visitor.cc new file mode 100644 index 0000000..5f18a07 --- /dev/null +++ b/tiger-compiler/src/assert/desugar-visitor.cc @@ -0,0 +1,33 @@ +/** + ** \file assert/desugar-visitor.cc + ** \brief Implementation of assert::DesugarVisitor. + */ + +#include <assert/desugar-visitor.hh> + +namespace assert +{ + + DesugarVisitor::DesugarVisitor(const bool desugar_for_p, + const bool desugar_string_cmp_p) + : super_type(desugar_for_p, desugar_string_cmp_p) + {} + + void DesugarVisitor::operator()(const ast::AssertExp& e) + { + // FIXME DONE: Some code was deleted here. (replace the AssertExp to a CallExp) + const ast::Location location = e.location_get(); + + const auto args = new ast::exps_type; + + const std::string condition_text = get_formatted_assert_cond(e); + const std::string filename = get_formatted_location(e.location_get()); + + args->push_back(recurse(e.cond_get())); + args->push_back(new ast::StringExp(location, condition_text)); + args->push_back(new ast::StringExp(location, filename)); + + result_ = new ast::CallExp(location, "assertion", args); + } + +} // namespace assert diff --git a/tiger-compiler/src/assert/desugar-visitor.hh b/tiger-compiler/src/assert/desugar-visitor.hh new file mode 100644 index 0000000..88d689e --- /dev/null +++ b/tiger-compiler/src/assert/desugar-visitor.hh @@ -0,0 +1,42 @@ +/** + ** \file assert/desugar-visitor.hh + ** \brief Declaration of assert::DesugarVisitor. + */ + +#pragma once + +#include <ast/assert-visitor.hh> +#include <desugar/desugar-visitor.hh> + +namespace assert { + + class DesugarVisitor + : public desugar::DesugarVisitor + , public ast::AssertConstVisitor + { + public: + using super_type = desugar::DesugarVisitor; + using super_type::operator(); + + // Build a DesugarVisitor + DesugarVisitor(bool desugar_for_p, bool desugar_string_cmp_p); + + // Desugar assertions + // \param e Node to visit + void operator()(const ast::AssertExp& e) override; + + private: + + // Retrieve the pretty-printed version of an assertion's condition as a + // string + // \param e Node to pretty-print. + inline std::string get_formatted_assert_cond(const ast::AssertExp& e); + + // Retrieve the pretty-printed version of a Location as a string + // \param loc Location record to pretty-print + inline std::string get_formatted_location(const ast::Location& loc); + }; + +} // namespace assert + +#include <assert/desugar-visitor.hxx> diff --git a/tiger-compiler/src/assert/desugar-visitor.hxx b/tiger-compiler/src/assert/desugar-visitor.hxx new file mode 100644 index 0000000..9e839c1 --- /dev/null +++ b/tiger-compiler/src/assert/desugar-visitor.hxx @@ -0,0 +1,39 @@ +/** + ** \file assert/desugar-visitor.hxx + ** \brief Implementation of assert::DesugarVisitor (inlined methods). + */ + +#pragma once + +#include <assert/desugar-visitor.hh> + +#include <sstream> +#include <string> + +#include <ast/pretty-printer.hh> + +namespace assert +{ + + inline std::string + DesugarVisitor::get_formatted_assert_cond(const ast::AssertExp& e) + { + std::stringstream stream{}; + ast::PrettyPrinter pretty_printer(stream); + + pretty_printer(e.cond_get()); + + return stream.str(); + } + + inline std::string + DesugarVisitor::get_formatted_location(const ast::Location& loc) + { + std::stringstream stream{}; + + stream << loc; + + return stream.str(); + } + +} diff --git a/tiger-compiler/src/assert/libassert.cc b/tiger-compiler/src/assert/libassert.cc new file mode 100644 index 0000000..8114b74 --- /dev/null +++ b/tiger-compiler/src/assert/libassert.cc @@ -0,0 +1,36 @@ +/** + ** \file assert/libassert.cc + ** \brief Implementation of functions exported by the assert module. + */ + +#include <assert/binder.hh> +#include <assert/libassert.hh> +#include <assert/renamer.hh> +#include <assert/type-checker.hh> +#include <misc/error.hh> + +namespace assert +{ + + misc::error bind(ast::Ast& last) + { + Binder binder; + binder(last); + return binder.error_get(); + } + + misc::error types_check(ast::Ast& tree) + { + TypeChecker typer; + typer(tree); + return typer.error_get(); + } + + misc::error rename(ast::Ast& ast) + { + Renamer renamer; + renamer(ast); + return misc::error{}; + } + +} diff --git a/tiger-compiler/src/assert/libassert.hh b/tiger-compiler/src/assert/libassert.hh new file mode 100644 index 0000000..aba01b1 --- /dev/null +++ b/tiger-compiler/src/assert/libassert.hh @@ -0,0 +1,79 @@ +/** + ** \file assert/libassert.hh + ** \brief Declare functions and variables exported by assert module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> + +namespace assert +{ + /*-------. + | Bind. | + `-------*/ + + /// \brief Bind the whole AST in place, return the error code + /// + /// \param last the ast you want to bind + /// + /// \return a misc::error that serve to indicate possible failure + misc::error bind(ast::Ast& last); + + /*----------------. + | Compute types. | + `----------------*/ + + /** \brief Check types allowing assertions. + + \param tree abstract syntax tree's root. + + \return success of the type-checking. */ + misc::error types_check(ast::Ast& tree); + + /*---------. + | Rename. | + `---------*/ + + /// \brief Rename the whole ast in place + /// + /// \param ast the ast you want to rename + /// + /// \return a misc::error that serve to indicate possible failure + misc::error rename(ast::Ast& ast); + + /*---------------------. + | Desugar assertions. | + `---------------------*/ + + /** \brief Remove assertions constructs from an AST. + + \param tree abstract syntax tree's root, whose bindings + and types have been computed, and whose + identifiers are all unique. + \param class_names the names of the class types of the AST + + \return the desugared, bound and type-checked AST. */ + template <typename A> + A* desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p); + + /** \brief Remove assertions constructs from an AST without recomputing + its bindings nor its types. + + This function acts like assertions::assertions_desugar, but stops just + after the desugaring step (in fact, assertions::desugar is built + upon this function). It is meant to be used as a test of + DesugarVisitor (i.e., even if the desugared tree is badly bound + or typed, it can still be pretty-printed). + + \param tree AST to desugar. + \param class_names the names of the class types of the AST + + \return the desugared AST. */ + template <typename A> + A* raw_desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p); + +} + +#include <assert/libassert.hxx> diff --git a/tiger-compiler/src/assert/libassert.hxx b/tiger-compiler/src/assert/libassert.hxx new file mode 100644 index 0000000..fcd2eb4 --- /dev/null +++ b/tiger-compiler/src/assert/libassert.hxx @@ -0,0 +1,45 @@ +/** + ** \file assert/libassert.hxx + ** \brief Implementation of functions exported by the assert module. + */ + +#pragma once + +#include <memory> + +#include <assert/desugar-visitor.hh> +#include <assert/libassert.hh> +#include <misc/contract.hh> +#include <misc/error.hh> + +namespace assert +{ + + template <typename A> void bind_and_types_check(A& tree) + { + misc::error e; + e << ::assert::bind(tree); + e.ice_on_error_here(); + e << ::assert::types_check(tree); + e.ice_on_error_here(); + } + + template <typename A> + A* raw_desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p) + { + DesugarVisitor desugar(desugar_for_p, desugar_string_cmp_p); + desugar(tree); + return dynamic_cast<A*>(desugar.result_get()); + } + + template <typename A> + A* desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p) + { + A* desugared = raw_desugar(tree, desugar_for_p, desugar_string_cmp_p); + assertion(desugared); + std::unique_ptr<A> desugared_ptr(desugared); + bind_and_types_check(*desugared_ptr); + return desugared_ptr.release(); + } + +} diff --git a/tiger-compiler/src/assert/local.am b/tiger-compiler/src/assert/local.am new file mode 100644 index 0000000..18a8421 --- /dev/null +++ b/tiger-compiler/src/assert/local.am @@ -0,0 +1,17 @@ +## assert module + +src_libtc_la_SOURCES += \ + %D%/libassert.hh \ + %D%/libassert.cc \ + %D%/libassert.hxx + +src_libtc_la_SOURCES += \ + %D%/binder.hh %D%/binder.cc \ + %D%/desugar-visitor.hh %D%/desugar-visitor.cc %D%/desugar-visitor.hxx \ + %D%/type-checker.hh %D%/type-checker.cc \ + %D%/renamer.hh %D%/renamer.cc + + +## tasks + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/assert/renamer.cc b/tiger-compiler/src/assert/renamer.cc new file mode 100644 index 0000000..7c63cfa --- /dev/null +++ b/tiger-compiler/src/assert/renamer.cc @@ -0,0 +1,18 @@ +/** + ** \file assert/renamer.cc + ** \brief Implementation of assert::Renamer. + */ + +#include <assert/renamer.hh> + +namespace assert +{ + + Renamer::Renamer() = default; + + void Renamer::operator()(ast::AssertExp& e) + { + ast::AssertVisitor::operator()(e); + } + +} // namespace assert diff --git a/tiger-compiler/src/assert/renamer.hh b/tiger-compiler/src/assert/renamer.hh new file mode 100644 index 0000000..6a5bd14 --- /dev/null +++ b/tiger-compiler/src/assert/renamer.hh @@ -0,0 +1,30 @@ +/** + ** \file assert/renamer.hh + ** \brief Declaration of assert::Renamer. + */ + +#pragma once + +#include <ast/assert-visitor.hh> +#include <bind/renamer.hh> + +namespace assert { + + class Renamer + : public bind::Renamer + , public ast::AssertVisitor + { + public: + using super_type = ::bind::Renamer; + using super_type::operator(); + + // Build a renamer + Renamer(); + + // Visiting method for AssertExp nodes (redirects to + // `ast::AssertVisitor::operator()(ast::AssertExp&)`) + // \param e Node to visit + void operator()(ast::AssertExp& e) override; + }; + +} // namespace assert diff --git a/tiger-compiler/src/assert/tasks.cc b/tiger-compiler/src/assert/tasks.cc new file mode 100644 index 0000000..3e43ecd --- /dev/null +++ b/tiger-compiler/src/assert/tasks.cc @@ -0,0 +1,53 @@ +/** + ** \file assert/tasks.cc + ** \brief Assert module related tasks' implementation. + */ + +#include <ast/tasks.hh> +#include <astclone/libastclone.hh> +#include <assert/libassert.hh> +#include <common.hh> +#include <desugar/tasks.hh> +#include <escapes/tasks.hh> +#include <llvmtranslate/tasks.hh> +#define DEFINE_TASKS 1 +#include <assert/tasks.hh> +#undef DEFINE_TASKS + +namespace assert::tasks +{ + void parse() {} + + void bind() + { + task_error() << ::assert::bind(*ast::tasks::the_program.get()) + << &misc::error::exit_on_error; + } + + void types_compute() + { + task_error() << ::assert::types_check(*ast::tasks::the_program.get()) + << &misc::error::exit_on_error; + } + + void rename() + { + task_error() << ::assert::rename(*ast::tasks::the_program.get()) + << &misc::error::exit_on_error; + } + + void desugar() + { + astclone::apply(::assert::desugar, ast::tasks::the_program, + desugar::tasks::desugar_for_p, + desugar::tasks::desugar_string_cmp_p); + } + + void raw_desugar() + { + astclone::apply(::assert::raw_desugar, ast::tasks::the_program, + desugar::tasks::desugar_for_p, + desugar::tasks::desugar_string_cmp_p); + } + +} // namespace assert::tasks diff --git a/tiger-compiler/src/assert/tasks.hh b/tiger-compiler/src/assert/tasks.hh new file mode 100644 index 0000000..7f062de --- /dev/null +++ b/tiger-compiler/src/assert/tasks.hh @@ -0,0 +1,59 @@ +/** + ** \file assert/tasks.hh + ** \brief Assert module related tasks. + */ + +#pragma once + +#include <task/libtask.hh> + +// The tasks of the assert module +namespace assert::tasks +{ + TASK_GROUP("Assert"); + + // Enable assert extensions. + BOOLEAN_TASK_DECLARE("assert", + "enable assert extensions", + enable_assert_extensions_p, + ""); + + // Parse the input file, allowing assertions. + TASK_DECLARE("assert-parse", + "parse a file, allowing assertions", + parse, + "assert parse"); + + // Bind the parsed file, allowing assertions. + TASK_DECLARE("assert-bindings-compute", + "bind the name uses to their definitions, allowing assertions", + bind, + "assert-parse"); + + // Check for type violation, allowing assertions. + TASK_DECLARE("assert-types-compute", + "check for type violations, allowing assertions", + types_compute, + "assert-bindings-compute"); + + // Perform a renaming, before desugaring assertions. + TASK_DECLARE("assert-rename", + "rename identifiers to unique names, allowing assertions", + rename, + "assert-bindings-compute"); + + // Remove syntactic sugar from the Ast. + TASK_DECLARE("assert-desugar", + "remove assertions constructs from the program", + desugar, + "assert-rename assert-types-compute"); + + // Remove syntactic sugar from the Ast without recomputing + // bindings nor types. + TASK_DECLARE("raw-assert-desugar", + "remove assertions constructs from the program " + "without recomputing bindings nor types", + raw_desugar, + "assert-rename assert-types-compute"); + +} // namespace assert::tasks diff --git a/tiger-compiler/src/assert/type-checker.cc b/tiger-compiler/src/assert/type-checker.cc new file mode 100644 index 0000000..894ff6f --- /dev/null +++ b/tiger-compiler/src/assert/type-checker.cc @@ -0,0 +1,21 @@ +/** + ** \file assert/type-checker.cc + ** \brief Implementation of assert::TypeChecker. + */ + +#include <assert/type-checker.hh> + +namespace assert +{ + + TypeChecker::TypeChecker() = default; + + void TypeChecker::operator()(ast::AssertExp& e) + { + // FIXME DONE: Some code was deleted here. (implement method documentation) + check_type(e.cond_get(), "assertion", type::Int::instance()); + + type_default(e, &type::Void::instance()); + } + +} // namespace assert diff --git a/tiger-compiler/src/assert/type-checker.hh b/tiger-compiler/src/assert/type-checker.hh new file mode 100644 index 0000000..e15e393 --- /dev/null +++ b/tiger-compiler/src/assert/type-checker.hh @@ -0,0 +1,30 @@ +/** + ** \file assert/type-checker.hh + ** \brief Declaration of assert::TypeChecker. + */ + +#pragma once + +#include <ast/assert-visitor.hh> +#include <type/type-checker.hh> + +namespace assert { + + class TypeChecker + : public type::TypeChecker + , public ast::AssertVisitor + { + public: + using super_type = type::TypeChecker; + using super_type::operator(); + + // Build a TypeChecker + TypeChecker(); + + // Type an AssertExp node. All AssertExp must follow the typing table : + // cond_ : int | assert cond_ : void + // \param e Node to visit + void operator()(ast::AssertExp& e) override; + }; + +} // assert diff --git a/tiger-compiler/src/ast/README.txt b/tiger-compiler/src/ast/README.txt new file mode 100644 index 0000000..4291b24 --- /dev/null +++ b/tiger-compiler/src/ast/README.txt @@ -0,0 +1,66 @@ +* README + +Tiger Abstract Syntax Tree nodes with their principal members. +Incomplete classes are tagged with a `*'. + +/Ast/ (Location location) + /Dec/ (symbol name) + FunctionDec (VarChunk formals, NameTy result, Exp body) + MethodDec () + TypeDec (Ty ty) + VarDec (NameTy type_name, Exp init) + + /Exp/ () + /Var/ () + FieldVar (Var var, symbol name) + SimpleVar (symbol name) + SubscriptVar (Var var, Exp index) + + ArrayExp (NameTy type_name, Exp size, Exp init) + AssertExp (Exp exp) + AssignExp (Var var, Exp exp) + BreakExp () + CallExp (symbol name, exps_type args) + MethodCallExp (Var object) + CastExp (Exp exp, Ty ty) + ForExp (VarDec vardec, Exp hi, Exp body) + IfExp (Exp test, Exp thenclause, Exp elseclause) + IntExp (int value) + LetExp (ChunkList chunks, Exp body) + NilExp () + ObjectExp (NameTy type_name) + OpExp (Exp left, Oper oper, Exp right) + RecordExp (NameTy type_name, fieldinits_type fields) + SeqExp (exps_type exps) + StringExp (string value) + WhileExp (Exp test, Exp body) + + /Ty/ () + ArrayTy (NameTy base_type) + ClassTy (NameTy super, ChunkList chunks) + NameTy (symbol name) + RecordTy (fields_type fields) + + ChunkList (list_type chunks) + + Field (symbol name, NameTy type_name) + + FieldInit (symbol name, Exp init) + + +Some of these classes also inherit from other classes. + +/Escapable/ + VarDec (NameTy type_name, Exp init) + +/Typable/ + /Dec/ (symbol name) + /Exp/ () + /Ty/ () + +/TypeConstructor/ + /Ty/ () + FunctionDec (VarChunk formals, NameTy result, Exp body) + NilExp () + TypeDec (Ty ty) + diff --git a/tiger-compiler/src/ast/all.hh b/tiger-compiler/src/ast/all.hh new file mode 100644 index 0000000..c55c3eb --- /dev/null +++ b/tiger-compiler/src/ast/all.hh @@ -0,0 +1,51 @@ +/** + ** \file ast/all.hh + ** \brief Include all the exported headers. + */ + +#pragma once + +#include <ast/fwd.hh> + +#include <ast/chunk.hh> + +#include <ast/array-exp.hh> +#include <ast/array-ty.hh> +#include <ast/assert-exp.hh> +#include <ast/assign-exp.hh> +#include <ast/ast.hh> +#include <ast/break-exp.hh> +#include <ast/call-exp.hh> +#include <ast/cast-exp.hh> +#include <ast/chunk-list.hh> +#include <ast/class-ty.hh> +#include <ast/dec.hh> +#include <ast/escapable.hh> +#include <ast/exp.hh> +#include <ast/field-init.hh> +#include <ast/field-var.hh> +#include <ast/field.hh> +#include <ast/for-exp.hh> +#include <ast/function-dec.hh> +#include <ast/if-exp.hh> +#include <ast/int-exp.hh> +#include <ast/let-exp.hh> +#include <ast/method-call-exp.hh> +#include <ast/method-dec.hh> +#include <ast/name-ty.hh> +#include <ast/nil-exp.hh> +#include <ast/object-exp.hh> +#include <ast/op-exp.hh> +#include <ast/record-exp.hh> +#include <ast/record-ty.hh> +#include <ast/seq-exp.hh> +#include <ast/simple-var.hh> +#include <ast/string-exp.hh> +#include <ast/subscript-var.hh> +#include <ast/ty.hh> +#include <ast/typable.hh> +#include <ast/type-constructor.hh> +#include <ast/type-dec.hh> +#include <ast/var-dec.hh> +#include <ast/var.hh> +#include <ast/while-exp.hh> diff --git a/tiger-compiler/src/ast/array-exp.cc b/tiger-compiler/src/ast/array-exp.cc new file mode 100644 index 0000000..8c0f241 --- /dev/null +++ b/tiger-compiler/src/ast/array-exp.cc @@ -0,0 +1,31 @@ +/** + ** \file ast/array-exp.cc + ** \brief Implementation of ast::ArrayExp. + */ + +#include <ast/array-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + ArrayExp::ArrayExp(const Location& location, + NameTy* type_name, + Exp* size, + Exp* init) + : Exp(location) + , type_name_(type_name) + , size_(size) + , init_(init) + {} + + ArrayExp::~ArrayExp() + { + delete type_name_; + delete size_; + delete init_; + } + + void ArrayExp::accept(ConstVisitor& v) const { v(*this); } + + void ArrayExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/array-exp.hh b/tiger-compiler/src/ast/array-exp.hh new file mode 100644 index 0000000..ceab385 --- /dev/null +++ b/tiger-compiler/src/ast/array-exp.hh @@ -0,0 +1,60 @@ +/** + ** \file ast/array-exp.hh + ** \brief Declaration of ast::ArrayExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/name-ty.hh> + +namespace ast +{ + /// ArrayExp. + class ArrayExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an ArrayExp node. + ArrayExp(const Location& location, NameTy* type_name, Exp* size, Exp* init); + ArrayExp(const ArrayExp&) = delete; + ArrayExp& operator=(const ArrayExp&) = delete; + /// Destroy an ArrayExp node. + ~ArrayExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return identifier of the stored elements type. + const NameTy& type_name_get() const; + /// Return identifier of the stored elements type. + NameTy& type_name_get(); + /// Return size of the array. + const Exp& size_get() const; + /// Return size of the array. + Exp& size_get(); + /// Return initial value assigned to all elements of the array. + const Exp& init_get() const; + /// Return initial value assigned to all elements of the array. + Exp& init_get(); + /** \} */ + + protected: + /// Identifier of the stored elements type. + NameTy* type_name_; + /// Size of the array. + Exp* size_; + /// Initial value assigned to all elements of the array. + Exp* init_; + }; +} // namespace ast +#include <ast/array-exp.hxx> diff --git a/tiger-compiler/src/ast/array-exp.hxx b/tiger-compiler/src/ast/array-exp.hxx new file mode 100644 index 0000000..cab3e8b --- /dev/null +++ b/tiger-compiler/src/ast/array-exp.hxx @@ -0,0 +1,22 @@ +/** + ** \file ast/array-exp.hxx + ** \brief Inline methods of ast::ArrayExp. + */ + +#pragma once + +#include <ast/array-exp.hh> + +namespace ast +{ + + inline const NameTy& ArrayExp::type_name_get() const { return *type_name_; } + inline NameTy& ArrayExp::type_name_get() { return *type_name_; } + + inline const Exp& ArrayExp::size_get() const { return *size_; } + inline Exp& ArrayExp::size_get() { return *size_; } + + inline const Exp& ArrayExp::init_get() const { return *init_; } + inline Exp& ArrayExp::init_get() { return *init_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/array-ty.cc b/tiger-compiler/src/ast/array-ty.cc new file mode 100644 index 0000000..d7af8b4 --- /dev/null +++ b/tiger-compiler/src/ast/array-ty.cc @@ -0,0 +1,21 @@ +/** + ** \file ast/array-ty.cc + ** \brief Implementation of ast::ArrayTy. + */ + +#include <ast/array-ty.hh> +#include <ast/visitor.hh> + +namespace ast +{ + ArrayTy::ArrayTy(const Location& location, NameTy* base_type) + : Ty(location) + , base_type_(base_type) + {} + + ArrayTy::~ArrayTy() { delete base_type_; } + + void ArrayTy::accept(ConstVisitor& v) const { v(*this); } + + void ArrayTy::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/array-ty.hh b/tiger-compiler/src/ast/array-ty.hh new file mode 100644 index 0000000..73bb956 --- /dev/null +++ b/tiger-compiler/src/ast/array-ty.hh @@ -0,0 +1,48 @@ +/** + ** \file ast/array-ty.hh + ** \brief Declaration of ast::ArrayTy. + */ + +#pragma once + +#include <ast/name-ty.hh> +#include <ast/ty.hh> + +namespace ast +{ + /// ArrayTy. + class ArrayTy : public Ty + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an ArrayTy node. + ArrayTy(const Location& location, NameTy* base_type); + ArrayTy(const ArrayTy&) = delete; + ArrayTy& operator=(const ArrayTy&) = delete; + /// Destroy an ArrayTy node. + ~ArrayTy() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return name of the base type. + const NameTy& base_type_get() const; + /// Return name of the base type. + NameTy& base_type_get(); + /** \} */ + + protected: + /// Name of the base type. + NameTy* base_type_; + }; +} // namespace ast +#include <ast/array-ty.hxx> diff --git a/tiger-compiler/src/ast/array-ty.hxx b/tiger-compiler/src/ast/array-ty.hxx new file mode 100644 index 0000000..c679b1c --- /dev/null +++ b/tiger-compiler/src/ast/array-ty.hxx @@ -0,0 +1,16 @@ +/** + ** \file ast/array-ty.hxx + ** \brief Inline methods of ast::ArrayTy. + */ + +#pragma once + +#include <ast/array-ty.hh> + +namespace ast +{ + + inline const NameTy& ArrayTy::base_type_get() const { return *base_type_; } + inline NameTy& ArrayTy::base_type_get() { return *base_type_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/assert-exp.cc b/tiger-compiler/src/ast/assert-exp.cc new file mode 100644 index 0000000..251f776 --- /dev/null +++ b/tiger-compiler/src/ast/assert-exp.cc @@ -0,0 +1,24 @@ +/** + ** \file ast/assert-exp.cc + ** \brief Implementation of ast::AssertExp. + */ + +#include <ast/assert-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + AssertExp::AssertExp(const Location& location, Exp* condition) + : Exp(location) + , cond_{condition} + {} + + AssertExp::~AssertExp() + { + delete cond_; + } + + void AssertExp::accept(ConstVisitor& v) const { v(*this); } + + void AssertExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/assert-exp.hh b/tiger-compiler/src/ast/assert-exp.hh new file mode 100644 index 0000000..6650ca4 --- /dev/null +++ b/tiger-compiler/src/ast/assert-exp.hh @@ -0,0 +1,42 @@ +/** + ** \file ast/assert-exp.hh + ** \brief Declaration of ast::AssertExp. + */ + +#pragma once + +#include <ast/exp.hh> + +namespace ast +{ + // AssertExp + class AssertExp : public Exp + { + public: + /// Construct an AssertExp node. + AssertExp(const Location& location, Exp* cond); + AssertExp(const AssertExp&) = delete; + AssertExp& operator=(const AssertExp&) = delete; + + /// Destroy an AssertExp node. + ~AssertExp() override; + + + // Here are the visitor methods + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + + // Some basic getters + // Return the asserted condition in const form + const Exp& cond_get() const; + // Return the asserted condition + Exp& cond_get(); + + protected: + // The condition that the assertion will evaluate + Exp* cond_; + }; +} // namespace ast +#include <ast/assert-exp.hxx> diff --git a/tiger-compiler/src/ast/assert-exp.hxx b/tiger-compiler/src/ast/assert-exp.hxx new file mode 100644 index 0000000..d5f8af8 --- /dev/null +++ b/tiger-compiler/src/ast/assert-exp.hxx @@ -0,0 +1,14 @@ +/** + ** \file ast/assert-exp.hxx + ** \brief Inline methods of ast::AssertExp. + */ + +#pragma once + +#include <ast/assert-exp.hh> + +namespace ast +{ + inline const Exp& AssertExp::cond_get() const { return *cond_; } + inline Exp& AssertExp::cond_get() { return *cond_; } +} // namespace ast diff --git a/tiger-compiler/src/ast/assert-visitor.hh b/tiger-compiler/src/ast/assert-visitor.hh new file mode 100644 index 0000000..0488c1c --- /dev/null +++ b/tiger-compiler/src/ast/assert-visitor.hh @@ -0,0 +1,51 @@ +/** + ** \file ast/object-visitor.hh + ** \brief Provide default visits for assertion nodes. + */ + +#pragma once + +#include <ast/visitor.hh> + +namespace ast +{ + template <template <typename> class Const> + class GenAssertVisitor : virtual public GenVisitor<Const> + { + public: + /// Super class type. + using super_type = GenVisitor<Const>; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Convenient abbreviation. + template <typename Type> using const_t = typename Const<Type>::type; + + /** \name Ctor & dtor. + ** \{ */ + /// Construct an object visitor. + GenAssertVisitor(); + /// Destroy an object visitor. + virtual ~GenAssertVisitor(); + /** \} */ + + /// \name Object-related visits. + /// \{ + void operator()(const_t<AssertExp>& e) override; + /// \} + }; + + /// Shorthand for a const visitor. + using AssertConstVisitor = GenAssertVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using AssertVisitor = GenAssertVisitor<misc::id_traits>; + +#ifdef SWIG + %template(AssertConstVisitor) GenAssertVisitor<misc::constify_traits>; + %template(AssertVisitor) GenAssertVisitor<misc::id_traits>; +#endif + +} // namespace ast + +#include <ast/assert-visitor.hxx> diff --git a/tiger-compiler/src/ast/assert-visitor.hxx b/tiger-compiler/src/ast/assert-visitor.hxx new file mode 100644 index 0000000..c55bb43 --- /dev/null +++ b/tiger-compiler/src/ast/assert-visitor.hxx @@ -0,0 +1,32 @@ +/** + ** \file ast/assert-visitor.hxx + ** \brief Implementation for ast/assert-visitor.hh. + */ + +#pragma once + +#include <ast/assert-exp.hh> +#include <ast/assert-visitor.hh> + +namespace ast +{ + template <template <typename> class Const> + GenAssertVisitor<Const>::GenAssertVisitor() + : GenVisitor<Const>() + {} + + template <template <typename> class Const> + GenAssertVisitor<Const>::~GenAssertVisitor() + {} + + /*-------------------------------. + | Assert-related visit method. | + `-------------------------------*/ + + template <template <typename> class Const> + void GenAssertVisitor<Const>::operator()(const_t<AssertExp>& e) + { + e.cond_get().accept(*this); + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/assign-exp.cc b/tiger-compiler/src/ast/assign-exp.cc new file mode 100644 index 0000000..d61b5c2 --- /dev/null +++ b/tiger-compiler/src/ast/assign-exp.cc @@ -0,0 +1,26 @@ +/** + ** \file ast/assign-exp.cc + ** \brief Implementation of ast::AssignExp. + */ + +#include <ast/assign-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + AssignExp::AssignExp(const Location& location, Var* var, Exp* exp) + : Exp(location) + , var_(var) + , exp_(exp) + {} + + AssignExp::~AssignExp() + { + delete var_; + delete exp_; + } + + void AssignExp::accept(ConstVisitor& v) const { v(*this); } + + void AssignExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/assign-exp.hh b/tiger-compiler/src/ast/assign-exp.hh new file mode 100644 index 0000000..ca575ec --- /dev/null +++ b/tiger-compiler/src/ast/assign-exp.hh @@ -0,0 +1,54 @@ +/** + ** \file ast/assign-exp.hh + ** \brief Declaration of ast::AssignExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/var.hh> + +namespace ast +{ + /// AssignExp. + class AssignExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an AssignExp node. + AssignExp(const Location& location, Var* var, Exp* exp); + AssignExp(const AssignExp&) = delete; + AssignExp& operator=(const AssignExp&) = delete; + /// Destroy an AssignExp node. + ~AssignExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return reference to the affected variable. + const Var& var_get() const; + /// Return reference to the affected variable. + Var& var_get(); + /// Return assigned value. + const Exp& exp_get() const; + /// Return assigned value. + Exp& exp_get(); + /** \} */ + + protected: + /// Reference to the affected variable. + Var* var_; + /// Assigned value. + Exp* exp_; + }; +} // namespace ast +#include <ast/assign-exp.hxx> diff --git a/tiger-compiler/src/ast/assign-exp.hxx b/tiger-compiler/src/ast/assign-exp.hxx new file mode 100644 index 0000000..afd96ff --- /dev/null +++ b/tiger-compiler/src/ast/assign-exp.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/assign-exp.hxx + ** \brief Inline methods of ast::AssignExp. + */ + +#pragma once + +#include <ast/assign-exp.hh> + +namespace ast +{ + + inline const Var& AssignExp::var_get() const { return *var_; } + inline Var& AssignExp::var_get() { return *var_; } + + inline const Exp& AssignExp::exp_get() const { return *exp_; } + inline Exp& AssignExp::exp_get() { return *exp_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/ast-nodes.mk b/tiger-compiler/src/ast/ast-nodes.mk new file mode 100644 index 0000000..ab35724 --- /dev/null +++ b/tiger-compiler/src/ast/ast-nodes.mk @@ -0,0 +1,41 @@ +AST_NODES = \ + src/ast/array-exp.hh src/ast/array-exp.hxx src/ast/array-exp.cc \ + src/ast/array-ty.hh src/ast/array-ty.hxx src/ast/array-ty.cc \ + src/ast/assert-exp.hh src/ast/assert-exp.hxx src/ast/assert-exp.cc \ + src/ast/assign-exp.hh src/ast/assign-exp.hxx src/ast/assign-exp.cc \ + src/ast/ast.hh src/ast/ast.hxx src/ast/ast.cc \ + src/ast/break-exp.hh src/ast/break-exp.hxx src/ast/break-exp.cc \ + src/ast/call-exp.hh src/ast/call-exp.hxx src/ast/call-exp.cc \ + src/ast/cast-exp.hh src/ast/cast-exp.hxx src/ast/cast-exp.cc \ + src/ast/chunk-list.hh src/ast/chunk-list.hxx src/ast/chunk-list.cc \ + src/ast/class-ty.hh src/ast/class-ty.hxx src/ast/class-ty.cc \ + src/ast/dec.hh src/ast/dec.hxx src/ast/dec.cc \ + src/ast/escapable.hh src/ast/escapable.hxx src/ast/escapable.cc \ + src/ast/exp.hh src/ast/exp.hxx src/ast/exp.cc \ + src/ast/field.hh src/ast/field.hxx src/ast/field.cc \ + src/ast/field-init.hh src/ast/field-init.hxx src/ast/field-init.cc \ + src/ast/field-var.hh src/ast/field-var.hxx src/ast/field-var.cc \ + src/ast/for-exp.hh src/ast/for-exp.hxx src/ast/for-exp.cc \ + src/ast/function-dec.hh src/ast/function-dec.hxx src/ast/function-dec.cc \ + src/ast/if-exp.hh src/ast/if-exp.hxx src/ast/if-exp.cc \ + src/ast/int-exp.hh src/ast/int-exp.hxx src/ast/int-exp.cc \ + src/ast/let-exp.hh src/ast/let-exp.hxx src/ast/let-exp.cc \ + src/ast/method-call-exp.hh src/ast/method-call-exp.hxx src/ast/method-call-exp.cc \ + src/ast/method-dec.hh src/ast/method-dec.hxx src/ast/method-dec.cc \ + src/ast/name-ty.hh src/ast/name-ty.hxx src/ast/name-ty.cc \ + src/ast/nil-exp.hh src/ast/nil-exp.hxx src/ast/nil-exp.cc \ + src/ast/object-exp.hh src/ast/object-exp.hxx src/ast/object-exp.cc \ + src/ast/op-exp.hh src/ast/op-exp.hxx src/ast/op-exp.cc \ + src/ast/record-exp.hh src/ast/record-exp.hxx src/ast/record-exp.cc \ + src/ast/record-ty.hh src/ast/record-ty.hxx src/ast/record-ty.cc \ + src/ast/seq-exp.hh src/ast/seq-exp.hxx src/ast/seq-exp.cc \ + src/ast/simple-var.hh src/ast/simple-var.hxx src/ast/simple-var.cc \ + src/ast/string-exp.hh src/ast/string-exp.hxx src/ast/string-exp.cc \ + src/ast/subscript-var.hh src/ast/subscript-var.hxx src/ast/subscript-var.cc \ + src/ast/ty.hh src/ast/ty.hxx src/ast/ty.cc \ + src/ast/typable.hh src/ast/typable.hxx src/ast/typable.cc \ + src/ast/type-constructor.hh src/ast/type-constructor.hxx src/ast/type-constructor.cc \ + src/ast/type-dec.hh src/ast/type-dec.hxx src/ast/type-dec.cc \ + src/ast/var.hh src/ast/var.hxx src/ast/var.cc \ + src/ast/var-dec.hh src/ast/var-dec.hxx src/ast/var-dec.cc \ + src/ast/while-exp.hh src/ast/while-exp.hxx src/ast/while-exp.cc diff --git a/tiger-compiler/src/ast/ast.cc b/tiger-compiler/src/ast/ast.cc new file mode 100644 index 0000000..136262f --- /dev/null +++ b/tiger-compiler/src/ast/ast.cc @@ -0,0 +1,15 @@ +/** + ** \file ast/ast.cc + ** \brief Implementation of ast::Ast. + */ + +#include <ast/ast.hh> +#include <ast/visitor.hh> + +namespace ast +{ + Ast::Ast(const Location& location) + : location_(location) + {} + +} // namespace ast diff --git a/tiger-compiler/src/ast/ast.hh b/tiger-compiler/src/ast/ast.hh new file mode 100644 index 0000000..1c3037c --- /dev/null +++ b/tiger-compiler/src/ast/ast.hh @@ -0,0 +1,48 @@ +/** + ** \file ast/ast.hh + ** \brief Declaration of ast::Ast. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <ast/location.hh> + +namespace ast +{ + /// Ast. + class Ast + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an Ast node. + explicit Ast(const Location& location); + Ast(const Ast&) = delete; + Ast& operator=(const Ast&) = delete; + /// Destroy an Ast node. + virtual ~Ast() = default; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + virtual void accept(ConstVisitor& v) const = 0; + /// Accept a non-const visitor \a v. + virtual void accept(Visitor& v) = 0; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return scanner position information. + const Location& location_get() const; + /// Set scanner position information. + void location_set(const Location&); + /** \} */ + + protected: + /// Scanner position information. + Location location_; + }; +} // namespace ast +#include <ast/ast.hxx> diff --git a/tiger-compiler/src/ast/ast.hxx b/tiger-compiler/src/ast/ast.hxx new file mode 100644 index 0000000..963aaba --- /dev/null +++ b/tiger-compiler/src/ast/ast.hxx @@ -0,0 +1,18 @@ +/** + ** \file ast/ast.hxx + ** \brief Inline methods of ast::Ast. + */ + +#pragma once + +#include <ast/ast.hh> + +namespace ast +{ + inline const Location& Ast::location_get() const { return location_; } + inline void Ast::location_set(const Location& location) + { + location_ = location; + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/break-exp.cc b/tiger-compiler/src/ast/break-exp.cc new file mode 100644 index 0000000..3c76946 --- /dev/null +++ b/tiger-compiler/src/ast/break-exp.cc @@ -0,0 +1,18 @@ +/** + ** \file ast/break-exp.cc + ** \brief Implementation of ast::BreakExp. + */ + +#include <ast/break-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + BreakExp::BreakExp(const Location& location) + : Exp(location) + {} + + void BreakExp::accept(ConstVisitor& v) const { v(*this); } + + void BreakExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/break-exp.hh b/tiger-compiler/src/ast/break-exp.hh new file mode 100644 index 0000000..25d89e8 --- /dev/null +++ b/tiger-compiler/src/ast/break-exp.hh @@ -0,0 +1,49 @@ +/** + ** \file ast/break-exp.hh + ** \brief Declaration of ast::BreakExp. + */ + +#pragma once + +#include <ast/exp.hh> + +namespace ast +{ + /// BreakExp. + class BreakExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a BreakExp node. + explicit BreakExp(const Location& location); + BreakExp(const BreakExp&) = delete; + BreakExp& operator=(const BreakExp&) = delete; + /// Destroy a BreakExp node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + // FIXME DONE: Some code was deleted here. + /// Return definition site. + const Exp* def_get() const; + Exp* def_get(); + // FIXME DONE: Some code was deleted here. + /// Set definition site. + void def_set(Exp*); + /** \} */ + + protected: + /// The loop it breaks. + Exp* def_ = nullptr; + }; +} // namespace ast +#include <ast/break-exp.hxx> diff --git a/tiger-compiler/src/ast/break-exp.hxx b/tiger-compiler/src/ast/break-exp.hxx new file mode 100644 index 0000000..604f754 --- /dev/null +++ b/tiger-compiler/src/ast/break-exp.hxx @@ -0,0 +1,21 @@ +/** + ** \file ast/break-exp.hxx + ** \brief Inline methods of ast::BreakExp. + */ + +#pragma once + +#include <ast/break-exp.hh> + +// Hint: this needs to be done at TC-3. + +namespace ast +{ + + // FIXME DONE: Some code was deleted here. + inline const Exp* BreakExp::def_get() const { return def_; } + inline Exp* BreakExp::def_get() { return def_; } + // FIXME DONE: Some code was deleted here. + inline void BreakExp::def_set(Exp* def) { def_ = def; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/call-exp.cc b/tiger-compiler/src/ast/call-exp.cc new file mode 100644 index 0000000..546ab5f --- /dev/null +++ b/tiger-compiler/src/ast/call-exp.cc @@ -0,0 +1,27 @@ +/** + ** \file ast/call-exp.cc + ** \brief Implementation of ast::CallExp. + */ + +#include <ast/call-exp.hh> +#include <ast/visitor.hh> +#include <misc/algorithm.hh> + +namespace ast +{ + CallExp::CallExp(const Location& location, misc::symbol name, exps_type* args) + : Exp(location) + , name_(name) + , args_(args) + {} + + CallExp::~CallExp() + { + misc::deep_clear(*args_); + delete args_; + } + + void CallExp::accept(ConstVisitor& v) const { v(*this); } + + void CallExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/call-exp.hh b/tiger-compiler/src/ast/call-exp.hh new file mode 100644 index 0000000..a4b6c90 --- /dev/null +++ b/tiger-compiler/src/ast/call-exp.hh @@ -0,0 +1,64 @@ +/** + ** \file ast/call-exp.hh + ** \brief Declaration of ast::CallExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/function-dec.hh> +#include <misc/symbol.hh> + +namespace ast +{ + /// CallExp. + class CallExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a CallExp node. + CallExp(const Location& location, misc::symbol name, exps_type* args); + CallExp(const CallExp&) = delete; + CallExp& operator=(const CallExp&) = delete; + /// Destroy a CallExp node. + ~CallExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return identifier of the called function. + misc::symbol name_get() const; + /// Set identifier of the called function. + void name_set(misc::symbol); + /// Return list of arguments passed to the function. + const exps_type& args_get() const; + /// Return list of arguments passed to the function. + exps_type& args_get(); + // FIXME DONE: Some code was deleted here. + /// Return definition site. + const FunctionDec* def_get() const; + FunctionDec* def_get(); + // FIXME DONE: Some code was deleted here. + /// Set definition site. + void def_set(FunctionDec*); + /** \} */ + + protected: + /// Identifier of the called function. + misc::symbol name_; + /// List of arguments passed to the function. + exps_type* args_; + /// Definition site. + FunctionDec* def_ = nullptr; + }; +} // namespace ast +#include <ast/call-exp.hxx> diff --git a/tiger-compiler/src/ast/call-exp.hxx b/tiger-compiler/src/ast/call-exp.hxx new file mode 100644 index 0000000..63b9c51 --- /dev/null +++ b/tiger-compiler/src/ast/call-exp.hxx @@ -0,0 +1,25 @@ +/** + ** \file ast/call-exp.hxx + ** \brief Inline methods of ast::CallExp. + */ + +#pragma once + +#include <ast/call-exp.hh> + +namespace ast +{ + + inline misc::symbol CallExp::name_get() const { return name_; } + inline void CallExp::name_set(misc::symbol name) { name_ = name; } + + inline const exps_type& CallExp::args_get() const { return *args_; } + inline exps_type& CallExp::args_get() { return *args_; } + + // FIXME DONE: Some code was deleted here. + inline const FunctionDec* CallExp::def_get() const { return def_; } + inline FunctionDec* CallExp::def_get() { return def_; } + // FIXME DONE: Some code was deleted here. + inline void CallExp::def_set(FunctionDec* def) { def_ = def; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/cast-exp.cc b/tiger-compiler/src/ast/cast-exp.cc new file mode 100644 index 0000000..e40f8f0 --- /dev/null +++ b/tiger-compiler/src/ast/cast-exp.cc @@ -0,0 +1,26 @@ +/** + ** \file ast/cast-exp.cc + ** \brief Implementation of ast::CastExp. + */ + +#include <ast/cast-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + CastExp::CastExp(const Location& location, Exp* exp, Ty* ty) + : Exp(location) + , exp_(exp) + , ty_(ty) + {} + + CastExp::~CastExp() + { + delete exp_; + delete ty_; + } + + void CastExp::accept(ConstVisitor& v) const { v(*this); } + + void CastExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/cast-exp.hh b/tiger-compiler/src/ast/cast-exp.hh new file mode 100644 index 0000000..a73e7bb --- /dev/null +++ b/tiger-compiler/src/ast/cast-exp.hh @@ -0,0 +1,62 @@ +/** + ** \file ast/cast-exp.hh + ** \brief Declaration of ast::CastExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/ty.hh> + +namespace ast +{ + /** \class ast::CastExp + ** \brief Cast the type of an expression to a given type. + ** + ** This node is only used in the bounds checking transformation + ** (see desugar::bounds_checks_add). You don't need to worry + ** about it (nor about the `cast' keyword) if you don't implement + ** this option. + */ + + class CastExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a CastExp node. + CastExp(const Location& location, Exp* exp, Ty* ty); + CastExp(const CastExp&) = delete; + CastExp& operator=(const CastExp&) = delete; + /// Destroy a CastExp node. + ~CastExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the cast expression. + const Exp& exp_get() const; + /// Return the cast expression. + Exp& exp_get(); + /// Return the target type. + const Ty& ty_get() const; + /// Return the target type. + Ty& ty_get(); + /** \} */ + + protected: + /// The cast expression. + Exp* exp_; + /// The target type. + Ty* ty_; + }; +} // namespace ast +#include <ast/cast-exp.hxx> diff --git a/tiger-compiler/src/ast/cast-exp.hxx b/tiger-compiler/src/ast/cast-exp.hxx new file mode 100644 index 0000000..4f248ad --- /dev/null +++ b/tiger-compiler/src/ast/cast-exp.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/cast-exp.hxx + ** \brief Inline methods of ast::CastExp. + */ + +#pragma once + +#include <ast/cast-exp.hh> + +namespace ast +{ + + inline const Exp& CastExp::exp_get() const { return *exp_; } + inline Exp& CastExp::exp_get() { return *exp_; } + + inline const Ty& CastExp::ty_get() const { return *ty_; } + inline Ty& CastExp::ty_get() { return *ty_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/chunk-interface.hh b/tiger-compiler/src/ast/chunk-interface.hh new file mode 100644 index 0000000..dcaad18 --- /dev/null +++ b/tiger-compiler/src/ast/chunk-interface.hh @@ -0,0 +1,25 @@ +/** + ** \file ast/chunk-interface.hh + ** \brief Declare the interface for ChunkInterface class. + */ + +#pragma once + +#include <ast/ast.hh> + +namespace ast +{ + /// Declare ChunkInterface + class ChunkInterface : public Ast + { + /** \name Ctors and dtors. + ** \{ */ + public: + /// Construct a ChunkInterface + explicit ChunkInterface(const Location& location); + /** \} */ + }; + +} // namespace ast + +#include <ast/chunk-interface.hxx> diff --git a/tiger-compiler/src/ast/chunk-interface.hxx b/tiger-compiler/src/ast/chunk-interface.hxx new file mode 100644 index 0000000..7824297 --- /dev/null +++ b/tiger-compiler/src/ast/chunk-interface.hxx @@ -0,0 +1,16 @@ +/** + ** \file ast/chunk-interface.hxx + ** \brief Inline methods for ast/chunk-interface.hh + */ + +#pragma once + +#include <ast/chunk-interface.hh> + +namespace ast +{ + inline ChunkInterface::ChunkInterface(const Location& location) + : Ast(location) + {} + +} // namespace ast diff --git a/tiger-compiler/src/ast/chunk-list.cc b/tiger-compiler/src/ast/chunk-list.cc new file mode 100644 index 0000000..6a530c4 --- /dev/null +++ b/tiger-compiler/src/ast/chunk-list.cc @@ -0,0 +1,58 @@ +/** + ** \file ast/chunk-list.cc + ** \brief Implementation of ast::ChunkList. + */ + +#include <ast/chunk-interface.hh> +#include <ast/chunk-list.hh> +#include <ast/visitor.hh> +#include <misc/algorithm.hh> + +namespace ast +{ + ChunkList::iterator ChunkList::begin() { return chunks_.begin(); } + + ChunkList::iterator ChunkList::end() { return chunks_.end(); } + + ChunkList::const_iterator ChunkList::begin() const { return chunks_.begin(); } + + ChunkList::const_iterator ChunkList::end() const { return chunks_.end(); } + + void ChunkList::push_front(ChunkInterface* d) + { + chunks_.emplace_front(d); + location_.begin = d->location_get().begin; + } + + void ChunkList::emplace_back(ChunkInterface* d) + { + chunks_.emplace_back(d); + location_.end = d->location_get().end; + } + + void ChunkList::splice_front(ChunkList& ds) + { + chunks_.splice(chunks_.begin(), ds.chunks_get()); + } + + void ChunkList::splice_back(ChunkList& ds) + { + chunks_.splice(chunks_.end(), ds.chunks_get()); + } + + ChunkList::ChunkList(const Location& location) + : Ast(location) + {} + + ChunkList::ChunkList(const Location& location, + const ChunkList::list_type& chunks) + : Ast(location) + , chunks_(chunks) + {} + + ChunkList::~ChunkList() { misc::deep_clear(chunks_); } + + void ChunkList::accept(ConstVisitor& v) const { v(*this); } + + void ChunkList::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/chunk-list.hh b/tiger-compiler/src/ast/chunk-list.hh new file mode 100644 index 0000000..3674e27 --- /dev/null +++ b/tiger-compiler/src/ast/chunk-list.hh @@ -0,0 +1,80 @@ +/** + ** \file ast/chunk-list.hh + ** \brief Declaration of ast::ChunkList. + */ + +#pragma once + +#include <ast/ast.hh> + +namespace ast +{ + /// ChunkList. + class ChunkList : public Ast + { + public: + using list_type = std::list<ChunkInterface*>; + /// Define value type + using value_type = list_type::value_type; + /// Define size type + using size_type = list_type::size_type; + /// Define reference to value type + using reference = list_type::reference; + /// Define const reference to value type + using const_reference = list_type::const_reference; + /// Define shorthand type for D-declations iterator. + using iterator = list_type::iterator; + /// Define shorthand type for D-declations const iterator. + using const_iterator = list_type::const_iterator; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + /// Prepend \a d. + void push_front(ChunkInterface* d); + /// Append \a d. + void emplace_back(ChunkInterface* d); + + /// Splice the content of \a ds in front of this list. + void splice_front(ChunkList& ds); + /// Splice the content of \a ds at the back this list. + void splice_back(ChunkList& ds); + + /// Construct a ChunkList node. + ChunkList(const Location& location); + + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a ChunkList node. + ChunkList(const Location& location, const ChunkList::list_type& chunks); + ChunkList(const ChunkList&) = delete; + ChunkList& operator=(const ChunkList&) = delete; + /// Destroy a ChunkList node. + ~ChunkList() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return declarations. + const ChunkList::list_type& chunks_get() const; + /// Return declarations. + ChunkList::list_type& chunks_get(); + /** \} */ + + protected: + /// Declarations. + ChunkList::list_type chunks_; + }; +} // namespace ast +#include <ast/chunk-list.hxx> diff --git a/tiger-compiler/src/ast/chunk-list.hxx b/tiger-compiler/src/ast/chunk-list.hxx new file mode 100644 index 0000000..6307760 --- /dev/null +++ b/tiger-compiler/src/ast/chunk-list.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/chunk-list.hxx + ** \brief Inline methods of ast::ChunkList. + */ + +#pragma once + +#include <ast/chunk-list.hh> + +namespace ast +{ + + inline const ChunkList::list_type& ChunkList::chunks_get() const + { + return chunks_; + } + inline ChunkList::list_type& ChunkList::chunks_get() { return chunks_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/chunk.hh b/tiger-compiler/src/ast/chunk.hh new file mode 100644 index 0000000..8f59fb0 --- /dev/null +++ b/tiger-compiler/src/ast/chunk.hh @@ -0,0 +1,139 @@ +/** + ** \file ast/chunk.hh + ** \brief Declaration of ast::Chunk. + */ + +#pragma once + +#include <vector> + +#include <ast/chunk-interface.hh> + +namespace ast +{ + template <typename D> + /// Abstract a "list of D-declarations" node. + class Chunk : public ChunkInterface + { + /** \name Member types + ** \{ */ + public: + /// Define shorthand type for list of D-declarations. + using Ds = std::vector<D*>; + /// Define value type + using value_type = typename Ds::value_type; + /// Define size type + using size_type = typename Ds::size_type; + /// Define reference to value type + using reference = typename Ds::reference; + /// Define const reference to value type + using const_reference = typename Ds::const_reference; + /// Define shorthand type for D-declations iterator. + using iterator = typename Ds::iterator; + /// Define shorthand type for D-declations const iterator. + using const_iterator = typename Ds::const_iterator; + + /** \} */ + + /** \name Ctor & dtor. + ** \{ */ + public: + /** \brief Construct an Chunk node with a list of D-declarations. + ** \param location scanner position informations + ** \param decs list of D-declarations */ + Chunk(const Location& location, Ds* decs); + explicit Chunk(const Location& location); + + /** \brief Destroys an Chunk node. + ** + ** Free list and its content. */ + ~Chunk() override; + + /** \} */ + + /** \name Visitors entry point. + ** \{ */ + public: + /// Accept a const visitor \a v. + void accept(Visitor& v) override; + + /// Accept a non-const visitor \a v. + void accept(ConstVisitor& v) const override; + + /** \} */ + + /** \name Accessors. + ** \{ */ + public: /** \brief Access specified element + ** /param pos position of the element to return */ + constexpr reference operator[](size_type pos); + + /** \brief Access specified const element + ** /param pos position of the element to return */ + constexpr const_reference operator[](size_type pos) const; + + /// Access to list of D-declarations (read and write). + Ds& decs_get(); + + /// Access to list of D-declarations (read only). + const Ds& decs_get() const; + + /** \} */ + + /** \name Iterators. + ** \{ */ + public: + /// Return an iterator to the begging. + iterator begin(); + /// Return a const iterator to the begging. + const_iterator begin() const; + + /// Return an iterator to the end. + iterator end(); + /// Return a const iterator to the end. + const_iterator end() const; + + /** \} */ + + /** \name Capacity. + ** \} */ + public: + /// Checks whether the container is empty. +#ifdef SWIG + constexpr bool empty() const noexcept; +#else /* SWIG */ + [[nodiscard]] constexpr bool empty() const noexcept; +#endif /* SWIG */ + /** \name Modifiers. + ** \{ */ + public: + /** \brief Erase the specified element from the container. + ** \param pos position of the element to remove. */ + constexpr iterator erase(const_iterator pos); + + /** \brief Erase the specified elements in range from the container. + ** \param first begin of the range + ** \param last end of the range */ + constexpr iterator erase(const_iterator first, const_iterator last); + + /** \brief Push \a d in front. + ** \param d declaration to push */ + Chunk<D>& push_front(D& d); + + /** \brief Push \a d in back. + ** \param d declaration to push */ + Chunk<D>& emplace_back(D& d); + + /** \} */ + + // SWIG 2 does not understand C++11 constructs, such as data + // member initializers. +#ifndef SWIG + private: + Ds* decs_ = new Ds(); +#endif + }; + +} // namespace ast + +#include <ast/chunk.hxx> diff --git a/tiger-compiler/src/ast/chunk.hxx b/tiger-compiler/src/ast/chunk.hxx new file mode 100644 index 0000000..a3c3ada --- /dev/null +++ b/tiger-compiler/src/ast/chunk.hxx @@ -0,0 +1,118 @@ +/** + ** \file ast/chunk.hxx + ** \brief Implementation of ast::Chunk. + */ + +#pragma once + +#include <ast/chunk.hh> +#include <ast/visitor.hh> +#include <misc/algorithm.hh> + +namespace ast +{ + template <typename D> + Chunk<D>::Chunk(const Location& location, Ds* decs) + : ChunkInterface(location) + , decs_(decs) + {} + + template <typename D> + Chunk<D>::Chunk(const Location& location) + : ChunkInterface(location) + {} + + template <typename D> Chunk<D>::~Chunk() + { + misc::deep_clear(*decs_); + delete decs_; + } + + template <typename D> inline void Chunk<D>::accept(Visitor& v) { v(*this); } + + template <typename D> inline void Chunk<D>::accept(ConstVisitor& v) const + { + v(*this); + } + + template <typename D> + inline constexpr typename Chunk<D>::reference + Chunk<D>::operator[](size_type pos) + { + return decs_->operator[](pos); + } + + template <typename D> + inline constexpr typename Chunk<D>::const_reference + Chunk<D>::operator[](size_type pos) const + { + return decs_->operator[](pos); + } + + template <typename D> inline typename Chunk<D>::Ds& Chunk<D>::decs_get() + { + return *decs_; + } + + template <typename D> + inline const typename Chunk<D>::Ds& Chunk<D>::decs_get() const + { + return *decs_; + } + + template <typename D> inline typename Chunk<D>::iterator Chunk<D>::begin() + { + return decs_->begin(); + } + + template <typename D> + inline typename Chunk<D>::const_iterator Chunk<D>::begin() const + { + return decs_->begin(); + } + + template <typename D> inline typename Chunk<D>::iterator Chunk<D>::end() + { + return decs_->end(); + } + + template <typename D> + inline typename Chunk<D>::const_iterator Chunk<D>::end() const + { + return decs_->end(); + } + + template <typename D> inline constexpr bool Chunk<D>::empty() const noexcept + { + return decs_->empty(); + } + + template <typename D> + inline constexpr typename Chunk<D>::iterator + Chunk<D>::erase(const_iterator pos) + { + return decs_->erase(pos); + } + + template <typename D> + inline constexpr typename Chunk<D>::iterator + Chunk<D>::erase(const_iterator first, const_iterator last) + { + return decs_->erase(first, last); + } + + template <typename D> Chunk<D>& Chunk<D>::push_front(D& d) + { + location_set(location_get() + d.location_get()); + decs_->insert(decs_->begin(), &d); + return *this; + } + + template <typename D> Chunk<D>& Chunk<D>::emplace_back(D& d) + { + location_set(location_get() + d.location_get()); + decs_->emplace_back(&d); + return *this; + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/class-ty.cc b/tiger-compiler/src/ast/class-ty.cc new file mode 100644 index 0000000..ca5e983 --- /dev/null +++ b/tiger-compiler/src/ast/class-ty.cc @@ -0,0 +1,22 @@ +/** + ** \file ast/class-ty.cc + ** \brief Implementation of ast::ClassTy. + */ + +#include <ast/class-ty.hh> +#include <ast/visitor.hh> + +namespace ast +{ + ClassTy::ClassTy(const Location& location, NameTy* super, ChunkList* chunks) + : Ty(location) + , super_(super) + , chunks_(chunks) + {} + + ClassTy::~ClassTy() { delete chunks_; } + + void ClassTy::accept(ConstVisitor& v) const { v(*this); } + + void ClassTy::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/class-ty.hh b/tiger-compiler/src/ast/class-ty.hh new file mode 100644 index 0000000..0045685 --- /dev/null +++ b/tiger-compiler/src/ast/class-ty.hh @@ -0,0 +1,55 @@ +/** + ** \file ast/class-ty.hh + ** \brief Declaration of ast::ClassTy. + */ + +#pragma once + +#include <ast/chunk-list.hh> +#include <ast/name-ty.hh> +#include <ast/ty.hh> + +namespace ast +{ + /// ClassTy. + class ClassTy : public Ty + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a ClassTy node. + ClassTy(const Location& location, NameTy* super, ChunkList* chunks); + ClassTy(const ClassTy&) = delete; + ClassTy& operator=(const ClassTy&) = delete; + /// Destroy a ClassTy node. + ~ClassTy() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return super class. + const NameTy& super_get() const; + /// Return super class. + NameTy& super_get(); + /// Return list of declarations. + const ChunkList& chunks_get() const; + /// Return list of declarations. + ChunkList& chunks_get(); + /** \} */ + + protected: + /// Super class. + NameTy* super_; + /// List of declarations. + ChunkList* chunks_; + }; +} // namespace ast +#include <ast/class-ty.hxx> diff --git a/tiger-compiler/src/ast/class-ty.hxx b/tiger-compiler/src/ast/class-ty.hxx new file mode 100644 index 0000000..70c291b --- /dev/null +++ b/tiger-compiler/src/ast/class-ty.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/class-ty.hxx + ** \brief Inline methods of ast::ClassTy. + */ + +#pragma once + +#include <ast/class-ty.hh> + +namespace ast +{ + + inline const NameTy& ClassTy::super_get() const { return *super_; } + inline NameTy& ClassTy::super_get() { return *super_; } + + inline const ChunkList& ClassTy::chunks_get() const { return *chunks_; } + inline ChunkList& ClassTy::chunks_get() { return *chunks_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/dec.cc b/tiger-compiler/src/ast/dec.cc new file mode 100644 index 0000000..b19ed0b --- /dev/null +++ b/tiger-compiler/src/ast/dec.cc @@ -0,0 +1,17 @@ +/** + ** \file ast/dec.cc + ** \brief Implementation of ast::Dec. + */ + +#include <ast/dec.hh> +#include <ast/visitor.hh> + +namespace ast +{ + Dec::Dec(const Location& location, misc::symbol name) + : Ast(location) + , Typable() + , name_(name) + {} + +} // namespace ast diff --git a/tiger-compiler/src/ast/dec.hh b/tiger-compiler/src/ast/dec.hh new file mode 100644 index 0000000..f61489e --- /dev/null +++ b/tiger-compiler/src/ast/dec.hh @@ -0,0 +1,50 @@ +/** + ** \file ast/dec.hh + ** \brief Declaration of ast::Dec. + */ + +#pragma once + +#include <ast/ast.hh> +#include <ast/typable.hh> +#include <misc/symbol.hh> + +namespace ast +{ + /// Dec. + class Dec + : public Ast + , public Typable + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a Dec node. + Dec(const Location& location, misc::symbol name); + Dec(const Dec&) = delete; + Dec& operator=(const Dec&) = delete; + /// Destroy a Dec node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override = 0; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override = 0; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return name of the defined entity. + misc::symbol name_get() const; + /// Set name of the defined entity. + void name_set(misc::symbol); + /** \} */ + + protected: + /// Name of the defined entity. + misc::symbol name_; + }; +} // namespace ast +#include <ast/dec.hxx> diff --git a/tiger-compiler/src/ast/dec.hxx b/tiger-compiler/src/ast/dec.hxx new file mode 100644 index 0000000..f3c12f5 --- /dev/null +++ b/tiger-compiler/src/ast/dec.hxx @@ -0,0 +1,15 @@ +/** + ** \file ast/dec.hxx + ** \brief Inline methods of ast::Dec. + */ + +#pragma once + +#include <ast/dec.hh> + +namespace ast +{ + inline misc::symbol Dec::name_get() const { return name_; } + inline void Dec::name_set(misc::symbol name) { name_ = name; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/default-visitor.hh b/tiger-compiler/src/ast/default-visitor.hh new file mode 100644 index 0000000..e4e2355 --- /dev/null +++ b/tiger-compiler/src/ast/default-visitor.hh @@ -0,0 +1,151 @@ +/** + ** \file ast/default-visitor.hh + ** \brief Traverse an Abstract Syntax Tree (w/o objects), doing nothing. + */ + +#pragma once + +#include <ast/visitor.hh> + +namespace ast +{ + /** \brief Just visit the whole Ast tree (except object-related nodes). + + GenDefaultVisitor<CONSTNESS-SELECTOR> visits non-object-related + node of the the whole Ast tree, but does nothing else. + + Beware, as there are no implementations visiting object-oriented + constructs (classes, objects, methods), hence this class is + abstract. + + ast::GenDefaultVisitor inherits virtually from ast::GenVisitor + to allow diamond inheritance, e.g. so that a subclass of + ast::GenDefaultVisitor can also inherit missing object-related + implementations from another class (inheriting from + ast::GenVisitor). + + \see ast::NonObjectVisitor for more information. */ + template <template <typename> class Const> + class GenDefaultVisitor : public virtual GenVisitor<Const> + { + public: + /// Super class type. + using super_type = GenVisitor<Const>; + + // Import overloaded \c operator() methods. + using super_type::operator(); + + /// Convenient abbreviation. + template <typename Type> using const_t = typename Const<Type>::type; + + /** \name Ctor & dtor. + ** \{ */ + /// Construct a default visitor. + GenDefaultVisitor(); + /// Destroy a default visitor. + virtual ~GenDefaultVisitor(); + /** \} */ + + /* We cannot simply use `using super_type::operator()' here, + otherwise the linker would complain about missing symbols for + these methods: + + GenVisitor<id_traits>::operator()(ast::MethodDec&) + GenVisitor<id_traits>::operator()(ast::MethodCallExp&) + + This behavior seems to come from the mix between diamond + inheritance and templates. We redefine the following operator + (delegating to GenVisitor's operator()) as a workaround. */ + void operator()(const_t<Ast>& e) override; + + /** \name Visit Variable related nodes. + ** \{ */ + void operator()(const_t<SimpleVar>& e) override; + void operator()(const_t<FieldVar>& e) override; + void operator()(const_t<SubscriptVar>& e) override; + /** \} */ + + /** \name Visit Expression related nodes. + ** \{ */ + void operator()(const_t<NilExp>& e) override; + void operator()(const_t<IntExp>& e) override; + void operator()(const_t<StringExp>& e) override; + void operator()(const_t<CallExp>& e) override; + void operator()(const_t<OpExp>& e) override; + void operator()(const_t<RecordExp>& e) override; + void operator()(const_t<SeqExp>& e) override; + void operator()(const_t<AssignExp>& e) override; + void operator()(const_t<IfExp>& e) override; + void operator()(const_t<WhileExp>& e) override; + void operator()(const_t<ForExp>& e) override; + void operator()(const_t<BreakExp>&) override; + void operator()(const_t<LetExp>& e) override; + void operator()(const_t<ArrayExp>& e) override; + void operator()(const_t<CastExp>& e) override; + void operator()(const_t<FieldInit>& e) override; + /** \} */ + + /** \name Visit Declaration related nodes. + ** + ** Visiting declarations is simple, but there are many clauses. + ** This is because, in Tiger, the declarations are processed by + ** chunks (a chunk of Function declarations, then Var or Type, + ** then ...). + ** So we have to explain + ** \li How to visit a list of chunks; + ** \li how to visit chunks of function, var, or type declarations; + ** \li how to visit a single function, var, or type declaration. + ** \{ */ + + /// Visit a list of function, type and/or variables declarations. + void operator()(const_t<ChunkList>& e) override; + + /// Visit a ChunkInterface chunks. + virtual void operator()(const_t<ChunkInterface>& e); + + template <typename ChunkType> + /** \brief Visit a chunk (i.e., a list of Function, Var, and Type declarations). + ** + ** It is exactly the same in the three cases, so the code is + ** factored via a template method. */ + void chunk_visit(const_t<ChunkType>& e); + + /// Visit Var declarations. + void operator()(const_t<VarChunk>& e) override; + void operator()(const_t<VarDec>& e) override; + + /// Visit Function declarations. + void operator()(const_t<FunctionChunk>& e) override; + void operator()(const_t<FunctionDec>& e) override; + + /// Visit Type declarations. + void operator()(const_t<TypeChunk>& e) override; + void operator()(const_t<TypeDec>& e) override; + + /** \} */ + + /** \name Visit Type related nodes. + ** \{ */ + void operator()(const_t<NameTy>& e) override; + void operator()(const_t<RecordTy>& e) override; + void operator()(const_t<ArrayTy>& e) override; + /** \} */ + + /** \name Visit Field related nodes. */ + void operator()(const_t<Field>& e) override; + }; + + /// Shorthand for a const visitor. + using DefaultConstVisitor = GenDefaultVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using DefaultVisitor = GenDefaultVisitor<misc::id_traits>; + +#ifdef SWIG + /// Shorthand for a const visitor. + %template(DefaultConstVisitor) GenDefaultVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + %template(DefaultVisitor) GenDefaultVisitor<misc::id_traits>; +#endif +} // namespace ast + +#include <ast/default-visitor.hxx> diff --git a/tiger-compiler/src/ast/default-visitor.hxx b/tiger-compiler/src/ast/default-visitor.hxx new file mode 100644 index 0000000..3d49e83 --- /dev/null +++ b/tiger-compiler/src/ast/default-visitor.hxx @@ -0,0 +1,247 @@ +/** + ** \file ast/default-visitor.hxx + ** \brief Implementation for ast/default-visitor.hh. + */ + +#pragma once + +#include <ast/all.hh> +#include <ast/default-visitor.hh> +#include <misc/algorithm.hh> + +namespace ast +{ + template <template <typename> class Const> + GenDefaultVisitor<Const>::GenDefaultVisitor() + : GenVisitor<Const>() + {} + + template <template <typename> class Const> + GenDefaultVisitor<Const>::~GenDefaultVisitor() + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Ast>& e) + { + super_type::operator()(e); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<SimpleVar>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<FieldVar>& e) + { + // FIXME DONE: Some code was deleted here. + e.var_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<SubscriptVar>& e) + { + e.var_get().accept(*this); + e.index_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<NilExp>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<IntExp>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<StringExp>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<CallExp>& e) + { + // FIXME DONE: Some code was deleted here. + for (auto arg : e.args_get()) + arg->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<OpExp>& e) + { + e.left_get().accept(*this); + e.right_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<RecordExp>& e) + { + // FIXME DONE: Some code was deleted here. + e.type_name_get().accept(*this); + for (auto field : e.fields_get()) + field->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<SeqExp>& e) + { + // FIXME DONE: Some code was deleted here. + for (auto exp : e.exps_get()) + exp->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<AssignExp>& e) + { + // FIXME DONE: Some code was deleted here. + e.var_get().accept(*this); + e.exp_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<IfExp>& e) + { + // FIXME DONE: Some code was deleted here. + e.test_get().accept(*this); + e.thenclause_get().accept(*this); + e.elseclause_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<WhileExp>& e) + { + e.test_get().accept(*this); + e.body_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<ForExp>& e) + { + e.vardec_get().accept(*this); + e.hi_get().accept(*this); + e.body_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<BreakExp>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<LetExp>& e) + { + // FIXME DONE: Some code was deleted here. + e.chunks_get().accept(*this); + e.body_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<ArrayExp>& e) + { + // FIXME: Some code was deleted here. + e.type_name_get().accept(*this); + e.size_get().accept(*this); + e.init_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<CastExp>& e) + { + e.exp_get().accept(*this); + e.ty_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<FieldInit>& e) + { + e.init_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<ChunkList>& e) + { + // FIXME DONE: Some code was deleted here. + for (auto chunk : e.chunks_get()) + chunk->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<ChunkInterface>& e) + { + e.accept(*this); + } + + template <template <typename> class Const> + template <typename ChunkType> + inline void GenDefaultVisitor<Const>::chunk_visit(const_t<ChunkType>& e) + { + for (const auto dec : e) + dec->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<VarChunk>& e) + { + chunk_visit<VarChunk>(e); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<VarDec>& e) + { + // `type_name' might be omitted. + this->accept(e.type_name_get()); + // `init' can be null in case of formal parameter. + this->accept(e.init_get()); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<FunctionChunk>& e) + { + chunk_visit<FunctionChunk>(e); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<FunctionDec>& e) + { + // FIXME DONE: Some code was deleted here. + e.formals_get().accept(*this); + if (e.result_get() != nullptr) + e.result_get()->accept(*this); + if (e.body_get() != nullptr) + e.body_get()->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<TypeChunk>& e) + { + chunk_visit<TypeChunk>(e); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<TypeDec>& e) + { + e.ty_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<NameTy>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<RecordTy>& e) + { + // FIXME DONE: Some code was deleted here. + for (auto field : e.fields_get()) + field->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<ArrayTy>& e) + { + e.base_type_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Field>& e) + { + e.type_name_get().accept(*this); + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/dumper-dot.cc b/tiger-compiler/src/ast/dumper-dot.cc new file mode 100644 index 0000000..aef9568 --- /dev/null +++ b/tiger-compiler/src/ast/dumper-dot.cc @@ -0,0 +1,438 @@ +/** + ** \file ast/dumper-dot.cc + ** \brief Implementation of ast::DumperDot. + */ + +#include <ast/all.hh> +#include <ast/dumper-dot.hh> +#include <misc/indent.hh> +#include <misc/symbol.hh> +#include <type/pretty-printer.hh> + +namespace ast +{ + using namespace ast; + + void DumperDot::dump(const std::string& field, const ast::Ast& e) + { + const std::string* old_parent_field = parent_field; + parent_field = &field; + e.accept(*this); + parent_field = old_parent_field; + } + + void DumperDot::dump(const std::string& field, const ast::Ast* e) + { + if (!e) + return; + const std::string* old_parent_field = parent_field; + parent_field = &field; + e->accept(*this); + parent_field = old_parent_field; + } + + DumperDot::DumperDot(std::ostream& ostr) + : ostr_(ostr) + {} + + void DumperDot::dump_type(const ast::Typable& e) + { + // FIXME DONE: Some code was deleted here (Call node_html_field on a e.type_get() if exist). + if (e.type_get()) + node_html_field("typable", *e.type_get()); + } + + void DumperDot::operator()(const ArrayExp& e) + { + unsigned long old_parent_id = node_html_header(e, "ArrayExp"); + dump_type(e); + // FIXME DONE:Some code was deleted here (node_html_ports with properties). + node_html_ports({"type_name", "size", "init"}); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump). + dump("type_name", e.type_name_get()); + dump("size", e.size_get()); + dump("init", e.init_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const ArrayTy& e) + { + unsigned long old_parent_id = node_html_header(e, "ArrayTy"); + dump_type(e); + node_html_ports({"base_type"}); + footer_and_link(old_parent_id); + dump("base_type", e.base_type_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const AssignExp& e) + { + unsigned long old_parent_id = node_html_header(e, "AssignExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_ports with properties). + node_html_ports({"exp", "var"}); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump). + dump("base_type", e.exp_get()); + dump("var", e.var_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const BreakExp& e) + { + unsigned long old_parent_id = node_html_header(e, "BreakExp"); + dump_type(e); + node_html_ports({"def"}); + footer_and_link(old_parent_id); + dump_def(e); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const CallExp& e) + { + unsigned long old_parent_id = node_html_header(e, "CallExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_field). + node_html_field("name", e.name_get()); + node_html_ports(); + // FIXME DONE: Some code was deleted here (node_html_port_list for each list). + node_html_port_list("args",e.args_get(), true); + node_html_one_port("def"); + footer_and_link(old_parent_id); + dump_def(e); + // FIXME DONE: Some code was deleted here (dump_list). + dump_list("args",e.args_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const CastExp& e) + { + unsigned long old_parent_id = node_html_header(e, "CastExp"); + dump_type(e); + node_html_ports({"exp", "ty"}); + footer_and_link(old_parent_id); + dump("exp", e.exp_get()); + dump("ty", e.ty_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const ClassTy& e) + { + unsigned long old_parent_id = node_html_header(e, "ClassTy"); + dump_type(e); + node_html_ports({"super", "chunks"}); + footer_and_link(old_parent_id); + dump("super", e.super_get()); + dump("chunks", e.chunks_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const Field& e) + { + unsigned long old_parent_id = node_html_header(e, "Field"); + node_html_field("name", e.name_get()); + node_html_ports({"type_name"}); + footer_and_link(old_parent_id); + dump("type_name", e.type_name_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const FieldInit& e) + { + unsigned long old_parent_id = node_html_header(e, "FieldInit"); + node_html_field("name", e.name_get()); + node_html_ports({"init"}); + footer_and_link(old_parent_id); + dump("init", e.init_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const FieldVar& e) + { + unsigned long old_parent_id = node_html_header(e, "FieldVar"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_field). + node_html_field("name", e.name_get()); + // FIXME DONE: Some code was deleted here (node_html_ports with properties). + node_html_ports({"var"}); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump). + dump("var",e.var_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const ForExp& e) + { + unsigned long old_parent_id = node_html_header(e, "ForExp"); + dump_type(e); + node_html_ports({"vardec", "hi", "body"}); + footer_and_link(old_parent_id); + dump("vardec", e.vardec_get()); + dump("hi", e.hi_get()); + dump("body", e.body_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const FunctionDec& e) + { + unsigned long old_parent_id = node_html_header(e, "FunctionDec"); + dump_type(e); + node_html_field("name", e.name_get()); + node_html_ports({"formals", "result", "body"}); + footer_and_link(old_parent_id); + dump("formals", e.formals_get()); + dump("result", e.result_get()); + dump("body", e.body_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const IfExp& e) + { + unsigned long old_parent_id = node_html_header(e, "IfExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_ports with properties). + node_html_ports({"test","then_clause","else_clause"}); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump). + dump("test",e.test_get()); + dump("then_clause",e.thenclause_get()); + dump("else_clause", e.elseclause_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const IntExp& e) + { + unsigned long old_parent_id = node_html_header(e, "IntExp"); + dump_type(e); + node_html_field("value", e.value_get()); + footer_and_link(old_parent_id); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const LetExp& e) + { + unsigned long old_parent_id = node_html_header(e, "LetExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_ports with properties). + node_html_ports({"chunk","body"}); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump). + dump("chunk", e.chunks_get()); + dump("body",e.body_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const MethodCallExp& e) + { + unsigned long old_parent_id = node_html_header(e, "MethodCallExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_field). + node_html_field("name", e.name_get()); + // FIXME DONE: Some code was deleted here (node_html_ports with properties). + node_html_ports({"object"}); + // FIXME DONE: Some code was deleted here (node_html_port_list for each list). + node_html_port_list("args",e.args_get(), true); + node_html_one_port("def"); + footer_and_link(old_parent_id); + dump_def(e); + // FIXME DONE: Some code was deleted here (dump and dump_list). + dump("object",e.object_get()); + dump_list("args",e.args_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const MethodDec& e) + { + unsigned long old_parent_id = node_html_header(e, "MethodDec"); + dump_type(e); + node_html_field("name", e.name_get()); + node_html_ports({"formals", "result", "body"}); + footer_and_link(old_parent_id); + dump("formals", e.formals_get()); + dump("result", e.result_get()); + dump("body", e.body_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const NameTy& e) + { + unsigned long old_parent_id = node_html_header(e, "NameTy"); + dump_type(e); + node_html_field("name", e.name_get()); + node_html_ports({"def"}); + footer_and_link(old_parent_id); + dump_def(e); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const NilExp& e) + { + unsigned long old_parent_id = node_html_header(e, "NilExp"); + dump_type(e); + footer_and_link(old_parent_id); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const ObjectExp& e) + { + unsigned long old_parent_id = node_html_header(e, "ObjectExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_ports with properties). + node_html_ports({"type_name"}); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump). + dump("type_name", e.type_name_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const OpExp& e) + { + unsigned long old_parent_id = node_html_header(e, "OpExp"); + dump_type(e); + node_html_field("oper", str(e.oper_get()), "'"); + node_html_ports({"left", "right"}); + footer_and_link(old_parent_id); + dump("left", e.left_get()); + dump("right", e.right_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const RecordExp& e) + { + unsigned long old_parent_id = node_html_header(e, "RecordExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_ports with properties). + node_html_ports({"type_name"}); + // FIXME DONE: Some code was deleted here (node_html_port_list for each list). + node_html_port_list("fields",e.fields_get()); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump and dump_list). + dump("type_name",e.type_name_get()); + dump_list("fields",e.fields_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const RecordTy& e) + { + unsigned long old_parent_id = node_html_header(e, "RecordTy"); + dump_type(e); + node_html_ports(); + // FIXME DONE: Some code was deleted here (node_html_port_list for each list). + node_html_port_list("fields", e.fields_get()); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump_list). + dump_list("fields",e.fields_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const SeqExp& e) + { + unsigned long old_parent_id = node_html_header(e, "SeqExp"); + dump_type(e); + node_html_ports(); + // FIXME DONE: Some code was deleted here (node_html_port_list for each list). + node_html_port_list("exps", e.exps_get()); + footer_and_link(old_parent_id); + // FIXME DONE: Some code was deleted here (dump_list). + dump_list("fields",e.exps_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const SimpleVar& e) + { + unsigned long old_parent_id = node_html_header(e, "SimpleVar"); + dump_type(e); + node_html_field("name", e.name_get()); + node_html_ports({"def"}); + footer_and_link(old_parent_id); + dump_def(e); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const StringExp& e) + { + unsigned long old_parent_id = node_html_header(e, "StringExp"); + dump_type(e); + // FIXME DONE: Some code was deleted here (node_html_field, use misc::escape). + // WTF ?????? Ce code est completement fait au pif je vous le dis :) + node_html_field("",misc::escape(e.value_get())); + footer_and_link(old_parent_id); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const SubscriptVar& e) + { + unsigned long old_parent_id = node_html_header(e, "SubscriptVar"); + dump_type(e); + node_html_ports({"var", "index"}); + footer_and_link(old_parent_id); + dump("var", e.var_get()); + dump("index", e.index_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const TypeDec& e) + { + unsigned long old_parent_id = node_html_header(e, "TypeDec"); + dump_type(e); + node_html_field("name", e.name_get()); + node_html_ports({"ty"}); + footer_and_link(old_parent_id); + dump("ty", e.ty_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const VarDec& e) + { + unsigned long old_parent_id = node_html_header(e, "VarDec"); + dump_type(e); + node_html_field("name", e.name_get()); + node_html_ports({"type_name", "init"}); + footer_and_link(old_parent_id); + dump("type_name", e.type_name_get()); + dump("init", e.init_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const WhileExp& e) + { + unsigned long old_parent_id = node_html_header(e, "WhileExp"); + dump_type(e); + node_html_ports({"test", "body"}); + footer_and_link(old_parent_id); + dump("test", e.test_get()); + dump("body", e.body_get()); + parent_id = old_parent_id; + } + + void DumperDot::operator()(const ast::AssertExp&) + { + // FIXME: Some code was deleted here. + } + + void DumperDot::operator()(const ast::ChunkList& e) + { + dump_chunk<ast::ChunkList>(e, "ChunkList"); + } + + void DumperDot::operator()(const ast::FunctionChunk& e) + { + dump_chunk<ast::FunctionChunk>(e, "FunctionChunk"); + } + + void DumperDot::operator()(const ast::MethodChunk& e) + { + dump_chunk<ast::MethodChunk>(e, "MethodChunk"); + } + + void DumperDot::operator()(const ast::TypeChunk& e) + { + dump_chunk<ast::TypeChunk>(e, "TypeChunk"); + } + + void DumperDot::operator()(const ast::VarChunk& e) + { + dump_chunk<ast::VarChunk>(e, "VarChunk"); + } +} // namespace ast diff --git a/tiger-compiler/src/ast/dumper-dot.hh b/tiger-compiler/src/ast/dumper-dot.hh new file mode 100644 index 0000000..a297f30 --- /dev/null +++ b/tiger-compiler/src/ast/dumper-dot.hh @@ -0,0 +1,112 @@ + +/** + ** \file ast/dumper-dot.hh + ** \brief Declaration of ast::DumperDot. + */ + +#pragma once + +#include <ast/default-visitor.hh> +#include <misc/concepts.hh> +#include <misc/escape.hh> + +namespace ast +{ + /// \brief Dump an Ast into dot format. + class DumperDot : public ast::DefaultConstVisitor + { + public: + using super_type = ast::DefaultConstVisitor; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build a DumperDot. + DumperDot(std::ostream& ostr); + + /// Destroy a DumperDot. + ~DumperDot() override = default; + + // Visit methods. + public: + void operator()(const ast::ArrayExp&) override; + void operator()(const ast::ArrayTy&) override; + void operator()(const ast::AssignExp&) override; + void operator()(const ast::BreakExp&) override; + void operator()(const ast::CallExp&) override; + void operator()(const ast::CastExp&) override; + void operator()(const ast::ChunkList&) override; + void operator()(const ast::ClassTy&) override; + void operator()(const ast::Field&) override; + void operator()(const ast::FieldInit&) override; + void operator()(const ast::FieldVar&) override; + void operator()(const ast::ForExp&) override; + void operator()(const ast::FunctionDec&) override; + void operator()(const ast::IfExp&) override; + void operator()(const ast::IntExp&) override; + void operator()(const ast::LetExp&) override; + void operator()(const ast::MethodCallExp&) override; + void operator()(const ast::MethodDec&) override; + void operator()(const ast::NameTy&) override; + void operator()(const ast::NilExp&) override; + void operator()(const ast::ObjectExp&) override; + void operator()(const ast::OpExp&) override; + void operator()(const ast::RecordExp&) override; + void operator()(const ast::RecordTy&) override; + void operator()(const ast::SeqExp&) override; + void operator()(const ast::SimpleVar&) override; + void operator()(const ast::StringExp&) override; + void operator()(const ast::SubscriptVar&) override; + void operator()(const ast::TypeDec&) override; + void operator()(const ast::VarDec&) override; + void operator()(const ast::WhileExp&) override; + void operator()(const ast::FunctionChunk&) override; + void operator()(const ast::MethodChunk&) override; + void operator()(const ast::TypeChunk&) override; + void operator()(const ast::VarChunk&) override; + void operator()(const ast::AssertExp&) override; + + protected: + void dump(const std::string& field, const ast::Ast& t); + void dump(const std::string& field, const ast::Ast* t); + template <typename Container> + requires misc::ConstIterable<Container> + void dump_list(const std::string& field, const Container& l); + template <typename T> void dump_def(const T& e) const; + void dump_type(const ast::Typable& e); + template <typename E> void dump_chunk(const E& e, const std::string& name); + + void display_link(unsigned long old_parent_id) const; + void footer_and_link(unsigned long old_parent_id) const; + + template <typename T> + unsigned long node_html_header(const T& e, const std::string& type); + template <typename T> + void node_html_field(const std::string& name, + const T& content, + const std::string& sep = ""); + void node_html_one_port(const std::string& p); + void node_html_ports(const std::vector<std::string>& ports = {}); + template <typename T> + void node_html_port_list(const std::string& name, + const T& list, + bool chunk = false); + void node_html_footer() const; + + protected: + /// The stream to print on. + std::ostream& ostr_; + + /// The parent id + unsigned long parent_id = -1; + + /// Number of fields + unsigned long inner_fields = 0; + + /// The parent field + const std::string* parent_field = nullptr; + }; + +} // namespace ast + +#include <ast/dumper-dot.hxx> diff --git a/tiger-compiler/src/ast/dumper-dot.hxx b/tiger-compiler/src/ast/dumper-dot.hxx new file mode 100644 index 0000000..30a7b33 --- /dev/null +++ b/tiger-compiler/src/ast/dumper-dot.hxx @@ -0,0 +1,214 @@ +/** + ** \file ast/dumper-dot.hxx + ** \brief Implementation of ast::DumperDot. + */ + +#pragma once + +#include <cstdint> +#include <ast/dumper-dot.hh> +#include <misc/indent.hh> + +namespace ast +{ + + template <typename Container> + requires misc::ConstIterable<Container> + inline void DumperDot::dump_list(const std::string& field, const Container& l) + { + const std::string* old_parent_field = parent_field; + auto it = l.begin(); + unsigned n = 0; + while (it != l.end()) + { + std::ostringstream o; + o << field; + if (std::next(it) != l.end() || n > 0) + o << n++; + const std::string field_name = o.str(); + parent_field = &field_name; + (*it++)->accept(*this); + } + parent_field = old_parent_field; + } + + template <typename T> inline void DumperDot::dump_def(const T& e) const + { + const ast::Ast* d = nullptr; + // FIXME: Some code was deleted here (set d using definition of e). + (void)e; + if (!d) + return; + ostr_ << parent_id << ":def:s -> " << reinterpret_cast<std::uintptr_t>(d) + << ":nodename [constraint=false, style=dashed, color=\"dimgray\"]" + << misc::iendl; + } + + inline void DumperDot::display_link(unsigned long old_parent_id) const + { + if (parent_field) + ostr_ << old_parent_id << ":" << *parent_field << ":s" + << " -> " << parent_id << ":nodename:n" << misc::iendl; + } + + inline void DumperDot::footer_and_link(unsigned long old_parent_id) const + { + node_html_footer(); + display_link(old_parent_id); + } + + template <typename E> + inline void DumperDot::dump_chunk(const E& e, const std::string& name) + { + unsigned long old_parent_id = parent_id; + parent_id = reinterpret_cast<std::uintptr_t>(&e); + ostr_ << parent_id << " [label=<" << misc::incendl + << "<table cellborder='0' cellspacing='0'>" << misc::incendl << "<tr>" + << misc::incendl; + inner_fields = 0; + node_html_port_list(name, e, true); + ostr_ << misc::decendl << "</tr>" << misc::decendl << "</table>" + << misc::decendl << ">]" << misc::iendl; + display_link(old_parent_id); + dump_list("nodename", e); + parent_id = old_parent_id; + } + + namespace + { + inline void node_html_begin_inner(std::ostream& ostr, bool list = false) + { + ostr << "<td cellpadding='0'>" << misc::incendl + << "<table border='0' cellborder='" << (list ? 0 : 1) << "'" + << " cellspacing='0' cellpadding='" << (list ? 0 : 2) << "'>" + << misc::incendl << "<tr>" << misc::incendl; + } + + inline void node_html_end_inner(std::ostream& ostr) + { + ostr << misc::decendl << "</tr>" << misc::decendl << "</table>" + << misc::decendl << "</td>"; + } + + inline void node_html_separator(std::ostream& ostr) + { + node_html_end_inner(ostr); + ostr << misc::decendl << "</tr>" << misc::iendl << "<tr>" + << misc::incendl; + node_html_begin_inner(ostr); + } + inline void node_html_tr(std::ostream& ostr, + const std::string& port, + const std::string content) + { + ostr << "<td port='" << port << "'>" << content << "</td>"; + } + inline bool ends_with(const std::string& value, const std::string& ending) + { + if (ending.size() > value.size()) + return false; + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); + } + inline std::string node_html_color(const std::string& type) + { + if (ends_with(type, "Dec")) + return "red1"; + else if (ends_with(type, "Var")) + return "orange1"; + else if (ends_with(type, "Ty")) + return "green3"; + else if (ends_with(type, "Exp")) + return "blue2"; + return "black"; + } + + template <typename T> std::string html_escape(const T& input) + { + std::ostringstream i; + i << input; + const std::string& str = i.str(); + std::ostringstream o; + const std::string& specials = "&<>"; + for (const auto& p : str) + if (p == '\\') + o << '\\' << '\\'; + else if (specials.find(p) != std::string::npos) + o << "&#" << static_cast<int>(static_cast<unsigned char>(p)) << ";"; + else + o << p; + return o.str(); + } + } // namespace + + template <typename T> + inline unsigned long DumperDot::node_html_header(const T& e, + const std::string& type) + { + unsigned long old_parent_id = parent_id; + parent_id = reinterpret_cast<std::uintptr_t>(&e); + ostr_ << parent_id << " [label=<" << misc::incendl + << "<table border='0' cellborder='0' cellspacing='0' cellpadding='0'" + << " color='" << node_html_color(type) << "'>" << misc::incendl + << "<tr>" << misc::incendl; + node_html_begin_inner(ostr_); + node_html_tr(ostr_, "nodename", type); + node_html_separator(ostr_); + inner_fields = 0; + return old_parent_id; + } + template <typename T> + inline void DumperDot::node_html_field(const std::string& name, + const T& content, + const std::string& sep) + { + std::ostringstream o; + o << name << ": " << sep << html_escape(content) << sep; + if (inner_fields++) + ostr_ << misc::iendl; + node_html_tr(ostr_, name, o.str()); + } + inline void DumperDot::node_html_one_port(const std::string& p) + { + if (inner_fields++) + ostr_ << misc::iendl; + node_html_tr(ostr_, p, p); + } + inline void DumperDot::node_html_ports(const std::vector<std::string>& ports) + { + if (inner_fields) + node_html_separator(ostr_); + inner_fields = 0; + for (auto p : ports) + node_html_one_port(p); + } + template <typename T> + inline void DumperDot::node_html_port_list(const std::string& name, + const T& list, + bool chunk) + { + if (inner_fields++) + ostr_ << misc::iendl; + const std::string ref = chunk ? "nodename" : name; + node_html_begin_inner(ostr_, true); + long int size = std::distance(list.begin(), list.end()); + ostr_ << "<td port='" << ref << "' colspan='" << (size ? size : 1) << "'>" + << name << "</td>"; + if (size > 1) + { + ostr_ << misc::decendl << "</tr>" << misc::iendl << "<tr>" + << misc::incindent; + for (long int n = 0; n < size; n++) + ostr_ << misc::iendl << "<td port='" << ref << n << "'>" << n + << "</td>"; + } + node_html_end_inner(ostr_); + } + inline void DumperDot::node_html_footer() const + { + if (!inner_fields) + ostr_ << "<td></td>"; + node_html_end_inner(ostr_); + ostr_ << misc::decendl << "</tr>" << misc::decendl << "</table>" + << misc::decendl << ">]" << misc::iendl; + } +} // namespace ast diff --git a/tiger-compiler/src/ast/escapable.cc b/tiger-compiler/src/ast/escapable.cc new file mode 100644 index 0000000..9ca80e4 --- /dev/null +++ b/tiger-compiler/src/ast/escapable.cc @@ -0,0 +1,14 @@ +/**
+ ** \file ast/escapable.cc
+ ** \brief Implementation of ast::Escapable.
+ */
+
+#include <ast/escapable.hh>
+#include <ast/visitor.hh>
+
+namespace ast
+{
+ // FIXME DONE: Some code was deleted here.
+ bool Escapable::escape_get() const { return escaping_; }
+ void Escapable::escape_set(bool escaped) { escaping_ = escaped; }
+} // namespace ast
diff --git a/tiger-compiler/src/ast/escapable.hh b/tiger-compiler/src/ast/escapable.hh new file mode 100644 index 0000000..b062114 --- /dev/null +++ b/tiger-compiler/src/ast/escapable.hh @@ -0,0 +1,30 @@ +/**
+ ** \file ast/escapable.hh
+ ** \brief Declaration of ast::Escapable.
+ */
+
+#pragma once
+
+#include <ast/fwd.hh>
+
+namespace ast
+{
+ /// Escapable.
+ class Escapable
+ {
+ // FIXME DONE: Some code was deleted here.
+ public:
+ // Returns whether the object is escaped
+ bool escape_get() const;
+ // Sets the escaped status of the object
+ void escape_set(bool escaped);
+ // Return definition site.
+ FunctionDec* def_get() const;
+ // Set definition site.
+ void def_set(FunctionDec*);
+ private:
+ bool escaping_ = true;
+ FunctionDec* def_ = nullptr;
+ };
+} // namespace ast
+#include <ast/escapable.hxx>
diff --git a/tiger-compiler/src/ast/escapable.hxx b/tiger-compiler/src/ast/escapable.hxx new file mode 100644 index 0000000..00cb4bb --- /dev/null +++ b/tiger-compiler/src/ast/escapable.hxx @@ -0,0 +1,16 @@ +/**
+ ** \file ast/escapable.hxx
+ ** \brief Inline methods of ast::Escapable.
+ */
+
+#pragma once
+
+#include <ast/escapable.hh>
+
+namespace ast
+{
+ // FIXME DONE: Some code was deleted here.
+ inline FunctionDec* Escapable::def_get() const { return def_; }
+ inline void Escapable::def_set(FunctionDec* dec) { def_ = dec; }
+
+} // namespace ast
diff --git a/tiger-compiler/src/ast/exp.cc b/tiger-compiler/src/ast/exp.cc new file mode 100644 index 0000000..1c3ce32 --- /dev/null +++ b/tiger-compiler/src/ast/exp.cc @@ -0,0 +1,16 @@ +/** + ** \file ast/exp.cc + ** \brief Implementation of ast::Exp. + */ + +#include <ast/exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + Exp::Exp(const Location& location) + : Ast(location) + , Typable() + {} + +} // namespace ast diff --git a/tiger-compiler/src/ast/exp.hh b/tiger-compiler/src/ast/exp.hh new file mode 100644 index 0000000..a4202c1 --- /dev/null +++ b/tiger-compiler/src/ast/exp.hh @@ -0,0 +1,43 @@ +/** + ** \file ast/exp.hh + ** \brief Declaration of ast::Exp. + */ + +#pragma once + +#include <ast/ast.hh> +#include <ast/typable.hh> + +namespace ast +{ + /// Exp. + class Exp + : public Ast + , public Typable + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an Exp node. + explicit Exp(const Location& location); + Exp(const Exp&) = delete; + Exp& operator=(const Exp&) = delete; + /// Destroy an Exp node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override = 0; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override = 0; + /// \} + + /** \name Accessors. + ** \{ */ + /** \} */ + + protected: + }; +} // namespace ast +#include <ast/exp.hxx> diff --git a/tiger-compiler/src/ast/exp.hxx b/tiger-compiler/src/ast/exp.hxx new file mode 100644 index 0000000..49a8274 --- /dev/null +++ b/tiger-compiler/src/ast/exp.hxx @@ -0,0 +1,11 @@ +/** + ** \file ast/exp.hxx + ** \brief Inline methods of ast::Exp. + */ + +#pragma once + +#include <ast/exp.hh> + +namespace ast +{} // namespace ast diff --git a/tiger-compiler/src/ast/field-init.cc b/tiger-compiler/src/ast/field-init.cc new file mode 100644 index 0000000..54b5266 --- /dev/null +++ b/tiger-compiler/src/ast/field-init.cc @@ -0,0 +1,22 @@ +/** + ** \file ast/field-init.cc + ** \brief Implementation of ast::FieldInit. + */ + +#include <ast/field-init.hh> +#include <ast/visitor.hh> + +namespace ast +{ + FieldInit::FieldInit(const Location& location, misc::symbol name, Exp* init) + : Ast(location) + , name_(name) + , init_(init) + {} + + FieldInit::~FieldInit() { delete init_; } + + void FieldInit::accept(ConstVisitor& v) const { v(*this); } + + void FieldInit::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/field-init.hh b/tiger-compiler/src/ast/field-init.hh new file mode 100644 index 0000000..9ea18b2 --- /dev/null +++ b/tiger-compiler/src/ast/field-init.hh @@ -0,0 +1,55 @@ +/** + ** \file ast/field-init.hh + ** \brief Declaration of ast::FieldInit. + */ + +#pragma once + +#include <ast/ast.hh> +#include <ast/exp.hh> +#include <misc/symbol.hh> + +namespace ast +{ + /// FieldInit. + class FieldInit : public Ast + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a FieldInit node. + FieldInit(const Location& location, misc::symbol name, Exp* init); + FieldInit(const FieldInit&) = delete; + FieldInit& operator=(const FieldInit&) = delete; + /// Destroy a FieldInit node. + ~FieldInit() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return name of the field. + misc::symbol name_get() const; + /// Set name of the field. + void name_set(misc::symbol); + /// Return initial value of the field. + const Exp& init_get() const; + /// Return initial value of the field. + Exp& init_get(); + /** \} */ + + protected: + /// Name of the field. + misc::symbol name_; + /// Initial value of the field. + Exp* init_; + }; +} // namespace ast +#include <ast/field-init.hxx> diff --git a/tiger-compiler/src/ast/field-init.hxx b/tiger-compiler/src/ast/field-init.hxx new file mode 100644 index 0000000..1a895b7 --- /dev/null +++ b/tiger-compiler/src/ast/field-init.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/field-init.hxx + ** \brief Inline methods of ast::FieldInit. + */ + +#pragma once + +#include <ast/field-init.hh> + +namespace ast +{ + + inline misc::symbol FieldInit::name_get() const { return name_; } + inline void FieldInit::name_set(misc::symbol name) { name_ = name; } + + inline const Exp& FieldInit::init_get() const { return *init_; } + inline Exp& FieldInit::init_get() { return *init_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/field-var.cc b/tiger-compiler/src/ast/field-var.cc new file mode 100644 index 0000000..7423fc1 --- /dev/null +++ b/tiger-compiler/src/ast/field-var.cc @@ -0,0 +1,22 @@ +/** + ** \file ast/field-var.cc + ** \brief Implementation of ast::FieldVar. + */ + +#include <ast/field-var.hh> +#include <ast/visitor.hh> + +namespace ast +{ + FieldVar::FieldVar(const Location& location, Var* var, misc::symbol name) + : Var(location) + , var_(var) + , name_(name) + {} + + FieldVar::~FieldVar() { delete var_; } + + void FieldVar::accept(ConstVisitor& v) const { v(*this); } + + void FieldVar::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/field-var.hh b/tiger-compiler/src/ast/field-var.hh new file mode 100644 index 0000000..06c0b9a --- /dev/null +++ b/tiger-compiler/src/ast/field-var.hh @@ -0,0 +1,60 @@ +/** + ** \file ast/field-var.hh + ** \brief Declaration of ast::FieldVar. + */ + +#pragma once + +#include <ast/var.hh> +#include <misc/symbol.hh> + +namespace ast +{ + /// FieldVar. + class FieldVar : public Var + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a FieldVar node. + FieldVar(const Location& location, Var* var, misc::symbol name); + FieldVar(const FieldVar&) = delete; + FieldVar& operator=(const FieldVar&) = delete; + /// Destroy a FieldVar node. + ~FieldVar() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the record that holds the field. + const Var& var_get() const; + /// Return the record that holds the field. + Var& var_get(); + /// Return the field's name. + misc::symbol name_get() const; + /// Set the field's name. + void name_set(misc::symbol); + /// Return handle the number of the field in the record that holds it. + int index_get() const; + /// Set handle the number of the field in the record that holds it. + void index_set(int); + /** \} */ + + protected: + /// The record that holds the field. + Var* var_; + /// The field's name. + misc::symbol name_; + /// Handle the number of the field in the record that holds it. + int index_ = -1; + }; +} // namespace ast +#include <ast/field-var.hxx> diff --git a/tiger-compiler/src/ast/field-var.hxx b/tiger-compiler/src/ast/field-var.hxx new file mode 100644 index 0000000..cfadc89 --- /dev/null +++ b/tiger-compiler/src/ast/field-var.hxx @@ -0,0 +1,22 @@ +/** + ** \file ast/field-var.hxx + ** \brief Inline methods of ast::FieldVar. + */ + +#pragma once + +#include <ast/field-var.hh> + +namespace ast +{ + + inline const Var& FieldVar::var_get() const { return *var_; } + inline Var& FieldVar::var_get() { return *var_; } + + inline misc::symbol FieldVar::name_get() const { return name_; } + inline void FieldVar::name_set(misc::symbol name) { name_ = name; } + + inline int FieldVar::index_get() const { return index_; } + inline void FieldVar::index_set(int index) { index_ = index; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/field.cc b/tiger-compiler/src/ast/field.cc new file mode 100644 index 0000000..7265f24 --- /dev/null +++ b/tiger-compiler/src/ast/field.cc @@ -0,0 +1,22 @@ +/** + ** \file ast/field.cc + ** \brief Implementation of ast::Field. + */ + +#include <ast/field.hh> +#include <ast/visitor.hh> + +namespace ast +{ + Field::Field(const Location& location, misc::symbol name, NameTy* type_name) + : Ast(location) + , name_(name) + , type_name_(type_name) + {} + + Field::~Field() { delete type_name_; } + + void Field::accept(ConstVisitor& v) const { v(*this); } + + void Field::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/field.hh b/tiger-compiler/src/ast/field.hh new file mode 100644 index 0000000..cbb6352 --- /dev/null +++ b/tiger-compiler/src/ast/field.hh @@ -0,0 +1,55 @@ +/** + ** \file ast/field.hh + ** \brief Declaration of ast::Field. + */ + +#pragma once + +#include <ast/ast.hh> +#include <ast/name-ty.hh> +#include <misc/symbol.hh> + +namespace ast +{ + /// Field. + class Field : public Ast + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a Field node. + Field(const Location& location, misc::symbol name, NameTy* type_name); + Field(const Field&) = delete; + Field& operator=(const Field&) = delete; + /// Destroy a Field node. + ~Field() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the field name. + misc::symbol name_get() const; + /// Set the field name. + void name_set(misc::symbol); + /// Return the field type name. + const NameTy& type_name_get() const; + /// Return the field type name. + NameTy& type_name_get(); + /** \} */ + + protected: + /// The field name. + misc::symbol name_; + /// The field type name. + NameTy* type_name_; + }; +} // namespace ast +#include <ast/field.hxx> diff --git a/tiger-compiler/src/ast/field.hxx b/tiger-compiler/src/ast/field.hxx new file mode 100644 index 0000000..0c7658c --- /dev/null +++ b/tiger-compiler/src/ast/field.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/field.hxx + ** \brief Inline methods of ast::Field. + */ + +#pragma once + +#include <ast/field.hh> + +namespace ast +{ + + inline misc::symbol Field::name_get() const { return name_; } + inline void Field::name_set(misc::symbol name) { name_ = name; } + + inline const NameTy& Field::type_name_get() const { return *type_name_; } + inline NameTy& Field::type_name_get() { return *type_name_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/for-exp.cc b/tiger-compiler/src/ast/for-exp.cc new file mode 100644 index 0000000..2ed51e1 --- /dev/null +++ b/tiger-compiler/src/ast/for-exp.cc @@ -0,0 +1,28 @@ +/** + ** \file ast/for-exp.cc + ** \brief Implementation of ast::ForExp. + */ + +#include <ast/for-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + ForExp::ForExp(const Location& location, VarDec* vardec, Exp* hi, Exp* body) + : Exp(location) + , vardec_(vardec) + , hi_(hi) + , body_(body) + {} + + ForExp::~ForExp() + { + delete vardec_; + delete hi_; + delete body_; + } + + void ForExp::accept(ConstVisitor& v) const { v(*this); } + + void ForExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/for-exp.hh b/tiger-compiler/src/ast/for-exp.hh new file mode 100644 index 0000000..b06a9be --- /dev/null +++ b/tiger-compiler/src/ast/for-exp.hh @@ -0,0 +1,60 @@ +/** + ** \file ast/for-exp.hh + ** \brief Declaration of ast::ForExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/var-dec.hh> + +namespace ast +{ + /// ForExp. + class ForExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a ForExp node. + ForExp(const Location& location, VarDec* vardec, Exp* hi, Exp* body); + ForExp(const ForExp&) = delete; + ForExp& operator=(const ForExp&) = delete; + /// Destroy a ForExp node. + ~ForExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return implicit variable declaration. + const VarDec& vardec_get() const; + /// Return implicit variable declaration. + VarDec& vardec_get(); + /// Return high bound of the loop. + const Exp& hi_get() const; + /// Return high bound of the loop. + Exp& hi_get(); + /// Return instructions executed in the loop. + const Exp& body_get() const; + /// Return instructions executed in the loop. + Exp& body_get(); + /** \} */ + + protected: + /// Implicit variable declaration. + VarDec* vardec_; + /// High bound of the loop. + Exp* hi_; + /// Instructions executed in the loop. + Exp* body_; + }; +} // namespace ast +#include <ast/for-exp.hxx> diff --git a/tiger-compiler/src/ast/for-exp.hxx b/tiger-compiler/src/ast/for-exp.hxx new file mode 100644 index 0000000..c81d30f --- /dev/null +++ b/tiger-compiler/src/ast/for-exp.hxx @@ -0,0 +1,22 @@ +/** + ** \file ast/for-exp.hxx + ** \brief Inline methods of ast::ForExp. + */ + +#pragma once + +#include <ast/for-exp.hh> + +namespace ast +{ + + inline const VarDec& ForExp::vardec_get() const { return *vardec_; } + inline VarDec& ForExp::vardec_get() { return *vardec_; } + + inline const Exp& ForExp::hi_get() const { return *hi_; } + inline Exp& ForExp::hi_get() { return *hi_; } + + inline const Exp& ForExp::body_get() const { return *body_; } + inline Exp& ForExp::body_get() { return *body_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/function-dec.cc b/tiger-compiler/src/ast/function-dec.cc new file mode 100644 index 0000000..c45ca0d --- /dev/null +++ b/tiger-compiler/src/ast/function-dec.cc @@ -0,0 +1,33 @@ +/** + ** \file ast/function-dec.cc + ** \brief Implementation of ast::FunctionDec. + */ + +#include <ast/function-dec.hh> +#include <ast/visitor.hh> + +namespace ast +{ + FunctionDec::FunctionDec(const Location& location, + misc::symbol name, + VarChunk* formals, + NameTy* result, + Exp* body) + : Dec(location, name) + , TypeConstructor() + , formals_(formals) + , result_(result) + , body_(body) + {} + + FunctionDec::~FunctionDec() + { + delete formals_; + delete result_; + delete body_; + } + + void FunctionDec::accept(ConstVisitor& v) const { v(*this); } + + void FunctionDec::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/function-dec.hh b/tiger-compiler/src/ast/function-dec.hh new file mode 100644 index 0000000..0a94e95 --- /dev/null +++ b/tiger-compiler/src/ast/function-dec.hh @@ -0,0 +1,72 @@ +/** + ** \file ast/function-dec.hh + ** \brief Declaration of ast::FunctionDec. + */ + +#pragma once + +#include <ast/chunk.hh> +#include <ast/dec.hh> +#include <ast/exp.hh> +#include <ast/name-ty.hh> +#include <ast/type-constructor.hh> +#include <ast/var-dec.hh> + +namespace ast +{ + /// FunctionDec. + class FunctionDec + : public Dec + , public TypeConstructor + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a FunctionDec node. + FunctionDec(const Location& location, + misc::symbol name, + VarChunk* formals, + NameTy* result, + Exp* body); + FunctionDec(const FunctionDec&) = delete; + FunctionDec& operator=(const FunctionDec&) = delete; + /// Destroy a FunctionDec node. + ~FunctionDec() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return formal arguments. + const VarChunk& formals_get() const; + /// Return formal arguments. + VarChunk& formals_get(); + /// Return result type. + const NameTy* result_get() const; + /// Return result type. + NameTy* result_get(); + /// Return instructions. + const Exp* body_get() const; + /// Return instructions. + Exp* body_get(); + /// Set instructions. + void body_set(Exp*); + /** \} */ + + protected: + /// Formal arguments. + VarChunk* formals_; + /// Result type. + NameTy* result_; + /// Instructions. + Exp* body_; + }; +} // namespace ast +#include <ast/function-dec.hxx> diff --git a/tiger-compiler/src/ast/function-dec.hxx b/tiger-compiler/src/ast/function-dec.hxx new file mode 100644 index 0000000..b9dda47 --- /dev/null +++ b/tiger-compiler/src/ast/function-dec.hxx @@ -0,0 +1,23 @@ +/** + ** \file ast/function-dec.hxx + ** \brief Inline methods of ast::FunctionDec. + */ + +#pragma once + +#include <ast/function-dec.hh> + +namespace ast +{ + + inline const VarChunk& FunctionDec::formals_get() const { return *formals_; } + inline VarChunk& FunctionDec::formals_get() { return *formals_; } + + inline const NameTy* FunctionDec::result_get() const { return result_; } + inline NameTy* FunctionDec::result_get() { return result_; } + + inline const Exp* FunctionDec::body_get() const { return body_; } + inline Exp* FunctionDec::body_get() { return body_; } + inline void FunctionDec::body_set(Exp* body) { body_ = body; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/fwd.hh b/tiger-compiler/src/ast/fwd.hh new file mode 100644 index 0000000..3d2a611 --- /dev/null +++ b/tiger-compiler/src/ast/fwd.hh @@ -0,0 +1,80 @@ +/** + ** \file ast/fwd.hh + ** \brief Forward declarations of all AST classes + ** (needed by the visitors). + */ + +#pragma once + +#include <list> +#include <vector> +#include <misc/fwd.hh> +#include <misc/vector.hh> + +namespace ast +{ + class ArrayExp; + class ArrayTy; + class AssertExp; + class AssignExp; + class Ast; + class BreakExp; + class CallExp; + class CastExp; + class ChunkList; + class ClassTy; + class Dec; + class Escapable; + class Exp; + class Field; + class FieldInit; + class FieldVar; + class ForExp; + class FunctionDec; + class IfExp; + class IntExp; + class LetExp; + class MethodCallExp; + class MethodDec; + class NameTy; + class NilExp; + class ObjectExp; + class OpExp; + class RecordExp; + class RecordTy; + class SeqExp; + class SimpleVar; + class StringExp; + class SubscriptVar; + class Ty; + class Typable; + class TypeConstructor; + class TypeDec; + class Var; + class VarDec; + class WhileExp; + + // From visitor.hh + template <template <typename> class Const> class GenVisitor; + using ConstVisitor = GenVisitor<misc::constify_traits>; + using Visitor = GenVisitor<misc::id_traits>; + + // Collections of nodes. + using exps_type = std::vector<Exp*>; + using fieldinits_type = std::vector<FieldInit*>; + using fields_type = std::vector<Field*>; + + // From chunk-interface.hh. + class ChunkInterface; + + // From chunk-list.hh. + class ChunkList; + + // From chunk.hh. + template <typename T> class Chunk; + using FunctionChunk = Chunk<FunctionDec>; + using MethodChunk = Chunk<MethodDec>; + using TypeChunk = Chunk<TypeDec>; + using VarChunk = Chunk<VarDec>; + +} // namespace ast diff --git a/tiger-compiler/src/ast/if-exp.cc b/tiger-compiler/src/ast/if-exp.cc new file mode 100644 index 0000000..ae1911f --- /dev/null +++ b/tiger-compiler/src/ast/if-exp.cc @@ -0,0 +1,31 @@ +/** + ** \file ast/if-exp.cc + ** \brief Implementation of ast::IfExp. + */ + +#include <ast/if-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + IfExp::IfExp(const Location& location, + Exp* test, + Exp* thenclause, + Exp* elseclause) + : Exp(location) + , test_(test) + , thenclause_(thenclause) + , elseclause_(elseclause) + {} + + IfExp::~IfExp() + { + delete test_; + delete thenclause_; + delete elseclause_; + } + + void IfExp::accept(ConstVisitor& v) const { v(*this); } + + void IfExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/if-exp.hh b/tiger-compiler/src/ast/if-exp.hh new file mode 100644 index 0000000..6b27452 --- /dev/null +++ b/tiger-compiler/src/ast/if-exp.hh @@ -0,0 +1,71 @@ +/** + ** \file ast/if-exp.hh + ** \brief Declaration of ast::IfExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/seq-exp.hh> + +namespace ast +{ + /// IfExp. + class IfExp : public Exp + { + public: + IfExp(const Location& location, Exp* test, Exp* thenclause) + : Exp(location) + , test_(test) + , thenclause_(thenclause) + , elseclause_(new SeqExp(location, new exps_type())) + {} + + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an IfExp node. + IfExp(const Location& location, + Exp* test, + Exp* thenclause, + Exp* elseclause); + IfExp(const IfExp&) = delete; + IfExp& operator=(const IfExp&) = delete; + /// Destroy an IfExp node. + ~IfExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return condition. + const Exp& test_get() const; + /// Return condition. + Exp& test_get(); + /// Return instructions executed if condition is true. + const Exp& thenclause_get() const; + /// Return instructions executed if condition is true. + Exp& thenclause_get(); + /// Return instructions executed if condition is false. + const Exp& elseclause_get() const; + /// Return instructions executed if condition is false. + Exp& elseclause_get(); + /** \} */ + + protected: + /// Condition. + Exp* test_; + /// Instructions executed if condition is true. + Exp* thenclause_; + /// Instructions executed if condition is false. + Exp* elseclause_; + }; +} // namespace ast +#include <ast/if-exp.hxx> diff --git a/tiger-compiler/src/ast/if-exp.hxx b/tiger-compiler/src/ast/if-exp.hxx new file mode 100644 index 0000000..7b6cb07 --- /dev/null +++ b/tiger-compiler/src/ast/if-exp.hxx @@ -0,0 +1,22 @@ +/** + ** \file ast/if-exp.hxx + ** \brief Inline methods of ast::IfExp. + */ + +#pragma once + +#include <ast/if-exp.hh> + +namespace ast +{ + + inline const Exp& IfExp::test_get() const { return *test_; } + inline Exp& IfExp::test_get() { return *test_; } + + inline const Exp& IfExp::thenclause_get() const { return *thenclause_; } + inline Exp& IfExp::thenclause_get() { return *thenclause_; } + + inline const Exp& IfExp::elseclause_get() const { return *elseclause_; } + inline Exp& IfExp::elseclause_get() { return *elseclause_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/int-exp.cc b/tiger-compiler/src/ast/int-exp.cc new file mode 100644 index 0000000..e2fb992 --- /dev/null +++ b/tiger-compiler/src/ast/int-exp.cc @@ -0,0 +1,19 @@ +/** + ** \file ast/int-exp.cc + ** \brief Implementation of ast::IntExp. + */ + +#include <ast/int-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + IntExp::IntExp(const Location& location, int value) + : Exp(location) + , value_(value) + {} + + void IntExp::accept(ConstVisitor& v) const { v(*this); } + + void IntExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/int-exp.hh b/tiger-compiler/src/ast/int-exp.hh new file mode 100644 index 0000000..a15bdf0 --- /dev/null +++ b/tiger-compiler/src/ast/int-exp.hh @@ -0,0 +1,44 @@ +/** + ** \file ast/int-exp.hh + ** \brief Declaration of ast::IntExp. + */ + +#pragma once + +#include <ast/exp.hh> + +namespace ast +{ + /// IntExp. + class IntExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an IntExp node. + IntExp(const Location& location, int value); + IntExp(const IntExp&) = delete; + IntExp& operator=(const IntExp&) = delete; + /// Destroy an IntExp node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return stored integer value. + int value_get() const; + /** \} */ + + protected: + /// Stored integer value. + int value_; + }; +} // namespace ast +#include <ast/int-exp.hxx> diff --git a/tiger-compiler/src/ast/int-exp.hxx b/tiger-compiler/src/ast/int-exp.hxx new file mode 100644 index 0000000..0308ba7 --- /dev/null +++ b/tiger-compiler/src/ast/int-exp.hxx @@ -0,0 +1,15 @@ +/** + ** \file ast/int-exp.hxx + ** \brief Inline methods of ast::IntExp. + */ + +#pragma once + +#include <ast/int-exp.hh> + +namespace ast +{ + + inline int IntExp::value_get() const { return value_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/let-exp.cc b/tiger-compiler/src/ast/let-exp.cc new file mode 100644 index 0000000..0ded46b --- /dev/null +++ b/tiger-compiler/src/ast/let-exp.cc @@ -0,0 +1,26 @@ +/** + ** \file ast/let-exp.cc + ** \brief Implementation of ast::LetExp. + */ + +#include <ast/let-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + LetExp::LetExp(const Location& location, ChunkList* chunks, Exp* body) + : Exp(location) + , chunks_(chunks) + , body_(body) + {} + + LetExp::~LetExp() + { + delete chunks_; + delete body_; + } + + void LetExp::accept(ConstVisitor& v) const { v(*this); } + + void LetExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/let-exp.hh b/tiger-compiler/src/ast/let-exp.hh new file mode 100644 index 0000000..7955eeb --- /dev/null +++ b/tiger-compiler/src/ast/let-exp.hh @@ -0,0 +1,55 @@ +/** + ** \file ast/let-exp.hh + ** \brief Declaration of ast::LetExp. + */ + +#pragma once + +#include <ast/chunk-list.hh> +#include <ast/exp.hh> +#include <misc/contract.hh> + +namespace ast +{ + /// LetExp. + class LetExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a LetExp node. + LetExp(const Location& location, ChunkList* chunks, Exp* body); + LetExp(const LetExp&) = delete; + LetExp& operator=(const LetExp&) = delete; + /// Destroy a LetExp node. + ~LetExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return list of declarations. + const ChunkList& chunks_get() const; + /// Return list of declarations. + ChunkList& chunks_get(); + /// Return list of instructions. + const Exp& body_get() const; + /// Return list of instructions. + Exp& body_get(); + /** \} */ + + protected: + /// List of declarations. + ChunkList* chunks_; + /// List of instructions. + Exp* body_; + }; +} // namespace ast +#include <ast/let-exp.hxx> diff --git a/tiger-compiler/src/ast/let-exp.hxx b/tiger-compiler/src/ast/let-exp.hxx new file mode 100644 index 0000000..9d6589c --- /dev/null +++ b/tiger-compiler/src/ast/let-exp.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/let-exp.hxx + ** \brief Inline methods of ast::LetExp. + */ + +#pragma once + +#include <ast/let-exp.hh> + +namespace ast +{ + + inline const ChunkList& LetExp::chunks_get() const { return *chunks_; } + inline ChunkList& LetExp::chunks_get() { return *chunks_; } + + inline const Exp& LetExp::body_get() const { return *body_; } + inline Exp& LetExp::body_get() { return *body_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/libast.cc b/tiger-compiler/src/ast/libast.cc new file mode 100644 index 0000000..0bebaa0 --- /dev/null +++ b/tiger-compiler/src/ast/libast.cc @@ -0,0 +1,44 @@ +/** + ** \file ast/libast.cc + ** \brief Public ast interface implementation. + */ + +#include <fstream> + +#include <ast/dumper-dot.hh> +#include <ast/libast.hh> +#include <ast/pretty-printer.hh> + +// Define exported ast functions. +namespace ast +{ + // Making the following variables const is more than merely + // stylistic. If they were not, Swig will create set/get for them, + // and there is no set (operator=), since it has a const member. + + /// xalloc slot to enable escapes display in Ast display. + const misc::xalloc<bool> escapes_display; + /// xalloc slot to enable bindings display in Ast display. + const misc::xalloc<bool> bindings_display; + + // Print the TREE on OSTR. + std::ostream& operator<<(std::ostream& ostr, const Ast& tree) + { + PrettyPrinter print(ostr); + print(tree); + return ostr; + } + + /// Dump \a a on \a ostr. + std::ostream& dump_dot(const Ast& tree, std::ostream& ostr) + { + DumperDot dump_dot(ostr); + ostr << misc::resetindent << "digraph structs {" << misc::incendl; + ostr << "splines=line;" << misc::iendl; + ostr << "node [shape=plaintext]" << misc::iendl; + dump_dot(tree); + ostr << misc::decendl << "}" << misc::iendl; + return ostr; + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/libast.hh b/tiger-compiler/src/ast/libast.hh new file mode 100644 index 0000000..121749d --- /dev/null +++ b/tiger-compiler/src/ast/libast.hh @@ -0,0 +1,26 @@ +/** + ** \file ast/libast.hh + ** \brief Public ast interface declaration. + */ + +#pragma once + +#include <iosfwd> + +#include <misc/xalloc.hh> + +#include <ast/fwd.hh> + +/// Ast management. +namespace ast +{ + extern const misc::xalloc<bool> escapes_display; + extern const misc::xalloc<bool> bindings_display; + + /// Output \a a on \a ostr. + std::ostream& operator<<(std::ostream& ostr, const Ast& tree); + + /// Dump \a a on \a ostr. + std::ostream& dump_dot(const Ast& tree, std::ostream& ostr); + +} // namespace ast diff --git a/tiger-compiler/src/ast/local.am b/tiger-compiler/src/ast/local.am new file mode 100644 index 0000000..1e520c5 --- /dev/null +++ b/tiger-compiler/src/ast/local.am @@ -0,0 +1,30 @@ +ast_basedir = src/ast/ +ast_srcdir = $(srcdir)/%D% +# Don't use ast_srcdir in the include, otherwise Automake can't resolve it. +include $(srcdir)/%D%/ast-nodes.mk + + +src_libtc_la_SOURCES += \ + %D%/location.hh \ + %D%/all.hh \ + %D%/chunk-interface.hh %D%/chunk-interface.hxx \ + %D%/chunk.hh %D%/chunk.hxx \ + %D%/fwd.hh \ + %D%/visitor.hh \ + $(AST_NODES) \ + %D%/default-visitor.hh %D%/default-visitor.hxx \ + %D%/dumper-dot.hh %D%/dumper-dot.hxx %D%/dumper-dot.cc \ + %D%/non-object-visitor.hh %D%/non-object-visitor.hxx \ + %D%/non-assert-visitor.hh %D%/non-assert-visitor.hxx \ + %D%/object-visitor.hh %D%/object-visitor.hxx \ + %D%/assert-visitor.hh %D%/assert-visitor.hxx \ + %D%/pretty-printer.hh %D%/pretty-printer.cc \ + %D%/visitor.hxx \ + %D%/libast.hh %D%/libast.cc + +dist_noinst_DATA += %D%/README.txt + +check_PROGRAMS += %D%/test-ast +%C%_test_ast_LDADD = src/libtc.la + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/ast/location.hh b/tiger-compiler/src/ast/location.hh new file mode 100644 index 0000000..9e6e398 --- /dev/null +++ b/tiger-compiler/src/ast/location.hh @@ -0,0 +1,14 @@ +/** + ** \file ast/location.hh + ** \brief Definition of ast::Location. + */ + +#pragma once + +#include <misc/symbol.hh> +#include <parse/location.hh> + +namespace ast +{ + using Location = parse::location; +} diff --git a/tiger-compiler/src/ast/method-call-exp.cc b/tiger-compiler/src/ast/method-call-exp.cc new file mode 100644 index 0000000..bdf1f41 --- /dev/null +++ b/tiger-compiler/src/ast/method-call-exp.cc @@ -0,0 +1,24 @@ +/** + ** \file ast/method-call-exp.cc + ** \brief Implementation of ast::MethodCallExp. + */ + +#include <ast/method-call-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + MethodCallExp::MethodCallExp(const Location& location, + misc::symbol name, + exps_type* args, + Var* object) + : CallExp(location, name, args) + , object_(object) + {} + + MethodCallExp::~MethodCallExp() { delete object_; } + + void MethodCallExp::accept(ConstVisitor& v) const { v(*this); } + + void MethodCallExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/method-call-exp.hh b/tiger-compiler/src/ast/method-call-exp.hh new file mode 100644 index 0000000..ef728d9 --- /dev/null +++ b/tiger-compiler/src/ast/method-call-exp.hh @@ -0,0 +1,70 @@ +/** + ** \file ast/method-call-exp.hh + ** \brief Declaration of ast::MethodCallExp. + */ + +#pragma once + +#include <ast/call-exp.hh> +#include <ast/method-dec.hh> +#include <ast/var.hh> + +namespace ast +{ + /** \class ast::MethodCallExp + ** \brief Method call. + ** + ** A method call is \em not a function call in the strict sense + ** of object-oriented programming. Inheritance is used as a + ** factoring tool here. + */ + + class MethodCallExp : public CallExp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a MethodCallExp node. + MethodCallExp(const Location& location, + misc::symbol name, + exps_type* args, + Var* object); + MethodCallExp(const MethodCallExp&) = delete; + MethodCallExp& operator=(const MethodCallExp&) = delete; + /// Destroy a MethodCallExp node. + ~MethodCallExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the object on which the method is called. + const Var& object_get() const; + /// Return the object on which the method is called. + Var& object_get(); + // FIXME DONE: Some code was deleted here. + /// Return definition site. + const MethodDec* def_get() const; + MethodDec* def_get(); + + // FIXME DONE: Some code was deleted here. + /// Set definition site. + void def_set(MethodDec*); + + /** \} */ + + protected: + /// The object on which the method is called. + Var* object_; + /// Definition site. + MethodDec* def_ = nullptr; + }; +} // namespace ast +#include <ast/method-call-exp.hxx> diff --git a/tiger-compiler/src/ast/method-call-exp.hxx b/tiger-compiler/src/ast/method-call-exp.hxx new file mode 100644 index 0000000..fa49ca9 --- /dev/null +++ b/tiger-compiler/src/ast/method-call-exp.hxx @@ -0,0 +1,21 @@ +/** + ** \file ast/method-call-exp.hxx + ** \brief Inline methods of ast::MethodCallExp. + */ + +#pragma once + +#include <ast/method-call-exp.hh> + +namespace ast +{ + + inline const Var& MethodCallExp::object_get() const { return *object_; } + inline Var& MethodCallExp::object_get() { return *object_; } + + // FIXME DONE: Some code was deleted here. + inline const MethodDec* MethodCallExp::def_get() const { return def_; } + inline MethodDec* MethodCallExp::def_get() { return def_; } + // FIXME DONE: Some code was deleted here. + inline void MethodCallExp::def_set(MethodDec* def) { def_ = def; } +} // namespace ast diff --git a/tiger-compiler/src/ast/method-dec.cc b/tiger-compiler/src/ast/method-dec.cc new file mode 100644 index 0000000..36f07d7 --- /dev/null +++ b/tiger-compiler/src/ast/method-dec.cc @@ -0,0 +1,22 @@ +/** + ** \file ast/method-dec.cc + ** \brief Implementation of ast::MethodDec. + */ + +#include <ast/method-dec.hh> +#include <ast/visitor.hh> + +namespace ast +{ + MethodDec::MethodDec(const Location& location, + misc::symbol name, + VarChunk* formals, + NameTy* result, + Exp* body) + : FunctionDec(location, name, formals, result, body) + {} + + void MethodDec::accept(ConstVisitor& v) const { v(*this); } + + void MethodDec::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/method-dec.hh b/tiger-compiler/src/ast/method-dec.hh new file mode 100644 index 0000000..d91cfd8 --- /dev/null +++ b/tiger-compiler/src/ast/method-dec.hh @@ -0,0 +1,45 @@ +/** + ** \file ast/method-dec.hh + ** \brief Declaration of ast::MethodDec. + */ + +#pragma once + +#include <ast/function-dec.hh> + +namespace ast +{ + /** \class ast::MethodDec + ** \brief Method declaration. + ** + ** A method declaration is \em not a function in the strict + ** sense of object-oriented programming. Inheritance is used + ** as a factoring tool here. + */ + + class MethodDec : public FunctionDec + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a MethodDec node. + MethodDec(const Location& location, + misc::symbol name, + VarChunk* formals, + NameTy* result, + Exp* body); + MethodDec(const MethodDec&) = delete; + MethodDec& operator=(const MethodDec&) = delete; + /// Destroy a MethodDec node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + }; +} // namespace ast +#include <ast/method-dec.hxx> diff --git a/tiger-compiler/src/ast/method-dec.hxx b/tiger-compiler/src/ast/method-dec.hxx new file mode 100644 index 0000000..2c75af3 --- /dev/null +++ b/tiger-compiler/src/ast/method-dec.hxx @@ -0,0 +1,11 @@ +/** + ** \file ast/method-dec.hxx + ** \brief Inline methods of ast::MethodDec. + */ + +#pragma once + +#include <ast/method-dec.hh> + +namespace ast +{} // namespace ast diff --git a/tiger-compiler/src/ast/name-ty.cc b/tiger-compiler/src/ast/name-ty.cc new file mode 100644 index 0000000..96610bb --- /dev/null +++ b/tiger-compiler/src/ast/name-ty.cc @@ -0,0 +1,19 @@ +/** + ** \file ast/name-ty.cc + ** \brief Implementation of ast::NameTy. + */ + +#include <ast/name-ty.hh> +#include <ast/visitor.hh> + +namespace ast +{ + NameTy::NameTy(const Location& location, misc::symbol name) + : Ty(location) + , name_(name) + {} + + void NameTy::accept(ConstVisitor& v) const { v(*this); } + + void NameTy::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/name-ty.hh b/tiger-compiler/src/ast/name-ty.hh new file mode 100644 index 0000000..de08848 --- /dev/null +++ b/tiger-compiler/src/ast/name-ty.hh @@ -0,0 +1,56 @@ +/** + ** \file ast/name-ty.hh + ** \brief Declaration of ast::NameTy. + */ + +#pragma once + +#include <ast/ty.hh> +#include <ast/type-dec.hh> +#include <misc/symbol.hh> + +namespace ast +{ + /// NameTy. + class NameTy : public Ty + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a NameTy node. + NameTy(const Location& location, misc::symbol name); + NameTy(const NameTy&) = delete; + NameTy& operator=(const NameTy&) = delete; + /// Destroy a NameTy node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the name of the type. + misc::symbol name_get() const; + /// Set the name of the type. + void name_set(misc::symbol); + /// Return definition site. + const TypeDec* def_get() const; + /// Return definition site. + TypeDec* def_get(); + /// Set definition site. + void def_set(TypeDec*); + /** \} */ + + protected: + /// The name of the type. + misc::symbol name_; + /// Definition site. + TypeDec* def_ = nullptr; + }; +} // namespace ast +#include <ast/name-ty.hxx> diff --git a/tiger-compiler/src/ast/name-ty.hxx b/tiger-compiler/src/ast/name-ty.hxx new file mode 100644 index 0000000..d93b383 --- /dev/null +++ b/tiger-compiler/src/ast/name-ty.hxx @@ -0,0 +1,20 @@ +/** + ** \file ast/name-ty.hxx + ** \brief Inline methods of ast::NameTy. + */ + +#pragma once + +#include <ast/name-ty.hh> + +namespace ast +{ + + inline misc::symbol NameTy::name_get() const { return name_; } + inline void NameTy::name_set(misc::symbol name) { name_ = name; } + + inline const TypeDec* NameTy::def_get() const { return def_; } + inline TypeDec* NameTy::def_get() { return def_; } + inline void NameTy::def_set(TypeDec* def) { def_ = def; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/nil-exp.cc b/tiger-compiler/src/ast/nil-exp.cc new file mode 100644 index 0000000..f51c442 --- /dev/null +++ b/tiger-compiler/src/ast/nil-exp.cc @@ -0,0 +1,19 @@ +/** + ** \file ast/nil-exp.cc + ** \brief Implementation of ast::NilExp. + */ + +#include <ast/nil-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + NilExp::NilExp(const Location& location) + : Exp(location) + , TypeConstructor() + {} + + void NilExp::accept(ConstVisitor& v) const { v(*this); } + + void NilExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/nil-exp.hh b/tiger-compiler/src/ast/nil-exp.hh new file mode 100644 index 0000000..c526b4b --- /dev/null +++ b/tiger-compiler/src/ast/nil-exp.hh @@ -0,0 +1,37 @@ +/** + ** \file ast/nil-exp.hh + ** \brief Declaration of ast::NilExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/type-constructor.hh> + +namespace ast +{ + /// NilExp. + class NilExp + : public Exp + , public TypeConstructor + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a NilExp node. + explicit NilExp(const Location& location); + NilExp(const NilExp&) = delete; + NilExp& operator=(const NilExp&) = delete; + /// Destroy a NilExp node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + }; +} // namespace ast +#include <ast/nil-exp.hxx> diff --git a/tiger-compiler/src/ast/nil-exp.hxx b/tiger-compiler/src/ast/nil-exp.hxx new file mode 100644 index 0000000..8d13ae6 --- /dev/null +++ b/tiger-compiler/src/ast/nil-exp.hxx @@ -0,0 +1,11 @@ +/** + ** \file ast/nil-exp.hxx + ** \brief Inline methods of ast::NilExp. + */ + +#pragma once + +#include <ast/nil-exp.hh> + +namespace ast +{} // namespace ast diff --git a/tiger-compiler/src/ast/non-assert-visitor.hh b/tiger-compiler/src/ast/non-assert-visitor.hh new file mode 100644 index 0000000..383768a --- /dev/null +++ b/tiger-compiler/src/ast/non-assert-visitor.hh @@ -0,0 +1,51 @@ +/** + ** \file ast/object-visitor.hh + ** \brief Provide default visits for assertion nodes. + */ + +#pragma once + +#include <ast/visitor.hh> + +namespace ast +{ + template <template <typename> class Const> + class GenNonAssertVisitor : virtual public GenVisitor<Const> + { + public: + /// Super class type. + using super_type = GenVisitor<Const>; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Convenient abbreviation. + template <typename Type> using const_t = typename Const<Type>::type; + + /** \name Ctor & dtor. + ** \{ */ + /// Construct an object visitor. + GenNonAssertVisitor(); + /// Destroy an object visitor. + virtual ~GenNonAssertVisitor(); + /** \} */ + + /// \name Object-related visits. + /// \{ + void operator()(const_t<AssertExp>& e) override; + /// \} + }; + + /// Shorthand for a const visitor. + using NonAssertConstVisitor = GenNonAssertVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using NonAssertVisitor = GenNonAssertVisitor<misc::id_traits>; + +#ifdef SWIG + %template(AssertNonConstVisitor) GenNonAssertVisitor<misc::constify_traits>; + %template(AssertNonVisitor) GenNonAssertVisitor<misc::id_traits>; +#endif + +} // namespace ast + +#include <ast/non-assert-visitor.hxx> diff --git a/tiger-compiler/src/ast/non-assert-visitor.hxx b/tiger-compiler/src/ast/non-assert-visitor.hxx new file mode 100644 index 0000000..dd0990d --- /dev/null +++ b/tiger-compiler/src/ast/non-assert-visitor.hxx @@ -0,0 +1,34 @@ +/** + ** \file ast/assert-visitor.hxx + ** \brief Implementation for ast/assert-visitor.hh. + */ + +#pragma once + +#include <ast/assert-exp.hh> +#include <ast/assert-visitor.hh> +#include <misc/contract.hh> + +namespace ast +{ + template <template <typename> class Const> + GenNonAssertVisitor<Const>::GenNonAssertVisitor() + : GenVisitor<Const>() + {} + + template <template <typename> class Const> + GenNonAssertVisitor<Const>::~GenNonAssertVisitor() + {} + + /*-------------------------------. + | Assert-related visit method. | + `-------------------------------*/ + + template <template <typename> class Const> + void GenNonAssertVisitor<Const>::operator()(const_t<AssertExp>&) + { + // We must not be here (there should be no assert feature in plain Tiger). + unreachable(); + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/non-object-visitor.hh b/tiger-compiler/src/ast/non-object-visitor.hh new file mode 100644 index 0000000..c3fcf1a --- /dev/null +++ b/tiger-compiler/src/ast/non-object-visitor.hh @@ -0,0 +1,92 @@ +/** + ** \file ast/non-object-visitor.hh + ** \brief Provide aborting visits for object-related nodes. + */ + +#pragma once + +#include <ast/visitor.hh> + +namespace ast +{ + /** GenNonObjectVisitor<CONSTNESS-SELECTOR> provides aborting visit + methods for object-related nodes. This class is meant to factor + the code visiting object-related nodes in visitors bound to + process AST \em without objects. + + ast::GenNonObjectVisitor inherits virtually from ast::GenVisitor + to allow diamond inheritance, notably for a ``compatibility'' + purpose with ast::GenDefaultVisitor. + + For instance, type::TypeChecker, a visitor that checks the types + of an AST without objects, inherits from ast::DefaultVisitor to + factor default (``empty'') traversal implementations, and from + ast::NonObjectVisitor to get an aborting behavior for + object-related nodes. + + \verbatim + + /ast::Visitor/ + ^ + (virtual) | (virtual) + ,--------------+--------------. + | | + | | + /ast::DefaultVisitor/ /ast::NonObjectVisitor/ + ^ ^ + | | + `--------------+--------------' + | + | + type::TypeChecker + + \endverbatim + */ + template <template <typename> class Const> + class GenNonObjectVisitor : virtual public GenVisitor<Const> + { + public: + /// Super class type. + using super_type = GenVisitor<Const>; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Convenient abbreviation. + template <typename Type> using const_t = typename Const<Type>::type; + + /** \name Ctor & dtor. + ** \{ */ + /// Construct a non-object visitor. + GenNonObjectVisitor(); + /// Destroy a non-object visitor. + virtual ~GenNonObjectVisitor(); + /** \} */ + + /// \name Object-related visits. + /// + /// The methods should not be used, since this visitor is for the + /// non-object flavor of the language. + /// \{ + void operator()(const_t<ClassTy>& e) override; + + void operator()(const_t<MethodChunk>& e) override; + void operator()(const_t<MethodDec>& e) override; + + void operator()(const_t<MethodCallExp>& e) override; + void operator()(const_t<ObjectExp>& e) override; + /// \} + }; + + /// Shorthand for a const visitor. + using NonObjectConstVisitor = GenNonObjectVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using NonObjectVisitor = GenNonObjectVisitor<misc::id_traits>; + +#ifdef SWIG + %template(NonObjectConstVisitor) GenNonObjectVisitor<misc::constify_traits>; + %template(NonObjectVisitor) GenNonObjectVisitor<misc::id_traits>; +#endif +} // namespace ast + +#include <ast/non-object-visitor.hxx> diff --git a/tiger-compiler/src/ast/non-object-visitor.hxx b/tiger-compiler/src/ast/non-object-visitor.hxx new file mode 100644 index 0000000..8d49932 --- /dev/null +++ b/tiger-compiler/src/ast/non-object-visitor.hxx @@ -0,0 +1,62 @@ +/** + ** \file ast/non-object-visitor.hxx + ** \brief Implementation for ast/non-object-visitor.hh. + */ + +#pragma once + +#include <ast/all.hh> +#include <ast/non-object-visitor.hh> +#include <misc/contract.hh> + +namespace ast +{ + template <template <typename> class Const> + GenNonObjectVisitor<Const>::GenNonObjectVisitor() + : GenVisitor<Const>() + {} + + template <template <typename> class Const> + GenNonObjectVisitor<Const>::~GenNonObjectVisitor() + {} + + /*-----------------------------------------. + | Object-related visit methods, disabled. | + `-----------------------------------------*/ + + template <template <typename> class Const> + void GenNonObjectVisitor<Const>::operator()(const_t<ClassTy>&) + { + // We must not be here (there should be no object feature in plain Tiger). + unreachable(); + } + + template <template <typename> class Const> + void GenNonObjectVisitor<Const>::operator()(const_t<MethodChunk>&) + { + // We must not be here (there should be no object feature in plain Tiger). + unreachable(); + } + + template <template <typename> class Const> + void GenNonObjectVisitor<Const>::operator()(const_t<MethodDec>&) + { + // We must not be here (there should be no object feature in plain Tiger). + unreachable(); + } + + template <template <typename> class Const> + void GenNonObjectVisitor<Const>::operator()(const_t<MethodCallExp>&) + { + // We must not be here (there should be no object feature in plain Tiger). + unreachable(); + } + + template <template <typename> class Const> + void GenNonObjectVisitor<Const>::operator()(const_t<ObjectExp>&) + { + // We must not be here (there should be no object feature in plain Tiger). + unreachable(); + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/object-exp.cc b/tiger-compiler/src/ast/object-exp.cc new file mode 100644 index 0000000..c2d5808 --- /dev/null +++ b/tiger-compiler/src/ast/object-exp.cc @@ -0,0 +1,19 @@ +/** + ** \file ast/object-exp.cc + ** \brief Implementation of ast::ObjectExp. + */ + +#include <ast/object-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + ObjectExp::ObjectExp(const Location& location, NameTy* type_name) + : Exp(location) + , type_name_(type_name) + {} + + void ObjectExp::accept(ConstVisitor& v) const { v(*this); } + + void ObjectExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/object-exp.hh b/tiger-compiler/src/ast/object-exp.hh new file mode 100644 index 0000000..efab0a5 --- /dev/null +++ b/tiger-compiler/src/ast/object-exp.hh @@ -0,0 +1,47 @@ +/** + ** \file ast/object-exp.hh + ** \brief Declaration of ast::ObjectExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/name-ty.hh> + +namespace ast +{ + /// ObjectExp. + class ObjectExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an ObjectExp node. + ObjectExp(const Location& location, NameTy* type_name); + ObjectExp(const ObjectExp&) = delete; + ObjectExp& operator=(const ObjectExp&) = delete; + /// Destroy an ObjectExp node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return name of the class from which the object is instantiated. + const NameTy& type_name_get() const; + /// Return name of the class from which the object is instantiated. + NameTy& type_name_get(); + /** \} */ + + protected: + /// Name of the class from which the object is instantiated. + NameTy* type_name_; + }; +} // namespace ast +#include <ast/object-exp.hxx> diff --git a/tiger-compiler/src/ast/object-exp.hxx b/tiger-compiler/src/ast/object-exp.hxx new file mode 100644 index 0000000..38fd5f6 --- /dev/null +++ b/tiger-compiler/src/ast/object-exp.hxx @@ -0,0 +1,16 @@ +/** + ** \file ast/object-exp.hxx + ** \brief Inline methods of ast::ObjectExp. + */ + +#pragma once + +#include <ast/object-exp.hh> + +namespace ast +{ + + inline const NameTy& ObjectExp::type_name_get() const { return *type_name_; } + inline NameTy& ObjectExp::type_name_get() { return *type_name_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/object-visitor.hh b/tiger-compiler/src/ast/object-visitor.hh new file mode 100644 index 0000000..b14c829 --- /dev/null +++ b/tiger-compiler/src/ast/object-visitor.hh @@ -0,0 +1,89 @@ +/** + ** \file ast/object-visitor.hh + ** \brief Provide default visits for object-related nodes. + */ + +#pragma once + +#include <ast/visitor.hh> + +namespace ast +{ + /** GenObjectVisitor<CONSTNESS-SELECTOR> provides default visit + methods for object-related nodes. This class is meant to factor + the code visiting object-related nodes. + + ast::GenObjectVisitor inherits virtually from ast::GenVisitor + to allow diamond inheritance, notably for a ``compatibility'' + purpose with ast::GenDefaultVisitor. + + For instance, bind::Binder, a visitor that handles bindings for + an AST without objects, inherits from ast::DefaultVisitor to + factor default (``empty'') traversal implementations for + non-object-related nodes, and from ast::ObjectVisitor for + object-related nodes. + + \verbatim + + /ast::Visitor/ + ^ + (virtual) | (virtual) + ,--------------+--------------. + | | + | | + /ast::DefaultVisitor/ /ast::ObjectVisitor/ + ^ ^ + | | + `--------------+--------------' + | + | + bind::Binder + + \endverbatim + */ + template <template <typename> class Const> + class GenObjectVisitor : virtual public GenVisitor<Const> + { + public: + /// Super class type. + using super_type = GenVisitor<Const>; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Convenient abbreviation. + template <typename Type> using const_t = typename Const<Type>::type; + + /** \name Ctor & dtor. + ** \{ */ + /// Construct an object visitor. + GenObjectVisitor(); + /// Destroy an object visitor. + virtual ~GenObjectVisitor(); + /** \} */ + + /// \name Object-related visits. + /// \{ + void operator()(const_t<ClassTy>& e) override; + + void operator()(const_t<MethodChunk>& e) override; + void operator()(const_t<MethodDec>& e) override; + + void operator()(const_t<MethodCallExp>& e) override; + void operator()(const_t<ObjectExp>& e) override; + /// \} + }; + + /// Shorthand for a const visitor. + using ObjectConstVisitor = GenObjectVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using ObjectVisitor = GenObjectVisitor<misc::id_traits>; + +#ifdef SWIG + %template(ObjectConstVisitor) GenObjectVisitor<misc::constify_traits>; + %template(ObjectVisitor) GenObjectVisitor<misc::id_traits>; +#endif + +} // namespace ast + +#include <ast/object-visitor.hxx> diff --git a/tiger-compiler/src/ast/object-visitor.hxx b/tiger-compiler/src/ast/object-visitor.hxx new file mode 100644 index 0000000..c4eccbb --- /dev/null +++ b/tiger-compiler/src/ast/object-visitor.hxx @@ -0,0 +1,69 @@ +/** + ** \file ast/object-visitor.hxx + ** \brief Implementation for ast/object-visitor.hh. + */ + +#pragma once + +#include <ast/all.hh> +#include <ast/object-visitor.hh> +#include <misc/contract.hh> + +namespace ast +{ + template <template <typename> class Const> + GenObjectVisitor<Const>::GenObjectVisitor() + : GenVisitor<Const>() + {} + + template <template <typename> class Const> + GenObjectVisitor<Const>::~GenObjectVisitor() + {} + + /*-------------------------------. + | Object-related visit methods. | + `-------------------------------*/ + + template <template <typename> class Const> + void GenObjectVisitor<Const>::operator()(const_t<ClassTy>& e) + { + // FIXME DONE: Some code was deleted here. + e.chunks_get().accept(*this); + } + + template <template <typename> class Const> + void GenObjectVisitor<Const>::operator()(const_t<MethodChunk>& e) + { + // FIXME DONE: Some code was deleted here. + for (const auto dec : e) + dec->accept(*this); + } + + template <template <typename> class Const> + void GenObjectVisitor<Const>::operator()(const_t<MethodDec>& e) + { + // FIXME DONE: Some code was deleted here. + e.formals_get().accept(*this); + if (e.result_get() != nullptr) + e.result_get()->accept(*this); + if (e.body_get() != nullptr) + e.body_get()->accept(*this); + } + + template <template <typename> class Const> + void GenObjectVisitor<Const>::operator()(const_t<MethodCallExp>& e) + { + // FIXME DONE: Some code was deleted here. + e.object_get().accept(*this); + for (auto arg : e.args_get()) + arg->accept(*this); + } + + template <template <typename> class Const> + void GenObjectVisitor<Const>::operator()(const_t<ObjectExp>& e) + { + // FIXME DONE: Some code was deleted here. + e.type_name_get().accept(*this); + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/op-exp.cc b/tiger-compiler/src/ast/op-exp.cc new file mode 100644 index 0000000..6db4940 --- /dev/null +++ b/tiger-compiler/src/ast/op-exp.cc @@ -0,0 +1,42 @@ +/** + ** \file ast/op-exp.cc + ** \brief Implementation of ast::OpExp. + */ + +#include <ast/op-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + OpExp::OpExp(const Location& location, + Exp* left, + OpExp::Oper oper, + Exp* right) + : Exp(location) + , left_(left) + , oper_(oper) + , right_(right) + {} + + OpExp::~OpExp() + { + delete left_; + delete right_; + } + + void OpExp::accept(ConstVisitor& v) const { v(*this); } + + void OpExp::accept(Visitor& v) { v(*this); } +} // namespace ast + +std::string str(ast::OpExp::Oper oper) +{ + static const std::unordered_map<ast::OpExp::Oper, std::string> op_str = { + {ast::OpExp::Oper::add, "+"}, {ast::OpExp::Oper::sub, "-"}, + {ast::OpExp::Oper::mul, "*"}, {ast::OpExp::Oper::div, "/"}, + {ast::OpExp::Oper::eq, "="}, {ast::OpExp::Oper::ne, "<>"}, + {ast::OpExp::Oper::lt, "<"}, {ast::OpExp::Oper::le, "<="}, + {ast::OpExp::Oper::gt, ">"}, {ast::OpExp::Oper::ge, ">="}}; + + return op_str.at(oper); +} diff --git a/tiger-compiler/src/ast/op-exp.hh b/tiger-compiler/src/ast/op-exp.hh new file mode 100644 index 0000000..372650f --- /dev/null +++ b/tiger-compiler/src/ast/op-exp.hh @@ -0,0 +1,80 @@ +/** + ** \file ast/op-exp.hh + ** \brief Declaration of ast::OpExp. + */ + +#pragma once + +#include <unordered_map> +#include <ast/exp.hh> + +namespace ast +{ + /// OpExp. + class OpExp : public Exp + { + public: + /// Operator qualifier. + enum class Oper + { + // Arithmetics. + /** \brief "+" */ add, + /** \brief "-" */ sub, + /** \brief "*" */ mul, + /** \brief "/" */ div, + + // Comparison. + /** \brief "=" */ eq, + /** \brief "<>" */ ne, + /** \brief "<" */ lt, + /** \brief "<=" */ le, + /** \brief ">" */ gt, + /** \brief ">=" */ ge + }; + + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct an OpExp node. + OpExp(const Location& location, Exp* left, OpExp::Oper oper, Exp* right); + OpExp(const OpExp&) = delete; + OpExp& operator=(const OpExp&) = delete; + /// Destroy an OpExp node. + ~OpExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return left operand. + const Exp& left_get() const; + /// Return left operand. + Exp& left_get(); + /// Return operator. + OpExp::Oper oper_get() const; + /// Return right operand. + const Exp& right_get() const; + /// Return right operand. + Exp& right_get(); + /** \} */ + + protected: + /// Left operand. + Exp* left_; + /// Operator. + OpExp::Oper oper_; + /// Right operand. + Exp* right_; + }; +} // namespace ast + +// Return a representation of an operator. +std::string str(ast::OpExp::Oper oper); +#include <ast/op-exp.hxx> diff --git a/tiger-compiler/src/ast/op-exp.hxx b/tiger-compiler/src/ast/op-exp.hxx new file mode 100644 index 0000000..a665c3a --- /dev/null +++ b/tiger-compiler/src/ast/op-exp.hxx @@ -0,0 +1,21 @@ +/** + ** \file ast/op-exp.hxx + ** \brief Inline methods of ast::OpExp. + */ + +#pragma once + +#include <ast/op-exp.hh> + +namespace ast +{ + + inline const Exp& OpExp::left_get() const { return *left_; } + inline Exp& OpExp::left_get() { return *left_; } + + inline OpExp::Oper OpExp::oper_get() const { return oper_; } + + inline const Exp& OpExp::right_get() const { return *right_; } + inline Exp& OpExp::right_get() { return *right_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/pretty-printer.cc b/tiger-compiler/src/ast/pretty-printer.cc new file mode 100644 index 0000000..91dbc87 --- /dev/null +++ b/tiger-compiler/src/ast/pretty-printer.cc @@ -0,0 +1,419 @@ +/** + ** \file ast/pretty-printer.cc + ** \brief Implementation of ast::PrettyPrinter. + */ + +#include <ast/all.hh> +#include <ast/libast.hh> +#include <ast/pretty-printer.hh> +#include <misc/escape.hh> +#include <misc/indent.hh> +#include <misc/separator.hh> + +#include <type/class.hh> + +namespace ast +{ + // Anonymous namespace: these functions are private to this file. + namespace + { + /// Output \a e on \a ostr. + inline std::ostream& operator<<(std::ostream& ostr, const Escapable& e) + { + if (escapes_display(ostr) + // FIXME DONE: Some code was deleted here. + && e.escape_get()) + ostr << "/* escaping */ "; + + return ostr; + } + + /// \brief Output \a e on \a ostr. + /// + /// Used to factor the output of the name declared, + /// and its possible additional attributes. + inline std::ostream& operator<<(std::ostream& ostr, const Dec& e) + { + ostr << e.name_get(); + if (bindings_display(ostr)) + ostr << " /* " << &e << " */"; + return ostr; + } + } // namespace + + PrettyPrinter::PrettyPrinter(std::ostream& ostr) + : ostr_(ostr) + {} + + void PrettyPrinter::operator()(const SimpleVar& e) + { + ostr_ << e.name_get(); + if (bindings_display(ostr_)) + ostr_ << " /* " << e.def_get() << " */"; + } + + void PrettyPrinter::operator()(const FieldVar& e) + { + // FIXME DONE: Some code was deleted here. + ostr_ << e.var_get() << "." << e.name_get(); + } + + /* Foo[10]. */ + void PrettyPrinter::operator()(const SubscriptVar& e) + { + ostr_ << e.var_get() << '[' << misc::incindent << e.index_get() + << misc::decindent << ']'; + } + + void PrettyPrinter::operator()(const CastExp& e) + { + ostr_ << "_cast(" << e.exp_get() << ", " << e.ty_get() << ')'; + } + + // FIXME DONE: Some code was deleted here. + void PrettyPrinter::operator()(const NilExp&) { ostr_ << "nil"; } + void PrettyPrinter::operator()(const IntExp& e) { ostr_ << e.value_get(); } + void PrettyPrinter::operator()(const StringExp& e) + { + ostr_ << "\"" << misc::escape(e.value_get()) << "\""; + } + + void PrettyPrinter::operator()(const ObjectExp& e) + { + ostr_ << "new " << e.type_name_get(); + if (bindings_display(ostr_)) + ostr_ << " /* " << &e.type_name_get() << " */"; + } + + void PrettyPrinter::operator()(const CallExp& e) + { + ostr_ << e.name_get(); + if (bindings_display(ostr_)) + ostr_ << " /* " << e.def_get() << " */"; + ostr_ << "("; + if (!e.args_get().empty()) + { + ostr_ << misc::separate(e.args_get(), ", "); + } + ostr_ << ")"; + } + + void PrettyPrinter::operator()(const MethodCallExp& e) + { + ostr_ << e.object_get() << "." << e.name_get(); + if (bindings_display(ostr_)) + ostr_ << " /* " << e.def_get() << " */"; + ostr_ << "("; + if (!e.args_get().empty()) + { + ostr_ << misc::separate(e.args_get(), ", "); + } + ostr_ << ")"; + } + + void PrettyPrinter::operator()(const OpExp& e) + { + ostr_ << e.left_get() << " " << str(e.oper_get()) << " " << e.right_get(); + } + + void PrettyPrinter::operator()(const RecordExp& e) + { + ostr_ << e.type_name_get(); + + ostr_ << " { "; + if (!e.fields_get().empty()) + { + ostr_ << misc::separate(e.fields_get(), ", "); + } + ostr_ << " }"; + } + + void PrettyPrinter::operator()(const RecordTy& e) + { + ostr_ << "{ "; + if (!e.fields_get().empty()) + { + ostr_ << misc::separate(e.fields_get(), ", "); + } + ostr_ << " }"; + } + + void PrettyPrinter::operator()(const ArrayTy& e) + { + ostr_ << "array of " << e.base_type_get(); + } + + void PrettyPrinter::operator()(const ClassTy& e) + { + ostr_ << "class extends " << e.super_get() << "{" << misc::decendl; + if (!e.chunks_get().chunks_get().empty()) + { + ostr_ << misc::separate(e.chunks_get(), "\n"); + } + ostr_ << " }" << misc::decendl; + } + + void PrettyPrinter::operator()(const Field& e) + { + ostr_ << e.name_get() << " : " << e.type_name_get(); + } + + void PrettyPrinter::operator()(const SeqExp& e) + { + if (e.exps_get().size() == 0) + ostr_ << "()"; + else if (e.exps_get().size() == 1) + ostr_ << *e.exps_get().at(0); + else if (e.exps_get().size() > 1) + { + ostr_ << '(' << misc::incendl << *e.exps_get().at(0); + for (size_t i = 1; i < e.exps_get().size(); ++i) + { + ostr_ << ";" << misc::iendl << *e.exps_get().at(i); + } + ostr_ << misc::decendl << ")"; + } + } + + void PrettyPrinter::operator()(const AssignExp& e) + { + ostr_ << e.var_get() << " := " << e.exp_get(); + } + + void PrettyPrinter::operator()(const IfExp& e) + { + ostr_ << "(if " << e.test_get() << " then" << misc::incendl + << e.thenclause_get() << misc::decendl; + ostr_ << "else" << misc::incendl << e.elseclause_get() << ")" + << misc::decendl; + } + + void PrettyPrinter::operator()(const WhileExp& e) + { + ostr_ << "(while "; + if (bindings_display(ostr_)) + { + ostr_ << "/* " << &e << " /* "; + } + ostr_ << e.test_get() << " do" << misc::incendl << e.body_get() << ")" + << misc::decendl; + } + + void PrettyPrinter::operator()(const ForExp& e) + { + ostr_ << "(for "; + if (bindings_display(ostr_)) + { + ostr_ << "/* " << &e << " */ "; + } + ostr_ << e.vardec_get().name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &e.vardec_get() << " */"; + } + ostr_ << " := " << *e.vardec_get().init_get() << " to " << e.hi_get() + << " do" << misc::incendl << e.body_get() << ")"; + } + + void PrettyPrinter::operator()(const BreakExp& e) + { + ostr_ << "break"; + if (bindings_display(ostr_)) + { + ostr_ << " /* " << e.def_get() << " */"; + } + } + + void PrettyPrinter::operator()(const LetExp& e) + { + ostr_ << "let" << misc::incendl << e.chunks_get() << misc::decendl << "in" + << misc::incendl << e.body_get() << misc::decendl << "end"; + } + + void PrettyPrinter::operator()(const ArrayExp& e) + { + ostr_ << e.type_name_get() << "[" << e.size_get() << "] of " + << e.init_get(); + } + + void PrettyPrinter::operator()(const FieldInit& e) + { + ostr_ << e.name_get() << " = " << e.init_get(); + } + + void PrettyPrinter::operator()(const VarChunk& e) + { + for (auto dec : e.decs_get()) + { + dec->accept(*this); + ostr_ << misc::iendl; + } + } + + void PrettyPrinter::operator()(const VarDec& e) + { + ostr_ << "var " << static_cast<Escapable>(e) << e.name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &e << " */"; + } + if (e.type_name_get() != nullptr) + ostr_ << " : " << *e.type_name_get(); + if (e.init_get() != nullptr) + ostr_ << " := " << *e.init_get(); + else + ostr_ << " := 0"; + } + + void PrettyPrinter::operator()(const TypeChunk& e) + { + for (auto dec : e.decs_get()) + { + dec->accept(*this); + ostr_ << misc::iendl; + } + } + + void PrettyPrinter::operator()(const TypeDec& e) + { + if (const auto c = dynamic_cast<const ClassTy*>(&e.ty_get()); c) + { + ostr_ << "class " << e.name_get(); + if (bindings_display(ostr_)) + ostr_ << " /* " << &e << " */"; + ostr_ << " extends " << c->super_get() + << misc::iendl << "{" << misc::incendl; + if (!c->chunks_get().chunks_get().empty()) + { + ostr_ << misc::separate(c->chunks_get(), "\n"); + } + ostr_ << misc::decendl << "}"; + } + else + { + ostr_ << "type " << e.name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &e << " */"; + } + ostr_ << " = " << e.ty_get(); + } + } + + void PrettyPrinter::operator()(const NameTy& e) + { + ostr_ << e.name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << e.def_get() << " */"; + } + } + + void PrettyPrinter::operator()(const FunctionChunk& e) + { + for (auto dec : e.decs_get()) + { + dec->accept(*this); + ostr_ << misc::iendl; + } + } + + void PrettyPrinter::operator()(const FunctionDec& e) + { + if (e.body_get() == nullptr) + ostr_ << "primitive "; + else + ostr_ << "function "; + ostr_ << e.name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &e << " */"; + } + ostr_ << "("; + auto decs = e.formals_get().decs_get(); + if (!decs.empty()) + { + ostr_ << static_cast<Escapable>(*decs.at(0)) << decs.at(0)->name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &decs.at(0) << " */"; + } + ostr_ << " : " << *decs.at(0)->type_name_get(); + for (size_t i = 1; i < decs.size(); ++i) + { + ostr_ << ", " << static_cast<Escapable>(*decs.at(i)) + << decs.at(i)->name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &decs.at(i) << " */"; + } + ostr_ << " : " << *decs.at(i)->type_name_get(); + } + } + ostr_ << ")"; + if (e.result_get() != nullptr) + { + ostr_ << " : " << *e.result_get(); + } + if (e.body_get() != nullptr) + { + ostr_ << " =" << misc::incendl << *e.body_get() << misc::decindent; + } + ostr_ << misc::iendl; + } + + void PrettyPrinter::operator()(const MethodChunk& e) + { + for (auto dec : e.decs_get()) + { + dec->accept(*this); + ostr_ << misc::iendl; + } + } + + void PrettyPrinter::operator()(const MethodDec& e) + { + ostr_ << "method " << e.name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &e << " */"; + } + ostr_ << "("; + auto decs = e.formals_get().decs_get(); + if (!decs.empty()) + { + ostr_ << static_cast<Escapable>(*decs.at(0)) << decs.at(0)->name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &decs.at(0) << " */"; + } + ostr_ << " : " << *decs.at(0)->type_name_get(); + for (size_t i = 1; i < decs.size(); ++i) + { + ostr_ << ", " << static_cast<Escapable>(*decs.at(i)) + << decs.at(i)->name_get(); + if (bindings_display(ostr_)) + { + ostr_ << " /* " << &decs.at(i) << " */"; + } + ostr_ << " : " << *decs.at(i)->type_name_get(); + } + } + ostr_ << ")"; + if (e.result_get() != nullptr) + { + ostr_ << " : " << *e.result_get(); + } + if (e.body_get() != nullptr) + { + ostr_ << " =" << misc::incendl << *e.body_get() << misc::decindent; + } + ostr_ << misc::iendl; + } + + void PrettyPrinter::operator()(const AssertExp& e) + { + ostr_ << "assert " << e.cond_get(); + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/pretty-printer.hh b/tiger-compiler/src/ast/pretty-printer.hh new file mode 100644 index 0000000..7532331 --- /dev/null +++ b/tiger-compiler/src/ast/pretty-printer.hh @@ -0,0 +1,96 @@ +/** + ** \file ast/pretty-printer.hh + ** \brief Declaration of ast::PrettyPrinter. + */ + +#pragma once + +#include <ast/assert-visitor.hh> +#include <ast/default-visitor.hh> +#include <ast/object-visitor.hh> + +namespace ast +{ + /// Visit an Ast and print the content of each node. + class PrettyPrinter + : virtual public DefaultConstVisitor + , virtual public ObjectConstVisitor + , virtual public AssertConstVisitor + { + public: + using super_type = DefaultConstVisitor; + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build to print on \a ostr. + PrettyPrinter(std::ostream& ostr); + + /// Visit methods. + /// \{ + void operator()(const SimpleVar& e) override; + void operator()(const FieldVar& e) override; + void operator()(const SubscriptVar& e) override; + void operator()(const CastExp& e) override; + // FIXME DONE: Some code was deleted here. + void operator()(const NilExp& e) override; + void operator()(const IntExp& e) override; + void operator()(const StringExp& e) override; + void operator()(const ObjectExp& e) override; + void operator()(const CallExp& e) override; + void operator()(const MethodCallExp& e) override; + void operator()(const OpExp& e) override; + void operator()(const RecordExp& e) override; + void operator()(const SeqExp& e) override; + void operator()(const AssignExp& e) override; + void operator()(const IfExp& e) override; + void operator()(const WhileExp& e) override; + void operator()(const ForExp& e) override; + void operator()(const BreakExp&) override; + void operator()(const LetExp& e) override; + void operator()(const ArrayExp& e) override; + void operator()(const FieldInit& e) override; + /// \} + + /// Visit Var declarations. + void operator()(const VarChunk& e) override; + void operator()(const VarDec& e) override; + + /// Visit Function declarations. + void operator()(const FunctionChunk& e) override; + void operator()(const FunctionDec& e) override; + void operator()(const MethodChunk& e) override; + void operator()(const MethodDec& e) override; + + /// Visit Type declarations. + void operator()(const TypeChunk& e) override; + void operator()(const TypeDec& e) override; + + /** \} */ + + /** \name Visit Type related nodes. + ** \{ */ + void operator()(const NameTy& e) override; + void operator()(const RecordTy& e) override; + void operator()(const ArrayTy& e) override; + void operator()(const ClassTy& e) override; + /** \} */ + + /** \name Visit Field related nodes. */ + void operator()(const Field& e) override; + + /** \name Visit Assertion nodes. */ + void operator()(const AssertExp& e) override; + + private: + // Factor pretty-printing of RecordExp and RecordTy. + template <typename RecordClass> void print_record(const RecordClass& e); + + // Whether we are in a ast::ClassTy. + bool within_classty_p_ = false; + + protected: + /// The stream to print on. + std::ostream& ostr_; + }; + +} // namespace ast diff --git a/tiger-compiler/src/ast/record-exp.cc b/tiger-compiler/src/ast/record-exp.cc new file mode 100644 index 0000000..6ca817a --- /dev/null +++ b/tiger-compiler/src/ast/record-exp.cc @@ -0,0 +1,30 @@ +/** + ** \file ast/record-exp.cc + ** \brief Implementation of ast::RecordExp. + */ + +#include <ast/record-exp.hh> +#include <ast/visitor.hh> +#include <misc/algorithm.hh> + +namespace ast +{ + RecordExp::RecordExp(const Location& location, + NameTy* type_name, + fieldinits_type* fields) + : Exp(location) + , type_name_(type_name) + , fields_(fields) + {} + + RecordExp::~RecordExp() + { + delete type_name_; + misc::deep_clear(*fields_); + delete fields_; + } + + void RecordExp::accept(ConstVisitor& v) const { v(*this); } + + void RecordExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/record-exp.hh b/tiger-compiler/src/ast/record-exp.hh new file mode 100644 index 0000000..f0ba79e --- /dev/null +++ b/tiger-compiler/src/ast/record-exp.hh @@ -0,0 +1,57 @@ +/** + ** \file ast/record-exp.hh + ** \brief Declaration of ast::RecordExp. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/field-init.hh> +#include <ast/name-ty.hh> + +namespace ast +{ + /// RecordExp. + class RecordExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a RecordExp node. + RecordExp(const Location& location, + NameTy* type_name, + fieldinits_type* fields); + RecordExp(const RecordExp&) = delete; + RecordExp& operator=(const RecordExp&) = delete; + /// Destroy a RecordExp node. + ~RecordExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return identifier of the record type. + const NameTy& type_name_get() const; + /// Return identifier of the record type. + NameTy& type_name_get(); + /// Return list of field initializations. + const fieldinits_type& fields_get() const; + /// Return list of field initializations. + fieldinits_type& fields_get(); + /** \} */ + + protected: + /// Identifier of the record type. + NameTy* type_name_; + /// List of field initializations. + fieldinits_type* fields_; + }; +} // namespace ast +#include <ast/record-exp.hxx> diff --git a/tiger-compiler/src/ast/record-exp.hxx b/tiger-compiler/src/ast/record-exp.hxx new file mode 100644 index 0000000..ab92edd --- /dev/null +++ b/tiger-compiler/src/ast/record-exp.hxx @@ -0,0 +1,22 @@ +/** + ** \file ast/record-exp.hxx + ** \brief Inline methods of ast::RecordExp. + */ + +#pragma once + +#include <ast/record-exp.hh> + +namespace ast +{ + + inline const NameTy& RecordExp::type_name_get() const { return *type_name_; } + inline NameTy& RecordExp::type_name_get() { return *type_name_; } + + inline const fieldinits_type& RecordExp::fields_get() const + { + return *fields_; + } + inline fieldinits_type& RecordExp::fields_get() { return *fields_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/record-ty.cc b/tiger-compiler/src/ast/record-ty.cc new file mode 100644 index 0000000..5614e96 --- /dev/null +++ b/tiger-compiler/src/ast/record-ty.cc @@ -0,0 +1,26 @@ +/** + ** \file ast/record-ty.cc + ** \brief Implementation of ast::RecordTy. + */ + +#include <ast/record-ty.hh> +#include <ast/visitor.hh> +#include <misc/algorithm.hh> + +namespace ast +{ + RecordTy::RecordTy(const Location& location, fields_type* fields) + : Ty(location) + , fields_(fields) + {} + + RecordTy::~RecordTy() + { + misc::deep_clear(*fields_); + delete fields_; + } + + void RecordTy::accept(ConstVisitor& v) const { v(*this); } + + void RecordTy::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/record-ty.hh b/tiger-compiler/src/ast/record-ty.hh new file mode 100644 index 0000000..d2d1a46 --- /dev/null +++ b/tiger-compiler/src/ast/record-ty.hh @@ -0,0 +1,49 @@ + +/** + ** \file ast/record-ty.hh + ** \brief Declaration of ast::RecordTy. + */ + +#pragma once + +#include <ast/field.hh> +#include <ast/ty.hh> + +namespace ast +{ + /// RecordTy. + class RecordTy : public Ty + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a RecordTy node. + RecordTy(const Location& location, fields_type* fields); + RecordTy(const RecordTy&) = delete; + RecordTy& operator=(const RecordTy&) = delete; + /// Destroy a RecordTy node. + ~RecordTy() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the field list. + const fields_type& fields_get() const; + /// Return the field list. + fields_type& fields_get(); + /** \} */ + + protected: + /// The field list. + fields_type* fields_; + }; +} // namespace ast +#include <ast/record-ty.hxx> diff --git a/tiger-compiler/src/ast/record-ty.hxx b/tiger-compiler/src/ast/record-ty.hxx new file mode 100644 index 0000000..6d1c91c --- /dev/null +++ b/tiger-compiler/src/ast/record-ty.hxx @@ -0,0 +1,16 @@ +/** + ** \file ast/record-ty.hxx + ** \brief Inline methods of ast::RecordTy. + */ + +#pragma once + +#include <ast/record-ty.hh> + +namespace ast +{ + + inline const fields_type& RecordTy::fields_get() const { return *fields_; } + inline fields_type& RecordTy::fields_get() { return *fields_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/seq-exp.cc b/tiger-compiler/src/ast/seq-exp.cc new file mode 100644 index 0000000..af6bc6c --- /dev/null +++ b/tiger-compiler/src/ast/seq-exp.cc @@ -0,0 +1,26 @@ +/** + ** \file ast/seq-exp.cc + ** \brief Implementation of ast::SeqExp. + */ + +#include <ast/seq-exp.hh> +#include <ast/visitor.hh> +#include <misc/algorithm.hh> + +namespace ast +{ + SeqExp::SeqExp(const Location& location, exps_type* exps) + : Exp(location) + , exps_(exps) + {} + + SeqExp::~SeqExp() + { + misc::deep_clear(*exps_); + delete exps_; + } + + void SeqExp::accept(ConstVisitor& v) const { v(*this); } + + void SeqExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/seq-exp.hh b/tiger-compiler/src/ast/seq-exp.hh new file mode 100644 index 0000000..882c11e --- /dev/null +++ b/tiger-compiler/src/ast/seq-exp.hh @@ -0,0 +1,47 @@ +/** + ** \file ast/seq-exp.hh + ** \brief Declaration of ast::SeqExp. + */ + +#pragma once + +#include <ast/exp.hh> + +namespace ast +{ + /// SeqExp. + class SeqExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a SeqExp node. + SeqExp(const Location& location, exps_type* exps); + SeqExp(const SeqExp&) = delete; + SeqExp& operator=(const SeqExp&) = delete; + /// Destroy a SeqExp node. + ~SeqExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return list of expressions. + const exps_type& exps_get() const; + /// Return list of expressions. + exps_type& exps_get(); + /** \} */ + + protected: + /// List of expressions. + exps_type* exps_; + }; +} // namespace ast +#include <ast/seq-exp.hxx> diff --git a/tiger-compiler/src/ast/seq-exp.hxx b/tiger-compiler/src/ast/seq-exp.hxx new file mode 100644 index 0000000..87e0488 --- /dev/null +++ b/tiger-compiler/src/ast/seq-exp.hxx @@ -0,0 +1,16 @@ +/** + ** \file ast/seq-exp.hxx + ** \brief Inline methods of ast::SeqExp. + */ + +#pragma once + +#include <ast/seq-exp.hh> + +namespace ast +{ + + inline const exps_type& SeqExp::exps_get() const { return *exps_; } + inline exps_type& SeqExp::exps_get() { return *exps_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/simple-var.cc b/tiger-compiler/src/ast/simple-var.cc new file mode 100644 index 0000000..74035e0 --- /dev/null +++ b/tiger-compiler/src/ast/simple-var.cc @@ -0,0 +1,19 @@ +/** + ** \file ast/simple-var.cc + ** \brief Implementation of ast::SimpleVar. + */ + +#include <ast/simple-var.hh> +#include <ast/visitor.hh> + +namespace ast +{ + SimpleVar::SimpleVar(const Location& location, misc::symbol name) + : Var(location) + , name_(name) + {} + + void SimpleVar::accept(ConstVisitor& v) const { v(*this); } + + void SimpleVar::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/simple-var.hh b/tiger-compiler/src/ast/simple-var.hh new file mode 100644 index 0000000..be47767 --- /dev/null +++ b/tiger-compiler/src/ast/simple-var.hh @@ -0,0 +1,56 @@ +/** + ** \file ast/simple-var.hh + ** \brief Declaration of ast::SimpleVar. + */ + +#pragma once + +#include <ast/var-dec.hh> +#include <ast/var.hh> +#include <misc/symbol.hh> + +namespace ast +{ + /// SimpleVar. + class SimpleVar : public Var + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a SimpleVar node. + SimpleVar(const Location& location, misc::symbol name); + SimpleVar(const SimpleVar&) = delete; + SimpleVar& operator=(const SimpleVar&) = delete; + /// Destroy a SimpleVar node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return variable's name. + misc::symbol name_get() const; + /// Set variable's name. + void name_set(misc::symbol); + /// Return definition site. + const VarDec* def_get() const; + /// Return definition site. + VarDec* def_get(); + /// Set definition site. + void def_set(VarDec*); + /** \} */ + + protected: + /// Variable's name. + misc::symbol name_; + /// Definition site. + VarDec* def_ = nullptr; + }; +} // namespace ast +#include <ast/simple-var.hxx> diff --git a/tiger-compiler/src/ast/simple-var.hxx b/tiger-compiler/src/ast/simple-var.hxx new file mode 100644 index 0000000..e89b622 --- /dev/null +++ b/tiger-compiler/src/ast/simple-var.hxx @@ -0,0 +1,20 @@ +/** + ** \file ast/simple-var.hxx + ** \brief Inline methods of ast::SimpleVar. + */ + +#pragma once + +#include <ast/simple-var.hh> + +namespace ast +{ + + inline misc::symbol SimpleVar::name_get() const { return name_; } + inline void SimpleVar::name_set(misc::symbol name) { name_ = name; } + + inline const VarDec* SimpleVar::def_get() const { return def_; } + inline VarDec* SimpleVar::def_get() { return def_; } + inline void SimpleVar::def_set(VarDec* def) { def_ = def; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/string-exp.cc b/tiger-compiler/src/ast/string-exp.cc new file mode 100644 index 0000000..78ff00f --- /dev/null +++ b/tiger-compiler/src/ast/string-exp.cc @@ -0,0 +1,19 @@ +/** + ** \file ast/string-exp.cc + ** \brief Implementation of ast::StringExp. + */ + +#include <ast/string-exp.hh> +#include <ast/visitor.hh> + +namespace ast +{ + StringExp::StringExp(const Location& location, const std::string& value) + : Exp(location) + , value_(value) + {} + + void StringExp::accept(ConstVisitor& v) const { v(*this); } + + void StringExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/string-exp.hh b/tiger-compiler/src/ast/string-exp.hh new file mode 100644 index 0000000..c9fd4f0 --- /dev/null +++ b/tiger-compiler/src/ast/string-exp.hh @@ -0,0 +1,47 @@ +/** + ** \file ast/string-exp.hh + ** \brief Declaration of ast::StringExp. + */ + +#pragma once + +#include <string> +#include <ast/exp.hh> + +namespace ast +{ + /// StringExp. + class StringExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a StringExp node. + StringExp(const Location& location, const std::string& value); + StringExp(const StringExp&) = delete; + StringExp& operator=(const StringExp&) = delete; + /// Destroy a StringExp node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return stored string value. + const std::string& value_get() const; + /// Return stored string value. + std::string& value_get(); + /** \} */ + + protected: + /// Stored string value. + std::string value_; + }; +} // namespace ast +#include <ast/string-exp.hxx> diff --git a/tiger-compiler/src/ast/string-exp.hxx b/tiger-compiler/src/ast/string-exp.hxx new file mode 100644 index 0000000..2c8a429 --- /dev/null +++ b/tiger-compiler/src/ast/string-exp.hxx @@ -0,0 +1,16 @@ +/** + ** \file ast/string-exp.hxx + ** \brief Inline methods of ast::StringExp. + */ + +#pragma once + +#include <ast/string-exp.hh> + +namespace ast +{ + + inline const std::string& StringExp::value_get() const { return value_; } + inline std::string& StringExp::value_get() { return value_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/subscript-var.cc b/tiger-compiler/src/ast/subscript-var.cc new file mode 100644 index 0000000..8c2ab4e --- /dev/null +++ b/tiger-compiler/src/ast/subscript-var.cc @@ -0,0 +1,26 @@ +/** + ** \file ast/subscript-var.cc + ** \brief Implementation of ast::SubscriptVar. + */ + +#include <ast/subscript-var.hh> +#include <ast/visitor.hh> + +namespace ast +{ + SubscriptVar::SubscriptVar(const Location& location, Var* var, Exp* index) + : Var(location) + , var_(var) + , index_(index) + {} + + SubscriptVar::~SubscriptVar() + { + delete var_; + delete index_; + } + + void SubscriptVar::accept(ConstVisitor& v) const { v(*this); } + + void SubscriptVar::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/subscript-var.hh b/tiger-compiler/src/ast/subscript-var.hh new file mode 100644 index 0000000..d771391 --- /dev/null +++ b/tiger-compiler/src/ast/subscript-var.hh @@ -0,0 +1,54 @@ +/** + ** \file ast/subscript-var.hh + ** \brief Declaration of ast::SubscriptVar. + */ + +#pragma once + +#include <ast/exp.hh> +#include <ast/var.hh> + +namespace ast +{ + /// SubscriptVar. + class SubscriptVar : public Var + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a SubscriptVar node. + SubscriptVar(const Location& location, Var* var, Exp* index); + SubscriptVar(const SubscriptVar&) = delete; + SubscriptVar& operator=(const SubscriptVar&) = delete; + /// Destroy a SubscriptVar node. + ~SubscriptVar() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the mother variable. + const Var& var_get() const; + /// Return the mother variable. + Var& var_get(); + /// Return the offset expression. + const Exp& index_get() const; + /// Return the offset expression. + Exp& index_get(); + /** \} */ + + protected: + /// The mother variable. + Var* var_; + /// The offset expression. + Exp* index_; + }; +} // namespace ast +#include <ast/subscript-var.hxx> diff --git a/tiger-compiler/src/ast/subscript-var.hxx b/tiger-compiler/src/ast/subscript-var.hxx new file mode 100644 index 0000000..5523240 --- /dev/null +++ b/tiger-compiler/src/ast/subscript-var.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/subscript-var.hxx + ** \brief Inline methods of ast::SubscriptVar. + */ + +#pragma once + +#include <ast/subscript-var.hh> + +namespace ast +{ + + inline const Var& SubscriptVar::var_get() const { return *var_; } + inline Var& SubscriptVar::var_get() { return *var_; } + + inline const Exp& SubscriptVar::index_get() const { return *index_; } + inline Exp& SubscriptVar::index_get() { return *index_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/tasks.cc b/tiger-compiler/src/ast/tasks.cc new file mode 100644 index 0000000..7ef95da --- /dev/null +++ b/tiger-compiler/src/ast/tasks.cc @@ -0,0 +1,32 @@ +/** + ** \file ast/tasks.cc + ** \brief Ast Tasks implementation. + */ + +#include <ast/libast.hh> +#include <misc/contract.hh> +#define DEFINE_TASKS 1 +#include <ast/tasks.hh> +#undef DEFINE_TASKS + +namespace ast::tasks +{ + // The abstract syntax tree. + std::unique_ptr<ast::ChunkList> the_program(nullptr); + + void ast_display() + { + // `the_program' should have been set by the parse module by now. + precondition(the_program) << "Could not dump the AST, root is null"; + std::cout << "/* == Abstract Syntax Tree. == */\n" + << *the_program << std::endl; + } + + void ast_dump() + { + // `the_program' should have been set by the parse module by now. + precondition(the_program) << "Could not dump the AST, root is null"; + ast::dump_dot(*the_program, std::cout); + } + +} // namespace ast::tasks diff --git a/tiger-compiler/src/ast/tasks.hh b/tiger-compiler/src/ast/tasks.hh new file mode 100644 index 0000000..490fc90 --- /dev/null +++ b/tiger-compiler/src/ast/tasks.hh @@ -0,0 +1,24 @@ +/** + ** \file ast/tasks.hh + ** \brief Ast module related tasks. + */ + +#pragma once + +#include <ast/chunk-list.hh> +#include <task/libtask.hh> + +namespace ast::tasks +{ + /// Global root node of abstract syntax tree. + extern std::unique_ptr<ast::ChunkList> the_program; + + TASK_GROUP("2. Abstract Syntax Tree"); + + /// Display the abstract syntax tree. + TASK_DECLARE("A|ast-display", "display the AST", ast_display, "parse"); + + /// Display the abstract syntax tree using a dumper. + TASK_DECLARE("ast-dump", "dump the AST", ast_dump, "parse"); + +} // namespace ast::tasks diff --git a/tiger-compiler/src/ast/ty.cc b/tiger-compiler/src/ast/ty.cc new file mode 100644 index 0000000..1b2f5d7 --- /dev/null +++ b/tiger-compiler/src/ast/ty.cc @@ -0,0 +1,17 @@ +/** + ** \file ast/ty.cc + ** \brief Implementation of ast::Ty. + */ + +#include <ast/ty.hh> +#include <ast/visitor.hh> + +namespace ast +{ + Ty::Ty(const Location& location) + : Ast(location) + , Typable() + , TypeConstructor() + {} + +} // namespace ast diff --git a/tiger-compiler/src/ast/ty.hh b/tiger-compiler/src/ast/ty.hh new file mode 100644 index 0000000..847713b --- /dev/null +++ b/tiger-compiler/src/ast/ty.hh @@ -0,0 +1,39 @@ +/** + ** \file ast/ty.hh + ** \brief Declaration of ast::Ty. + */ + +#pragma once + +#include <ast/ast.hh> +#include <ast/typable.hh> +#include <ast/type-constructor.hh> + +namespace ast +{ + /// Ty. + class Ty + : public Ast + , public Typable + , public TypeConstructor + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a Ty node. + explicit Ty(const Location& location); + Ty(const Ty&) = delete; + Ty& operator=(const Ty&) = delete; + /// Destroy a Ty node. + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override = 0; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override = 0; + /// \} + }; +} // namespace ast +#include <ast/ty.hxx> diff --git a/tiger-compiler/src/ast/ty.hxx b/tiger-compiler/src/ast/ty.hxx new file mode 100644 index 0000000..8bce3c5 --- /dev/null +++ b/tiger-compiler/src/ast/ty.hxx @@ -0,0 +1,11 @@ +/** + ** \file ast/ty.hxx + ** \brief Inline methods of ast::Ty. + */ + +#pragma once + +#include <ast/ty.hh> + +namespace ast +{} // namespace ast diff --git a/tiger-compiler/src/ast/typable.cc b/tiger-compiler/src/ast/typable.cc new file mode 100644 index 0000000..889aa4b --- /dev/null +++ b/tiger-compiler/src/ast/typable.cc @@ -0,0 +1,15 @@ +/** + ** \file ast/typable.cc + ** \brief Implementation of ast::Typable. + */ + +#include <ast/typable.hh> +#include <ast/visitor.hh> + +namespace ast +{ + // FIXME DONE: Some code was deleted here. + Typable::Typable() + : type_(nullptr) + {} +} // namespace ast diff --git a/tiger-compiler/src/ast/typable.hh b/tiger-compiler/src/ast/typable.hh new file mode 100644 index 0000000..50c004e --- /dev/null +++ b/tiger-compiler/src/ast/typable.hh @@ -0,0 +1,50 @@ +/** + ** \file ast/typable.hh + ** \brief Declaration of ast::Typable. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <type/fwd.hh> + +namespace ast +{ + /** \class ast::Typable + ** \brief Hold a type information. + ** + ** A Typable node holds a type information (type::Type) about + ** this node. This can be: + ** \li the type of the node itself, if it is a Exp or a Ty, or + ** \li the type of of the declared object, in case of a Dec. + */ + + class Typable + { + // FIXME DONE: Some code was deleted here. + + public: + // Only need for basic constructor and destructor here + Typable(); + virtual ~Typable() = default; + + /* We prohibit copy and assignment as to avoid having + ** multiple Typable referencing the same Type which could + ** potentially mess up the TypeConstructor + */ + Typable(const Typable&) = delete; + Typable& operator=(const Typable&) = delete; + + // Basic getters/setters + void type_set(const type::Type*); + const type::Type* type_get() const; + + // Making sure we can visit thoses "nodes" + virtual void accept(ConstVisitor& v) const = 0; + virtual void accept(Visitor& v) = 0; + + private: + const type::Type* type_; + }; +} // namespace ast +#include <ast/typable.hxx> diff --git a/tiger-compiler/src/ast/typable.hxx b/tiger-compiler/src/ast/typable.hxx new file mode 100644 index 0000000..3af401a --- /dev/null +++ b/tiger-compiler/src/ast/typable.hxx @@ -0,0 +1,24 @@ +/** + ** \file ast/typable.hxx + ** \brief Inline methods of ast::Typable. + */ + +#pragma once + +#include <ast/typable.hh> + +namespace ast +{ + // FIXME DONE: Some code was deleted here. + + inline void Typable::type_set(const type::Type* typ) + { + // Explicitely casting it? + type_ = typ; + } + + inline const type::Type* Typable::type_get() const + { + return type_; + } +} // namespace ast diff --git a/tiger-compiler/src/ast/type-constructor.cc b/tiger-compiler/src/ast/type-constructor.cc new file mode 100644 index 0000000..7786864 --- /dev/null +++ b/tiger-compiler/src/ast/type-constructor.cc @@ -0,0 +1,31 @@ +/** + ** \file ast/type-constructor.cc + ** \brief Implementation of ast::TypeConstructor. + */ + +#include <ast/type-constructor.hh> +#include <ast/visitor.hh> + +namespace ast +{ + // FIXME DONE: Some code was deleted here. + TypeConstructor::TypeConstructor() + : reference_(nullptr) + {} + + TypeConstructor::~TypeConstructor() + { + // IF SOME MEMORY ERRORS HAPPENS, THIS IS THE CULPRIT + delete reference_; + } + + void TypeConstructor::accept(ConstVisitor& v) const + { + v(this); + } + + void TypeConstructor::accept(Visitor& v) + { + v(this); + } +} // namespace ast diff --git a/tiger-compiler/src/ast/type-constructor.hh b/tiger-compiler/src/ast/type-constructor.hh new file mode 100644 index 0000000..87f57d8 --- /dev/null +++ b/tiger-compiler/src/ast/type-constructor.hh @@ -0,0 +1,38 @@ +/** + ** \file ast/type-constructor.hh + ** \brief Declaration of ast::TypeConstructor. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <type/fwd.hh> + +namespace ast +{ + /** \class ast::TypeConstructor + ** \brief Create a new type. + */ + + class TypeConstructor + { + // FIXME DONE: Some code was deleted here. + public: + // Basic default constructor + // BUT we have a custom destructor this time (daring today aren't we?) + TypeConstructor(); + ~TypeConstructor(); + + // Basic accessors + void created_type_set(const type::Type*); + const type::Type* created_type_get() const; + + // Implementing the visitor so this can also be visited + void accept(ConstVisitor& v) const; + void accept(Visitor& v); + + private: + const type::Type* reference_; + }; +} // namespace ast +#include <ast/type-constructor.hxx> diff --git a/tiger-compiler/src/ast/type-constructor.hxx b/tiger-compiler/src/ast/type-constructor.hxx new file mode 100644 index 0000000..86933f3 --- /dev/null +++ b/tiger-compiler/src/ast/type-constructor.hxx @@ -0,0 +1,23 @@ +/** + ** \file ast/type-constructor.hxx + ** \brief Inline methods of ast::TypeConstructor. + */ + +#pragma once + +#include <ast/type-constructor.hh> +#include <type/types.hh> + +namespace ast +{ + // FIXME DONE: Some code was deleted here. + inline void TypeConstructor::created_type_set(const type::Type* typ) + { + reference_ = typ; + } + + inline const type::Type* TypeConstructor::created_type_get() const + { + return reference_; + } +} // namespace ast diff --git a/tiger-compiler/src/ast/type-dec.cc b/tiger-compiler/src/ast/type-dec.cc new file mode 100644 index 0000000..336c42f --- /dev/null +++ b/tiger-compiler/src/ast/type-dec.cc @@ -0,0 +1,22 @@ +/** + ** \file ast/type-dec.cc + ** \brief Implementation of ast::TypeDec. + */ + +#include <ast/type-dec.hh> +#include <ast/visitor.hh> + +namespace ast +{ + TypeDec::TypeDec(const Location& location, misc::symbol name, Ty* ty) + : Dec(location, name) + , TypeConstructor() + , ty_(ty) + {} + + TypeDec::~TypeDec() { delete ty_; } + + void TypeDec::accept(ConstVisitor& v) const { v(*this); } + + void TypeDec::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/type-dec.hh b/tiger-compiler/src/ast/type-dec.hh new file mode 100644 index 0000000..0e19926 --- /dev/null +++ b/tiger-compiler/src/ast/type-dec.hh @@ -0,0 +1,51 @@ +/** + ** \file ast/type-dec.hh + ** \brief Declaration of ast::TypeDec. + */ + +#pragma once + +#include <ast/dec.hh> +#include <ast/ty.hh> +#include <ast/type-constructor.hh> + +namespace ast +{ + /// TypeDec. + class TypeDec + : public Dec + , public TypeConstructor + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a TypeDec node. + TypeDec(const Location& location, misc::symbol name, Ty* ty); + TypeDec(const TypeDec&) = delete; + TypeDec& operator=(const TypeDec&) = delete; + /// Destroy a TypeDec node. + ~TypeDec() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return type definition. + const Ty& ty_get() const; + /// Return type definition. + Ty& ty_get(); + /** \} */ + + protected: + /// Type definition. + Ty* ty_; + }; +} // namespace ast +#include <ast/type-dec.hxx> diff --git a/tiger-compiler/src/ast/type-dec.hxx b/tiger-compiler/src/ast/type-dec.hxx new file mode 100644 index 0000000..69dc086 --- /dev/null +++ b/tiger-compiler/src/ast/type-dec.hxx @@ -0,0 +1,16 @@ +/** + ** \file ast/type-dec.hxx + ** \brief Inline methods of ast::TypeDec. + */ + +#pragma once + +#include <ast/type-dec.hh> + +namespace ast +{ + + inline const Ty& TypeDec::ty_get() const { return *ty_; } + inline Ty& TypeDec::ty_get() { return *ty_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/var-dec.cc b/tiger-compiler/src/ast/var-dec.cc new file mode 100644 index 0000000..85e7eba --- /dev/null +++ b/tiger-compiler/src/ast/var-dec.cc @@ -0,0 +1,31 @@ +/** + ** \file ast/var-dec.cc + ** \brief Implementation of ast::VarDec. + */ + +#include <ast/var-dec.hh> +#include <ast/visitor.hh> + +namespace ast +{ + + VarDec::VarDec(const Location& location, + misc::symbol name, + NameTy* type_name, + Exp* init) + : Dec(location, name) + , Escapable() + , type_name_(type_name) + , init_(init) + {} + + VarDec::~VarDec() + { + delete type_name_; + delete init_; + } + + void VarDec::accept(ConstVisitor& v) const { v(*this); } + + void VarDec::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/var-dec.hh b/tiger-compiler/src/ast/var-dec.hh new file mode 100644 index 0000000..fd15b7e --- /dev/null +++ b/tiger-compiler/src/ast/var-dec.hh @@ -0,0 +1,61 @@ +/** + ** \file ast/var-dec.hh + ** \brief Declaration of ast::VarDec. + */ + +#pragma once + +#include <ast/dec.hh> +#include <ast/escapable.hh> +#include <ast/exp.hh> +#include <ast/name-ty.hh> + +namespace ast +{ + /// VarDec. + class VarDec + : public Dec + , public Escapable + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a VarDec node. + VarDec(const Location& location, + misc::symbol name, + NameTy* type_name, + Exp* init); + VarDec(const VarDec&) = delete; + VarDec& operator=(const VarDec&) = delete; + /// Destroy a VarDec node. + ~VarDec() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return optional type of the declared variable. + const NameTy* type_name_get() const; + /// Return optional type of the declared variable. + NameTy* type_name_get(); + /// Return the initial value (expression) assigned to the variable. + const Exp* init_get() const; + /// Return the initial value (expression) assigned to the variable. + Exp* init_get(); + /** \} */ + + protected: + /// Optional type of the declared variable. + NameTy* type_name_; + /// The initial value (expression) assigned to the variable. + Exp* init_; + }; +} // namespace ast +#include <ast/var-dec.hxx> diff --git a/tiger-compiler/src/ast/var-dec.hxx b/tiger-compiler/src/ast/var-dec.hxx new file mode 100644 index 0000000..c96c3f4 --- /dev/null +++ b/tiger-compiler/src/ast/var-dec.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/var-dec.hxx + ** \brief Inline methods of ast::VarDec. + */ + +#pragma once + +#include <ast/var-dec.hh> + +namespace ast +{ + + inline const NameTy* VarDec::type_name_get() const { return type_name_; } + inline NameTy* VarDec::type_name_get() { return type_name_; } + + inline const Exp* VarDec::init_get() const { return init_; } + inline Exp* VarDec::init_get() { return init_; } + +} // namespace ast diff --git a/tiger-compiler/src/ast/var.cc b/tiger-compiler/src/ast/var.cc new file mode 100644 index 0000000..6bea70c --- /dev/null +++ b/tiger-compiler/src/ast/var.cc @@ -0,0 +1,15 @@ +/** + ** \file ast/var.cc + ** \brief Implementation of ast::Var. + */ + +#include <ast/var.hh> +#include <ast/visitor.hh> + +namespace ast +{ + Var::Var(const Location& location) + : Exp(location) + {} + +} // namespace ast diff --git a/tiger-compiler/src/ast/var.hh b/tiger-compiler/src/ast/var.hh new file mode 100644 index 0000000..286e392 --- /dev/null +++ b/tiger-compiler/src/ast/var.hh @@ -0,0 +1,26 @@ +/** + ** \file ast/var.hh + ** \brief Declaration of ast::Var. + */ + +#pragma once + +#include <ast/exp.hh> + +namespace ast +{ + /// Var. + class Var : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a Var node. + explicit Var(const Location& location); + Var(const Var&) = delete; + Var& operator=(const Var&) = delete; + /// Destroy a Var node. + /** \} */ + }; +} // namespace ast +#include <ast/var.hxx> diff --git a/tiger-compiler/src/ast/var.hxx b/tiger-compiler/src/ast/var.hxx new file mode 100644 index 0000000..1f54528 --- /dev/null +++ b/tiger-compiler/src/ast/var.hxx @@ -0,0 +1,11 @@ +/** + ** \file ast/var.hxx + ** \brief Inline methods of ast::Var. + */ + +#pragma once + +#include <ast/var.hh> + +namespace ast +{} // namespace ast diff --git a/tiger-compiler/src/ast/visitor.hh b/tiger-compiler/src/ast/visitor.hh new file mode 100644 index 0000000..420db00 --- /dev/null +++ b/tiger-compiler/src/ast/visitor.hh @@ -0,0 +1,114 @@ +/** + ** \file ast/visitor.hh + ** \brief Definition of ast::Visitor. + */ + +#pragma once + +#include <functional> +#include <ast/fwd.hh> +#include <misc/select-const.hh> + +namespace ast +{ + /** \brief Root class of all Ast visitors. + ** + ** GenVisitor<CONSTIFY> is the root class of all Ast visitors. */ + template <template <typename> class Const> class GenVisitor + { + /** \name Ctor & dtor. + ** \{ */ + public: + /// Convenient abbreviation. + template <typename Type> using const_t = typename Const<Type>::type; + + /// Destroy a GenVisitor. + virtual ~GenVisitor(); + /** \} */ + + /// The entry point: visit \a e. + virtual void operator()(const_t<Ast>& e); + virtual void operator()(const_t<ArrayExp>&) = 0; + virtual void operator()(const_t<ArrayTy>&) = 0; + virtual void operator()(const_t<AssignExp>&) = 0; + virtual void operator()(const_t<BreakExp>&) = 0; + virtual void operator()(const_t<CallExp>&) = 0; + virtual void operator()(const_t<CastExp>&) = 0; + virtual void operator()(const_t<ChunkList>&) = 0; + virtual void operator()(const_t<ClassTy>&) = 0; + virtual void operator()(const_t<Field>&) = 0; + virtual void operator()(const_t<FieldInit>&) = 0; + virtual void operator()(const_t<FieldVar>&) = 0; + virtual void operator()(const_t<ForExp>&) = 0; + virtual void operator()(const_t<FunctionDec>&) = 0; + virtual void operator()(const_t<IfExp>&) = 0; + virtual void operator()(const_t<IntExp>&) = 0; + virtual void operator()(const_t<LetExp>&) = 0; + virtual void operator()(const_t<MethodCallExp>&) = 0; + virtual void operator()(const_t<MethodDec>&) = 0; + virtual void operator()(const_t<NameTy>&) = 0; + virtual void operator()(const_t<NilExp>&) = 0; + virtual void operator()(const_t<ObjectExp>&) = 0; + virtual void operator()(const_t<OpExp>&) = 0; + virtual void operator()(const_t<RecordExp>&) = 0; + virtual void operator()(const_t<RecordTy>&) = 0; + virtual void operator()(const_t<SeqExp>&) = 0; + virtual void operator()(const_t<SimpleVar>&) = 0; + virtual void operator()(const_t<StringExp>&) = 0; + virtual void operator()(const_t<SubscriptVar>&) = 0; + virtual void operator()(const_t<TypeDec>&) = 0; + virtual void operator()(const_t<VarDec>&) = 0; + virtual void operator()(const_t<WhileExp>&) = 0; + + virtual void operator()(const_t<FunctionChunk>&) = 0; + virtual void operator()(const_t<MethodChunk>&) = 0; + virtual void operator()(const_t<TypeChunk>&) = 0; + virtual void operator()(const_t<VarChunk>&) = 0; + + virtual void operator()(const_t<AssertExp>&) = 0; + + /// Helper to visit nodes manipulated via a pointer. + template <class E> void operator()(E* e); + + protected: + /** A convenient shortcut for recurring code like this: + + \code + if (e) + e->accept(*this); + \endcode + + However, the drawback of this approach is that it doesn't take + care of the constness, and any \a const violation will be + reported \em within the body of this method, not at its + corresponding call site. + + We cannot use the misc/select_const.hh approach here, since + the compiler cannot resolve a function overloaded or + specialized on an associated type of a template. E.g., writing + \a accept like this: + + \code + template <typename E> + void accept(const_t<E>* e); + \endcode + + won't work directly. Of course, one can help the compiler, + providing it with \a E + + \code + accept<ast::NameTy>(e.result_get()); + \endcode + + but this is painful. */ + template <typename E> void accept(E* e); + }; + + /// Shorthand for a const visitor. + using ConstVisitor = GenVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using Visitor = GenVisitor<misc::id_traits>; + +} // namespace ast + +#include <ast/visitor.hxx> diff --git a/tiger-compiler/src/ast/visitor.hxx b/tiger-compiler/src/ast/visitor.hxx new file mode 100644 index 0000000..2765f80 --- /dev/null +++ b/tiger-compiler/src/ast/visitor.hxx @@ -0,0 +1,36 @@ +/** + ** \file ast/visitor.hxx + ** \brief Definition of ast::Visitor. + */ + +#pragma once + +#include <ast/ast.hh> +#include <ast/visitor.hh> + +namespace ast +{ + template <template <typename> class Const> GenVisitor<Const>::~GenVisitor() {} + + template <template <typename> class Const> + void GenVisitor<Const>::operator()(const_t<ast::Ast>& e) + { + e.accept(*this); + } + + template <template <typename> class Const> + template <class E> + void GenVisitor<Const>::operator()(E* e) + { + e->accept(*this); + } + + template <template <typename> class Const> + template <typename E> + void GenVisitor<Const>::accept(E* e) + { + if (e) + e->accept(*this); + } + +} // namespace ast diff --git a/tiger-compiler/src/ast/while-exp.cc b/tiger-compiler/src/ast/while-exp.cc new file mode 100644 index 0000000..3ffa5e1 --- /dev/null +++ b/tiger-compiler/src/ast/while-exp.cc @@ -0,0 +1,26 @@ +/** + ** \file ast/while-exp.cc + ** \brief Implementation of ast::WhileExp. + */ + +#include <ast/visitor.hh> +#include <ast/while-exp.hh> + +namespace ast +{ + WhileExp::WhileExp(const Location& location, Exp* test, Exp* body) + : Exp(location) + , test_(test) + , body_(body) + {} + + WhileExp::~WhileExp() + { + delete test_; + delete body_; + } + + void WhileExp::accept(ConstVisitor& v) const { v(*this); } + + void WhileExp::accept(Visitor& v) { v(*this); } +} // namespace ast diff --git a/tiger-compiler/src/ast/while-exp.hh b/tiger-compiler/src/ast/while-exp.hh new file mode 100644 index 0000000..0f8a787 --- /dev/null +++ b/tiger-compiler/src/ast/while-exp.hh @@ -0,0 +1,53 @@ +/** + ** \file ast/while-exp.hh + ** \brief Declaration of ast::WhileExp. + */ + +#pragma once + +#include <ast/exp.hh> + +namespace ast +{ + /// WhileExp. + class WhileExp : public Exp + { + public: + /** \name Ctor & dtor. + ** \{ */ + /// Construct a WhileExp node. + WhileExp(const Location& location, Exp* test, Exp* body); + WhileExp(const WhileExp&) = delete; + WhileExp& operator=(const WhileExp&) = delete; + /// Destroy a WhileExp node. + ~WhileExp() override; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return exit condition of the loop. + const Exp& test_get() const; + /// Return exit condition of the loop. + Exp& test_get(); + /// Return instructions executed in the loop. + const Exp& body_get() const; + /// Return instructions executed in the loop. + Exp& body_get(); + /** \} */ + + protected: + /// Exit condition of the loop. + Exp* test_; + /// Instructions executed in the loop. + Exp* body_; + }; +} // namespace ast +#include <ast/while-exp.hxx> diff --git a/tiger-compiler/src/ast/while-exp.hxx b/tiger-compiler/src/ast/while-exp.hxx new file mode 100644 index 0000000..d6b9195 --- /dev/null +++ b/tiger-compiler/src/ast/while-exp.hxx @@ -0,0 +1,19 @@ +/** + ** \file ast/while-exp.hxx + ** \brief Inline methods of ast::WhileExp. + */ + +#pragma once + +#include <ast/while-exp.hh> + +namespace ast +{ + + inline const Exp& WhileExp::test_get() const { return *test_; } + inline Exp& WhileExp::test_get() { return *test_; } + + inline const Exp& WhileExp::body_get() const { return *body_; } + inline Exp& WhileExp::body_get() { return *body_; } + +} // namespace ast diff --git a/tiger-compiler/src/astclone/cloner.cc b/tiger-compiler/src/astclone/cloner.cc new file mode 100644 index 0000000..8e26fa7 --- /dev/null +++ b/tiger-compiler/src/astclone/cloner.cc @@ -0,0 +1,311 @@ +/** + ** \file astclone/cloner.cc + ** \brief Implementation of astclone::Cloner. + */ + +#include <ast/all.hh> +#include <astclone/cloner.hh> +#include <misc/symbol.hh> + +namespace astclone +{ + using namespace ast; + + Cloner::Cloner() + : result_(nullptr) + {} + + Ast* Cloner::result_get() { return result_; } + + void Cloner::operator()(const ast::ArrayExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& loc = e.location_get(); + NameTy* type_name = recurse(e.type_name_get()); + Exp* size = recurse(e.size_get()); + Exp* init = recurse(e.init_get()); + result_ = new ArrayExp(loc, type_name, size, init); + } + + void Cloner::operator()(const ast::ArrayTy& e) + { + const Location& location = e.location_get(); + NameTy* base_type = recurse(e.base_type_get()); + result_ = new ArrayTy(location, base_type); + } + + void Cloner::operator()(const ast::AssertExp& e) + { + const Location& location = e.location_get(); + Exp* cond = recurse(e.cond_get()); + result_ = new AssertExp(location, cond); + } + + void Cloner::operator()(const ast::AssignExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + Var* variable = recurse(e.var_get()); + Exp* value = recurse(e.exp_get()); + result_ = new AssignExp(location, variable, value); + } + + void Cloner::operator()(const ast::BreakExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + result_ = new BreakExp(location); + } + + void Cloner::operator()(const ast::CallExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + // Apparently, we DO have a default copy constructor for symbols... + misc::symbol id = e.name_get(); + exps_type* args = recurse_collection(e.args_get()); + result_ = new CallExp(location, id, args); + } + + void Cloner::operator()(const ast::CastExp& e) + { + const Location& location = e.location_get(); + Exp* exp = recurse(e.exp_get()); + Ty* ty = recurse(e.ty_get()); + result_ = new CastExp(location, exp, ty); + } + + void Cloner::operator()(const ast::ChunkList& e) + { + const Location& location = e.location_get(); + ChunkList::list_type chunks = *recurse_collection(e.chunks_get()); + result_ = new ChunkList(location, chunks); + } + + void Cloner::operator()(const ast::ClassTy& e) + { + const Location& location = e.location_get(); + NameTy* super = recurse(e.super_get()); + ChunkList* chunks = recurse(e.chunks_get()); + result_ = new ClassTy(location, super, chunks); + } + + void Cloner::operator()(const ast::Field& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + NameTy* type_name = recurse(e.type_name_get()); + result_ = new Field(location, name, type_name); + } + + void Cloner::operator()(const ast::FieldInit& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + Exp* init = recurse(e.init_get()); + result_ = new FieldInit(location, name, init); + } + + void Cloner::operator()(const ast::FieldVar& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + Var* variable = recurse(e.var_get()); + misc::symbol id = e.name_get(); + result_ = new FieldVar(location, variable, id); + } + + void Cloner::operator()(const ast::ForExp& e) + { + const Location& location = e.location_get(); + VarDec* vardec = recurse(e.vardec_get()); + Exp* hi = recurse(e.hi_get()); + Exp* body = recurse(e.body_get()); + result_ = new ForExp(location, vardec, hi, body); + } + + void Cloner::operator()(const ast::FunctionDec& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + VarChunk* formals = recurse(e.formals_get()); + NameTy* result = recurse(e.result_get()); + Exp* body = recurse(e.body_get()); + auto fundec = new FunctionDec(location, name, formals, result, body); + result_ = fundec; + } + + void Cloner::operator()(const ast::IfExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + Exp* cond = recurse(e.test_get()); + Exp* thenclause = recurse(e.thenclause_get()); + Exp* elseclause = recurse(e.elseclause_get()); + result_ = new IfExp(location, cond, thenclause, elseclause); + } + + void Cloner::operator()(const ast::IntExp& e) + { + const Location& location = e.location_get(); + int value = e.value_get(); + result_ = new IntExp(location, value); + } + + void Cloner::operator()(const ast::LetExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + Exp* body = recurse(e.body_get()); + ChunkList* chunky = recurse(e.chunks_get()); + result_ = new LetExp(location, chunky, body); + } + + void Cloner::operator()(const ast::MethodCallExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + misc::symbol id = e.name_get(); + exps_type* args = recurse_collection(e.args_get()); + Var* variable = recurse(e.object_get()); + result_ = new MethodCallExp(location, id, args, variable); + } + + void Cloner::operator()(const ast::MethodDec& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + VarChunk* formals = recurse(e.formals_get()); + NameTy* result = recurse(e.result_get()); + Exp* body = recurse(e.body_get()); + result_ = new MethodDec(location, name, formals, result, body); + } + + void Cloner::operator()(const ast::NameTy& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + result_ = new NameTy(location, name); + } + + void Cloner::operator()(const ast::NilExp& e) + { + const Location& location = e.location_get(); + result_ = new NilExp(location); + } + + void Cloner::operator()(const ast::ObjectExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + NameTy* name = recurse(e.type_name_get()); + result_ = new ObjectExp(location, name); + } + + void Cloner::operator()(const ast::OpExp& e) + { + const Location& location = e.location_get(); + Exp* left = recurse(e.left_get()); + OpExp::Oper oper = e.oper_get(); + Exp* right = recurse(e.right_get()); + result_ = new OpExp(location, left, oper, right); + } + + void Cloner::operator()(const ast::RecordExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + NameTy* name = recurse(e.type_name_get()); + fieldinits_type* fields = recurse_collection(e.fields_get()); + result_ = new RecordExp(location, name, fields); + } + + void Cloner::operator()(const ast::RecordTy& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + fields_type* fields = recurse_collection(e.fields_get()); + result_ = new RecordTy(location, fields); + } + + void Cloner::operator()(const ast::SeqExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + exps_type* types = recurse_collection(e.exps_get()); + result_ = new SeqExp(location, types); + } + + void Cloner::operator()(const ast::SimpleVar& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + result_ = new SimpleVar(location, name); + } + + void Cloner::operator()(const ast::StringExp& e) + { + // FIXME DONE: Some code was deleted here. + const Location& location = e.location_get(); + const std::string& txt = e.value_get(); + result_ = new StringExp(location, txt); + } + + void Cloner::operator()(const ast::SubscriptVar& e) + { + const Location& location = e.location_get(); + Var* var = recurse(e.var_get()); + Exp* index = recurse(e.index_get()); + result_ = new SubscriptVar(location, var, index); + } + + void Cloner::operator()(const ast::TypeDec& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + Ty* ty = recurse(e.ty_get()); + result_ = new TypeDec(location, name, ty); + } + + void Cloner::operator()(const ast::VarDec& e) + { + const Location& location = e.location_get(); + misc::symbol name = e.name_get(); + NameTy* type_name = recurse(e.type_name_get()); + Exp* init = recurse(e.init_get()); + // FIXME DONE: Some code was deleted here (Cloned node instantiation). + result_ = new VarDec(location, name, type_name, init); + } + + void Cloner::operator()(const ast::WhileExp& e) + { + const Location& location = e.location_get(); + Exp* test = recurse(e.test_get()); + Exp* body = recurse(e.body_get()); + result_ = new WhileExp(location, test, body); + } + + void Cloner::operator()(const ast::FunctionChunk& e) + { + chunk_visit<ast::FunctionChunk>(e); + } + + void Cloner::operator()(const ast::MethodChunk& e) + { + // FIXME DONE: Some code was deleted here. + chunk_visit<ast::MethodChunk>(e); + } + + void Cloner::operator()(const ast::TypeChunk& e) + { + // FIXME DONE: Some code was deleted here. + chunk_visit<ast::TypeChunk>(e); + } + + void Cloner::operator()(const ast::VarChunk& e) + { + // FIXME DONE: Some code was deleted here. + chunk_visit<ast::VarChunk>(e); + } + +} // namespace astclone diff --git a/tiger-compiler/src/astclone/cloner.hh b/tiger-compiler/src/astclone/cloner.hh new file mode 100644 index 0000000..f1405d1 --- /dev/null +++ b/tiger-compiler/src/astclone/cloner.hh @@ -0,0 +1,102 @@ +/** + ** \file astclone/cloner.hh + ** \brief Declaration of astclone::Cloner. + */ + +#pragma once + +#include <ast/default-visitor.hh> + +namespace astclone +{ + /// \brief Duplicate an Ast. + class Cloner : public ast::DefaultConstVisitor + { + public: + using super_type = ast::DefaultConstVisitor; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build a Cloner. + Cloner(); + + /// Destroy a Cloner. + ~Cloner() override = default; + + // Return the cloned Ast. + ast::Ast* result_get(); + + template <typename T> T* recurse(const T& t); + + template <typename T> T* recurse(const T* t); + + /** \brief Clone a collection object. + + Using overloading for this method is tempting, but it would + lead to the same prototype than the first \a recurse method. + + A partial specialization for \a std::list<T> would work, but is + not allowed by C++ standard. As a consequence, we are stuck to + using different names. + */ + template <typename CollectionType> + CollectionType* recurse_collection(const CollectionType& c); + + // Visit methods. + public: + void operator()(const ast::ArrayExp&) override; + void operator()(const ast::ArrayTy&) override; + void operator()(const ast::AssertExp&) override; + void operator()(const ast::AssignExp&) override; + void operator()(const ast::BreakExp&) override; + void operator()(const ast::CallExp&) override; + void operator()(const ast::CastExp&) override; + void operator()(const ast::ChunkList&) override; + void operator()(const ast::ClassTy&) override; + void operator()(const ast::Field&) override; + void operator()(const ast::FieldInit&) override; + void operator()(const ast::FieldVar&) override; + void operator()(const ast::ForExp&) override; + void operator()(const ast::FunctionDec&) override; + void operator()(const ast::IfExp&) override; + void operator()(const ast::IntExp&) override; + void operator()(const ast::LetExp&) override; + void operator()(const ast::MethodCallExp&) override; + void operator()(const ast::MethodDec&) override; + void operator()(const ast::NameTy&) override; + void operator()(const ast::NilExp&) override; + void operator()(const ast::ObjectExp&) override; + void operator()(const ast::OpExp&) override; + void operator()(const ast::RecordExp&) override; + void operator()(const ast::RecordTy&) override; + void operator()(const ast::SeqExp&) override; + void operator()(const ast::SimpleVar&) override; + void operator()(const ast::StringExp&) override; + void operator()(const ast::SubscriptVar&) override; + void operator()(const ast::TypeDec&) override; + void operator()(const ast::VarDec&) override; + void operator()(const ast::WhileExp&) override; + + template <typename ChunkType> + /** \brief Visit a chunk (i.e., a list of Function, Var, and Type declarations). + ** + ** It is exactly the same in the three cases, so the code is + ** factored via a template method. */ + void chunk_visit(const ChunkType& e); + + // As we can't mix template and virtual methods, we have to + // duplicate these methods. That's too bad. :( + void operator()(const ast::FunctionChunk&) override; + void operator()(const ast::MethodChunk&) override; + void operator()(const ast::TypeChunk&) override; + void operator()(const ast::VarChunk&) override; + + protected: + /// The cloned Ast. + ast::Ast* result_; + }; + +} // namespace astclone + +#include <astclone/cloner.hxx> diff --git a/tiger-compiler/src/astclone/cloner.hxx b/tiger-compiler/src/astclone/cloner.hxx new file mode 100644 index 0000000..9e7d161 --- /dev/null +++ b/tiger-compiler/src/astclone/cloner.hxx @@ -0,0 +1,72 @@ +/** + ** \file astclone/cloner.hxx + ** \brief Template methods of astclone::Cloner. + */ + +#pragma once + +#include <ast/libast.hh> +#include <astclone/cloner.hh> + +namespace astclone +{ + using namespace ast; + + template <typename T> T* Cloner::recurse(const T& t) + { + t.accept(*this); + T* res = dynamic_cast<T*>(result_); + assertion(res); + return res; + } + + template <typename T> T* Cloner::recurse(const T* const t) + { + T* res = nullptr; + if (t) + { + t->accept(*this); + res = dynamic_cast<T*>(result_); + assertion(res); + } + return res; + } + + template <typename CollectionType> + CollectionType* Cloner::recurse_collection(const CollectionType& c) + { + auto res = new CollectionType; + + using elt_type = typename CollectionType::value_type; + for (const elt_type& e : c) + { + e->accept(*this); + auto elt = dynamic_cast<elt_type>(result_); + assertion(elt); + res->emplace_back(elt); + } + + return res; + } + + template <typename ChunkType> void Cloner::chunk_visit(const ChunkType& e) + { + const Location& location = e.location_get(); + + // The type of the list contained by this node. + using elt_type = typename ChunkType::Ds; + // The cloned list of declarations. + auto decs = new elt_type; + + for (const typename elt_type::value_type& i : e) + { + i->accept(*this); + auto dec = dynamic_cast<typename elt_type::value_type>(result_); + assertion(dec); + decs->emplace_back(dec); + } + // The cloned ChunkInterface. + result_ = new ChunkType(location, decs); + } + +} // namespace astclone diff --git a/tiger-compiler/src/astclone/libastclone.hh b/tiger-compiler/src/astclone/libastclone.hh new file mode 100644 index 0000000..d62fea9 --- /dev/null +++ b/tiger-compiler/src/astclone/libastclone.hh @@ -0,0 +1,42 @@ +/** + ** \file astclone/libastclone.hh + ** \brief Declare functions and variables exported by the Astclone module. + */ + +#pragma once + +#include <memory> + +#include <ast/fwd.hh> + +/// Cloning an ast::Ast. +namespace astclone +{ + /** \brief Make a deep copy of an AST. + ** \param tree abstract syntax tree's root. + ** \return the cloned AST. */ + template <typename T> T* clone(const T& tree); + + template <typename A> using applicable = auto(const A&) -> A*; + + template <typename A> + using applicable_with_bools = auto(const A&, bool, bool) -> A*; + + template <typename A, typename B> + using applicable_object = auto(const A&, const B&) -> A*; + + /// Have the pure function \a f side effect on \a t. + template <typename A> void apply(applicable<A> f, std::unique_ptr<A>& t1); + + template <typename A> + void apply(applicable_with_bools<A> f, + std::unique_ptr<A>& t1, + bool cond_1, + bool cond_2); + + template <typename A, typename B> + void apply(applicable_object<A, B> f, std::unique_ptr<A>& t1, const B& t3); + +} // namespace astclone + +#include <astclone/libastclone.hxx> diff --git a/tiger-compiler/src/astclone/libastclone.hxx b/tiger-compiler/src/astclone/libastclone.hxx new file mode 100644 index 0000000..b6fe380 --- /dev/null +++ b/tiger-compiler/src/astclone/libastclone.hxx @@ -0,0 +1,40 @@ +#pragma once + +#include <ast/exp.hh> +#include <astclone/cloner.hh> +#include <astclone/libastclone.hh> + +// Define exported clone functions. +namespace astclone +{ + template <typename T> T* clone(const T& tree) + { + Cloner clone; + clone(tree); + return dynamic_cast<T*>(clone.result_get()); + } + + template <typename A> void apply(applicable<A> f, std::unique_ptr<A>& t1) + { + A* t2 = f(*t1); + t1.reset(t2); + } + + template <typename A> + void apply(applicable_with_bools<A> f, + std::unique_ptr<A>& t1, + bool cond_1, + bool cond_2) + { + A* t2 = f(*t1, cond_1, cond_2); + t1.reset(t2); + } + + template <typename A, typename B> + void apply(applicable_object<A, B> f, std::unique_ptr<A>& t1, B& t3) + { + A* t2 = f(*t1, t3); + t1.reset(t2); + } + +} // namespace astclone diff --git a/tiger-compiler/src/astclone/local.am b/tiger-compiler/src/astclone/local.am new file mode 100644 index 0000000..302667b --- /dev/null +++ b/tiger-compiler/src/astclone/local.am @@ -0,0 +1,12 @@ +## astclone module. + +src_libtc_la_SOURCES += \ + %D%/cloner.hh %D%/cloner.hxx %D%/cloner.cc \ + %D%/libastclone.hh %D%/libastclone.hxx + +check_PROGRAMS += %D%/test-cloner +%C%_test_cloner_LDADD = src/libtc.la +%C%_test_cloner_CPPFLAGS = $(AM_CPPFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" + + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/astclone/tasks.cc b/tiger-compiler/src/astclone/tasks.cc new file mode 100644 index 0000000..ae6d973 --- /dev/null +++ b/tiger-compiler/src/astclone/tasks.cc @@ -0,0 +1,26 @@ +/** + ** \file astclone/tasks.cc + ** \brief Astclone module related tasks' implementation. + **/ + +#include <ast/tasks.hh> + +#include <astclone/libastclone.hh> +#define DEFINE_TASKS 1 +#include <astclone/tasks.hh> +#undef DEFINE_TASKS + +#include <common.hh> + +namespace astclone::tasks +{ + void clone() + { + ast::ChunkList* ds = ::astclone::clone(*ast::tasks::the_program); + if (!ds) + task_error() << misc::error::error_type::failure << "Cloning Failed\n" + << &misc::error::exit; + ast::tasks::the_program.reset(ds); + } + +} // namespace astclone::tasks diff --git a/tiger-compiler/src/astclone/tasks.hh b/tiger-compiler/src/astclone/tasks.hh new file mode 100644 index 0000000..25b346b --- /dev/null +++ b/tiger-compiler/src/astclone/tasks.hh @@ -0,0 +1,20 @@ +/** + ** \file astclone/tasks.hh + ** \brief Astclone module tasks. + */ + +#pragma once + +#include <misc/fwd.hh> + +#include <task/libtask.hh> + +/// Tasks of the astclone module. +namespace astclone::tasks +{ + TASK_GROUP("2.5 Cloning"); + + /// Clone ast::tasks::the_program, and replace it with its copy. + TASK_DECLARE("clone", "clone the Ast", clone, "parse"); + +} // namespace astclone::tasks diff --git a/tiger-compiler/src/bind/binder.cc b/tiger-compiler/src/bind/binder.cc new file mode 100644 index 0000000..171e199 --- /dev/null +++ b/tiger-compiler/src/bind/binder.cc @@ -0,0 +1,183 @@ +/** + ** \file bind/binder.cc + ** \brief Implementation for bind/binder.hh. + */ + +#include <ast/all.hh> +#include <bind/binder.hh> + +#include <misc/contract.hh> + +namespace bind +{ + /*-----------------. + | Error handling. | + `-----------------*/ + + /// The error handler. + const misc::error& Binder::error_get() const { return error_; } + + // FIXME DONE: Some code was deleted here. + void Binder::operator()(ast::SimpleVar& e) + { + ast::VarDec* init = scoped_map_var_.get(e.name_get()); + if (init == nullptr) + { + err_undef(e.location_get(), e.name_get()); + return; + } + e.def_set(init); + } + + void Binder::operator()(ast::CallExp& e) + { + ast::FunctionDec* init = scoped_map_fun_.get(e.name_get()); + if (init == nullptr) + { + err_undef(e.location_get(), e.name_get()); + return; + } + e.def_set(init); + super_type::operator()(e); + } + + void Binder::operator()(ast::NameTy& e) + { + misc::symbol test_string("string"); + misc::symbol test_int("int"); + if (e.name_get() == test_int || e.name_get() == test_string) + { + auto init = scoped_map_ty_.get(e.name_get()); + e.def_set(init); + } + else + { + auto init = scoped_map_ty_.get(e.name_get()); + if (init == nullptr) + { + err_type_undef(e.location_get(), e.name_get()); + return; + } + e.def_set(init); + } + } + + void Binder::operator()(ast::WhileExp& e) + { + bool actual = in_a_while_; + e.test_get().accept(*this); + loop_.emplace_back(&e); + in_a_while_ = true; + begin_scope(); + e.body_get().accept(*this); + end_scope(); + loop_.pop_back(); + in_a_while_ = actual; + } + + void Binder::operator()(ast::ForExp& e) + { + bool actual = in_a_while_; + begin_scope(); + in_a_while_ = true; + e.vardec_get().accept(*this); + e.hi_get().accept(*this); + loop_.emplace_back(&e); + e.body_get().accept(*this); + end_scope(); + loop_.pop_back(); + in_a_while_ = actual; + } + + void Binder::operator()(ast::BreakExp& e) + { + if (loop_.empty() || !in_a_while_) + { + error_ << misc::error::error_type::bind; + error_ << e.location_get() << ": 'break' outside any loop\n"; + } + else + { + e.def_set(loop_.back()); + } + } + + //Declaration of function + + void Binder::operator()(ast::VarDec& e) + { + bool actual = in_a_while_; + scoped_map_var_.put(e.name_get(), &e); + in_a_while_ = false; + super_type::operator()(e); + in_a_while_ = actual; + } + + void Binder::operator()(ast::FunctionChunk& e) + { + chunk_visit<ast::FunctionDec>(e); + } + + void Binder::operator()(ast::TypeChunk& e) + { + chunk_visit<ast::TypeDec>(e); + } + + template <class D> void Binder::chunk_visit(ast::Chunk<D>& e) + { + misc::scoped_map<const misc::symbol, D*> def = misc::scoped_map<const misc::symbol, D*>(); + for (auto machala : e) + { + visit_dec_head<D>(*machala); + } + for (auto dec : e) + { + auto init = def.get(dec->name_get()); + if (init == nullptr) + { + def.put(dec->name_get(), dec); + visit_dec_bod<D>(*dec); + } + else + { + err_ddec(dec->location_get(), init->location_get(), dec->name_get()); + } + } + } + + void Binder::operator()(ast::FunctionDec& e) + { + if (e.name_get() == "_main") + { + if (is_main_already_) + { + error_ << misc::error::error_type::bind; + error_ << "Un deuxieme _main??????\n"; + return; + } + is_main_already_ = true; + if (in_a_scope_) + { + error_ << misc::error::error_type::bind; + error_ << "BRADDOCK, je vous préviens, attention ou vous mettez votre _main!!"; + return; + } + } + scoped_map_fun_.put(e.name_get(), &e); + bool scope = in_a_scope_; + bool actual = in_a_while_; + in_a_while_ = false; + in_a_scope_ = true; + this->accept(e.result_get()); + body_func(e); + in_a_while_ = actual; + in_a_scope_ = scope; + } + + void Binder::operator()(ast::TypeDec& e) + { + scoped_map_ty_.put(e.name_get(), &e); + super_type::operator()(e); + } + +} // namespace bind diff --git a/tiger-compiler/src/bind/binder.hh b/tiger-compiler/src/bind/binder.hh new file mode 100644 index 0000000..f5a4ab3 --- /dev/null +++ b/tiger-compiler/src/bind/binder.hh @@ -0,0 +1,138 @@ +/** + ** \file bind/binder.hh + ** \brief Declaration of bind::Binder. + **/ + +#pragma once + +#include <unordered_map> + +#include <ast/assert-visitor.hh> +#include <ast/default-visitor.hh> +#include <ast/object-visitor.hh> + +#include <misc/error.hh> +#include <misc/fwd.hh> +#include <misc/scoped-map.hh> + +namespace bind +{ + /** \brief Binding identifier uses to their definitions. + ** + ** When the \c Binder finds a declaration (of a variable/formal, function, + ** or type), it keeps a pointer to it. When it finds a use, it binds it + ** to its definition, i.e., it annotates it with a pointer to the + ** declaration. + ** + ** The \c Binder diagnoses identifier use errors (invalid multiple + ** definitions, unbound identifiers etc.). + ** + ** Since identifier bindings depend on scopes, it needs an environment. + ** + ** In the original Tiger by A. Appel, there are two namespaces: on + ** the one hand types, and on the other hand functions and variables. + ** Here, at EPITA, we will use three name spaces: we will allow + ** variables and functions with the same name. + ** + ** Moreover, object constructs make use of two additional name + ** spaces: one for class attributes and one for methods (actually + ** these two name spaces only live within the scope of a class). + ** + ** Note that this Binder is mainly doing nothing: it is just + ** interested in declarations and uses. To avoid writing + ** all the methods that `do nothing but walk', it derives + ** from \c ast::DefaultVisitor. + **/ + class Binder + : public ast::DefaultVisitor + , public ast::ObjectVisitor + , public ast::AssertVisitor + { + public: + /// Super class type. + using super_type = ast::DefaultVisitor; + /// Import all the overloaded \c operator() methods. + using super_type::operator(); + + /// The error handler. + const misc::error& error_get() const; + + // FIXME DONE: Some code was deleted here. + + void operator()(ast::SimpleVar& e) override; + void operator()(ast::CallExp& e) override; + void operator()(ast::WhileExp& e) override; + void operator()(ast::ForExp& e) override; + void operator()(ast::BreakExp&) override; + void operator()(ast::NameTy& e) override; + + // ---------------- // + // Visiting /Dec/. // + // ---------------- // + + /// Visit Var declarations. + void operator()(ast::VarDec& e) override; + + /// Visit Chunk + template <class D> void chunk_visit(ast::Chunk<D>& e); + + /// Visit Function declarations. + void operator()(ast::FunctionChunk& e) override; + void operator()(ast::FunctionDec& e) override; + /// Visit Type declarations. + void operator()(ast::TypeChunk& e) override; + void operator()(ast::TypeDec& e) override; + + Binder(); + + + /// \name Type and Function declarations + /// \{ + + /// When traversing a function (or a type) we both have to bind + /// its body (i.e., we need to enter a new scope and push the + /// arguments in it), *and* we have to store the function's + /// declaration in the current scope (so that other functions can + /// call it). + + /// We first introduce the function's name in the outer + /// environment so that the function can call itself recursively. + /// In the mean time, we also check for uniqueness. Then, as a + /// second step, we process the contents of all the functions + /// belonging to the current chunk. + + // FIXME DONE: Some code was deleted here. + //function a effect sur les scoped map + void body_func(ast::FunctionDec& e); + void begin_scope(); + void end_scope(); + template <class D> void visit_dec_bod(D& e); + template <class D> void visit_dec_head(D& e); + + + //function that handles error + void err_undef(const ast::Location& loc, const misc::symbol& name); + void err_type_undef(const ast::Location& loc, const misc::symbol& name); + void err_ddec(const ast::Location& loc, + const ast::Location& first, + const misc::symbol& name); + void is_there__main(void); + + protected: + /// Binding errors handler. + misc::error error_; + + // FIXME DONE: Some code was deleted here (More members). + // J'ai copier pretty print en sah + template <typename Type> using t = typename Type::type; + misc::scoped_map<const misc::symbol, ast::VarDec*> scoped_map_var_; + misc::scoped_map<const misc::symbol, ast::TypeDec*> scoped_map_ty_; + misc::scoped_map<const misc::symbol, ast::FunctionDec*> scoped_map_fun_; + std::vector<ast::Exp*> loop_; + bool is_main_already_ = false; + bool in_a_while_ = false; + bool in_a_scope_ = false; + }; +} // namespace bind + +#include <bind/binder.hxx> diff --git a/tiger-compiler/src/bind/binder.hxx b/tiger-compiler/src/bind/binder.hxx new file mode 100644 index 0000000..0facad9 --- /dev/null +++ b/tiger-compiler/src/bind/binder.hxx @@ -0,0 +1,124 @@ +/** + ** \file bind/binder.hxx + ** \brief Inline methods of bind::Binder. + **/ + +// FIXME DONE: Some code was deleted here. + +#pragma once + +#include <bind/binder.hh> + +namespace bind +{ + inline Binder::Binder() + { + scoped_map_fun_ = misc::scoped_map<const misc::symbol, ast::FunctionDec*>(); + scoped_map_var_ = misc::scoped_map<const misc::symbol, ast::VarDec*>(); + scoped_map_ty_ = misc::scoped_map<const misc::symbol, ast::TypeDec*>(); + loop_ = std::vector<ast::Exp*>(); + error_ = misc::error(); + } + + inline void Binder::body_func(ast::FunctionDec& e) + { + begin_scope(); + bool tmp = in_a_scope_; + in_a_scope_ = true; + e.formals_get().accept(*this); + this->accept(e.body_get()); + in_a_scope_ = tmp; + end_scope(); + } + + template <> + inline void Binder::visit_dec_head<ast::FunctionDec>(ast::FunctionDec& e) + { + if (e.name_get() == "_main") + { + if (is_main_already_) + { + error_ << misc::error::error_type::bind; + error_ << "Un deuxieme _main??????\n"; + return; + } + is_main_already_ = true; + if (in_a_scope_) + { + error_ << misc::error::error_type::bind; + error_ << "BRADDOCK, je vous préviens, attention ou vous mettez votre _main!!"; + return; + } + } + scoped_map_fun_.put(e.name_get(), &e); + } + + template <> + inline void Binder::visit_dec_bod<ast::FunctionDec>(ast::FunctionDec& e) + { + bool actual = in_a_while_; + in_a_while_ = false; + this->accept(e.result_get()); + body_func(e); + in_a_while_ = actual; + } + + /// @brief visit header so the Ast can know this type exist + /// @param e an Dec of a funcrion or ast + template <> + inline void Binder::visit_dec_head<ast::TypeDec>(ast::TypeDec& e) + { + scoped_map_ty_.put(e.name_get(), &e); + } + + template <> + inline void Binder::visit_dec_bod<ast::TypeDec>(ast::TypeDec& e) + { + super_type::operator()(e); + } + + inline void Binder::begin_scope() + { + scoped_map_fun_.scope_begin(); + scoped_map_ty_.scope_begin(); + scoped_map_var_.scope_begin(); + } + + inline void Binder::end_scope() + { + scoped_map_fun_.scope_end(); + scoped_map_ty_.scope_end(); + scoped_map_var_.scope_end(); + } + + inline void Binder::err_undef(const ast::Location& loc, const misc::symbol& name) + { + error_ << misc::error::error_type::bind; + error_ << loc << ": undeclared variable: " << name << "\n"; + } + + inline void Binder::err_type_undef(const ast::Location& loc, const misc::symbol& name) + { + error_ << misc::error::error_type::bind; + error_ << loc << ": undeclared type: " << name << "\n"; + } + + inline void Binder::is_there__main() + { + if (!is_main_already_) + { + error_ << misc::error::error_type::bind; + error_ << "Bah bro il est ou ton _main??????\n"; + } + } + + inline void Binder::err_ddec(const ast::Location& loc, + const ast::Location& first, + const misc::symbol& name) + { + error_ << misc::error::error_type::bind; + error_ << loc << ": redefinition: " << name << "\n"; + error_ << first << ": first definition\n"; + } + +} // namespace bind
\ No newline at end of file diff --git a/tiger-compiler/src/bind/libbind.cc b/tiger-compiler/src/bind/libbind.cc new file mode 100644 index 0000000..63dc4ee --- /dev/null +++ b/tiger-compiler/src/bind/libbind.cc @@ -0,0 +1,31 @@ +/** + ** \file bind/libbind.cc + ** \brief Define exported bind functions. + */ + + +// FIXME DONE: Some code was deleted here. + +#include <ast/ast.hh> +#include <misc/error.hh> + +#include "renamer.hh" +#include "binder.hh" + +namespace bind +{ + misc::error bind(ast::ChunkList* d) + { + Binder bdc = Binder(); + bdc(d); + bdc.is_there__main(); + return bdc.error_get(); + } + + misc::error rename(ast::Ast& tree) + { + Renamer renamer; + renamer(tree); + return misc::error{}; + } +} diff --git a/tiger-compiler/src/bind/libbind.hh b/tiger-compiler/src/bind/libbind.hh new file mode 100644 index 0000000..f4b8bb5 --- /dev/null +++ b/tiger-compiler/src/bind/libbind.hh @@ -0,0 +1,29 @@ +/** +** \file bind/libbind.hh + ** \brief Interface of the bind module. + */ + +// FIXME DONE: Some code was deleted here. + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> +#include <bind/binder.hh> + +namespace bind +{ + /// \brief Bind the whole ast in place, return the error code + /// + /// \param last the ast you want to bind + /// + /// \return a misc::error that serve to indicate possible failure + misc::error bind(ast::ChunkList* d); + + /// \brief Rename the whole ast in place + /// + /// \param ast the ast you want to rename + /// + /// \return a misc::error that serve to indicate possible failure + misc::error rename(ast::Ast& ast); +} // namespace bind diff --git a/tiger-compiler/src/bind/local.am b/tiger-compiler/src/bind/local.am new file mode 100644 index 0000000..ae4d7b4 --- /dev/null +++ b/tiger-compiler/src/bind/local.am @@ -0,0 +1,17 @@ +## bind module. + +src_libtc_la_SOURCES += \ + %D%/binder.hh %D%/binder.hxx %D%/binder.cc \ + %D%/libbind.hh %D%/libbind.cc +src_libtc_la_SOURCES += \ + %D%/renamer.hh %D%/renamer.hxx %D%/renamer.cc + + +TASKS += %D%/tasks.hh %D%/tasks.cc + +## ------- ## +## Tests. ## +## ------- ## + +check_PROGRAMS += %D%/test-bind +%C%_test_bind_LDADD = src/libtc.la diff --git a/tiger-compiler/src/bind/renamer.cc b/tiger-compiler/src/bind/renamer.cc new file mode 100644 index 0000000..1f13de0 --- /dev/null +++ b/tiger-compiler/src/bind/renamer.cc @@ -0,0 +1,63 @@ +/** + ** \file bind/renamer.cc + ** \brief Implementation of bind::Renamer. + */ + +#include <bind/renamer.hh> + +namespace bind +{ + using namespace ast; + + // FIXME DONE: Some code was deleted here. + + void Renamer::operator()(ast::VarDec& e) + { + if (renames_.find(&e) != renames_.end()) + super_type::operator()(e); + new_name_(&e); + e.name_set(renames_.at(&e)); + super_type::operator()(e); + } + void Renamer::operator()(ast::TypeDec& e) + { + if (renames_.find(&e) != renames_.end()) + super_type::operator()(e); + new_name_(&e); + e.name_set(renames_.at(&e)); + super_type::operator()(e); + } + void Renamer::operator()(ast::FunctionDec& e) + { + if (e.body_get() == nullptr || e.name_get() == "_main" + || renames_.find(&e) != renames_.end()) + { + super_type::operator()(e); + return; + } + new_name_(&e); + e.name_set(renames_.at(&e)); + super_type::operator()(e); + } + void Renamer::operator()(ast::SimpleVar& e) + { + if (renames_.find(e.def_get()) != renames_.end()) + e.name_set(renames_.at(e.def_get())); + } + void Renamer::operator()(ast::NameTy& e) + { + if (renames_.find(e.def_get()) != renames_.end()) + e.name_set(renames_.at(e.def_get())); + } + void Renamer::operator()(ast::CallExp& e) + { + if (e.def_get() == nullptr || e.def_get()->body_get() == nullptr + || e.name_get() == "_main") + super_type::operator()(e); + else + { + e.name_set(renames_.at(e.def_get())); + super_type::operator()(e); + } + } +} // namespace bind diff --git a/tiger-compiler/src/bind/renamer.hh b/tiger-compiler/src/bind/renamer.hh new file mode 100644 index 0000000..f4a1e82 --- /dev/null +++ b/tiger-compiler/src/bind/renamer.hh @@ -0,0 +1,62 @@ +/** + ** \file bind/renamer.hh + ** \brief Implementation of bind::Renamer. + */ + +#pragma once + +#include <map> +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> + +namespace bind +{ + /// Perform identifier renaming within an AST (in place), + /// without support for objects. + class Renamer + : public ast::DefaultVisitor + , public ast::NonObjectVisitor + , public ast::NonAssertVisitor + { + public: + using super_type = ast::DefaultVisitor; + + // Import overloaded virtual functions. + using super_type::operator(); + + // FIXME DONE: Some code was deleted here. + + // Visit methods. + /// \brief Process a declaration body or a usage site. + /// + /// \a def is the definition site of \e (must be equal to + /// \a e if it is a Dec node). + template <class E, class Def> void visit(E& e, const Def* def); + + /// \name Visiting definition sites. + /// \{ + // FIXME DONE: Some code was deleted here. + void operator()(ast::VarDec& e) override; + void operator()(ast::TypeDec& e) override; + void operator()(ast::FunctionDec& e) override; + /// \} + + /// \name Visiting usage sites. + /// \{ + // FIXME DONE: Some code was deleted here. + void operator()(ast::SimpleVar& e) override; + void operator()(ast::NameTy& e) override; + void operator()(ast::CallExp& e) override; + /// \} + + private: + // FIXME DONE: Some code was deleted here. + std::map<const ast::Dec*, misc::symbol> renames_; + + void new_name_(const ast::Dec*); + }; + +} // namespace bind + +#include <bind/renamer.hxx> diff --git a/tiger-compiler/src/bind/renamer.hxx b/tiger-compiler/src/bind/renamer.hxx new file mode 100644 index 0000000..f01b7c8 --- /dev/null +++ b/tiger-compiler/src/bind/renamer.hxx @@ -0,0 +1,24 @@ +/** + ** \file bind/renamer.hxx + ** \brief Template methods of bind::Renamer. + */ + +#pragma once + +#include <sstream> +#include <bind/renamer.hh> + +namespace bind +{ + // FIXME DONE: Some code was deleted here. + inline void Renamer::new_name_(const ast::Dec* dec) + { + renames_.insert_or_assign(dec, misc::symbol::fresh(dec->name_get())); + } + + template <class E, class Def> void Renamer::visit(E& e, const Def* def) + { + // FIXME DONE: Some code was deleted here. + operator()(e); + } +} // namespace bind diff --git a/tiger-compiler/src/bind/tasks.cc b/tiger-compiler/src/bind/tasks.cc new file mode 100644 index 0000000..6492463 --- /dev/null +++ b/tiger-compiler/src/bind/tasks.cc @@ -0,0 +1,34 @@ +/** + ** \file bind/tasks.cc + ** \brief Bind module tasks implementation. + */ + +// FIXME DONE: Some code was deleted here. +#include <ast/libast.hh> +#include <ast/tasks.hh> +#include <bind/libbind.hh> +#define DEFINE_TASKS 1 +#include <bind/tasks.hh> +#undef DEFINE_TASKS +#include <common.hh> + +namespace bind::tasks +{ + + void bind() { + misc::error& program_error = task_error(); + program_error << bind::bind(ast::tasks::the_program.get()); + program_error.exit_on_error(); + } + + void display_bind() { + ast::bindings_display(std::cout) = true; + } + + void rename() { + misc::error& program_error = task_error(); + program_error << bind::rename(*ast::tasks::the_program); + program_error.exit_on_error(); + } + +} diff --git a/tiger-compiler/src/bind/tasks.hh b/tiger-compiler/src/bind/tasks.hh new file mode 100644 index 0000000..5ad970d --- /dev/null +++ b/tiger-compiler/src/bind/tasks.hh @@ -0,0 +1,42 @@ +/** + ** \file bind/tasks.hh + ** \brief Bind module related tasks. + */ + +// FIXME DONE: Some code was deleted here. +#pragma once + +#include <task/libtask.hh> + +namespace bind::tasks +{ + TASK_GROUP("4. Binding"); + + // Binding tasks + DISJUNCTIVE_TASK_DECLARE("bound", + "default the binding to Tiger " + "(without objects nor overloading)", + "bindings-compute" + " combine-bindings-compute" + " object-bindings-compute" + " assert-bindings-compute"); + + TASK_DECLARE("b|bindings-compute", + "bind the name uses to their definitions", + bind, + "parse"); + + TASK_DECLARE("B|bindings-display", + "enable the bindings display in the next --ast-display " + "invocation. does not imply --bindings-compute", + display_bind, + ""); + + // Renaming tasks + + TASK_DECLARE("rename", + "rename identifiers", + rename, + "bindings-compute"); + +} diff --git a/tiger-compiler/src/callgraph/call-graph-visitor.cc b/tiger-compiler/src/callgraph/call-graph-visitor.cc new file mode 100644 index 0000000..6ad31c5 --- /dev/null +++ b/tiger-compiler/src/callgraph/call-graph-visitor.cc @@ -0,0 +1,56 @@ +/** + ** \file callgraph/call-graph-visitor.cc + ** \brief Implementation of callgraph::CallGraphVisitor. + **/ + +#include <ast/call-exp.hh> +#include <ast/function-dec.hh> +#include <callgraph/call-graph-visitor.hh> + +namespace callgraph +{ + const CallGraph* CallGraphVisitor::create(const ast::Ast& tree) + { + // Create a new empty callgraph + callgraph_ = new CallGraph(); + + // Launch visitor. + tree.accept(*this); + + // Return created callgraph. + return callgraph_; + } + + CallGraph* CallGraphVisitor::create(ast::Ast& tree) + { + return const_cast<CallGraph*>(create(const_cast<const ast::Ast&>(tree))); + } + + /*-----------. + | Visiting. | + `-----------*/ + + void CallGraphVisitor::operator()(const ast::CallExp& e) + { + // FIXME: Some code was deleted here (Link the Caller with the CallExp's declaration). + } + + void CallGraphVisitor::operator()(const ast::FunctionChunk& e) + { + // First define the nodes for each defined function. + for (ast::FunctionDec* f : e) + callgraph_->fundec_add(f); + // Now bind callers and callees. + super_type::operator()(e); + } + + void CallGraphVisitor::operator()(const ast::FunctionDec& e) + { + // Current function becomes temporarily the caller function. + ast::FunctionDec* save = caller; + caller = const_cast<ast::FunctionDec*>(&e); + super_type::operator()(e); + caller = save; + } + +} // namespace callgraph diff --git a/tiger-compiler/src/callgraph/call-graph-visitor.hh b/tiger-compiler/src/callgraph/call-graph-visitor.hh new file mode 100644 index 0000000..97d79b4 --- /dev/null +++ b/tiger-compiler/src/callgraph/call-graph-visitor.hh @@ -0,0 +1,38 @@ +/** + ** \file callgraph/call-graph-visitor.hh + ** \brief Definition of callgraph::CallGraphVisitor. + **/ +#pragma once + +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> +#include <callgraph/fundec-graph.hh> + +namespace callgraph +{ + /// Computes the CallGraph. + class CallGraphVisitor + : protected ast::DefaultConstVisitor + , protected ast::NonObjectConstVisitor + , protected ast::NonAssertConstVisitor + { + public: + using super_type = ast::DefaultConstVisitor; + using super_type::operator(); + const CallGraph* create(const ast::Ast& tree); + CallGraph* create(ast::Ast& tree); + + protected: + void operator()(const ast::CallExp& e) override; + void operator()(const ast::FunctionChunk& e) override; + void operator()(const ast::FunctionDec& e) override; + + protected: + /// Current function. + ast::FunctionDec* caller = nullptr; + /// Call graph. + CallGraph* callgraph_ = nullptr; + }; + +} // namespace callgraph diff --git a/tiger-compiler/src/callgraph/fundec-graph.hh b/tiger-compiler/src/callgraph/fundec-graph.hh new file mode 100644 index 0000000..1addd0d --- /dev/null +++ b/tiger-compiler/src/callgraph/fundec-graph.hh @@ -0,0 +1,76 @@ +/** + ** \file callgraph/fundec-graph.hh + ** \brief Declare and define fundec graph. + */ + +#pragma once + +#include <map> + +#include <boost/graph/adjacency_list.hpp> + +#include <ast/function-dec.hh> +#include <misc/graph.hh> + +namespace callgraph +{ + /*--------------. + | FundecGraph. | + `--------------*/ + + class FundecGraph : public misc::directed_graph<ast::FunctionDec*> + { + public: + /// Add a vertex to the graph, and attach a function definition to it. + void fundec_add(ast::FunctionDec* f); + /// Create an edge between two vertices, identified by the + /// FunctionDec attached to each of them. + void fundec_link(ast::FunctionDec* fu, ast::FunctionDec* fv); + + /// Retrieve the vertex handle corresponding to a FunctionDec. + vertex_descriptor hfundec_get(ast::FunctionDec* f) const; + + // Search if FunctionDec 'searched' is 'start' or one of its parent. + ast::FunctionDec* hfundec_deep_get(ast::FunctionDec* start, + ast::FunctionDec* searched) const; + + protected: + /// Print the label of vertex of a graph. + std::ostream& vertex_print(vertex_descriptor v, + std::ostream& ostr) const override; + + using hfundecs_type = std::map<ast::FunctionDec*, vertex_descriptor>; + + hfundecs_type hfundecs; + }; + + using CallGraph = FundecGraph; + using ParentGraph = FundecGraph; + + /*------------. + | Iterators. | + `------------*/ + + /// Iterator on the vertices of a FundecGraph. + using fundecgraph_vertex_iter_type = + boost::graph_traits<FundecGraph>::vertex_iterator; + /// Iterator on the edges of a FundecGraph. + using fundecgraph_edge_iter_type = + boost::graph_traits<FundecGraph>::edge_iterator; + /// Iterator on the neighborhood of a vertex of a FundecGraph. + using fundecgraph_neighb_iter_type = + boost::graph_traits<FundecGraph>::adjacency_iterator; + + /// \name Aliases. + /// \{ + /// Iterator on the vertices of a CallGraph. + using callgraph_vertex_iter_type = fundecgraph_vertex_iter_type; + /// Iterator on the neighborhood of a vertex of a CallGraph. + using callgraph_neighb_iter_type = fundecgraph_neighb_iter_type; + /// Iterator on the neighborhood of a vertex of a ParentGraph. + using parentgraph_neighb_iter_type = fundecgraph_neighb_iter_type; + /// \} + +} // namespace callgraph + +#include <callgraph/fundec-graph.hxx> diff --git a/tiger-compiler/src/callgraph/fundec-graph.hxx b/tiger-compiler/src/callgraph/fundec-graph.hxx new file mode 100644 index 0000000..d5d863e --- /dev/null +++ b/tiger-compiler/src/callgraph/fundec-graph.hxx @@ -0,0 +1,56 @@ +/** + ** \file callgraph/fundec-graph.hxx + ** \brief Inline methods for callgraph/fundec-graph.hh. + */ + +#pragma once + +#include <callgraph/fundec-graph.hh> + +namespace callgraph +{ + inline void FundecGraph::fundec_add(ast::FunctionDec* f) + { + hfundecs[f] = this->vertex_add(f); + } + + inline void FundecGraph::fundec_link(ast::FunctionDec* fu, + ast::FunctionDec* fv) + { + vertex_descriptor u = hfundecs[fu]; + vertex_descriptor v = hfundecs[fv]; + boost::add_edge(u, v, *this); + } + + inline FundecGraph::vertex_descriptor + FundecGraph::hfundec_get(ast::FunctionDec* f) const + { + hfundecs_type::const_iterator i = hfundecs.find(f); + assertion(i != hfundecs.end()); + return i->second; + } + + inline ast::FunctionDec* + FundecGraph::hfundec_deep_get(ast::FunctionDec* start, + ast::FunctionDec* searched) const + { + if (start == searched) + return searched; + + hfundecs_type::const_iterator i = hfundecs.find(start); + parentgraph_neighb_iter_type parent = + boost::adjacent_vertices(i->second, *this).first; + + if ((*this)[*parent] == start) + return nullptr; + + return hfundec_deep_get((*this)[*parent], searched); + } + + inline std::ostream& FundecGraph::vertex_print(vertex_descriptor v, + std::ostream& ostr) const + { + return ostr << (*this)[v]->name_get(); + } + +} // namespace callgraph diff --git a/tiger-compiler/src/callgraph/libcallgraph.cc b/tiger-compiler/src/callgraph/libcallgraph.cc new file mode 100644 index 0000000..be23b16 --- /dev/null +++ b/tiger-compiler/src/callgraph/libcallgraph.cc @@ -0,0 +1,36 @@ +/** + ** \file callgraph/libcallgraph.cc + ** \brief Define exported callgraph functions. + */ + +#include <callgraph/call-graph-visitor.hh> +#include <callgraph/libcallgraph.hh> +#include <callgraph/parent-graph-visitor.hh> + +#include <misc/contract.hh> +#include <misc/set.hh> + +namespace callgraph +{ + + // Build the callgraph. + const CallGraph* callgraph_compute(const ast::Ast& tree) + { + CallGraphVisitor callgraph_visitor; + return callgraph_visitor.create(tree); + } + + CallGraph* callgraph_compute(ast::Ast& tree) + { + CallGraphVisitor callgraph_visitor; + return callgraph_visitor.create(tree); + } + + // Build the parentgraph. + ParentGraph* parentgraph_compute(ast::Ast& tree) + { + ParentGraphVisitor parentgraph_visitor; + return parentgraph_visitor.create(tree); + } + +} // namespace callgraph diff --git a/tiger-compiler/src/callgraph/libcallgraph.hh b/tiger-compiler/src/callgraph/libcallgraph.hh new file mode 100644 index 0000000..b928bac --- /dev/null +++ b/tiger-compiler/src/callgraph/libcallgraph.hh @@ -0,0 +1,28 @@ +/** + ** \file callgraph/libcallgraph.hh + ** \brief Declare functions and variables exported by callgraph module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <callgraph/fundec-graph.hh> + +/// Computing static link related information. +namespace callgraph +{ + +#ifdef SWIG + %newobject callgraph_compute; +#endif + /// Build the callgraph. + const CallGraph* callgraph_compute(const ast::Ast& tree); + CallGraph* callgraph_compute(ast::Ast& tree); + +#ifdef SWIG + %newobject parentgraph_compute; +#endif + /// Build the parentgraph. + ParentGraph* parentgraph_compute(ast::Ast& tree); + +} // namespace callgraph diff --git a/tiger-compiler/src/callgraph/local.am b/tiger-compiler/src/callgraph/local.am new file mode 100644 index 0000000..695075f --- /dev/null +++ b/tiger-compiler/src/callgraph/local.am @@ -0,0 +1,14 @@ +## module callgraph. + +# Compiling. +# Libcallgraph +src_libtc_la_SOURCES += \ + %D%/libcallgraph.hh %D%/libcallgraph.cc \ + %D%/fundec-graph.hh %D%/fundec-graph.hxx \ + %D%/call-graph-visitor.hh %D%/call-graph-visitor.cc \ + %D%/parent-graph-visitor.hh %D%/parent-graph-visitor.cc + +src_libtc_la_LDFLAGS += $(BOOST_GRAPH_LDFLAGS) +src_libtc_la_LIBADD += $(BOOST_GRAPH_LIBS) + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/callgraph/parent-graph-visitor.cc b/tiger-compiler/src/callgraph/parent-graph-visitor.cc new file mode 100644 index 0000000..f99121d --- /dev/null +++ b/tiger-compiler/src/callgraph/parent-graph-visitor.cc @@ -0,0 +1,42 @@ +/** + ** \file callgraph/parent-graph-visitor.cc + ** \brief Implementation for callgraph::ParentGraphVisitor. + **/ + +#include <ast/function-dec.hh> +#include <callgraph/parent-graph-visitor.hh> + +namespace callgraph +{ + ParentGraph* ParentGraphVisitor::create(ast::Ast& tree) + { + // Create a new empty parentgraph + parentgraph = new ParentGraph(); + + // Launch visitor. + tree.accept(*this); + + // Return created parentgraph. + return parentgraph; + } + + void ParentGraphVisitor::operator()(ast::FunctionChunk& e) + { + for (ast::FunctionDec* f : e) + { + parentgraph->fundec_add(f); + parentgraph->fundec_link(f, parent); + } + super_type::operator()(e); + } + + void ParentGraphVisitor::operator()(ast::FunctionDec& e) + { + // Current function becomes temporarily the parent function. + ast::FunctionDec* tmp = parent; + parent = &e; + super_type::operator()(e); + parent = tmp; + } + +} // namespace callgraph diff --git a/tiger-compiler/src/callgraph/parent-graph-visitor.hh b/tiger-compiler/src/callgraph/parent-graph-visitor.hh new file mode 100644 index 0000000..be16987 --- /dev/null +++ b/tiger-compiler/src/callgraph/parent-graph-visitor.hh @@ -0,0 +1,36 @@ +/** + ** \file callgraph/parent-graph-visitor.hh + ** \brief Definition of callgraph::ParentGraphVisitor. + **/ +#pragma once + +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> +#include <callgraph/fundec-graph.hh> + +namespace callgraph +{ + /// Compute the ParentGraph. + class ParentGraphVisitor + : public ast::DefaultVisitor + , public ast::NonObjectVisitor + , public ast::NonAssertVisitor + { + using super_type = ast::DefaultVisitor; + using super_type::operator(); + + public: + ParentGraph* create(ast::Ast& tree); + + void operator()(ast::FunctionChunk& e) override; + void operator()(ast::FunctionDec& e) override; + + protected: + /// Current function. + ast::FunctionDec* parent = nullptr; + /// Parent graph. + ParentGraph* parentgraph = nullptr; + }; + +} // namespace callgraph diff --git a/tiger-compiler/src/callgraph/tasks.cc b/tiger-compiler/src/callgraph/tasks.cc new file mode 100644 index 0000000..e4d003b --- /dev/null +++ b/tiger-compiler/src/callgraph/tasks.cc @@ -0,0 +1,52 @@ +/** + ** \file callgraph/tasks.cc + ** \brief Callgraph module related tasks' implementation. + */ + +#include <ostream> + +#include <ast/libast.hh> +#include <ast/tasks.hh> +#define DEFINE_TASKS 1 +#include <callgraph/tasks.hh> +#undef DEFINE_TASKS +#include <callgraph/libcallgraph.hh> + +namespace callgraph::tasks +{ + /*------------. + | CallGraph. | + `------------*/ + + static std::unique_ptr<CallGraph> callgraph; + + void callgraph_compute() + { + callgraph.reset(::callgraph::callgraph_compute(*ast::tasks::the_program)); + } + + void callgraph_dump() + { + precondition(callgraph.get()); + callgraph->print("call"); + } + + /*--------------. + | ParentGraph. | + `--------------*/ + + static std::unique_ptr<ParentGraph> parentgraph; + + void parentgraph_compute() + { + parentgraph.reset( + ::callgraph::parentgraph_compute(*ast::tasks::the_program)); + } + + void parentgraph_dump() + { + precondition(parentgraph.get()); + parentgraph->print("parent"); + } + +} // namespace callgraph::tasks diff --git a/tiger-compiler/src/callgraph/tasks.hh b/tiger-compiler/src/callgraph/tasks.hh new file mode 100644 index 0000000..e1fc584 --- /dev/null +++ b/tiger-compiler/src/callgraph/tasks.hh @@ -0,0 +1,46 @@ +/** + ** \file callgraph/tasks.hh + ** \brief Callgraph module related tasks. + */ + +#pragma once + +#include <callgraph/fundec-graph.hh> +#include <task/libtask.hh> + +/// The Tasks of the escapes module. +namespace callgraph::tasks +{ + TASK_GROUP("3. Callgraph"); + + /*-------------. + | Call graph. | + `-------------*/ + + /// Build the call graph. + TASK_DECLARE("callgraph-compute", + "build the call graph", + callgraph_compute, + "bindings-compute"); + /// Dump the callgraph. + TASK_DECLARE("callgraph-dump", + "dump the call graph", + callgraph_dump, + "callgraph-compute"); + + /*---------------. + | Parent graph. | + `---------------*/ + + /// Build the parent graph. + TASK_DECLARE("parentgraph-compute", + "build the parent graph", + parentgraph_compute, + "parse"); + /// Dump the parentgraph. + TASK_DECLARE("parentgraph-dump", + "dump the parent graph", + parentgraph_dump, + "parentgraph-compute"); + +} // namespace callgraph::tasks diff --git a/tiger-compiler/src/combine/binder.cc b/tiger-compiler/src/combine/binder.cc new file mode 100644 index 0000000..c5fc66e --- /dev/null +++ b/tiger-compiler/src/combine/binder.cc @@ -0,0 +1,17 @@ +/** + ** \file combine/combine-binder.cc + ** \brief Implementation of combine::Binder. + */ + +#include <ast/all.hh> +#include <combine/binder.hh> + +namespace combine +{ + /*---------------. + | Visiting Dec. | + `---------------*/ + + // FIXME: Some code was deleted here. + +} // namespace combine diff --git a/tiger-compiler/src/combine/binder.hh b/tiger-compiler/src/combine/binder.hh new file mode 100644 index 0000000..1789b47 --- /dev/null +++ b/tiger-compiler/src/combine/binder.hh @@ -0,0 +1,16 @@ +/** + ** \file combine/binder.hh + ** \brief Declaration of combine::Binder. + **/ + +#pragma once + +#include <object/binder.hh> +#include <overload/binder.hh> + +namespace combine +{ + /// \brief Compute bindings with support for objects and overload. + // FIXME: Some code was deleted here (class Binder inheriting from object::Binder and overload::Binder). + +} // namespace combine diff --git a/tiger-compiler/src/combine/libcombine.cc b/tiger-compiler/src/combine/libcombine.cc new file mode 100644 index 0000000..29834de --- /dev/null +++ b/tiger-compiler/src/combine/libcombine.cc @@ -0,0 +1,26 @@ +/** + ** \file combine/libcombine.cc + ** \brief Define exported combine functions. + */ + +#include <combine/libcombine.hh> + +// FIXME: Some code was deleted here. + +namespace combine +{ + std::pair<overload::overfun_bindings_type, misc::error> + combine_bind(ast::Ast& tree, bool overloads_enabled) + { + // FIXME: Some code was deleted here. + } + + misc::error + combine_types_check(ast::Ast& tree, + const overload::overfun_bindings_type& combine_bindings, + bool overloads_enabled) + { + // FIXME: Some code was deleted here. + } + +} // namespace combine diff --git a/tiger-compiler/src/combine/libcombine.hh b/tiger-compiler/src/combine/libcombine.hh new file mode 100644 index 0000000..66f3c4c --- /dev/null +++ b/tiger-compiler/src/combine/libcombine.hh @@ -0,0 +1,44 @@ +/** + ** \file combine/libcombine.hh + ** \brief Declare functions and variables exported by combine module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> +#include <overload/binder.hh> + +namespace combine +{ + /*-------. + | Bind. | + `-------*/ + + /** \brief Bind identifier uses to their definition, allowing + function overloading, and return a list of potential definition + sites for each function call. + + \param tree AST to bind. + + \return a pair whose first element is the potential function + bindings, and the second element the error status. */ + std::pair<overload::overfun_bindings_type, misc::error> + combine_bind(ast::Ast& tree, bool overloads_enabled); + + /*------------------------------. + | Compute types with overload. | + `------------------------------*/ + + /** \brief Check types allowing function overloading. + + \param tree abstract syntax tree's root. + \param combine_bindings potential function bindings. + + \return success of the type-checking. */ + misc::error + combine_types_check(ast::Ast& tree, + const overload::overfun_bindings_type& combine_bindings, + bool overloads_enabled); + +} // namespace combine diff --git a/tiger-compiler/src/combine/local.am b/tiger-compiler/src/combine/local.am new file mode 100644 index 0000000..7fd348c --- /dev/null +++ b/tiger-compiler/src/combine/local.am @@ -0,0 +1,6 @@ +src_libtc_la_SOURCES += \ + %D%/libcombine.hh %D%/libcombine.cc \ + %D%/binder.hh %D%/binder.cc \ + %D%/type-checker.hh %D%/type-checker.cc + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/combine/tasks.cc b/tiger-compiler/src/combine/tasks.cc new file mode 100644 index 0000000..48470ac --- /dev/null +++ b/tiger-compiler/src/combine/tasks.cc @@ -0,0 +1,74 @@ +/** + ** \file combine/tasks.cc + ** \brief Combine module related tasks' implementation. + */ + +#include <ast/tasks.hh> +#include <bind/tasks.hh> +#include <desugar/tasks.hh> +#include <escapes/tasks.hh> +#include <object/tasks.hh> + +#include <astclone/libastclone.hh> +#include <desugar/libdesugar.hh> +#include <inlining/libinlining.hh> + +#include <common.hh> + +#include <combine/libcombine.hh> +#define DEFINE_TASKS 1 +#include <combine/tasks.hh> +#undef DEFINE_TASKS + +namespace combine::tasks +{ + std::unique_ptr<overload::overfun_bindings_type> the_overfun_bindings = + nullptr; + + void combine_bindings_compute() + { + auto result = + ::combine::combine_bind(*ast::tasks::the_program, c_overload_p); + ::combine::tasks::the_overfun_bindings = + std::make_unique<overload::overfun_bindings_type>( + std::move(result.first)); + + task_error() << result.second << &misc::error::exit_on_error; + } + + void combine_types_compute() + { + task_error() << ::combine::combine_types_check( + *ast::tasks::the_program, *::combine::tasks::the_overfun_bindings, + c_overload_p); + the_overfun_bindings.reset(); + task_error().exit_on_error(); + } + + void combine_rename() + { + // FIXME: Some code was deleted here (Call appropriate renaming tasks). + } + + void combine_desugar() + { + if (c_object_p) + ::object::tasks::object_desugar(); + + if (c_desugar_p) + ::desugar::tasks::desugar(); + + if (c_bounds_p) + astclone::apply(::desugar::bounds_checks_add, ast::tasks::the_program); + + if (c_inline_p) + astclone::apply(::inlining::inline_expand, ast::tasks::the_program); + + if (c_prune_p) + astclone::apply(::inlining::prune, ast::tasks::the_program); + + if (c_escapes_p) + ::escapes::tasks::escapes_compute(); + } + +} // namespace combine::tasks diff --git a/tiger-compiler/src/combine/tasks.hh b/tiger-compiler/src/combine/tasks.hh new file mode 100644 index 0000000..67cc169 --- /dev/null +++ b/tiger-compiler/src/combine/tasks.hh @@ -0,0 +1,56 @@ +/** + ** \file combine/tasks.hh + ** \brief Combine module related tasks. + */ + +#pragma once + +#include <overload/binder.hh> +#include <task/libtask.hh> + +namespace combine::tasks +{ + TASK_GROUP("Combine"); + + BOOLEAN_TASK_DECLARE("c-object", "combine objects", c_object_p, "object"); + BOOLEAN_TASK_DECLARE("c-bounds", "combine bounds checking", c_bounds_p, ""); + BOOLEAN_TASK_DECLARE("c-escapes", "combine escapes", c_escapes_p, ""); + BOOLEAN_TASK_DECLARE("c-desugar", + "combine for and string comparison desugaring", + c_desugar_p, + "desugar-for desugar-string-cmp"); + BOOLEAN_TASK_DECLARE("c-inline", "combine inlining", c_inline_p, ""); + BOOLEAN_TASK_DECLARE("c-prune", "combine pruning", c_prune_p, ""); + BOOLEAN_TASK_DECLARE("c-overload", "combine overloading", c_overload_p, ""); + + BOOLEAN_TASK_DECLARE( + "a|c-all", + "combine all compiler options", + c_all_p, + "c-object c-bounds c-escapes c-desugar c-inline c-prune c-overload"); + + TASK_DECLARE("combine-bindings-compute", + "bind the identifiers, " + "allowing various compiler options", + combine_bindings_compute, + "parse"); + + TASK_DECLARE("combine-types-compute", + "check for type violations, " + "allowing various compiler options", + combine_types_compute, + "combine-bindings-compute"); + + TASK_DECLARE("combine-rename", + "rename identifiers to unique names, " + "allowing various compiler options", + combine_rename, + "combine-types-compute"); + + TASK_DECLARE("c|combine-desugar", + "remove object and complex constructs from the program" + "allowing various compiler options", + combine_desugar, + "combine-rename"); + +} // namespace combine::tasks diff --git a/tiger-compiler/src/combine/type-checker.cc b/tiger-compiler/src/combine/type-checker.cc new file mode 100644 index 0000000..67cbfbc --- /dev/null +++ b/tiger-compiler/src/combine/type-checker.cc @@ -0,0 +1,14 @@ +/** + ** \file combine/type-checker.cc + ** \brief Implementation for combine/type-checker.hh. + */ + +#include <ast/all.hh> +#include <combine/type-checker.hh> +#include <type/types.hh> + +namespace combine +{ + // FIXME: Some code was deleted here. + +} // namespace combine diff --git a/tiger-compiler/src/combine/type-checker.hh b/tiger-compiler/src/combine/type-checker.hh new file mode 100644 index 0000000..02370d2 --- /dev/null +++ b/tiger-compiler/src/combine/type-checker.hh @@ -0,0 +1,20 @@ +/** + ** \file combine/type-checker.hh + ** \brief Declaration of combine::TypeChecker. + */ + +#pragma once + +#include <object/type-checker.hh> +#include <overload/binder.hh> +#include <overload/type-checker.hh> +#include <type/class.hh> +#include <type/types.hh> + +namespace combine +{ + /// Perform type checking, allowing objects, and compute + /// the bindings of the object's methods and fields. + // FIXME: Some code was deleted here (class TypeChecker inheriting from overload::TypeChecker and object::TypeChecker). + +} // namespace combine diff --git a/tiger-compiler/src/common.cc b/tiger-compiler/src/common.cc new file mode 100644 index 0000000..0375220 --- /dev/null +++ b/tiger-compiler/src/common.cc @@ -0,0 +1,19 @@ +/** + ** \file common.cc + ** \brief Common definitions. + */ + +#include <common.hh> + +// Sole argument: the file to process. +const char* filename; + +// The current state of and error. +misc::error& task_error() +{ + static misc::error task_error_; + return task_error_; +} + +// Counting the time spent in the various tasks. +misc::timer task_timer; diff --git a/tiger-compiler/src/common.hh b/tiger-compiler/src/common.hh new file mode 100644 index 0000000..ca647a9 --- /dev/null +++ b/tiger-compiler/src/common.hh @@ -0,0 +1,49 @@ +/** + ** \file src/common.hh + ** \brief Definitions common to the whole task system. + ** + ** These variables are global. As such they should not be used + ** in the libraries, but only in the "imperative" subsystem: the + ** tasks. + */ + +#pragma once + +#include <misc/error.hh> +#include <misc/timer.hh> + +#ifdef SWIG +// Warning 451: Setting a const char * variable may leak memory. +%warnfilter(451) program_name; +%warnfilter(451) program_version; +%warnfilter(451) program_bug_address; +%warnfilter(451) program_doc; +%warnfilter(451) program_args_doc; +%warnfilter(451) filename; +#endif /* ! SWIG */ + +/// \name Program identity. +/// \{ +/// Name of this program. +extern const char* program_name; + +/// Version string of this program. +extern const char* program_version; + +/// Bug report address of this program. +extern const char* program_bug_address; + +/// Describe program and accepted arguments. +extern const char* program_doc; +extern const char* program_args_doc; + +/// \} + +/// Timing the tasks. +extern misc::timer task_timer; + +/// Sole argument: the file to process. +extern const char* filename; + +/// The current state of error. +extern misc::error& task_error(); diff --git a/tiger-compiler/src/desugar/bounds-checking-visitor.cc b/tiger-compiler/src/desugar/bounds-checking-visitor.cc new file mode 100644 index 0000000..30f691e --- /dev/null +++ b/tiger-compiler/src/desugar/bounds-checking-visitor.cc @@ -0,0 +1,31 @@ +/** + ** \file desugar/bounds-checking-visitor.cc + ** \brief Implementation of desugar::BoundsCheckingVisitor. + */ + +#include <ast/all.hh> +#include <ast/libast.hh> +#include <desugar/bounds-checking-visitor.hh> +#include <misc/symbol.hh> +#include <parse/libparse.hh> + +namespace desugar +{ + namespace + { + /// Return the name of the boxed type for \a s. + std::string box(misc::symbol s) { return "_box_" + s.get(); } + + } // namespace + + BoundsCheckingVisitor::BoundsCheckingVisitor() + : super_type() + {} + + /*-----------------------. + | Array bounds checking. | + `-----------------------*/ + + // FIXME: Some code was deleted here. + +} // namespace desugar diff --git a/tiger-compiler/src/desugar/bounds-checking-visitor.hh b/tiger-compiler/src/desugar/bounds-checking-visitor.hh new file mode 100644 index 0000000..65e955d --- /dev/null +++ b/tiger-compiler/src/desugar/bounds-checking-visitor.hh @@ -0,0 +1,46 @@ +/** + ** \file desugar/bounds-checking-visitor.hh + ** \brief Declaration of desugar::BoundsCheckingVisitor. + */ + +#pragma once + +#include <map> + +#include <astclone/cloner.hh> +#include <parse/tweast.hh> + +namespace desugar +{ + /// \brief Add dynamic array bounds checks while duplicating an AST. + class BoundsCheckingVisitor : public astclone::Cloner + { + public: + /// Parent type. + using super_type = astclone::Cloner; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build a BoundsCheckingVisitor. + BoundsCheckingVisitor(); + + /// \name Visit methods. + /// \{ + // FIXME: Some code was deleted here. + /// \} + + private: + /// The bounds checking runtime. + /// + /// Additional definitions to insert in the prelude. + static const std::string prelude; + + // Symbols would be nicer, but maps of symbols are + // inconvenient since there is no default ctor. + using boxes_type = std::map<const type::Array*, std::string>; + /// Map from an array type to the corresponding `box' type name. + boxes_type boxes_; + }; + +} // namespace desugar diff --git a/tiger-compiler/src/desugar/desugar-visitor.cc b/tiger-compiler/src/desugar/desugar-visitor.cc new file mode 100644 index 0000000..770cdf6 --- /dev/null +++ b/tiger-compiler/src/desugar/desugar-visitor.cc @@ -0,0 +1,144 @@ +/** + ** \file desugar/desugar-visitor.cc + ** \brief Implementation of desugar::DesugarVisitor. + */ + +#include <ast/all.hh> +#include <ast/libast.hh> +#include <desugar/desugar-visitor.hh> +#include <misc/algorithm.hh> +#include <misc/symbol.hh> +#include <parse/libparse.hh> +#include <parse/tweast.hh> + +namespace desugar +{ + DesugarVisitor::DesugarVisitor(bool desugar_for_p, bool desugar_string_cmp_p) + : super_type() + , desugar_for_p_(desugar_for_p) + , desugar_string_cmp_p_(desugar_string_cmp_p) + {} + + /*-----------------------------. + | Desugar string comparisons. | + `-----------------------------*/ + void DesugarVisitor::operator()(const ast::OpExp& e) + { + // FIXME DONE: Some code was deleted here. + // Check to see that all of the elements to operate on are strings + if (dynamic_cast<const type::String*>(e.left_get().type_get()) == nullptr + || dynamic_cast<const type::String*>(e.right_get().type_get()) == nullptr) + { + return super_type::operator()(e); + } + + const auto left = recurse(e.left_get()); + const auto right = recurse(e.right_get()); + + misc::variant<ast::Exp*, ast::ChunkList*> res; + + switch (e.oper_get()) + { + case ast::OpExp::Oper::eq: + { + res = parse::parse(parse::Tweast() << "streq(" << left << ", " << right << ") = 0"); + break; + } + case ast::OpExp::Oper::ne: + { + res = parse::parse(parse::Tweast() << "streq(" << left << ", " << right << ") <> 0"); + break; + } + case ast::OpExp::Oper::lt: + { + res = parse::parse(parse::Tweast() << "strcmp(" << left << ", " << right << ") = -1"); + break; + } + case ast::OpExp::Oper::le: + { + res = parse::parse(parse::Tweast() << "strcmp(" << left << ", " << right << ") <= 0"); + break; + } + case ast::OpExp::Oper::gt: + { + res = parse::parse(parse::Tweast() << "strcmp(" << left << ", " << right << ") = 1"); + break; + } + case ast::OpExp::Oper::ge: + { + res = parse::parse(parse::Tweast() << "strcmp(" << left << ", " << right << ") >= 0"); + break; + } + default: + break; + } + + ast::Exp* final = std::get<ast::Exp*>(res); + result_ = final; + } + + /*----------------------. + | Desugar `for' loops. | + `----------------------*/ + + /*<<- + Desugar `for' loops as `while' loops: + + for i := lo to hi do + body + + is transformed as: + + let + var _lo := lo + var _hi := hi + var i := _lo + in + if i <= _hi then + while 1 do + ( + body; + if i = _hi then + break; + i := i + 1 + ) + end + + Notice that: + + - a `_hi' variable is introduced so that `hi' is evaluated only + once; + + - a `_lo' variable is introduced to prevent `i' from being in the + scope of `_hi'; + + - a first test is performed before entering the loop, so that the + loop condition becomes `i < _hi' (instead of `i <= _hi'); this + is done to prevent overflows on INT_MAX. + ->>*/ + + void DesugarVisitor::operator()(const ast::ForExp& e) + { + // FIXME DONE: Some code was deleted here. + auto var_init = recurse(e.vardec_get().init_get()); + auto lim = recurse(e.hi_get()); + auto body = recurse(e.body_get()); + auto iterator = e.vardec_get().name_get(); + auto res = parse::parse(parse::Tweast() << "let " + "var _lo := " << var_init << + " var _hi := " << lim << + " var " << iterator << " := _lo " + "in " + "if " << iterator << " <= _hi then " + "while 1 do " + "( " << body << ";" + "if " << iterator << " = _hi then " + "break;" + << iterator << " := " << iterator << " + 1 " + ") " + "end"); + ast::Exp* final = std::get<ast::Exp*>(res); + result_ = final; + } + +} // namespace desugar diff --git a/tiger-compiler/src/desugar/desugar-visitor.hh b/tiger-compiler/src/desugar/desugar-visitor.hh new file mode 100644 index 0000000..c6eee69 --- /dev/null +++ b/tiger-compiler/src/desugar/desugar-visitor.hh @@ -0,0 +1,42 @@ +/** + ** \file desugar/desugar-visitor.hh + ** \brief Declaration of desugar::DesugarVisitor. + */ + +#pragma once + +#include <map> + +#include <astclone/cloner.hh> + +namespace desugar +{ + /// \brief Desugar some syntactic structures while duplicating an Ast. + class DesugarVisitor : public astclone::Cloner + { + public: + /// Superclass. + using super_type = astclone::Cloner; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build a DesugarVisitor. + DesugarVisitor(bool desugar_for_p, bool desugar_string_cmp_p); + + /// \name Visit methods. + /// \{ + /// Desugar string comparisons. + void operator()(const ast::OpExp&) override; + /// Desugar `for' loops as `while' loops. + void operator()(const ast::ForExp&) override; + /// \} + + private: + /// Desugar `for' loops? + bool desugar_for_p_; + /// Desugar string comparisons? + bool desugar_string_cmp_p_; + }; + +} // namespace desugar diff --git a/tiger-compiler/src/desugar/libdesugar.hh b/tiger-compiler/src/desugar/libdesugar.hh new file mode 100644 index 0000000..d758ed2 --- /dev/null +++ b/tiger-compiler/src/desugar/libdesugar.hh @@ -0,0 +1,85 @@ +/** + ** \file desugar/libdesugar.hh + ** \brief Declare functions exported by the desugar module. + */ + +#pragma once + +#include <misc/error.hh> + +/// Desugaring an ast::Ast. +namespace desugar +{ + /*----------. + | Helpers. | + `----------*/ + + /// Recompute the bindings and the types of the AST \a tree. + /// + /// Raise an Internal Compiler Error on failure. + template <typename A> void bind_and_types_check(A& tree); + + /*----------. + | Desugar. | + `----------*/ + + /** \brief Remove the syntactic sugar from an AST. + + \param tree abstract syntax tree's root, whose bindings + and types have been computed, and whose + identifiers are all unique. + \param desugar_for_p desugar `for' loops. + \param desugar_string_cmp_p desugar string comparisons. + + \return the desugared, bound and type-checked AST. */ + template <typename A> + A* desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p); + + /** \brief Remove the syntactic sugar from an AST without + recomputing its bindings nor its types. + + This function acts like desugar::desugar, but stops just after + the desugaring step (in fact, desugar::desugar is built upon + this function). It is meant to be used as a test of + DesugarVisitor (i.e., even if the desugared tree is badly bound + or typed, it can still be pretty-printed). + + \param tree AST to desugar. + \param desugar_for_p desugar `for' loops. + \param desugar_string_cmp_p desugar string comparisons. + + \return the desugared AST. */ + template <typename A> + A* raw_desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p); + + /*-----------------------. + | Array bounds checking. | + `-----------------------*/ + + /** \brief Add runtime checks of array bounds. + + \param tree abstract syntax tree's root, whose bindings and types + have been computed, and whose identifiers are all unique. + + \return the AST with bounds checks, with bindings and type-checked. */ + + template <typename A> A* bounds_checks_add(const A& tree); + + /** \brief Remove the syntactic sugar from an AST without + recomputing its bindings nor its types. + + This function acts like desugar::bounds_checks_add, but stops + just after the tranformation step (in fact, + desugar::bounds_checks_add is built upon this function). It is + meant to be used as a test of BoundsCheckingVisitor (i.e., even + if the tree with bounds checks is badly bound or typed, it can + still be pretty-printed). + + \param tree AST on which bounds checks are to be added + + \return the AST with bounds checks. */ + template <typename A> A* raw_bounds_checks_add(const A& tree); + +} // namespace desugar + +#include <desugar/libdesugar.hxx> diff --git a/tiger-compiler/src/desugar/libdesugar.hxx b/tiger-compiler/src/desugar/libdesugar.hxx new file mode 100644 index 0000000..191bd8d --- /dev/null +++ b/tiger-compiler/src/desugar/libdesugar.hxx @@ -0,0 +1,95 @@ +#pragma once + +/** + ** \file desugar/libdesugar.hxx + ** \brief Functions exported by the desugar module. + */ + +#include <memory> + +#include <ast/chunk-list.hh> +#include <ast/exp.hh> +#include <bind/libbind.hh> +#include <desugar/bounds-checking-visitor.hh> +#include <desugar/desugar-visitor.hh> +#include <desugar/libdesugar.hh> +#include <escapes/libescapes.hh> +#include <overload/liboverload.hh> +#include <type/libtype.hh> + +namespace desugar +{ + /*----------. + | Helpers. | + `----------*/ + + template <typename A> void bind_and_types_check(A& tree) + { + misc::error e; + // FIXME DONE: Some code was deleted here. + e << bind::bind(&tree); + e.ice_on_error_here(); + e << type::types_check(tree); + e.ice_on_error_here(); + } + + // Explicit instantiation. + template void bind_and_types_check<ast::ChunkList>(ast::ChunkList&); + + /*----------. + | Desugar. | + `----------*/ + + template <typename A> + A* raw_desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p) + { + // Desugar. + DesugarVisitor desugar(desugar_for_p, desugar_string_cmp_p); + desugar(tree); + return dynamic_cast<A*>(desugar.result_get()); + } + + template <typename A> + A* desugar(const A& tree, bool desugar_for_p, bool desugar_string_cmp_p) + { + // Desugar. + A* desugared = raw_desugar(tree, desugar_for_p, desugar_string_cmp_p); + assertion(desugared); + std::unique_ptr<A> desugared_ptr(desugared); + // Recompute the bindings and the types. + bind_and_types_check(*desugared_ptr); + return desugared_ptr.release(); + } + + /// Explicit instantiations. + template ast::ChunkList* raw_desugar(const ast::ChunkList&, bool, bool); + template ast::ChunkList* desugar(const ast::ChunkList&, bool, bool); + + /*-----------------------. + | Array bounds checking. | + `-----------------------*/ + + template <typename A> A* raw_bounds_checks_add(const A& tree) + { + // Add array bounds checking code. + BoundsCheckingVisitor add_bounds_checks; + add_bounds_checks(tree); + return dynamic_cast<A*>(add_bounds_checks.result_get()); + } + + template <typename A> A* bounds_checks_add(const A& tree) + { + // Add bounds checks. + A* transformed = raw_bounds_checks_add(tree); + assertion(transformed); + std::unique_ptr<A> transformed_ptr(transformed); + // Recompute the bindings and the types. + bind_and_types_check(*transformed_ptr); + return transformed_ptr.release(); + } + + /// Explicit instantiations. + template ast::ChunkList* raw_bounds_checks_add(const ast::ChunkList&); + template ast::ChunkList* bounds_checks_add(const ast::ChunkList&); + +} // namespace desugar diff --git a/tiger-compiler/src/desugar/local.am b/tiger-compiler/src/desugar/local.am new file mode 100644 index 0000000..701d537 --- /dev/null +++ b/tiger-compiler/src/desugar/local.am @@ -0,0 +1,27 @@ +## desugar module. +src_libtc_la_SOURCES += \ + %D%/desugar-visitor.hh %D%/desugar-visitor.cc \ + %D%/libdesugar.hh %D%/libdesugar.hxx + +check_PROGRAMS += \ + %D%/test-string-cmp-desugar \ + %D%/test-for-loops-desugar + + + +%C%_test_string_cmp_desugar_LDADD = src/libtc.la +%C%_test_string_cmp_desugar_CPPFLAGS = $(AM_CPPFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" + +%C%_test_for_loops_desugar_LDADD = src/libtc.la +%C%_test_for_loops_desugar_CPPFLAGS = $(AM_CPPFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" + +src_libtc_la_SOURCES += \ + %D%/bounds-checking-visitor.hh %D%/bounds-checking-visitor.cc + +check_PROGRAMS += \ + %D%/test-bounds-checking + +%C%_test_bounds_checking_LDADD = src/libtc.la +%C%_test_bounds_checking_CPPFLAGS = $(AM_CPPFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/desugar/tasks.cc b/tiger-compiler/src/desugar/tasks.cc new file mode 100644 index 0000000..0b83cb7 --- /dev/null +++ b/tiger-compiler/src/desugar/tasks.cc @@ -0,0 +1,49 @@ +/** + ** \file desugar/tasks.cc + ** \brief Desugar module related tasks' implementation. + **/ + +#include <ast/tasks.hh> +#include <astclone/libastclone.hh> +#include <common.hh> +#include <desugar/libdesugar.hh> +#define DEFINE_TASKS 1 +#include <desugar/tasks.hh> +#undef DEFINE_TASKS + +namespace desugar::tasks +{ + /*-------------. + | Desugaring. | + `-------------*/ + + void desugar() + { + astclone::apply(::desugar::desugar, ast::tasks::the_program, desugar_for_p, + desugar_string_cmp_p); + /// Escape after desugaring if escape was done before + if (escapes::escaped) + escapes::escapes_compute(*ast::tasks::the_program); + } + + void raw_desugar() + { + astclone::apply(::desugar::raw_desugar, ast::tasks::the_program, + desugar_for_p, desugar_string_cmp_p); + } + + /*-----------------------. + | Array bounds checking. | + `-----------------------*/ + + void bounds_checks_add() + { + astclone::apply(::desugar::bounds_checks_add, ast::tasks::the_program); + } + + void raw_bounds_checks_add() + { + astclone::apply(::desugar::raw_bounds_checks_add, ast::tasks::the_program); + } + +} // namespace desugar::tasks diff --git a/tiger-compiler/src/desugar/tasks.hh b/tiger-compiler/src/desugar/tasks.hh new file mode 100644 index 0000000..44a68e6 --- /dev/null +++ b/tiger-compiler/src/desugar/tasks.hh @@ -0,0 +1,75 @@ +/** + ** \file desugar/tasks.hh + ** \brief Desugar module tasks. + */ + +#pragma once + +#include <config.h> +#include <misc/fwd.hh> +#include <task/libtask.hh> + +/// Tasks of the desugar module. +namespace desugar::tasks +{ + TASK_GROUP("Desugaring and bounds-checking"); + + /*-------------. + | Desugaring. | + `-------------*/ + + /// Enable translation of `for' loops into `while' loops. + BOOLEAN_TASK_DECLARE("desugar-for", "desugar `for' loops", desugar_for_p, ""); + + /// Enable string comparison desugaring. + BOOLEAN_TASK_DECLARE("desugar-string-cmp", + "desugar string comparisons", + desugar_string_cmp_p, + ""); + + /// Default the removal of syntactic sugar from the AST to Tiger + /// (without overloading). + DISJUNCTIVE_TASK_DECLARE("desugared", + "Default the removal of syntactic sugar " + "from the AST to Tiger (without overloading)", + " assert-desugar" + " desugar"); + + /// Remove syntactic sugar from the Ast. + TASK_DECLARE("desugar", "desugar the AST", desugar, "types-compute rename"); + + /* FIXME: Careful with this options (and with + --raw-bounds-checks-add), as they leave the AST in a bad state + (without bindings nor types). For instance, + + tc --raw-desugar -H will probably result in a SEGV. + + since hir-compute wants a type-checked AST. */ + + /// Remove syntactic sugar from the AST without recomputing + /// bindings nor types. + TASK_DECLARE("raw-desugar", + "desugar the AST without recomputing " + "bindings nor types", + raw_desugar, + "typed rename"); + + /*-----------------------. + | Array bounds checking. | + `-----------------------*/ + + /// Enable emission of dynamic array bounds checking code. + TASK_DECLARE("bounds-checks-add", + "add dynamic bounds checks", + bounds_checks_add, + "types-compute rename"); + + /// Enable emission of dynamic array bounds checking code without + /// recomputing bindings nor types. + TASK_DECLARE("raw-bounds-checks-add", + "add bounds-checking to the AST without recomputing " + "bindings nor types", + raw_bounds_checks_add, + "typed rename"); + +} // namespace desugar::tasks diff --git a/tiger-compiler/src/doc.hh b/tiger-compiler/src/doc.hh new file mode 100644 index 0000000..e629911 --- /dev/null +++ b/tiger-compiler/src/doc.hh @@ -0,0 +1,23 @@ +/** + +\mainpage + +These pages document the implementation of the EPITA Tiger Compiler +Project (http://tiger.lrde.epita.fr). + +This very important project in the EPITA curriculum consists in the +implementation of a Tiger compiler in C++. The Tiger language is +described by Andrew Appel in his "Modern Compiler Implementation" +books. + +Most of the material students need to implement their Tiger Project is +to be found in the http://www.lrde.epita.fr/~tiger/ directory. In +particular, reading http://www.lrde.epita.fr/~tiger/assignments.html +is mandatory. + +The reader will find more information in various places listed on +http://tiger.lrde.epita.fr/. + +*/ + +Local Variables : mode : text End: diff --git a/tiger-compiler/src/escapes/escapes-visitor.cc b/tiger-compiler/src/escapes/escapes-visitor.cc new file mode 100644 index 0000000..f0fd5e2 --- /dev/null +++ b/tiger-compiler/src/escapes/escapes-visitor.cc @@ -0,0 +1,31 @@ +/** + ** \file escapes/escapes-visitor.cc + ** \brief Implementation for escapes/escapes-visitor.hh. + */ + +#include <ast/all.hh> +#include <escapes/escapes-visitor.hh> +#include <misc/contract.hh> + +namespace escapes +{ + // FIXME DONE: Some code was deleted here. + + void EscapesVisitor::operator()(ast::SimpleVar& e) + { + if (depth_map_.at(e.def_get()) < current_depth_) + e.def_get()->escape_set(true); + } + void EscapesVisitor::operator()(ast::VarDec& e) + { + e.escape_set(false); + depth_map_.insert_or_assign(&e, current_depth_); + super_type::operator()(e); + } + void EscapesVisitor::operator()(ast::FunctionDec& e) + { + current_depth_++; + super_type::operator()(e); + current_depth_--; + } +} // namespace escapes diff --git a/tiger-compiler/src/escapes/escapes-visitor.hh b/tiger-compiler/src/escapes/escapes-visitor.hh new file mode 100644 index 0000000..3b4023f --- /dev/null +++ b/tiger-compiler/src/escapes/escapes-visitor.hh @@ -0,0 +1,75 @@ +/** + ** \file escapes/escapes-visitor.hh + ** \brief Compute the escapes. + ** + ** Variables and formals of a function may escape, i.e., be accessed + ** by an inner function. + ** + ** When the semantic analysis finds a declaration of variable or + ** formal FOO, it needs to know whether it escapes or not. This + ** requires an additional pass, before the semantic analysis, just to + ** spot the potential escapes of FOO. + ** + ** In order to pass the result to the semantic analysis which walks + ** across the ast, the most natural and easy solution consists in + ** tagging the various VarDec and Field. + ** + ** Now, how shall we compute the escapes? + ** The answer is obvious: we need to walk the ast, searching for variables + ** declared then accessed by an inner function. + ** + ** If a variable is accessed from a nested function + ** + ** 2è possibilité : + ** - Aucune n'échappe + ** - Echappe si accessed dans une fonction nestée + ** Attention + */ + +#pragma once + +#include <map> + +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> + +namespace escapes +{ + /** \brief Compute the escapes. + ** + ** The EscapeVisitor is extremely similar to type::TypeChecker: + ** in its course of operation it must relate uses to + ** definitions. Therefore it will be run after the bind::Binder. + ** It also needs auxiliary information about the definitions (their + ** depth): a simple map suffices, since scoping issues were handled + ** by the bind::Binder. + ** + ** Note that this EscapesVisitor is mainly doing nothing: it is just + ** interested in declaration and uses of variables/formals (and, of + ** course, function declaration...). It would be somewhat stupid to + ** write all the methods that `do nothing but walk'. This is why we + ** will inherit from the non const ast::DefaultVisitor. + **/ + class EscapesVisitor + : public ast::DefaultVisitor + , public ast::NonObjectVisitor + , public ast::NonAssertVisitor + { + public: + /// Super class type. + using super_type = ast::DefaultVisitor; + /// Import all the overloaded visit methods. + using super_type::operator(); + + // FIXME DONE: Some code was deleted here. + void operator()(ast::SimpleVar& e) override; + void operator()(ast::VarDec& e) override; + void operator()(ast::FunctionDec& e) override; + protected: + std::map<ast::VarDec*, size_t> depth_map_; + private: + size_t current_depth_ = 0; + }; + +} // namespace escapes diff --git a/tiger-compiler/src/escapes/libescapes.cc b/tiger-compiler/src/escapes/libescapes.cc new file mode 100644 index 0000000..6124d54 --- /dev/null +++ b/tiger-compiler/src/escapes/libescapes.cc @@ -0,0 +1,23 @@ +/** + ** \file escapes/libescapes.cc + ** \brief Define exported escapes functions. + */ + +#include <escapes/escapes-visitor.hh> +#include <escapes/libescapes.hh> + +namespace escapes +{ + /** Walk the tree, and set the escape flag of variables and arguments + if they do escape. */ + void escapes_compute(ast::Ast& tree) + { + /// boolean to check if an escapes pass was done in desugar + escapes::escaped = true; + EscapesVisitor escapes_compute; + escapes_compute(tree); + } + + bool escaped = false; + +} // namespace escapes diff --git a/tiger-compiler/src/escapes/libescapes.hh b/tiger-compiler/src/escapes/libescapes.hh new file mode 100644 index 0000000..2d3dd73 --- /dev/null +++ b/tiger-compiler/src/escapes/libescapes.hh @@ -0,0 +1,23 @@ +/** + ** \file escapes/libescapes.hh + ** \brief Declare functions and variables exported by escapes module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> + +/// Computing escape and static link related information. +namespace escapes +{ + /// Compute the escaping variables. + void escapes_compute(ast::Ast& tree); + + /// This boolean is used to know whether escape pass + /// was made + /// FIXME: this is a dirty fix it should be replaced with a + /// new implementation of tasks + extern bool escaped; + +} // namespace escapes diff --git a/tiger-compiler/src/escapes/local.am b/tiger-compiler/src/escapes/local.am new file mode 100644 index 0000000..ab6c63a --- /dev/null +++ b/tiger-compiler/src/escapes/local.am @@ -0,0 +1,7 @@ +## escape module. + +src_libtc_la_SOURCES += \ + %D%/libescapes.hh %D%/libescapes.cc \ + %D%/escapes-visitor.hh %D%/escapes-visitor.cc + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/escapes/tasks.cc b/tiger-compiler/src/escapes/tasks.cc new file mode 100644 index 0000000..42e6919 --- /dev/null +++ b/tiger-compiler/src/escapes/tasks.cc @@ -0,0 +1,38 @@ +/** + ** \file escapes/tasks.cc + ** \brief Escapes module related tasks' implementation. + */ + +#include <ostream> + +#include <ast/libast.hh> +#include <ast/tasks.hh> +#include <escapes/libescapes.hh> +#define DEFINE_TASKS 1 +#include <escapes/tasks.hh> +#undef DEFINE_TASKS +#include <misc/xalloc.hh> + +namespace escapes::tasks +{ + + /*--------------------. + | Static Link tasks. | + `--------------------*/ + + void escapes_compute() { escapes::escapes_compute(*ast::tasks::the_program); } + + /* WARNING. It is very tempting to use BOOLEAN_TASK_DECLARE with + these stream flags, since it factors out the need for the + following routines. Unfortunately when the Tasks are created, + not all the misc::xalloc are instantiated, so the registered + address of these flags is likely to change if there are other + xalloc in between. It is the index that is constant, not the + address of the pword. + + Of course we could have Tasks dedicated to misc::xalloc, but + that's not nice. */ + + void escapes_display() { ast::escapes_display(std::cout) = true; } + +} // namespace escapes::tasks diff --git a/tiger-compiler/src/escapes/tasks.hh b/tiger-compiler/src/escapes/tasks.hh new file mode 100644 index 0000000..636c13f --- /dev/null +++ b/tiger-compiler/src/escapes/tasks.hh @@ -0,0 +1,28 @@ +/** + ** \file escapes/tasks.hh + ** \brief Escapes module related tasks. + */ + +#pragma once + +#include <task/libtask.hh> + +/// The Tasks of the escapes module. +namespace escapes::tasks +{ + TASK_GROUP("3. Escapes"); + + /// Compute variables escaping. + TASK_DECLARE("e|escapes-compute", + "compute the escaping variables " + "and the functions requiring a static link", + escapes_compute, + "bound"); + + /// Display escaped variables. + TASK_DECLARE("E|escapes-display", + "enable escape display in the AST", + escapes_display, + "parse"); + +} // namespace escapes::tasks diff --git a/tiger-compiler/src/inlining/inliner.cc b/tiger-compiler/src/inlining/inliner.cc new file mode 100644 index 0000000..85bbc88 --- /dev/null +++ b/tiger-compiler/src/inlining/inliner.cc @@ -0,0 +1,64 @@ +/** + ** \file inlining/inliner.cc + ** \brief Implementation of inlining::Inliner. + */ + +#include <boost/graph/transitive_closure.hpp> + +#include <ranges> +#include <callgraph/libcallgraph.hh> +#include <inlining/inliner.hh> +#include <parse/libparse.hh> +#include <parse/tweast.hh> + +namespace inlining +{ + using namespace ast; + + Inliner::Inliner(const ast::Ast& tree) + : super_type() + , rec_funs_() + { + // Compute the transitive closure of the call graph to compute the + // set of recursive functions. + const callgraph::CallGraph* graph = callgraph::callgraph_compute(tree); + callgraph::CallGraph closure; + boost::transitive_closure(*graph, closure); + + // Compute the parent graph to get nested functions. + const callgraph::ParentGraph* parentGraph = + callgraph::parentgraph_compute(const_cast<ast::Ast&>(tree)); + + // Re-attach properties to the vertices. + for (auto [i, i_end] = boost::vertices(closure); i != i_end; ++i) + closure[*i] = (*graph)[*i]; + // Detect recursive functions. + for (auto [i, i_end] = boost::vertices(closure); i != i_end; ++i) + { + for (auto [j, j_end] = boost::adjacent_vertices(*i, closure); + j != j_end; ++j) + { + // Check all parent functions to detect cases like these : + // function a() = + // ( + // let + // function b() = a() + // in + // end + // ) + if (parentGraph->hfundec_deep_get(closure[*i], closure[*j])) + rec_funs_.insert(closure[*j]); + } + } + delete graph; + delete parentGraph; + } + + const misc::set<const ast::FunctionDec*>& Inliner::rec_funs_get() const + { + return rec_funs_; + } + + // FIXME: Some code was deleted here. + +} // namespace inlining diff --git a/tiger-compiler/src/inlining/inliner.hh b/tiger-compiler/src/inlining/inliner.hh new file mode 100644 index 0000000..fc2f71f --- /dev/null +++ b/tiger-compiler/src/inlining/inliner.hh @@ -0,0 +1,44 @@ +/** + ** \file inlining/inliner.hh + ** \brief Declaration of inlining::Inliner. + */ + +#pragma once + +#include <map> + +#include <ast/function-dec.hh> +#include <astclone/cloner.hh> +#include <misc/scoped-map.hh> +#include <misc/set.hh> + +namespace inlining +{ + /// Perform inline expansion of functions. + class Inliner : public astclone::Cloner + { + public: + using super_type = astclone::Cloner; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build an Inliner. + Inliner(const ast::Ast& tree); + + /// \name Visit methods. + /// \{ + // FIXME: Some code was deleted here. + /// \} + + /// \name Getters. + /// \{ + const misc::set<const ast::FunctionDec*>& rec_funs_get() const; + /// \} + + private: + /// Recursive functions of the program. + misc::set<const ast::FunctionDec*> rec_funs_; + }; + +} // namespace inlining diff --git a/tiger-compiler/src/inlining/libinlining.cc b/tiger-compiler/src/inlining/libinlining.cc new file mode 100644 index 0000000..3f6e6b9 --- /dev/null +++ b/tiger-compiler/src/inlining/libinlining.cc @@ -0,0 +1,54 @@ +/** + ** \file inlining/libinlining.cc + ** \brief Functions exported by the inlining module. + */ + +#include <memory> + +#include <ast/exp.hh> +#include <desugar/libdesugar.hh> +#include <inlining/inliner.hh> +#include <inlining/libinlining.hh> +#include <inlining/pruner.hh> + +namespace inlining +{ + /*-----------. + | Inlining. | + `-----------*/ + + template <typename A> A* inline_expand(const A& tree) + { + // Inline. + Inliner inline_expand(tree); + inline_expand(tree); + A* inlined = dynamic_cast<A*>(inline_expand.result_get()); + assertion(inlined); + std::unique_ptr<A> inlined_ptr(inlined); + // Recompute the bindings and the types. + desugar::bind_and_types_check(*inlined_ptr); + return inlined_ptr.release(); + } + + template ast::ChunkList* inline_expand(const ast::ChunkList&); + + /*-------------------. + | Function pruning. | + `-------------------*/ + + template <typename A> A* prune(const A& tree) + { + // Prune unused functions. + Pruner prune; + prune(tree); + A* pruned = dynamic_cast<A*>(prune.result_get()); + assertion(pruned); + std::unique_ptr<A> pruned_ptr(pruned); + // Recompute the bindings and the types. + desugar::bind_and_types_check(*pruned_ptr); + return pruned_ptr.release(); + } + + template ast::ChunkList* prune(const ast::ChunkList&); + +} // namespace inlining diff --git a/tiger-compiler/src/inlining/libinlining.hh b/tiger-compiler/src/inlining/libinlining.hh new file mode 100644 index 0000000..183349a --- /dev/null +++ b/tiger-compiler/src/inlining/libinlining.hh @@ -0,0 +1,42 @@ +/** + ** \file inlining/libinlining.hh + ** \brief Declare functions exported by the inlining module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> + +/// Inlining functions of an ast::Ast. +namespace inlining +{ + /*-----------. + | Inlining. | + `-----------*/ + + /** Perform inline expansion of function bodies. + + \param tree abstract syntax tree's root, whose bindings and types + have been computed. Each identifier must also be unique + within the AST to avoid name capture. + + \return the AST where the bodies of non recursive functions have + been expanded (inlined), with bindings and type-checked. + */ + template <typename A> A* inline_expand(const A& tree); + + /*-------------------. + | Function pruning. | + `-------------------*/ + + /** Prune unused function from the AST. + + \param tree abstract syntax tree's root, whose bindings and types + have been computed, and whose identifiers are all unique. + + \return the AST whose unused functions have been removed, with + bindings and type-checked. */ + template <typename A> A* prune(const A& tree); + +} // namespace inlining diff --git a/tiger-compiler/src/inlining/local.am b/tiger-compiler/src/inlining/local.am new file mode 100644 index 0000000..7a127bd --- /dev/null +++ b/tiger-compiler/src/inlining/local.am @@ -0,0 +1,8 @@ +## inlining module. +src_libtc_la_SOURCES += \ + %D%/inliner.hh %D%/inliner.cc \ + %D%/pruner.hh %D%/pruner.cc \ + %D%/libinlining.hh %D%/libinlining.cc + + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/inlining/pruner.cc b/tiger-compiler/src/inlining/pruner.cc new file mode 100644 index 0000000..edb20eb --- /dev/null +++ b/tiger-compiler/src/inlining/pruner.cc @@ -0,0 +1,34 @@ +/** + ** \file inlining/pruner.cc + ** \brief Implementation of inlining::Pruner. + */ + +#include <inlining/pruner.hh> + +namespace inlining +{ + using namespace ast; + + ast::FunctionChunk* Pruner::prune(ast::FunctionChunk& e) + { + while (true) + { + auto [remove_begin, remove_end] = + std::ranges::remove_if(e, [&](ast::FunctionDec* func_dec) { + if (!func_dec->body_get() || func_dec->name_get() == "_main") + return false; + else + return called_functions_[func_dec->name_get()] == 0; + }); + + if (remove_begin == remove_end) + break; + e.erase(remove_begin, remove_end); + } + + return new FunctionChunk(e.location_get(), &e.decs_get()); + } + + // FIXME: Some code was deleted here. + +} // namespace inlining diff --git a/tiger-compiler/src/inlining/pruner.hh b/tiger-compiler/src/inlining/pruner.hh new file mode 100644 index 0000000..30a0609 --- /dev/null +++ b/tiger-compiler/src/inlining/pruner.hh @@ -0,0 +1,42 @@ +/** + ** \file inlining/pruner.hh + ** \brief Declaration of inlining::Pruner. + */ + +#pragma once + +#include <map> + +#include <astclone/cloner.hh> +#include <misc/set.hh> + +namespace inlining +{ + /// Prune useless function declarations within an AST. + class Pruner : public astclone::Cloner + { + public: + using super_type = astclone::Cloner; + + // Import overloaded virtual functions. + using super_type::operator(); + + using func_vect = std::vector<ast::FunctionDec*>; + using func_count = std::map<misc::symbol, int>; + + /// \name Visit methods. + /// \{ + // FIXME: Some code was deleted here. + /// \} + + ast::FunctionChunk* prune(ast::FunctionChunk& e); + + private: + // Keep track of current nested functions + std::vector<const ast::FunctionDec*> current_functions_; + + // Associate function dec with the number of CallExp calling it + func_count called_functions_; + }; + +} // namespace inlining diff --git a/tiger-compiler/src/inlining/tasks.cc b/tiger-compiler/src/inlining/tasks.cc new file mode 100644 index 0000000..f812d03 --- /dev/null +++ b/tiger-compiler/src/inlining/tasks.cc @@ -0,0 +1,31 @@ +/** + ** \file inlining/tasks.cc + ** \brief Inlining module related tasks' implementation. + **/ + +#include <ast/tasks.hh> +#include <astclone/libastclone.hh> +#include <common.hh> +#include <inlining/libinlining.hh> +#define DEFINE_TASKS 1 +#include <inlining/tasks.hh> +#undef DEFINE_TASKS + +namespace inlining::tasks +{ + /*-----------. + | Inlining. | + `-----------*/ + + void inline_expand() + { + astclone::apply(::inlining::inline_expand, ast::tasks::the_program); + } + + /*-------------------. + | Function pruning. | + `-------------------*/ + + void prune() { astclone::apply(::inlining::prune, ast::tasks::the_program); } + +} // namespace inlining::tasks diff --git a/tiger-compiler/src/inlining/tasks.hh b/tiger-compiler/src/inlining/tasks.hh new file mode 100644 index 0000000..6c190ed --- /dev/null +++ b/tiger-compiler/src/inlining/tasks.hh @@ -0,0 +1,36 @@ +/** + ** \file inlining/tasks.hh + ** \brief Inlining module tasks. + */ + +#pragma once + +#include <config.h> +#include <misc/fwd.hh> +#include <task/libtask.hh> + +namespace inlining::tasks +{ + TASK_GROUP("Inlining"); + + /*-----------. + | Inlining. | + `-----------*/ + + /// Expand the body of functions at the call sites. + TASK_DECLARE("inline", + "inline functions", + inline_expand, + "types-compute rename"); + + /*-------------------. + | Function pruning. | + `-------------------*/ + + /// Prune unused function definitions from the AST. + TASK_DECLARE("prune", + "prune unused functions", + prune, + "rename types-compute"); + +} // namespace inlining::tasks diff --git a/tiger-compiler/src/llvmtranslate/escapes-collector.cc b/tiger-compiler/src/llvmtranslate/escapes-collector.cc new file mode 100644 index 0000000..4984805 --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/escapes-collector.cc @@ -0,0 +1,151 @@ +#include <ast/all.hh> +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> +#include <llvmtranslate/escapes-collector.hh> + +namespace llvmtranslate +{ + /// LLVM IR doesn't support static link and nested functions. + /// In order to translate those functions to LLVM IR, we use a technique + /// called Lambda Lifting, which consists in passing a pointer to + /// the escaped variables to the nested function using that variable. + /// + /// In order to do that, we need a visitor to collect these kind of + /// variables and associate them to each function. + + class EscapesCollector + : public ast::DefaultConstVisitor + , public ast::NonObjectConstVisitor + , public ast::NonAssertConstVisitor + { + public: + /// Super class. + using super_type = ast::DefaultConstVisitor; + /// Import overloaded operator() methods. + using super_type::operator(); + + EscapesCollector() + : did_modify_{false} + , escaped_{} + {} + + escaped_map_type& escaped_get() { return escaped_; } + + void operator()(const ast::FunctionChunk& e) override + { + const bool saved_did_modify = did_modify_; + + // Iterate on the chunk in order to iteratively collect all the callee + // functions' escaped variables. + did_modify_ = !e.empty(); + while (did_modify_) + { + did_modify_ = false; + super_type::operator()(e); + } + + did_modify_ = saved_did_modify; + } + + void operator()(const ast::FunctionDec& e) override + { + // Keep track of the current function + // FIXME DONE: Some code was deleted here. + const auto function_type = \ + dynamic_cast<const type::Function*>(&e.type_get()->actual()); + + precondition(function_type != nullptr); + + // Last visited function + const type::Function* previous_scope = current_; + + if (previous_scope == nullptr) + { + // Then we are at the first depth of the program, hence _main + main_function_ = function_type; + } + + current_ = function_type; + + if (!escaped_.contains(current_)) + { + escaped_[current_] = misc::set<const ast::VarDec*>{}; + } + + super_type::operator()(e); + + // Guess we only really need to go back one step before + current_ = previous_scope; + } + + void operator()(const ast::CallExp& e) override + { + super_type::operator()(e); + + // FIXME DONE: Some code was deleted here. + const auto escaped_reference = escaped_.find(current_); + precondition(escaped_reference != escaped_.end()); + const auto escaped_state = escaped_reference->second; + + const size_t before_size = escaped_state.size(); + + super_type::operator()(e.def_get()); + + // Check whether there are any newly collected escaped variables. + // If there are, mark the iteration as modified. + // FIXME DONE: Some code was deleted here. + const size_t after_size = escaped_state.size(); + + did_modify_ = before_size != after_size; + } + + void operator()(const ast::SimpleVar& e) override + { + // Associate escaped variables declared in parent frames with their + // functions + // FIXME DONE: Some code was deleted here. + if (current_ == main_function_ || !e.def_get()->escape_get()) + { + super_type::operator()(e); + return; + } + + // Checking that the variable is escaped because not all variables are + auto attached_to = escaped_.find(current_); + + // We have a function to attach this to + if (attached_to != escaped_.end() + && !attached_to->second.contains(e.def_get())) + { + attached_to->second.insert(e.def_get()); + did_modify_ = true; + } + + super_type::operator()(e); + } + + private: + /// Whether any modification was done during the iteration. + bool did_modify_ = false; + + /// Associate a set of variables with their function. + escaped_map_type escaped_; + + /// Current visiting function. + // FIXME DONE: Some code was deleted here. + const type::Function* current_ = nullptr; + + /// The reference to the main function + const type::Function* main_function_ = nullptr; + }; + + escaped_map_type collect_escapes(const ast::Ast& ast) + { + EscapesCollector collect; + collect(ast); + + return std::move(collect.escaped_get()); + } + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/escapes-collector.hh b/tiger-compiler/src/llvmtranslate/escapes-collector.hh new file mode 100644 index 0000000..dab93c6 --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/escapes-collector.hh @@ -0,0 +1,13 @@ +#pragma once + +#include <ast/fwd.hh> +#include <llvmtranslate/fwd.hh> + +namespace llvmtranslate +{ + + /// Collect all escapes for every function in the ast, and store it in a map. + /// This is used for Lambda Lifting. + escaped_map_type collect_escapes(const ast::Ast& ast); + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/fwd.hh b/tiger-compiler/src/llvmtranslate/fwd.hh new file mode 100644 index 0000000..1eef45e --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/fwd.hh @@ -0,0 +1,31 @@ +#pragma once + +#include <map> + +#include <ast/fwd.hh> +#include <misc/set.hh> +#include <type/fwd.hh> + +namespace llvm +{ + // From llvm/IR/Module.h. + class Module; + + // From llvm/IR/DerivedTypes.h + class StructType; + + // From llvm/IR/Type.h + class Type; + + // From llvm/IR/LLVMContext.h + class LLVMContext; + +} // namespace llvm + +namespace llvmtranslate +{ + using escaped_map_type = + std::map<const type::Function*, misc::set<const ast::VarDec*>>; + using frame_map_type = escaped_map_type; + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/generate-runtime.sh b/tiger-compiler/src/llvmtranslate/generate-runtime.sh new file mode 100755 index 0000000..2e05445 --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/generate-runtime.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +[ -z "$1" ] && echo "$0: Missing input file" && exit +[ -z "$2" ] && echo "$0: Missing output file" && exit + +input=$1 +output=$2 + +awk 'BEGIN { + print("#include <string>"); + print("#include <llvmtranslate/libllvmtranslate.hh>"); + print(""); + print("namespace llvmtranslate"); + print("{"); + print("const char* runtime_string()"); + print("{"); + print(" return"); + printf("R\"0("); # The zero here only serves as an identifier for the raw string, it does not changes the code behavior at all. + } + /^#(<<|>>)/ { + next; + } + { + print($0); + } + END { + print(")0\";"); + print("}"); + print("} // namespace llvmtranslate"); + }' "$input" > "$output".tmp + +mv "$output".tmp "$output" diff --git a/tiger-compiler/src/llvmtranslate/libllvmtranslate.cc b/tiger-compiler/src/llvmtranslate/libllvmtranslate.cc new file mode 100644 index 0000000..b86b03a --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/libllvmtranslate.cc @@ -0,0 +1,47 @@ +/** + ** \file llvmtranslate/libllvmtranslate.hh + ** \brief Public llvmtranslate module interface implementation. + **/ + +#include <ast/default-visitor.hh> +#include <ast/non-object-visitor.hh> +#include <common.hh> // program_name + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include <llvm/AsmParser/Parser.h> +#include <llvm/IR/LLVMContext.h> +#include <llvm/IR/Module.h> +#include <llvm/IR/Verifier.h> +#include <llvm/Support/SourceMgr.h> // llvm::SMDiagnostic +#include <llvmtranslate/escapes-collector.hh> +#include <llvmtranslate/fwd.hh> +#include <llvmtranslate/libllvmtranslate.hh> +#include <llvmtranslate/translator.hh> + +#pragma GCC diagnostic pop + +namespace llvmtranslate +{ + std::pair<std::unique_ptr<llvm::LLVMContext>, std::unique_ptr<llvm::Module>> + translate(const ast::Ast& the_program) + { + auto ctx = std::make_unique<llvm::LLVMContext>(); + auto module = std::make_unique<llvm::Module>(program_name, *ctx); + + Translator translate{*module, collect_escapes(the_program)}; + translate(the_program); + + llvm::verifyModule(*module); + + return {std::move(ctx), std::move(module)}; + } + + std::unique_ptr<llvm::Module> runtime_get(llvm::LLVMContext& ctx) + { + llvm::SMDiagnostic diag; + return llvm::parseAssemblyString(runtime_string(), diag, ctx); + } + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/libllvmtranslate.hh b/tiger-compiler/src/llvmtranslate/libllvmtranslate.hh new file mode 100644 index 0000000..cf431e9 --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/libllvmtranslate.hh @@ -0,0 +1,31 @@ +/** + ** \file llvmtranslate/libllvmtranslate.hh + ** \brief Public llvmtranslate module interface declaration. + */ + +#pragma once + +#include <memory> +#include <utility> + +#include <llvm/IR/LLVMContext.h> + +#include <ast/fwd.hh> +#include <llvmtranslate/fwd.hh> + +/// Translation from ast::Ast to llvm::Value. +namespace llvmtranslate +{ + /// Translate the file into a llvm::Module. + std::pair<std::unique_ptr<llvm::LLVMContext>, std::unique_ptr<llvm::Module>> + translate(const ast::Ast& the_program); + + /// Load the runtime as a llvm::Module. + std::unique_ptr<llvm::Module> runtime_get(llvm::LLVMContext& ctx); + + /// The LLVM runtime as a string, loaded from the generated file. + /// This function is implemented in $(build_dir)/src/llvmtranslate/runtime.cc + /// For more information take a look at `local.am`. + const char* runtime_string(); + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/llvm-type-visitor.cc b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.cc new file mode 100644 index 0000000..a2130c9 --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.cc @@ -0,0 +1,110 @@ +/** + ** \file llvmtranslate/llvm-type-visitor.cc + ** \brief Implementation of llvmtranslator::LLVMTypeVisitor. + */ + +#include <llvm/IR/DerivedTypes.h> + +#include <llvmtranslate/llvm-type-visitor.hh> +#include <type/types.hh> + +namespace llvmtranslate +{ + LLVMTypeVisitor::LLVMTypeVisitor(llvm::LLVMContext& ctx) + : ctx_{ctx} + {} + + llvm::Type* LLVMTypeVisitor::llvm_type_get() { return type_; } + + llvm::Type* LLVMTypeVisitor::get_record_ltype(const type::Record* e) + { + return structs_[e]; + } + + llvm::Type* LLVMTypeVisitor::llvm_type(const type::Type& type) + { + operator()(type); + return type_; + } + + void LLVMTypeVisitor::operator()(const type::Nil& e) + { + if (auto record_type = e.record_type_get()) + type_ = llvm_type(*record_type); + else + unreachable(); + } + + void LLVMTypeVisitor::operator()(const type::Void&) + { + // FIXME DONE: Some code was deleted here (Void types can be interpreted as int or void type). + // Je cite : "a variable can hold a void value (that are represented using an integer in LLVM)" + // Mais du coup, je sais pas ce qui détermine quel type on lui donne. + // En attendant je mets void tout le temps jusqu'à ce qu'on trouve pourquoi on le mettrait à int + // Y a un monde où ça se gère au dessus (genre les vardecs et les assignexp ?) + // Source : Un YAKA qui dit que en gros on a le choix mais qu'il vaut mieux le représenter comme int + // Du coup pour le distinguer c'est un int sur 0 bits + type_ = llvm::Type::getIntNTy(ctx_,0); + } + + void LLVMTypeVisitor::operator()(const type::Int&) + { + type_ = llvm::Type::getInt32Ty(ctx_); + } + + void LLVMTypeVisitor::operator()(const type::String&) + { + // Strings are pointers to characters in LLVM. + // FIXME DONE: Some code was deleted here. + // Bon vu qu'on a pas accès à la taille de la string on y va yolo. + // Du coup c'est un PointerType de Int8 (du coup 8 bits pour un char) + type_ = llvm::PointerType::get(llvm::Type::getInt8Ty(ctx_), 0); + } + + void LLVMTypeVisitor::operator()(const type::Named& e) + { + // FIXME DONE: Some code was deleted here. + llvm_type(e.actual()); + + const std::string reference_name = e.name_get().get(); + + if (const auto record_ref = dynamic_cast<const type::Record*>(&e.type_get()->actual()); + record_ref != nullptr && structs_[record_ref]->getName() != reference_name) + { + structs_[record_ref]->setName(reference_name); + } + } + + void LLVMTypeVisitor::operator()(const type::Record& e) + { + // If the record was never translated, translate it + if (!structs_[&e]) + { + // We need to create the struct in two passes to support recursive + // types, like 'type a = { next : a } + // So, first we create an empty struct + structs_[&e] = llvm::StructType::create(ctx_); + // Then set the body of the structure + std::vector<llvm::Type*> field_types; + field_types.reserve(e.fields_get().size()); + // FIXME DONE: Some code was deleted here. + //Martial: Copy and convert the fields ig + for (const auto& field : e.fields_get()) + { + super_type::operator()(field.type_get()); + field_types.push_back(type_); + } + //Martial: Set the body with the converted types + structs_[&e]->setBody(std::move(field_types), false); + } + type_ = llvm::PointerType::getUnqual(structs_[&e]); + } + + void LLVMTypeVisitor::operator()(const type::Array& e) + { + // Arrays are pointers to the array elements, like in C. + // FIXME DONE: Some code was deleted here. + llvm_type(*e.get_element_type()); + type_ = llvm::PointerType::getUnqual(type_); + } +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/llvm-type-visitor.hh b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.hh new file mode 100644 index 0000000..d19ea9d --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.hh @@ -0,0 +1,50 @@ +/** + ** \file llvmtranslate/llvm-type-visitor.hh + ** \brief Definition of llvmtranslator::LLVMTypeVisitor. + */ + +#pragma once + +#include <llvm/IR/LLVMContext.h> + +#include <llvmtranslate/fwd.hh> +#include <type/default-visitor.hh> + +namespace llvmtranslate +{ + /// Visit a Type and return the corresponding llvm::Type. + class LLVMTypeVisitor : public type::DefaultConstVisitor + { + public: + using super_type = type::DefaultConstVisitor; + // Import overloaded virtual functions. + using super_type::operator(); + + LLVMTypeVisitor(llvm::LLVMContext& ctx); + + llvm::Type* llvm_type_get(); + llvm::Type* get_record_ltype(const type::Record* e); + + /// Visit methods. + /// \{ + void operator()(const type::Nil& e) override; + void operator()(const type::Void& e) override; + void operator()(const type::Int& e) override; + void operator()(const type::String& e) override; + void operator()(const type::Named& e) override; + void operator()(const type::Record& e) override; + void operator()(const type::Array& e) override; + /// \} + + private: + /// The global context. + llvm::LLVMContext& ctx_; + /// The type of the last visited node. + llvm::Type* type_ = nullptr; + /// llvm::StructTypes for each type::Record + std::map<const type::Record*, llvm::StructType*> structs_; + /// Recurse and return the type_. + llvm::Type* llvm_type(const type::Type& type); + }; + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/local.am b/tiger-compiler/src/llvmtranslate/local.am new file mode 100644 index 0000000..d5a752b --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/local.am @@ -0,0 +1,39 @@ +# Compile the LLVM Tiger runtime +EXTRA_DIST += %D%/tiger-runtime.c +CLEANFILES += %D%/runtime.ll +%D%/runtime.ll: %D%/tiger-runtime.c + $(AM_V_CC)$(CLANG) -c -m32 -std=c99 -emit-llvm -S -o $@ $^ + +LLVM_RUNTIME_GENERATION = %D%/generate-runtime.sh +EXTRA_DIST += $(LLVM_RUNTIME_GENERATION) +CLEANFILES += %D%/runtime.cc +%D%/runtime.cc: %D%/runtime.ll + $(AM_V_GEN)$(srcdir)/$(LLVM_RUNTIME_GENERATION) $< $@ + +## llvmtranslate module. +src_libtc_la_SOURCES += \ + %D%/escapes-collector.cc %D%/escapes-collector.hh \ + %D%/translator.hh %D%/translator.hxx %D%/translator.cc \ + %D%/libllvmtranslate.cc %D%/libllvmtranslate.hh \ + %D%/llvm-type-visitor.cc %D%/llvm-type-visitor.hh \ + %D%/fwd.hh + +nodist_src_libtc_la_SOURCES += %D%/runtime.cc + +## FIXME: Add SWIG support for this module + +## LLVM flags are found using `llvm-config`. + +if STATIC_LLVM +EXTRA_LLVM_CONFIG_FLAGS = --link-static +else +EXTRA_LLVM_CONFIG_FLAGS = +endif + +AM_CXXFLAGS += `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --cppflags` +src_libtc_la_LDFLAGS += \ + `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --ldflags` \ + `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --libs core linker asmparser irprinter` \ + `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --system-libs` + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/llvmtranslate/tasks.cc b/tiger-compiler/src/llvmtranslate/tasks.cc new file mode 100644 index 0000000..09f865c --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/tasks.cc @@ -0,0 +1,64 @@ +/** + ** \file llvmtranslate/tasks.cc + ** \brief LLVM Translate tasks. + */ + +#include <memory> + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include <llvm/Config/llvm-config.h> // LLVM_VERSION_* +#include <llvm/IR/Module.h> +#include <llvm/IR/PassManager.h> +#include <llvm/IRPrinter/IRPrintingPasses.h> +#include <llvm/Linker/Linker.h> +#include <llvm/Support/raw_ostream.h> // llvm::outs() + +#pragma GCC diagnostic pop + +#include <ast/tasks.hh> +#include <llvmtranslate/fwd.hh> +#include <llvmtranslate/libllvmtranslate.hh> +#define DEFINE_TASKS 1 +#include <llvmtranslate/tasks.hh> +#undef DEFINE_TASKS + +namespace llvmtranslate::tasks +{ + std::pair<std::unique_ptr<llvm::LLVMContext>, std::unique_ptr<llvm::Module>> + module = {nullptr, nullptr}; + + /// Translate the AST to LLVM IR. + void llvm_compute() { module = translate(*ast::tasks::the_program); } + + /// Display the LLVM IR. + void llvm_display() + { + // If the runtime has to be displayed, get the runtime module, + // link it with the program module and print it. + if (llvm_runtime_display_p) + { + auto runtime = runtime_get(*module.first); +#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 7 + auto link = + llvm::Linker::LinkModules(module.second.get(), runtime.get()); +#else + auto link = + llvm::Linker::linkModules(*module.second, std::move(runtime)); +#endif + (void)link; + postcondition(!link); // Returns true on error + } + + auto& out = llvm::outs(); + llvm::PrintModulePass printer{out}; +#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 8 + printer.run(*module.second); +#else + llvm::ModuleAnalysisManager dummy_mam; + printer.run(*module.second, dummy_mam); +#endif + } + +} // namespace llvmtranslate::tasks diff --git a/tiger-compiler/src/llvmtranslate/tasks.hh b/tiger-compiler/src/llvmtranslate/tasks.hh new file mode 100644 index 0000000..ade231a --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/tasks.hh @@ -0,0 +1,34 @@ +#pragma once + +#include <llvmtranslate/fwd.hh> +#include <task/libtask.hh> + +namespace llvmtranslate::tasks +{ + /// The global translation module of ast;:tasks::the_program. + extern std::pair<std::unique_ptr<llvm::LLVMContext>, + std::unique_ptr<llvm::Module>> + module; + + TASK_GROUP("5.5. Translation to LLVM Intermediate Representation"); + + /// Translate the AST to LLVM IR. + TASK_DECLARE("llvm-compute", + "translate to LLVM IR", + llvm_compute, + "typed desugar-for desugar-string-cmp desugar escapes-compute"); + + /// Activate displaying the runtime along with the LLVM IR. + BOOLEAN_TASK_DECLARE("llvm-runtime-display", + "enable runtime displaying" + "along with the LLVM IR", + llvm_runtime_display_p, + ""); + + /// Display the LLVM IR. + TASK_DECLARE("llvm-display", + "display the LLVM IR", + llvm_display, + "llvm-compute"); + +} // namespace llvmtranslate::tasks diff --git a/tiger-compiler/src/llvmtranslate/tiger-runtime.c b/tiger-compiler/src/llvmtranslate/tiger-runtime.c new file mode 100644 index 0000000..f952e0f --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/tiger-runtime.c @@ -0,0 +1,304 @@ +/** + \file llvm/tiger-runtime.c + \brief C Implementation of the Tiger runtime. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#define EXIT_RUNTIME_FAILURE 120 + +// FWD, declared by tc +void tc_main(int); + +/** \name Internal functions (calls generated by the compiler only). */ +/** \{ */ + +/** \brief Allocate an array and fill it with a default value. + \param size The size of the array. + \param elt The default element. + + An element size is always the size of a word on 32-bit systems. +*/ +int *tc_init_array(int size, int elt) +{ + int *arr = (int *)malloc(size * sizeof (elt)); + for (size_t i = 0; i < size; ++i) + arr[i] = elt; + return arr; +} +/** \} */ + +/** \name Miscellaneous */ +/** \{ */ + +/** \brief Equivalent to operator! in C. + \param i The integer to be inversed. +*/ +int tc_not(int i) +{ + return !i; +} + +/** \brief Exit the program with the desired status. + \param status The status code. +*/ +void tc_exit(int status) +{ + exit(status); +} +/** \} */ + +/** \name Operations on strings. */ +/** \{ */ + +// Small trick. Add a \0 after each char in the consts +// This is filled in tc_main +static char consts[512] = { 0 }; + +/** \brief Get a string containing the character represented by the ascii value + * of \a i. + \param i The ascii value of the desired character. +*/ +const char *tc_chr(int i) +{ + if (!(0 <= i && i <= 255)) + { + fputs("chr: character out of range\n", stderr); + exit(EXIT_RUNTIME_FAILURE); + } + return consts + i * 2; +} + +/** \brief Concatenate two strings. + \param a The first string. + \param b The second string. +*/ +const char *tc_concat(const char *a, const char *b) +{ + size_t len_a = strlen(a); + size_t len_b = strlen(b); + if (len_a == 0) + return b; + else if (len_b == 0) + return a; + else + { + int i = 0; + int n = len_a + len_b; + char *t = (char *) malloc(n + 1); + for (i = 0; i < len_a; i++) + t[i] = a[i]; + for (i = 0; i < len_b; i++) + t[i + len_a] = b[i]; + t[n] = '\0'; + return t; + } +} + +/** \brief Get the ascii value of a character. + \param s The string representing the desired character to be converted. + + Inverse of `tc_chr`. +*/ +int tc_ord(const char *s) +{ + size_t len = strlen(s); + if (len == 0) + return -1; + else + return s[0]; +} + +/** \brief Get the size of a string. + \param s The string. +*/ +int tc_size(const char *s) +{ + return strlen(s); +} + +/** \brief Return a part of the string \a s. + \param s The source string + \param first The first character of the extraction (starting at 0) + \param n The number of characters to get. + + \a first and \a n must be positive, and one shall not go beyond the size + of \a s. Otherwise, exit with runtime failure. +*/ +const char *tc_substring(const char *s, int first, int n) +{ + size_t len = strlen(s); + if (!(0 <= first + && 0 <= n + && first + n <= len)) + { + fputs("substring: arguments out of bounds\n", stderr); + exit(EXIT_RUNTIME_FAILURE); + } + + if (n == 1) + return consts + s[first] * 2; + else + { + char *t = (char *) malloc(n + 1); + for (int i = 0; i < n; i++) + t[i] = s[first + i]; + t[n] = '\0'; + return t; + } +} + +/** \brief Compare two strings. + \param lhs The first string. + \param rhs The second string. +*/ +int tc_strcmp(const char *lhs, const char *rhs) +{ + return strcmp(lhs, rhs); +} + +/** \brief Return a part of the string \a s. + \param s The source string + \param first The first character of the extraction (starting at 0) + \param n The number of characters to get. + + \a first and \a n must be positive, and one shall not go beyond the size + of \a s. Otherwise, exit with runtime failure. +*/ +int tc_streq(const char *lhs, const char *rhs) +{ + return strcmp(lhs, rhs) == 0; +} +/** \} */ + +/** \name Input/Output. */ +/** \{ */ + +/** \brief Get a character from the standard input. +*/ +const char *tc_getchar() +{ + int i = getc(stdin); + if (i == EOF) + return ""; + else + return consts + i * 2; +} + +/** \brief Print a string on the standard output. + \param s The string to be printed. +*/ +void tc_print(const char *s) +{ + printf("%s", s); +} + +/** \brief Print a string on the standard error. + \param s The string to be printed. +*/ +void tc_print_err(const char *s) +{ + fprintf(stderr, "%s", s); +} + +/** \brief Print an int on the standard error. + \param i The int to be printed. +*/ +void tc_print_int(int i) +{ + printf("%d", i); +} + +/** \brief Flush the standard output. +*/ +void tc_flush() +{ + fflush(stdout); +} + +/** \} */ + +/** \name Custom primitives. */ +/** \{ */ + +/** \brief Terminate the program with an error if condition if false + \param condition Condition to assert, represented as a Tiger 32 bits integer + \param instruction_str The condition represented as a string, used for + pretty-printing. Can be NULL + \param filename File in which the assertion comes from. Can be NULL +*/ +void tc_assertion(const int condition, const char* instruction_str, + const char* filename) +{ + if (condition) + { + return; + } + + if (instruction_str == NULL) + { + instruction_str = ""; + } + + if (filename == NULL) + { + filename = ""; + } + + fprintf(stderr, "%s: %s assertion is false. aborting\n", filename, + instruction_str); + abort(); +} + +/** + * \brief Proceed to a fork syscall and return the resulting process id. + * \return the resulting pid for the parent, 0 for the child, or -1 if the + * process forking failed + */ +int tc_fork() + { + return fork(); + } + +/** + * \brief Wait for the process specified by the provided id to end and return + * the resulting status code. + * \param pid a process, referenced by its identifier + * \return the process' status code + */ +int tc_wait_pid(int pid) + { + int status_code = -1; + waitpid(pid, &status_code, WUNTRACED | WCONTINUED); + return status_code; + } + +/** + * \brief Determine the exit code specified by a status code and return it. + * \param status_code any process status code + * \return the implied exit code + */ +int tc_get_exit_code(int status_code) + { + return WEXITSTATUS(status_code); + } + +/** \} */ + +int main() +{ + // Fill the `consts` array with every character in the ascii table, followed + // by a null character. + for (int i = 0; i < 512; i += 2) + { + consts[i] = i / 2; + consts[i + 1] = 0; + } + + tc_main(0); + return 0; +} diff --git a/tiger-compiler/src/llvmtranslate/translator.cc b/tiger-compiler/src/llvmtranslate/translator.cc new file mode 100644 index 0000000..2c27db5 --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/translator.cc @@ -0,0 +1,737 @@ +/** + ** \file llvmtranslate/translator.cc + ** \brief Implementation of llvmtranslate::Translator + */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include <llvm/Config/llvm-config.h> // LLVM_VERSION_* +#include <llvm/IR/LLVMContext.h> +#include <llvm/IR/Verifier.h> // llvm::verifyFunction +#include <llvm/Support/Casting.h> +#include <llvm/TargetParser/Host.h> // llvm::sys +#include <llvm/TargetParser/Triple.h> + +#pragma GCC diagnostic pop + +#include <ast/all.hh> +#include <llvmtranslate/translator.hh> + +#include "llvm-type-visitor.hh" + +namespace llvmtranslate +{ + using namespace std::string_literals; + + namespace + { + // Shorthands for integer type and pointer to integer type. + inline llvm::IntegerType* i32_t(llvm::LLVMContext& ctx) + { + return llvm::Type::getInt32Ty(ctx); + } + + inline llvm::PointerType* i32p_t(llvm::LLVMContext& ctx) + { + return llvm::PointerType::get(llvm::Type::getInt32Ty(ctx), 0); + } + + llvm::AllocaInst* create_alloca(llvm::Function* ll_function, + llvm::Type* ll_type, + const std::string& name) + { + // Create an IRBuilder starting at the beginning of the current entry + // block. LLVM treats allocas as local variables only if they occur at the + // beginning of a function. + llvm::IRBuilder<> tmp(&ll_function->getEntryBlock(), + ll_function->getEntryBlock().begin()); + return tmp.CreateAlloca(ll_type, nullptr, name); + } + + // Set default attributes to the functions + void set_default_attributes(llvm::Function& the_function, + const ast::FunctionDec& e) + { + the_function.addFnAttr(llvm::Attribute::NoUnwind); // No exceptions in TC + if (!e.body_get()) // Inline primitives + the_function.addFnAttr(llvm::Attribute::InlineHint); + } + + std::string function_dec_name(const ast::FunctionDec& e) + { + // Rename "_main" to "tc_main" + if (e.name_get() == "_main") + return "tc_main"; + // Prefix all the primitives with "tc_" + if (!e.body_get()) + return "tc_" + e.name_get().get(); + return e.name_get().get(); + } + } // namespace + + Translator::Translator(llvm::Module& module, escaped_map_type&& escaped) + : module_{module} + , ctx_{module_.getContext()} + , builder_{ctx_} + , escaped_{std::move(escaped)} + , type_visitor_{ctx_} + { + // The current process triple. + auto process_triple = llvm::Triple(llvm::sys::getProcessTriple()); + // Set the 32-bit version of the triple. + module_.setTargetTriple(process_triple.get32BitArchVariant().str()); + } + + void Translator::operator()(const ast::Ast& e) + { + translate(e); + value_ = nullptr; + } + + llvm::Value* Translator::translate(const ast::Ast& node) + { + node.accept(*this); + return value_; + } + + llvm::Value* Translator::access_var(const ast::Var& e) + { + if (auto var_ast = dynamic_cast<const ast::SimpleVar*>(&e)) + { + // FIXME DONE: Some code was deleted here. + // Chopper la VarDec + const ast::VarDec* dec = var_ast->def_get(); + if (dec == nullptr) + { + // This is probably a binder error + unreachable(); + } + // getFunction --> getValueSymbolTable --> lookup + auto all_vars = locals_[current_function_]; + // Ou si c'est un argument, juste iterer sur les arguments jusqu'a trouver la bonne + return all_vars[dec]; + + } + else if (auto arr_ast = dynamic_cast<const ast::SubscriptVar*>(&e)) + { + // FIXME DONE: Some code was deleted here. + // Here we have the case of an access in an array (var[index]) + // Faire un call recursif sur la variable parent afin d'obtenir une reference + llvm::Value* mother_val = translate(arr_ast->var_get()); + + // Utiliser cette ref pour trouver le type associe + llvm::Type* mother_ty = llvm_type(*(arr_ast->var_get().type_get())); + + // Creer une array ref afin de pouvoir stocker les indexs + // Ici, l'index en question est index_ de la variable e + llvm::Value* index_exp = translate(arr_ast->index_get()); + //std::vector<llvm::Value*> = + const auto index = llvm::ArrayRef(index_exp); + // Et il n'y a plus qu'a cree tout ca en utilisant un llvm::IRBuilderBase::CreateGEP + + return builder_.CreateGEP(mother_ty, mother_val, index, "subscriptptr_"s + "some_index"); + } + else if (auto field_ast = dynamic_cast<const ast::FieldVar*>(&e)) + { + const ast::Var* var = nullptr; + // FIXME DONE: Some code was deleted here. + // Wait, isn't this going to recurse and create many instances of the thing? + // Or it is just a small piece of the path towards the full path to this var? + var = &(field_ast->var_get()); + + auto var_val = translate(*var); + + const type::Record* record_type = nullptr; + // FIXME DONE: Some code was deleted here. + record_type = dynamic_cast<const type::Record*>(&var->type_get()->actual()); + + if (record_type == nullptr) + { + // This could be a typing error (we do not have a var pointing to a record?) + unreachable(); + } + + misc::symbol field_name; + // FIXME DONE: Some code was deleted here. + field_name = field_ast->name_get(); + + int index = -1; + // FIXME DONE: Some code was deleted here (Get the index of the field). + index = record_type->field_index(field_name); + + // The GEP instruction provides us with safe pointer arithmetics, + // usually used with records or arrays. + llvm::Type* record_ltype = nullptr; + // FIXME DONE: Some code was deleted here (Get record's corresponding LLVM type). + llvm_type(*record_type); + record_ltype = type_visitor_.get_record_ltype(record_type); + + return builder_.CreateStructGEP(record_ltype, var_val, index, + "fieldptr_"s + field_name.get()); + } + else + unreachable(); + } + + llvm::Value* Translator::init_array(llvm::Value* count_val, + llvm::Value* init_val) + { + // Cast everything so that it is conform to the signature of init_array + // int *init_array(int, int) + + // We need to separate the pointers and the ints. + // LLVM requires a ptrtoint instruction for pointers + // and a bitcast for others. + auto init_val_cast = init_val->getType()->isPointerTy() + ? builder_.CreatePtrToInt(init_val, i32_t(ctx_), "init_array_ptrtoint") + : builder_.CreateBitCast(init_val, i32_t(ctx_), "init_array_bitcast"); + + // Create the init_array function: + // First, the arguments (int*, int, int) + std::vector<llvm::Type*> arg_type{i32_t(ctx_), init_val_cast->getType()}; + + // Then, create the FunctionType. + auto init_array_ltype = + llvm::FunctionType::get(i32p_t(ctx_), arg_type, false); + + // Get the llvm::Function from the module related to the name and type + auto init_array_function = + module_.getOrInsertFunction("tc_init_array", init_array_ltype); + + // Prepare the arguments. + std::vector<llvm::Value*> arg_vals{count_val, init_val_cast}; + + // Create the call. + auto init_array_call = + builder_.CreateCall(init_array_function, arg_vals, "init_array_call"); + + // Cast the result of the call in the desired type. + return builder_.CreateBitCast(init_array_call, + init_val->getType()->getPointerTo(), + "init_array_call_cast"); + } + + llvm::Type* Translator::llvm_type(const type::Type& type) + { + type_visitor_(type); + return type_visitor_.llvm_type_get(); + } + + llvm::FunctionType* + Translator::llvm_function_type(const type::Function& function_type) + { + // Prepare the arguments + std::vector<llvm::Type*> args_types; + // First, if there are any escaped vars, create ptr arguments for it + // (Lambda lifting) + + if (auto escapes_it = escaped_.find(&function_type); + escapes_it != std::end(escaped_)) + { + auto& escapes = escapes_it->second; + args_types.reserve(escapes.size() + + function_type.formals_get().fields_get().size()); + for (const auto dec : escapes) + { + llvm::Type* var_ltype = nullptr; + // FIXME DONE: Some code was deleted here (Get the llvm type of the VarDec). + var_ltype = llvm_type(*dec->type_get()); + + args_types.emplace_back(llvm::PointerType::getUnqual(var_ltype)); + } + } + else + args_types.reserve(function_type.formals_get().fields_get().size()); + + // Then, the actual arguments + for (const auto& field : function_type.formals_get()) + args_types.emplace_back(llvm_type(field.type_get())); + + llvm::Type* result_ltype = nullptr; + // FIXME DONE: Some code was deleted here (If the result is void typed, we assign llvm void type to result_ltype). + result_ltype = dynamic_cast<const type::Void*>(&function_type.result_get().actual()) != nullptr + ? llvm::Type::getVoidTy(ctx_) + : llvm_type(function_type.result_get()); + + return llvm::FunctionType::get(result_ltype, args_types, false); + } + + void Translator::operator()(const ast::SimpleVar& e) + { + // Void var types are actually Ints represented by a 0 + // FIXME DONE: Some code was deleted here. + value_ = access_var(e); + } + + void Translator::operator()(const ast::FieldVar& e) + { + // FIXME DONE: Some code was deleted here. + value_ = access_var(e); + } + + void Translator::operator()(const ast::SubscriptVar& e) + { + // FIXME DONE: Some code was deleted here. + value_ = access_var(e); + } + + void Translator::operator()(const ast::NilExp& e) + { + // FIXME DONE: Some code was deleted here (Get the record_type of the Nil type, and create a null pointer). + const llvm::Type* pointer_type = llvm_type(*e.type_get()); + value_ = llvm::ConstantPointerNull::get(pointer_type->getPointerTo()); + } + + void Translator::operator()(const ast::IntExp& e) + { + // FIXME DONE: Some code was deleted here (Integers in Tiger are all 32bit signed). + value_ = builder_.getInt32(e.value_get()); + } + + void Translator::operator()(const ast::StringExp& e) + { + // FIXME DONE: Some code was deleted here (Strings are translated as `i8*` values, like C's `char*`). + value_ = builder_.CreateGlobalStringPtr(e.value_get(), "str_exp"); + } + + void Translator::operator()(const ast::RecordExp& e) + { + // Get the record type + const type::Record* record_type = nullptr; + // FIXME DONE: Some code was deleted here. + record_type = dynamic_cast<const type::Record*>(&e.type_get()->actual()); + + // Type the record and use get_record_ltype() to get its LLVM type + llvm_type(*record_type); + auto struct_ltype = type_visitor_.get_record_ltype(record_type); + + // The size of the structure and cast it to int + auto sizeof_val = llvm::ConstantExpr::getSizeOf(struct_ltype); + sizeof_val = llvm::ConstantExpr::getTruncOrBitCast(sizeof_val, i32_t(ctx_)); + + // Generate the instruction calling Malloc + auto malloc_val = builder_.CreateMalloc( + i32_t(ctx_), struct_ltype, sizeof_val, nullptr, nullptr, "malloccall"); + + // Init the fields + // FIXME DONE: Some code was deleted here. + auto& fields = e.fields_get(); + + assertion(fields.size() == record_type->fields_get().size()); + + for (const auto field : fields) + { + const auto field_value = translate(field->init_get()); + const auto field_index = record_type->field_index(field->name_get()); + + assertion(field_index >= 0); + + const auto field_llvm_address = builder_.CreateStructGEP( + struct_ltype, malloc_val, field_index, "field_" + field->name_get().get() + ); + + value_ = builder_.CreateStore(field_value, field_llvm_address); + } + + value_ = malloc_val; + } + + void Translator::operator()(const ast::OpExp& e) + { + // FIXME DONE: Some code was deleted here. + // The comparison instructions returns an i1, and we need an i32, since everything + // is an i32 in Tiger. Use a zero-extension to avoid this. + const auto left_operand = \ + get_dereferenced(translate(e.left_get()), e.left_get().type_get()); + const auto right_operand = \ + get_dereferenced(translate(e.right_get()), e.right_get().type_get()); + + switch (e.oper_get()) + { + case ast::OpExp::Oper::add: + value_ = builder_.CreateAdd(left_operand, right_operand, "op_add"); + break; + case ast::OpExp::Oper::sub: + value_ = builder_.CreateSub(left_operand, right_operand, "op_sub"); + break; + case ast::OpExp::Oper::mul: + value_ = builder_.CreateMul(left_operand, right_operand, "op_mul"); + break; + case ast::OpExp::Oper::div: + value_ = builder_.CreateSDiv(left_operand, right_operand, "op_div"); + break; + case ast::OpExp::Oper::eq: + value_ = builder_.CreateICmpEQ(left_operand, right_operand, "op_eq"); + break; + case ast::OpExp::Oper::ne: + value_ = builder_.CreateICmpNE(left_operand, right_operand, "op_ne"); + break; + case ast::OpExp::Oper::gt: + value_ = builder_.CreateICmpSGT(left_operand, right_operand, "op_gt"); + break; + case ast::OpExp::Oper::ge: + value_ = builder_.CreateICmpSGE(left_operand, right_operand, "op_ge"); + break; + case ast::OpExp::Oper::lt: + value_ = builder_.CreateICmpSLT(left_operand, right_operand, "op_lt"); + break; + case ast::OpExp::Oper::le: + value_ = builder_.CreateICmpSLE(left_operand, right_operand, "op_le"); + break; + } + + value_ = builder_.CreateZExtOrTrunc(value_, i32_t(ctx_), "op_zext"); + } + + void Translator::operator()(const ast::SeqExp& e) + { + // An empty SeqExp is an empty expression, so we should return an int + // containing 0, since its type is void. + // FIXME DONE: Some code was deleted here. + if (e.exps_get().empty()) + { + value_ = get_void_value(); + return; + } + + for (const auto& exp : e.exps_get()) + { + translate(*exp); + + if (dynamic_cast<const ast::Var*>(exp) != nullptr) + { + value_ = get_dereferenced(value_, exp->type_get()); + } + } + } + + void Translator::operator()(const ast::AssignExp& e) + { + // FIXME DONE: Some code was deleted here. + llvm::Value* value = \ + get_dereferenced(translate(e.exp_get()), e.exp_get().type_get()); + llvm::Value* variable = translate(e.var_get()); + + value_ = builder_.CreateStore(value, variable); + } + + void Translator::operator()(const ast::IfExp& e) + { + // FIXME: Some code was deleted here (IfExps are handled in a similar way to Kaleidoscope (see LangImpl5.html)). + llvm::Value* cond = translate(e.test_get()); + + auto zero_val = llvm::ConstantInt::getSigned(cond->getType(), 0); + + // The condition may not be correct we are checking that this is different than 0 + cond = builder_.CreateICmpNE(cond, zero_val, "ifcond"); + + // We create the blocks + auto then_block = llvm::BasicBlock::Create(ctx_, "then_body", current_function_); + auto else_block = llvm::BasicBlock::Create(ctx_, "else_body", current_function_); + auto after_if = llvm::BasicBlock::Create(ctx_, "after_if", current_function_); + + // Explicitely create the if statement + builder_.CreateCondBr(cond, then_block, else_block); + + // We now tell the builder to insert newly added code to this block + builder_.SetInsertPoint(then_block); + + llvm::Value* then_body = translate(e.thenclause_get()); + + // Create an unconditional jump to the end of the if statement + builder_.CreateBr(after_if); + + // Very akward case where we have an if inside another if + // Apparently this is done to not disturb the PHI node + then_block = builder_.GetInsertBlock(); + + // We add the else block to the current function + // Function is private so we directly add the block to the function + // current_function_->getBasicBlockList().push_back(else_block); + + // We are now adding stuff in the else block + builder_.SetInsertPoint(else_block); + + llvm::Value* else_body = translate(e.elseclause_get()); + + builder_.CreateBr(after_if); + + else_block = builder_.GetInsertBlock(); + + // Finally we add the remaining block, the one to be executer after the if + // current_function_->getBasicBlockList().push_back(after_if); + + builder_.SetInsertPoint(after_if); + + if (dynamic_cast<const type::Void*>(e.type_get()) == nullptr) + { + // And now for the star of the show: The PHI node + // This is a value that can take multiple values depending on the path + llvm::PHINode* phi = builder_.CreatePHI(llvm_type(*e.elseclause_get().type_get()), 2, "phinode"); + + phi->addIncoming(then_body, then_block); + phi->addIncoming(else_body, else_block); + + value_ = phi; + } + } + + void Translator::operator()(const ast::WhileExp& e) + { + // Bb containing the test and the branching + auto test_bb = llvm::BasicBlock::Create(ctx_, "test", current_function_); + auto body_bb = llvm::BasicBlock::Create(ctx_, "body", current_function_); + auto after_bb = + llvm::BasicBlock::Create(ctx_, "afterloop", current_function_); + + // Save the after block for breaks + loop_end_[&e] = after_bb; + + // Explicitly fall through from the current block + builder_.CreateBr(test_bb); + + // Start inside the test BasicBlock + builder_.SetInsertPoint(test_bb); + + auto cond_val = translate(e.test_get()); + auto zero_val = llvm::ConstantInt::getSigned(cond_val->getType(), 0); + auto cmp_val = builder_.CreateICmpNE(cond_val, zero_val, "loopcond"); + + // Create the branching + builder_.CreateCondBr(cmp_val, body_bb, after_bb); + + // Translate the body inside the body BasicBlock + builder_.SetInsertPoint(body_bb); + // Don't store the return value, is should be void. + translate(e.body_get()); + + // Go back to the Test BasicBlock + builder_.CreateBr(test_bb); + + // Continue after the loop BasicBlock + builder_.SetInsertPoint(after_bb); + } + + void Translator::operator()(const ast::BreakExp& e) + { + // FIXME DONE: Some code was deleted here. + const auto while_node = dynamic_cast<const ast::WhileExp*>(e.def_get()); + + precondition(loop_end_.contains(while_node)); + + value_ = builder_.CreateBr(loop_end_.at(while_node)); + } + + void Translator::operator()(const ast::ArrayExp& e) + { + // Translate the number of elements, + // fill the array with the default value, then + // return the pointer to the allocated zone. + // FIXME DONE: Some code was deleted here (Use `init_array`). + llvm::Value* size = translate(e.size_get()); + llvm::Value* init_value = translate(e.init_get()); + value_ = init_array(size, init_value); + } + + void Translator::operator()(const ast::CastExp& e) + { + auto exp_val = translate(e.exp_get()); + llvm::Type* ltype = nullptr; + // FIXME DONE: Some code was deleted here (Destination llvm type). + ltype = llvm_type(*e.type_get()); + value_ = builder_.CreateBitCast(exp_val, ltype, "cast_exp"); + } + + void Translator::operator()(const ast::FunctionChunk& e) + { + for (const auto& fdec : e) + visit_function_dec_header(*fdec); + + for (const auto& fdec : e) + // There is nothing to translate for primitives. + if (fdec->body_get()) + visit_function_dec_body(*fdec); + } + + void Translator::visit_function_dec_header(const ast::FunctionDec& e) + { + bool is_main = e.name_get() == "_main"; + bool is_primitive = e.body_get() == nullptr; + auto name = function_dec_name(e); + + const type::Type* node_type = nullptr; + // FIXME DONE: Some code was deleted here. + node_type = &e.type_get()->actual(); + + auto& function_type = static_cast<const type::Function&>(*node_type); + auto function_ltype = llvm_function_type(function_type); + + // Main and primitives have External linkage. + // Other Tiger functions are treated as "static" functions in C. + auto linkage = is_main || is_primitive ? llvm::Function::ExternalLinkage + : llvm::Function::InternalLinkage; + + auto the_function = + llvm::Function::Create(function_ltype, linkage, name, &module_); + set_default_attributes(*the_function, e); + + auto& escaped = escaped_[&function_type]; + + // Name each argument of the function + for (auto arg_it = the_function->arg_begin(); + arg_it != the_function->arg_end(); ++arg_it) + { + auto i = std::distance(the_function->arg_begin(), arg_it); + auto var = escaped.size() && static_cast<size_t>(i) < escaped.size() + ? *std::next(escaped_[&function_type].begin(), i) + : e.formals_get()[i - escaped.size()]; + + arg_it->setName(var->name_get().get()); + } + } + + void Translator::visit_function_dec_body(const ast::FunctionDec& e) + { + auto the_function = module_.getFunction(function_dec_name(e)); + + // Save the old function in case a nested function occurs. + auto old_insert_point = builder_.saveIP(); + auto old_function = current_function_; + current_function_ = the_function; + + // Create a new basic block to start the function. + auto bb = llvm::BasicBlock::Create(ctx_, "entry_"s + e.name_get().get(), + the_function); + builder_.SetInsertPoint(bb); + + const type::Type* node_type = nullptr; + // FIXME DONE: Some code was deleted here. + node_type = &e.type_get()->actual(); + + auto& function_type = static_cast<const type::Function&>(*node_type); + auto& escaped = escaped_[&function_type]; + auto& formals = e.formals_get(); + + auto arg_it = the_function->arg_begin(); + + for (const auto var : escaped) + { + locals_[current_function_][var] = &*arg_it; + ++arg_it; + } + + // FIXME DONE: Some code was deleted here (Create alloca instructions for each variable). + for (size_t i = 0; i < formals.decs_get().size(); i++) + { + const ast::VarDec* const parameter = formals.decs_get().at(i); + locals_[current_function_][parameter] = arg_it + i; + } + + translate(*e.body_get()); + + // FIXME DONE: Some code was deleted here (Create a return instruction). + if (dynamic_cast<const type::Void*>(&function_type.result_get().actual()) != nullptr) + { + value_ = builder_.CreateRetVoid(); + } + else + { + if (dynamic_cast<const ast::Var*>(e.body_get()) != nullptr) + { + value_ = get_dereferenced(value_, e.body_get()->type_get()); + } + + value_ = builder_.CreateRet(value_); + } + + // Validate the generated code, checking for consistency. + llvm::verifyFunction(*the_function); + + // Restore the context of the old function. + current_function_ = old_function; + builder_.restoreIP(old_insert_point); + } + + void Translator::operator()(const ast::CallExp& e) + { + // Look up the name in the global module table. + // If it's a primitive, rename the call to tc_name. + // + // Then, add the escaped variables and the rest of the arguments to the + // list of arguments, and return the correct value. + // FIXME DONE: Some code was deleted here. + const auto function_type = dynamic_cast<const type::Function*>(e.def_get()->type_get()); + + precondition(function_type != nullptr); + + std::string function_name = e.name_get().get(); + + if (e.def_get()->body_get() == nullptr) + { + // then the call references a primitive + function_name = "tc_" + function_name; + } + + llvm::Function* function = module_.getFunction(function_name); + std::vector<llvm::Value*> args; + std::string twine = "call_" + e.name_get().get(); + + assertion(function != nullptr); + + for (auto& var : escaped_[function_type]) + { + args.push_back(translate(*var)); + } + + for (auto& parameter : e.args_get()) + { + llvm::Value* variable = translate(*parameter); + llvm::Value* value = dynamic_cast<ast::Var*>(parameter) != nullptr + ? get_dereferenced(variable, parameter->type_get()) + : variable; + args.push_back(value); + } + + if (dynamic_cast<const type::Void*>(&function_type->result_get().actual()) != nullptr) + { + twine = ""; + } + + value_ = builder_.CreateCall(function, args, twine); + } + + void Translator::operator()(const ast::VarDec& e) + { + // Void var types are actually Ints represented by a 0 + // FIXME DONE: Some code was deleted here. + if (const auto existing_ref = declaration_in_current_scope(e)) + { + value_ = existing_ref; + return; + } + + llvm::Type* variable_type = llvm_type(*e.type_get()); + + llvm::Value* init_value = translate(*e.init_get()); + + llvm::Value* variable = create_alloca(current_function_, variable_type, + "var_" + e.name_get().get()); + + locals_[current_function_][&e] = variable; + value_ = builder_.CreateStore(init_value, variable); + } + + llvm::Value* Translator::declaration_in_current_scope(const ast::VarDec& e) const + { + if (!locals_.contains(current_function_)) + { + return nullptr; + } + + const auto current_scope = locals_.at(current_function_); + + return current_scope.contains(&e) ? current_scope.at(&e) : nullptr; + } + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/llvmtranslate/translator.hh b/tiger-compiler/src/llvmtranslate/translator.hh new file mode 100644 index 0000000..3ddb625 --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/translator.hh @@ -0,0 +1,145 @@ +/** + ** \file llvmtranslate/translator.hh + ** \brief Declaration of llvmtranslate::Translator + */ + +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include <llvm/IR/IRBuilder.h> +#include <llvm/IR/Module.h> +#include <llvm/IR/Value.h> + +#pragma GCC diagnostic pop + +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> +#include <llvmtranslate/fwd.hh> +#include <llvmtranslate/llvm-type-visitor.hh> + +namespace llvmtranslate +{ + class Translator + : public ast::DefaultConstVisitor + , public ast::NonObjectConstVisitor + , public ast::NonAssertConstVisitor + { + public: + /// Super class. + using super_type = ast::DefaultConstVisitor; + /// Import overloaded operator() methods. + using super_type::operator(); + + Translator(llvm::Module& module, escaped_map_type&& escaped); + + /// Run the translation. + void operator()(const ast::Ast& e) override; + + /// \brief Run this visitor on \a node, and return its translation. + /// + /// It is also guaranteed that \a value_ is set to it. + /// More generally, this routine saves from direct calls to \a accept. + llvm::Value* translate(const ast::Ast& node); + + /// \name Lvalues + /// \{ + void operator()(const ast::SimpleVar& e) override; + void operator()(const ast::FieldVar& e) override; + void operator()(const ast::SubscriptVar& e) override; + /// \} + + /// \name Expressions + /// \{ + void operator()(const ast::NilExp&) override; + void operator()(const ast::IntExp& e) override; + void operator()(const ast::StringExp& e) override; + void operator()(const ast::RecordExp& e) override; + void operator()(const ast::CallExp& e) override; + void operator()(const ast::OpExp& e) override; + void operator()(const ast::SeqExp& e) override; + void operator()(const ast::AssignExp& e) override; + void operator()(const ast::IfExp& e) override; + void operator()(const ast::WhileExp& e) override; + void operator()(const ast::BreakExp&) override; + void operator()(const ast::ArrayExp& e) override; + void operator()(const ast::CastExp& e) override; + /// \} + + /// \name Declarations + /// \{ + void operator()(const ast::FunctionChunk& e) override; + void visit_function_dec_header(const ast::FunctionDec& e); + void visit_function_dec_body(const ast::FunctionDec& e); + void operator()(const ast::VarDec& e) override; + + /** + * Check for the existence of a VarDec node within the current function + * scoped (locals_[current_function_]). + * + * If the node does exist within the scope, its LLVM reference is returned + * afterward. If not, nullptr is returned instead. + * + * @param e The node to look for + * @return The associated LLVM value if e does exist within the scope. + * nullptr otherwise + */ + llvm::Value* declaration_in_current_scope(const ast::VarDec& e) const; + + /// \} + + protected: + /// The translation of the last visited node. + llvm::Value* value_ = nullptr; + + /// The current function. + llvm::Function* current_function_ = nullptr; + + /// The module containing the metadata and this file's AST. + llvm::Module& module_; + + /// The global context of the translator. + llvm::LLVMContext& ctx_; + + /// IR builder to simplify building nodes and instructions. + llvm::IRBuilder<> builder_; + + /// Access for each "variable". + /// Since the AST doesn't contain the arguments added + /// for the lambda lifting, we need to identify them by their declaration. + std::map<const llvm::Function*, std::map<const ast::VarDec*, llvm::Value*>> + locals_; + + /// For each loop, the basic block immediately after it. + std::map<const ast::WhileExp*, llvm::BasicBlock*> loop_end_; + + /// Access every escaped variable for each function. + escaped_map_type escaped_; + + /// The llvm type translator. + LLVMTypeVisitor type_visitor_; + + /// Get a Tiger void value. + inline llvm::Value* get_void_value(); + + inline llvm::Value* get_dereferenced(llvm::Value* ptr, const type::Type* type); + + private: + /// Get a LLVM access to a variable, usually to be loaded right after. + llvm::Value* access_var(const ast::Var& e); + + /// Call the init_array function that allocates and initialize the array. + llvm::Value* init_array(llvm::Value* count_val, llvm::Value* init_val); + + /// Get a llvm::Type from a type::Type using the type_visitor_. + llvm::Type* llvm_type(const type::Type& type); + + /// Create a llvm function from a function type. + llvm::FunctionType* llvm_function_type(const type::Function& function_t); + }; + +} // namespace llvmtranslate + +#include <llvmtranslate/translator.hxx> diff --git a/tiger-compiler/src/llvmtranslate/translator.hxx b/tiger-compiler/src/llvmtranslate/translator.hxx new file mode 100644 index 0000000..a509d4d --- /dev/null +++ b/tiger-compiler/src/llvmtranslate/translator.hxx @@ -0,0 +1,30 @@ +/** + ** \file llvmtranslate/translator.hxx + ** \brief Inline methods for llvmtranslate::Translator. + */ + +#pragma once + +#include <llvmtranslate/translator.hh> + +namespace llvmtranslate +{ + + inline llvm::Value* Translator::get_void_value() + { + return builder_.getIntN(0, 0); + } + + inline llvm::Value* Translator::get_dereferenced(llvm::Value* ptr, + const type::Type* type) + { + if (!ptr->getType()->isPointerTy()) + { + return ptr; + } + + return builder_.CreateLoad(llvm_type(*type), ptr, ptr->getName() + "_dereferenced"); + } + + +} // namespace llvmtranslate diff --git a/tiger-compiler/src/local.am b/tiger-compiler/src/local.am new file mode 100644 index 0000000..dfd658e --- /dev/null +++ b/tiger-compiler/src/local.am @@ -0,0 +1,75 @@ +## ------------ ## +## version.cc. ## +## ------------ ## + +EXTRA_DIST += src/version.cc.in +CLEANFILES += src/version.stamp +src/version.stamp: $(top_srcdir)/ChangeLog $(srcdir)/src/version.cc.in + $(AM_V_GEN)rm -f src/version.tmp + $(AM_V_at)touch src/version.tmp +# Be sure not to have `/' in Id. The embedded date may be +# separated by `/' instead of `-', what sed dislikes. + $(AM_V_at)\ + Id=`sed -n '/^\$$Id/{s,/,-,g;p;q;}' $(top_srcdir)/ChangeLog`; \ + sed -e "s/@ID@/$$Id/" $(srcdir)/src/version.cc.in >src/version.cc.tmp + $(AM_V_at)$(top_srcdir)/build-aux/bin/move-if-change \ + src/version.cc.tmp src/version.cc + $(AM_V_at)mv -f src/version.tmp src/version.stamp + +src/version.cc: src/version.stamp + $(AM_V_at)if test -f src/version.cc; then :; else \ + rm -f src/version.stamp; \ + $(MAKE) $(AM_MAKEFLAGS) src/version.stamp; \ + fi + + +## ------- ## +## libtc. ## +## ------- ## + +lib_LTLIBRARIES = src/libtc.la +src_libtc_la_SOURCES = src/version.hh src/common.cc +nodist_src_libtc_la_SOURCES = src/version.cc +src_libtc_la_LDFLAGS = $(BOOST_PROGRAM_OPTIONS_LDFLAGS) -lreflex +BUILT_SOURCES += $(nodist_src_libtc_la_SOURCES) +CLEANFILES += $(nodist_src_libtc_la_SOURCES) +src_libtc_la_LIBADD = \ + $(top_builddir)/lib/misc/libmisc.la \ + $(BOOST_PROGRAM_OPTIONS_LIBS) + + +## ---- ## +## tc. ## +## ---- ## + +bin_PROGRAMS = src/tc +dist_src_tc_SOURCES = \ + src/doc.hh \ + $(TASKS) \ + src/common.hh \ + src/tc.cc + +src_tc_LDADD = src/libtc.la + + +## --------- ## +## Modules. ## +## --------- ## + +TASKS = +include src/task/local.am +include src/ast/local.am +include src/parse/local.am +include src/astclone/local.am +include src/bind/local.am +include src/escapes/local.am +include src/callgraph/local.am +include src/type/local.am +include src/object/local.am +include src/overload/local.am +include src/desugar/local.am +include src/assert/local.am +include src/testsuite/local.am +include src/inlining/local.am +include src/llvmtranslate/local.am +include src/combine/local.am diff --git a/tiger-compiler/src/object/binder.cc b/tiger-compiler/src/object/binder.cc new file mode 100644 index 0000000..8220da5 --- /dev/null +++ b/tiger-compiler/src/object/binder.cc @@ -0,0 +1,176 @@ +/** + ** \file object/binder.cc + ** \brief Implementation of object::Binder. + */ + +#include <ast/all.hh> +#include <object/binder.hh> + +namespace object +{ + /*---------. + | Visits. | + `---------*/ + + /* Handle the case of `self'. If the variable is not named `self', handle it + like the normal `Binder'. If it is `self', it must be bound to a definiton + site, unless: + * it is inside a method, + * AND `self` is not overridden. + If those conditions are met, `self' is an implicitly defined instance of + the class. + + Variable `self' will have a meaningful definition after the object + constructs have been desugared. */ + + void Binder::operator()(ast::SimpleVar& e) + { + // FIXME DONE: Some code was deleted here. + misc::symbol test_self("self"); + if (e.name_get() == test_self && within_method_dec_ && !overrided_self_) + { + e.def_set(nullptr); + } + else + { + super_type::operator()(e); + } + } + + // Handle the case of `Object'. + void Binder::operator()(ast::NameTy& e) + { + // FIXME DONE: Some code was deleted here. + misc::symbol test_obj("Object"); + if (e.name_get() == test_obj) + { + e.def_set(nullptr); + } + else + { + super_type::operator()(e); + } + } + + /*---------------. + | Visiting Exp. | + `---------------*/ + + void Binder::operator()(ast::ForExp& e) + { + bool saved_within_class_ty = within_class_ty_; + within_class_ty_ = false; + super_type::operator()(e); + within_class_ty_ = saved_within_class_ty; + } + + /*-------------------. + | Visiting ClassTy. | + `-------------------*/ + + void Binder::operator()(ast::ClassTy& e) + { + // FIXME DONE: Some code was deleted here (Scope begins). + begin_scope(); + e.super_get().accept(*this); + bool saved_within_class_ty = within_class_ty_; + within_class_ty_ = true; + bool saved_within_method_dec = within_method_dec_; + within_method_dec_ = false; + e.chunks_get().accept(*this); + within_method_dec_ = saved_within_method_dec; + within_class_ty_ = saved_within_class_ty; + // FIXME DONE: Some code was deleted here (Scope ends). + end_scope(); + } + + /*---------------. + | Visiting Dec. | + `---------------*/ + + void Binder::operator()(ast::VarDec& e) + { + if (within_class_ty_) + { + within_class_ty_ = false; + // Don't bind class attributes. + super_type::super_type::operator()(e); + within_class_ty_ = true; + } + else + { + // But still bind other variable declarations. + super_type::operator()(e); + if (e.name_get() == "self" && within_method_dec_) + overrided_self_ = true; + } + } + + void Binder::operator()(ast::FunctionChunk& e) + { + chunk_visit<ast::FunctionDec>(e); + } + + template <class D> void Binder::chunk_visit(ast::Chunk<D>& e) + { + // Shorthand. + using chunk_type = ast::Chunk<D>; + // FIXME: Some code was deleted here (Two passes: once on headers, then on bodies). + for (auto machala : e) + { + if (dynamic_cast<ast::MethodDec*>(machala)) + { + scoped_map_fun_.put(machala->name_get(), machala); + } + } + super_type::operator()(e); + } + + // This trampoline is needed, since `virtual' and `template' cannot + // be mixed. + template <> + void Binder::visit_dec_header<ast::FunctionDec>(ast::FunctionDec& e) + { + // FIXME DONE: Some code was deleted here (Call the super type). + super_type::operator()(e); + } + + // Compute the bindings of this function's body. + template <> void Binder::visit_dec_body<ast::FunctionDec>(ast::FunctionDec& e) + { + bool saved_within_class_ty = within_class_ty_; + within_class_ty_ = false; + bool saved_within_method_dec = within_method_dec_; + within_method_dec_ = false; + // FIXME DONE: Some code was deleted here (Call the super type). + super_type::operator()(e); + within_method_dec_ = saved_within_method_dec; + within_class_ty_ = saved_within_class_ty; + } + + /* We can't bind methods definitions without types, so we don't + store them. Nonetheless, object::Binder must still recurse + through the children of ast::MethodChunk to bind other names. + + Note that as we defer the binding of methods to the + type-checkimg, there is no need to visit method in two-pass (like + bind::Binder does for functions for instance). */ + void Binder::operator()(ast::MethodDec& e) + { + // FIXME DONE: Some code was deleted here (Scope begins). + begin_scope(); + bool saved_within_class_ty = within_class_ty_; + within_class_ty_ = false; + bool saved_within_method_dec = within_method_dec_; + within_method_dec_ = true; + bool saved_overrided_self = overrided_self_; + overrided_self_ = false; + ast::DefaultVisitor::operator()(static_cast<ast::FunctionDec&>(e)); + within_method_dec_ = saved_within_method_dec; + within_class_ty_ = saved_within_class_ty; + overrided_self_ = saved_overrided_self; + // FIXME DONE: Some code was deleted here (Scope ends). + end_scope(); + } + +} // namespace object diff --git a/tiger-compiler/src/object/binder.hh b/tiger-compiler/src/object/binder.hh new file mode 100644 index 0000000..dad6b57 --- /dev/null +++ b/tiger-compiler/src/object/binder.hh @@ -0,0 +1,98 @@ +/** + ** \file object/binder.hh + ** \brief Declaration of object::Binder. + **/ + +#pragma once + +#include <bind/binder.hh> + +namespace object +{ + /** \brief Computing bindings with support for objects. + ** + ** Inheritance is declared virtual to enable diamond inheritance with + ** the combine::Binder (src/combine/binder.hh), inheriting from + ** overload::Binder and object::Binder, both inheriting from bind::Binder. + **/ + class Binder : virtual public bind::Binder + { + public: + /// Super class type. + using super_type = ::bind::Binder; + /// Import all the overloaded operator() methods. + using super_type::operator(); + + /* The visiting methods. */ + public: + // ---------------- // + // Visiting /Exp/. // + // ---------------- // + + void operator()(ast::ForExp& e) override; + + /// Visit a Variable instantiation. + void operator()(ast::SimpleVar& e) override; + + // ---------------- // + // Visiting /Ty/. // + // ---------------- // + + /// Visit a type name. + void operator()(ast::NameTy& e) override; + + /// Visit a class definition. + void operator()(ast::ClassTy& e) override; + + // ---------------- // + // Visiting /Dec/. // + // ---------------- // + + /// Check a set of definitions: unique names, browse headers, then bodies. + template <class D> void chunk_visit(ast::Chunk<D>& e); + + /// Check a Function declaration header. + template <class D> void visit_dec_header(D& e); + + /// Check a Function declaration body. + template <class D> void visit_dec_body(D& e); + + // Visit a variable declaration. + void operator()(ast::VarDec&) override; + + /// Visit a chunk of Function declarations. + void operator()(ast::FunctionChunk&) override; + + /// No longer used. + void operator()(ast::MethodDec&) override; + + private: + /// Are we (immediately) within a class definition? + /// + /// This predicate is used to prevent the bindings of VarChunk that + /// are direct children of a ClassTy and which represent + /// attributes of that class definition; the binding of these + /// attributes is deferred to the type-checking phase (as for + /// records' fields). This predicate should be false within a + /// non-ClassTy node declaring variables, even if that node + /// belongs (indirectly) to the subtree of a ClassTy. + bool within_class_ty_ = false; + + /// Are we (immediately) within a method definition? + /// + /// This predicate is used to determine whether `self' is a valid + /// variable. This is only the case for nodes that belong to the + /// subtree of a MethodDec node, except for FunctionDec and + /// ClassTy subtrees, which ``reset'' the context. + bool within_method_dec_ = false; + + /// Is the implicit `self' formal of the current MethodDec overrided by an + /// other variable named `self' local to the current method? + /// + /// This predicate is used to determine whether a SimpleVar named `self` + /// should be bound to its potential definition or if it is bound to + /// nullptr, representing the current object. + bool overrided_self_ = false; + }; + +} // namespace object diff --git a/tiger-compiler/src/object/desugar-visitor.cc b/tiger-compiler/src/object/desugar-visitor.cc new file mode 100644 index 0000000..db61a80 --- /dev/null +++ b/tiger-compiler/src/object/desugar-visitor.cc @@ -0,0 +1,1127 @@ +/** + ** \file object/desugar-visitor.cc + ** \brief Implementation of object::DesugarVisitor. + */ + +#include <sstream> + +#include <algorithm> +#include <ast/all.hh> +#include <misc/contract.hh> +#include <misc/set.hh> +#include <misc/symbol.hh> +#include <object/desugar-visitor.hh> +#include <parse/libparse.hh> +#include <parse/tweast.hh> +#include <type/class.hh> +#include <type/function.hh> +#include <type/record.hh> + +namespace object +{ + DesugarVisitor::DesugarVisitor(const class_names_type& names) + : class_names_(names) + {} + + /*---------------------------. + | Handling names and types. | + `---------------------------*/ + + namespace + { + // Desugar prefixes. + + // Prefix of every class id (label). + const char* class_id_prefix = "_id_"; + // Prefix of every record holding the contents of a desugared class. + const char* class_contents_prefix = "_contents_"; + // Prefix of every variant storing the possible dynamic type for + // a given static type. + const char* class_variant_prefix = "_variant_"; + // Prefix of the fields in the variant. + const char* variant_field_prefix = "field_"; + // Prefix of constructors. + const char* class_ctor_prefix = "_new_"; + // Prefix of methods. + const char* method_prefix = "_method_"; + + // Useful routines. + + // Try to get the class type of \a t. If \a t is not a class + // type, return a null pointer. + const type::Class* class_type_query(const ast::Typable& t) + { + // FIXME DONE: Some code was deleted here. + return dynamic_cast<const type::Class*>(&t.type_get()->actual()); + } + + // Like class_type_query, but ensure the type is actually a class. + const type::Class* class_type_get(const ast::Typable& t) + { + const type::Class* class_type = class_type_query(t); + postcondition(class_type); + return class_type; + } + + } // namespace + + /*------------------. + | Code generation. | + `------------------*/ + + std::string DesugarVisitor::type_symbol(const type::Type* type) + { + // FIXME DONE: Some code was deleted here (Check int, then string, then class name). + if (dynamic_cast<const type::Int*>(type)) + { + return "int"; + } + else if (dynamic_cast<const type::String*>(type)) + { + return "string"; + } + else if (const auto named = dynamic_cast<const type::Named*>(type)) + { + return named->name_get().get(); + } + else + { + throw std::invalid_argument("type was neither a builtin or named"); + } + } + + std::string DesugarVisitor::upcast_fun_name(const type::Class* from, + const type::Class* to) + { + std::stringstream s; + s << "_upcast_" << class_names_(from) << "_to_" << class_names_(to); + return s.str(); + } + + std::string DesugarVisitor::downcast_fun_name(const type::Class* from, + const type::Class* to) + { + std::stringstream s; + s << "_downcast_" << class_names_(from) << "_to_" << class_names_(to); + return s.str(); + } + + std::string DesugarVisitor::dispatch_fun_name(const type::Class* owner, + const type::Method* method) + { + std::stringstream s; + // If an extension is found, add it to the dispatch suffix. + if (dispatch_map_.find(method) != dispatch_map_.end()) + s << "_dispatch_" << dispatch_map_[method] << "_" << class_names_(owner) + << "_" << method->name_get(); + else + s << "_dispatch_" << class_names_(owner) << "_" << method->name_get(); + + return s.str(); + } + + void DesugarVisitor::adapt_type(ast::Exp*& source_exp, + const type::Class* source_type, + const type::Class* target_type) + { + // If the source type is different from the target type, (up)cast + // the source expression to the latter. + if (source_type && target_type && source_type != target_type) + source_exp = parse::parse(parse::Tweast() + << upcast_fun_name(source_type, target_type) + << " (" << source_exp << ")"); + } + + ast::Exp* DesugarVisitor::variant_exp(const type::Class* static_type, + const std::string& exact_type, + const field_inits_type& inits) + { + parse::Tweast input; + misc::symbol static_type_name = class_names_(static_type); + input << " " << class_variant_prefix << static_type_name + << " {" + " exact_type = " + << exact_type; + /* For each field of the variant, store the corresponding + initialization value if one was given, otherwise set the field + to `nil'. */ + // Fields of the static type. + for (const type::Class* c = static_type; c; c = c->super_get()) + // Don't generate slots for classes with no data. + if (c->has_data()) + { + input << ",\n" << variant_field_prefix << class_names_(c) << " = "; + // These fields must have a value (we don't need to put an + // assertion here, misc::map::operator() already handles this. + std::string init = inits.operator()(c); + input << init; + } + // Potential fields of the dynamic type (from subclasses of the + // static type). + for (const type::Class* subclass : static_type->subclasses_get()) + // Don't generate slots for classes with no data. + if (subclass->has_data()) + { + input << ",\n" + << variant_field_prefix << class_names_(subclass) << " = "; + // These fields might be nil. + const auto i = inits.find(subclass); + if (i != inits.end()) + input << i->second; + else + input << "nil"; + } + input << " }\n"; + return parse::parse(input); + } + + // Syntactic sugar. + ast::Exp* DesugarVisitor::variant_exp(const type::Class* static_type, + const type::Class* dynamic_type, + const field_inits_type& inits) + { + std::string exact_type = class_id_prefix + class_names_(dynamic_type).get(); + return variant_exp(static_type, exact_type, inits); + } + + void DesugarVisitor::fill_variant_fields(const type::Class* class_type, + parse::Tweast* input) + { + // Don't generate slots for classes with no data. + if (class_type->has_data()) + { + misc::symbol class_name = class_names_(class_type); + *input << ",\n" + << " " << variant_field_prefix << class_name << " : " + << " " << class_contents_prefix << class_name; + } + } + + // Desugar a class type as a variant (record) type. + parse::Tweast* DesugarVisitor::variant_ty(const type::Class* class_type) + { + auto input = new parse::Tweast; + *input << " {" + << " exact_type : int"; + // Actual data slots. + /* First, populate the variant with mandatory fields (always non + nil) starting with the type of the visited class, then super + classes. */ + for (const type::Class* c = class_type; c; c = c->super_get()) + fill_variant_fields(c, input); + /* Then add all subclasses types. These might be nil, according to + the exact type of the object. */ + for (const type::Class* subclass : class_type->subclasses_get()) + fill_variant_fields(subclass, input); + *input << " }\n"; + return input; + } + + void DesugarVisitor::fill_init_list(const type::Class* class_type, + field_inits_type& inits) + { + // Populate the initialization list with classes + // owning actual data only. + if (class_type->has_data()) + { + std::string field_name = class_names_(class_type); + misc::put(inits, class_type, + std::string("source.") + variant_field_prefix + field_name); + } + } + + parse::Tweast* DesugarVisitor::cast_function(const std::string& name, + const type::Class* source, + const type::Class* target, + const type::Class* exact_type) + { + auto input = new parse::Tweast; + *input << " function " << name << " (source : " << class_variant_prefix + << class_names_(source) + << ") :" + " " + << class_variant_prefix << class_names_(target) << " = "; + + // Copy all fields from the source. + field_inits_type inits; + // First, fields from the class and its super classes... + for (const type::Class* c = source; c; c = c->super_get()) + fill_init_list(c, inits); + // ...then, fields from the subclasses. + for (const type::Class* c : source->subclasses_get()) + fill_init_list(c, inits); + *input << variant_exp(target, exact_type, inits) << "\n"; + return input; + } + + parse::Tweast* DesugarVisitor::upcast_function(const type::Class* source, + const type::Class* target) + { + return cast_function(upcast_fun_name(source, target), source, target, + source); + } + + parse::Tweast* DesugarVisitor::downcast_function(const type::Class* source, + const type::Class* target) + { + return cast_function(downcast_fun_name(source, target), source, target, + target); + } + + ast::Exp* DesugarVisitor::dispatch_switch(const type::Class* class_type, + const type::Method* method, + const ast::TypeChunk* typechunk, + const type::Method* dispatch_method) + { + parse::Tweast input; + misc::symbol method_name = method->name_get(); + // If a recursive call is needed, dispatch_method will not be + // nullptr. It means that during the dispatch function + // declaration, formals of the mother method have been used + // (because of the use of dispatch_map_, we cannot use child's + // formals. + const ast::MethodDec* def; + // FIXME DONE: Some code was deleted here (Initialize def). + def = dispatch_method ? dispatch_method->def_get() : method->def_get(); + const ast::VarChunk& formals = def->formals_get(); + + // Create a case for each method redefinition. + classes_type overridings; + for (const type::Class* c : class_type->subclasses_get()) + if (std::ranges::any_of(*typechunk, [c](const ast::TypeDec* tmp) { + return class_type_query(*tmp) == c; + })) + overridings.emplace_back(c); + + if (overridings.empty()) + // No dispatch is needed; simply call the method. + input << method_call(class_names_(class_type), method_name, "self", + formals); + else + { + // Emit code for self. + bool first = true; + // Emit code for other types. + for (auto c : overridings) + { + if (!first) + input << " else "; + input << " if self.exact_type = " << class_id_prefix + << class_names_(c) << " then "; + // We search for the nearest implementation of our method. + const type::Class* nearest_c = c; + while (nearest_c && !(nearest_c->owned_meth_find(method_name))) + nearest_c = nearest_c->super_get(); + input << method_call( + class_names_(nearest_c), method_name, + ((*class_type != *nearest_c) + ? downcast_fun_name(class_type, nearest_c) + " (self)" + : "self"), + formals); + first = false; + } + input << " else "; + // If this is a sub dispatch function, call the last dispatch + // method built so we don't need to write every single test + // for exact_type. + if (dispatch_method) + { + input << "_dispatch_"; + if ((dispatch_map_[method] - 1) != 1) + input << (dispatch_map_[method] - 1) << "_"; + input << class_names_(class_type) << "_" << method->name_get() + << " (self"; + // Get the other arguments. + for (const ast::VarDec* arg : formals) + input << ", " << arg->name_get(); + input << ")"; + } + else + input << method_call(class_names_(class_type), method_name, "self", + formals); + } + input << '\n'; + return parse::parse(input); + } + + parse::Tweast* DesugarVisitor::method_call(misc::symbol class_name, + misc::symbol method_name, + const std::string& target, + const ast::VarChunk& formals) + { + auto input = new parse::Tweast; + *input << method_prefix << class_name << "_" << method_name << " ("; + + // Pass the target. + // FIXME DONE: Some code was deleted here. + *input << target; + + // Pass other arguments. + // FIXME DONE: Some code was deleted here. + for (const auto var : formals) + { + // FIXME: Is potentially wrong + *input << ", " << *recurse(*var); + } + + *input << ")"; + return input; + } + + /*------------------------. + | Visiting declarations. | + `------------------------*/ + + void DesugarVisitor::operator()(const ast::ChunkList& e) + { + const ast::Location& location = e.location_get(); + // This is an inlined and specialized version of + // astclone::Cloner::recurse_collection. Maybe we could factor + // it, but it's not easy to see whether we could benefit from + // this. (Maybe a variant would be appropriate.) + ast::ChunkList::list_type contents; + for (const ast::ChunkInterface* d : e) + { + d->accept(*this); + // The result can be either an ast::ChunkInterface* or an ast::ChunkList*. + auto chunk = dynamic_cast<ast::ChunkInterface*>(result_); + if (chunk) + contents.emplace_back(chunk); + else + { + auto chunklist = dynamic_cast<ast::ChunkList*>(result_); + if (chunklist) + { + contents.splice(contents.end(), chunklist->chunks_get()); + delete chunklist; + } + else + abort(); + } + } + result_ = new ast::ChunkList(location, contents); + } + + /*-----------------------------. + | Desugar class declarations. | + `-----------------------------*/ + + void DesugarVisitor::desugar_constructor(parse::Tweast& functions, + const type::Class* cls, + misc::symbol class_name) + { + functions << " function " << class_ctor_prefix << class_name + << "() : " + " " + << class_variant_prefix << class_name + << " = " + " let"; + // Initialize each mandatory field of the variant (i.e., + // the fields holding the attributes of the classes and + // its super classes). + for (const type::Class* c = cls; c; c = c->super_get()) + if (c->has_data()) + { + functions << " var contents_" << class_names_(c) << " := " + << " " << class_contents_prefix << class_names_(c) << " { "; + + for (auto a = c->attrs_get().begin(); a != c->attrs_get().end(); a++) + { + if (a != c->attrs_get().begin()) + functions << ", "; + const ast::VarDec* attr; + // FIXME DONE: Some code was deleted here (Initialize attr). + attr = a->def_get(); + misc::symbol attr_name = attr->name_get(); + // Partially clone the contents of the VarDec + // (cloning the whole VarDec would leak memory). + + ast::Exp* attr_init = recurse(attr->init_get()); + // Cast the initialization value if needed. + if (attr->init_get() && attr->type_name_get()) + adapt_type(attr_init, class_type_query(*attr->init_get()), + class_type_query(*attr->type_name_get())); + functions << attr_name << " = " << attr_init; + } + functions << " } "; + } + functions << " in "; + // Create a list of initializations for each field of the + // variant being constructed. + field_inits_type inits; + for (const type::Class* c = cls; c; c = c->super_get()) + if (c->has_data()) + { + // FIXME DONE: Some code was deleted here. + std::string base = "contents_"; + std::string name = class_names_(c); + misc::put(inits, c, base + name); + } + // Create the contents of the variant. + functions << variant_exp(cls, cls, inits) << " end\n"; + } + + void DesugarVisitor::desugar_method(parse::Tweast& functions, + const type::Method* method, + misc::symbol class_name) + { + functions << " function " << method_prefix << class_name << "_" + << method->name_get() << " (self : " << class_variant_prefix + << class_name; + // Get the other arguments. + const ast::MethodDec* def; + // FIXME DONE: Some code was deleted here (Initiliaze def). + def = method->def_get(); + for (const ast::VarDec* arg : def->formals_get()) + functions << ", " << arg->name_get() << " : " + << recurse(*arg->type_name_get()); + functions << ")"; + if (def->result_get()) + functions << " : " << recurse(def->result_get()); + ast::Exp* body = recurse(def->body_get()); + // Cast the return value of the function if needed. + if (def->result_get()) + adapt_type(body, class_type_query(*def->body_get()), + class_type_query(*def->result_get())); + functions << " = " << body << "\n"; + } + + void DesugarVisitor::dispatch_function(parse::Tweast& functions, + const ast::TypeChunk& e, + const type::Class* cls, + const type::Method* method, + misc::symbol class_name, + dispatch_list_type& sub_dispatches) + { + for (const type::Class* c = cls->super_get(); c; c = c->super_get()) + { + // If this class is not defined in the current chunk, then + // the subdispatch method is not needed yet (since it can + // only be called after the declaration of the said class. + if (std::ranges::any_of(e, [c](const ast::TypeDec* t) { + return class_type_query(*t) == c; + })) + continue; + + // Determine if the class c implements our method. If not, + // we do not need to write a sub dispatch method for it. + auto meth_it = std::ranges::find_if( + c->meths_get(), [method](const type::Method* meth) { + return meth->name_get() == method->name_get(); + }); + + if (meth_it == c->meths_get().end()) + continue; + + const type::Method* parent_method = *meth_it; + + // Since we're looping inside a chunk, we do not rebuild an + // already built sub dispatch method. + auto disp_it = sub_dispatches.emplace(c, parent_method); + + if (!disp_it.second) + continue; + + // Increments the dispatch counter. + if (dispatch_map_.find(parent_method) == dispatch_map_.end()) + dispatch_map_[parent_method] = 2; + else + dispatch_map_[parent_method] = dispatch_map_[parent_method] + 1; + + // Keep track of the added dispatch. + dispatch_added_ += parent_method; + + // We build the subdispatch method. + functions << " function " << dispatch_fun_name(c, parent_method) + << " (self : " << class_variant_prefix << class_names_(c); + // Get the other arguments. + const ast::MethodDec* def; + // FIXME DONE: Some code was deleted here (Initialize def). + def = parent_method->def_get(); + for (const ast::VarDec* arg : def->formals_get()) + functions << ", " << arg->name_get() << " : " + << recurse(*arg->type_name_get()); + functions << ")"; + if (def->result_get()) + functions << " : " << recurse(def->result_get()); + functions << " = " << dispatch_switch(c, parent_method, &e, method); + } + + functions << " function " << dispatch_fun_name(cls, method) + << " (self : " << class_variant_prefix << class_name; + // Get the other arguments. + const ast::MethodDec* def; + // FIXME DONE: Some code was deleted here (Initialize def). + def = method->def_get(); + for (const ast::VarDec* arg : def->formals_get()) + functions << ", " << arg->name_get() << " : " + << recurse(*arg->type_name_get()); + functions << ")"; + if (def->result_get() != nullptr) + functions << " : " << recurse(def->result_get()); + functions << " = " << dispatch_switch(cls, method, &e); + } + + void DesugarVisitor::handle_class(const ast::TypeChunk& e, + const type::Class* cls, + parse::Tweast& functions, + dispatch_list_type& sub_dispatches) + { + misc::symbol class_name = class_names_(cls); + + /*---------------------------. + | Introduce a new class id. | + `---------------------------*/ + + class_ids_ << " var " << class_id_prefix << class_name + << " := " << cls->id_get() << "\n"; + + /*----------------------------------------------------. + | Create a record holding the actual class contents. | + `----------------------------------------------------*/ + + if (cls->has_data()) + { + types_ << " type " << class_contents_prefix << class_name << " =" + << " { "; + // FIXME DONE: Some code was deleted here (Populate the record with attributes (names and types)). + const auto& attributes = cls->attrs_get(); + const auto& first = attributes.begin(); + + for (auto attribute = first; attribute != attributes.end(); ++attribute) + { + if (attribute != first) + types_ << ", "; + + types_ << attribute->name_get() << " : " + << type_symbol(&attribute->type_get()); + } + + types_ << " }\n"; + } + + /*--------------------------------------------------------------. + | Create a variant able to store any dynamic type corresponding | + | to this (static) class type. | + `--------------------------------------------------------------*/ + + types_ << " type " << class_variant_prefix << class_name << " =" + << variant_ty(cls); + + /*-----------------------. + | Create a constructor. | + `-----------------------*/ + + desugar_constructor(functions, cls, class_name); + + /*-------------------------------------------------------------. + | Create conversion routines from the class type to any of its | + | super types. | + `-------------------------------------------------------------*/ + + for (const type::Class* super_type = cls->super_get(); super_type; + super_type = super_type->super_get()) + funs_tweast << upcast_function(cls, super_type); + + /*-------------------------------------------------------------. + | Create conversion routines from the class type to any of its | + | subtypes. | + `-------------------------------------------------------------*/ + + for (const type::Class* subclass : cls->subclasses_get()) + functions << downcast_function(cls, subclass); + + for (const type::Method* m : cls->meths_get()) + { + desugar_method(functions, m, class_name); + dispatch_function(functions, e, cls, m, class_name, sub_dispatches); + } + } + + /* All type-related code is emitted into the top-level chunklist, so + that all classes are stored in the same typechunk, allowing them + to see their subclasses and be able to build a variant for each + of them. */ + void DesugarVisitor::operator()(const ast::TypeChunk& e) + { + parse::Tweast functions; + // Is used to know what class/method pair we already have seen for the + // sub dispatch functions. + dispatch_list_type sub_dispatches; + for (const ast::TypeDec* t : e) + { + const type::Class* cls = nullptr; + // FIXME DONE: Some code was deleted here (Get the ty's class type). + cls = class_type_get(t->ty_get()); + + if (cls) + handle_class(e, cls, functions, sub_dispatches); + else + { + /* FIXME: In the rest of the visitor, the + simply-clone-this-node case is handled before the + desugar-this-node case. */ + // Otherwise, clone the type declaration. + ast::TypeDec* typedec = recurse(*t); + assertion(typedec); + types_ << *typedec << '\n'; + } + } + + ast::ChunkList* funs_list = parse::parse(functions); + result_ = funs_list; + } + + /*------------------------------------------------. + | Desugar class instantiations and object usage. | + `------------------------------------------------*/ + + void DesugarVisitor::operator()(const ast::VarDec& e) + { + /* We don't desugar everything using concrete syntax here, because + it would require a lot of additional manipulations, as we + cannot easily produce a single VarDec from a parsing. Also + working from VarChunk (with an `s') doesn't help much either, as + VarDec (with no `s') are also found in FunctionDec. */ + + // If this is not an object instantiation, delegate to the cloner. + + const type::Class* class_type = class_type_query(e); + if (!class_type) + return super_type::operator()(e); + + // Otherwise, handle the different cases. + const ast::Location& location = e.location_get(); + ast::NameTy* type_name = nullptr; + ast::Exp* init = nullptr; + if (e.init_get()) + { + // Object (variable) declaration. + if (e.type_name_get()) + { + type_name = recurse(e.type_name_get()); + init = recurse(e.init_get()); + // If the dynamic type is non-nil and different from the + // static type, cast INIT to the latter. + // FIXME DONE: Some code was deleted here. + if (!init) + { + unreachable(); + } + + const type::Class* name_type = class_type_get(*type_name); + const type::Class* init_type = class_type_get(*init); + if (name_type != init_type) + { + adapt_type(init, name_type, init_type); + } + // Up to here + } + else + // No manifest type: simply clone the declaration as-is. + return super_type::operator()(e); + } + else + // Formal declaration. + type_name = recurse(e.type_name_get()); + + misc::symbol name = e.name_get(); + + result_ = new ast::VarDec(location, name, type_name, init); + postcondition(type_name || init); + } + + // Desugar a class instantiation as a call to the desugared ctor routine. + void DesugarVisitor::operator()(const ast::ObjectExp& e) + { + // FIXME DONE: Some code was deleted here. + const ast::Location location = e.location_get(); + + const std::string class_name = e.type_name_get().name_get(); + + const std::string call_name = class_ctor_prefix + class_name; + const auto args = new ast::exps_type; + result_ = new ast::CallExp(location, call_name, args); + } + + void DesugarVisitor::operator()(const ast::IfExp& e) + { + // FIXME DONE: Some code was deleted here. + ast::Exp* cond = recurse(e.test_get()); + ast::Exp* then_clause = recurse(e.thenclause_get()); + ast::Exp* else_clause = recurse(e.elseclause_get()); + result_ = new ast::IfExp(e.location_get(), cond, then_clause, else_clause); + } + + void DesugarVisitor::operator()(const ast::AssignExp& e) + { + // If this is not an object assignment, delegate to the cloner. + const type::Class* lhs_class_type; + // FIXME DONE: Some code was deleted here. + lhs_class_type = class_type_query(e.var_get()); + + if (!lhs_class_type) + return super_type::operator()(e); + + // Duplicate the subtrees of E. + ast::Var* var = nullptr; + // FIXME DONE: Some code was deleted here (Recurse). + var = recurse(e.var_get()); + + ast::Exp* exp = nullptr; + // FIXME DONE: Some code was deleted here (Recurse). + exp = recurse(e.exp_get()); + + // If the RHS type is non-nil and different from the LHS type, + // cast EXP to the latter. + // FIXME DONE: Some code was deleted here. + const type::Class* rhs_class_type = class_type_get(*exp); + if (lhs_class_type != rhs_class_type) + { + adapt_type(exp, rhs_class_type, lhs_class_type); + } + // Up to here + + ast::Exp* assignment = + parse::parse(parse::Tweast() << var << " := " << exp); + result_ = assignment; + } + + ast::exps_type* DesugarVisitor::recurse_args(const ast::exps_type& actuals, + const type::Record& formals) + { + // For each argument, check the type of the actual argument + // against the formal one. If they are two different class types, + // convert the actual argument to the expected type. + auto args = new ast::exps_type; + ast::exps_type::const_iterator i; + type::Record::const_iterator j; + for (i = actuals.begin(), j = formals.begin(); + i != actuals.end() && j != formals.end(); ++i, ++j) + { + // Clone the argument. + ast::Exp* arg = recurse(**i); + + // In the case of a class, handle the case of a (non-nil) actual + // argument having a different type than the corresponding + // formal. + const type::Type* formal_type = &j->type_get().actual(); + auto formal_class_type = dynamic_cast<const type::Class*>(formal_type); + // FIXME DONE: Some code was deleted here. + // How to check if this arg is non nil ? + // Checking if arg is not a nullptr ? + if (formal_class_type && arg) + { + const type::Type* arg_type = &((*i)->type_get()->actual()); + auto arg_class_type = dynamic_cast<const type::Class*>(arg_type); + adapt_type(arg, arg_class_type, formal_class_type); + } + // Up to here + args->emplace_back(arg); + } + return args; + } + + void DesugarVisitor::operator()(const ast::ArrayExp& e) + { + // We want to allow this: + // let + // class A {} + // class B extends A {} + // type arrtype = array of A + // var arr := arrtype[10] of new B + // in + // arr[0] := new B; + // arr[1] := new A + // end + // FIXME DONE: Some code was deleted here. + + parse::Tweast input; + const type::Type* contener_type = &(e.type_name_get().type_get()->actual()); + const type::Type* init_type = &(e.init_get().type_get()->actual()); + + auto contener_class_type = dynamic_cast<const type::Class*>(contener_type); + auto init_class_type = dynamic_cast<const type::Class*>(init_type); + + ast::Exp* reced = recurse(e.init_get()); + if (contener_class_type && init_class_type && reced) + { + adapt_type(reced, init_class_type, contener_class_type); + } + + result_ = recurse(e); + } + + void DesugarVisitor::operator()(const ast::RecordExp& e) + { + // We want to allow this: + // let + // class A {} + // class B extends A {} + // type rectype = { a : A } + // var rec := rectype { a = new B } + // in + // end + // FIXME DONE: Some code was deleted here. + + const type::Type* rec_type = &(e.type_get()->actual()); + auto actual_rec_type = dynamic_cast<const type::Record*>(rec_type); + + if (actual_rec_type) + { + ast::fieldinits_type::const_iterator i; + type::Record::const_iterator j; + for (i = e.fields_get().begin(), j = actual_rec_type->fields_get().begin(); + i != e.fields_get().end() && j != actual_rec_type->fields_get().end(); + ++i, ++j) + { + const type::Type* given_type = &((*i)->init_get().type_get()->actual()); + const type::Type* asked_type = &j->type_get().actual(); + auto class_given = dynamic_cast<const type::Class*>(given_type); + auto class_asked = dynamic_cast<const type::Class*>(asked_type); + + ast::Exp* reced = recurse((*i)->init_get()); + + if (class_given && class_asked && reced) + { + adapt_type(reced, class_given, class_asked); + } + } + } + + // Maybe need to change this because we may need to create another + result_ = recurse(e); + } + + void DesugarVisitor::operator()(const ast::CallExp& e) + { + const type::Function* function_type; + // FIXME DONE: Some code was deleted here. + const ast::FunctionDec* interm = e.def_get(); + function_type = dynamic_cast<const type::Function*>(interm); + + if (!interm) + { + unreachable(); + } + + const ast::Location& location = e.location_get(); + // FIXME DONE: Some code was deleted here (Grab name). + misc::symbol name = e.name_get(); + + // FIXME DONE: Some code was deleted here (Actual arguments). + const ast::exps_type& arguments = e.args_get(); + + // (Types of) formal arguments. + const type::Record& formals = function_type->formals_get(); + // Desugar the arguments and handle possible polymorphic assignments. + // FIXME DONE: Some code was deleted here. + ast::exps_type* final_args = recurse_args(arguments, formals); + + // FIXME DONE: Some code was deleted here (Instantiate into result). + result_ = new ast::CallExp(location, name, final_args); + } + + /*------------------------------------. + | Desugar accesses to class members. | + `------------------------------------*/ + + void DesugarVisitor::operator()(const ast::FieldVar& e) + { + // Check the type of the variable to see whether it is a class or + // a record. + const type::Class* class_type; + // FIXME DONE: Some code was deleted here. + class_type = class_type_query(e.var_get()); + + // If this is not a class, delegate to the cloner. + if (!class_type) + return super_type::operator()(e); + + // FIXME DONE: Some code was deleted here (Grab name). + misc::symbol name = e.name_get(); + + // Otherwise, desugar this FieldVar as an access to an attribute. + + // Look for the attribute within the class and its base classes. + const type::Class* owner = nullptr; + for (const type::Class* c = class_type; c; c = c->super_get()) + { + // FIXME DONE: Some code was deleted here. + if (c->attr_find(name)) + { + owner = c; + break; + } + } + assertion(owner); + + ast::Var* var; + // FIXME DONE: Some code was deleted here (Recurse). + var = recurse(e.var_get()); + + ast::Exp* attr_var = + parse::parse(parse::Tweast() << var << "." << variant_field_prefix + << class_names_(owner) << "." + // FIXME DONE ??: Some code was deleted here. + << name + ); + result_ = attr_var; + } + + void DesugarVisitor::operator()(const ast::LetExp& e) + { + // Save the current scope situation for dispatched methods + auto saved_dispatch_added_ = dispatch_added_; + dispatch_added_.clear(); + + super_type::operator()(e); + + // After exiting the scope, remove unreachable dispatch methods + for (const auto* meth : dispatch_added_) + { + if (dispatch_map_[meth] == 2) + dispatch_map_.take(meth); + else + dispatch_map_[meth] -= 1; + } + + // Resume the current scope + dispatch_added_ = saved_dispatch_added_; + } + + void DesugarVisitor::operator()(const ast::MethodCallExp& e) + { + const type::Method* method_type; + // FIXME DONE: Some code was deleted here (Initialize method_type). + method_type = dynamic_cast<const type::Method*>(&e.def_get()->type_get()->actual()); + // Maybe we need this ? + if (!method_type) + { + unreachable(); + } + + const type::Class* owner_type = method_type->owner_get(); + + const ast::Location& location = e.location_get(); + std::string name = dispatch_fun_name(owner_type, method_type); + + // FIXME DONE: Some code was deleted here (Fetch actual arguments). + // ??????????????????????????????????????????????????????????? + const ast::exps_type actual_args = e.args_get(); + + // (Types of) formal arguments. + const type::Record& formals = method_type->formals_get(); + // Desugar the arguments and handle possible polymorphic assignements. + ast::exps_type* args; + // FIXME DONE: Some code was deleted here (Initialize args). + args = recurse_args(actual_args, formals); + + // Process the target of the method call, and convert it to the + // expected type if needed. + ast::Exp* object; + // FIXME DONE: Some code was deleted here (Recurse). + object = recurse(e.object_get()); + + // FIXME DONE: Some code was deleted here (Adapt type). + const type::Class* item_type = class_type_query(*object); + adapt_type(object, item_type, owner_type); + // Prepend the target to the actual arguments, as the desugared + // method expects to find it as its first arguments. + args->insert(args->begin(), object); + + // Turn the method call into a function call to the desugared method. + // FIXME DONE: Some code was deleted here (Instanciate into result). + ast::Var* finial = dynamic_cast<ast::Var*>(object); + if (!finial) + { + unreachable(); + } + result_ = new ast::MethodCallExp(location, name, args, finial); + } + + /*--------------------------. + | New types and functions. | + `--------------------------*/ + + // Introduce a desugared builtin Object in the top-level function. + void DesugarVisitor::operator()(const ast::FunctionDec& e) + { + bool is_main; + // FIXME DONE: Some code was deleted here (Setup is_main). + is_main = e.name_get() == "_main"; + if (is_main) + { + // Desugared data structures of the builtin Object. + types_ << " type " << class_variant_prefix + << "Object =" << variant_ty(&type::Class::object_instance()); + // Object's class id. + class_ids_ << " var " << class_id_prefix + << "Object := " + " " + << type::Class::object_instance().id_get(); + } + + // Process E. + super_type::operator()(e); + if (is_main) + { + // Object's ctor. + funs_tweast << " function " << class_ctor_prefix + << "Object() :" + " " + << class_variant_prefix << "Object ="; + // Initialize the variant (a single field is filled, the one + // corresponding to Object). + field_inits_type object_init; + misc::put(object_init, &type::Class::object_instance(), + std::string(class_contents_prefix) + "Object {}"); + // Create the variant. + funs_tweast << variant_exp(&type::Class::object_instance(), + &type::Class::object_instance(), + object_init); + + // Parse the built TWEASTs. + ast::ChunkList* types = parse::parse(types_); + ast::ChunkList* class_ids = parse::parse(class_ids_); + ast::ChunkList* funs = parse::parse(funs_tweast); + // Gather these declarations. + types->splice_back(*class_ids); + types->splice_back(*funs); + // Add them to the top of the program. + auto res = dynamic_cast<ast::FunctionDec*>(result_); + parse::Tweast input; + input << "let " << types << " in " << res->body_get() << " end"; + res->body_set(parse::parse(input)); + } + + // Cast the return value of the function if needed. + if (e.body_get() && e.result_get()) + { + const type::Class* body_type = class_type_query(*e.body_get()); + const type::Class* result_type = class_type_query(*e.result_get()); + if (body_type && result_type && body_type != result_type) + { + auto res = dynamic_cast<ast::FunctionDec*>(result_); + parse::Tweast input; + input << upcast_fun_name(body_type, result_type) << " (" + << res->body_get() << ")"; + res->body_set(parse::parse(input)); + } + } + } + + void DesugarVisitor::operator()(const ast::NameTy& e) + { + // Check if E is the name of a class; if not, just clone it. + const type::Class* class_type = class_type_query(e); + if (!class_type) + return super_type::operator()(e); + + // Otherwise, desugar the name of E. + const ast::Location& location = e.location_get(); + result_ = new ast::NameTy( + location, class_variant_prefix + class_names_(class_type).get()); + } + +} // namespace object diff --git a/tiger-compiler/src/object/desugar-visitor.hh b/tiger-compiler/src/object/desugar-visitor.hh new file mode 100644 index 0000000..3c2b455 --- /dev/null +++ b/tiger-compiler/src/object/desugar-visitor.hh @@ -0,0 +1,301 @@ +/** + ** \file object/desugar-visitor.hh + ** \brief Declaration of object::DesugarVisitor. + */ + +#pragma once + +#include <astclone/cloner.hh> +#include <object/fwd.hh> +#include <parse/tweast.hh> + +namespace object +{ + /// \brief Desugar some object structures while duplicating an Ast. + class DesugarVisitor : public astclone::Cloner + { + public: + /// Superclass. + using super_type = astclone::Cloner; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build a DesugarVisitor. + DesugarVisitor(const class_names_type& class_names); + + /// Special version of the visit of a ChunkList allowing the + /// transformation of an ast::ChunkInterface to either a single ast::ChunkInterface or + /// to an ast::ChunkList. + void operator()(const ast::ChunkList& e) override; + + /// \name Desugar class declarations. + /// \{ + + /// Desugar class declarations. + void operator()(const ast::TypeChunk& e) override; + /// Handle the builtin Object. + void operator()(const ast::FunctionDec& e) override; + /// Desugar class names. + void operator()(const ast::NameTy& e) override; + + /// \} + + /// \name Desugar class instantiations and object usage. + /// \{ + + /// Desugar polymorphic initializations. + void operator()(const ast::VarDec& e) override; + /// Desugar manifest objects. + void operator()(const ast::ObjectExp& e) override; + /// Desugar polymorphic branching. + void operator()(const ast::IfExp& e) override; + /// Desugar polymorphic assignments. + void operator()(const ast::AssignExp& e) override; + /// Desugar polymorphic insertion. + void operator()(const ast::ArrayExp& e) override; + void operator()(const ast::RecordExp& e) override; + /// Add object casts around arguments when needed. + void operator()(const ast::CallExp& e) override; + + /// \} + + /// \name Desugar accesses to class members. + /// \{ + + /// Desugar accesses to attributes. + void operator()(const ast::FieldVar& e) override; + + /// Handle dynamic dispatch within scope. + void operator()(const ast::LetExp& e) override; + + /// Desugar calls to methods. + void operator()(const ast::MethodCallExp& e) override; + + /// \} + + /// \brief Desugar actuals arguments in routine calls. + /// + /// \param actuals the actual arguments of the initial AST + /// \param formals the formal arguments of the routine + /// \return the desugared actual arguments + ast::exps_type* recurse_args(const ast::exps_type& actuals, + const type::Record& formals); + + /// A list of classes (e.g., useful to represent a set of subclasses). + using classes_type = std::vector<const type::Class*>; + + private: + /// \name Code generation. + /// \{ + + using pair_class_method = + std::pair<const type::Class*, const type::Method*>; + using dispatch_list_type = std::set<pair_class_method>; + + /// \brief Desugar the constructor of the current class + /// + /// \param functions the tweast containing the constructor being desugared + /// \param cls the class whose constructor is being desugared + /// \param class_name the desugared version of the class name + void desugar_constructor(parse::Tweast& functions, + const type::Class* cls, + misc::symbol class_name); + + /// \brief Desugar the current method. + /// + /// \param functions the tweast containing the method being desugared + /// \param method the methods being desugared + /// \param class_name the desugared version of the class name + void desugar_method(parse::Tweast& functions, + const type::Method* method, + misc::symbol class_name); + + /// \brief Dispatch the method between the different classes. + /// + /// \param functions the tweast containing the dispatch + /// \param e the block of type being processed + /// \param cls the cls being desugared + /// \param method the method being dispatched + /// \param class_name the desugared version of the class name + /// \param sub_dispatches the processed classes/methods for dispatch + void dispatch_function(parse::Tweast& functions, + const ast::TypeChunk& e, + const type::Class* cls, + const type::Method* method, + misc::symbol class_name, + dispatch_list_type& sub_dispatches); + + /// \brief Handle a class declaration. + /// + /// This is a declaration of a class: remove it and replace it with + /// + /// (1) a class type label (integer constant), + /// (2) new data structures: + /// - an actual structure holding the data of the class, + /// - a variant able to store any concrete type for this + /// class; + /// (3) new functions: + /// - a constructor, + /// - conversion routines used in polymorphic assignments (upcasts), + /// - conversion routines used in dynamic dispatch (downcasts), + /// - (desugared) methods, + /// - dispatch functions. + /// + /// We must take care of the order in which these declarations + /// are injected, since (1), (2) and (3) are declaration of + /// different kinds (variable, type and function respectively). + /// Mixing them would break the block of type declarations + /// (TypeChunk, or ``chunk'' of TypeDec's) being currently + /// visited. Thus we collect all class definitions from E, + /// replace them by new data structures ((2)) and inject the + /// rest of the new material *after* the TypeChunk ((1) and + /// (3)). + void handle_class(const ast::TypeChunk& e, + const type::Class* cls, + parse::Tweast& functions, + dispatch_list_type& sub_dispatches); + + /// \brief Return the name of a type. + /// + /// \param type either a builtin type or a type::Named + /// \result the name of the type + std::string type_symbol(const type::Type* type); + + /// Return the name of the upcast function between types \a from + /// and \a to. + std::string upcast_fun_name(const type::Class* from, const type::Class* to); + /// Return the name of the downcast function between types \a from + /// and \a to. + std::string downcast_fun_name(const type::Class* from, + const type::Class* to); + + /// Return the name of the dispatch function for \a method, + /// defined in class \a owner. + std::string dispatch_fun_name(const type::Class* owner, + const type::Method* method); + + /// Check if the type \a source_type of \a source_exp matches + /// \a target_type, and generate a type conversion wrapper if + /// needed. + void adapt_type(ast::Exp*& source_exp, + const type::Class* source_type, + const type::Class* target_type); + + /// The type of a list of initializations for the field of a variant. + using field_inits_type = misc::map<const type::Class*, std::string>; + + /// \brief Generate a variant expression. + /// + /// \param static_type the type of the class whose variant is built + /// \param exact_type the exact type of the data stored in the variant + /// \param inits the initalization value of the variant (must be + /// of type \a dynamic_type). + /// \return the generated variant expression + ast::Exp* variant_exp(const type::Class* static_type, + const std::string& exact_type, + const field_inits_type& inits); + + /// Syntactic sugar for the previous routine. + ast::Exp* variant_exp(const type::Class* static_type, + const type::Class* dynamic_type, + const field_inits_type& inits); + + /// \brief Populate a variant fields. + /// + /// \param class_type the type of the class used to populate the variant + /// \param input the tweast containing the variant being built + void fill_variant_fields(const type::Class* class_type, + parse::Tweast* input); + + /// \brief Generate code for a variant type on a TWEAST. + /// + /// Contrary to object::DesugarVisitor::variant_exp, we + /// cannot directly generate an expression, since the client + /// TWEAST won't accept an ast::Ty* as a metavariable. + /// + /// \param class_type the type of the class whose variant is built + /// \return the TWEAST on which the code is generated + parse::Tweast* variant_ty(const type::Class* class_type); + + /// \brief Populate an initialization list. + /// + /// \param class_type the type of the class used to populate the list + /// \param inits the initialization list + void fill_init_list(const type::Class* class_type, field_inits_type& inits); + + /// \brief Generate a conversion routine. + /// + /// \param name the type of the generated function + /// \param source the type of the converted value + /// \param target the target type of the conversion + /// \param exact_type the exact type of the returned variant + /// \return the TWEAST on which the code is generated + parse::Tweast* cast_function(const std::string& name, + const type::Class* source, + const type::Class* target, + const type::Class* exact_type); + + /// Syntactic sugar for object::DesugarVisitor::cast_function, + /// used to generate an upcast function. + parse::Tweast* upcast_function(const type::Class* source, + const type::Class* target); + + /// Syntactic sugar for object::DesugarVisitor::cast_function, + /// used to generate a downcast function. + parse::Tweast* downcast_function(const type::Class* source, + const type::Class* target); + + /// \brief Generate an expression looking like a switch + /// expression, to dispatch a method call. Do not generate code + /// for classes outside typechunk. If this function is used to create a sub + /// dispatch function, recursive call should be true, in order to call + /// the previous dispatch function if nothing matches. + /// + /// \param class_type the static type of the class owning the method + /// \param method the called method + /// \param typechunk the current chunk + /// \param dispatch_method the recursively called dispatch method + /// \return the generated expression + ast::Exp* dispatch_switch(const type::Class* class_type, + const type::Method* method, + const ast::TypeChunk* typechunk, + const type::Method* dispatch_method = nullptr); + + /// \brief Generate a (static) call to a desugared method. + /// + /// \note The dynamic dispatch must have been performed before + /// using this generator. + /// + /// \param class_name the class where the method resides + /// \param method_name the name of the method + /// \param target the name of the target (object) + /// \param formals the actual arguments (other than the target) + /// \return the TWEAST on which the code is generated + parse::Tweast* method_call(misc::symbol class_name, + misc::symbol method_name, + const std::string& target, + const ast::VarChunk& formals); + + /// \} + + private: + /// The names of the classes defined in the program. + class_names_type class_names_; + + /// TWEAST of desugared types. + parse::Tweast types_; + /// TWEAST of class ids. + parse::Tweast class_ids_; + + /// TWEAST of upcast functions. + parse::Tweast funs_tweast; + + /// Vector keeping track of added dispatch functions within a scope. + misc::vector<const type::Method*> dispatch_added_; + + /// Map / counter giving the correct current dispatch extensions for a method + misc::map<const type::Method*, unsigned int> dispatch_map_; + }; + +} // namespace object diff --git a/tiger-compiler/src/object/fwd.hh b/tiger-compiler/src/object/fwd.hh new file mode 100644 index 0000000..7b7d779 --- /dev/null +++ b/tiger-compiler/src/object/fwd.hh @@ -0,0 +1,16 @@ +/** + ** \file object/fwd.hh + ** \brief Forward declarations of using types. + */ + +#pragma once + +#include <misc/map.hh> +#include <misc/symbol.hh> +#include <type/class.hh> + +namespace object +{ + /// Names associated to class types. + using class_names_type = misc::map<const type::Class*, misc::symbol>; +} // namespace object diff --git a/tiger-compiler/src/object/libobject.cc b/tiger-compiler/src/object/libobject.cc new file mode 100644 index 0000000..b7c7f72 --- /dev/null +++ b/tiger-compiler/src/object/libobject.cc @@ -0,0 +1,49 @@ +/** + ** \file object/libobject.cc + ** \brief Define exported object functions. + */ + +// FIXME DONE: Some code was deleted here. +#include <object/libobject.hh> +#include <object/binder.hh> +#include <object/renamer.hh> +#include <object/type-checker.hh> + +namespace object +{ + /*-------. + | Bind. | + `-------*/ + + // FIXME DONE: Some code was deleted here. + misc::error bind_obj(ast::ChunkList* d) + { + Binder bdc = Binder(); + bdc(d); + return bdc.error_get(); + } + + /*----------------. + | Compute types. | + `----------------*/ + + misc::error types_check(ast::Ast& tree) + { + TypeChecker type; + type(tree); + return type.error_get(); + } + + /*---------. + | Rename. | + `---------*/ + + class_names_type* rename(ast::Ast& tree) + { + // Rename. + Renamer rename; + rename(tree); + return rename.class_names_get(); + } + +} // namespace object diff --git a/tiger-compiler/src/object/libobject.hh b/tiger-compiler/src/object/libobject.hh new file mode 100644 index 0000000..d153ef4 --- /dev/null +++ b/tiger-compiler/src/object/libobject.hh @@ -0,0 +1,82 @@ +/** + ** \file object/libobject.hh + ** \brief Declare functions and variables exported by object module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> +#include <object/fwd.hh> + +namespace object +{ + /*-------. + | Bind. | + `-------*/ + + // FIXME DONE: Some code was deleted here. + /// \brief Bind the whole ast_object in place, return the error code + /// + /// \param last the ast you want to bind + /// + /// \return a misc::error that serve to indicate possible failure + misc::error bind_obj(ast::ChunkList* last); + + /*----------------. + | Compute types. | + `----------------*/ + + /** \brief Check types allowing objects. + + \param tree abstract syntax tree's root. + + \return success of the type-checking. */ + misc::error types_check(ast::Ast& tree); + + /*---------. + | Rename. | + `---------*/ + + /// Rename the variables of an AST so that they each have a unique + /// name, with support for objects. + /// + /// \param tree abstract syntax tree's root, whose bindings and types + /// have been computed. + /// \return a newly allocated dictionnary of class names + class_names_type* rename(ast::Ast& tree); + + /*------------------. + | Desugar objects. | + `------------------*/ + + /** \brief Remove objects constructs from an AST. + + \param tree abstract syntax tree's root, whose bindings + and types have been computed, and whose + identifiers are all unique. + \param class_names the names of the class types of the AST + + \return the desugared, bound and type-checked AST. */ + template <typename A> + A* desugar(const A& tree, const class_names_type& class_names); + + /** \brief Remove objects constructs from an AST without recomputing + its bindings nor its types. + + This function acts like object::object_desugar, but stops just + after the desugaring step (in fact, object::desugar is built + upon this function). It is meant to be used as a test of + DesugarVisitor (i.e., even if the desugared tree is badly bound + or typed, it can still be pretty-printed). + + \param tree AST to desugar. + \param class_names the names of the class types of the AST + + \return the desugared AST. */ + template <typename A> + A* raw_desugar(const A& tree, const class_names_type& class_names); + +} // namespace object + +#include <object/libobject.hxx> diff --git a/tiger-compiler/src/object/libobject.hxx b/tiger-compiler/src/object/libobject.hxx new file mode 100644 index 0000000..7c75b08 --- /dev/null +++ b/tiger-compiler/src/object/libobject.hxx @@ -0,0 +1,48 @@ +#pragma once + +/** + ** \file object/libobject.hxx + ** \brief Functions exported by the object module. + */ + +#include <memory> + +#include <desugar/libdesugar.hh> +#include <object/desugar-visitor.hh> +#include <object/fwd.hh> + +namespace object +{ + + /*------------------. + | Desugar objects. | + `------------------*/ + + template <typename A> + A* raw_desugar(const A& tree, const class_names_type& class_names) + { + // Desugar. + ::object::DesugarVisitor desugar(class_names); + desugar(tree); + return dynamic_cast<A*>(desugar.result_get()); + } + + template <typename A> + A* desugar(const A& tree, const class_names_type& class_names) + { + // Desugar. + A* desugared = raw_desugar(tree, class_names); + assertion(desugared); + std::unique_ptr<A> desugared_ptr(desugared); + // Recompute the bindings and the types. + ::desugar::bind_and_types_check(*desugared_ptr); + return desugared_ptr.release(); + } + + /// Explicit instantiations. + template ast::ChunkList* raw_desugar(const ast::ChunkList&, + const class_names_type&); + template ast::ChunkList* desugar(const ast::ChunkList&, + const class_names_type&); + +} // namespace object diff --git a/tiger-compiler/src/object/local.am b/tiger-compiler/src/object/local.am new file mode 100644 index 0000000..3c1eda9 --- /dev/null +++ b/tiger-compiler/src/object/local.am @@ -0,0 +1,33 @@ +## object module. + +src_libtc_la_SOURCES += \ + %D%/libobject.hh \ + %D%/libobject.cc \ + %D%/libobject.hxx \ + %D%/fwd.hh + +src_libtc_la_SOURCES += %D%/binder.hh %D%/binder.cc + +src_libtc_la_SOURCES += %D%/type-checker.hh %D%/type-checker.cc \ + %D%/renamer.hh %D%/renamer.cc + +src_libtc_la_SOURCES += %D%/desugar-visitor.hh %D%/desugar-visitor.cc + + +# Tests. +check_PROGRAMS += %D%/test-parse +%C%_test_parse_LDADD = src/libtc.la + +check_PROGRAMS += %D%/test-bind +%C%_test_bind_LDADD = src/libtc.la + +check_PROGRAMS += %D%/test-type +%C%_test_type_LDADD = src/libtc.la + +# FIXME: Add a test for object::Renamer. + +check_PROGRAMS += %D%/test-desugar +%C%_test_desugar_LDADD = src/libtc.la + + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/object/renamer.cc b/tiger-compiler/src/object/renamer.cc new file mode 100644 index 0000000..0abae10 --- /dev/null +++ b/tiger-compiler/src/object/renamer.cc @@ -0,0 +1,142 @@ +/** + ** \file object/renamer.cc + ** \brief Implementation of object::Renamer. + */ + +#include <memory> + +#include <object/renamer.hh> + +namespace object +{ + using namespace ast; + + Renamer::Renamer() + : super_type() + , class_names_(new class_names_type) + , within_class_ty_(false) + { + // Give a name to the built-in class Object. + misc::put(*class_names_, &type::Class::object_instance(), "Object"); + } + + /** + * Get the node's type as a class, if it references one + * @param ast Any typable node + * @return The node's referenced class type if there is one, nullptr if not + */ + static inline const type::Class* get_referenced_class_type(const Typable& ast) + { + return dynamic_cast<const type::Class*>(&ast.type_get()->actual()); + } + + /*----------------------------. + | Visiting definition sites. | + `----------------------------*/ + + void Renamer::operator()(ast::VarDec& e) + { + if (within_class_ty_) + // Don't rename class attributes. + super_type::super_type::operator()(e); + else + // But still rename other variable declarations. + super_type::operator()(e); + } + + void Renamer::operator()(ast::MethodChunk& e) + { + // FIXME DONE: Some code was deleted here (Just recurse on children nodes). + chunk_visit<MethodChunk>(e); + } + + void Renamer::operator()(ast::MethodDec& e) + { + bool saved_within_class_ty = within_class_ty_; + within_class_ty_ = false; + /* We can't call bind::Binder::visit directly, because this method + delegates the recursion task to DefaultVisitor, which doesn't + have a proper operator() for MethodDec. This visitor however + knows how to handle a FunctionDec; therefore we upcast the + MethodDec to a FunctionDec before visiting it. */ + ast::FunctionDec& fundec = e; + visit(fundec, &fundec); + within_class_ty_ = saved_within_class_ty; + } + + void Renamer::operator()(ast::TypeDec& e) + { + // Rename. + // FIXME DONE: Some code was deleted here. + if (class_names_->find(get_referenced_class_type(e)) == class_names_->end()) + super_type::operator()(e); + + // Collect the name of the classes. + // FIXME DONE: Some code was deleted here. + if (auto classtype = get_referenced_class_type(e)) + { + class_names_->insert(std::make_pair(classtype, e.name_get())); + } + } + + /*-----------------------. + | Visiting usage sites. | + `-----------------------*/ + + void Renamer::operator()(ast::MethodCallExp& e) + { + // FIXME DONE: Some code was deleted here. + super_type::operator()(e.object_get()); + e.name_set(e.def_get()->name_get()); + CallExp& upcast = e; + precondition(upcast.def_get() == nullptr); + super_type::operator()(upcast); + } + + /*--------------------------------------. + | Visiting other object-related nodes. | + `--------------------------------------*/ + + void Renamer::operator()(ast::ClassTy& e) + { + // FIXME DONE: Some code was deleted here. + bool saved_within_class_ty = within_class_ty_; + within_class_ty_ = true; + if (e.super_get().name_get() != "Object") + e.super_get().name_set(e.super_get().def_get()->name_get()); + super_type::operator()(e.chunks_get()); + within_class_ty_ = saved_within_class_ty; + } + + void Renamer::operator()(ast::ObjectExp& e) + { + // FIXME DONE: Some code was deleted here. + if (auto name = class_names_->find(get_referenced_class_type(e)); + name != class_names_->end()) + e.type_name_get().name_set(name->second); + else + { + this->accept(e.type_name_get().def_get()); + e.type_name_get().name_set(e.type_name_get().def_get()->name_get()); + class_names_->insert( + std::make_pair(get_referenced_class_type(e), + e.type_name_get().name_get())); + } + } + + void Renamer::operator()(ast::LetExp& e) + { + // FIXME DONE: Some code was deleted here. + bool saved_within_class_ty = within_class_ty_; + within_class_ty_ = false; + super_type::operator()(e); + within_class_ty_ = saved_within_class_ty; + } + + /*--------------. + | Class names. | + `--------------*/ + + class_names_type* Renamer::class_names_get() const { return class_names_; } + +} // namespace object diff --git a/tiger-compiler/src/object/renamer.hh b/tiger-compiler/src/object/renamer.hh new file mode 100644 index 0000000..6c62a86 --- /dev/null +++ b/tiger-compiler/src/object/renamer.hh @@ -0,0 +1,79 @@ +/** + ** \file object/renamer.hh + ** \brief Implementation of object::Renamer. + */ + +#pragma once + +#include <map> + +#include <bind/renamer.hh> +#include <object/fwd.hh> + +namespace object +{ + /// \brief Perform identifier renaming within an AST (in place), + /// with support for objects. + class Renamer : public bind::Renamer + { + public: + using super_type = ::bind::Renamer; + + // Import overloaded virtual functions. + using super_type::operator(); + + /// Build a Renamer. + Renamer(); + + // Visit methods. + /// \name Visiting definition sites. + /// \{ + /// This method is like bind::Binder's, but prevent the renaming + /// of attributes. + void operator()(ast::VarDec& e) override; + /// Rename methods. + void operator()(ast::MethodChunk& e) override; + /// Rename a method. + void operator()(ast::MethodDec& e) override; + /// In addition to performing the renaming, collect the name of + /// the classes. + void operator()(ast::TypeDec& e) override; + /// \} + + /// \name Visiting usage sites. + /// \{ + void operator()(ast::MethodCallExp& e) override; + /// \} + + /// \name Visiting other object-related nodes. + /// + /// These methods should be part of an ObjectDefaultVisitor, but + /// our current design makes the implementation (and the use) of + /// such a visitor difficult. + /// \{ + void operator()(ast::ClassTy& e) override; + void operator()(ast::ObjectExp& e) override; + /// \} + + /// \name Visiting LetExp. + /// + /// In order to handle variable declarations that might be + /// situated in a ClassTy and yet do not qualify as attributes. + /// \{ + void operator()(ast::LetExp& e) override; + /// \} + + /// Class names. + /// \{ + /// Get the class names. + class_names_type* class_names_get() const; + + private: + /// Dictionnary mapping class types to their names. + class_names_type* class_names_; + /// Are we in a class definition? + bool within_class_ty_; + /// \} + }; + +} // namespace object diff --git a/tiger-compiler/src/object/tasks.cc b/tiger-compiler/src/object/tasks.cc new file mode 100644 index 0000000..c4d0929 --- /dev/null +++ b/tiger-compiler/src/object/tasks.cc @@ -0,0 +1,52 @@ +/** + ** \file object/tasks.cc + ** \brief Object module related tasks' implementation. + */ + +#include <memory> + +#include <ast/tasks.hh> +#include <astclone/libastclone.hh> +#include <common.hh> +#include <object/libobject.hh> +#define DEFINE_TASKS 1 +#include <object/tasks.hh> +#undef DEFINE_TASKS + +namespace object::tasks +{ + void object_parse() {} + + // FIXME DONE: Some code was deleted here. + void object_bind() + { + task_error() << bind_obj(ast::tasks::the_program.get()) + << &misc::error::exit_on_error; + } + + void object_types_compute() + { + task_error() << ::object::types_check(*ast::tasks::the_program) + << &misc::error::exit_on_error; + } + + static std::unique_ptr<class_names_type> class_names; + + void object_rename() + { + class_names.reset(::object::rename(*ast::tasks::the_program)); + } + + void object_desugar() + { + astclone::apply(::object::desugar, ast::tasks::the_program, + *class_names.get()); + } + + void raw_object_desugar() + { + astclone::apply(::object::raw_desugar, ast::tasks::the_program, + *class_names.get()); + } + +} // namespace object::tasks diff --git a/tiger-compiler/src/object/tasks.hh b/tiger-compiler/src/object/tasks.hh new file mode 100644 index 0000000..9a108f3 --- /dev/null +++ b/tiger-compiler/src/object/tasks.hh @@ -0,0 +1,60 @@ +/** + ** \file object/tasks.hh + ** \brief Object module related tasks. + */ + +#pragma once + +#include <overload/binder.hh> +#include <task/libtask.hh> + +namespace object::tasks +{ + TASK_GROUP("Object"); + + /// Enable object extensions. + BOOLEAN_TASK_DECLARE("o|object", + "enable object extensions", + enable_object_extensions_p, + ""); + + /// Parse the input file, allowing objects. + TASK_DECLARE("object-parse", + "parse a file, allowing objects", + object_parse, + "object parse"); + + // FIXME DONE: Some code was deleted here. + TASK_DECLARE("object-bindings-compute", + "bind the name uses to their definitions, allowing objects", + object_bind, + "object-parse"); + + /// Check for type violation, allowing objects. + TASK_DECLARE("object-types-compute", + "check for type violations, " + "allowing objects", + object_types_compute, + "object-bindings-compute"); + + /// Perform a renaming, before desugaring objects. + TASK_DECLARE("object-rename", + "rename identifiers to unique names, allowing objects", + object_rename, + "object-types-compute"); + + /// Remove syntactic sugar from the Ast. + TASK_DECLARE("object-desugar", + "remove object constructs from the program", + object_desugar, + "object-rename"); + + /// Remove syntactic sugar from the Ast without recomputing + /// bindings nor types. + TASK_DECLARE("raw-object-desugar", + "remove object constructs from the program " + "without recomputing bindings nor types", + raw_object_desugar, + "object-rename"); + +} // namespace object::tasks diff --git a/tiger-compiler/src/object/type-checker.cc b/tiger-compiler/src/object/type-checker.cc new file mode 100644 index 0000000..76c4e88 --- /dev/null +++ b/tiger-compiler/src/object/type-checker.cc @@ -0,0 +1,405 @@ +/** + ** \file object/type-checker.cc + ** \brief Implementation for object/type-checker.hh. + */ + +#include <iostream> +#include <memory> +#include <sstream> +#include <boost/iterator/zip_iterator.hpp> + +#include <ast/all.hh> +#include <object/type-checker.hh> +#include <type/types.hh> + +namespace object +{ + TypeChecker::TypeChecker() + : super_type() + { + // Reset the subclasses of Object. This is required if several + // trees are processed during the compilation. + type::Class::object_instance().subclasses_clear(); + + // `self' variables are the only valid variables having a null + // declaration site. Use this property to tag them as read-only. + // FIXME DONE: Some code was deleted here. + var_read_only_.insert(nullptr); + } + + /*--------------------------. + | The core of the visitor. | + `--------------------------*/ + + /*-----------------. + | Visiting /Var/. | + `-----------------*/ + + void TypeChecker::operator()(ast::SimpleVar& e) + { + // FIXME DONE: Some code was deleted here. + if (e.name_get() == "self") + type_default(e, current_); + else + super_type::operator()(e); + } + + void TypeChecker::operator()(ast::FieldVar& e) + { + const type::Type* def_type = nullptr; + // FIXME DONE: Some code was deleted here (Grab type). + def_type = type(e.var_get()); + auto class_type = dynamic_cast<const type::Class*>(&def_type->actual()); + + if (class_type) + { + // FIXME DONE: Some code was deleted here. + auto attr = class_type->attr_find(e.name_get()); + if (attr == nullptr) + error(e, "No such attribute found"); + else + type_default(e, attr->def_get()->type_get()); + } + else + super_type::operator()(e); + } + + /*----------------. + | Visiting /Ty/. | + `----------------*/ + + // Handle the case of `Object'. + void TypeChecker::operator()(ast::NameTy& e) + { + // FIXME DONE: Some code was deleted here. + if (e.name_get() == "Object") + { + type_default(e, &type::Class::object_instance()); + } + else + super_type::operator()(e); + } + + /*-----------------. + | Visiting /Exp/. | + `-----------------*/ + + void TypeChecker::operator()(ast::IfExp& e) + { + // We want to handle the following case + // let + // class A {} + // class B extends A { method print() = () } + // var a := + // if 1 then + // new B + // else + // new A + // in + // a.print() /* error */ + // end + // FIXME DONE: Some code was deleted here. + //Martial: Pré typage + super_type::operator()(e); + //Martial: Check mentionné au-dessus + if (auto then_class = dynamic_cast<type::Class*>(&e.thenclause_get()); + then_class) + { + auto else_class = dynamic_cast<type::Class*>(&e.elseclause_get()); + if (then_class->id_get() != else_class->id_get()) + type_mismatch(e, "then clause", *e.thenclause_get().type_get(), + "else clause", *e.elseclause_get().type_get()); + } + } + + void TypeChecker::operator()(ast::OpExp& e) + { + // We want to only compare equal static object types. + // Otherwise, the desugarer emits wrong code on: + // + // let + // class A {} + // class B extends A {} + // var a := new A + // var b := new B + // in + // a = b + // end + // FIXME DONE: Some code was deleted here. + //Martial: Pré typage + super_type::operator()(e); + //Martial: Check mentionné au-dessus + + const ast::OpExp::Oper operation_type = e.oper_get(); + const auto a_class = + dynamic_cast<const type::Class*>(&e.left_get().type_get()->actual()); + const auto b_class = + dynamic_cast<const type::Class*>(&e.right_get().type_get()->actual()); + + if (!a_class || !b_class || operation_type < ast::OpExp::Oper::eq + || operation_type > ast::OpExp::Oper::ne) + { + return; + } + + if (a_class->id_get() != b_class->id_get()) + { + type_mismatch(e, "left operand", *e.left_get().type_get(), + "right operand", *e.right_get().type_get()); + } + } + + void TypeChecker::operator()(ast::ObjectExp& e) + { + // FIXME DONE: Some code was deleted here. + type(e.type_name_get()); + type_default(e, e.type_name_get().type_get()); + } + + void TypeChecker::operator()(ast::MethodCallExp& e) + { + // FIXME DONE: Some code was deleted here. + type(e.object_get()); + for (ast::Exp* exp : e.args_get()) + { + type(*exp); + } + const auto actual_params = e.args_get(); + + const auto obj = + dynamic_cast<const type::Class*>(&e.object_get().type_get()->actual()); + assertion(obj != nullptr); + const auto meth = obj->meth_find(e.name_get()); + + if (!meth) + { + // std::string error_message = "method "; + error(e, + "method " + e.name_get().get() + + " does not exist within the " + "class"); + type_default(e, &default_type); + return; + } + + e.def_set(const_cast<ast::MethodDec*>(meth->def_get())); + const auto expected_params = e.def_get()->formals_get().decs_get(); + + if (actual_params.size() != expected_params.size()) + { + error(e, + std::string(std::to_string(expected_params.size()) + + " parameters expected but got " + + std::to_string(actual_params.size()))); + } + else + { + std::for_each( + boost::make_zip_iterator( + boost::make_tuple(expected_params.begin(), actual_params.begin())), + boost::make_zip_iterator( + boost::make_tuple(expected_params.end(), actual_params.end())), + [this, &e](const boost::tuple<ast::VarDec*, ast::Exp*>& params) { + check_types(e, "expected", *params.get<0>(), "actual", + *params.get<1>()); + }); + } + + type_default(e, + &dynamic_cast<const type::Method*>(e.def_get()->type_get()) + ->result_get()); + } + + /*-----------------. + | Visiting /Dec/. | + `-----------------*/ + + /*--------------------. + | Visiting TypeChunk. | + `--------------------*/ + + void TypeChecker::operator()(ast::TypeChunk& e) + { + // Visit the header and the body of the typechunk, as in + // type::TypeChecker. + super_type::operator()(e); + + // However, class members are not considered part of the body of + // their class here; they are processed separately to allow valid + // uses of the class from its members. + for (ast::TypeDec* typedec : e) + { + ast::Ty& ty = typedec->ty_get(); + if (auto classty = dynamic_cast<ast::ClassTy*>(&ty)) + visit_dec_members(*classty); + } + } + + /*----------------------. + | Visiting MethodChunk. | + `----------------------*/ + + void TypeChecker::operator()(ast::MethodChunk& e) + { + precondition(within_class_body_p_); + within_class_body_p_ = false; + + // Two passes: once on headers, then on bodies. + for (ast::MethodDec* m : e) + visit_dec_header(*m); + for (ast::MethodDec* m : e) + visit_dec_body(*m); + + within_class_body_p_ = true; + } + + // Store the type of this method. + void TypeChecker::visit_dec_header(ast::MethodDec& e) + { + assertion(current_); + + // FIXME DONE: Some code was deleted here. + const type::Record* formals = type(e.formals_get()); + + const type::Type* return_type = e.result_get() != nullptr + ? type(*e.result_get()) + : &type::Void::instance(); + + auto type = + std::make_unique<type::Method>(e.name_get(), current_, formals, *return_type, &e); + + // Check for multiple definitions in the current class. + for (const type::Method* m : current_->meths_get()) + if (m->name_get() == e.name_get()) + return error(e, "method multiply defined", e.name_get()); + + // Check for signature conformance w.r.t. super class, if applicable. + const auto* super_meth_type = + dynamic_cast<const type::Method*>(current_->meth_type(e.name_get())); + // FIXME DONE: Some code was deleted here. + if (super_meth_type && !super_meth_type->compatible_with(*type.get())) + type_mismatch(e, "super class method signature", *super_meth_type, + "child class method signature", *type.get()); + else + type_default(e, type.get()); + current_->meth_add(type.release()); + } + + // Type check this method's body. + void TypeChecker::visit_dec_body(ast::MethodDec& e) + { + visit_routine_body<type::Method>(e); + } + + /*---------------. + | Visit VarDec. | + `---------------*/ + + void TypeChecker::operator()(ast::VarDec& e) + { + // Signal that we are not directly inside a class' body, to avoid binding + // spurious members. + // + // For example: + // let + // class A = + // { + // var a := let var b := 0 in b end + // } + // var toto := new A + // in + // toto.a /* Valid */ + // toto.b /* Invalid */ + // end + bool saved_within_class_body = within_class_body_p_; + within_class_body_p_ = false; + super_type::operator()(e); + within_class_body_p_ = saved_within_class_body; + + /* If we are directly inside a class declaration then E is an attribute: + record it into the CURRENT_ class. */ + if (within_class_body_p_) + { + assertion(current_); + + if (current_->attr_type(e.name_get())) + error(e, "attribute multiply defined", e.name_get()); + else + current_->attr_add(&e); + } + } + + /*-------------. + | Visit /Ty/. | + `-------------*/ + + // Don't handle members, as visit_dec_members is in charge of this task. + void TypeChecker::operator()(ast::ClassTy& e) + { + // FIXME DONE: Some code was deleted here (Create class). + /* + ** Moi sur le point de me faire chier dessus en code review parce que ma + ** variable est en français juste pour éviter le keyword `class' + */ + auto classe = std::make_unique<type::Class>(); + + type_default(e, classe.get()); + + /* ------------------- * + * Superclass handling * + * ------------------- */ + + // FIXME DONE: Some code was deleted here (Set the type of the super class). + + const type::Type* supertype; + if (e.super_get().def_get() == nullptr) + supertype = type(e.super_get()); + else + supertype = type(e.super_get().def_get()->ty_get()); + + classe->super_set(dynamic_cast<const type::Class*>(&supertype->actual())); + + // FIXME DONE: Some code was deleted here (Recursively update the list of subclasses of the super classes). + if (!classe->sound()) + error(e, "infinite type inheritance recursion detected"); + + std::vector<const type::Class*> previous; + for (auto super = classe->super_get(); super != nullptr + && std::ranges::find(previous, super) == previous.end(); + super = super->super_get()) + { + super->subclass_add(classe.get()); + previous.push_back(super); + } + + /* à un moment je crois qu'il faut */ + created_type_default(e, classe.release()); + /* mais ça serait trop simple si je savais quand */ + } + + // Handle the members of a class. + void TypeChecker::visit_dec_members(ast::ClassTy& e) + { + assertion(!within_class_body_p_); // Should be false by the time we get here + const type::Type* type = nullptr; + // FIXME DONE: Some code was deleted here. + // là je crois faut get un type + type = e.type_get(); + + assertion(type); + auto class_type = dynamic_cast<const type::Class*>(type); + assertion(class_type); + + type::Class* saved_class_type = current_; + within_class_body_p_ = true; + // Make the type writable, so that we can add references to the + // types of the members. + current_ = const_cast<type::Class*>(class_type); + e.chunks_get().accept(*this); + + // Set back the status we had before we visited the members. + current_ = saved_class_type; + within_class_body_p_ = false; + } + +} // namespace object diff --git a/tiger-compiler/src/object/type-checker.hh b/tiger-compiler/src/object/type-checker.hh new file mode 100644 index 0000000..c965d25 --- /dev/null +++ b/tiger-compiler/src/object/type-checker.hh @@ -0,0 +1,108 @@ +/** + ** \file object/type-checker.hh + ** \brief Checking an ObjectTiger program in a Tiger program. + */ + +#pragma once + +#include <type/class.hh> +#include <type/type-checker.hh> +#include <type/types.hh> + +namespace object +{ + /** \brief Perform type checking, allowing objects, and compute + ** the bindings of the object's methods and fields. + ** + ** Inheritence is declared virtual to enable diamond inheritance with + ** the TypeChecker (src/combine/type-checker.hh), inheriting + ** from overload::TypeChecker and object::TypeChecker, both inheriting from + ** type::TypeChecker. + **/ + class TypeChecker : virtual public type::TypeChecker + { + public: + /// Superclass. + using super_type = type::TypeChecker; + using super_type::operator(); + + /// Construction. + TypeChecker(); + + protected: + // ------------------------- // + // The core of the visitor. // + // ------------------------- // + + // ---------------- // + // Visiting /Var/. // + // ---------------- // + + void operator()(ast::FieldVar& e) override; + void operator()(ast::SimpleVar& e) override; + + // --------------- // + // Visiting /Ty/. // + // --------------- // + + void operator()(ast::NameTy& e) override; + + // ---------------- // + // Visiting /Exp/. // + // ---------------- // + + // Method exp. + void operator()(ast::IfExp& e) override; + void operator()(ast::OpExp& e) override; + void operator()(ast::ObjectExp& e) override; + void operator()(ast::MethodCallExp& e) override; + + // ---------------- // + // Visiting /Dec/. // + // ---------------- // + + /** Visit a chunk of type declarations. + + This method is like type::TypeChecker's one, except that it + processes class declarations in three steps, instead of two + (visit the headers, then the bodies): + + <ol> + <li>visit headers, as in type::TypeChecker;</li> + <li>visit bodies, which ignore all members of the class;</li> + <li>visit members, i.e., attributes and methods.</li> + </ol> + + This way, a method of a class can use the type of this class + (e.g., return \a self), as that type has been created in the + second step, and the method is processed in the third. */ + void operator()(ast::TypeChunk& e) override; + + // Check a Method's declaration header. + void visit_dec_header(ast::MethodDec& e); + // Check a Method's declaration body. + void visit_dec_body(ast::MethodDec& e); + + /// Visit a chunk of method declarations. + void operator()(ast::MethodChunk& e) override; + + /// Visit a single Variable Declaration. + void operator()(ast::VarDec& e) override; + + // --------------- // + // Visiting /Ty/. // + // --------------- // + + // Visit a class definition \em without its members. + void operator()(ast::ClassTy& e) override; + // Visit the members of a class. + virtual void visit_dec_members(ast::ClassTy& e); + + private: + /// Current visited class. + type::Class* current_ = nullptr; + /// Are we directly within an ast::ClassTy's body? + bool within_class_body_p_ = false; + }; + +} // namespace object diff --git a/tiger-compiler/src/overload/binder.cc b/tiger-compiler/src/overload/binder.cc new file mode 100644 index 0000000..14610fe --- /dev/null +++ b/tiger-compiler/src/overload/binder.cc @@ -0,0 +1,45 @@ +/** + ** \file overload/binder.cc + ** \brief Implementation of overload::Binder. + */ + +#include <ast/all.hh> +#include <overload/binder.hh> + +namespace overload +{ + overfun_bindings_type& Binder::overfun_bindings_get() + { + return overfun_bindings_; + } + + /*---------. + | Scopes. | + `---------*/ + + // FIXME: Some code was deleted here. + + /*-------------------. + | Visiting methods. | + `-------------------*/ + + // Same as Binder's, but do not set the definition site of E. + void Binder::operator()(ast::CallExp& e) + { + // FIXME: Some code was deleted here. + } + + // Insert the prototype of the function in the environment. + void Binder::visit_dec_header(ast::FunctionDec& e) + { + // FIXME: Some code was deleted here (Checks for the _main case). + overfuns_.put(e.name_get(), e); + } + + void Binder::operator()(ast::FunctionChunk& e) + { + // Two passes: once on headers, then on bodies. + // FIXME: Some code was deleted here. + } + +} // namespace overload diff --git a/tiger-compiler/src/overload/binder.hh b/tiger-compiler/src/overload/binder.hh new file mode 100644 index 0000000..607debb --- /dev/null +++ b/tiger-compiler/src/overload/binder.hh @@ -0,0 +1,65 @@ +/** + ** \file overload/binder.hh + ** \brief Declaration of overload::Binder. + **/ + +#pragma once + +#include <map> + +#include <bind/binder.hh> +#include <overload/over-table.hh> + +namespace overload +{ + /// Type of the dictionary of potential function bindings. + using overfun_bindings_type = std::multimap<ast::CallExp*, ast::FunctionDec*>; + + /** \brief Computing bindings with support for overloaded functions. + ** + ** This visitor inherits from Binder, but redefines methods + ** dealing with function declarations and uses, to allow + ** overloading. + ** + ** As overloading requires some knowledge of the type of the + ** arguments of a function, no real function binding is done here. + ** We store all potential function declarations (``homonyms'') for + ** each function call, and we'll let the overload::TypeChecker + ** decide later. + ** + ** Inheritance is declared virtual to enable diamond inheritance with + ** the combine::Binder (src/combine/binder.hh), inheriting from + ** overload::Binder and object::Binder, both inheriting from bind::Binder. + **/ + class Binder : virtual public bind::Binder + { + public: + /// Super class type. + using super_type = bind::Binder; + /// Import all the overloaded operator() methods. + using super_type::operator(); + + /* The visiting methods. */ + /// Visit a function call. + void operator()(ast::CallExp& e) override; + + /// Check a function declaration header. + void visit_dec_header(ast::FunctionDec& e); + /// Visit a chunk of function declarations. + void operator()(ast::FunctionChunk& e) override; + + /// Return the function bindings. + overfun_bindings_type& overfun_bindings_get(); + + // FIXME: Some code was deleted here (Overload scope handling). + /** \} */ + + private: + using overtable_type = OverTable<ast::FunctionDec>; + /// The environment of (overloaded) functions. + overtable_type overfuns_; + /// The potential function bindings. + overfun_bindings_type overfun_bindings_; + }; + +} // namespace overload diff --git a/tiger-compiler/src/overload/liboverload.cc b/tiger-compiler/src/overload/liboverload.cc new file mode 100644 index 0000000..7662300 --- /dev/null +++ b/tiger-compiler/src/overload/liboverload.cc @@ -0,0 +1,28 @@ +/** + ** \file overload/liboverload.cc + ** \brief Define exported type functions. + */ + +#include <overload/binder.hh> +#include <overload/liboverload.hh> +#include <overload/type-checker.hh> + +namespace overload +{ + std::pair<overfun_bindings_type, misc::error> bind(ast::Ast& tree) + { + Binder bind; + bind(tree); + return std::pair(std::move(bind.overfun_bindings_get()), + std::move(bind.error_get())); + } + + misc::error types_check(ast::Ast& tree, + const overfun_bindings_type& overfun_bindings) + { + TypeChecker type{overfun_bindings}; + type(tree); + return type.error_get(); + } + +} // namespace overload diff --git a/tiger-compiler/src/overload/liboverload.hh b/tiger-compiler/src/overload/liboverload.hh new file mode 100644 index 0000000..6c89862 --- /dev/null +++ b/tiger-compiler/src/overload/liboverload.hh @@ -0,0 +1,36 @@ +/** + ** \file overload/liboverload.hh + ** \brief Declare functions and variables exported by overload module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> +#include <overload/binder.hh> + +/** + ** \brief Perform type checking, allowing function overloading. + */ +namespace overload +{ + /** \brief Bind identifier uses to their definition, allowing + function overloading, and return a list of potential definition + sites for each function call. + + \param tree AST to bind. + + \return a pair whose first element is the potential function + bindings, and the second element the error status. */ + std::pair<overfun_bindings_type, misc::error> bind(ast::Ast& tree); + + /** \brief Check types allowing function overloading. + + \param tree abstract syntax tree's root. + \param overfun_bindings potential function bindings. + + \return success of the type-checking. */ + misc::error types_check(ast::Ast& tree, + const overfun_bindings_type& overfun_bindings); + +} // namespace overload diff --git a/tiger-compiler/src/overload/local.am b/tiger-compiler/src/overload/local.am new file mode 100644 index 0000000..022ce5f --- /dev/null +++ b/tiger-compiler/src/overload/local.am @@ -0,0 +1,9 @@ +## overload module. + +src_libtc_la_SOURCES += \ + %D%/over-table.hh %D%/over-table.hxx \ + %D%/binder.hh %D%/binder.cc \ + %D%/type-checker.hh %D%/type-checker.cc \ + %D%/liboverload.hh %D%/liboverload.cc + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/overload/over-table.hh b/tiger-compiler/src/overload/over-table.hh new file mode 100644 index 0000000..04ddb34 --- /dev/null +++ b/tiger-compiler/src/overload/over-table.hh @@ -0,0 +1,63 @@ +/** + ** \file overload/over-table.hh + ** \brief Checking/translating an OverTiger program in a Tiger program. + */ + +#pragma once + +#include <map> +#include <vector> + +namespace overload +{ + template <typename T> class OverTable + { + public: + using map_type = std::multimap<const misc::symbol, T*>; + using iterator = typename map_type::iterator; + using const_iterator = typename map_type::const_iterator; + using oversymtab_type = std::vector<map_type>; + using range_type = std::pair<const_iterator, const_iterator>; + + /// Create a new over table. + OverTable(); + + /// \name Symbol handling + /// \{ + /// Put \a key in the map and add the value to the associated container. + void put(misc::symbol key, T& value); + + /// Return the range associated to the key. + /// + /// If the key is not found, the beginning and the end of the range are + /// equal. + range_type get(misc::symbol key); + /// \} + + /// \name Scopes. + /// \{ + /// \brief Open a new scope. + /// + /// All further type related declarations will be limited to this scope. + void scope_begin(); + + /// \brief Close the last scope. + /// + /// Forget everything (i.e. every type related informations) since the + /// latest scope_begin(). + void scope_end(); + /// \} + + /// Print the table + std::ostream& dump(std::ostream& ostr) const; + + protected: + oversymtab_type oversymtab_; + }; + + template <typename T> + std::ostream& operator<<(std::ostream& ostr, const OverTable<T>& tbl); + +} // namespace overload + +#include <overload/over-table.hxx> diff --git a/tiger-compiler/src/overload/over-table.hxx b/tiger-compiler/src/overload/over-table.hxx new file mode 100644 index 0000000..de8f98b --- /dev/null +++ b/tiger-compiler/src/overload/over-table.hxx @@ -0,0 +1,64 @@ +/** + ** \file overload/over-table.hxx + ** \brief Inline methods and template implementations for overload/over-table.hh. + */ + +#pragma once + +#include <ostream> +#include <ranges> + +#include <overload/over-table.hh> + +namespace overload +{ + template <typename T> OverTable<T>::OverTable() + { + oversymtab_.emplace_back(); + } + + template <typename T> void OverTable<T>::put(misc::symbol key, T& value) + { + oversymtab_.back().emplace(key, &value); + } + + template <typename T> + typename OverTable<T>::range_type OverTable<T>::get(misc::symbol key) + { + precondition(!oversymtab_.empty()); + auto& map = oversymtab_.back(); + return map.equal_range(key); + } + + template <typename T> void OverTable<T>::scope_begin() + { + oversymtab_.emplace_back(oversymtab_.back()); + } + + template <typename T> void OverTable<T>::scope_end() + { + precondition(!oversymtab_.empty()); + oversymtab_.pop_back(); + } + + template <typename T> + std::ostream& OverTable<T>::dump(std::ostream& ostr) const + { + ostr << "<overTable>\n"; + for (const auto& m : oversymtab_ | std::views::reverse) + { + ostr << "<scope>\n"; + for (const auto& im : m) + ostr << im.first << " : " << im->second.size() << '\n'; + ostr << "</scope>\n"; + } + return ostr << "</overTable>\n"; + } + + template <typename T> + std::ostream& operator<<(std::ostream& ostr, const OverTable<T>& tbl) + { + return tbl.dump(ostr); + } + +} // namespace overload diff --git a/tiger-compiler/src/overload/tasks.cc b/tiger-compiler/src/overload/tasks.cc new file mode 100644 index 0000000..9cfde5a --- /dev/null +++ b/tiger-compiler/src/overload/tasks.cc @@ -0,0 +1,38 @@ +/** + ** \file overload/tasks.cc + ** \brief Overload module related tasks' implementation. + */ + +#include <memory> + +#include <ast/tasks.hh> +#include <common.hh> +#include <overload/liboverload.hh> +#define DEFINE_TASKS 1 +#include <overload/tasks.hh> +#undef DEFINE_TASKS + +namespace overload::tasks +{ + std::unique_ptr<overfun_bindings_type> the_overfun_bindings = nullptr; + + void overfun_bindings_compute() + { + auto result = ::overload::bind(*ast::tasks::the_program); + ::overload::tasks::the_overfun_bindings = + std::make_unique<overfun_bindings_type>(std::move(result.first)); + + task_error() << result.second << &misc::error::exit_on_error; + } + + void overfun_types_compute() + { + task_error() << ::overload::types_check( + *ast::tasks::the_program, *::overload::tasks::the_overfun_bindings); + + // Force the unique_ptr to be freed. + the_overfun_bindings.reset(); + task_error().exit_on_error(); + } + +} // namespace overload::tasks diff --git a/tiger-compiler/src/overload/tasks.hh b/tiger-compiler/src/overload/tasks.hh new file mode 100644 index 0000000..31edad1 --- /dev/null +++ b/tiger-compiler/src/overload/tasks.hh @@ -0,0 +1,29 @@ +/** + ** \file overload/tasks.hh + ** \brief Overload module tasks. + */ + +#pragma once + +#include <overload/binder.hh> +#include <task/libtask.hh> + +namespace overload::tasks +{ + TASK_GROUP("4.5 Type checking with overloading"); + + /// Compute bindings, allowing function overloading. + TASK_DECLARE("overfun-bindings-compute", + "bind the identifiers, " + "allowing function overloading", + overfun_bindings_compute, + "parse"); + + /// Check for type violation, allowing function overloading. + TASK_DECLARE("O|overfun-types-compute", + "check for type violations, " + "allowing function overloading", + overfun_types_compute, + "overfun-bindings-compute"); + +} // namespace overload::tasks diff --git a/tiger-compiler/src/overload/type-checker.cc b/tiger-compiler/src/overload/type-checker.cc new file mode 100644 index 0000000..1f32113 --- /dev/null +++ b/tiger-compiler/src/overload/type-checker.cc @@ -0,0 +1,21 @@ +/** + ** \file overload/type-checker.cc + ** \brief Implementation for overload/type-checker.hh. + */ + +#include <algorithm> +#include <sstream> + +#include <misc/indent.hh> +#include <overload/type-checker.hh> +#include <type/types.hh> + +namespace overload +{ + TypeChecker::TypeChecker(const overfun_bindings_type& overfun_bindings) + : overfun_bindings_{overfun_bindings} + {} + + // FIXME: Some code was deleted here. + +} // namespace overload diff --git a/tiger-compiler/src/overload/type-checker.hh b/tiger-compiler/src/overload/type-checker.hh new file mode 100644 index 0000000..719db72 --- /dev/null +++ b/tiger-compiler/src/overload/type-checker.hh @@ -0,0 +1,37 @@ +/** + ** \file overload/type-checker.hh + ** \brief Checking/translating an OverTiger program in a Tiger program. + */ + +#pragma once + +#include <overload/binder.hh> +#include <type/type-checker.hh> +#include <type/types.hh> + +namespace overload +{ + /** \brief Perform type checking, allowing function overload, and compute + ** the bindings of the functions. + ** + ** Inheritance is declared virtual to enable diamond inheritance with + ** the combine::TypeChecker (src/combine/type-checker.hh), inheriting + ** from overload::TypeChecker and object::TypeChecker, both inheriting from + ** type::TypeChecker. + **/ + class TypeChecker : virtual public type::TypeChecker + { + public: + /// Superclass. + using super_type = type::TypeChecker; + using super_type::operator(); + + TypeChecker(const overfun_bindings_type& overfun_bindings); + virtual ~TypeChecker() = default; + + // FIXME: Some code was deleted here. + private: + const overfun_bindings_type& overfun_bindings_; + }; + +} // namespace overload diff --git a/tiger-compiler/src/parse/fwd.hh b/tiger-compiler/src/parse/fwd.hh new file mode 100644 index 0000000..19ed898 --- /dev/null +++ b/tiger-compiler/src/parse/fwd.hh @@ -0,0 +1,38 @@ +/** + ** \file parse/fwd.hh + ** \brief Forward declarations for the parse module. + */ + +#pragma once + +#include <misc/variant.hh> + +// From ast/. +namespace ast +{ + class Exp; + class ChunkList; + +} // namespace ast + +namespace parse +{ + // From scantiger.hh. + class Lexer; + + // From parsetiger.yy. + class parser; + + // From location.hh. + class location; + + // From tiger-driver.hh. + class TigerDriver; + + // From tweast.hh + class Tweast; + + /// Result of a parse: an Exp (*.tig) or a ChunkList (*.tih). + using ast_type = misc::variant<ast::Exp*, ast::ChunkList*>; + +} // namespace parse diff --git a/tiger-compiler/src/parse/generate-prelude.sh b/tiger-compiler/src/parse/generate-prelude.sh new file mode 100755 index 0000000..d81d28b --- /dev/null +++ b/tiger-compiler/src/parse/generate-prelude.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +[ -z "$1" ] && echo "$0: Missing input file" && exit +[ -z "$2" ] && echo "$0: Missing output file" && exit + +input=$1 +output=$2 + +awk 'BEGIN { + print("#include \"parse/tiger-driver.hh\""); + print(""); + print("namespace parse"); + print("{"); + print(" const char* TigerDriver::prelude () const"); + print(" {"); + print(" return"); + printf("R\"("); + } + /^#(<<|>>)/ { + next; + } + { + print($0); + } + END { + print(")\";"); + print(" }"); + print("} // namespace parse"); + }' "$input" > "$output".tmp + +mv "$output".tmp "$output" diff --git a/tiger-compiler/src/parse/libparse.cc b/tiger-compiler/src/parse/libparse.cc new file mode 100644 index 0000000..7c963b6 --- /dev/null +++ b/tiger-compiler/src/parse/libparse.cc @@ -0,0 +1,137 @@ +/** + ** \file parse/libparse.cc + ** \brief Functions and variables exported by the parse module. + */ + +#include <ast/chunk-interface.hh> +#include <ast/chunk-list.hh> +#include <misc/file-library.hh> +#include <misc/indent.hh> +#include <misc/symbol.hh> +#include <parse/libparse.hh> +#include <parse/location.hh> +#include <parse/tasks.hh> +#include <parse/tiger-driver.hh> +#include <parse/tweast.hh> + +// Define exported parse functions. +namespace parse +{ + ast::ChunkList* parse_prelude() + { + if (tasks::prelude.empty()) + { + return nullptr; + } + + TigerDriver td; + + return std::get<ast::ChunkList*>(td.parse(td.prelude())); + } + + // Parse a Tiger file, return the corresponding abstract syntax. + std::pair<ast::ChunkList*, misc::error> parse(const std::string& prelude, + const std::string& fname, + misc::file_library& library, + bool scan_trace_p, + bool parse_trace_p, + bool enable_object_extensions_p, + bool enable_assert_extensions_p) + { + // Current directory must be that of the file currently processed. + library.push_current_directory(misc::path(fname).parent_path()); + + TigerDriver td(library); + td.scan_trace(scan_trace_p).parse_trace(parse_trace_p); + td.enable_object_extensions(enable_object_extensions_p); + td.enable_assert_extensions(enable_assert_extensions_p); + + ast::ChunkList* res = nullptr; + + ast_type tree = td.parse_file(fname); + + ast::Exp** exp = std::get_if<ast::Exp*>(&tree); + ast::ChunkList** chunks = std::get_if<ast::ChunkList*>(&tree); + + // Try to parse the program as an expression, and check that the + // parsing did not fail in that case. + if (exp && *exp) + { + Tweast in; + + if (!prelude.empty()) + { + ast::ChunkList* prelude_chunks = + (prelude == "builtin" + ? std::get<ast::ChunkList*>(td.parse(td.prelude())) + : td.parse_import(prelude, location())); + if (prelude_chunks) + in << prelude_chunks; + } + in << "function _main() = (" << *exp << "; ())"; + res = td.parse(in); + } + // Try to parse the program as a list of declarations, and check + // that the parsing did not fail in that case. + else if (chunks && *chunks) + { + // Try to parse the program as a list of declarations. + res = *chunks; + } + // Otherwise, the parsing failed, and an error will be returned as + // second member of the return value. + + // Ensure that directory stack is not modified by parse. + library.pop_current_directory(); + + return std::pair(res, td.error_get()); + } + + ast_type parse(Tweast& input) + { + TigerDriver td; + td.enable_extensions(); + td.enable_object_extensions(); + ast_type res = td.parse(input); + if (td.error_get()) + { + misc::error e; + e << "Failed to resolve Tweast:" << misc::incendl << input; + e << "Got: " << td.error_get(); + e.ice_here(); + } + return res; + } + + ast::Exp* parse(const std::string& str, bool enable_object_extensions_p) + { + TigerDriver td; + td.enable_object_extensions(enable_object_extensions_p); + ast::Exp* res = td.parse(str); + td.error_get().ice_on_error_here(); + return res; + } + + ast::ChunkList* parse_unit(const std::string& str, + bool enable_object_extensions_p) + { + TigerDriver td; + td.enable_object_extensions(enable_object_extensions_p); + std::string rewrite = "function _main() = (" + str + "; ())"; + ast::ChunkList* res = td.parse(rewrite); + td.error_get().ice_on_error_here(); + return res; + } + + // Parse a set of declarations. + ast::ChunkInterface* parse_chunks(Tweast& in) + { + ast::ChunkList* dl = parse(in); + assertion(dl->chunks_get().size() == 1); + ast::ChunkInterface* res = dl->chunks_get().front(); + dl->chunks_get().pop_front(); + delete dl; + return res; + } + +} // namespace parse diff --git a/tiger-compiler/src/parse/libparse.hh b/tiger-compiler/src/parse/libparse.hh new file mode 100644 index 0000000..7781bb6 --- /dev/null +++ b/tiger-compiler/src/parse/libparse.hh @@ -0,0 +1,76 @@ +/** + ** \file parse/libparse.hh + ** \brief Declare functions and variables exported by parse module. + */ + +#pragma once + +#include <set> +#include <string> +#include <utility> + +#include <ast/fwd.hh> +#include <misc/error.hh> +#include <misc/file-library.hh> +#include <parse/fwd.hh> + +/// Parsing the input, delivering an ast::Ast. +namespace parse +{ + /// \brief Parse a Tiger file, return the corresponding abstract syntax tree. + /// + /// \param prelude name of the prelude file. + /// \param fname path and name of the tiger file. + /// \param library library for managing search path. + /// \param scan_trace_p display information on scan step. + /// \param parse_trace_p display information on parse step. + /// \param enable_object_extensions_p enable object constructions + /// + /// \return a pair composed of a pointer to an abstract parse tree + /// (set to `nullptr' upon failure) and an error status. + /// This function is the only interface available between + /// the scanner/parser and the rest of the program. + +#ifdef SWIG + %newobject parse; +#endif + + /** + * \brief Parse the default prelude and return it. + * Return null if no-prelude is set. + * \return an AST representing the EPITA Tiger default prelude + */ + ast::ChunkList* parse_prelude(); + + std::pair<ast::ChunkList*, misc::error> + parse(const std::string& prelude, + const std::string& fname, + misc::file_library& library, + bool scan_trace_p, + bool parse_trace_p, + bool enable_object_extensions_p = false, + bool enable_assert_extensions_p = false); + + /// \brief Parse a Tweast. + /// + /// Extensions are enabled. Raises an exception on errors. + ast_type parse(Tweast& input); + + /// Parse a std::string. Used for unit tests. + ast::Exp* parse(const std::string& str, + bool enable_object_extensions_p = false); + + /// Parse a std::string. Used for unit tests. + /// The declaration of the _main function is automatically added. + ast::ChunkList* parse_unit(const std::string& str, + bool enable_object_extensions_p = false); + + /// \brief Parse a set of declarations. + /// + /// Wrapper around parse::parse to return the single ast::ChunkInterface + /// to be found in the input (expected to contain ChunkList). + /// + /// Used by desugar::BoundsCheckingVisitor and object::ObjectDesugarVisitor. + ast::ChunkInterface* parse_chunks(Tweast& in); + +} // namespace parse diff --git a/tiger-compiler/src/parse/local.am b/tiger-compiler/src/parse/local.am new file mode 100644 index 0000000..1d80d2a --- /dev/null +++ b/tiger-compiler/src/parse/local.am @@ -0,0 +1,125 @@ +## -------------------- ## +## Scanner generation. ## +## -------------------- ## + +# Definition of flags used by reflex +REFLEX_HEADER = %D%/scantiger.hh +REFLEX_FLAGS = -d --flex --header-file=$(REFLEX_HEADER) +SOURCES_REFLEX = $(REFLEX_HEADER) %D%/scantiger.cc + +MAINTAINERCLEANFILES += %D%/scantiger.cc $(REFLEX_HEADER) +EXTRA_DIST += %D%/scantiger.ll + +# Rule to generate all files with reflex +%D%/scantiger.cc $(REFLEX_HEADER) : %D%/scantiger.ll + $(AM_V_GEN)mkdir -p $(@D) + $(AM_V_at)rm -f $@ +# Generate scantiger.cc and scantiger.hh + $(AM_V_at) $(REFLEX) $< -o %D%/scantiger.cc $(REFLEX_FLAGS) + +## ------------------- ## +## Parser generation. ## +## ------------------- ## +BISONXX = $(top_builddir)/build-aux/bin/bison++ +BISONXX_IN = $(top_srcdir)/build-aux/bin/bison++.in +BISONXXFLAGS = \ + $(if $(V:0=),--verbose) +AM_BISONFLAGS = \ + --warnings=all,dangling-alias \ + --report=all + +## Use this additional bison flag to display counterexamples in the tty. +#AM_BISONFLAGS += --warnings=counterexamples + +# We do not use Automake features here. +SOURCES_PARSETIGER_YY = \ + %D%/location.hh \ + %D%/parsetiger.cc \ + %D%/parsetiger.hh +BUILT_SOURCES += $(SOURCES_PARSETIGER_YY) + +# Ship %D%/stack.hh only if GLR is disabled, as Bison does not +# generate this file for GLR parsers. +dist-hook: dist-hook-parse +dist-hook-parse: + @grep '%glr-parser' $(srcdir)/%D%/parsetiger.yy >/dev/null \ + || cp -p $(srcdir)/%D%/stack.hh $(distdir)/src/parse/ + +# Compile the parser and save cycles. +# This code comes from "Handling Tools that Produce Many Outputs", +# from the Automake documentation. +EXTRA_DIST += \ + %D%/parsetiger.stamp \ + %D%/parsetiger.yy +# The dependency is on bison++.in and not bison++, since bison++ is +# regenerated at distribution time, and voids the time stamps (which +# we don't want!). +%D%/parsetiger.stamp: %D%/parsetiger.yy $(BISONXX_IN) + $(AM_V_GEN)mkdir -p $(@D) + $(AM_V_at)rm -f $@ $@.tmp + $(AM_V_at)echo '$@ rebuilt because of: $?' >$@.tmp + $(AM_V_at)$(MAKE) $(BISONXX) + $(AM_V_at)$(BISONXX) $(BISONXXFLAGS) \ + -r $(srcdir)/src \ + -- \ + $< $(srcdir)/%D%/parsetiger.cc \ + $(AM_BISONFLAGS) $(BISONFLAGS) + $(AM_V_at)mv -f $@.tmp $@ + +## If Make does not know it will generate in the srcdir, then when +## trying to compile from *.cc to *.lo, it will not apply VPATH +## lookup, since it expects the file to be in builddir. So *here*, +## make srcdir explicit. +$(addprefix $(srcdir)/, $(SOURCES_PARSETIGER_YY)): %D%/parsetiger.stamp + $(AM_V_GEN)if test -f $@; then :; else \ + rm -f $<; \ + $(MAKE) $(AM_MAKEFLAGS) $<; \ + fi + +# We tried several times to run make from ast/ to build location.hh. +# Unfortunately, because of different, but equivalent, paths, BSD Make +# was unable to build them. The following hook is here to address this. +.PHONY: generate-parser +generate-parser: $(SOURCES_PARSETIGER_YY) + +PARSE_PRELUDE_GENERATION = %D%/generate-prelude.sh +EXTRA_DIST += $(PARSE_PRELUDE_GENERATION) +CLEANFILES += %D%/prelude.cc +%D%/prelude.cc: $(top_srcdir)/data/prelude.tih + $(AM_V_GEN)$(srcdir)/$(PARSE_PRELUDE_GENERATION) $< $@ + +## ---------- ## +## libparse. ## +## ---------- ## + +src_libtc_la_SOURCES += \ + $(SOURCES_REFLEX) \ + $(SOURCES_PARSETIGER_YY) \ + %D%/fwd.hh \ + %D%/libparse.hh %D%/libparse.cc \ + %D%/metavar-map.hh %D%/metavar-map.hxx \ + %D%/tiger-driver.hh %D%/tiger-driver.cc \ + %D%/tweast.hh %D%/tweast.cc %D%/tweast.hxx +src_libtc_la_SOURCES += \ + %D%/tiger-factory.hh %D%/tiger-factory.hxx + +FORMAT_IGNORE += $REFLEX_HEADER + +nodist_src_libtc_la_SOURCES += \ + %D%/prelude.cc + +## ------- ## +## Tests. ## +## ------- ## + +check_PROGRAMS += \ + %D%/test-parse \ + %D%/test-tweast + +# Find the prelude. +%C%_test_parse_CPPFLAGS = $(AM_CPPFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" +%C%_test_parse_LDADD = src/libtc.la +%C%_test_tweast_LDADD = src/libtc.la + + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/parse/metavar-map.hh b/tiger-compiler/src/parse/metavar-map.hh new file mode 100644 index 0000000..113db0f --- /dev/null +++ b/tiger-compiler/src/parse/metavar-map.hh @@ -0,0 +1,48 @@ +/** + ** \file parse/metavar-map.hh + ** \brief Declaration of parse::MetavarMap. + */ + +#pragma once + +#include <string> + +#include <misc/map.hh> + +namespace parse +{ + /// A generic map of metavariables. + template <typename Data> class MetavarMap + { + public: + /// Build a map of metavariables of kind \a name. + MetavarMap(const std::string& name); + virtual ~MetavarMap(); + + /// Generate a (concrete syntax) Tiger statement for metavariable + /// number \a key (of kind \p Data). + std::string show(unsigned key) const; + + /// Print the MetavarMap on \a ostr. + std::ostream& dump(std::ostream& ostr) const; + + protected: + /// Append a metavariable to the map. + virtual std::string append_(unsigned& key, Data* data); + /// Extract a metavariable. + virtual Data* take_(unsigned key); + + /// Name of the kind of variable. + const std::string name_; + /// Metavariables. + using map_type = misc::map<unsigned, Data*>; + map_type map_; + }; + + /// Output \a m onto \a ostr. + template <typename Data> + std::ostream& operator<<(std::ostream& ostr, const MetavarMap<Data>& m); + +} // namespace parse + +#include <parse/metavar-map.hxx> diff --git a/tiger-compiler/src/parse/metavar-map.hxx b/tiger-compiler/src/parse/metavar-map.hxx new file mode 100644 index 0000000..7dc2bbc --- /dev/null +++ b/tiger-compiler/src/parse/metavar-map.hxx @@ -0,0 +1,69 @@ +/** + ** \file parse/metavar-map.hxx + ** \brief Implementation of parse::MetavarMap. + */ + +#pragma once + +#include <sstream> +#include <string> + +#include <misc/contract.hh> +#include <misc/indent.hh> +#include <parse/metavar-map.hh> + +namespace parse +{ + template <typename Data> + MetavarMap<Data>::MetavarMap(const std::string& name) + : name_(name) + , map_() + {} + + template <typename Data> MetavarMap<Data>::~MetavarMap() + { + // At this point, every metavariable should have been taken from the map. + assertion(map_.empty()) << *this << "not empty, aborting.\n"; + } + + template <typename Data> + std::string MetavarMap<Data>::show(unsigned key) const + { + return '_' + name_ + '(' + std::to_string(key) + ')'; + } + + template <typename Data> + std::ostream& MetavarMap<Data>::dump(std::ostream& ostr) const + { + ostr << "MetavarMap<" << name_ << "> = ["; + + if (map_.empty()) + return ostr << " ]" << misc::iendl; + + ostr << misc::incindent; + + for (const auto& [k, v] : map_) + ostr << misc::iendl << show(k) << " -> " << v; + + return ostr << misc::decendl << "]" << misc::iendl; + } + + template <typename Data> + std::string MetavarMap<Data>::append_(unsigned& count, Data* data) + { + map_[count] = data; + return show(count++); + } + + template <typename Data> Data* MetavarMap<Data>::take_(unsigned key) + { + return map_.take(key); + } + + template <typename Data> + std::ostream& operator<<(std::ostream& ostr, const MetavarMap<Data>& m) + { + return m.dump(ostr); + } + +} // namespace parse diff --git a/tiger-compiler/src/parse/tasks.cc b/tiger-compiler/src/parse/tasks.cc new file mode 100644 index 0000000..1423696 --- /dev/null +++ b/tiger-compiler/src/parse/tasks.cc @@ -0,0 +1,53 @@ +/** + ** \file parse/tasks.cc + ** \brief Parse module related tasks' implementation. + **/ + +#include <cstdlib> +#include <iostream> +#include <memory> + +#include <assert/tasks.hh> +#include <ast/tasks.hh> +#include <common.hh> +#include <misc/file-library.hh> +#include <object/tasks.hh> +#include <parse/libparse.hh> +#define DEFINE_TASKS 1 +#include <parse/tasks.hh> +#undef DEFINE_TASKS + +namespace parse::tasks +{ + const char* tc_pkgdatadir = getenv("TC_PKGDATADIR"); + misc::file_library l = + misc::file_library(tc_pkgdatadir ? tc_pkgdatadir : PKGDATADIR); + + void no_prelude() { prelude = ""; } + + void parse() + { + precondition(filename != nullptr); + bool scan_trace = scan_trace_p || getenv("SCAN"); + bool parse_trace = parse_trace_p || getenv("PARSE"); + std::pair<ast::ChunkList*, misc::error> result = + ::parse::parse(prelude, filename, l, scan_trace, parse_trace, + object::tasks::enable_object_extensions_p, + assert::tasks::enable_assert_extensions_p); + + // If the parsing completely failed, stop. + task_error() << result.second; + if (!result.first) + task_error().exit(); + + // FIXME DONE: Some code was deleted here (Set `the_program' to the result of parsing). + ast::tasks::the_program = std::unique_ptr<ast::ChunkList>{result.first}; + } + + void library_display() { std::cout << l << '\n'; } + + void library_append(const std::string& dir) { l.append_dir(dir); } + + void library_prepend(const std::string& dir) { l.prepend_dir(dir); } + +} // namespace parse::tasks diff --git a/tiger-compiler/src/parse/tasks.hh b/tiger-compiler/src/parse/tasks.hh new file mode 100644 index 0000000..0ff37e1 --- /dev/null +++ b/tiger-compiler/src/parse/tasks.hh @@ -0,0 +1,51 @@ +/** + ** \file parse/tasks.hh + ** \brief Parse module tasks. + */ + +#pragma once + +#include <misc/fwd.hh> +#include <task/libtask.hh> + +/// Tasks of the parse module. +namespace parse::tasks +{ + /// Global library for search path. + extern misc::file_library file_library; + + TASK_GROUP("1. Parsing"); + + /// Enable scanner trace. + BOOLEAN_TASK_DECLARE("scan-trace", "trace the scanning", scan_trace_p, ""); + /// Enable parser trace. + BOOLEAN_TASK_DECLARE("parse-trace", "trace the parse", parse_trace_p, ""); + /// Prelude declarations. + STRING_TASK_DECLARE("prelude", + "builtin", + "name of the prelude. Defaults to \"builtin\" " + "denoting the builtin prelude", + prelude, + ""); + /// Prelude declarations. + TASK_DECLARE("X|no-prelude", "don't include prelude", no_prelude, ""); + /// Parse the input file, store the ast into ast::tasks::the_program. + TASK_DECLARE("parse", "parse a file", parse, ""); + + /// Display library search path. + TASK_DECLARE("library-display", + "display library search path", + library_display, + ""); + /// Append directory DIR to the search path. + MULTIPLE_STRING_TASK_DECLARE("P|library-append", + "append directory DIR to the search path", + library_append, + ""); + /// Prepend directory DIR to the search path. + MULTIPLE_STRING_TASK_DECLARE("p|library-prepend", + "prepend directory DIR to the search path", + library_prepend, + ""); + +} // namespace parse::tasks diff --git a/tiger-compiler/src/parse/tiger-driver.cc b/tiger-compiler/src/parse/tiger-driver.cc new file mode 100644 index 0000000..d26d11d --- /dev/null +++ b/tiger-compiler/src/parse/tiger-driver.cc @@ -0,0 +1,215 @@ +/** + ** \file parse/tiger-driver.cc + ** \brief Implementation of parse::TigerDriver. + */ + +#include <cstdlib> +#include <fstream> + +#include <parse/parsetiger.hh> +#include <parse/scantiger.hh> +#include <parse/tiger-driver.hh> + +namespace parse +{ + TigerDriver::TigerDriver(const misc::file_library& lib) + : library_(lib) + {} + + // Yes, this is exactly the default dtor. But because it is defined + // here, in the *.cc file, instead of "= default" in the *.hh file, + // there is no need for all the members to be complete defined in + // the header. In particular, the scanner_ member acts as a Pimpl, + // so we really want the generation of the dtor to be delayed to + // here, not in the header. + TigerDriver::~TigerDriver() = default; + + /// Set the scanner traces. + TigerDriver& TigerDriver::scan_trace(bool b) + { + scan_trace_p_ = b; + return *this; + } + + /// Set the parser traces. + TigerDriver& TigerDriver::parse_trace(bool b) + { + parse_trace_p_ = b; + return *this; + } + + /// Enable object extensions. + TigerDriver& TigerDriver::enable_object_extensions(bool b) + { + enable_object_extensions_p_ = b; + return *this; + } + + /// Enable assert extensions. + TigerDriver& TigerDriver::enable_assert_extensions(bool b) + { + enable_assert_extensions_p_ = b; + return *this; + } + + /// Enable syntax extensions. + TigerDriver& TigerDriver::enable_extensions(bool b) + { + enable_extensions_p_ = b; + return *this; + } + + /// Parse a Tiger file or string. + ast_type TigerDriver::parse_() + { + std::string* fn = std::get_if<std::string>(&input_); + + /* The (address of) the string behind the symbol FILENAME is + guaranteed to remain valid even after the symbol has been + destroyed, so we can safely pass `&filename.name_get()' to + `location_.initialize()'. As for other symbols, the + corresponding string will be deallocated at the end of the + program. */ + misc::symbol filename(fn == nullptr ? "" + : *fn == "-" ? "standard input" + : *fn); + location_.initialize(&filename.get()); + + std::shared_ptr<std::istream> in; + if (fn == nullptr) + // Parse a Tweast. + in = (std::make_shared<std::stringstream>( + std::get<Tweast*>(input_)->input_get())); + else if (*fn == "-") + // Parse from the standard input. + in.reset(&std::cin, [](...) {}); + else + { + // Parse from a file. + in = std::make_shared<std::ifstream>(*fn); + if (in->fail()) + error_ << misc::error::error_type::failure << program_name + << ": cannot open `" << filename << "': " << strerror(errno) + << std::endl + << &misc::error::exit; + } + + // FIXME DONE: Some code was deleted here (Initialize Lexer and enable scan traces). + Lexer lexer(*in); + lexer.set_debug(scan_trace_p_); + + // FIXME DONE: Some code was deleted here (Initialize the parser and enable parse traces). + parser parser(*this, lexer); + parser.set_debug_level(parse_trace_p_); + + // FIXME DONE: Some code was deleted here (Run the parser). + parser.parse(); + + ast_type res = ast_; + ast_ = static_cast<ast::Exp*>(nullptr); + + return res; + } + + /*---------------. + | Parse a file. | + `---------------*/ + + ast_type TigerDriver::parse_file(const misc::path& name) + { + if (parse_trace_p_) + std::cerr << "Parsing file: " << name << '\n'; + input_ = name.string(); + return parse_(); + } + + /*----------------. + | Parse a Tweast. | + `----------------*/ + + ast_type TigerDriver::parse_input(Tweast& s, bool extensions) + { + std::swap(extensions, enable_extensions_p_); + // Merge all aggregated Tweasts into a single one. + s.flatten(); + if (parse_trace_p_) + std::cerr << "Parsing string: " << s.input_get() << '\n'; + input_ = &s; + ast_type res = parse_(); + std::swap(extensions, enable_extensions_p_); + return res; + } + + ast_type TigerDriver::parse(Tweast& s) { return parse_input(s, true); } + + /*-----------------. + | Parse a string. | + `-----------------*/ + + ast_type TigerDriver::parse(const std::string& s) + { + Tweast in(s); + return parse_input(in, false); + } + + /*-----------------------. + | Parse a Tiger import. | + `-----------------------*/ + + ast::ChunkList* TigerDriver::parse_import(const std::string& name, + const location& loc) + { + // Try to find directory containing the file to import. + misc::path directory_path = library_.find_file(name); + + if (directory_path.empty()) + { + error_ << misc::error::error_type::failure << loc << ": " << name + << ": file not found.\n"; + return nullptr; + } + + // Complete path of file (directory + filename). + misc::path absolute_path = + directory_path / misc::path(misc::path(name).filename()); + + // Detect recursive inclusion. + if (open_files_.find(absolute_path) != open_files_.end()) + { + error_ << misc::error::error_type::failure << loc << ": " << name + << ": recursive inclusion.\n" + << open_files_[absolute_path] + << ": initial inclusion was here.\n"; + return nullptr; + } + + library_.push_current_directory(directory_path); + open_files_[absolute_path] = loc; + // Save the inputs, and reset them. + input_type saved_input = input_; + location saved_location = location_; + // Parse the imported file. + ast::ChunkList* res = nullptr; + try + { + res = parse_file(absolute_path); + } + catch (const std::bad_variant_access& e) + { + error_ << misc::error::error_type::parse << absolute_path + << ": imported from " << loc + << ": syntax error, unexpected exp, expecting chunks.\n"; + error_.exit(); + } + // Set the inputs back to their original values. + input_ = saved_input; + location_ = saved_location; + + open_files_.erase(absolute_path); + library_.pop_current_directory(); + return res; + } + + const misc::error& TigerDriver::error_get() const { return error_; } + +} // namespace parse diff --git a/tiger-compiler/src/parse/tiger-driver.hh b/tiger-compiler/src/parse/tiger-driver.hh new file mode 100644 index 0000000..266f8c2 --- /dev/null +++ b/tiger-compiler/src/parse/tiger-driver.hh @@ -0,0 +1,110 @@ +/** + ** \file parse/tiger-driver.hh + ** \brief Declaration of parse::TigerDriver. + */ + +#pragma once + +#include <map> +#include <memory> +#include <stack> + +#include <ast/fwd.hh> +#include <common.hh> +#include <misc/error.hh> +#include <misc/file-library.hh> +#include <parse/parsetiger.hh> +#include <parse/tweast.hh> + +namespace parse +{ + /// Conduct the scanner and the parser. + class TigerDriver + { + public: + /// The parsed object is either a file, represented by the filename + /// or a Tweast. + using input_type = misc::variant<std::string, Tweast*>; + + TigerDriver(const misc::file_library& lib = misc::file_library()); + ~TigerDriver(); + + friend class parser; + /// Parse the Tiger file \a name. + ast_type parse_file(const misc::path& name); + /// Parse the Tweast \a s. Extensions are enabled. + ast_type parse(Tweast& s); + /// Parse the string \a s. Extensions are automatically disabled. + ast_type parse(const std::string& s); + + /// Parse a Tiger prelude, return the list of chunk. + ast::ChunkList* parse_import(const std::string& name, const location& loc); + + /// Return the error status of the parsing. + const misc::error& error_get() const; + + /// The default prelude. + const char* prelude() const; + + /// Set the scanner traces. + TigerDriver& scan_trace(bool b = true); + + /// Set the parser traces. + TigerDriver& parse_trace(bool b = true); + + /// Enable object extensions. + TigerDriver& enable_object_extensions(bool b = true); + + /// Enable object extensions. + TigerDriver& enable_assert_extensions(bool b = true); + + /// Enable syntax extensions. + TigerDriver& enable_extensions(bool b = true); + + private: + /// \name Handling the scanner. + /// \{ + /// Verbose scanning? + bool scan_trace_p_ = getenv("SCAN"); + + /// The list of open files, and the location of their request. + std::map<misc::path, location> open_files_; + /// \} + + /// \name Running the parse. + /// \{ + /// Parse a Tiger program, return the AST. + ast_type parse_(); + + /// Parse a Tweast. \a extensions temporarily enable or disable + /// extensions for the string parsing. This method is used to factor + /// code between parse(Tweast) and parse(const std::string)) + ast_type parse_input(Tweast& input, bool extensions); + + /// Parse a Tiger prelude \a f, return the list of chunk. + ast::ChunkList* parse_prelude(const std::string& f); + + public: + /// The result of the parse. + ast_type ast_; + /// Parsing errors handler. + misc::error error_; + /// Verbose parsing? + bool parse_trace_p_ = getenv("PARSE"); + /// \} + + /// The location requesting the import. + location location_; + /// The source to parse. + input_type input_; + /// The file library for imports. + misc::file_library library_; + /// Allow object extensions? + bool enable_object_extensions_p_ = false; + /// Allow assert extensions? + bool enable_assert_extensions_p_ = false; + /// Allow language extensions (reserved identifiers, new keywords)? + bool enable_extensions_p_ = false; + }; + +} // namespace parse diff --git a/tiger-compiler/src/parse/tiger-factory.hh b/tiger-compiler/src/parse/tiger-factory.hh new file mode 100644 index 0000000..9165bb6 --- /dev/null +++ b/tiger-compiler/src/parse/tiger-factory.hh @@ -0,0 +1,144 @@ +/** + ** \file parse/tiger-factory.hh + ** \brief Declaration of parse::TigerFactory. + */ + +#pragma once + +#include <ast/all.hh> +#include <ast/fwd.hh> +#include <parse/location.hh> + +namespace parse +{ + ast::IntExp* make_IntExp(const location& location, int num); + + ast::StringExp* make_StringExp(const location& location, std::string string); + + ast::ObjectExp* make_ObjectExp(const location& location, + ast::NameTy* type_name); + + ast::CallExp* make_CallExp(const location& location, + misc::symbol name, + ast::exps_type* args); + + ast::MethodCallExp* make_MethodCallExp(const location& location, + misc::symbol name, + ast::exps_type* args, + ast::Var* object); + + ast::RecordExp* make_RecordExp(const location& location, + ast::NameTy* type_name, + ast::fieldinits_type* fields); + + ast::ArrayExp* make_ArrayExp(const location& location, + ast::NameTy* type_name, + ast::Exp* size, + ast::Exp* init); + + ast::NilExp* make_NilExp(const location& location); + + ast::SeqExp* make_SeqExp(const location& location, ast::exps_type* exps); + + ast::AssignExp* + make_AssignExp(const location& location, ast::Var* var, ast::Exp* exp); + + ast::IfExp* make_IfExp(const location& location, + ast::Exp* test, + ast::Exp* thenclause, + ast::Exp* elseclause); + + ast::IfExp* + make_IfExp(const location& location, ast::Exp* test, ast::Exp* thenclause); + + ast::WhileExp* + make_WhileExp(const location& location, ast::Exp* test, ast::Exp* body); + + ast::ForExp* make_ForExp(const location& location, + ast::VarDec* vardec, + ast::Exp* hi, + ast::Exp* body); + + ast::BreakExp* make_BreakExp(const location& location); + + ast::LetExp* + make_LetExp(const location& location, ast::ChunkList* decs, ast::Exp* body); + + ast::OpExp* make_OpExp(const location& location, + ast::Exp* left, + ast::OpExp::Oper oper, + ast::Exp* right); + + ast::CastExp* + make_CastExp(const location& location, ast::Exp* exp, ast::Ty* ty); + + ast::SimpleVar* make_SimpleVar(const location& location, misc::symbol name); + + ast::FieldVar* + make_FieldVar(const location& location, ast::Var* var, misc::symbol name); + + ast::SubscriptVar* + make_SubscriptVar(const location& location, ast::Var* var, ast::Exp* index); + + /* Use expansion parameter pack to handle when we have one or empty arguments */ + template <class... T> ast::exps_type* make_exps_type(T... exps); + + ast::ChunkList* make_ChunkList(const location& location); + + ast::TypeChunk* make_TypeChunk(const location& location); + + ast::TypeDec* + make_TypeDec(const location& location, misc::symbol name, ast::Ty* ty); + + ast::RecordTy* make_RecordTy(const location& location, + ast::fields_type* fields); + + ast::ArrayTy* make_ArrayTy(const location& location, ast::NameTy* base_type); + + template <class... T> ast::fields_type* make_fields_type(T... types); + + ast::Field* make_Field(const location& location, + misc::symbol name, + ast::NameTy* type_name); + + ast::NameTy* make_NameTy(const location& location, misc::symbol name); + + template <class... T> + ast::fieldinits_type* make_fieldinits_type(T... inits_types); + + ast::FieldInit* + make_FieldInit(const location& location, misc::symbol name, ast::Exp* init); + + ast::ClassTy* make_ClassTy(const location& location, + ast::NameTy* super, + ast::ChunkList* decs); + + ast::VarChunk* make_VarChunk(const location& location); + + ast::VarDec* make_VarDec(const location& location, + misc::symbol name, + ast::NameTy* type_name, + ast::Exp* init); + + ast::MethodChunk* make_MethodChunk(const location& location); + + ast::MethodDec* make_MethodDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body); + + ast::FunctionDec* make_FunctionDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body); + + template <class... T> ast::FunctionChunk* make_FunctionChunk(T... args); + + // For the custom tiger extension + ast::AssertExp* make_AssertExp(const location& location, + ast::Exp* condition); +} // namespace parse + +#include <parse/tiger-factory.hxx> diff --git a/tiger-compiler/src/parse/tiger-factory.hxx b/tiger-compiler/src/parse/tiger-factory.hxx new file mode 100644 index 0000000..39d8531 --- /dev/null +++ b/tiger-compiler/src/parse/tiger-factory.hxx @@ -0,0 +1,272 @@ +#pragma once +#include <parse/tiger-factory.hh> + +namespace parse +{ + inline ast::IntExp* make_IntExp(const location& location, int num) + { + return new ast::IntExp(location, num); + } + + inline ast::StringExp* make_StringExp(const location& location, + std::string string) + { + // FIXME DONE: Some code was deleted here (Constructor of StringExp). + return new ast::StringExp(location, string); + } + + inline ast::ObjectExp* make_ObjectExp(const location& location, + ast::NameTy* type_name) + { + // FIXME DONE: Some code was deleted here (Constructor of Object). + return new ast::ObjectExp(location, type_name); + } + + inline ast::CallExp* make_CallExp(const location& location, + misc::symbol name, + ast::exps_type* args) + { + // FIXME DONE: Some code was deleted here (Constructor of CallExp). + return new ast::CallExp(location, name, args); + } + + inline ast::MethodCallExp* make_MethodCallExp(const location& location, + misc::symbol name, + ast::exps_type* args, + ast::Var* object) + { + // FIXME DONE: Some code was deleted here (Constructor of MethodCallExp). + return new ast::MethodCallExp(location, name, args, object); + } + + inline ast::RecordExp* make_RecordExp(const location& location, + ast::NameTy* type_name, + ast::fieldinits_type* fields) + { + // FIXME DONE: Some code was deleted here (Constructor of RecordExp). + return new ast::RecordExp(location, type_name, fields); + } + + inline ast::ArrayExp* make_ArrayExp(const location& location, + ast::NameTy* type_name, + ast::Exp* size, + ast::Exp* init) + { + // FIXME DONE: Some code was deleted here (Constructor of ArrayExp). + return new ast::ArrayExp(location, type_name, size, init); + } + + inline ast::NilExp* make_NilExp(const location& location) + { + return new ast::NilExp(location); + } + + inline ast::SeqExp* make_SeqExp(const location& location, + ast::exps_type* exps) + { + // FIXME DONE: Some code was deleted here (Constructor of SeqExp). + return new ast::SeqExp(location, exps); + } + + inline ast::AssignExp* + make_AssignExp(const location& location, ast::Var* var, ast::Exp* exp) + { + // FIXME DONE: Some code was deleted here (Constructor of AssignExp). + return new ast::AssignExp(location, var, exp); + } + + inline ast::IfExp* make_IfExp(const location& location, + ast::Exp* test, + ast::Exp* thenclause, + ast::Exp* elseclause) + { + // FIXME DONE: Some code was deleted here (Constructor of IfExp). + return new ast::IfExp(location, test, thenclause, elseclause); + } + + inline ast::IfExp* + make_IfExp(const location& location, ast::Exp* test, ast::Exp* thenclause) + { + // FIXME DONE: Some code was deleted here (Constructor of IfExp). + return new ast::IfExp(location, test, thenclause); + } + + inline ast::WhileExp* + make_WhileExp(const location& location, ast::Exp* test, ast::Exp* body) + { + return new ast::WhileExp(location, test, body); + } + + inline ast::ForExp* make_ForExp(const location& location, + ast::VarDec* vardec, + ast::Exp* hi, + ast::Exp* body) + { + return new ast::ForExp(location, vardec, hi, body); + } + + inline ast::BreakExp* make_BreakExp(const location& location) + { + // FIXME DONE: Some code was deleted here (Constructor of BreakExp). + return new ast::BreakExp(location); + } + + inline ast::LetExp* + make_LetExp(const location& location, ast::ChunkList* decs, ast::Exp* body) + { + // FIXME DONE: Some code was deleted here (Constructor of LetExp). + return new ast::LetExp(location, decs, body); + } + + inline ast::OpExp* make_OpExp(const location& location, + ast::Exp* left, + ast::OpExp::Oper oper, + ast::Exp* right) + { + return new ast::OpExp(location, left, oper, right); + } + + inline ast::CastExp* + make_CastExp(const location& location, ast::Exp* exp, ast::Ty* ty) + { + return new ast::CastExp(location, exp, ty); + } + + inline ast::SimpleVar* make_SimpleVar(const location& location, + misc::symbol name) + { + return new ast::SimpleVar(location, name); + } + + inline ast::FieldVar* + make_FieldVar(const location& location, ast::Var* var, misc::symbol name) + { + // FIXME DONE: Some code was deleted here (Constructor of FieldVar). + return new ast::FieldVar(location, var, name); + } + + inline ast::SubscriptVar* + make_SubscriptVar(const location& location, ast::Var* var, ast::Exp* index) + { + return new ast::SubscriptVar(location, var, index); + } + + /* Use expansion parameter pack to handle one or empty arguments */ + template <class... T> inline ast::exps_type* make_exps_type(T... exps) + { + return new ast::exps_type{exps...}; + } + + inline ast::ChunkList* make_ChunkList(const location& location) + { + return new ast::ChunkList(location); + } + + inline ast::TypeChunk* make_TypeChunk(const location& location) + { + return new ast::TypeChunk(location); + } + + inline ast::TypeDec* + make_TypeDec(const location& location, misc::symbol name, ast::Ty* ty) + { + return new ast::TypeDec(location, name, ty); + } + + inline ast::RecordTy* make_RecordTy(const location& location, + ast::fields_type* fields) + { + // FIXME DONE: Some code was deleted here (Constructor of RecordTy). + return new ast::RecordTy(location, fields); + } + + inline ast::ArrayTy* make_ArrayTy(const location& location, + ast::NameTy* base_type) + { + return new ast::ArrayTy(location, base_type); + } + + template <class... T> inline ast::fields_type* make_fields_type(T... types) + { + return new ast::fields_type{types...}; + } + + inline ast::Field* make_Field(const location& location, + misc::symbol name, + ast::NameTy* type_name) + { + return new ast::Field(location, name, type_name); + } + + inline ast::NameTy* make_NameTy(const location& location, misc::symbol name) + { + return new ast::NameTy(location, name); + } + + template <class... T> + inline ast::fieldinits_type* make_fieldinits_type(T... inits_types) + { + return new ast::fieldinits_type{inits_types...}; + } + + inline ast::FieldInit* + make_FieldInit(const location& location, misc::symbol name, ast::Exp* init) + { + return new ast::FieldInit(location, name, init); + } + + inline ast::ClassTy* make_ClassTy(const location& location, + ast::NameTy* super, + ast::ChunkList* decs) + { + return new ast::ClassTy(location, super, decs); + } + + inline ast::VarChunk* make_VarChunk(const location& location) + { + return new ast::VarChunk(location); + } + + inline ast::VarDec* make_VarDec(const location& location, + misc::symbol name, + ast::NameTy* type_name, + ast::Exp* init) + { + return new ast::VarDec(location, name, type_name, init); + } + + inline ast::MethodChunk* make_MethodChunk(const location& location) + { + return new ast::MethodChunk(location); + } + + inline ast::MethodDec* make_MethodDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body) + { + return new ast::MethodDec(location, name, formals, result, body); + } + + inline ast::FunctionDec* make_FunctionDec(const location& location, + misc::symbol name, + ast::VarChunk* formals, + ast::NameTy* result, + ast::Exp* body) + { + return new ast::FunctionDec(location, name, formals, result, body); + } + + template <class... T> inline ast::FunctionChunk* make_FunctionChunk(T... args) + { + return new ast::FunctionChunk(args...); + } + + // For the custom tiger extension + inline ast::AssertExp* make_AssertExp(const location& location, + ast::Exp* condition) + { + return new ast::AssertExp(location, condition); + } +} // namespace parse diff --git a/tiger-compiler/src/parse/tweast.cc b/tiger-compiler/src/parse/tweast.cc new file mode 100644 index 0000000..fbb21ac --- /dev/null +++ b/tiger-compiler/src/parse/tweast.cc @@ -0,0 +1,87 @@ +/** + ** \file parse/tweast.cc + ** \brief Implementation of parse::Tweast. + */ + +#include <sstream> + +#include <boost/algorithm/string/replace.hpp> + +#include <parse/tweast.hh> + +namespace parse +{ + unsigned Tweast::count_ = 0; + + Tweast::Tweast() + : Tweast("") + {} + + Tweast::Tweast(const std::string& str) + : MetavarMap<ast::Exp>::MetavarMap("exp") + , MetavarMap<ast::Var>::MetavarMap("lvalue") + , MetavarMap<ast::NameTy>::MetavarMap("namety") + , MetavarMap<ast::ChunkList>::MetavarMap("chunks") + , MetavarMap<Tweast>::MetavarMap("tweast") + , input_(str) + {} + + void Tweast::flatten() + { + using tweasts_type = MetavarMap<Tweast>; + using tweasts_map_type = tweasts_type::map_type; + + // Recursively flatten each Tweast metavariable. + for (const tweasts_map_type::value_type& t : tweasts_type::map_) + t.second->flatten(); + + // Grab each non-Tweast metavariable of Tweast metavariables, and + // renumber the metavariables. + for (const tweasts_map_type::value_type& t : tweasts_type::map_) + { + unsigned tweast_index = t.first; + Tweast& tweast = *t.second; + + // Tweast input. + std::string tweast_input = tweast.input_get(); + // Move metavariables to THIS and rename them. + move_metavars_<ast::Exp>(tweast, tweast_input); + move_metavars_<ast::Var>(tweast, tweast_input); + move_metavars_<ast::NameTy>(tweast, tweast_input); + move_metavars_<ast::ChunkList>(tweast, tweast_input); + + // Replace the tweast metavariable reference with its input string. + std::string tweast_var = tweasts_type::show(tweast_index); + std::string input = input_.str(); + boost::algorithm::replace_first(input, tweast_var, tweast_input); + input_.str(input); + } + + // Delete aggregated Tweasts, and remove their associated + // metavariable from the map of Tweasts. + for (const tweasts_map_type::value_type& t : tweasts_type::map_) + delete t.second; + tweasts_type::map_.clear(); + } + + std::string Tweast::input_get() const { return input_.str(); } + + std::ostream& Tweast::dump(std::ostream& ostr) const + { + ostr << "input = \"" << input_.str() << "\"" << misc::iendl; + + MetavarMap<ast::Exp>::dump(ostr); + MetavarMap<ast::Var>::dump(ostr); + MetavarMap<ast::NameTy>::dump(ostr); + MetavarMap<ast::ChunkList>::dump(ostr); + MetavarMap<Tweast>::dump(ostr); + + return ostr << misc::decendl; + } + + std::ostream& operator<<(std::ostream& ostr, const Tweast& in) + { + return in.dump(ostr); + } + +} // namespace parse diff --git a/tiger-compiler/src/parse/tweast.hh b/tiger-compiler/src/parse/tweast.hh new file mode 100644 index 0000000..910f694 --- /dev/null +++ b/tiger-compiler/src/parse/tweast.hh @@ -0,0 +1,79 @@ +/** + ** \file parse/tweast.hh + ** \brief Declaration of parse::Tweast. + */ + +#pragma once + +#include <iostream> +#include <map> +#include <sstream> + +#include <ast/fwd.hh> + +#include <misc/map.hh> +#include <misc/symbol.hh> +#include <parse/metavar-map.hh> + +namespace parse +{ + /// \brief TWEAST stands for ``Text With Embedded Abstract Syntax Trees''. + /// + /// Aggregate string to parse and tables of metavariables. + class Tweast + : public MetavarMap<ast::Exp> + , public MetavarMap<ast::Var> + , public MetavarMap<ast::NameTy> + , public MetavarMap<ast::ChunkList> + , public MetavarMap<parse::Tweast> + { + public: + Tweast(); + Tweast(const std::string& str); + + /// \brief Stream manipulator. + /// + /// Append Tiger expressions to the string to parse. + template <typename T> Tweast& operator<<(const T& t); + + /// Metavariables manipulator. + template <typename T> T* take(unsigned s); + + /// Move the contents of all aggregated Tweast metavariables into + /// the current Tweast. + void flatten(); + + /// Get the current input string. + std::string input_get() const; + + /// Print the table + std::ostream& dump(std::ostream& ostr) const; + + protected: + // Insert base class members in the current scope. + using MetavarMap<ast::Exp>::append_; + using MetavarMap<ast::Var>::append_; + using MetavarMap<ast::NameTy>::append_; + using MetavarMap<ast::ChunkList>::append_; + using MetavarMap<Tweast>::append_; + + /// Fake append (default case, i.e. when \a data is not a metavariable). + template <typename T> T& append_(unsigned&, T& data) const; + + template <typename T> + void move_metavars_(Tweast& tweast, std::string& input); + + protected: + /// The next identifier suffix to create. + static unsigned count_; + + /// The string to parse. + std::stringstream input_; + }; + + /// Display the content of the tweast. + std::ostream& operator<<(std::ostream& ostr, const Tweast& in); + +} // namespace parse + +#include <parse/tweast.hxx> diff --git a/tiger-compiler/src/parse/tweast.hxx b/tiger-compiler/src/parse/tweast.hxx new file mode 100644 index 0000000..3cb9592 --- /dev/null +++ b/tiger-compiler/src/parse/tweast.hxx @@ -0,0 +1,75 @@ +/** + ** \file parse/tweast.hxx + ** \brief implements inline methods of parse/tweast.hh + */ + +#pragma once + +#include <algorithm> + +#include <boost/algorithm/string/replace.hpp> + +#include <misc/algorithm.hh> +#include <misc/error.hh> +#include <parse/tweast.hh> + +namespace parse +{ + template <typename T> T& Tweast::append_(unsigned&, T& data) const + { + return data; + } + + template <typename T> Tweast& Tweast::operator<<(const T& t) + { + input_ << append_(count_, t); + return *this; + } + + template <typename T> T* Tweast::take(unsigned s) + { + T* t = nullptr; + try + { + t = MetavarMap<T>::take_(s); + } + catch (const std::range_error& e) + { + // Print the contents of the Tweast before dying. + misc::error error; + error << e.what() << std::endl; + error << *this; + error.ice_here(); + } + return t; + } + + template <typename T> + void Tweast::move_metavars_(Tweast& tweast, std::string& input) + { + using metavars_type = MetavarMap<T>; + for (const typename metavars_type::map_type::value_type& var : + tweast.metavars_type::map_) + { + // Append the variable from VAR to the enclosing Tweast. + unsigned old_num = var.first; + // Warning, this is not thread-safe. + unsigned new_num = count_; + T* data = var.second; + metavars_type::map_[new_num] = data; + ++count_; + + // Rename metavariables according to the numbering scheme + // within the input string. + std::string old_str = metavars_type::show(old_num); + std::string new_str = metavars_type::show(new_num); + // FIXME: This is inefficient, since the string is viewed + // each time a metavariable is processed. Better store + // each (old_num, new_num) pair in a map, and process + // the string in a single pass. + boost::algorithm::replace_first(input, old_str, new_str); + } + tweast.metavars_type::map_.clear(); + } + +} // namespace parse diff --git a/tiger-compiler/src/task/argument-task.cc b/tiger-compiler/src/task/argument-task.cc new file mode 100644 index 0000000..82194f3 --- /dev/null +++ b/tiger-compiler/src/task/argument-task.cc @@ -0,0 +1,29 @@ +/** + ** \file task/argument-task.cc + ** \brief ArgumentTask class implementation. + */ + +#include <task/argument-task.hh> +#include <task/task-register.hh> + +namespace task +{ + ArgumentTask::ArgumentTask(const char* name, + const char* module_name, + const char* desc, + const char* argname, + std::string deps) + : Task(name, module_name, desc, deps) + , argname_(argname) + { + // Register this task. + TaskRegister::instance().register_task(*this); + } + + const std::string& ArgumentTask::arg_get() const { return arg_; } + + void ArgumentTask::arg_set(const std::string& arg) const { arg_ = arg; } + + const char* ArgumentTask::argname_get() const { return argname_; } + +} // namespace task diff --git a/tiger-compiler/src/task/argument-task.hh b/tiger-compiler/src/task/argument-task.hh new file mode 100644 index 0000000..775b084 --- /dev/null +++ b/tiger-compiler/src/task/argument-task.hh @@ -0,0 +1,58 @@ +/** + ** \file task/argument-task.hh + ** \brief Declare the task::ArgumentTask class. + */ + +#pragma once + +#include <string> + +#include <task/task.hh> + +namespace task +{ + /** \brief A `Task' expecting arguments prior to be executed. + + This class embodies tasks which need an additional parameter for their + `execute()' method. Therefore a call to `arg_set()' must be made prior to + the `execute()' method, to set the value of this expected argument. + This class also automates the registering of its derived classes, as does its + sibling class `SimpleTask' for tasks without additional argument. + */ + class ArgumentTask : public Task + { + /** \name Ctor & dtor. + ** \{ */ + public: + /// Construct and register an ArgumentTask. + ArgumentTask(const char* name, + const char* module_name, + const char* desc, + const char* argname, + std::string deps = ""); + + /** \} */ + + /** \name Accessors. + ** \{ */ + public: + /// Access to 'arg'. + const std::string& arg_get() const; + + /// Access to `arg'. + virtual void arg_set(const std::string& arg) const; + + /// Access to `argname'. + const char* argname_get() const; + + /** \} */ + + protected: + /// ArgumentTask argument value. + mutable std::string arg_; + + /// Argument name to be displayed when printing. + const char* argname_; + }; + +} // namespace task diff --git a/tiger-compiler/src/task/boolean-task.cc b/tiger-compiler/src/task/boolean-task.cc new file mode 100644 index 0000000..9923082 --- /dev/null +++ b/tiger-compiler/src/task/boolean-task.cc @@ -0,0 +1,22 @@ +/** + ** \file task/boolean-task.cc + ** \brief Implementation of task::BooleanTask. + ** + */ + +#include <task/boolean-task.hh> + +namespace task +{ + BooleanTask::BooleanTask(bool& flag, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : SimpleTask(name, module_name, desc, deps) + , flag_(flag) + {} + + void BooleanTask::execute() const { flag_ = true; } + +} //namespace task diff --git a/tiger-compiler/src/task/boolean-task.hh b/tiger-compiler/src/task/boolean-task.hh new file mode 100644 index 0000000..88298b3 --- /dev/null +++ b/tiger-compiler/src/task/boolean-task.hh @@ -0,0 +1,28 @@ +/** + ** \file task/boolean-task.hh + ** \brief Declare the task::BooleanTask class. + ** + */ +#pragma once + +#include <task/simple-task.hh> + +namespace task +{ + /// A simple Task that sets a Boolean variable to true. + class BooleanTask : public SimpleTask + { + public: + BooleanTask(bool& flag, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + + private: + bool& flag_; + }; + +} // namespace task diff --git a/tiger-compiler/src/task/disjunctive-task.cc b/tiger-compiler/src/task/disjunctive-task.cc new file mode 100644 index 0000000..07661bf --- /dev/null +++ b/tiger-compiler/src/task/disjunctive-task.cc @@ -0,0 +1,32 @@ +/** + ** \file task/disjunctive-task.cc + ** \brief Implementation of task::DisjunctiveTask. + ** + */ + +#include <task/disjunctive-task.hh> + +namespace task +{ + DisjunctiveTask::DisjunctiveTask(const char* module_name, + const char* desc, + const char* name, + std::string deps) + : SimpleTask(name, module_name, desc, deps) + {} + + void DisjunctiveTask::execute() const + { + // Nothing to do. + } + + Task::deps_type + DisjunctiveTask::resolve_dependencies(tasks_list_type& active_tasks) const + { + if (!active_tasks.empty() || dependencies_.empty()) + return Task::deps_type(); + else + return Task::deps_type(1, *dependencies_.begin()); + } + +} // namespace task diff --git a/tiger-compiler/src/task/disjunctive-task.hh b/tiger-compiler/src/task/disjunctive-task.hh new file mode 100644 index 0000000..6cb52fc --- /dev/null +++ b/tiger-compiler/src/task/disjunctive-task.hh @@ -0,0 +1,27 @@ +/** + ** \file task/disjunctive-task.hh + ** \brief Declare the DisjunctiveTask class. + ** + */ +#pragma once + +#include <task/simple-task.hh> + +namespace task +{ + /// A Task that makes sure that AT LEAST one of its dependencies is + /// scheduled. + class DisjunctiveTask : public SimpleTask + { + public: + DisjunctiveTask(const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + deps_type + resolve_dependencies(tasks_list_type& active_tasks) const override; + }; + +} //namespace task diff --git a/tiger-compiler/src/task/function-task.cc b/tiger-compiler/src/task/function-task.cc new file mode 100644 index 0000000..309a276 --- /dev/null +++ b/tiger-compiler/src/task/function-task.cc @@ -0,0 +1,22 @@ +/** + ** \file task/function-task.cc + ** \brief Implementation of task::FunctionTask. + ** + */ + +#include <task/function-task.hh> + +namespace task +{ + FunctionTask::FunctionTask(callback_type& callback, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : SimpleTask(name, module_name, desc, deps) + , execute_(callback) + {} + + void FunctionTask::execute() const { execute_(); } + +} //namespace task diff --git a/tiger-compiler/src/task/function-task.hh b/tiger-compiler/src/task/function-task.hh new file mode 100644 index 0000000..a08b285 --- /dev/null +++ b/tiger-compiler/src/task/function-task.hh @@ -0,0 +1,31 @@ +/** + ** \file task/function-task.hh + ** \brief Declare the task::FunctionTask class. + ** + */ +#pragma once + +#include <task/simple-task.hh> + +namespace task +{ + /// A simple Task that invokes a callback function. + class FunctionTask : public SimpleTask + { + public: + using callback_type = auto() -> void; + + FunctionTask(callback_type& callback, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + public: + void execute() const override; + + private: + callback_type& execute_; + }; + +} //namespace task diff --git a/tiger-compiler/src/task/fwd.hh b/tiger-compiler/src/task/fwd.hh new file mode 100644 index 0000000..620016f --- /dev/null +++ b/tiger-compiler/src/task/fwd.hh @@ -0,0 +1,22 @@ +/** + ** \file task/fwd.hh + ** \brief Interface to the Task module. + ** + */ +#pragma once + +namespace task +{ + // task.hh + class Task; + + // argument-task.hh + class ArgumentTask; + + // simple-task.hh + class SimpleTask; + + // task-register.hh + class TaskRegister; + +} // namespace task diff --git a/tiger-compiler/src/task/int-task.cc b/tiger-compiler/src/task/int-task.cc new file mode 100644 index 0000000..bf26271 --- /dev/null +++ b/tiger-compiler/src/task/int-task.cc @@ -0,0 +1,55 @@ +/** + ** \file task/int-task.cc + ** \brief IntTask class implementation. + ** + */ + +#include <exception> +#include <iostream> + +#include <common.hh> +#include <task/int-task.hh> + +namespace task +{ + IntTask::IntTask(int& var, + int min, + int max, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : ArgumentTask(name, module_name, desc, "NUM", deps) + , var_(var) + , min_(min) + , max_(max) + {} + + void IntTask::arg_set(const std::string& arg) const + { + arg_ = arg; + + try + { + var_ = stol(arg); + } + catch (const std::invalid_argument& e) + { + std::cerr << program_name << ": expected an integer: " << arg << '\n'; + throw; + } + + if (!(min_ <= var_ && var_ <= max_)) + { + std::cerr << program_name << ": invalid integer: " << var_ << '\n'; + + throw std::invalid_argument(std::string("invalid integer: ") + arg_); + } + } + + void IntTask::execute() const + { + // Assignment done in arg_set. + } + +} //namespace task diff --git a/tiger-compiler/src/task/int-task.hh b/tiger-compiler/src/task/int-task.hh new file mode 100644 index 0000000..a45518d --- /dev/null +++ b/tiger-compiler/src/task/int-task.hh @@ -0,0 +1,33 @@ +/** + ** \file task/int-task.hh + ** \brief Declare the IntTask class. + ** + */ +#pragma once + +#include <task/argument-task.hh> + +namespace task +{ + /// A simple Task that sets an Int variable. + class IntTask : public ArgumentTask + { + public: + IntTask(int& var, + int min, + int max, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + void arg_set(const std::string& arg) const override; + + private: + int& var_; + int min_; + int max_; + }; + +} //namespace task diff --git a/tiger-compiler/src/task/libtask.hh b/tiger-compiler/src/task/libtask.hh new file mode 100644 index 0000000..518d377 --- /dev/null +++ b/tiger-compiler/src/task/libtask.hh @@ -0,0 +1,107 @@ +/** + ** \file task/libtask.hh + ** \brief Interface to the Task module. + ** + */ + +/* This file must not be protected against multiple inclusion, + because it might be read to define or declare the tasks. */ + +#include <task/boolean-task.hh> +#include <task/disjunctive-task.hh> +#include <task/function-task.hh> +#include <task/int-task.hh> +#include <task/multiple-string-task.hh> +#include <task/string-task.hh> + +/// Handling of Task. +namespace task +{} // namespace task + +/** A means to concatenate tokens with delayed evaluation. */ +#define CONCAT_(A, B) A##B +#define CONCAT(A, B) CONCAT_(A, B) + +/** Make a "unique" token using the line number of invocation of this + macro. + + Conflicts are possible, but unlikely: tasks declared at the same + line number, but in different files, are not a problem, since + anyway the tasks are static, and in different namespaces. + + Just don't declare two tasks on the same line! */ +#define TASK_UNIQUE() CONCAT(task_, __LINE__) + +/*------------------------------. +| Easy instantiation of Tasks. | +`------------------------------*/ + +#undef TASK_GROUP +#undef TASK_DECLARE +#undef BOOLEAN_TASK_DECLARE +#undef INT_TASK_DECLARE +#undef STRING_TASK_DECLARE +#undef MULTIPLE_STRING_TASK_DECLARE +#undef DISJUNCTIVE_TASK_DECLARE + +// Should we define the objects, or just declare them? +#if DEFINE_TASKS + +/// Define the current Task group name. +# define TASK_GROUP(Name) const char group_name[] = Name + +/// Instantiate a FunctionTask. +# define TASK_DECLARE(Name, Help, Routine, Deps) \ + extern void(Routine)(); \ + static task::FunctionTask task_##Routine(Routine, group_name, Help, Name, \ + Deps) + +/// Instantiate a BooleanTask. +# define BOOLEAN_TASK_DECLARE(Name, Help, Flag, Deps) \ + bool Flag; \ + static task::BooleanTask task_##Flag(Flag, group_name, Help, Name, Deps) + +# define TASK_UNIQUE_NAME_(Line) task_##Line + +# define TASK_UNIQUE_NAME TASK_UNIQUE_NAME_(__LINE__) + +/// Instantiate an IntTask. +# define INT_TASK_DECLARE(Name, Min, Max, Help, Flag, Deps) \ + static task::IntTask TASK_UNIQUE()(Flag, Min, Max, group_name, Help, Name, \ + Deps) + +/// Instantiate a StringTask. +# define STRING_TASK_DECLARE(Name, Default, Help, Flag, Deps) \ + std::string Flag = Default; \ + static task::StringTask task_##Flag(Flag, group_name, Help, Name, Deps) + +/// Instanciate a MultipleStringTask +# define MULTIPLE_STRING_TASK_DECLARE(Name, Help, Routine, Deps) \ + extern task::MultipleStringTask::callback_type Routine; \ + static task::MultipleStringTask task_##Routine(Routine, group_name, Help, \ + Name, Deps) + +/// Instantiate a DisjunctiveTask. +# define DISJUNCTIVE_TASK_DECLARE(Name, Help, Deps) \ + static task::DisjunctiveTask task_##Routine(group_name, Help, Name, Deps) + +#else // !DEFINE_TASKS + +/// Define the current Task group name. +# define TASK_GROUP(Name) extern const char* group_name +/// Instantiate a FunctionTask. +# define TASK_DECLARE(Name, Help, Routine, Deps) extern void(Routine)() +/// Instantiate a BooleanTask. +# define BOOLEAN_TASK_DECLARE(Name, Help, Flag, Deps) extern bool Flag; +/// Instantiate an IntTask. +# define INT_TASK_DECLARE(Name, Min, Max, Help, Flag, Deps) +/// Instantiate a StringTask. +# define STRING_TASK_DECLARE(Name, Default, Help, Flag, Deps) \ + extern std::string Flag; +/// Instantiate a MultipleStringTask. +# define MULTIPLE_STRING_TASK_DECLARE(Name, Help, Routine, Deps) \ + extern void(Routine)(std::string); +/// Instantiate a DisjunctiveTask. +# define DISJUNCTIVE_TASK_DECLARE(Name, Help, Deps) + +#endif // !DEFINE_TASKS diff --git a/tiger-compiler/src/task/local.am b/tiger-compiler/src/task/local.am new file mode 100644 index 0000000..a9bd6aa --- /dev/null +++ b/tiger-compiler/src/task/local.am @@ -0,0 +1,15 @@ +## Task module. +src_libtc_la_SOURCES += \ + %D%/fwd.hh %D%/libtask.hh \ + %D%/task.hh %D%/task.hxx %D%/task.cc \ + %D%/boolean-task.hh %D%/boolean-task.cc \ + %D%/function-task.hh %D%/function-task.cc \ + %D%/int-task.hh %D%/int-task.cc \ + %D%/string-task.hh %D%/string-task.cc \ + %D%/multiple-string-task.hh %D%/multiple-string-task.cc \ + %D%/disjunctive-task.hh %D%/disjunctive-task.cc \ + %D%/task-register.hh %D%/task-register.cc %D%/task-register.hxx \ + %D%/simple-task.hh %D%/simple-task.cc \ + %D%/argument-task.hh %D%/argument-task.cc + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/task/multiple-string-task.cc b/tiger-compiler/src/task/multiple-string-task.cc new file mode 100644 index 0000000..a789663 --- /dev/null +++ b/tiger-compiler/src/task/multiple-string-task.cc @@ -0,0 +1,21 @@ +/** + ** \file task/multiple-string-task.cc + ** \brief Implementation of task::MultipleStringTask. + */ + +#include <task/multiple-string-task.hh> + +namespace task +{ + MultipleStringTask::MultipleStringTask(callback_type& cb, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : ArgumentTask(name, module_name, desc, "DIR", deps) + , execute_(cb) + {} + + void MultipleStringTask::execute() const { execute_(arg_get()); } + +} //namespace task diff --git a/tiger-compiler/src/task/multiple-string-task.hh b/tiger-compiler/src/task/multiple-string-task.hh new file mode 100644 index 0000000..a770b3b --- /dev/null +++ b/tiger-compiler/src/task/multiple-string-task.hh @@ -0,0 +1,34 @@ +/** + ** \file task/multiple-string-task.hh + ** \brief Declaration of task::MultipleStringTask. + ** + */ + +#pragma once + +#include <string> + +#include <task/argument-task.hh> + +namespace task +{ + /// A simple task that invokes callback function with string argument. + class MultipleStringTask : public ArgumentTask + { + public: + using callback_type = auto(const std::string&) -> void; + + MultipleStringTask(callback_type& cb, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + public: + void execute() const override; + + public: + callback_type& execute_; + }; + +} // namespace task diff --git a/tiger-compiler/src/task/simple-task.cc b/tiger-compiler/src/task/simple-task.cc new file mode 100644 index 0000000..031a182 --- /dev/null +++ b/tiger-compiler/src/task/simple-task.cc @@ -0,0 +1,21 @@ +/** + ** \file task/simple-task.cc + ** \brief SimpleTask class implementation. + */ + +#include <task/simple-task.hh> +#include <task/task-register.hh> + +namespace task +{ + SimpleTask::SimpleTask(const char* name, + const char* module_name, + const char* desc, + std::string deps) + : Task(name, module_name, desc, deps) + { + // Register this task. + TaskRegister::instance().register_task(*this); + } + +} // namespace task diff --git a/tiger-compiler/src/task/simple-task.hh b/tiger-compiler/src/task/simple-task.hh new file mode 100644 index 0000000..b44b8ea --- /dev/null +++ b/tiger-compiler/src/task/simple-task.hh @@ -0,0 +1,34 @@ +/** + ** \file task/simple-task.hh + ** \brief Declare the task::SimpleTask class. + */ + +#pragma once + +#include <string> + +#include <task/task.hh> + +namespace task +{ + /** \brief A code factoring class for tasks without arguments. + + The purpose of this class is to avoid duplicating the code which register a + task without arguments. The sibling class task::ArgumentTask does pretty much + the same (with extended functionnalities) for tasks with arguments. + */ + class SimpleTask : public Task + { + /** \name Ctor. */ + /** \{ */ + public: + /// Construct and register a SimpleTask. + SimpleTask(const char* name, + const char* module_name, + const char* desc, + std::string deps = ""); + + /** \} */ + }; + +} // namespace task diff --git a/tiger-compiler/src/task/string-task.cc b/tiger-compiler/src/task/string-task.cc new file mode 100644 index 0000000..db7776c --- /dev/null +++ b/tiger-compiler/src/task/string-task.cc @@ -0,0 +1,22 @@ +/** + ** \file task/string-task.cc + ** \brief Implementation of task::StringTask. + ** + */ + +#include <task/string-task.hh> + +namespace task +{ + StringTask::StringTask(std::string& var, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : ArgumentTask(name, module_name, desc, "STRING", deps) + , var_(var) + {} + + void StringTask::execute() const { var_ = arg_get(); } + +} //namespace task diff --git a/tiger-compiler/src/task/string-task.hh b/tiger-compiler/src/task/string-task.hh new file mode 100644 index 0000000..9e06293 --- /dev/null +++ b/tiger-compiler/src/task/string-task.hh @@ -0,0 +1,28 @@ +/** + ** \file task/string-task.hh + ** \brief Declare the task::StringTask class. + ** + */ +#pragma once + +#include <task/argument-task.hh> + +namespace task +{ + /// A simple Task that sets a String variable. + class StringTask : public ArgumentTask + { + public: + StringTask(std::string& var, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + + private: + std::string& var_; + }; + +} //namespace task diff --git a/tiger-compiler/src/task/task-register.cc b/tiger-compiler/src/task/task-register.cc new file mode 100644 index 0000000..d449eed --- /dev/null +++ b/tiger-compiler/src/task/task-register.cc @@ -0,0 +1,353 @@ +/** + ** \file task/task-register.cc + ** \brief Implementation of task::TaskRegister. + ** + */ + +#include <algorithm> +#include <exception> +#include <filesystem> +#include <fstream> +#include <map> + +#include <common.hh> +#include <misc/algorithm.hh> +#include <task/argument-task.hh> +#include <task/disjunctive-task.hh> +#include <task/simple-task.hh> +#include <task/task-register.hh> + +namespace task +{ + // Singleton stuff + TaskRegister& TaskRegister::instance() + { + static TaskRegister instance_; + return instance_; + } + + // Register this task. + void TaskRegister::register_task(const SimpleTask& task) + { + auto it = register_task_(task); + // The task was already registered. + if (it == modules_.end()) + return; + + // Callback when the option is parsed. + auto cb = [this, &task](bool f) { + if (f) + enable_task(task.name_get()); + }; + + namespace po = boost::program_options; + auto v = po::bool_switch()->notifier(cb); + + it->second.add_options()(task.fullname_get(), v, task.desc_get()); + } + + void TaskRegister::register_task(const ArgumentTask& task) + { + auto it = register_task_(task); + // The taks was already registered. + if (it == modules_.end()) + return; + + // Callback when the option is parsed. + auto cb = [this, &task](const std::string& arg) { + task.arg_set(arg); + enable_task(task.name_get()); + }; + + namespace po = boost::program_options; + auto v = po::value<std::string>() + ->value_name(task.argname_get()) + ->required() + ->notifier(cb); + + it->second.add_options()(task.fullname_get(), v, task.desc_get()); + } + + // Internal task registration. + // Add the task to the list of tasks and return an iterrator to the module + // the option belongs to. The module is created if needed. + // In case something went wrong, the end of `modules_' is returned. + TaskRegister::indexed_module_type::iterator + TaskRegister::register_task_(const Task& task) + { + if (task_list_.find(task.name_get()) != task_list_.end()) + { + task_error() << misc::error::error_type::failure << program_name + << ": TaskRegister::register_task(" << task.name_get() + << "): task name already registered.\n"; + return modules_.end(); + } + task_list_[task.name_get()] = &task; + + // Short-hands. + namespace po = boost::program_options; + const std::string& module_name = task.module_name_get(); + auto it = modules_.find(module_name); + if (it == modules_.end()) + it = modules_.emplace(module_name, po::options_description(module_name)) + .first; + // Return the iterator on the module the task belongs to. + return it; + } + + // Request the execution of the task task_name. + void TaskRegister::enable_task(const std::string& task_name) + { + if (task_list_.find(task_name) == task_list_.end()) + task_error() << misc::error::error_type::failure << program_name + << ": TaskRegister::enable_task(" << task_name + << "): this task has not been registered.\n"; + else + { + const Task* task = task_list_.find(task_name)->second; + resolve_dependencies(*task); + // FIXME: for efficiency, resolve_dependency should be called once. + // FIXME: detect cycle. + task_order_.emplace_back(task); + } + } + + // Return the number of tasks to execute. + int TaskRegister::nb_of_task_to_execute_get() { return task_order_.size(); } + + // Resolve dependencies between tasks. + void TaskRegister::resolve_dependencies(const Task& task) + { + tasks_list_type enabled_tasks; + + // Retrieved already active tasks. + for (const std::string& s : task.dependencies_get()) + if (task_list_.find(s) == task_list_.end()) + { + task_error() << misc::error::error_type::failure << program_name + << ": TaskRegister::resolve_dependencies(\"" + << task.name_get() << "\"): unknown task: \"" << s << '"' + << std::endl; + } + else + { + const Task* task_dep = task_list_.find(s)->second; + if (misc::has(task_order_, task_dep)) + enabled_tasks.emplace_back(task_dep); + } + + // Ask the task which dependent tasks should be activated. + const Task::deps_type dep_tasks = task.resolve_dependencies(enabled_tasks); + + // Activate them. + for (const std::string& s : dep_tasks) + { + if (task_list_.find(s) != task_list_.end()) + if (!misc::has(task_order_, task_list_.find(s)->second)) + enable_task(s); + } + } + + // Check whether one of the options in os has the string_key s. + template <typename T> + static bool + is_parsed(const std::string& s, + const std::vector<boost::program_options::basic_option<T>>& os) + { + using option = boost::program_options::basic_option<T>; + + return std::ranges::find_if( + os, [&s](const option& o) { return s == o.string_key; }) + != end(os); + } + + char* TaskRegister::parse_arg(int argc, char* argv[]) + { + // Short-hand. + namespace po = boost::program_options; + std::string input_file; + + // Create the category containing `help', `version' and `usage'. + po::options_description generic; + generic.add_options()("help,?", "Give this help list")( + "usage", "Give a short usage message")( + "version", "Print program version")("license", "Print program license"); + + // Positional parameter. + po::options_description hidden; + po::positional_options_description positional; + hidden.add_options()("input-file", + po::value<std::string>(&input_file)->required(), + "Input file"); + positional.add("input-file", 1); + + // Create the top-level category, with visible options. + po::options_description visible_desc(program_doc); + + // Add each category to the top-level one. + for (const auto& i : modules_) + visible_desc.add(i.second); + visible_desc.add(generic); + + // Sum of visible options and positional argument. + po::options_description all_opts = visible_desc; + all_opts.add(hidden); + + // Give control to boost. + try + { + // Sadly we can't use `boost::program_options::variables_map'. By doing + // so, we would lose the chronological order of the tasks, which is + // capital for TC to work. + auto parsed = po::command_line_parser(argc, argv) + .options(all_opts) + .positional(positional) + .run(); + + // We don't want to fail if these options are present. So we take care + // of them right now. + if (is_parsed("help", parsed.options)) + std::cout << visible_desc; + else if (is_parsed("version", parsed.options)) + std::cout << program_version; + else if (is_parsed("usage", parsed.options)) + std::cout << "tc [OPTIONS...] INPUT-FILE\n"; + else if (is_parsed("license", parsed.options)) + { + std::filesystem::path licenses("licenses"); + + if (!std::filesystem::exists(licenses)) + { + std::cerr << program_name << ": cannot open licenses directory" + << ": No such file or directory\n"; + } + else if (!std::filesystem::is_directory(licenses)) + { + std::cerr << program_name << ": cannot open licenses directory" + << ": Not a directory\n"; + } + else + { + using directory_iterator = std::filesystem::directory_iterator; + for (const auto& entry : directory_iterator(licenses)) + { + const auto& path = entry.path(); + + if (!std::filesystem::is_regular_file(path) + || !path.has_extension() + || path.extension() != ".license") + continue; + + std::ifstream license(path); + if (!license.is_open()) + continue; + + std::cout << "\n === " << path.stem().string() + << " license notice ===\n\n" + << license.rdbuf() + << "\n === " << path.stem().string() + << " license notice ===\n" + << std::endl; + } + } + } + else + { + // Replace the traditional calls to `vm.store()' and `vm.notify()'. + for (const auto& i : parsed.options) + { + auto option = parsed.description->find(i.string_key, false); + + po::variable_value v; + option.semantic()->parse(v.value(), i.value, true); + option.semantic()->notify(v.value()); + } + + // If no input file is given, throw. + if (input_file.size() == 0) + throw po::error("no file name"); + } + } + catch (const po::error& e) + { + std::cerr << program_name << ": " << e.what() + << "\ntc [OPTIONS...] INPUT-FILE\n" + "Try `tc --help' or `tc --usage' for more information.\n"; + throw std::invalid_argument("command line parsing error"); + } + catch (const std::invalid_argument& e) + { + throw; + } + + char* input_file_ = nullptr; + if (!input_file.empty()) + { + input_file_ = new char[input_file.size() + 1]; + strcpy(input_file_, input_file.c_str()); + } + return input_file_; + } + + // Display registered Tasks. + std::ostream& TaskRegister::print_task_list(std::ostream& ostr) + { + ostr << "List of registered tasks:\n"; + for (const tasks_by_name_type::value_type& i : task_list_) + ostr << "\t* " << i.first << '\n'; + return ostr << std::endl; + } + + // Dump task graph. + std::ostream& TaskRegister::print_task_graph(std::ostream& ostr) + { + ostr << "/* Task graph */\n" + << "digraph Tasks {\n" + << " node [shape=box, fontsize=14]\n" + // Preserve the order of the children. + << " graph [ordering=out]\n"; + + for (const tasks_by_name_type::value_type& i : task_list_) + { + const Task& task = *i.second; + if (dynamic_cast<const DisjunctiveTask*>(&task)) + ostr << " \"" << task.name_get() << "\" [shape=diamond]\n"; + ostr << " \"" << task.name_get() << "\""; + if (task.dependencies_get().size()) + { + ostr << " -> {"; + for (const std::string& s : task.dependencies_get()) + ostr << " \"" << s << "\""; + ostr << " } "; + } + ostr << '\n'; + } + + return ostr << "}\n"; + } + + // Display registered Tasks execution order. + std::ostream& TaskRegister::print_task_order(std::ostream& ostr) + { + ostr << "List of Task Order:\n"; + for (const Task* t : task_order_) + ostr << "\t* " << t->name_get() << std::endl; + return ostr << std::endl; + } + + // Execute tasks, checking dependencies. + void TaskRegister::execute() + { + // FIXME: should be the only one to call resolve_dependency. + for (const Task* t : task_order_) + { + std::string pref(t->module_name_get()); + if (!pref.empty()) + pref = pref[0] + std::string(": "); + timer_.push(pref + t->name_get()); + t->execute(); + timer_.pop(pref + t->name_get()); + } + } + +} // namespace task diff --git a/tiger-compiler/src/task/task-register.hh b/tiger-compiler/src/task/task-register.hh new file mode 100644 index 0000000..6608e02 --- /dev/null +++ b/tiger-compiler/src/task/task-register.hh @@ -0,0 +1,118 @@ +/** + ** \file task/task-register.hh + ** \brief Declare the task::TaskRegister class. + ** + */ + +#pragma once + +#include <iosfwd> +#include <map> +#include <string> +#include <vector> + +#include <boost/program_options.hpp> + +#include <misc/timer.hh> +#include <task/fwd.hh> + +namespace task +{ + /** \brief Register Tasks. + + The purpose of the TaskRegister class is to collect tasks + and organize their execution using their dependencies (like 'make'). + For modeling details, see Design Patterns: singleton. + */ + class TaskRegister + { + // Make it non-copyable. + TaskRegister(const TaskRegister&) = delete; + TaskRegister& operator=(const TaskRegister&) = delete; + + /// \name Ctor & dtor. + private: + /// Construct a TaskRegister. + TaskRegister() = default; + + public: + /// Access to the unique TaskRegister. + static TaskRegister& instance(); + + /** \name Tasks registering. + ** \{ */ + /// Register task \a task. + void register_task(const SimpleTask& task); + void register_task(const ArgumentTask& task); + /// Register the task \a task_name for execution. + void enable_task(const std::string& task_name); + + /// Return the number of tasks to execute. + int nb_of_task_to_execute_get(); + /** \} */ + + /// \name Task reordering. + private: + /** \brief Resolve dependencies between tasks. + ** + ** Resolve dependencies on tasks registered for execution. + ** Make a depth first search of implicit tasks graph, + ** check cycles and build an ordered list of tasks. */ + void resolve_dependencies(const Task& task); + + public: + /** \brief Parse \a argv and determine which tasks to execute. + ** + ** Use boost::program_options. */ + char* parse_arg(int argc, char* argv[]); + + /** \name Display TaskRegister content. + ** \{ */ + /// Display registered Tasks. + std::ostream& print_task_list(std::ostream& ostr); + /// Display task graph. + std::ostream& print_task_graph(std::ostream& ostr); + /// Display registered Tasks execution order. + std::ostream& print_task_order(std::ostream& ostr); + + /** \name Using registered Tasks. + ** \{ */ + /// Execute tasks, checking dependencies. + void execute(); + /** \} */ + + /** \name Time management. + ** \{ */ + /// Access to the tasks timer. + const misc::timer& timer_get() const; + /** \} */ + + /// Ordered vector of tasks. + using tasks_list_type = std::vector<const Task*>; + + private: + /// Associate a task name to a task. + using tasks_by_name_type = std::map<const std::string, Task const*>; + /// Associate a module name to a task module. + using indexed_module_type = + std::map<const std::string, boost::program_options::options_description>; + + // Common code between the two overload of `register_task'. + indexed_module_type::iterator register_task_(const Task& task); + + /// 'string to task' map. + tasks_by_name_type task_list_; + + /// 'ordered for execution' tasks list. + tasks_list_type task_order_; + + /// Tasks timer. + misc::timer timer_; + + /// Task modules. + indexed_module_type modules_; + }; + +} // namespace task + +#include <task/task-register.hxx> diff --git a/tiger-compiler/src/task/task-register.hxx b/tiger-compiler/src/task/task-register.hxx new file mode 100644 index 0000000..a72969a --- /dev/null +++ b/tiger-compiler/src/task/task-register.hxx @@ -0,0 +1,15 @@ +/** + ** \file task/task-register.hxx + ** \brief Inline methods of task::TaskRegister. + ** + */ + +#pragma once + +#include <task/task-register.hh> + +namespace task +{ + inline const misc::timer& TaskRegister::timer_get() const { return timer_; } + +} // namespace task. diff --git a/tiger-compiler/src/task/task.cc b/tiger-compiler/src/task/task.cc new file mode 100644 index 0000000..c675ffa --- /dev/null +++ b/tiger-compiler/src/task/task.cc @@ -0,0 +1,68 @@ +/** + ** \file task/task.cc + ** \brief Implementation of task::Task. + ** + */ + +#include <iostream> +#include <iterator> + +#include <task/task.hh> + +namespace task +{ + Task::Task(const char* name, + const char* module_name, + const char* desc, + const std::string& deps) + : name_(normalize(name)) + , fullname_(name_) + , module_name_(module_name) + , desc_(desc) + { + // Compute its dependencies. + std::string::size_type start = 0; + while (start < deps.size()) + { + std::string::size_type end = deps.find(' ', start); + if (end > deps.size()) + end = deps.size(); + dependencies_.emplace_back(normalize(deps.substr(start, end - start))); + start = end + 1; + } + + // See if it has a short option, such as "h|help". + if (name_.size() >= 2 && name_[1] == '|') + { + fullname_ += ','; + fullname_ += name_[0]; + name_.erase(0, 2); + fullname_.erase(0, 2); + } + } + + Task::deps_type Task::resolve_dependencies(tasks_list_type&) const + { + // By default, consider that all dependencies are required. + return dependencies_; + } + + /// Display dependencies of this task. + void Task::print_dependencies() const + { + std::cout << "Dependencies for task " << name_ << ":\n"; + for (const std::string& s : dependencies_) + std::cout << "\t" << s << '\n'; + std::cout << std::flush; + } + + std::string Task::normalize(const std::string& task_name) + { + std::string normalized_name; + std::ranges::replace_copy( + task_name, std::inserter(normalized_name, normalized_name.begin()), '_', + '-'); + return normalized_name; + } + +} // namespace task diff --git a/tiger-compiler/src/task/task.hh b/tiger-compiler/src/task/task.hh new file mode 100644 index 0000000..6081f1f --- /dev/null +++ b/tiger-compiler/src/task/task.hh @@ -0,0 +1,103 @@ +/** + ** \file task/task.hh + ** \brief Declare the task::Task class. + */ + +#pragma once + +#include <string> +#include <vector> + +#include <task/task-register.hh> + +namespace task +{ + /** \brief A function bound to a command line option. + + The purpose of the Task class is to abstract the execution of a module + and the tasks on which its execution depends. This is an implementation + of the Command Design Pattern. + */ + class Task + { + /** \name Ctor & dtor. */ + /** \{ */ + public: + /** \brief Construct a Task. + + \param name name of this task (used for long option) + \param module_name name of the module to which the task belongs. + \param desc description of this task + \param deps optional space separated list of task names + */ + Task(const char* name, + const char* module_name, + const char* desc, + const std::string& deps = ""); + + /// Destroy this Task. + virtual ~Task() = default; + /** \}*/ + + /** \name Abstract methods. + ** \{ */ + public: + /// Execute this task. + virtual void execute() const = 0; + /** \} */ + + using tasks_list_type = TaskRegister::tasks_list_type; + + using deps_type = std::vector<std::string>; + + virtual deps_type resolve_dependencies(tasks_list_type& active_tasks) const; + + /** \name Accessors. + ** \{ */ + public: + /** Access to 'name'. + + 'const char*' is preferred to 'std::string' because TaskRegister + calls function requiring 'const char*'. + The use of 'std::string::c_str()' is so forbidden and a call to + 'strdup(std::string::c_str())' would imply dummy memory leaks. */ + const char* name_get() const; + + /// Access to 'module_name'. + const char* module_name_get() const; + + /// Access to 'fullname'. + const char* fullname_get() const; + + /// Access to 'desc'. + const char* desc_get() const; + + /// Access to tasks dependencies. + const deps_type& dependencies_get() const; + + /** \} */ + + public: + /// Display dependencies of this task . + void print_dependencies() const; + + public: + /// Normalize the name of a task. + static std::string normalize(const std::string& task_name); + + protected: + /// Task name. + std::string name_; + /// Task name plus short name. + std::string fullname_; + /// Module name to which the task belongs. + const char* module_name_; + /// A short description of this task (displayed in program usage). + const char* desc_; + /// Contains the name of the tasks on which this one depends. + deps_type dependencies_; + }; + +} // namespace task + +#include <task/task.hxx> diff --git a/tiger-compiler/src/task/task.hxx b/tiger-compiler/src/task/task.hxx new file mode 100644 index 0000000..02fad0e --- /dev/null +++ b/tiger-compiler/src/task/task.hxx @@ -0,0 +1,24 @@ +/** + ** \file task/task.hxx + ** \brief Inline methods for task/task.hh. + */ +#pragma once + +#include <task/task.hh> + +namespace task +{ + inline const char* Task::name_get() const { return name_.c_str(); } + + inline const char* Task::module_name_get() const { return module_name_; } + + inline const char* Task::fullname_get() const { return fullname_.c_str(); } + + inline const char* Task::desc_get() const { return desc_; } + + inline const Task::deps_type& Task::dependencies_get() const + { + return dependencies_; + } + +} // namespace task diff --git a/tiger-compiler/src/task/tasks.cc b/tiger-compiler/src/task/tasks.cc new file mode 100644 index 0000000..33eb35f --- /dev/null +++ b/tiger-compiler/src/task/tasks.cc @@ -0,0 +1,28 @@ +/** + ** \file task/tasks.cc + ** \brief Task module related tasks. + */ + +#include <iostream> + +#include <common.hh> +#include <task/task-register.hh> +#define DEFINE_TASKS 1 +#include <task/tasks.hh> +#undef DEFINE_TASKS + +// Task module related tasks' implementation. +namespace task::tasks +{ + void tasks_list() { TaskRegister::instance().print_task_list(std::cout); } + + void tasks_graph() { TaskRegister::instance().print_task_graph(std::cout); } + + void tasks_selection() + { + TaskRegister::instance().print_task_order(std::cout); + } + + void time_report() { task_timer.dump_on_destruction(std::cerr); } + +} // namespace task::tasks diff --git a/tiger-compiler/src/task/tasks.hh b/tiger-compiler/src/task/tasks.hh new file mode 100644 index 0000000..b18943d --- /dev/null +++ b/tiger-compiler/src/task/tasks.hh @@ -0,0 +1,23 @@ +/** + ** \file task/tasks.hh + ** \brief Task module related tasks. + */ + +#pragma once + +#include <task/libtask.hh> + +namespace task::tasks +{ + TASK_GROUP("0. Tasks"); + + /// List all the existing tasks. + TASK_DECLARE("task-list", "list registered tasks", tasks_list, ""); + /// Dump task graph. + TASK_DECLARE("task-graph", "show task graph", tasks_graph, ""); + /// List the selected tasks in order. + TASK_DECLARE("task-selection", "list tasks to be run", tasks_selection, ""); + /// Ask for a time report at the end of the execution. + TASK_DECLARE("time-report", "report execution times", time_report, ""); + +} // namespace task::tasks diff --git a/tiger-compiler/src/tc.cc b/tiger-compiler/src/tc.cc new file mode 100644 index 0000000..0453714 --- /dev/null +++ b/tiger-compiler/src/tc.cc @@ -0,0 +1,53 @@ +/** + ** \file tc.cc + ** \brief The compiler driver. + */ + +#include <cstdlib> +#include <exception> +#include <iostream> +#include <stdexcept> +#include <common.hh> + +#include <task/task-register.hh> + +int main(int argc, char** argv) +{ + program_name = argv[0]; + + try + { + task_timer.start(); + task_timer.push("rest"); + + filename = task::TaskRegister::instance().parse_arg(argc, argv); + task_error().exit_on_error(); + + // If `help', `usage' or `version' is called, just exit. + if (filename == nullptr) + return 0; + + if (task::TaskRegister::instance().nb_of_task_to_execute_get() == 0) + task::TaskRegister::instance().enable_task("parse"); + + task::TaskRegister::instance().execute(); + task_timer << task::TaskRegister::instance().timer_get(); + task_error().exit_on_error(); + } + + // Required to enable stack unwinding. + catch (const std::invalid_argument& e) + { + return 64; + } + catch (const std::runtime_error& e) + { + if (e.what() != std::string("")) + std::cerr << e.what() << '\n'; + } + catch (const misc::error& e) + { + std::cerr << e; + return e.status_get_value(); + } +} diff --git a/tiger-compiler/src/testsuite/libtestsuite.cc b/tiger-compiler/src/testsuite/libtestsuite.cc new file mode 100644 index 0000000..86a5661 --- /dev/null +++ b/tiger-compiler/src/testsuite/libtestsuite.cc @@ -0,0 +1,30 @@ +/** + ** \file testsuite/libtestsuite.cc + ** \brief Functions exported by the testsuite module. + */ + +#include <testsuite/libtestsuite.hh> + +#include <testsuite/tests-collector.hh> +#include <testsuite/testsuite-generator.hh> + +namespace testsuite +{ + + std::vector<const ast::FunctionDec*> + find_program_tests(const ast::ChunkList& program) + { + TestsCollector tests_collector; + + tests_collector(program); + + return tests_collector.tests_get(); + } + + ast::ChunkInterface* + create_testsuite_runtime(const std::vector<const ast::FunctionDec*>& tests) + { + return generate_testsuite_runtime(tests); + } + +} // namespace testsuite diff --git a/tiger-compiler/src/testsuite/libtestsuite.hh b/tiger-compiler/src/testsuite/libtestsuite.hh new file mode 100644 index 0000000..2c42e04 --- /dev/null +++ b/tiger-compiler/src/testsuite/libtestsuite.hh @@ -0,0 +1,41 @@ +/** + ** \file testsuite/libtestsuite.hh + ** \brief Declare functions and variables exported by testsuite module. + */ + +#pragma once + +#include <ast/function-dec.hh> + +namespace testsuite + { + + /** + * Find all the tests functions of a program and return references to each of + * them. + * + * A function is considered a test function if it has the following + * properties :\n + * - It is located at the first level of a program file\n + * - Its name starts with test_* + * + * @param program the program to analyse + * @return a vector containing a reference to each test function + */ + std::vector<const ast::FunctionDec*> + find_program_tests(const ast::ChunkList& program); + + /** + * Create a testsuite runtime, which runs each function referenced in the + * vector. + * + * The return tree may then be appended to the main program AST list to be + * compiled along with the rest of this latter. + * + * @param tests the tests to run + * @return an AST tree describing the resulting testsuite runtime + */ + ast::ChunkInterface* + create_testsuite_runtime(const std::vector<const ast::FunctionDec*>& tests); + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/local.am b/tiger-compiler/src/testsuite/local.am new file mode 100644 index 0000000..0cd44bb --- /dev/null +++ b/tiger-compiler/src/testsuite/local.am @@ -0,0 +1,15 @@ +## testsuite module + +src_libtc_la_SOURCES += \ + %D%/libtestsuite.hh \ + %D%/libtestsuite.cc + +src_libtc_la_SOURCES += \ + %D%/tests-collector.hh %D%/tests-collector.cc \ + %D%/testsuite-generator.hh %D%/testsuite-generator.cc + + +## tasks + +TASKS += %D%/tasks.hh %D%/tasks.cc + diff --git a/tiger-compiler/src/testsuite/tasks.cc b/tiger-compiler/src/testsuite/tasks.cc new file mode 100644 index 0000000..c7f1164 --- /dev/null +++ b/tiger-compiler/src/testsuite/tasks.cc @@ -0,0 +1,49 @@ +/** + ** \file testsuite/tasks.hh + ** \brief Testsuite module related tasks' implementation. + */ + +#include <parse/libparse.hh> +#include <parse/tweast.hh> +#include <testsuite/libtestsuite.hh> + +#include <ast/tasks.hh> +#include <parse/tasks.hh> +#define DEFINE_TASKS 1 +#include <testsuite/tasks.hh> +#undef DEFINE_TASKS + +namespace testsuite::tasks + { + + void add_testsuite_runtime() + { + /** + * A testsuite being a series of function declaration, the prelude is + * not included by default. As such, we need to insert it on our own + * within the AST. + */ + parse::Tweast new_program; + + const auto prelude = parse::parse_prelude(); + const auto current_program = ast::tasks::the_program.release(); + + const auto tests = find_program_tests(*current_program); + + const auto testsuite_runtime = create_testsuite_runtime(tests); + + current_program->emplace_back(testsuite_runtime); + + if (prelude != nullptr) + { + new_program << prelude; + } + + new_program << current_program; + + const auto new_ast = parse::parse(new_program); + + ast::tasks::the_program = std::unique_ptr<ast::ChunkList>{new_ast}; + } + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/tasks.hh b/tiger-compiler/src/testsuite/tasks.hh new file mode 100644 index 0000000..463e7e0 --- /dev/null +++ b/tiger-compiler/src/testsuite/tasks.hh @@ -0,0 +1,21 @@ +/** + ** \file testsuite/tasks.hh + ** \brief Testsuite module related tasks. + */ + +#pragma once + +#include <task/libtask.hh> + +namespace testsuite::tasks + { + + TASK_GROUP("Testsuite"); + + TASK_DECLARE("testsuite", + "run all declared test_* functions within a testsuite runtime " + "(preludes included automatically, _main must not be set)", + add_testsuite_runtime, + "parse"); + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/tests-collector.cc b/tiger-compiler/src/testsuite/tests-collector.cc new file mode 100644 index 0000000..b3365b2 --- /dev/null +++ b/tiger-compiler/src/testsuite/tests-collector.cc @@ -0,0 +1,19 @@ +/** + ** \file testsuite/tests-collector.cc + ** \brief Implementation of testsuite::TestsCollector. + */ + +#include <testsuite/tests-collector.hh> + +namespace testsuite + { + + void TestsCollector::operator()(const ast::FunctionDec& e) + { + if (is_a_test_function(e)) + { + tests_.push_back(&e); + } + } + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/tests-collector.hh b/tiger-compiler/src/testsuite/tests-collector.hh new file mode 100644 index 0000000..d7e3ef1 --- /dev/null +++ b/tiger-compiler/src/testsuite/tests-collector.hh @@ -0,0 +1,53 @@ +/** + ** \file testsuite/tests-collector.hh + ** \brief Declaration of testsuite::TestsCollector. + */ + +#pragma once +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> + +namespace testsuite + { + + class TestsCollector + : public ast::DefaultConstVisitor + , public ast::NonObjectConstVisitor + , public ast::NonAssertConstVisitor + { + + public: + using super_type = ast::DefaultConstVisitor; + using super_type::operator(); + + TestsCollector() = default; + + /** + * Return all the functions recognized as test functions by the + * visitor. + * + * @return a vector of references to FunctionDec nodes that represents + * test functions + */ + inline std::vector<const ast::FunctionDec*>& tests_get(); + + void operator()(const ast::FunctionDec& e) override; + + protected: + // The tests identified by the visitor + std::vector<const ast::FunctionDec*> tests_; + + /** + * Return whether the function declaration is identified as a test + * function. + * @param node an ast node + * @return true if the node references a valid test function, false + * otherwise + */ + inline bool is_a_test_function(const ast::FunctionDec& node) ; + }; + + } // namespace testsuite + +#include <testsuite/tests-collector.hxx> diff --git a/tiger-compiler/src/testsuite/tests-collector.hxx b/tiger-compiler/src/testsuite/tests-collector.hxx new file mode 100644 index 0000000..9570dc1 --- /dev/null +++ b/tiger-compiler/src/testsuite/tests-collector.hxx @@ -0,0 +1,21 @@ +#pragma once + +#include <testsuite/tests-collector.hh> + +namespace testsuite + { + + inline std::vector<const ast::FunctionDec*>& TestsCollector::tests_get() + { + return tests_; + } + + inline bool + TestsCollector::is_a_test_function(const ast::FunctionDec& node) + { + return node.name_get().get().starts_with("test_") + && node.formals_get().empty(); + } + + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/testsuite-generator.cc b/tiger-compiler/src/testsuite/testsuite-generator.cc new file mode 100644 index 0000000..87ca479 --- /dev/null +++ b/tiger-compiler/src/testsuite/testsuite-generator.cc @@ -0,0 +1,143 @@ +/** + ** \file testsuite/testsuite-generator.cc + ** \brief Implementation of testsuite::TestsuiteGenerator. + */ + +#include <testsuite/testsuite-generator.hh> + +#include <ast/function-dec.hh> +#include <parse/libparse.hh> +#include <parse/tweast.hh> + +namespace testsuite + { + + /** + * Generate a function which calls the provided test with no arguments. + * @param definitions the testsuite definition + * @param test_reference the function to create a unit test for + * @return the new function's name + */ + static std::string add_test_to_testsuite( + parse::Tweast& definitions, + const ast::FunctionDec* test_reference) + { + const std::string test_name = test_reference->name_get().get(); + const std::string callback_name = "testsuite_" + test_name; + + definitions << "function " + callback_name + "() = " + "let " + "var child_pid := -1 " + "var status_code := -1 " + "var exit_code := -1 " + "in " + "child_pid := fork(); " + "if (child_pid = -1) then ( " + "print_err(\"could not run child process. skipping test\"); " + "skipped := skipped + 1 " + ") " + "else if (child_pid = 0) then ( " + + test_name + "(); " + "exit(0) " + ") " + "else ( " + "status_code := wait_pid(child_pid); " + + "if (status_code = 0) then ( " + "print(\" o \") " + ") " + "else (" + "print(\" x \") " + "); " + + "print(\"" + test_name + " test \"); " + "if (status_code = 0) then ( " + "print(\"passed\"); " + "passed := passed + 1 " + ") " + "else (" + "print(\"failed with \"); " + "exit_code := get_exit_code(status_code); " + "if (status_code = 6) then " + "print(\"assertion failure\") " + "else if (status_code = 8) then " + "print(\"illegal instruction\") " + "else if (status_code = 11) then " + "print(\"segmentation fault\") " + "else if (exit_code = 120) then " + "print(\"runtime failure\") " + "else ( " + "print(\"other error code \"); " + "print_int(exit_code) " + "); " + "failed := failed + 1" + "); " + "print(\"\\n\"); " + "flush() " + ") " + "end "; + + return callback_name; + } + + /** + * Build the testsuite runtime, parse the result and return the AST. + * @param definitions the function declarations to add within the runtime + * @param names the names of the tests to run, once declared + * @return the resulting AST + */ + static ast::ChunkInterface* build_testsuite( + const parse::Tweast& definitions, + const std::vector<std::string>& names) + { + parse::Tweast runtime; + + runtime << "function _main() = let " + "var skipped := 0 " + "var passed := 0 " + "var failed := 0 "; + + runtime << definitions.input_get(); + + runtime << " in " + "print(\"====================== Begin testing =====================\\n\\n\"); "; + + for (const std::string& test : names) + { + runtime << test << "(); "; + } + + runtime << "print(\"\\n=============== TC-RITERION testing result ===============\\n\"); " + "print(\" o Passed: \"); " + "print_int(passed); " + "print(\"\\n o Failed: \"); " + "print_int(failed); " + "if (skipped > 0) then ( " + "print(\"\\n o Skipped: \"); " + "print_int(skipped) " + "); " + "print(\"\\n==========================================================\\n\"); " + "flush() " + "end"; + + return parse::parse_chunks(runtime); + } + + ast::ChunkInterface* + generate_testsuite_runtime( + const std::vector<const ast::FunctionDec*>& tests) + { + parse::Tweast declarations; + std::vector<std::string> test_names; + + for (const auto test : tests) + { + const std::string test_name = + add_test_to_testsuite(declarations, test); + test_names.push_back(test_name); + } + + return build_testsuite(declarations, test_names); + } + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/testsuite-generator.hh b/tiger-compiler/src/testsuite/testsuite-generator.hh new file mode 100644 index 0000000..c7d9424 --- /dev/null +++ b/tiger-compiler/src/testsuite/testsuite-generator.hh @@ -0,0 +1,23 @@ +/** + ** \file testsuite/testsuite-generator.hh + ** \brief Declaration of testsuite::TestsuiteGenerator. + */ + +#pragma once + +#include <ast/chunk-interface.hh> + +namespace testsuite + { + + /** + * Generate a new AST providing a _main function. This main function + * calls each provided function within the context of a testsuite runtime. + * + * @param tests a vector referencing every test to run within the testsuite + * @return a new AST which can be appened to the main program + */ + ast::ChunkInterface* + generate_testsuite_runtime(const std::vector<const ast::FunctionDec*>& tests); + + } // namespace testsuite diff --git a/tiger-compiler/src/type/README.txt b/tiger-compiler/src/type/README.txt new file mode 100644 index 0000000..12bc011 --- /dev/null +++ b/tiger-compiler/src/type/README.txt @@ -0,0 +1,92 @@ +* README + +Hierarchy of types for the Tiger language, with (most of) their +interfaces. + + +ostream& operator<<(ostream& ostr, const Type& t) +bool operator==(const Type& lhs, const Type& rhs); +bool operator!=(const Type& lhs, const Type& rhs); + +/Type/ + virtual ~Type() {} + virtual const Type& actual() const; + virtual bool compatible_with(const Type& other) const; + virtual void accept(ConstVisitor& v) const; + virtual void accept(Visitor& v); + + Nil + bool compatible_with(const Type& other) const override; + const Type* record_type_get() const; + void set_record_type(const Type& type) const; + + Void + + Int + + String + + Named(misc::symbol name, const Type* type) + const Type* type_get() const; + void type_set(const Type *type) const; + misc::symbol name_get() const; + void name_set(misc::symbol name); + const Type& actual() const override; + const bool sound() const; + bool compatible_with(const Type& other) const override; + + Array(const Type& type) + const Type& type_get() const; + + Record() + const Type* field_type(misc::symbol key) const; + int field_index(misc::symbol key) const; + const list<Field> fields_get() const; + void field_add(const Field& field); + void field_add(misc::symbol name, const Type& type); + const_iterator begin() const; + const_iterator end() const; + + Class(const Class* super = nullptr) + const Type* attr_type(misc::symbol key) const; + const Type* meth_type(misc::symbol key) const; + const attrs_type& attrs_get() const; + const meths_type& meths_get() const; + const Attribute* attr_find(misc::symbol key) const; + const Attribute* owner_attr_find(misc::symbol key) const; + const Method* owned_meth_find(misc::symbol key) const; + void attr_add(const Attribute& attr); + void attr_add(const VarDec* def); + void meth_add(const Method* method); + bool has_data() const; + unsigned id_get() const; + const Class* super_get() const; + void super_set(const Class* type); + const subclasses_type& subclasses_get() const; + void subclass_add(const Class* subclass) const; + void subclasses_clear() const; + const Class* common_root(const Class& other) const; + bool sound() const; + static const Class& object_instance(); + + Function(const Record* formals, const Type& result) + + Method(misc::symbol name, const Class* owner, const Record* formals, + const Type& result, MethodDec* def) + misc::symbol name_get() const; + const Class* owner_get() const; + const Type& type_get() const; + const MethodDec* def_get() const; + MethodDec* def_get(); + void name_set(misc::symbol name); + void def_set(MethodDec* def); + +Field(misc::symbol name, const Type& type) + misc::symbol name_get() const; + const Type& type_get() const; + +Attribute(const ast::VarDec* def) + misc::symbol name_get() const; + const Type& type_get() const; + const ast::VarDec* def_get() const; + void def_set(const ast::VarDec* def); diff --git a/tiger-compiler/src/type/array.cc b/tiger-compiler/src/type/array.cc new file mode 100644 index 0000000..1cf51ca --- /dev/null +++ b/tiger-compiler/src/type/array.cc @@ -0,0 +1,28 @@ +/** + ** \file type/array.cc + ** \brief Implementation for type/array.hh. + */ + +#include <type/array.hh> +#include <type/visitor.hh> + +namespace type +{ + + // FIXME DONE: Some code was deleted here. + + Array::Array(const Type* const element_type) + : element_type_(element_type) + {} + + void Array::accept(ConstVisitor& v) const + { + v(*this); + } + + void Array::accept(Visitor& v) + { + v(*this); + } + +} // namespace type diff --git a/tiger-compiler/src/type/array.hh b/tiger-compiler/src/type/array.hh new file mode 100644 index 0000000..417eeee --- /dev/null +++ b/tiger-compiler/src/type/array.hh @@ -0,0 +1,30 @@ +/** + ** \file type/array.hh + ** \brief The class Array. + */ +#pragma once + +#include <type/fwd.hh> +#include <type/type.hh> + +namespace type +{ + /// Array types. + class Array : public Type + { + // FIXME DONE: Some code was deleted here. + public: + Array(const Type* element_type); + + void accept(ConstVisitor& v) const override; + void accept(Visitor& v) override; + + inline const Type* get_element_type() const; + + protected: + const Type* const element_type_; + }; + +} // namespace type + +#include <type/array.hxx> diff --git a/tiger-compiler/src/type/array.hxx b/tiger-compiler/src/type/array.hxx new file mode 100644 index 0000000..1ed65da --- /dev/null +++ b/tiger-compiler/src/type/array.hxx @@ -0,0 +1,18 @@ +/** + ** \file type/array.hxx + ** \brief Inline methods for type::Array. + */ +#pragma once + +#include <misc/contract.hh> +#include <type/array.hh> + +namespace type +{ + // FIXME DONE: Some code was deleted here. + inline const Type* Array::get_element_type() const + { + return element_type_; + } + +} // namespace type diff --git a/tiger-compiler/src/type/attribute.cc b/tiger-compiler/src/type/attribute.cc new file mode 100644 index 0000000..bfbe537 --- /dev/null +++ b/tiger-compiler/src/type/attribute.cc @@ -0,0 +1,16 @@ +/** + ** \file type/attribute.cc + ** \brief Implementation for type/attribute.hh. + */ + +#include <ostream> + +#include <type/attribute.hh> + +namespace type +{ + Attribute::Attribute(const ast::VarDec* def) + : def_(def) + {} + +} // namespace type diff --git a/tiger-compiler/src/type/attribute.hh b/tiger-compiler/src/type/attribute.hh new file mode 100644 index 0000000..d252da4 --- /dev/null +++ b/tiger-compiler/src/type/attribute.hh @@ -0,0 +1,44 @@ +/** + ** \file type/attribute.hh + ** \brief The class Attribute (of a class type). + */ +#pragma once + +#include <ast/var-dec.hh> + +namespace type +{ + + /** \brief The base object for Class attributes. + ** + ** Very much like Named, but it is *not* a Type. */ + class Attribute + { + /** \name Constructor. + ** \{ */ + public: + /** \brief Construct a Attribute. + ** \param def attribute's definition site.*/ + explicit Attribute(const ast::VarDec* def); + /** \} */ + + /** \name Accessors. + ** \{ */ + public: + /// Return the attribute's name. + misc::symbol name_get() const; + /// Return the attribute's type. + const Type& type_get() const; + /// Return attribute's definition site. + const ast::VarDec* def_get() const; + /// Set the attribute's definition site. + void def_set(const ast::VarDec* def); + /** \} */ + + private: + const ast::VarDec* def_; + }; + +} // namespace type + +#include <type/attribute.hxx> diff --git a/tiger-compiler/src/type/attribute.hxx b/tiger-compiler/src/type/attribute.hxx new file mode 100644 index 0000000..e59177d --- /dev/null +++ b/tiger-compiler/src/type/attribute.hxx @@ -0,0 +1,25 @@ +/** + ** \file type/attribute.hxx + ** \brief Inline methods for type::Attribute. + */ +#pragma once + +#include <misc/contract.hh> + +namespace type +{ + inline misc::symbol Attribute::name_get() const { return def_->name_get(); } + + inline const Type& Attribute::type_get() const + { + // FIXME DONE: Some code was deleted here. + precondition(def_->type_get() != nullptr); + + return *def_->type_get(); + } + + inline const ast::VarDec* Attribute::def_get() const { return def_; } + + inline void Attribute::def_set(const ast::VarDec* def) { def_ = def; } + +} // namespace type diff --git a/tiger-compiler/src/type/builtin-types.cc b/tiger-compiler/src/type/builtin-types.cc new file mode 100644 index 0000000..7ea70ad --- /dev/null +++ b/tiger-compiler/src/type/builtin-types.cc @@ -0,0 +1,22 @@ +/** + ** \file type/builtin-types.cc + ** \brief Implementation for type/builtin-types.hh. + */ + +#include <ostream> + +#include <type/builtin-types.hh> +#include <type/visitor.hh> + +namespace type +{ + // FIXME DONE: Some code was deleted here (Int, String, Void). + void Int::accept(ConstVisitor& v) const { v(instance()); } + void Int::accept(Visitor& v) { v(instance()); } + + void Void::accept(ConstVisitor& v) const { v(instance()); } + void Void::accept(Visitor& v) { v(instance()); } + + void String::accept(ConstVisitor& v) const { v(instance()); } + void String::accept(Visitor& v) { v(instance()); } +} // namespace type diff --git a/tiger-compiler/src/type/builtin-types.hh b/tiger-compiler/src/type/builtin-types.hh new file mode 100644 index 0000000..40295eb --- /dev/null +++ b/tiger-compiler/src/type/builtin-types.hh @@ -0,0 +1,40 @@ +/** + ** \file type/builtin-types.hh + ** \brief The classes Int, String, Void. + */ +#pragma once + +#include <misc/singleton.hh> +#include <type/fwd.hh> +#include <type/type.hh> + +namespace type +{ + // FIXME DONE: Some code was deleted here (Other types : Int, String, Void). + + class Int + : public Type + , public misc::Singleton<Int> + { + public: + void accept(ConstVisitor& v) const override; + void accept(Visitor& v) override; + }; + class Void + : public Type + , public misc::Singleton<Void> + { + public: + void accept(ConstVisitor& v) const override; + void accept(Visitor& v) override; + }; + class String + : public Type + , public misc::Singleton<String> + { + public: + void accept(ConstVisitor& v) const override; + void accept(Visitor& v) override; + }; + +} // namespace type diff --git a/tiger-compiler/src/type/class.cc b/tiger-compiler/src/type/class.cc new file mode 100644 index 0000000..a1876d3 --- /dev/null +++ b/tiger-compiler/src/type/class.cc @@ -0,0 +1,123 @@ +/** + ** \file type/class.cc + ** \brief Implementation for type/class.hh. + */ + +#include <ostream> + +#include <misc/algorithm.hh> +#include <type/class.hh> +#include <type/visitor.hh> + +namespace type +{ + Class::Class(const Class* super) + : Type() + , id_(fresh_id()) + , super_(super) + , subclasses_() + {} + + void Class::accept(ConstVisitor& v) const { v(*this); } + + void Class::accept(Visitor& v) { v(*this); } + + const Type* Class::attr_type(misc::symbol key) const + { + // FIXME DONE: Some code was deleted here. + const auto& attribute = std::ranges::find_if( + attrs_, + [&key] (const misc::symbol& name) { return name == key; }, + [] (const Attribute& attr) { return attr.name_get(); } + ); + + return attribute != attrs_.end() ? &attribute->type_get() : nullptr; + } + + const Type* Class::meth_type(misc::symbol key) const + { + // FIXME DONE: Some code was deleted here. + const auto& method = std::ranges::find_if( + meths_, + [&key] (const misc::symbol& name) { return name == key; }, + [] (const Method* const meth) { return meth->name_get(); } + ); + + return method != meths_.end() ? &(*method)->type_get() : nullptr; + } + + // FIXME DONE: Some code was deleted here (Find common super class). + const Class* Class::common_root(const Class& other) const + { + // temporary note: i'm tired so maybe the algo sucks ass :c + if (*this == other) + { + return this; + } + + std::vector<const Class*> super_classes; + + for (auto this_sc = this; this_sc != nullptr; this_sc = this_sc->super_) + { + super_classes.push_back(this_sc); + } + + for (auto other_sc = &other; other_sc != nullptr; other_sc = other_sc->super_) + { + if (auto matching = std::ranges::find(super_classes, other_sc); + matching != super_classes.end()) + { + return *matching; + } + } + + return nullptr; + } + + // FIXME DONE: Some code was deleted here (Super class soundness test). + bool Class::sound() const + { + auto reference = this; + std::vector<const Class*> previous; + + while (reference != nullptr) + { + if (std::ranges::find(previous, reference) != previous.end()) + { + return false; + } + + previous.push_back(reference); + reference = reference->super_; + } + + return true; + } + + // FIXME DONE: Some code was deleted here (Special implementation of "compatible_with" for Class). + bool Class::compatible_with(const Type& other) const + { + if (Type::compatible_with(other) || dynamic_cast<const Nil*>(&other)) + { + return true; + } + + const auto ptr = dynamic_cast<const Class*>(&other); + + return ptr && common_root(*ptr); + } + + const Class& Class::object_instance() + { + // FIXME DONE: Some code was deleted here. + static const Class object { nullptr }; + return object; + } + + unsigned Class::fresh_id() + { + static unsigned counter_ = 0; + return counter_++; + } + +} // namespace type diff --git a/tiger-compiler/src/type/class.hh b/tiger-compiler/src/type/class.hh new file mode 100644 index 0000000..37779e8 --- /dev/null +++ b/tiger-compiler/src/type/class.hh @@ -0,0 +1,159 @@ +/** + ** \file type/class.hh + ** \brief The class Class. + */ +#pragma once + +#include <vector> + +#include <misc/symbol.hh> +#include <type/attribute.hh> +#include <type/method.hh> +#include <type/type.hh> + +namespace type +{ + /** \brief Class types. + ** + ** List of Attributes and Methods. */ + class Class : public Type + { + /** \name Constructor. + ** \{ */ + public: + /** \brief Construct a Class. + ** + ** \param super Super class. */ + explicit Class(const Class* super = nullptr); + /** \} */ + + /// \name Visitors entry point. + /** \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /** \} */ + + /** \name Attribute and Method elementary manipulation. + ** \{ */ + /// \brief Return the attribute type associated to \a key. + /// + /// The search is performed throughout the super classes. + const Type* attr_type(misc::symbol key) const; + /// \brief Return the method type associated to \a key. + /// + /// The search is performed throughout the super classes. + const Type* meth_type(misc::symbol key) const; + /** \} */ + + /** \name Internal Attribute list manipulators. + ** \{ */ + /// List of Attribute's. + using attrs_type = std::vector<Attribute>; + /// List of Method's. + using meths_type = std::vector<const Method*>; + /// List of Subclasses's. + using subclasses_type = std::vector<const Class*>; + + /// Return the list of stored Attributes (read only). + const attrs_type& attrs_get() const; + /// Return the list of stored Methods (read only). + const meths_type& meths_get() const; + + /// \brief Find an attribute using its name, return `nullptr' if + /// not found. + /// + /// The search is performed throughout the super classes. + const Attribute* attr_find(misc::symbol key) const; + /// \brief Find a method using its name, return `nullptr' if + /// not found. + /// + /// The search is performed throughout the super classes. + const Method* meth_find(misc::symbol key) const; + + /// \brief Find an owned attribute using its name, return + /// `nullptr' if not found. + /// + /// The search is restricted to the class. + const Attribute* owned_attr_find(misc::symbol key) const; + + /// \brief Find an owned method using its name, return `nullptr' + /// if not found. + /// + /// The search is restricted to the class. + const Method* owned_meth_find(misc::symbol key) const; + + /// Add an already existing Attribute to the list. + void attr_add(const Attribute& attr); + /// Create a Attribute then add it to the list. + void attr_add(const ast::VarDec* def); + + /// Add an already existing Method to the list. + void meth_add(const Method* method); + /** \} */ + + /// Does this class have actual data (i.e., owned attributes)? + bool has_data() const; + + /** \name Accessors. + ** \{ */ + /// Return the unique identifier of the class. + unsigned id_get() const; + + /// Return the type of the super class. + const Class* super_get() const; + /// Set the type of the super class. + void super_set(const Class* type); + + /// Return (the transitive closure of) the list of subclasses. + const subclasses_type& subclasses_get() const; + + /// \brief Add a class to the list of subclasses. + /// + /// Although this method alters \a this, it is const, since types + /// are mostly manipulated as const entities. + void subclass_add(const Class* subclass) const; + + /// \brief Erase all the subclasses. + /// + /// This method is const for the same reason as + /// type::Class::subclass_add. + void subclasses_clear() const; + /** \} */ + + /** \name Type resolution. + ** \{ */ + /** \brief Find the common super class. */ + const Class* common_root(const Class& other) const; + + /** \brief Check that the definition of this class is sound, + ** i.e. that there is no recursive inheritance. */ + bool sound() const; + /** \} */ + + // FIXME DONE: Some code was deleted here (Inherited methods). + bool compatible_with(const Type& other) const override; + + /// Return the unique instance of the class type `Object'. + static const Class& object_instance(); + + private: + /// Return a fresh identifier. + static unsigned fresh_id(); + + /// Class unique identifier + unsigned id_; + /// Super class. + const Class* super_; + /// Sub classes. + mutable subclasses_type subclasses_; + /// Attributes list. + attrs_type attrs_; + /// Methods list. + meths_type meths_; + }; + +} // namespace type + +#include <type/class.hxx> diff --git a/tiger-compiler/src/type/class.hxx b/tiger-compiler/src/type/class.hxx new file mode 100644 index 0000000..0a41d17 --- /dev/null +++ b/tiger-compiler/src/type/class.hxx @@ -0,0 +1,88 @@ +/** + ** \file type/class.hxx + ** \brief Inline methods for type::Class. + */ +#pragma once + +#include <iostream> + +#include <misc/algorithm.hh> +#include <type/class.hh> + +namespace type +{ + inline const Class::attrs_type& Class::attrs_get() const { return attrs_; } + + inline const Attribute* Class::attr_find(misc::symbol key) const + { + for (const Class* cur = this; cur; cur = cur->super_get()) + { + const Attribute* attr = cur->owned_attr_find(key); + if (attr) + return attr; + } + return nullptr; + } + + inline const Method* Class::meth_find(misc::symbol key) const + { + for (const Class* cur = this; cur; cur = cur->super_get()) + { + const Method* meth = cur->owned_meth_find(key); + if (meth) + return meth; + } + return nullptr; + } + + inline const Attribute* Class::owned_attr_find(misc::symbol key) const + { + for (const Attribute& at : attrs_get()) + if (at.name_get() == key) + return &at; + return nullptr; + } + + inline const Method* Class::owned_meth_find(misc::symbol key) const + { + for (const Method* m : meths_get()) + if (m->name_get() == key) + return m; + return nullptr; + } + + inline void Class::attr_add(const Attribute& attr) + { + attrs_.emplace_back(attr); + } + + inline void Class::attr_add(const ast::VarDec* def) + { + attrs_.emplace_back(def); + } + + inline const Class::meths_type& Class::meths_get() const { return meths_; } + + inline void Class::meth_add(const Method* meth) { meths_.emplace_back(meth); } + + inline bool Class::has_data() const { return !attrs_.empty(); } + + inline unsigned Class::id_get() const { return id_; } + + inline const Class* Class::super_get() const { return super_; } + + inline void Class::super_set(const Class* super) { super_ = super; } + + inline const Class::subclasses_type& Class::subclasses_get() const + { + return subclasses_; + } + + inline void Class::subclass_add(const Class* subclass) const + { + subclasses_.emplace_back(subclass); + } + + inline void Class::subclasses_clear() const { subclasses_.clear(); } + +} // namespace type diff --git a/tiger-compiler/src/type/default-visitor.hh b/tiger-compiler/src/type/default-visitor.hh new file mode 100644 index 0000000..dbcfc0c --- /dev/null +++ b/tiger-compiler/src/type/default-visitor.hh @@ -0,0 +1,65 @@ +/** + ** \file type/default-visitor.hh + ** \brief Traverse a Type, doing nothing. + */ + +#pragma once + +#include <type/visitor.hh> + +namespace type +{ + template <template <typename> class Const> + class GenDefaultVisitor : public GenVisitor<Const> + { + public: + /// Convenient abbreviation. + template <typename Type> using const_t = typename Const<Type>::type; + + /// Super class type. + using super_type = GenVisitor<Const>; + + // Import overloaded \c operator() methods. + using super_type::operator(); + + /** \name Ctor & dtor. + ** \{ */ + /// Construct a default visitor. + GenDefaultVisitor(); + /// Destroy a default visitor. + virtual ~GenDefaultVisitor(); + /** \} */ + + /** \name Visit basic types. + ** \{ */ + void operator()(const_t<Nil>& e) override; + void operator()(const_t<Void>& e) override; + void operator()(const_t<Int>& e) override; + void operator()(const_t<String>& e) override; + /** \} */ + + /** \name Visit composed types. + ** \{ */ + void operator()(const_t<Named>& e) override; + void operator()(const_t<Array>& e) override; + void operator()(const_t<Record>& e) override; + void operator()(const_t<Class>& e) override; + void operator()(const_t<Function>& e) override; + void operator()(const_t<Method>& e) override; + /** \} */ + }; + + /// Shorthand for a const visitor. + using DefaultConstVisitor = GenDefaultVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using DefaultVisitor = GenDefaultVisitor<misc::id_traits>; + +#ifdef SWIG + /// Shorthand for a const visitor. + %template(DefaultConstVisitor) GenDefaultVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + %template(DefaultVisitor) GenDefaultVisitor<misc::id_traits>; +#endif +} // namespace type + +#include <type/default-visitor.hxx> diff --git a/tiger-compiler/src/type/default-visitor.hxx b/tiger-compiler/src/type/default-visitor.hxx new file mode 100644 index 0000000..02cbd15 --- /dev/null +++ b/tiger-compiler/src/type/default-visitor.hxx @@ -0,0 +1,95 @@ +/** + ** \file type/default-visitor.hxx + ** \brief Implementation for type/default-visitor.hh. + */ + +#pragma once + +#include <misc/algorithm.hh> +#include <type/class.hh> +#include <type/default-visitor.hh> +#include <type/types.hh> + +namespace type +{ + template <template <typename> class Const> + GenDefaultVisitor<Const>::GenDefaultVisitor() + : GenVisitor<Const>() + {} + + template <template <typename> class Const> + GenDefaultVisitor<Const>::~GenDefaultVisitor() = default; + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Nil>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Void>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Int>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<String>&) + {} + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Named>& e) + { + // FIXME DONE: Some code was deleted here. + e.type_get()->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Array>& e) + { + // FIXME DONE: Some code was deleted here. + e.get_element_type()->accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Record>& e) + { + // FIXME DONE: Some code was deleted here. + for (auto field : e) + { + field.type_get().accept(*this); + } + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Class>& e) + { + // FIXME DONE: Some code was deleted here. + for (auto attr : e.attrs_get()) + { + attr.type_get().accept(*this); + } + for (auto meth : e.meths_get()) + { + meth->accept(*this); + } + for (auto subclass : e.attrs_get()) + { + subclass.type_get().accept(*this); + } + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Function>& e) + { + // FIXME DONE: Some code was deleted here. + e.formals_get().accept(*this); + e.result_get().accept(*this); + } + + template <template <typename> class Const> + void GenDefaultVisitor<Const>::operator()(const_t<Method>& e) + { + e.Function::accept(*this); + } + +} // namespace type diff --git a/tiger-compiler/src/type/field.cc b/tiger-compiler/src/type/field.cc new file mode 100644 index 0000000..e7ec949 --- /dev/null +++ b/tiger-compiler/src/type/field.cc @@ -0,0 +1,17 @@ +/** + ** \file type/field.cc + ** \brief Implementation for type/field.hh. + */ + +#include <ostream> + +#include <type/field.hh> + +namespace type +{ + Field::Field(misc::symbol name, const Type& type) + : name_(name) + , type_(type) + {} + +} // namespace type diff --git a/tiger-compiler/src/type/field.hh b/tiger-compiler/src/type/field.hh new file mode 100644 index 0000000..e1f5609 --- /dev/null +++ b/tiger-compiler/src/type/field.hh @@ -0,0 +1,45 @@ +/** + ** \file type/field.hh + ** \brief The class Field (of a record type). + */ +#pragma once + +#include <misc/symbol.hh> +#include <type/fwd.hh> +#include <type/type.hh> + +namespace type +{ + /** \brief The base type for Record fields. + ** + ** Very much like Named, but it is *not* a Type. */ + class Field + { + public: + /** \name Ctor & dtor. + ** \{ */ + /** \brief Construct a Field. + ** \param name field's identifier. + ** \param type field's type. */ + Field(misc::symbol name, const Type& type); + /** \} */ + + /** \name Accessors. + ** \{ */ + /// Return the field's name. + misc::symbol name_get() const; + /// Return the field's type. + const Type& type_get() const; + /** \} */ + + protected: + /// Field's identifier. + misc::symbol name_; + + /// Field's type. + const Type& type_; + }; + +} // namespace type + +#include <type/field.hxx> diff --git a/tiger-compiler/src/type/field.hxx b/tiger-compiler/src/type/field.hxx new file mode 100644 index 0000000..b4d9630 --- /dev/null +++ b/tiger-compiler/src/type/field.hxx @@ -0,0 +1,16 @@ +/** + ** \file type/field.hxx + ** \brief Inline methods for type::Field. + */ +#pragma once + +#include <misc/contract.hh> +#include <type/field.hh> + +namespace type +{ + inline misc::symbol Field::name_get() const { return name_; } + + inline const Type& Field::type_get() const { return type_; } + +} // namespace type diff --git a/tiger-compiler/src/type/function.cc b/tiger-compiler/src/type/function.cc new file mode 100644 index 0000000..91cef2c --- /dev/null +++ b/tiger-compiler/src/type/function.cc @@ -0,0 +1,38 @@ +/** + ** \file type/function.cc + ** \brief Implementation for type/function.hh. + */ + +#include <ostream> +#include <ranges> + +#include <type/function.hh> +#include <type/visitor.hh> + +namespace type +{ + Function::Function(const Record* formals, const Type& result) + : result_(result) + { + precondition(formals); + + formals_ = formals; + } + + Function::~Function() { delete formals_; } + + void Function::accept(ConstVisitor& v) const { v(*this); } + + void Function::accept(Visitor& v) { v(*this); } + + // FIXME DONE: Some code was deleted here. + bool Function::compatible_with(const Type& other) const + { + const auto other_function = dynamic_cast<const Function*>(&other); + + return other_function != nullptr \ + && other_function->formals_get().compatible_with(this->formals_get()) \ + && other_function->result_get().compatible_with(this->result_get()); + } + +} // namespace type diff --git a/tiger-compiler/src/type/function.hh b/tiger-compiler/src/type/function.hh new file mode 100644 index 0000000..2accb82 --- /dev/null +++ b/tiger-compiler/src/type/function.hh @@ -0,0 +1,59 @@ +/** + ** \file type/function.hh + ** \brief The class Function. + */ +#pragma once + +#include <type/fwd.hh> +#include <type/record.hh> +#include <type/type.hh> + +namespace type +{ + /** \brief Function types. + ** + ** Encapsulate the signature of a function, i.e. the type structures + ** of both function's arguments and its result. */ + class Function : public Type + { + public: + /** \brief Construct a Function. + ** + ** \param formals type structures of formal arguments. + ** \param result type structure of what function returns. */ + Function(const Record* formals, const Type& result); + + /** \brief Destructor. + **/ + ~Function() override; + + /// \name Visitors entry point. + /** \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /** \} */ + + /** \name Accessors. + ** \{ */ + /// Return the type structures of the function's arguments. + const Record& formals_get() const; + /// Return the type structure of the function's result. + const Type& result_get() const; + /** \} */ + + // FIXME DONE: Some code was deleted here (Special implementation of "compatible_with" for Function). + bool compatible_with(const Type& other) const override; + + protected: + /// Formals' types. + const Record* formals_; + + /// Result's type. + const Type& result_; + }; + +} // namespace type + +#include <type/function.hxx> diff --git a/tiger-compiler/src/type/function.hxx b/tiger-compiler/src/type/function.hxx new file mode 100644 index 0000000..1784650 --- /dev/null +++ b/tiger-compiler/src/type/function.hxx @@ -0,0 +1,16 @@ +/** + ** \file type/function.hxx + ** \brief Inline methods for type::Function. + */ +#pragma once + +#include <misc/contract.hh> +#include <type/function.hh> + +namespace type +{ + inline const Record& Function::formals_get() const { return *formals_; } + + inline const Type& Function::result_get() const { return result_; } + +} // namespace type diff --git a/tiger-compiler/src/type/fwd.hh b/tiger-compiler/src/type/fwd.hh new file mode 100644 index 0000000..9fa9fca --- /dev/null +++ b/tiger-compiler/src/type/fwd.hh @@ -0,0 +1,31 @@ +/** + ** \file type/fwd.hh + ** \brief Forward declarations for the type module. + */ + +#pragma once + +#include <misc/fwd.hh> + +namespace type +{ + class Array; + class Attribute; + class Class; + class Field; + class Function; + class Int; + class Method; + class Named; + class Nil; + class Record; + class String; + class Type; + class Void; + + // From visitor.hh + template <template <typename> class Const> class GenVisitor; + using ConstVisitor = GenVisitor<misc::constify_traits>; + using Visitor = GenVisitor<misc::id_traits>; + +} // namespace type diff --git a/tiger-compiler/src/type/libtype.cc b/tiger-compiler/src/type/libtype.cc new file mode 100644 index 0000000..50392d7 --- /dev/null +++ b/tiger-compiler/src/type/libtype.cc @@ -0,0 +1,19 @@ +/** + ** \file type/libtype.cc + ** \brief Define the function exported by type module. + */ + +#include <ast/exp.hh> +#include <type/libtype.hh> +#include <type/type-checker.hh> + +namespace type +{ + misc::error types_check(ast::Ast& tree) + { + TypeChecker type; + type(tree); + return type.error_get(); + } + +} // namespace type diff --git a/tiger-compiler/src/type/libtype.hh b/tiger-compiler/src/type/libtype.hh new file mode 100644 index 0000000..01fa60e --- /dev/null +++ b/tiger-compiler/src/type/libtype.hh @@ -0,0 +1,19 @@ +/** + ** \file type/libtype.hh + ** \brief Declare the function exported by type module. + */ + +#pragma once + +#include <ast/fwd.hh> +#include <misc/error.hh> + +/// Type-checking an ast::Ast. +namespace type +{ + /** \brief Check types in a (bound) AST. + ** \param tree abstract syntax tree's root. + ** \return synthesis of the errors possibly found. */ + misc::error types_check(::ast::Ast& tree); + +} // namespace type diff --git a/tiger-compiler/src/type/local.am b/tiger-compiler/src/type/local.am new file mode 100644 index 0000000..e071bc1 --- /dev/null +++ b/tiger-compiler/src/type/local.am @@ -0,0 +1,29 @@ +## type module. +EXTRA_DIST += %D%/README.txt + +src_libtc_la_SOURCES += \ + %D%/fwd.hh %D%/types.hh \ + %D%/array.hh %D%/array.cc %D%/array.hxx \ + %D%/builtin-types.hh %D%/builtin-types.cc \ + %D%/nil.hh %D%/nil.cc \ + %D%/default-visitor.hh %D%/default-visitor.hxx \ + %D%/field.hh %D%/field.cc %D%/field.hxx \ + %D%/named.hh %D%/named.cc %D%/named.hxx \ + %D%/pretty-printer.hh %D%/pretty-printer.cc \ + %D%/record.hh %D%/record.cc %D%/record.hxx \ + %D%/type.hh %D%/type.hxx %D%/type.cc \ + %D%/type-checker.hh %D%/type-checker.hxx %D%/type-checker.cc \ + %D%/libtype.hh %D%/libtype.cc \ + %D%/visitor.hh %D%/visitor.hxx + +src_libtc_la_SOURCES += \ + %D%/function.hh %D%/function.cc %D%/function.hxx +src_libtc_la_SOURCES += \ + %D%/attribute.hh %D%/attribute.cc %D%/attribute.hxx \ + %D%/class.hh %D%/class.cc %D%/class.hxx \ + %D%/method.hh %D%/method.cc %D%/method.hxx + +check_PROGRAMS += %D%/test-type +%C%_test_type_LDADD = src/libtc.la + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/type/method.cc b/tiger-compiler/src/type/method.cc new file mode 100644 index 0000000..5726948 --- /dev/null +++ b/tiger-compiler/src/type/method.cc @@ -0,0 +1,48 @@ +/** + ** \file type/method.cc + ** \brief Implementation for type/method.hh. + */ + +#include <iostream> + +#include <type/method.hh> +#include <type/visitor.hh> + +namespace type +{ + Method::Method(misc::symbol name, + const Class* owner, + const Record* formals, + const Type& result, + ast::MethodDec* def) + : Function(formals, result) + , name_(name) + , owner_(owner) + , def_(def) + {} + + Method::Method(misc::symbol name, + const Record* formals, + const Type& result, + ast::MethodDec* def) + : Function(formals, result) + , name_(name) + , owner_(nullptr) + , def_(def) + {} + + void Method::accept(ConstVisitor& v) const { v(*this); } + + void Method::accept(Visitor& v) { v(*this); } + + // FIXME DONE: Some code was deleted here. + bool Method::compatible_with(const Type& other) const + { + const auto other_method = dynamic_cast<const Method*>(&other); + + return other_method != nullptr \ + && other_method->formals_get().compatible_with(other) \ + && other_method->result_get().compatible_with(other); + } + +} // namespace type diff --git a/tiger-compiler/src/type/method.hh b/tiger-compiler/src/type/method.hh new file mode 100644 index 0000000..f9c3d8c --- /dev/null +++ b/tiger-compiler/src/type/method.hh @@ -0,0 +1,90 @@ +/** + ** \file type/method.hh + ** \brief The class Method. + */ +#pragma once + +#include <ast/method-dec.hh> +#include <type/function.hh> + +namespace type +{ + // Forward declaration. + class Class; + + /** \brief Method types. + ** + ** Encapsulate the signature of a method, i.e. the type structures + ** of both method's arguments and its result. */ + class Method : public Function + { + /// A shortcut for the super type of this class. + using super_type = Function; + + public: + /** \brief Construct a FunEntry. + ** + ** \param name The method's identifier. + ** \param owner The type::Class owning this method. + ** \param formals The type structures of formal arguments. + ** \param result The type structure of what method returns. + ** \param def The method's definition site. */ + Method(misc::symbol name, + const Class* owner, + const Record* formals, + const Type& result, + ast::MethodDec* def); + + /** \brief Construct a FunEntry. + ** + ** \param name The method's identifier. + ** \param formals The type structures of formal arguments. + ** \param result The type structure of what method returns. + ** \param def The method's definition site. */ + Method(misc::symbol name, + const Record* formals, + const Type& result, + ast::MethodDec* def); + + /// \name Visitors entry point. + /** \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /** \} */ + + /** \name Accessors. + ** \{ */ + /// Return the method's name. + misc::symbol name_get() const; + /// Return the method's owner (enclosing class). + const Class* owner_get() const; + /// Return the method's type. + const Type& type_get() const; + /// Return the method's definiton site. + const ast::MethodDec* def_get() const; + /// Return the method's definiton site. + ast::MethodDec* def_get(); + + /// Set the method's name. + void name_set(misc::symbol name); + /// set the method's definiton site. + void def_set(ast::MethodDec* def); + /** \} */ + + // FIXME DONE: Some code was deleted here (Special implementation of "compatible_with" for type::Method). + bool compatible_with(const Type& other) const override; + + private: + /// Method's identifier. + misc::symbol name_; + /// The Class where this Method is defined. + const Class* owner_; + /// Method's definition site. + ast::MethodDec* def_; + }; + +} // namespace type + +#include <type/method.hxx> diff --git a/tiger-compiler/src/type/method.hxx b/tiger-compiler/src/type/method.hxx new file mode 100644 index 0000000..d026252 --- /dev/null +++ b/tiger-compiler/src/type/method.hxx @@ -0,0 +1,27 @@ +/** + ** \file type/method.hxx + ** \brief Inline methods for type::Method. + */ +#pragma once + +namespace type +{ + inline misc::symbol Method::name_get() const { return name_; } + + inline const Class* Method::owner_get() const { return owner_; } + + inline const Type& Method::type_get() const + { + // FIXME DONE: Some code was deleted here. + return *def_->type_get(); + } + + inline const ast::MethodDec* Method::def_get() const { return def_; } + + inline ast::MethodDec* Method::def_get() { return def_; } + + inline void Method::name_set(misc::symbol name) { name_ = name; } + + inline void Method::def_set(ast::MethodDec* def) { def_ = def; } + +} // namespace type diff --git a/tiger-compiler/src/type/named.cc b/tiger-compiler/src/type/named.cc new file mode 100644 index 0000000..ff092f6 --- /dev/null +++ b/tiger-compiler/src/type/named.cc @@ -0,0 +1,61 @@ +/** + ** \file type/named.cc + ** \brief Implementation for type/named.hh. + */ + +#include <type/named.hh> +#include <type/visitor.hh> +#include <vector> + +namespace type +{ + Named::Named(misc::symbol name) + : name_(name) + , type_(nullptr) + {} + + Named::Named(misc::symbol name, const Type* type) + : name_(name) + , type_(type) + {} + + // Inherited functions + void Named::accept(ConstVisitor& v) const + { + // FIXME DONE: Some code was deleted here. + v(*this); + } + + void Named::accept(Visitor& v) + { + // FIXME DONE: Some code was deleted here. + v(*this); + } + + bool Named::sound() const + { + // FIXME DONE: Some code was deleted here (Sound). + auto reference = dynamic_cast<const Named*>(type_); + std::vector<const Named*> previous; + + while (reference != nullptr) + { + if (std::ranges::find(previous, reference) != previous.end()) + { + return false; + } + + previous.push_back(reference); + reference = dynamic_cast<const Named*>(reference->type_get()); + } + + return true; + } + + bool Named::compatible_with(const Type& other) const + { + // FIXME DONE: Some code was deleted here (Special implementation of "compatible_with" for Named). + return type_->compatible_with(other); + } + +} // namespace type diff --git a/tiger-compiler/src/type/named.hh b/tiger-compiler/src/type/named.hh new file mode 100644 index 0000000..f8b914f --- /dev/null +++ b/tiger-compiler/src/type/named.hh @@ -0,0 +1,90 @@ +/** + ** \file type/named.hh + ** \brief The class Named. + */ +#pragma once + +#include <misc/symbol.hh> +#include <type/fwd.hh> +#include <type/type.hh> + +namespace type +{ + /** \brief Named types. + ** + ** Named types are used when new types are defined, i.e., in + ** \b Example: let type name_ = type_. + */ + class Named : public Type + { + /** \name Ctor & dtor. + ** \{ */ + public: + /** \brief Construct a Named type. + ** \param name user defined type's identifier. */ + explicit Named(misc::symbol name); + /** \brief Construct a Named type. + ** \param name user defined type's identifier. + ** \param type defined type's structure */ + Named(misc::symbol name, const Type* type); + /** \} */ + + /// \name Visitors entry point. + /** \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /** \} */ + + /** \name Accessors. + ** \{ */ + /// Return the user defined type's structure. + const Type* type_get() const; + + /** \brief Set the defined type's structure. + ** + ** This is the version which is used by TypeChecker which needs to + ** assign a value to the Named type. */ + void type_set(const Type* type) const; + + /// Return the name of this type. + misc::symbol name_get() const; + + /// (Re)set the name of this type. + void name_set(misc::symbol name); + /** \} */ + + /** \name Type resolution. + ** \{ */ + /// The type pointed to ultimately. + const Type& actual() const override; + + /** \brief Whether the definition of this named type is sound, + ** i.e. that there is no recursive dependency. */ + bool sound() const; + /** \} */ + + bool compatible_with(const Type& other) const override; + + protected: + /// Name of the defined type. + misc::symbol name_; + + /** \brief The Type pointed to. + ** + ** "Mutable const" because most of the time types are handled as + ** const objects. But Named types are built in two steps: first + ** they are built without any value for \a type_ (by + ** TypeChecker::visit_dec_header <ast::TypeDec>), and then + ** they are completed (by TypeChecker::visit_dec_body <ast::TypeDec>). + ** Because the second step of the construction is actually seen + ** as a modification by the C++ type system, we have it accept + ** it thanks to mutable. + **/ + mutable const Type* type_{}; + }; + +} // namespace type + +#include <type/named.hxx> diff --git a/tiger-compiler/src/type/named.hxx b/tiger-compiler/src/type/named.hxx new file mode 100644 index 0000000..1b84bb6 --- /dev/null +++ b/tiger-compiler/src/type/named.hxx @@ -0,0 +1,28 @@ +/** + ** \file type/named.hxx + ** \brief Inline methods for type::Named. + */ +#pragma once + +#include <misc/contract.hh> +#include <type/named.hh> + +namespace type +{ + inline const Type* Named::type_get() const { return type_; } + + inline void Named::type_set(const Type* type) const { type_ = type; } + + inline misc::symbol Named::name_get() const { return name_; } + + inline void Named::name_set(misc::symbol name) { name_ = name; } + + inline const Type& Named::actual() const + { + // FIXME DONE: Some code was deleted here. + precondition(type_ != nullptr); + + return type_->actual(); + } + +} // namespace type diff --git a/tiger-compiler/src/type/nil.cc b/tiger-compiler/src/type/nil.cc new file mode 100644 index 0000000..d3c663c --- /dev/null +++ b/tiger-compiler/src/type/nil.cc @@ -0,0 +1,34 @@ +/** + ** \file type/nil.cc + ** \brief Implementation for type/nil.hh. + */ + +#include <ostream> + +#include <type/class.hh> +#include <type/nil.hh> +#include <type/record.hh> +#include <type/visitor.hh> + +namespace type +{ + /*------. + | Nil. | + `------*/ + + void Nil::accept(ConstVisitor& v) const { v(*this); } + + void Nil::accept(Visitor& v) { v(*this); } + + bool Nil::compatible_with(const Type& other) const + { + // FIXME DONE: Some code was deleted here + return dynamic_cast<const Record*>(&other.actual()) != nullptr + || dynamic_cast<const Class*>(&other.actual()) != nullptr; + } + + const Type* Nil::record_type_get() const { return record_type_; } + + void Nil::record_type_set(const Type& type) const { record_type_ = &type; } + +} // namespace type diff --git a/tiger-compiler/src/type/nil.hh b/tiger-compiler/src/type/nil.hh new file mode 100644 index 0000000..4807ac0 --- /dev/null +++ b/tiger-compiler/src/type/nil.hh @@ -0,0 +1,47 @@ +/** + ** \file type/nil.hh + ** \brief The Nil type. + */ +#pragma once + +#include <type/fwd.hh> +#include <type/type.hh> + +namespace type +{ + /*------. + | Nil. | + `------*/ + + class Record; + + /// The builtin type of `nil'. + /// The Nil type is the type of a `nil` expression. + class Nil : public Type + { + public: + /// \name Visitors entry point. + /** \{ */ + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /** \} */ + + bool compatible_with(const Type& other) const override; + + /// Get the associated record type. + const Type* record_type_get() const; + + /// Set the record type used with the nil. + void record_type_set(const Type& type) const; + + private: + /// The actual record_type that it represents. + /// Nil types are either compatible with `type::Record` or `type::Class`. + /// record_type represents the actual type that the `nil` was meant to be + /// used with. + mutable const Type* record_type_ = nullptr; + }; + +} // namespace type diff --git a/tiger-compiler/src/type/pretty-printer.cc b/tiger-compiler/src/type/pretty-printer.cc new file mode 100644 index 0000000..a6f4a99 --- /dev/null +++ b/tiger-compiler/src/type/pretty-printer.cc @@ -0,0 +1,127 @@ +/** + ** \file type/pretty-printer.cc + ** \brief Implementation for type/pretty-printer.hh. + */ + +#include <type/libtype.hh> +#include <type/pretty-printer.hh> +#include <type/type.hh> +#include <type/types.hh> + +namespace type +{ + namespace + { + template <typename Type> + std::ostream& print_type(std::ostream& ostr, const Type& type) + { + PrettyPrinter printer{ostr}; + printer(type); + return ostr; + } + + /// How many times did we go through operator()(const Named&)? + inline long int& indent(std::ostream& o) + { + // The slot to store the current indentation level. + static const int indent_index = std::ios::xalloc(); + return o.iword(indent_index); + } + + } // namespace + + std::ostream& operator<<(std::ostream& ostr, const Attribute& e) + { + return print_type(ostr, e); + } + + std::ostream& operator<<(std::ostream& ostr, const Field& e) + { + return print_type(ostr, e); + } + + std::ostream& operator<<(std::ostream& ostr, const Type& e) + { + return print_type(ostr, e); + } + + PrettyPrinter::PrettyPrinter(std::ostream& ostr) + : ostr_{ostr} + {} + + void PrettyPrinter::operator()(const Nil& e) + { + ostr_ << "nil = "; + if (auto record_type = e.record_type_get()) + ostr_ << *record_type; + else + ostr_ << "(null)"; + } + + void PrettyPrinter::operator()(const Void&) { ostr_ << "void"; } + + void PrettyPrinter::operator()(const Int&) + { + // FIXME DONE: Some code was deleted here. + ostr_ << "int"; + } + + void PrettyPrinter::operator()(const String&) + { + // FIXME DONE: Some code was deleted here. + ostr_ << "string"; + } + + void PrettyPrinter::operator()(const Named& e) + { + // FIXME DONE: Some code was deleted here. + if (const misc::symbol& name = e.name_get(); + name != "int" && name != "string") + ostr_ << name << ": "; + + super_type::operator()(e); + } + + void PrettyPrinter::operator()(const Array& e) + { + // FIXME DONE: Some code was deleted here. + ostr_ << "array of "; + super_type::operator()(e); + } + + void PrettyPrinter::operator()(const Record& e) + { + // FIXME DONE: Some code was deleted here. + ostr_ << "record {" << misc::incendl; + super_type::operator()(e); + ostr_ << misc::decendl << "}"; + } + + void PrettyPrinter::operator()(const Class& e) + { + // FIXME DONE: Some code was deleted here. + ostr_ << "class {" << misc::incendl; + super_type::operator()(e); + ostr_ << misc::decendl << "}"; + } + + void PrettyPrinter::operator()(const Function& e) + { + // FIXME DONE: Some code was deleted here. + ostr_ << "function ("; + e.formals_get().accept(*this); + ostr_ << ") -> "; + e.result_get().accept(*this); + } + + void PrettyPrinter::operator()(const Attribute& e) + { + ostr_ << e.name_get() << " : " << e.type_get(); + } + + void PrettyPrinter::operator()(const Field& e) + { + ostr_ << e.name_get() << " : " << e.type_get(); + } + +} // namespace type diff --git a/tiger-compiler/src/type/pretty-printer.hh b/tiger-compiler/src/type/pretty-printer.hh new file mode 100644 index 0000000..786cfcf --- /dev/null +++ b/tiger-compiler/src/type/pretty-printer.hh @@ -0,0 +1,67 @@ +/** + ** \file type/pretty-printer.hh + ** \brief Print the type hierarchy. + */ + +#pragma once + +#include <ostream> + +#include <type/default-visitor.hh> +#include <type/fwd.hh> + +namespace type +{ + class PrettyPrinter : public DefaultConstVisitor + { + public: + /// Super class type. + using super_type = DefaultConstVisitor; + + // Import overloaded \c operator() methods. + using super_type::operator(); + + /** \name Ctor & dtor. + ** \{ */ + /// Construct a pretty printer. + explicit PrettyPrinter(std::ostream& ostr); + /** \} */ + + /** \name Visit basic types. + ** \{ */ + void operator()(const Nil& e) override; + void operator()(const Void& e) override; + void operator()(const Int& e) override; + void operator()(const String& e) override; + /** \} */ + + /** \name Visit composed types. + ** \{ */ + void operator()(const Named& e) override; + void operator()(const Array& e) override; + void operator()(const Record& e) override; + void operator()(const Class& e) override; + void operator()(const Function& e) override; + /** \} */ + + /** \name Visit Non type types. + ** \{ */ + void operator()(const Attribute& e); + void operator()(const Field& e); + /** \} */ + + private: + /// The stream to print on. + std::ostream& ostr_; + }; + + /// Overload redirection operator for Type. + std::ostream& operator<<(std::ostream& ostr, const Type& t); + + /// Overload redirection operator for Attribute. + std::ostream& operator<<(std::ostream& ostr, const Attribute& obj); + + /// Overload redirection operator for Field. + std::ostream& operator<<(std::ostream& ostr, const Field& obj); + +} // namespace type diff --git a/tiger-compiler/src/type/record.cc b/tiger-compiler/src/type/record.cc new file mode 100644 index 0000000..174e192 --- /dev/null +++ b/tiger-compiler/src/type/record.cc @@ -0,0 +1,52 @@ +/** + ** \file type/record.cc + ** \brief Implementation for type/record.hh. + */ + +#include <algorithm> +#include <ostream> + +#include <type/builtin-types.hh> +#include <type/nil.hh> +#include <type/record.hh> +#include <type/visitor.hh> + +namespace type +{ + void Record::accept(ConstVisitor& v) const { v(*this); } + + void Record::accept(Visitor& v) { v(*this); } + + // FIXME DONE: Some code was deleted here (Field manipulators). + const Type* Record::field_type(const misc::symbol key) const + { + const auto result = std::ranges::find_if( + fields_, + [&key] (const misc::symbol& name) { return name == key; }, + [] (const Field& field) { return field.name_get(); } + ); + + return result != fields_.end() ? &result->type_get() : nullptr; + } + + int Record::field_index(const misc::symbol key) const + { + const auto result = std::ranges::find_if( + fields_, + [&key] (const misc::symbol& name) { return name == key; }, + [] (const Field& field) { return field.name_get(); } + ); + + return result != fields_.end() \ + ? static_cast<int>(std::distance(fields_.begin(), result)) \ + : -1; + } + + // FIXME DONE: Some code was deleted here (Special implementation of "compatible_with" for Record). + bool Record::compatible_with(const Type& other) const + { + return Type::compatible_with(other) \ + || dynamic_cast<const Nil*>(&other) != nullptr; + } + +} // namespace type diff --git a/tiger-compiler/src/type/record.hh b/tiger-compiler/src/type/record.hh new file mode 100644 index 0000000..d5a7d82 --- /dev/null +++ b/tiger-compiler/src/type/record.hh @@ -0,0 +1,69 @@ +/** + ** \file type/record.hh + ** \brief The class Record. + */ +#pragma once + +#include <vector> + +#include <misc/indent.hh> +#include <misc/symbol.hh> +#include <type/field.hh> +#include <type/fwd.hh> +#include <type/type.hh> + +namespace type +{ + /** \brief Record types. + ** + ** List of Field s. */ + class Record : public Type + { + /// \name Visitors entry point. + /// \{ */ + public: + /// Accept a const visitor \a v. + void accept(ConstVisitor& v) const override; + /// Accept a non-const visitor \a v. + void accept(Visitor& v) override; + /// \} + + /** \name Field elementary manipulation. + ** \{ */ + /// Return the type associated to \a key. + const Type* field_type(misc::symbol key) const; + /** \brief Return the index of the field associated to \a key. + ** + ** The index of a field is its position in the list. */ + int field_index(misc::symbol key) const; + /** \} */ + + /** \name Internal Field list manipulators. + ** \{ */ + /// List of Field's. + using fields_type = std::vector<Field>; + /// Return the Field list stored in this (read only). + const fields_type& fields_get() const; + + /// Add an already existing Field to the list. + void field_add(const Field& field); + /// Create a Field then add it to the list. + void field_add(misc::symbol name, const Type& type); + + /// Iterators over Field's. + using const_iterator = fields_type::const_iterator; + const_iterator begin() const; + const_iterator end() const; + /** \} */ + + // FIXME DONE: Some code was deleted here (Inherited method). + bool compatible_with(const Type& other) const override; + + protected: + /// Fields list. + fields_type fields_; + }; + +} // namespace type + +#include <type/record.hxx> diff --git a/tiger-compiler/src/type/record.hxx b/tiger-compiler/src/type/record.hxx new file mode 100644 index 0000000..2907cb0 --- /dev/null +++ b/tiger-compiler/src/type/record.hxx @@ -0,0 +1,34 @@ +/** + ** \file type/record.hxx + ** \brief Inline methods for type::Record. + */ +#pragma once + +#include <misc/contract.hh> +#include <type/record.hh> + +namespace type +{ + inline const Record::fields_type& Record::fields_get() const + { + return fields_; + } + + inline void Record::field_add(const Field& field) + { + fields_.emplace_back(field); + } + + inline void Record::field_add(misc::symbol name, const Type& type) + { + fields_.emplace_back(name, type); + } + + inline Record::const_iterator Record::begin() const + { + return fields_.begin(); + } + + inline Record::const_iterator Record::end() const { return fields_.end(); } + +} // namespace type diff --git a/tiger-compiler/src/type/tasks.cc b/tiger-compiler/src/type/tasks.cc new file mode 100644 index 0000000..261e015 --- /dev/null +++ b/tiger-compiler/src/type/tasks.cc @@ -0,0 +1,22 @@ +/** + ** \file type/tasks.cc + ** \brief Type module task implementations. + */ + +#include <ast/tasks.hh> +#include <common.hh> +#include <type/libtype.hh> +#define DEFINE_TASKS 1 +#include <type/tasks.hh> +#undef DEFINE_TASKS + +// Type module related tasks' implementation. +namespace type::tasks +{ + void types_check() + { + task_error() << ::type::types_check(*ast::tasks::the_program) + << &misc::error::exit_on_error; + } + +} // namespace type::tasks diff --git a/tiger-compiler/src/type/tasks.hh b/tiger-compiler/src/type/tasks.hh new file mode 100644 index 0000000..e6add54 --- /dev/null +++ b/tiger-compiler/src/type/tasks.hh @@ -0,0 +1,29 @@ +/** + ** \file type/tasks.hh + ** \brief Type module tasks. + */ + +#pragma once + +#include <task/libtask.hh> + +namespace type::tasks +{ + TASK_GROUP("4. Type checking"); + + /// Default the type-checking to Tiger (without objects nor overloading). + DISJUNCTIVE_TASK_DECLARE("T|typed", + "default the type-checking to Tiger " + "(without objects nor overloading)", + "types-compute" + " combine-types-compute" + " object-types-compute" + " assert-types-compute"); + + /// Check for type violation. + TASK_DECLARE("types-compute", + "check for type violations", + types_check, + "bindings-compute"); + +} // namespace type::tasks diff --git a/tiger-compiler/src/type/type-checker.cc b/tiger-compiler/src/type/type-checker.cc new file mode 100644 index 0000000..fcecc87 --- /dev/null +++ b/tiger-compiler/src/type/type-checker.cc @@ -0,0 +1,639 @@ +/** + ** \file type/type-checker.cc + ** \brief Implementation for type/type-checker.hh. + */ + +#include <memory> +#include <boost/iterator/zip_iterator.hpp> +#include <misc/contract.hh> + +#include <ast/all.hh> +#include <type/type-checker.hh> +#include <type/types.hh> + +namespace type +{ + TypeChecker::TypeChecker() + : super_type() + , error_() + {} + + const Type* TypeChecker::type(ast::Typable& e) + { + // FIXME DONE: Some code was deleted here. + if (e.type_get() == nullptr) + e.accept(*this); + return e.type_get(); + } + + const Record* TypeChecker::type(const ast::fields_type& e) + { + const auto res = new Record; + // FIXME DONE: Some code was deleted here. + for (const auto& var : e) + { + if (res->field_index(var->name_get()) != -1) + { + error(*var, + var->name_get().get() + + " attribute is already present in record"); + } + + res->field_add(var->name_get(), *type(var->type_name_get())); + } + return res; + } + + const Record* TypeChecker::type(const ast::VarChunk& e) + { + const auto res = new Record; + + for (const auto& var : e) + { + res->field_add(var->name_get(), *type(*var)); + } + + return res; + } + + const misc::error& TypeChecker::error_get() const { return error_; } + + /*-----------------. + | Error handling. | + `-----------------*/ + + void TypeChecker::error(const ast::Ast& ast, const std::string& msg) + { + error_ << misc::error::error_type::type << ast.location_get() << ": " << msg + << std::endl; + } + + void TypeChecker::type_mismatch(const ast::Ast& ast, + const std::string& exp1, + const Type& type1, + const std::string& exp2, + const Type& type2) + { + error_ << misc::error::error_type::type << ast.location_get() + << ": type mismatch" << misc::incendl << exp1 << " type: " << type1 + << misc::iendl << exp2 << " type: " << type2 << misc::decendl; + } + + void TypeChecker::check_types(const ast::Ast& loc, + const std::string& exp1, + const Type& type1, + const std::string& exp2, + const Type& type2) + { + // FIXME DONE: Some code was deleted here (Check for type mismatch). + if (!type1.actual().compatible_with(type2.actual())) + type_mismatch(loc, exp1, type1, exp2, type2); + + // If any of the type is Nil, set its `record_type_` to the other type. + if (!error_) + { + // FIXME DONE: Some code was deleted here. + if (auto nil = dynamic_cast<const Nil*>(&type1); nil != nullptr) + nil->record_type_set(type2); + else if (nil = dynamic_cast<const Nil*>(&type2); nil != nullptr) + nil->record_type_set(type1); + } + } + + void TypeChecker::check_types(const ast::Ast& ast, + const std::string& exp1, + ast::Typable& type1, + const std::string& exp2, + ast::Typable& type2) + { + // Ensure evaluation order. + type(type1); + type(type2); + // FIXME DONE: Some code was deleted here (Check types). + check_types(ast, exp1, *type1.type_get(), exp2, *type2.type_get()); + } + + /*--------------------------. + | The core of the visitor. | + `--------------------------*/ + + /*-----------------. + | Visiting /Var/. | + `-----------------*/ + + void TypeChecker::operator()(ast::SimpleVar& e) + { + // FIXME DONE: Some code was deleted here. + // It is assumed SimpleVar references a variable that has already been typed + precondition(e.def_get()->type_get() != nullptr); + type_default(e, e.def_get()->type_get()); + } + + // FIXME DONE: Some code was deleted here. + + void TypeChecker::operator()(ast::FieldVar& e) + { + const auto record = + dynamic_cast<const Record*>(&type(e.var_get())->actual()); + + if (record == nullptr) + { + error(e.var_get(), "variable is not a record"); + type_default(e, &default_type); + return; + } + + const int index = record->field_index(e.name_get()); + + if (index < 0) + { + error(e, + e.name_get().get() + " attribute does not exist within record"); + type_default(e, &default_type); + return; + } + + const Type& type = record->fields_get().at(index).type_get(); + + type_default(e, &type); + } + + void TypeChecker::operator()(ast::SubscriptVar& e) + { + check_type(e.index_get(), "array subscript value", Int::instance()); + + auto array = dynamic_cast<const Array*>(&type(e.var_get())->actual()); + if (array == nullptr) + { + error(e, "index does not reference an array"); + type_default(e, &default_type); + } + else + { + type_default(e, array->get_element_type()); + } + } + + /*-----------------. + | Visiting /Exp/. | + `-----------------*/ + + // Literals. + void TypeChecker::operator()(ast::NilExp& e) + { + auto nil_ptr = std::make_unique<Nil>(); + type_default(e, nil_ptr.get()); + created_type_default(e, nil_ptr.release()); + } + + void TypeChecker::operator()(ast::IntExp& e) + { + // FIXME DONE: Some code was deleted here. + type_default(e, &Int::instance()); + } + + void TypeChecker::operator()(ast::StringExp& e) + { + // FIXME DONE: Some code was deleted here. + type_default(e, &String::instance()); + } + + // Complex values. + + void TypeChecker::operator()(ast::RecordExp& e) + { + // FIXME DONE: Some code was deleted here. + + const Type* named_record = type(e.type_name_get()); + const auto record = dynamic_cast<const Record*>(&named_record->actual()); + + assertion(record != nullptr); + + // everything must be typed first ! + for (const auto field : e.fields_get()) + { + type(field->init_get()); + } + + if (e.fields_get().size() != record->fields_get().size()) + { + error(e, std::string("insufficient amount of arguments")); + } + else + { + auto record_fields = record->fields_get(); + auto exp_fields = e.fields_get(); + + std::for_each( + boost::make_zip_iterator( + boost::make_tuple(record_fields.begin(), exp_fields.begin())), + boost::make_zip_iterator( + boost::make_tuple(record_fields.end(), exp_fields.end())), + [this](const boost::tuple<Field&, ast::FieldInit*>& params) { + const misc::symbol& expected_name = params.get<0>().name_get(); + const Type& expected_type = params.get<0>().type_get(); + + const misc::symbol& actual_name = params.get<1>()->name_get(); + const ast::Exp& actual_node = params.get<1>()->init_get(); + + if (expected_name != actual_name) + { + error(actual_node, + std::string("name mismatch: expected " + + expected_name.get() + " but got " + + actual_name.get())); + } + + check_types(actual_node, expected_name.get() + " actual", + *actual_node.type_get(), + expected_name.get() + "expected", expected_type); + }); + } + + type_default(e, named_record); + } + + void TypeChecker::operator()(ast::OpExp& e) + { + // FIXME DONE: Some code was deleted here. + + ast::OpExp::Oper oper = e.oper_get(); + type(e.left_get()); + type(e.right_get()); + + check_types(e, "left operand", *e.left_get().type_get(), "right operand", + *e.right_get().type_get()); + + if (const auto comparaison_type = &e.left_get().type_get()->actual(); + (oper < ast::OpExp::Oper::eq || oper > ast::OpExp::Oper::ne) + && (dynamic_cast<const Int*>(comparaison_type) == nullptr + && dynamic_cast<const String*>(comparaison_type) == nullptr)) + { + error_ << misc::error::error_type::type << e.location_get() + << ": type mismatch" << misc::incendl + << "order operations are only possible on int and string, not " + << *comparaison_type << misc::decendl; + } + + type_default(e, &Int::instance()); + } + + // FIXME DONE: Some code was deleted here. + + void TypeChecker::operator()(ast::ArrayExp& e) + { + // Typing order + type(e.type_name_get()); + type(e.size_get()); + type(e.init_get()); + + const Type* actual_type = e.type_name_get().type_get(); + const auto array_type = dynamic_cast<const Array*>(&actual_type->actual()); + + if (array_type == nullptr) + { + error(e.type_name_get(), "type does not reference an array"); + type_default(e, &default_type); + return; + } + + check_type(e.size_get(), "array size", Int::instance()); + check_types(e, "array element", *array_type->get_element_type(), + "initializer", *e.init_get().type_get()); + + type_default(e, actual_type); + } + + void TypeChecker::operator()(ast::AssignExp& e) + { + check_types(e, "variable", e.var_get(), "value", e.exp_get()); + + if (auto svar = dynamic_cast<ast::SimpleVar*>(&e.var_get()); + svar && var_read_only_.find(svar->def_get()) != var_read_only_.end()) + error(e, "variable is read-only"); + + type_default(e, &Void::instance()); + } + + void TypeChecker::operator()(ast::BreakExp& e) + { + type_default(e, &Void::instance()); + } + + void TypeChecker::operator()(ast::CallExp& e) + { + for (ast::Exp* exp : e.args_get()) + { + type(*exp); + } + + const auto actual_params = e.args_get(); + const auto expected_params = e.def_get()->formals_get().decs_get(); + + if (actual_params.size() != expected_params.size()) + { + error(e, + std::string(std::to_string(expected_params.size()) + + " parameters expected but got " + + std::to_string(actual_params.size()))); + } + else + { + std::for_each( + boost::make_zip_iterator( + boost::make_tuple(expected_params.begin(), actual_params.begin())), + boost::make_zip_iterator( + boost::make_tuple(expected_params.end(), actual_params.end())), + [this, &e](const boost::tuple<ast::VarDec*, ast::Exp*>& params) { + check_types(e, "expected", *params.get<0>(), "actual", + *params.get<1>()); + }); + } + + type_default( + e, &dynamic_cast<const Function*>(e.def_get()->type_get())->result_get()); + } + + void TypeChecker::operator()(ast::CastExp& e) + { + check_types(e, "target", e.ty_get(), "value", e.exp_get()); + + created_type_default(e, e.ty_get().type_get()); + } + + void TypeChecker::operator()(ast::ForExp& e) + { + check_types(e, "iterator", e.vardec_get(), "high bound", e.hi_get()); + + check_type(e.hi_get(), "high bound", Int::instance()); + + var_read_only_.insert(&e.vardec_get()); + + check_type(e.body_get(), "for body", Void::instance()); + + /* Break cannot be used in assignments */ + const auto iterator_node = + dynamic_cast<const ast::IntExp*>(e.vardec_get().init_get()); + const auto hi_node = dynamic_cast<const ast::IntExp*>(&e.hi_get()); + + if (iterator_node != nullptr && hi_node != nullptr + && iterator_node->value_get() > hi_node->value_get()) + { + error(e.vardec_get(), "iterator initial value is higher than limit"); + } + + type_default(e, e.body_get().type_get()); + } + + void TypeChecker::operator()(ast::IfExp& e) + { + check_type(e.test_get(), "condition", Int::instance()); + + check_types(e, "then chunk return", e.thenclause_get(), "else chunk return", + e.elseclause_get()); + + type_default(e, e.elseclause_get().type_get()); + } + + void TypeChecker::operator()(ast::LetExp& e) + { + e.chunks_get().accept(*this); + + type_default(e, type(e.body_get())); + } + + void TypeChecker::operator()(ast::SeqExp& e) + { + if (e.exps_get().empty()) + { + type_default(e, &Void::instance()); + return; + } + + for (ast::Exp* exp : e.exps_get()) + { + type(*exp); + } + + type_default(e, e.exps_get().back()->type_get()); + } + + void TypeChecker::operator()(ast::WhileExp& e) + { + check_type(e.test_get(), "condition", Int::instance()); + + check_type(e.body_get(), "while body", Void::instance()); + + type_default(e, e.body_get().type_get()); + } + + /*-----------------. + | Visiting /Dec/. | + `-----------------*/ + + /*------------------------. + | Visiting FunctionChunk. | + `------------------------*/ + + void TypeChecker::operator()(ast::FunctionChunk& e) + { + chunk_visit<ast::FunctionDec>(e); + } + + void TypeChecker::operator()(ast::FunctionDec&) + { + // We must not be here. + unreachable(); + } + + // Store the type of this function. + template <> + void TypeChecker::visit_dec_header<ast::FunctionDec>(ast::FunctionDec& e) + { + // FIXME DONE: Some code was deleted here. + // Typing order + const Record* formals = type(e.formals_get()); + + const Type* return_type = + e.result_get() != nullptr ? type(*e.result_get()) : &Void::instance(); + + auto type = std::make_unique<Function>(formals, *return_type); + + type_default(e, type.get()); + created_type_default(e, type.release()); + } + + // Type check this function's body. + template <> + void TypeChecker::visit_dec_body<ast::FunctionDec>(ast::FunctionDec& e) + { + visit_routine_body<Function, ast::FunctionDec>(e); + } + + /*---------------. + | Visit VarDec. | + `---------------*/ + + void TypeChecker::operator()(ast::VarDec& e) + { + // FIXME DONE: Some code was deleted here. + const Type* declared_type = + e.type_name_get() != nullptr ? type(*e.type_name_get()) : nullptr; + + const Type* actual_type = + e.init_get() != nullptr ? type(*e.init_get()) : nullptr; + + if (actual_type != nullptr && actual_type->actual() == Void::instance()) + { + error(e, "VarDec type cannot be void"); + actual_type = &default_type; + } + + if (declared_type == nullptr) + { + if (actual_type != nullptr + && dynamic_cast<const Nil*>(&actual_type->actual()) != nullptr) + { + error(e, "Nil cannot be used with anonymous type declaration"); + } + + type_default(e, actual_type); + } + else + { + if (actual_type != nullptr) + check_types(e, "explicit variable declaration", *declared_type, + "init expression", *actual_type); + + type_default(e, declared_type); + } + } + + /*--------------------. + | Visiting TypeChunk. | + `--------------------*/ + + void TypeChecker::operator()(ast::TypeChunk& e) + { + chunk_visit<ast::TypeDec>(e); + } + + void TypeChecker::operator()(ast::TypeDec&) + { + // We must not be here. + unreachable(); + } + + // Store this type. + template <> void TypeChecker::visit_dec_header<ast::TypeDec>(ast::TypeDec& e) + { + // We only process the head of the type declaration, to set its + // name in E. A declaration has no type in itself; here we store + // the type declared by E. + // FIXME DONE: Some code was deleted here. + if (e.type_get() != nullptr) + { + return; + } + + auto type = std::make_unique<Named>(e.name_get()); + + type_default(e, type.get()); + created_type_default(e, type.release()); + } + + // Bind the type body to its name. + template <> void TypeChecker::visit_dec_body<ast::TypeDec>(ast::TypeDec& e) + { + // FIXME DONE: Some code was deleted here. + const auto named = dynamic_cast<const Named*>(e.type_get()); + + assertion(named != nullptr); + + named->type_set(type(e.ty_get())); + + if (!named->sound()) + { + error(e, "infinite type reference recursion detected"); + } + } + + /*------------------. + | Visiting /Chunk/. | + `------------------*/ + + template <class D> void TypeChecker::chunk_visit(ast::Chunk<D>& e) + { + // FIXME DONE: Some code was deleted here. + if (auto var = dynamic_cast<ast::VarDec*>(e.decs_get().front()); + var != nullptr) + { + type(*var); + return; + } + + for (D* declaration : e) + { + visit_dec_header(*declaration); + } + + for (D* declaration : e) + { + visit_dec_body(*declaration); + } + + for (D* declaration : e) + { + if (const auto maybe_main = + dynamic_cast<ast::FunctionDec*>(declaration); + maybe_main != nullptr && maybe_main->name_get() == "_main") + type_main(maybe_main); + } + } + + void TypeChecker::type_main(ast::FunctionDec* const e) + { + if (!e->formals_get().decs_get().empty()) + { + error(*e, "_main function should have no arguments"); + } + + check_type(*e->body_get(), "_main return type", Void::instance()); + } + + /*-------------. + | Visit /Ty/. | + `-------------*/ + + void TypeChecker::operator()(ast::NameTy& e) + { + // FIXME DONE: Some code was deleted here (Recognize user defined types, and built-in types). + const Type* type = e.name_get() == "int" ? &Int::instance() + : e.name_get() == "string" ? &String::instance() + : this->type(*e.def_get()); + + auto named = std::make_unique<Named>(e.name_get(), type); + + type_default(e, named.get()); + created_type_default(e, named.release()); + } + + void TypeChecker::operator()(ast::RecordTy& e) + { + // FIXME DONE: Some code was deleted here. + std::unique_ptr<const Record> record(type(e.fields_get())); + + type_default(e, record.get()); + created_type_default(e, record.release()); + } + + void TypeChecker::operator()(ast::ArrayTy& e) + { + // FIXME DONE: Some code was deleted here. + auto array = std::make_unique<const Array>(type(e.base_type_get())); + + type_default(e, array.get()); + created_type_default(e, array.release()); + } + +} // namespace type diff --git a/tiger-compiler/src/type/type-checker.hh b/tiger-compiler/src/type/type-checker.hh new file mode 100644 index 0000000..789a31f --- /dev/null +++ b/tiger-compiler/src/type/type-checker.hh @@ -0,0 +1,279 @@ +/** + ** \file type/type-checker.hh + ** \brief Perform type checking and other semantical checks. + */ + +#pragma once + +#include <cassert> +#include <string> + +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> +#include <misc/error.hh> +#include <misc/set.hh> +#include <type/fwd.hh> + +namespace type +{ + class TypeChecker + : public ast::DefaultVisitor + , public ast::NonObjectVisitor + , public ast::NonAssertVisitor + { + public: + using super_type = ast::DefaultVisitor; + using super_type::operator(); + + /// Construction. + TypeChecker(); + /// The error handler. + const misc::error& error_get() const; + + protected: + /// Run this visitor on \a e, and return its type. + /// + /// Note that it is also guaranteed that \a type_ is set to this type. + /// More generally, using \a type allows to avoid calling \a accept + /// directly. + const Type* type(ast::Typable& e); + const Record* type(const ast::fields_type& e); + const Record* type(const ast::VarChunk& e); + + // ------------------ // + // Helping routines. // + // ------------------ // + protected: + /// \name Error handling. + /// \{ + + /// Report an error. + void error(const ast::Ast& ast, const std::string& msg); + + /// Report an error. + /// + /// \param ast the node whose location is used in the error message + /// \param msg the error message + /// \param exp the culprit. It must support << output + template <typename T> + void error(const ast::Ast& ast, const std::string& msg, const T& exp); + + /// Report an error, and recover from it. + /// + /// \param loc the node whose location is used in the error message + /// and whose type is set to nil as an attempt to + /// recover. + /// \param msg the error message + /// \param exp the culprit. It must support << output + template <typename T, typename U> + void error_and_recover(T& loc, const std::string& msg, const U& exp); + + /// Report a type_mismatch between two entities. + /// \param ast the guilty node, used to report the location. + /// \param exp1 the string denoting the first exp. + /// \param type1 its type + /// \param exp2 similarly + /// \param type2 similarly + void type_mismatch(const ast::Ast& ast, + const std::string& exp1, + const Type& type1, + const std::string& exp2, + const Type& type2); + /// \} + + /// \name Checking types. + /// \{ + + /// \brief Check the type of an Exp. + /// + /// \param e the expression/declaration to check + /// \param s the string to refer to \a e in error messages + /// (e.g., "index", "left operand" etc.). + /// \param t the expected type + /// + /// On failure, report the error, and set the type of \a e to \a t. + /// This should be used for `ast::Exp` and `ast::Dec` nodes. + template <typename NodeType> + void check_type(NodeType& e, const std::string& s, const Type& t); + + /** \brief Check the type compatibility. + ** + ** An error message is output if types are incompatible. + ** + ** \param loc a node which location is used to report errors + ** \param exp1 the syntactic category of the first entity, + ** (used in error messages) + ** \param type1 the first type + ** \param exp2 same as above + ** \param type2 same as above + **/ + void check_types(const ast::Ast& loc, + const std::string& exp1, + const Type& type1, + const std::string& exp2, + const Type& type2); + void check_types(const ast::Ast& loc, + const std::string& exp1, + ast::Typable& type1, + const std::string& exp2, + ast::Typable& type2); + /// \} + + protected: + /// \name Setting types. + /// \{ + + /// Set the type of a node, if it isn't already set + /// (an existing type won't be overwritten). + /// + /// \param e the bound node + /// \param type the type + template <typename NodeType> + void type_default(NodeType& e, const type::Type* type); + + /// Same as type_default, but for a created type. + template <typename NodeType> + void created_type_default(NodeType& e, const type::Type* type); + + /// Set the type of a node (overwriting allowed). + /// + /// \param e the bound node + /// \param type the type + template <typename NodeType> + void type_set(NodeType& e, const type::Type* type); + /// \} + + // ------------------------- // + // The core of the visitor. // + // ------------------------- // + + // ---------------- // + // Visiting /Var/. // + // ---------------- // + + void operator()(ast::SimpleVar& e) override; + // FIXME DONE: Some code was deleted here (Other Var nodes). + void operator()(ast::FieldVar& e) override; + void operator()(ast::SubscriptVar& e) override; + + // ---------------- // + // Visiting /Exp/. // + // ---------------- // + + // Literals. + void operator()(ast::NilExp&) override; + void operator()(ast::IntExp&) override; + void operator()(ast::StringExp&) override; + + // Complex values. + void operator()(ast::RecordExp& e) override; + void operator()(ast::OpExp& e) override; + // FIXME DONE: Some code was deleted here (Other Exp nodes). + void operator()(ast::ArrayExp& e) override; + void operator()(ast::AssignExp& e) override; + void operator()(ast::BreakExp& e) override; + // I don't put MethodCallExp because it should be managed by CallExp + void operator()(ast::CallExp& e) override; + void operator()(ast::CastExp& e) override; + void operator()(ast::ForExp& e) override; + void operator()(ast::IfExp& e) override; + void operator()(ast::LetExp& e) override; + void operator()(ast::SeqExp& e) override; + void operator()(ast::WhileExp& e) override; + + // ---------------- // + // Visiting /Dec/. // + // ---------------- // + + /// \name Type and Function declarations. + /// \{ + + /// Be sure to read the documentation of + /// bind::Binder::chunk_visit. + /// + /// When type-checking a function (or a type) we need two + /// traversals: one to register the function's type (to enable + /// mutual recursions) and a second one to check the bodies. + + /// This is why there are actually three methods: chunk_visit, + /// visit_dec_header, and visit_dec_body. It turns out that + /// chunk_visit can be written for both functions and types, but of + /// course, traversals of headers and bodies differ. + + /// Check a set of definitions: unique names, browse headers, then + /// bodies. + template <class D> void chunk_visit(ast::Chunk<D>& e); + + /// Check a Function or Type declaration header. + template <class D> void visit_dec_header(D& e); + + /// Check a Function or Type declaration body. + template <class D> void visit_dec_body(D& e); + + /// Generic type-checking of a routine's body. + template <typename Routine_Type, typename Routine_Node> + void visit_routine_body(Routine_Node& e); + + /// Visit a chunk of function declarations. + void operator()(ast::FunctionChunk& e) override; + /// No longer used. + void operator()(ast::FunctionDec&) override; + + /// Visit a chunk of type declarations. + void operator()(ast::TypeChunk& e) override; + /// No longer used. + void operator()(ast::TypeDec&) override; + /// \} + + /// Visit a single Variable Declaration. + void operator()(ast::VarDec& e) override; + + // --------------- // + // Visiting /Ty/. // + // --------------- // + + void operator()(ast::NameTy& e) override; + void operator()(ast::RecordTy& e) override; + void operator()(ast::ArrayTy& e) override; + + protected: + /// Error handler. + misc::error error_; + /// Set of for index variable definitions, which are read only. + misc::set<const ast::VarDec*> var_read_only_; + + // ------------------- // + // Custom attributes. // + // ------------------- // + + // Default type to use when a node is badly typed + const Type& default_type = Int::instance(); + + // ------------------- // + // Custom methods. // + // ------------------- // + + /** + * @brief Check that the provided FunctionDec represents a valid main_ + * function, typing-wise. + * @param e Node to check. It is assumed it represents a main_ function + */ + void type_main(ast::FunctionDec* e); + }; + + /// Visit the lhs of an ast::FunctionDec. + template <> + void TypeChecker::visit_dec_header<ast::FunctionDec>(ast::FunctionDec& e); + /// Visit the rhs of an ast::FunctionDec. + template <> + void TypeChecker::visit_dec_body<ast::FunctionDec>(ast::FunctionDec& e); + + /// Visit the lhs of an ast::TypeDec. + template <> void TypeChecker::visit_dec_header<ast::TypeDec>(ast::TypeDec& e); + /// Visit the rhs of an ast::TypeDec. + template <> void TypeChecker::visit_dec_body<ast::TypeDec>(ast::TypeDec& e); + +} // namespace type + +#include <type/type-checker.hxx> diff --git a/tiger-compiler/src/type/type-checker.hxx b/tiger-compiler/src/type/type-checker.hxx new file mode 100644 index 0000000..bcdf348 --- /dev/null +++ b/tiger-compiler/src/type/type-checker.hxx @@ -0,0 +1,127 @@ +/** + ** \file type/type-checker.hxx + ** \brief Inline methods of type::TypeChecker. + */ + +#pragma once + +#include <ast/all.hh> +#include <type/pretty-printer.hh> +#include <type/type-checker.hh> +#include <type/types.hh> + +namespace type +{ + namespace + { + const Nil nil_error_instance{}; + + } + + /*----------------. + | Setting types. | + `----------------*/ + + template <typename NodeType> + void TypeChecker::type_default(NodeType& e, const type::Type* type) + { + // FIXME DONE: Some code was deleted here. + // We can check here to be sure that the type we use really is a native Tiger type + const auto casted = dynamic_cast<ast::Typable*>(&e); + + assertion(casted != nullptr); + + if (casted->type_get() != nullptr) + { + return; + } + + type_set(e, type); + } + + template <typename NodeType> + void TypeChecker::created_type_default(NodeType& e, const type::Type* type) + { + // FIXME DONE: Some code was deleted here. + const auto casted = dynamic_cast<ast::TypeConstructor*>(&e); + + assertion(casted != nullptr); + + if (casted->created_type_get() != nullptr) + { + return; + } + + casted->created_type_set(type); + } + + template <typename NodeType> + void TypeChecker::type_set(NodeType& e, const type::Type* type) + { + // FIXME DONE: Some code was deleted here (Basically e.type_set(type)). + const auto casted = dynamic_cast<ast::Typable*>(&e); + + assertion(casted != nullptr); + + casted->type_set(type); + } + + /*-----------------. + | Error handling. | + `-----------------*/ + + template <typename T> + void + TypeChecker::error(const ast::Ast& ast, const std::string& msg, const T& exp) + { + error_ << misc::error::error_type::type << ast.location_get() << ": " << msg + << ": " << exp << std::endl; + } + + template <typename T, typename U> + void + TypeChecker::error_and_recover(T& loc, const std::string& msg, const U& exp) + { + error(loc, msg, exp); + loc.type_set(&nil_error_instance); + } + + template <typename NodeType> + void TypeChecker::check_type(NodeType& e, + const std::string& s, + const Type& t) + { + // FIXME DONE: Some code was deleted here. + const auto casted = dynamic_cast<ast::Typable*>(&e); + + assertion(casted != nullptr); + + if (type(*casted)->actual() == t) + return; + + type_mismatch(e, s, *casted->type_get(), "expected", t); + } + + /*------------. + | Functions. | + `------------*/ + + template <typename Routine_Type, typename Routine_Node> + void TypeChecker::visit_routine_body(Routine_Node& e) + { + // FIXME DONE: Some code was deleted here. + if (e.body_get() == nullptr) + return; + type(*e.body_get()); + if (e.type_get()) + { + auto rt = dynamic_cast<const Routine_Type*>(e.type_get()); + check_types(e, "routine", rt->result_get(), "body", *e.body_get()->type_get()); + } + else + { + created_type_default(e, e.body_get()->type_get()); + } + } + +} // namespace type diff --git a/tiger-compiler/src/type/type.cc b/tiger-compiler/src/type/type.cc new file mode 100644 index 0000000..e0e448d --- /dev/null +++ b/tiger-compiler/src/type/type.cc @@ -0,0 +1,18 @@ +/** + ** \file type/type.cc + ** \brief Implementation for type/type.hh. + */ + +#include <ostream> + +#include <type/type.hh> + +namespace type +{ + const Type& Type::actual() const { return *this; } + + bool Type::compatible_with(const Type& other) const { return *this == other; } + + const misc::xalloc<bool> hide_actual_types; + +} // namespace type diff --git a/tiger-compiler/src/type/type.hh b/tiger-compiler/src/type/type.hh new file mode 100644 index 0000000..a5f9777 --- /dev/null +++ b/tiger-compiler/src/type/type.hh @@ -0,0 +1,66 @@ +/** + ** \file type/type.hh + ** \brief The class Type. + */ +#pragma once + +#include <misc/xalloc.hh> +#include <type/fwd.hh> + +namespace type +{ + /// Abstract a type. + class Type + { + /** \name Ctor & dtor. + ** \{ */ + public: + /// Destroys a Type. + virtual ~Type() = default; + /** \} */ + + /// \name Visitors entry point. + /// \{ */ + /// Accept a const visitor \a v. + virtual void accept(ConstVisitor& v) const = 0; + /// Accept a non-const visitor \a v. + virtual void accept(Visitor& v) = 0; + /// \} + + /** \name Accessors. + ** \{ */ + /// Return the actual type held by THIS. + virtual const Type& actual() const; + /** \} */ + + /** \brief Whether two types are "compatible". + ** + ** I.e., whether "a = b", "a <> b", "a := b" are correctly typed + ** with a of type \a this, and b of type \a other). + ** + ** By default two types are compatible (in the sense of "=" and "<>", + ** not w.r.t. an order) only when they are equal. + ** + ** In the case of assignment, "rec := nil" is valid, but "nil := rec" + ** is not, which suggest that we should have a non commutative + ** assignment specific compatibility check. But since "nil := ..." + ** is incorrect syntactically, that is not needed. + */ + virtual bool compatible_with(const Type& other) const; + }; + + /** \brief Compare two Type s. + ** + ** Return true if \a a and \a b are equivalent Tiger Types. E.g., + ** if \a a and \a b are different but point to the same type, then + ** return true. */ + bool operator==(const Type& lhs, const Type& rhs); + /// !(a == b). + bool operator!=(const Type& lhs, const Type& rhs); + + /// Hide actual types? (i.e., print only the surface type?) + extern const misc::xalloc<bool> hide_actual_types; + +} // namespace type + +#include <type/type.hxx> diff --git a/tiger-compiler/src/type/type.hxx b/tiger-compiler/src/type/type.hxx new file mode 100644 index 0000000..392c854 --- /dev/null +++ b/tiger-compiler/src/type/type.hxx @@ -0,0 +1,37 @@ +/** + ** \file type/type.hxx + ** \brief Inline methods for type::Type. + */ +#pragma once + +#include <misc/contract.hh> +#include <type/type.hh> + +namespace type +{ + inline bool operator==(const Type& lhs, const Type& rhs) + { + // FIXME DONE: Some code was deleted here. + const Type* lhs_primal = &lhs.actual(); + const Type* rhs_primal = &rhs.actual(); + + while (&rhs_primal->actual() != rhs_primal && rhs_primal != lhs_primal) + { + rhs_primal = &rhs_primal->actual(); + } + + while (&lhs_primal->actual() != lhs_primal && lhs_primal != rhs_primal) + { + lhs_primal = &lhs_primal->actual(); + } + + return lhs_primal == rhs_primal; + } + + inline bool operator!=(const Type& lhs, const Type& rhs) + { + // FIXME DONE: Some code was deleted here. + return !(lhs == rhs); + } + +} // namespace type diff --git a/tiger-compiler/src/type/types.hh b/tiger-compiler/src/type/types.hh new file mode 100644 index 0000000..b0d3771 --- /dev/null +++ b/tiger-compiler/src/type/types.hh @@ -0,0 +1,18 @@ +/** + ** \file type/types.hh + ** \brief Include all exported headers. + */ + +#pragma once + +#include <type/array.hh> +// FIXME: We should include "attribute.hh" and "class.hh" as well, but +// currently that would break the compilation (because of recursive +// dependencies). Investigate and fix this. +#include <type/builtin-types.hh> +#include <type/field.hh> +#include <type/function.hh> +#include <type/named.hh> +#include <type/nil.hh> +#include <type/record.hh> +#include <type/type.hh> diff --git a/tiger-compiler/src/type/visitor.hh b/tiger-compiler/src/type/visitor.hh new file mode 100644 index 0000000..4e78ec0 --- /dev/null +++ b/tiger-compiler/src/type/visitor.hh @@ -0,0 +1,57 @@ +/** + ** \file type/visitor.hh + ** \brief Definition of type::Visitor. + */ +#pragma once + +#include <misc/select-const.hh> +#include <type/fwd.hh> + +namespace type +{ + /** \brief Root class of all Type visitors. + ** + ** GenVisitor<CONSTIFY> is the root class of all Type visitors. */ + template <template <typename> class Const> class GenVisitor + { + /** \name Ctor & dtor. + ** \{ */ + public: + /// Convenient abbreviation. + template <typename T> using const_t = typename Const<T>::type; + + /// Destroy a GenVisitor. + virtual ~GenVisitor(); + /** \} */ + + /// The entry point: visit \a e. + virtual void operator()(const_t<Type>& e); + virtual void operator()(const_t<Nil>&) = 0; + virtual void operator()(const_t<Void>&) = 0; + virtual void operator()(const_t<Int>&) = 0; + virtual void operator()(const_t<String>&) = 0; + virtual void operator()(const_t<Named>&) = 0; + virtual void operator()(const_t<Array>&) = 0; + virtual void operator()(const_t<Record>&) = 0; + virtual void operator()(const_t<Class>&) = 0; + virtual void operator()(const_t<Function>&) = 0; + virtual void operator()(const_t<Method>&) = 0; + + /// Helper to visit nodes manipulated via a pointer. + template <class E> void operator()(E* e); + }; + + /// Shorthand for a const visitor. + using ConstVisitor = GenVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + using Visitor = GenVisitor<misc::id_traits>; + +#ifdef SWIG + /// Shorthand for a const visitor. + %template(GenConstVisitor) GenVisitor<misc::constify_traits>; + /// Shorthand for a non const visitor. + %template(GenVisitor) GenVisitor<misc::id_traits>; +#endif +} // namespace type + +#include <type/visitor.hxx> diff --git a/tiger-compiler/src/type/visitor.hxx b/tiger-compiler/src/type/visitor.hxx new file mode 100644 index 0000000..93a0f9b --- /dev/null +++ b/tiger-compiler/src/type/visitor.hxx @@ -0,0 +1,29 @@ +/** + ** \file type/visitor.hxx + ** \brief Definition of type::Visitor. + */ + +#pragma once + +#include <type/type.hh> +#include <type/visitor.hh> + +namespace type +{ + template <template <typename> class Const> + GenVisitor<Const>::~GenVisitor() = default; + + template <template <typename> class Const> + void GenVisitor<Const>::operator()(const_t<Type>& e) + { + e.accept(*this); + } + + template <template <typename> class Const> + template <class E> + void GenVisitor<Const>::operator()(E* e) + { + e->accept(*this); + } + +} // namespace type diff --git a/tiger-compiler/src/version.cc.in b/tiger-compiler/src/version.cc.in new file mode 100644 index 0000000..24d699c --- /dev/null +++ b/tiger-compiler/src/version.cc.in @@ -0,0 +1,34 @@ +/** -*- C++ -*- + ** \file version.cc + ** \brief Common definitions. + */ + +#include <config.h> +#include <authors.h> + + +/*-------------------. +| Program identity. | +`-------------------*/ + +/** Name of this program. + +Be sure to have something usable even before main is started, +in case some ctor of a static object need to issue an error +message. */ +const char *program_name = PACKAGE_NAME; + +// Version string of this program. +const char *program_version = +"tc (" PACKAGE_STRING ")\n" +"@ID@\n" +"\n" +PACKAGE_SHORT_AUTHORS; + +// Bug report address of this program. +const char *program_bug_address = PACKAGE_BUGREPORT; + +/// Describe program and accepted arguments. +const char *program_doc = + "Tiger Compiler, Copyright (C) 2004-2021 LRDE."; +const char *program_args_doc = "INPUT-FILE"; diff --git a/tiger-compiler/src/version.hh b/tiger-compiler/src/version.hh new file mode 100644 index 0000000..c535db1 --- /dev/null +++ b/tiger-compiler/src/version.hh @@ -0,0 +1,23 @@ +/** + ** \file src/version.hh + ** \brief Version definitions. + */ + +#pragma once + +/// \name Program identity. +/// \{ +/// Name of this program. +extern const char* program_name; + +/// Version string of this program. +extern const char* program_version; + +/// Bug report address of this program. +extern const char* program_bug_address; + +/// Describe program and accepted arguments. +extern const char* program_doc; +extern const char* program_args_doc; + +/// \} |
