import symbol:* import tree:* import vector:* import stack:* import map:* import util:* import string:* import mem:* import io:* import os:* import importer:* import ast_nodes:* import type:* adt search_type { none, function: vector<*type> } obj ast_transformation (Object) { var ast_to_syntax: map<*ast_node, *tree> var type_def_to_this: map<*ast_node, *ast_node> var fourth_pass_worklist: queue<*ast_node> fun construct(): *ast_transformation { ast_to_syntax.construct() type_def_to_this.construct() fourth_pass_worklist.construct() return this } fun copy_construct(old: *ast_transformation) { ast_to_syntax.copy_construct(&old->ast_to_syntax) type_def_to_this.copy_construct(&old->type_def_to_this) fourth_pass_worklist.copy_construct(&old->fourth_pass_worklist) } fun operator=(old: ref ast_transformation) { destruct() copy_construct(&old) } fun destruct() { ast_to_syntax.destruct() type_def_to_this.destruct() fourth_pass_worklist.destruct() } // first pass defines all type_defs (objects and aliases), ADTs, and top-level if-comps/passthroughs fun first_pass(file_name: string, parse_tree: *tree, importer: *importer): pair<*ast_node, vector<*ast_node>> { var translation_unit = ast_translation_unit_ptr(file_name) parse_tree->children.for_each(fun(child: *tree) { if (child->data.name == "type_def") { translation_unit->translation_unit.children.add(first_pass_type_def(child, translation_unit, false)) } else if (child->data.name == "adt_def") { var name = concat_symbol_tree(get_node("identifier", child)) var adt_def_node = ast_adt_def_ptr(name) adt_def_node->adt_def.self_type = type_ptr(adt_def_node, set(string("Object"))) translation_unit->translation_unit.children.add(adt_def_node) ast_to_syntax.set(adt_def_node, child) add_to_scope("~enclosing_scope", translation_unit, adt_def_node) add_to_scope(name, adt_def_node, translation_unit) } else if (child->data.name == "if_comp") { var if_comp_node = transform_if_comp(child, translation_unit) translation_unit->translation_unit.children.add(if_comp_node) ast_to_syntax.set(if_comp_node, child) } }) // now do all imports // re return a vector of them so importer can fix them (and our translation unit scope) // up with actual pointers to the other ASTs var imports = vector<*ast_node>() parse_tree->children.for_each(fun(child: *tree) { if (child->data.name == "import") { var import_identifier_children = get_nodes("identifier", child) var name = concat_symbol_tree(import_identifier_children[0]) var import_node = ast_import_ptr(name, translation_unit) imports.add(import_node) translation_unit->translation_unit.children.add(import_node) ast_to_syntax.set(import_node, child) add_to_scope("~enclosing_scope", translation_unit, import_node) import_node->import.imported = from_vector(import_identifier_children.slice(1,-1).map(fun(ident: *tree):string return concat_symbol_tree(ident);)) if (get_node("\"\\*\"", child)) import_node->import.starred = true } }) return make_pair(translation_unit, imports) } fun transform_traits(traits_node: *tree): set { if (!traits_node) return set() return from_vector(get_nodes("scoped_identifier", traits_node).map(fun(s: *tree): string return concat_symbol_tree(s);)) } fun first_pass_type_def(child: *tree, scope: *ast_node, instantiate_template: bool): *ast_node { var name = concat_symbol_tree(get_node("identifier", child)) var template_dec = get_node("template_dec", child) if (template_dec && !instantiate_template) { var template_types = vector() var template_type_replacements = map() // XXX add traits get_nodes("template_param", template_dec).for_each(fun(template_param: *tree) { template_types.add(concat_symbol_tree(get_node("identifier", template_param))) template_type_replacements.set(template_types.last(), type_ptr(transform_traits(get_node("traits", template_param)))) }) var template = ast_template_ptr(name, child, template_types, template_type_replacements, false) add_to_scope("~enclosing_scope", scope, template) add_to_scope(name, template, scope) return template } else { // pass in whether or not this is a union var type_def_node = ast_type_def_ptr(name, concat_symbol_tree(get_node("obj_nonterm", child)) == "uni") /*var type_def_node = ast_type_def_ptr(name, false)*/ /*var type_def_node = ast_type_def_ptr(name)*/ type_def_node->type_def.self_type = type_ptr(type_def_node, transform_traits(get_node("traits", child))) ast_to_syntax.set(type_def_node, child) add_to_scope("~enclosing_scope", scope, type_def_node) add_to_scope(name, type_def_node, scope) return type_def_node } } // defines inside of objects + ADTs, outside declaration statements, and function prototypes fun second_pass(parse_tree: *tree, translation_unit: *ast_node) { /*println(string("Second Pass for ") + translation_unit->translation_unit.name)*/ // we go through the parse tree for getting functions, but we're going through the ast for the things we've already set up and using the ast_to_syntax map parse_tree->children.for_each(fun(child: *tree) { if (child->data.name == "function") { // also handles templated function var function_node = second_pass_function(child, translation_unit, map(), true) translation_unit->translation_unit.children.add(function_node) ast_to_syntax.set(function_node, child) } else if (child->data.name == "declaration_statement") { // second pass declaration can actually just call a normal transform (but maybe should be it's own method to do so because typedef has to do it too?)... translation_unit->translation_unit.children.add(transform_declaration_statement(child, translation_unit, map())) } else if (child->data.name == "compiler_intrinsic") { translation_unit->translation_unit.children.add(transform_compiler_intrinsic(child, translation_unit, map())) } }) // work on the ones already started translation_unit->translation_unit.children.for_each(fun(node: *ast_node) { match(*node) { ast_node::type_def(backing) second_pass_type_def(ast_to_syntax[node], node, translation_unit, map()) ast_node::adt_def(backing) second_pass_adt_def(ast_to_syntax[node], node, translation_unit, map()) } }) } fun second_pass_type_def(type_def_syntax: *tree, node: *ast_node, scope: *ast_node, template_replacements: map) { type_def_syntax->children.for_each(fun(child: *tree) { if (child->data.name == "declaration_statement") { var declaration_node = transform_declaration_statement(child, node, template_replacements) node->type_def.variables.add(declaration_node) ast_to_syntax.set(declaration_node, child) } else if (child->data.name == "function") { // again, also handles templates var function_node = second_pass_function(child, node, template_replacements, true) node->type_def.methods.add(function_node) ast_to_syntax.set(function_node, child) } }) } fun second_pass_adt_def(adt_def_syntax: *tree, node: *ast_node, scope: *ast_node, template_replacements: map) { get_nodes("adt_option", adt_def_syntax).for_each(fun(adt_option: *tree) { var ident_type: *type var type_syntax = get_node("type", adt_option) if (type_syntax) ident_type = transform_type(type_syntax, scope, template_replacements) else ident_type = type_ptr(base_type::no_type_adt_option()) var option_name = concat_symbol_tree(get_node("identifier", adt_option)) var identifier = ast_identifier_ptr(option_name, ident_type, node) node->adt_def.options.add(identifier) // we add the identifier first so that it's found before the function when doing option.thingy add_to_scope(option_name, identifier, node) add_to_scope("~enclosing_scope", node, identifier) ast_to_syntax.set(identifier, adt_option) var function_node = null() if (type_syntax) { var identifier_param = ast_identifier_ptr(option_name, ident_type, node) function_node = ast_function_ptr(option_name, type_ptr(vector(get_ast_type(identifier_param)), node->adt_def.self_type), vector(identifier_param), false) } else { function_node = ast_function_ptr(option_name, type_ptr(vector<*type>(), node->adt_def.self_type), vector<*ast_node>(), false) } add_to_scope(option_name, function_node, node) add_to_scope("~enclosing_scope", node, function_node) node->adt_def.option_funcs.add(function_node) }) // we fake operator==, operator!=, copy_construct, operator=, and destruct like so // note they don't even have real parameters (but the type has them correctly) or bodies // I'm not sure this is the correct enclosing scope, but I'm not sure how to do it with the function either var equals_param = ast_identifier_ptr(string("in"), node->adt_def.self_type->clone_with_indirection(0,true), node) var nequals_param = ast_identifier_ptr(string("in"), node->adt_def.self_type->clone_with_indirection(0,true), node) var copy_construct_param = ast_identifier_ptr(string("in"), node->adt_def.self_type->clone_with_indirection(1,false), node) var assign_param = ast_identifier_ptr(string("in"), node->adt_def.self_type->clone_with_indirection(0,true), node) vector( make_pair("operator==", ast_function_ptr(string("operator=="), type_ptr(vector(equals_param->identifier.type), type_ptr(base_type::boolean())), vector(equals_param), false)), make_pair("operator!=", ast_function_ptr(string("operator!="), type_ptr(vector(nequals_param->identifier.type), type_ptr(base_type::boolean())), vector(nequals_param), false)), make_pair("construct", ast_function_ptr(string("construct"), type_ptr(vector<*type>(), node->adt_def.self_type->clone_with_increased_indirection()), vector<*ast_node>(), false)), make_pair("copy_construct", ast_function_ptr(string("copy_construct"), type_ptr(vector(copy_construct_param->identifier.type), type_ptr(base_type::void_return())), vector(copy_construct_param), false)), make_pair("operator=", ast_function_ptr(string("operator="), type_ptr(vector(assign_param->identifier.type), type_ptr(base_type::void_return())), vector(assign_param), false)), make_pair("destruct", ast_function_ptr(string("destruct"), type_ptr(vector<*type>(), type_ptr(base_type::void_return())), vector<*ast_node>(), false)) ).for_each(fun(func_pair: pair<*char, *ast_node>) { node->adt_def.regular_funcs.add(func_pair.second) add_to_scope(string(func_pair.first), func_pair.second, node) add_to_scope("~enclosing_scope", node, func_pair.second) }) } fun second_pass_function(node: *tree, scope: *ast_node, template_replacements: map, do_raw_template: bool): *ast_node { var func_identifier_node = get_node("func_identifier", node) var function_name = string("lambda") if (func_identifier_node) function_name = concat_symbol_tree(func_identifier_node) var template_dec = get_node("template_dec", node) if (do_raw_template && template_dec) { var template_types = vector() var template_type_replacements = map() get_nodes("template_param", template_dec).for_each(fun(template_param: *tree) { template_types.add(concat_symbol_tree(get_node("identifier", template_param))) template_type_replacements.set(template_types.last(), type_ptr(transform_traits(get_node("traits", template_param)))) }) /*template_type_replacements.for_each(fun(key: string, value: *type) println(string("MAP: ") + key + " : " + value->to_string());)*/ /*println("MAP DONE")*/ var template = ast_template_ptr(function_name, node, template_types, template_type_replacements, true) add_to_scope(function_name, template, scope) add_to_scope("~enclosing_scope", scope, template) return template } // check to see if it is a template // figure out return type var typed_return_node = get_node("typed_return", node) // darn no ternary yet var return_type = null() if (typed_return_node) return_type = transform_type(get_node("type", typed_return_node), scope, template_replacements) else return_type = type_ptr(base_type::void_return()) if (return_type->is_none()) error(node, "return type none") // transform parameters var parameters = vector<*ast_node>() get_nodes("typed_parameter", node).for_each(fun(child: *tree) { // note the temporary null() which gets replaced below, as the dependency is circular var param_type = transform_type(get_node("type", child), scope, template_replacements) if (param_type->is_none()) error(child, "parameter type none") parameters.add(ast_identifier_ptr(concat_symbol_tree(get_node("identifier", child)), param_type, null())) }) var is_variadic = get_node("\"...\"", node) != null>() // figure out function type and make function_node var function_node = ast_function_ptr(function_name, type_ptr(parameters.map(fun(parameter: *ast_node): *type return parameter->identifier.type;), return_type, 0, false, is_variadic), parameters, get_node("\"ext\"", node) != null>(), is_variadic) // fix up the enclosing_scope's parameters.for_each(fun(n: *ast_node) n->identifier.enclosing_scope = function_node;) // add to scope add_to_scope(function_name, function_node, scope) add_to_scope("~enclosing_scope", scope, function_node) // add parameters to scope of function parameters.for_each(fun(parameter: *ast_node) add_to_scope(parameter->identifier.name, parameter, function_node);) return function_node } // The third pass finishes up by doing all function bodies (top level and methods in objects) fun third_pass(parse_tree: *tree, translation_unit: *ast_node) { /*println(string("Third Pass for ") + translation_unit->translation_unit.name)*/ translation_unit->translation_unit.children.for_each(fun(node: *ast_node) { match(*node) { ast_node::type_def(backing) { // make sure not a template? or the method not a template? // also same body problem as below if (!is_template(node)) { node->type_def.methods.for_each(fun(method: *ast_node) { if (!is_template(method)) method->function.body_statement = transform_statement(get_node("statement", ast_to_syntax[method]), method, map()) }) } } ast_node::function(backing) { // make sure not a template // huh, I guess I can't actually assign to the backing. // This is actually a little bit of a problem, maybe these should be pointers also. All the pointers! if (!node->function.is_extern) node->function.body_statement = transform_statement(get_node("statement", ast_to_syntax[node]), node, map()) } } }) } // The fourth pass generates the class templates that have not yet been generated in a worklist loop fun fourth_pass(parse_tree: *tree, translation_unit: *ast_node) { /*println(string("Fourth Pass for ") + translation_unit->translation_unit.name)*/ while (!fourth_pass_worklist.empty()) { var partially_inst_type_def = fourth_pass_worklist.pop() partially_inst_type_def->type_def.methods.for_each(fun(method: *ast_node) { // if this is a templated method, we've either instantiatedit if we need it or not if we didn't, so we don't do anything with it here if (is_template(method)) return var template = partially_inst_type_def->type_def.scope[string("~enclosing_scope")][0] var template_types = template->template.template_types var real_types = template->template.instantiated_map.reverse_get(partially_inst_type_def) var replacements = map() for (var i = 0; i < template_types.size; i++;) replacements.set(template_types[i], real_types[i].clone()) method->function.body_statement = transform_statement(get_node("statement", ast_to_syntax[method]), method, replacements) }) } } fun transform_type(node: *tree, scope: *ast_node, template_replacements: map): *type { // check for references and step down // always get to pre-reffed level var is_ref = get_node("\"ref\"", node) != null>() var real_node = get_node("pre_reffed", node) // check for indirection and step down var indirection = 0 while (get_node("pre_reffed", real_node)) { real_node = get_node("pre_reffed", real_node) indirection++ } var template_inst = get_node("template_inst", real_node) if (template_inst) { var name = concat_symbol_tree(get_node("scoped_identifier", real_node)) /*println(string("trying to instantiate a template object: ") + name)*/ var real_types = get_nodes("type", template_inst).map(fun(t: *tree): *type return transform_type(t, scope, template_replacements);) var real_types_deref = real_types.map(fun(t:*type):type return *t;) var results = scope_lookup(name, scope) var fitting_types = vector>() for (var i = 0; i < results.size; i++;) { if (!is_template(results[i]) || results[i]->template.is_function) continue /*println(to_string(i) + " is an object template!")*/ var template_types = results[i]->template.template_types var template_type_replacements = results[i]->template.template_type_replacements if (template_types.size != real_types.size) continue var num_satisfied_traits = 0 var satisfied_traits = true template_type_replacements.for_each(fun(key: string, value: *type) num_satisfied_traits += value->traits.size();) /*println("FOR FIND OR INSTATINTATE PREEEE")*/ /*template_type_replacements.for_each(fun(key: string, value: *type) println(string("MAP: ") + key + " : " + value->to_string());)*/ /*println("MAP DONE")*/ for (var j = 0; j < template_types.size; j++;) { satisfied_traits = satisfied_traits && real_types[j]->traits.contains(template_type_replacements[template_types[j]]->traits) && (real_types[j]->indirection == 0 || template_type_replacements[template_types[j]]->traits.size() == 0) template_type_replacements[template_types[j]] = real_types[j] /*println("Just made")*/ /*println(template_types[j])*/ /*println("equal to")*/ /*println(real_types[j]->to_string())*/ } if (!satisfied_traits) { /*println(name + " did not satisfy traits!")*/ continue } /*println("FOR FIND OR INSTATINTATE")*/ /*template_type_replacements.for_each(fun(key: string, value: *type) println(string("MAP: ") + key + " : " + value->to_string());)*/ /*println("MAP DONE")*/ var inst_type = null() // check if already instantiated if (results[i]->template.instantiated_map.contains_key(real_types_deref)) { /*println("USING CACHED TEMPLATE OBJECT")*/ inst_type = results[i]->template.instantiated_map[real_types_deref] } else { /*print("Not using cached template - was looking for:\n\t\t")*/ var typeStr = string() real_types_deref.for_each(fun(t: type) typeStr += t.to_string(false) + " ";) /*print(typeStr)*/ /*println("\ninstead, only had:")*/ results[i]->template.instantiated_map.for_each(fun(key: vector, value: *ast_node) { /*print("\t\t")*/ var hasTypStr = string() key.for_each(fun(t: type) hasTypStr += t.to_string(false) + " ";) /*print(hasTypStr)*/ if (typeStr == hasTypStr) error(node, "they're equal but really shouldnt be") /*println()*/ }) /*println("donr")*/ if (real_types.any_true(fun(t: *type): bool return t->is_none() || t ->is_template_type();)) { error(node, "Instantiating types for templated object are not all real types!") } inst_type = first_pass_type_def(results[i]->template.syntax_node, results[i], true) // no change up it's name so we can see that it's instantiated when printed out and keep track of it inst_type->type_def.name += "<" + typeStr + ">" // add to instantiated_map so we only instantiate with a paticular set of types once // put in map first for recursive purposes results[i]->template.instantiated_map.set(real_types_deref, inst_type) results[i]->template.instantiated.add(inst_type) second_pass_type_def(results[i]->template.syntax_node, inst_type, results[i], template_type_replacements) fourth_pass_worklist.push(inst_type) } fitting_types.add(make_pair(inst_type, num_satisfied_traits)) } if (fitting_types.size == 0) { println("no working templated object found") error(node, "FREAK OUT AUTOMATON") return null() } return fitting_types.max(fun(a: pair<*ast_node, int>, b: pair<*ast_node, int>): bool return a.second < b.second;).first->type_def.self_type->clone_with_indirection(indirection, is_ref) } var type_syntax_str = concat_symbol_tree(real_node) /*println(type_syntax_str + " *************************")*/ if (template_replacements.contains_key(type_syntax_str)) { /*print("Is in template_replacements, returning: ")*/ var to_ret = template_replacements[type_syntax_str]->clone_with_increased_indirection(indirection, is_ref) /*println(to_ret->to_string())*/ return to_ret } // should take into account references... if (type_syntax_str == "void") return type_ptr(base_type::void_return(), indirection, is_ref) else if (type_syntax_str == "bool") return type_ptr(base_type::boolean(), indirection, is_ref) else if (type_syntax_str == "char") return type_ptr(base_type::character(), indirection, is_ref) else if (type_syntax_str == "uchar") return type_ptr(base_type::ucharacter(), indirection, is_ref) else if (type_syntax_str == "short") return type_ptr(base_type::short_int(), indirection, is_ref) else if (type_syntax_str == "ushort") return type_ptr(base_type::ushort_int(), indirection, is_ref) else if (type_syntax_str == "int") return type_ptr(base_type::integer(), indirection, is_ref) else if (type_syntax_str == "uint") return type_ptr(base_type::uinteger(), indirection, is_ref) else if (type_syntax_str == "long") return type_ptr(base_type::long_int(), indirection, is_ref) else if (type_syntax_str == "ulong") return type_ptr(base_type::ulong_int(), indirection, is_ref) else if (type_syntax_str == "float") return type_ptr(base_type::floating(), indirection, is_ref) else if (type_syntax_str == "double") return type_ptr(base_type::double_precision(), indirection, is_ref) else if (get_node("function_type", real_node)) { var types = get_nodes("type", get_node("function_type", real_node)).map(fun(node: *tree): *type transform_type(node, scope, template_replacements);) return type_ptr(types.slice(0,-2), types.last(), indirection, is_ref) } else { // do lookup for objects, ADTs, templates, etc var possibilities = scope_lookup(type_syntax_str, scope) /*print("There are "); print(possibilities.size); println(" possibilites for this object type lookup")*/ for (var i = 0; i < possibilities.size; i++;) { match(*possibilities[i]) { ast_node::type_def(backing) return backing.self_type->clone_with_indirection(indirection, is_ref) ast_node::adt_def(backing) return backing.self_type->clone_with_indirection(indirection, is_ref) } } // error("no types found for " + type_syntax_str) /*println("no types found for " + type_syntax_str)*/ return type_ptr(base_type::none(), indirection, is_ref) } } fun transform(node: *tree, scope: *ast_node, template_replacements: map): *ast_node return transform(node, scope, search_type::none(), template_replacements) fun transform(node: *tree, scope: *ast_node, searching_for: search_type, template_replacements: map): *ast_node { var name = node->data.name if (name == "identifier" || name == "scoped_identifier") { return transform_identifier(node, scope, searching_for) } else if (name == "code_block") { return transform_code_block(node, scope, template_replacements) } else if (name == "if_comp") { return transform_if_comp(node, scope) } else if (name == "simple_passthrough") { return transform_simple_passthrough(node, scope) } else if (name == "statement") { return transform_statement(node, scope, template_replacements) } else if (name == "declaration_statement") { return transform_declaration_statement(node, scope, template_replacements) } else if (name == "assignment_statement") { return transform_assignment_statement(node, scope, template_replacements) } else if (name == "if_statement") { return transform_if_statement(node, scope, template_replacements) } else if (name == "while_loop") { return transform_while_loop(node, scope, template_replacements) } else if (name == "for_loop") { return transform_for_loop(node, scope, template_replacements) } else if (name == "return_statement") { return transform_return_statement(node, scope, template_replacements) } else if (name == "continue_statement" || name == "break_statement") { return transform_branching_statement(node, scope) } else if (name == "defer_statement") { return transform_defer_statement(node, scope, template_replacements) } else if (name == "match_statement") { return transform_match_statement(node, scope, template_replacements) } else if (name == "function_call") { return transform_function_call(node, scope, template_replacements) } else if (name == "compiler_intrinsic") { return transform_compiler_intrinsic(node, scope, template_replacements) } else if (name == "lambda") { return transform_lambda(node, scope, template_replacements) } else if (name == "boolean_expression" || name == "and_boolean_expression" || name == "bitwise_or" || name == "bitwise_xor" || name == "bitwise_and" || name == "bool_exp" || name == "expression" || name == "shiftand" || name == "term" || name == "factor" || name == "unarad" || name == "access_operation" || name == "cast_expression" ) { // for now, assume passthrough and just transform underneath return transform_expression(node, scope, searching_for, template_replacements) } else if (name == "bool" || name == "string" || name == "character" || name == "number" ) { /*println(string("transforming value: ") + name)*/ return transform_value(node, scope) } print("FAILED TO TRANSFORM: "); print(name + ": "); println(concat_symbol_tree(node)) error(node, "FAILED TO TRANSFORM") return null() } fun transform_all(nodes: vector<*tree>, scope: *ast_node, template_replacements: map): vector<*ast_node> { return nodes.map(fun(node: *tree): *ast_node return transform(node, scope, template_replacements);) } fun make_this(object: *ast_node): *ast_node { if (!type_def_to_this.contains_key(object)) type_def_to_this[object] = ast_identifier_ptr("this", object->type_def.self_type->clone_with_indirection(1), object) return type_def_to_this[object] } fun transform_identifier(node: *tree, scope: *ast_node, searching_for: search_type): *ast_node { // first, we check for and generate this var name = concat_symbol_tree(node) if (name == "this") { while (!is_type_def(scope)) scope = get_ast_scope(scope)->get(string("~enclosing_scope"))[0] return make_this(scope) } match (searching_for) { search_type::none() return identifier_lookup(name, scope) search_type::function(type_vec) { var possible_func = function_lookup(name, scope, type_vec) if (possible_func) return possible_func var possible_obj = identifier_lookup(name, scope) // sometimes identifier lookup doesn't return identfiers, i.e. function values, etc if (possible_obj && is_identifier(possible_obj)) { var possible_obj_type = get_ast_type(possible_obj) // note operator() has had it's () stripped out... if (possible_obj_type->is_object() && has_method(possible_obj_type->type_def, "operator", type_vec)) { /*println("Has the operator()!")*/ return possible_obj } } } } /*println("FAILED SEARCH FOR")*/ return null() } fun transform_value(node: *tree, scope: *ast_node): *ast_node { var value_str = concat_symbol_tree(node) var value_type = null() if (value_str[0] == '"') // " // Comment hack for emacs now value_type = type_ptr(base_type::character(), 1) else if (value_str[0] == '\'') //'// lol, comment hack for vim syntax highlighting (my fault, of course) value_type = type_ptr(base_type::character()) else if (value_str == "true" || value_str == "false") value_type = type_ptr(base_type::boolean()) else { // should differentiate between float and double... var contains_dot = false for (var i = 0; i < value_str.length(); i++;) { if (value_str[i] == '.') { contains_dot = true break } } if (contains_dot) if (value_str[value_str.length()-1] == 'f') value_type = type_ptr(base_type::floating()) //value_type = type_ptr(base_type::floating()) else value_type = type_ptr(base_type::double_precision()) //value_type = type_ptr(base_type::floating()) else value_type = type_ptr(base_type::integer()) } return ast_value_ptr(value_str, value_type) } fun transform_code_block(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var new_block = ast_code_block_ptr() add_to_scope("~enclosing_scope", scope, new_block) new_block->code_block.children = transform_all(node->children, new_block, template_replacements) return new_block } fun transform_if_comp(node: *tree, scope: *ast_node): *ast_node { var new_if_comp = ast_if_comp_ptr() new_if_comp->if_comp.wanted_generator = concat_symbol_tree(get_node("identifier", node)) new_if_comp->if_comp.statement = transform_statement(get_node("statement", node), scope, map()) return new_if_comp } fun transform_simple_passthrough(node: *tree, scope: *ast_node): *ast_node { var new_passthrough = ast_simple_passthrough_ptr() new_passthrough->simple_passthrough.passthrough_str = concat_symbol_tree(get_node("triple_quoted_string", node)).slice(3,-4) // setup passthrough params and string var passthrough_params = get_node("passthrough_params", node) if (!passthrough_params) return new_passthrough var in_passthrough_params = get_node("in_passthrough_params", passthrough_params) var out_passthrough_params = get_node("out_passthrough_params", passthrough_params) var linker_str = get_node("opt_string", passthrough_params) if (in_passthrough_params) get_nodes("param_assign", in_passthrough_params).for_each(fun(p: *tree) { var idents = get_nodes("identifier", p) if (idents.size == 2) new_passthrough->simple_passthrough.in_params.add(make_pair(transform_identifier(idents[0], scope, search_type::none()), concat_symbol_tree(idents[1]))) else new_passthrough->simple_passthrough.in_params.add(make_pair(transform_identifier(idents[0], scope, search_type::none()), concat_symbol_tree(idents[0]))) }) if (out_passthrough_params) get_nodes("param_assign", out_passthrough_params).for_each(fun(p: *tree) { var idents = get_nodes("identifier", p) if (idents.size == 2) new_passthrough->simple_passthrough.out_params.add(make_pair(transform_identifier(idents[0], scope, search_type::none()), concat_symbol_tree(idents[1]))) else new_passthrough->simple_passthrough.out_params.add(make_pair(transform_identifier(idents[0], scope, search_type::none()), concat_symbol_tree(idents[0]))) }) if (linker_str) new_passthrough->simple_passthrough.linker_str = concat_symbol_tree(linker_str).slice(1,-2) return new_passthrough } fun transform_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var to_ret = ast_statement_ptr(transform(node->children[0], scope, template_replacements)); ast_to_syntax.set(to_ret, node) return to_ret } fun transform_declaration_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { // this might have an init position method call var identifiers = get_nodes("identifier", node) var name = concat_symbol_tree(identifiers[0]) // may have type, or an expression, or both var type_syntax_node = get_node("type", node) var expression_syntax_node = get_node("boolean_expression", node) var expression = null() // we do it early so that if there is a type_syntax_node we can add to scope so that the expression can find this for things like rec closures var identifier = ast_identifier_ptr(name, null(), scope) add_to_scope(name, identifier, scope) if (type_syntax_node) identifier->identifier.type = transform_type(type_syntax_node, scope, template_replacements) if (expression_syntax_node) { expression = transform(expression_syntax_node, scope, template_replacements) if (!type_syntax_node) identifier->identifier.type = get_ast_type(expression)->clone_without_ref() } if (!identifier->identifier.type) error(node, "declaration statement with no type or expression from which to inference type") if (identifier->identifier.type->is_none() || (identifier->identifier.type->indirection == 0 && identifier->identifier.type->is_void())) error(node, "declaration statement with bad type") var declaration = ast_declaration_statement_ptr(identifier, expression, get_node("\"ext\"", node) != null>()) // ok, deal with the possible init position method call if (identifiers.size == 2) { var parameters = get_nodes("parameter", node).map(fun(child: *tree): *ast_node return transform(get_node("boolean_expression", child), scope, template_replacements);) var method = transform(identifiers[1], identifier->identifier.type->type_def, search_type::function(parameters.map(fun(i:*ast_node):*type return get_ast_type(i);)), template_replacements) if (!method) error(identifiers[1], "Cannot find method for declaration site method call") declaration->declaration_statement.init_method_call = make_method_call(identifier, method, parameters) } return declaration } fun transform_assignment_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var to_assign = transform(get_node("boolean_expression", node), scope, template_replacements) // for []= overloading if (get_node("\"=\"", node)) { /*println("Regular Assignment!")*/ var factor_part = get_node("factor", node) if (factor_part->children.size == 1) { /*println("Factor has only one child!")*/ var inner_unarad = get_node("unarad", factor_part) if (get_node("\"]\"", inner_unarad)) { var assign_to = transform(get_node("unarad", inner_unarad), scope, template_replacements) var assign_idx = transform(get_node("expression", inner_unarad), scope, template_replacements) var possible_bracket_assign = find_and_make_any_operator_overload_call(string("[]="), vector(assign_to, assign_idx, to_assign), scope, template_replacements) if (possible_bracket_assign) { /*println("Computed and returning []=!")*/ return possible_bracket_assign } // else println("Could not Compute and return []=!") } // else println("Inner Unarad does not have [!") } // else println("Factor not 1 child") } // else println("Not regular assignment") var assign_to = transform(get_node("factor", node), scope, template_replacements) if (get_node("\"=\"", node)) { var possible_assign = find_and_make_any_operator_overload_call(string("="), vector(assign_to, to_assign), scope, template_replacements) if (possible_assign) { /*println("Computed and returning operator=")*/ return possible_assign } } else if (get_node("\"\\+=\"", node)) { var possible_assign = find_and_make_any_operator_overload_call(string("+="), vector(assign_to, to_assign), scope, template_replacements) if (possible_assign) { /*print("Computed and returning operator+=")*/ return possible_assign } to_assign = make_operator_call("+", vector(assign_to, to_assign)) } else if (get_node("\"-=\"", node)) { var possible_assign = find_and_make_any_operator_overload_call(string("-="), vector(assign_to, to_assign), scope, template_replacements) if (possible_assign) { /*print("Computed and returning operator-=")*/ return possible_assign } to_assign = make_operator_call("-", vector(assign_to, to_assign)) } else if (get_node("\"\\*=\"", node)) { var possible_assign = find_and_make_any_operator_overload_call(string("*="), vector(assign_to, to_assign), scope, template_replacements) if (possible_assign) { /*print("Computed and returning operator*=")*/ return possible_assign } to_assign = make_operator_call("*", vector(assign_to, to_assign)) } else if (get_node("\"/=\"", node)){ var possible_assign = find_and_make_any_operator_overload_call(string("/="), vector(assign_to, to_assign), scope, template_replacements) if (possible_assign) { /*print("Computed and returning operator/=")*/ return possible_assign } to_assign = make_operator_call("/", vector(assign_to, to_assign)) } var assignment = ast_assignment_statement_ptr(assign_to, to_assign) return assignment } fun transform_if_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { if (get_node("AmbiguityInner", node)) error(node, "Ambigious two ifs with one else!") var if_statement = ast_if_statement_ptr(transform_expression(get_node("boolean_expression", node), scope, template_replacements)) // one variable declarations might be in a code_block-less if statement add_to_scope("~enclosing_scope", scope, if_statement) var statements = transform_all(get_nodes("statement", node), if_statement, template_replacements) if_statement->if_statement.then_part = statements[0] // we have an else if (statements.size == 2) if_statement->if_statement.else_part = statements[1] return if_statement } fun transform_while_loop(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var while_loop = ast_while_loop_ptr(transform_expression(get_node("boolean_expression", node), scope, template_replacements)) add_to_scope("~enclosing_scope", scope, while_loop) while_loop->while_loop.statement = transform(get_node("statement", node), while_loop, template_replacements) return while_loop } fun transform_for_loop(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var for_loop = ast_for_loop_ptr() add_to_scope("~enclosing_scope", scope, for_loop) var statements = get_nodes("statement", node) for_loop->for_loop.init = transform(statements[0], for_loop, template_replacements) for_loop->for_loop.condition = transform(get_node("boolean_expression", node), for_loop, template_replacements) for_loop->for_loop.update = transform(statements[1], for_loop, template_replacements) for_loop->for_loop.body = transform(statements[2], for_loop, template_replacements) return for_loop } fun transform_return_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var return_value = get_node("boolean_expression", node) var to_ret: *ast_node if (return_value) to_ret = ast_return_statement_ptr(transform(return_value, scope, template_replacements)) else to_ret = ast_return_statement_ptr(null()) ast_to_syntax.set(to_ret, node) return to_ret } fun transform_branching_statement(node: *tree, scope: *ast_node): *ast_node { if (node->data.name == "break_statement") return ast_branching_statement_ptr(branching_type::break_stmt()) return ast_branching_statement_ptr(branching_type::continue_stmt()) } fun transform_defer_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { return ast_defer_statement_ptr(transform(node->children[0], scope, template_replacements)) } fun transform_match_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var to_ret = ast_match_statement_ptr(transform(get_node("boolean_expression", node), scope, template_replacements)) get_nodes("case_statement", node).for_each(fun(syntax: *tree) to_ret->match_statement.cases.add(transform_case_statement(syntax, scope, template_replacements));) return to_ret } fun transform_case_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var to_ret = ast_case_statement_ptr() var the_adts = scope_lookup(concat_symbol_tree(get_node("scoped_identifier", get_node("scoped_identifier", node))), scope).filter(fun(i: *ast_node): bool return is_adt_def(i);) if (the_adts.size != 1) error(node, string("the number of adts found was not 1, it was ") + the_adts.size + " for " + concat_symbol_tree(get_node("scoped_identifier", node))) var the_adt = the_adts[0] var the_option_name = concat_symbol_tree(get_node("identifier", get_node("scoped_identifier", node))) var the_option = the_adt->adt_def.options.find_first_satisfying(fun(option: *ast_node): bool return option->identifier.name == the_option_name;) to_ret->case_statement.option = the_option var possible_ident = get_node("identifier", node) if (possible_ident) { var ident = ast_identifier_ptr(concat_symbol_tree(possible_ident), the_option->identifier.type, scope) to_ret->case_statement.unpack_ident = ident add_to_scope(ident->identifier.name, ident, to_ret) } //add to scope add_to_scope("~enclosing_scope", scope, to_ret) to_ret->case_statement.statement = transform(get_node("statement", node), to_ret, template_replacements) return to_ret } fun transform_function_call(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { // don't bother with a full transform for parameters with their own function, just get the boolean expression and transform it var parameters = get_nodes("parameter", node).map(fun(child: *tree): *ast_node return transform(get_node("boolean_expression", child), scope, template_replacements);) var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);) var func = transform(get_node("unarad", node), scope, search_type::function(parameter_types), template_replacements) // may return an identifier of type object if doing operator() - but the () have been stripped out by importer if (get_ast_type(func)->is_object()) { /*println("Making an operator() method call!")*/ return make_method_call(func, "operator", parameters) } var f = ast_function_call_ptr(func, parameters) /*print("function call function ")*/ /*println(f->function_call.func)*/ /*print("function call parameters ")*/ /*f->function_call.parameters.for_each(fun(param: *ast_node) print(param);)*/ return f } fun transform_compiler_intrinsic(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { // don't bother with a full transform for parameters with their own function, just get the boolean expression and transform it var parameters = vector() var type_parameters = vector<*type>() if (get_nodes("intrinsic_parameter", node).size) parameters = get_nodes("intrinsic_parameter", node).map(fun(child: *tree): string return concat_symbol_tree(child);) if (get_nodes("type", node).size) type_parameters = get_nodes("type", node).map(fun(child: *tree): *type return transform_type(child, scope, template_replacements);) return ast_compiler_intrinsic_ptr(concat_symbol_tree(get_node("identifier", node)), parameters, type_parameters, type_ptr(base_type::ulong_int())) /*return ast_compiler_intrinsic_ptr(concat_symbol_tree(get_node("identifier", node)), parameters, type_parameters, type_ptr(base_type::integer()))*/ } fun transform_lambda(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var function_node = second_pass_function(node, scope, template_replacements, false) function_node->function.body_statement = transform_statement(get_node("statement", node), function_node, template_replacements) function_node->function.closed_variables = find_closed_variables(function_node, function_node->function.body_statement) /*println(string("Found ") + function_node->function.closed_variables.size() + " closed variables!")*/ while (!is_translation_unit(scope)) scope = get_ast_scope(scope)->get(string("~enclosing_scope"))[0] scope->translation_unit.lambdas.add(function_node) return function_node } fun find_closed_variables(func: *ast_node, node: *ast_node): set<*ast_node> { if (!node) return set<*ast_node>() match (*node) { ast_node::identifier(backing) { if (!in_scope_chain(backing.enclosing_scope, func)) return set(node); } ast_node::statement(backing) { return find_closed_variables(func, backing.child) } ast_node::code_block(backing) { var to_ret = set<*ast_node>() backing.children.for_each(fun(n: *ast_node) to_ret += find_closed_variables(func, n);) return to_ret } ast_node::function_call(backing) { if (is_function(backing.func) && (backing.func->function.name == "." || backing.func->function.name == "->")) return find_closed_variables(func, backing.parameters.first()) var to_ret = find_closed_variables(func, backing.func) backing.parameters.for_each(fun(n: *ast_node) to_ret += find_closed_variables(func, n);) return to_ret } ast_node::function(backing) { // now if this is a method, it is called without an access operator because we are in a lambda inside another method // this is because we don't look at the right side of access operators, note the above function_call case if (backing.scope.contains_key(string("~enclosing_scope"))) { var enclosing = backing.scope[string("~enclosing_scope")][0] if (is_type_def(enclosing)) return set(make_this(enclosing)) if (is_template(enclosing) && is_type_def(enclosing->template.scope[string("~enclosing_scope")][0])) return set(make_this(enclosing->template.scope[string("~enclosing_scope")][0])) } // if this is a lambda, we need to check all of the things it closes over // we don't need an if - if it's empty and not a lambda, it's empty // and we don't close over actual functions var to_ret = set<*ast_node>() backing.closed_variables.for_each(fun(n: *ast_node) to_ret += find_closed_variables(func, n);) return to_ret } ast_node::return_statement(backing) { return find_closed_variables(func, backing.return_value) } ast_node::if_statement(backing) { return find_closed_variables(func, backing.condition) + find_closed_variables(func, backing.then_part) + find_closed_variables(func, backing.else_part) } ast_node::match_statement(backing) { var to_ret = set<*ast_node>() backing.cases.for_each(fun(n: *ast_node) to_ret += find_closed_variables(func, n);) return to_ret } ast_node::case_statement(backing) { return find_closed_variables(func, backing.statement) } ast_node::while_loop(backing) { return find_closed_variables(func, backing.condition) + find_closed_variables(func, backing.statement) } ast_node::for_loop(backing) { return find_closed_variables(func, backing.init) + find_closed_variables(func, backing.condition) + find_closed_variables(func, backing.update) + find_closed_variables(func, backing.body) } ast_node::return_statement(backing) { return find_closed_variables(func, backing.return_value) } ast_node::defer_statement(backing) { return find_closed_variables(func, backing.statement) } ast_node::assignment_statement(backing) { return find_closed_variables(func, backing.to) + find_closed_variables(func, backing.from) } ast_node::declaration_statement(backing) { return find_closed_variables(func, backing.expression) + find_closed_variables(func, backing.init_method_call) } ast_node::if_comp(backing) { return find_closed_variables(func, backing.statement) } } return set<*ast_node>() } fun in_scope_chain(node: *ast_node, high_scope: *ast_node): bool { if (node == high_scope) return true if (get_ast_scope(node)->contains_key(string("~enclosing_scope"))) return in_scope_chain(get_ast_scope(node)->get(string("~enclosing_scope"))[0], high_scope) return false } fun transform_expression(node: *tree, scope: *ast_node, template_replacements: map): *ast_node return transform_expression(node, scope, search_type::none(), template_replacements) fun transform_expression(node: *tree, scope: *ast_node, searching_for: search_type, template_replacements: map): *ast_node { var func_name = string() var parameters = vector<*ast_node>() if (node->children.size == 1) { var possible_value = transform(node->children[0], scope, searching_for, template_replacements) if (!possible_value) match (searching_for) { search_type::function(type_vec) possible_value = find_or_instantiate_template_function(concat_symbol_tree(node->children[0]), null>(), scope, type_vec, template_replacements, map()); } if (!possible_value) error(node, concat_symbol_tree(node) + ": HAS NO POSSIBLE FUNCTION OR FUNCTION TEMPLATE SOLUTIONS") return possible_value } else if (node->children.size == 2) { var template_inst = get_node("template_inst", node) if (template_inst) { var identifier = get_node("scoped_identifier", node) var result = null() match (searching_for) { // I guess this should never happen? search_type::none() error(template_inst, "TEMPLATE LOOKUP WITHOUT PERENS ()") search_type::function(type_vec) result = find_or_instantiate_template_function(concat_symbol_tree(identifier), template_inst, scope, type_vec, template_replacements, map()) } if (!result) error(node, "Could not find templated function " + concat_symbol_tree(identifier) + " even though had a template_inst") return result } var check_if_post = concat_symbol_tree(node->children[1]) if (check_if_post == "--" || check_if_post == "++") { // give the post-operators a special suffix so the c_generator knows to emit them post func_name = concat_symbol_tree(node->children[1]) + "p" parameters = vector(transform(node->children[0], scope, template_replacements)) } else { func_name = concat_symbol_tree(node->children[0]) parameters = vector(transform(node->children[1], scope, template_replacements)) } } else { func_name = concat_symbol_tree(node->children[1]) if (func_name == "[") func_name += "]" if (func_name == "cast") return ast_cast_ptr(transform(get_node("boolean_expression", node), scope, template_replacements), transform_type(get_node("type", node), scope, template_replacements)) var first_param = transform(node->children[0], scope, template_replacements) var second_param = null() if (func_name == "." || func_name == "->") { /*println("PRE VANILLA TRY FOR SECOND PARAM")*/ var first_param_type = get_ast_type(first_param) if (!first_param_type) error(node, "Cannot get type from left side of access operation") second_param = transform(node->children[2], first_param_type->type_def, searching_for, template_replacements) // template member functions // XXX add in template inst if it exists if (!second_param) match (searching_for) { search_type::function(type_vec) { var template_inst = get_node("template_inst", node) var inherited_replacements = map() var parent = get_ast_scope(get_ast_type(first_param)->type_def)->get(string("~enclosing_scope"))[0] if (is_template(parent)) { /*println("TEMPLATE type PARENT IS TEMPLATE")*/ for (var i = 0; i < parent->template.template_types.size; i++;) inherited_replacements[parent->template.template_types[i]] = parent->template.instantiated_map.reverse_get(get_ast_type(first_param)->type_def)[i].clone() } else { /*println("TEMPLATE type PARENT IS NOT TEMPLATE")*/ } var method_name = concat_symbol_tree(node->children[2]) /*println("PRE TEMPLATE TRY FOR SECOND PARAM")*/ second_param = find_or_instantiate_template_function(method_name, template_inst, get_ast_type(first_param)->type_def, type_vec, template_replacements, inherited_replacements); if (!second_param) { error(node, "Could not find method " + method_name + " on the right side of (. or ->) " + concat_symbol_tree(node->children[0]) + ", whole string: " + concat_symbol_tree(node) + ", left type: " + get_ast_type(first_param)->to_string()) } } } // this is the case where it's null but not a method call. Should add default to case above and move there if (!second_param) { error(node, "Could not find member " + concat_symbol_tree(node->children[2]) + " on the right side of (. or ->) " + concat_symbol_tree(node->children[0]) + ", whole string: " + concat_symbol_tree(node) + ", left type: " + get_ast_type(first_param)->to_string()) } } else { second_param = transform(node->children[2], scope, template_replacements) } parameters = vector(first_param, second_param) } var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);) // check for operator overloading var possible_overload_call = find_and_make_any_operator_overload_call(func_name, parameters, scope, template_replacements) if (possible_overload_call) return possible_overload_call return ast_function_call_ptr(get_builtin_function(func_name, parameter_types, node), parameters) } fun find_and_make_any_operator_overload_call(func_name: string, parameters: vector<*ast_node>, scope: *ast_node, template_replacements: map): *ast_node { var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);) var possible_overload = null() if (parameter_types[0]->is_object() && parameter_types[0]->indirection == 0) { possible_overload = function_lookup(string("operator")+func_name, parameter_types.first()->type_def, parameter_types.slice(1,-1)) if (!possible_overload) { var inherited_replacements = map() var parent = get_ast_scope(parameter_types.first()->type_def)->get(string("~enclosing_scope"))[0] if (is_template(parent)) { /*println("TEMPLATE type PARENT IS TEMPLATE")*/ for (var i = 0; i < parent->template.template_types.size; i++;) inherited_replacements[parent->template.template_types[i]] = parent->template.instantiated_map.reverse_get(parameter_types.first()->type_def)[i].clone() } else { /*println("TEMPLATE type PARENT IS NOT TEMPLATE")*/ } possible_overload = find_or_instantiate_template_function(string("operator")+func_name, null>(), parameter_types.first()->type_def, parameter_types.slice(1,-1), template_replacements, inherited_replacements) } if (possible_overload) return make_method_call(parameters.first(), possible_overload, parameters.slice(1,-1)) } possible_overload = function_lookup(string("operator")+func_name, scope, parameter_types) if (!possible_overload) possible_overload = find_or_instantiate_template_function(string("operator")+func_name, null>(), scope, parameter_types, template_replacements, map()) if (possible_overload) return ast_function_call_ptr(possible_overload, parameters) return null() } fun find_or_instantiate_template_function(name: string, template_inst: *tree, scope: *ast_node, param_types: vector<*type>, template_replacements: map, replacements_base: map): *ast_node { // replacments base is for templated methods starting off with the replacements of their parent (possibly templated) object /*println(string("trying to instantiate a template function: ") + name)*/ var results = scope_lookup(name, scope) var real_types = vector<*type>() var real_types_deref = vector() var had_real_types = false if (template_inst) { real_types = get_nodes("type", template_inst).map(fun(t: *tree): *type return transform_type(t, scope, template_replacements);) real_types_deref = real_types.map(fun(t:*type):type return *t;) had_real_types = true } var fitting_functions = vector>() for (var i = 0; i < results.size; i++;) { if (is_template(results[i]) && results[i]->template.is_function) { /*println(string() + i + " is a template!")*/ var template_types = results[i]->template.template_types var template_type_replacements = results[i]->template.template_type_replacements if (!had_real_types) { var unify_template_type_replacements = template_type_replacements // reset the vars, cuz we might be iterating through multiple of them real_types = vector<*type>() real_types_deref = vector() // Template Function Instance Inference time var typed_params = get_nodes("typed_parameter", results[i]->template.syntax_node).map(fun(t: *tree): *tree return get_node("type",t);) if (param_types.size != typed_params.size) continue for (var j = 0; j < typed_params.size; j++;) unify_type(typed_params[j], param_types[j], &unify_template_type_replacements, template_replacements) for (var j = 0; j < template_types.size; j++;) { var t = unify_template_type_replacements[template_types[j]]; real_types.add(t) real_types_deref.add(*t) } } else if (template_types.size != real_types.size) continue var num_satisfied_traits = 0 var satisfied_traits = true template_type_replacements.for_each(fun(key: string, value: *type) num_satisfied_traits += value->traits.size();) // check if already instantiated var inst_func = null() /*println(string("FOR TEMPLATE FUNCTION ") + name)*/ /*println("FOR FIND OR INSTATINTATE PREEEE")*/ /*template_type_replacements.for_each(fun(key: string, value: *type) println(string("MAP: ") + key + " : " + value->to_string());)*/ /*println("MAP DONE")*/ for (var j = 0; j < template_types.size; j++;) { /*print("NEW TRAITS IN ALREADY: ")*/ /*template_type_replacements[template_types[j]]->traits.for_each(fun (t: string) print(t + " ");)*/ /*println()*/ /*print("NEW TRAITS PASSED IN: ")*/ /*real_types[j]->traits.for_each(fun (t: string) print(t + " ");)*/ /*println()*/ satisfied_traits = satisfied_traits && real_types[j]->traits.contains(template_type_replacements[template_types[j]]->traits) && (real_types[j]->indirection == 0 || template_type_replacements[template_types[j]]->traits.size() == 0) template_type_replacements[template_types[j]] = real_types[j] /*println("Just made")*/ /*println(template_types[j])*/ /*println("equal to")*/ /*println(real_types[j]->to_string())*/ } replacements_base.for_each(fun(key: string, value: *type) { template_type_replacements[key] = value /*println("Just Inherited")*/ /*println(key)*/ /*println("equal to")*/ /*println(value->to_string())*/ }) /*println("FOR FIND OR INSTATINTATE")*/ /*template_type_replacements.for_each(fun(key: string, value: *type) println(string("MAP: ") + key + " : " + value->to_string());)*/ /*println("MAP DONE")*/ if (!satisfied_traits) { /*println("Did not satisfy traits!")*/ continue } if (real_types.any_true(fun(t: *type): bool return t->is_none() || t ->is_template_type();)) { /*println("Instantiating types not all real types!")*/ continue } if (results[i]->template.instantiated_map.contains_key(real_types_deref)) { /*println("USING CACHED TEMPLATE FUNCITON")*/ inst_func = results[i]->template.instantiated_map[real_types_deref] } else { inst_func = second_pass_function(results[i]->template.syntax_node, results[i], template_type_replacements, false) // add to instantiated_map so we only instantiate with a paticular set of types once // put in map first for recursive purposes results[i]->template.instantiated_map.set(real_types_deref, inst_func) results[i]->template.instantiated.add(inst_func) // and fully instantiate it inst_func->function.body_statement = transform_statement(get_node("statement", results[i]->template.syntax_node), inst_func, template_type_replacements) } if (function_satisfies_params(inst_func, param_types)) fitting_functions.add(make_pair(inst_func, num_satisfied_traits)) /*else*/ /*println(string("this paticular ") + name + " did not satisfy params")*/ } } if (fitting_functions.size == 0) { /*println("no working templated method found")*/ return null() } return fitting_functions.max(fun(a: pair<*ast_node, int>, b: pair<*ast_node, int>): bool return a.second < b.second;).first } } fun get_from_scope(node: *ast_node, member: *char): *ast_node return get_from_scope(node, string(member)) fun get_from_scope(node: *ast_node, member: string): *ast_node return get_ast_scope(node)->get(member).first() fun has_method(object: *ast_node, name: *char, parameter_types: vector<*type>): bool return has_method(object, string(name), parameter_types); fun has_method(object: *ast_node, name: string, parameter_types: vector<*type>): bool { /*println("HAS METHOD:")*/ var to_ret = function_lookup(name, object, parameter_types) || false /*println("HAS METHOD result:")*/ /*println(to_ret)*/ return to_ret } fun make_method_call(object_ident: *ast_node, name: *char, parameters: vector<*ast_node>): *ast_node return make_method_call(object_ident, string(name), parameters); fun make_method_call(object_ident: *ast_node, name: string, parameters: vector<*ast_node>): *ast_node { /*println("MAKE METHOD CALL OUT:")*/ // note that this type_def is the adt_def if this is an adt type var method = function_lookup(name, get_ast_type(object_ident)->type_def, parameters.map(fun(param: *ast_node): *type return get_ast_type(param);)) /*print("Here is the Method: ")*/ /*println(method)*/ return make_method_call(object_ident, method, parameters) } fun make_method_call(object_ident: *ast_node, method: *ast_node, parameters: vector<*ast_node>): *ast_node { /*println("MAKE METHOD CALL IN:")*/ var access_op = "." if (get_ast_type(object_ident)->indirection) access_op = "->" var method_access = ast_function_call_ptr(get_builtin_function(string(access_op), vector(get_ast_type(object_ident), get_ast_type(method))), vector(object_ident, method)) return ast_function_call_ptr(method_access, parameters) } fun make_operator_call(func: *char, params: vector<*ast_node>): *ast_node return make_operator_call(string(func), params); fun make_operator_call(func: string, params: vector<*ast_node>): *ast_node { return ast_function_call_ptr(get_builtin_function(func, params.map(fun(p:*ast_node): *type return get_ast_type(p);)), params) } fun get_builtin_function(name: *char, param_types: vector<*type>): *ast_node return get_builtin_function(string(name), param_types, null>()) fun get_builtin_function(name: string, param_types: vector<*type>): *ast_node return get_builtin_function(name, param_types, null>()) fun get_builtin_function(name: string, param_types: vector<*type>, syntax: *tree): *ast_node { // none of the builtin functions should take in references param_types = param_types.map(fun(t: *type): *type return t->clone_without_ref();) if (name == "==" || name == "!=" || name == ">" || name == "<" || name == "<=" || name == ">" || name == ">=" || name == "&&" || name == "||" || name == "!") return ast_function_ptr(name, type_ptr(param_types, type_ptr(base_type::boolean())), vector<*ast_node>(), false) if (name == "." || name == "->") { if (name == "->" && param_types[0]->indirection == 0) error(syntax, string("drereferencing not a pointer: ") + name) else if (name == "." && param_types[0]->indirection != 0) error(syntax, string("dot operator on a pointer: ") + name) else return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>(), false) } if (name == "[]") { if (param_types[0]->indirection == 0) error(syntax, string("drereferencing not a pointer: ") + name) else return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_decreased_indirection()), vector<*ast_node>(), false) } if (name == "&" && param_types.size == 1) return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_increased_indirection()), vector<*ast_node>(), false) if (name == "\*" && param_types.size == 1) { if (param_types[0]->indirection == 0) error(syntax, string("drereferencing not a pointer: ") + name) else return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_decreased_indirection()), vector<*ast_node>(), false) } if (param_types.size > 1 && param_types[1]->rank() > param_types[0]->rank()) return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>(), false) return ast_function_ptr(name, type_ptr(param_types, param_types[0]), vector<*ast_node>(), false) } fun unify_type(template_type: *tree, param_type: *type, new_map: *map, template_replacements: map) { /*println(string("Unifying type: ") + concat_symbol_tree(template_type))*/ // first get rid of the reference if we have it - we don't care for unification if (get_node("pre_reffed", template_type)) template_type = get_node("pre_reffed", template_type) // There are a couple options for the template parameter type here // 1) template type - perfect, stick it in the map, that's what we're here for // 2) basic type - stick it in the map, it won't get copied out so no worries // 3) pointer type, go down a level // 4) function type - fine, unify on all parameters and return types // 5) instantiated template - fun stuff, have to figure out what it was origionally // instantiated with, but we don't have to worry about it yet as I haven't gotten // to object templates at all ;) if (template_type->children.size == 1) { if (get_node("function_type", template_type)) { /*println("UNIFYING FUNCTION")*/ var template_function_types = get_nodes("type", get_node("function_type", template_type)) if (!param_type->is_function() || template_function_types.size -1 != param_type->parameter_types.size) { /*println("not combining function because:")*/ /*println(param_type->is_function())*/ /*println(template_function_types.size-1)*/ /*println(param_type->parameter_types.size)*/ return; } for (var i = 0; i < template_function_types.size-1; i++;) unify_type(template_function_types[i], param_type->parameter_types[i], new_map, template_replacements) unify_type(template_function_types.last(), param_type->return_type, new_map, template_replacements) } else { /*println(string("setting ") + concat_symbol_tree(template_type) + " equal to " + param_type->to_string())*/ new_map->set(concat_symbol_tree(template_type), param_type) } } else if (get_node("\"\\*\"", template_type)) { // only unify on decreased indirection if we're not already at 0 if (param_type->indirection) unify_type(template_type->children[1], param_type->clone_with_decreased_indirection(), new_map, template_replacements) else return; } else if (get_node("template_inst", template_type)) { if (param_type->is_object()) { var enclosing_template = param_type->type_def->type_def.scope[string("~enclosing_scope")][0] if (is_template(enclosing_template)) { var inst_params = enclosing_template->template.instantiated_map.reverse_get(param_type->type_def) var template_params = get_nodes("type", get_node("template_inst", template_type)) if (inst_params.size == template_params.size) { /*println("TEMPLATE inference should be successful")*/ for (var i = 0; i < inst_params.size; i++;) unify_type(template_params[i], inst_params[i].clone(), new_map, template_replacements) } else { /*println("TEMPLATE inference hit different sizes")*/ } } else { // error("TEMPLATE inference hit non parent template") /*println("TEMPLATE inference hit non parent template")*/ } } else { // error("TEMPLATE inference hit non object") /*println("TEMPLATE inference hit non object")*/ } } else { println(template_type->children[0]->data.name) println(template_type->children[0]->data.data) error(template_type, "TYPE INFERENCE NOT GOOD ENOUGH") } } fun function_satisfies_params(node: *ast_node, param_types: vector<*type>): bool { var func_type = get_ast_type(node) var func_param_types = func_type->parameter_types var param_string = string() param_types.for_each(fun(t: *type) param_string += t->to_string() + ", ";) if (!func_type->is_variadic && func_param_types.size != param_types.size) { /*println(string("type sizes don't match ") + param_types.size + " with needed " + param_string)*/ return false } else if (param_types.size < func_param_types.size) { return false } // note we iterate over the func_param_types which will stop short if function is variadic // just like we want for (var j = 0; j < func_param_types.size; j++;) { // don't care about references if (!func_param_types[j]->equality(param_types[j], false)) { /*println(string("types don't match ") + func_param_types[j]->to_string() + " with needed " + param_types[j]->to_string())*/ if (func_param_types[j]->to_string() == param_types[j]->to_string()) error(string("types aren't equal, but their string rep is (and ref doesn't even matter): ") + func_param_types[j]->to_string() + " vs " + param_types[j]->to_string() ) return false } } return true } fun function_lookup(name: string, scope: *ast_node, param_types: vector<*type>): *ast_node { /*println(string("doing function lookup for: ") + name)*/ var results = scope_lookup(name, scope) /*print(results.size); println(" number of results")*/ for (var i = 0; i < results.size; i++;) { if ((is_function(results[i]) || (is_identifier(results[i]) && get_ast_type(results[i])->is_function())) && function_satisfies_params(results[i], param_types)) { return results[i] } } /*println(string("function lookup failed for ") + name)*/ return null() } fun identifier_lookup(name: ref string, scope: *ast_node): *ast_node { /*println(string("doing identifier lookup for: ") + name)*/ var results = scope_lookup(name, scope) if (!results.size) { /*println(string("identifier lookup failed for ") + name)*/ return null() } return results[0] } fun scope_lookup(name: ref string, scope: *ast_node): vector<*ast_node> { // println("*****Doing a name lookup for*****") // println(name) var results = vector(scope) name.split("::").for_each(fun(i: string) { // println(string("based on split, looking up: ") + i) var next_results = vector<*ast_node>() results.for_each(fun(s: *ast_node) { // print("looking in scope: ") // println(s) scope_lookup_helper(i, s, set<*ast_node>()).for_each(fun (result: *ast_node) { if (!next_results.contains(result)) next_results.add(result) }) }) results = next_results }) return results } fun scope_lookup_helper(name: ref string, scope: *ast_node, visited: set<*ast_node>): vector<*ast_node> { // need to do properly scopded lookups // print("scope is: ") // get_ast_scope(scope)->for_each(fun(key: string, value: vector<*ast_node>) print(key + " ");) // println() var results = vector<*ast_node>() // prevent re-checking the same one... if (visited.contains(scope)) return results visited.add(scope) if (get_ast_scope(scope)->contains_key(name)) { // println(name + " is in scope, adding to results") results += get_ast_scope(scope)->get(name) } if (get_ast_scope(scope)->contains_key(string("~enclosing_scope"))) results += scope_lookup_helper(name, get_ast_scope(scope)->get(string("~enclosing_scope"))[0], visited) if (is_translation_unit(scope)) { scope->translation_unit.children.for_each(fun(child: *ast_node) { if (is_import(child)) { if (child->import.imported.contains(name)) { // println(name + " is indeed imported") results += scope_lookup_helper(name, child->import.translation_unit, visited) } else if (child->import.starred) { // println("import has an import *, checking along it") results += scope_lookup_helper(name, child->import.translation_unit, visited) } else { // println(name + " is not imported (this time)") // print("import imports") // child->import.imported.for_each(fun(it: string) print(it + " ");) } } }) } return results } fun concat_symbol_tree(node: *tree): string { var str.construct(): string if (node->data.data != "no_value") str += node->data.data node->children.for_each(fun(child: *tree) str += concat_symbol_tree(child);) return str } fun get_node(lookup: *char, parent: *tree): *tree { return get_node(string(lookup), parent) } fun get_node(lookup: string, parent: *tree): *tree { var results = get_nodes(lookup, parent) if (results.size > 1) error(parent, "get node too many results!") if (results.size) return results[0] return null>() } fun get_nodes(lookup: *char, parent: *tree): vector<*tree> { return get_nodes(string(lookup), parent) } fun get_nodes(lookup: string, parent: *tree): vector<*tree> { return parent->children.filter(fun(node: *tree):bool return node->data.name == lookup;) } fun add_to_scope(name: *char, to_add: *ast_node, add_to: *ast_node) { add_to_scope(string(name), to_add, add_to) } fun add_to_scope(name: string, to_add: *ast_node, add_to: *ast_node) { var add_to_map = get_ast_scope(add_to) if (add_to_map->contains_key(name)) (*add_to_map)[name].add(to_add) else add_to_map->set(name, vector(to_add)) } fun get_first_terminal(source: *tree): *tree { if (!source) return null>() if (source->data.terminal) return source if (source->children.size == 0) return null>() return get_first_terminal(source->children.first()) } fun assert(works: bool, message: *char) { if (!works) error(message) } fun assert(works: bool, message: string) { if (!works) error(message) } fun error(source: *tree, message: *char) error(source, string(message)); fun error(source: *tree, message: string) { source = get_first_terminal(source) if (source) error(source->data.source + ": " + source->data.position + " " + message) error(message) }