summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/object/desugar-visitor.cc
diff options
context:
space:
mode:
authorMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
committerMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
commit967be9e750221ab2ab783f95df79bb26d290a45e (patch)
tree6802900a5e975f9f68b169f0f503f040056d6952 /tiger-compiler/src/object/desugar-visitor.cc
add: added projectsHEADmain
Diffstat (limited to 'tiger-compiler/src/object/desugar-visitor.cc')
-rw-r--r--tiger-compiler/src/object/desugar-visitor.cc1127
1 files changed, 1127 insertions, 0 deletions
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