import symbol:* import tree:* import vec:* import queue:* import stack:* import map:* import util:* import str:* import mem:* import io:* import os:* import importer:* import ast_nodes:* import type:* import pass_common:* adt search_type { none, function: vec<*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: str, parse_tree: *tree): pair<*ast_node, vec<*ast_node>> { var translation_unit = _translation_unit(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 = _adt_def(name) adt_def_node->adt_def.self_type = type_ptr(adt_def_node, set(str("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 vec of them so importer can fix them (and our translation unit scope) // up with actual pointers to the other ASTs var imports = vec<*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 = _import(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):str 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): str 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 = vec() 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 = _template(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 = _type_def(name, concat_symbol_tree(get_node("obj_nonterm", child)) == "uni") /*var type_def_node = _type_def(name, false)*/ /*var type_def_node = _type_def(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) { // 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())) } }) // 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 = _ident(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 = _ident(option_name, ident_type, node) function_node = _function(option_name, type_ptr(vec(get_ast_type(identifier_param)), node->adt_def.self_type, 0, false, false, true), vec(identifier_param), false) } else { function_node = _function(option_name, type_ptr(vec<*type>(), node->adt_def.self_type, 0, false, false, true), vec<*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 = _ident(str("in"), node->adt_def.self_type->clone_with_indirection(0,true), node) var nequals_param = _ident(str("in"), node->adt_def.self_type->clone_with_indirection(0,true), node) var copy_construct_param = _ident(str("in"), node->adt_def.self_type->clone_with_indirection(1,false), node) var assign_param = _ident(str("in"), node->adt_def.self_type->clone_with_indirection(0,true), node) vec( make_pair("operator==", _function(str("operator=="), type_ptr(vec(equals_param->identifier.type), type_ptr(base_type::boolean()), 0, false, false, true), vec(equals_param), _ident("this", node->adt_def.self_type->clone_with_indirection(1), node), false, false)), make_pair("operator!=", _function(str("operator!="), type_ptr(vec(nequals_param->identifier.type), type_ptr(base_type::boolean()), 0, false, false, true), vec(nequals_param), _ident("this", node->adt_def.self_type->clone_with_indirection(1), node), false, false)), make_pair("construct", _function(str("construct"), type_ptr(vec<*type>(), node->adt_def.self_type->clone_with_increased_indirection(), 0, false, false, true), vec<*ast_node>(), _ident("this", node->adt_def.self_type->clone_with_indirection(1), node), false, false)), make_pair("copy_construct", _function(str("copy_construct"), type_ptr(vec(copy_construct_param->identifier.type), node->adt_def.self_type->clone_with_increased_indirection(), 0, false, false, true), vec(copy_construct_param), _ident("this", node->adt_def.self_type->clone_with_indirection(1), node), false, false)), make_pair("operator=", _function(str("operator="), type_ptr(vec(assign_param->identifier.type), type_ptr(base_type::void_return()), 0, false, false, true), vec(assign_param), _ident("this", node->adt_def.self_type->clone_with_indirection(1), node), false, false)), make_pair("destruct", _function(str("destruct"), type_ptr(vec<*type>(), type_ptr(base_type::void_return()), 0, false, false, true), vec<*ast_node>(), _ident("this", node->adt_def.self_type->clone_with_indirection(1), node), false, false)) ).for_each(fun(func_pair: pair<*char, *ast_node>) { node->adt_def.regular_funcs.add(func_pair.second) add_to_scope(str(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 = str("__compiler_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 = vec() 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)))) }) var template = _template(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 = vec<*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(_ident(concat_symbol_tree(get_node("identifier", child)), param_type, null())) }) var is_variadic = get_node("\"...\"", node) != null>() var is_raw = function_name != "__compiler_lambda__" var this_param = null() if (is_type_def(scope)) { this_param = _ident("this", scope->type_def.self_type->clone_with_indirection(1), scope) } else if (is_template(scope)) { var parent_scope = get_ast_scope(scope)->get(str("~enclosing_scope"))[0] if (is_type_def(parent_scope)) { this_param = _ident("this", parent_scope->type_def.self_type->clone_with_indirection(1), parent_scope) } } // figure out function type and make function_node var function_node = _function(function_name, type_ptr(parameters.map(fun(parameter: *ast_node): *type return parameter->identifier.type;), return_type, 0, false, is_variadic, is_raw), parameters, this_param, 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), and top level compiler intrinsics fun third_pass(parse_tree: *tree, translation_unit: *ast_node) { translation_unit->translation_unit.children.for_each(fun(node: *ast_node) { match(*node) { ast_node::type_def(backing) { // also same body problem as below backing.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 if (!backing.is_extern) backing.body_statement = transform_statement(get_node("statement", ast_to_syntax[node]), node, map()) } } }) parse_tree->children.for_each(fun(child: *tree) { if (child->data.name == "compiler_intrinsic") { translation_unit->translation_unit.children.add(transform_compiler_intrinsic(child, translation_unit, 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) { 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[str("~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)) 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 = vec>() for (var i = 0; i < results.size; i++;) { if (!is_template(results[i]) || results[i]->template.is_function) continue 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: str, value: *type) num_satisfied_traits += value->traits.size();) 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] } if (!satisfied_traits) continue var inst_type = null() // check if already instantiated if (results[i]->template.instantiated_map.contains_key(real_types_deref)) { inst_type = results[i]->template.instantiated_map[real_types_deref] } else { var typeStr = str() real_types_deref.for_each(fun(t: type) typeStr += t.to_string(false) + " ";) results[i]->template.instantiated_map.for_each(fun(key: vec, value: *ast_node) { var hasTypStr = str() key.for_each(fun(t: type) hasTypStr += t.to_string(false) + " ";) if (typeStr == hasTypStr) error(node, "they're equal but really shouldnt be") }) 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) if (template_replacements.contains_key(type_syntax_str)) { var to_ret = template_replacements[type_syntax_str]->clone_with_increased_indirection(indirection, is_ref) 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 function_type = get_node("function_type", real_node) var types = get_nodes("type", function_type).map(fun(node: *tree): *type transform_type(node, scope, template_replacements);) return type_ptr(types.slice(0,-2), types.last(), indirection, is_ref, false, get_node("\"run\"", function_type) != null>()) // check for raw function pointer } else { // do lookup for objects, ADTs, templates, etc var possibilities = scope_lookup(type_syntax_str, scope) 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) 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" ) { return transform_expression(node, scope, searching_for, template_replacements) } else if (name == "bool" || name == "string" || name == "character" || name == "number" ) { 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: vec<*tree>, scope: *ast_node, template_replacements: map): vec<*ast_node> { return nodes.map(fun(node: *tree): *ast_node return transform(node, scope, template_replacements);) } 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_function(scope) || scope->function.this_param == null()) scope = get_ast_scope(scope)->get(str("~enclosing_scope"))[0] if (!is_function(scope)) error(node, "Couldn't find this") return scope->function.this_param } 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)) { return possible_obj } } } } 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) var start = 1 var end = value_str.length() -1 if (value_str.length() > 3 && value_str[1] == '"' && value_str[2] == '"') { value_str = value_str.slice(3,-4) } else { var new_str.construct(end-start): str var escaped = false for (var i = 1; i < value_str.length()-1; i++;) { if (escaped) { escaped = false if (value_str[i] == 'n') new_str += '\n' else if (value_str[i] == 't') new_str += '\t' else new_str += value_str[i] } else if (value_str[i] == '\\') { escaped = true } else { new_str += value_str[i] } } value_str = new_str } } else if (value_str[0] == '\'') { //'// lol, comment hack for vim syntax highlighting (my fault, of course) value_type = type_ptr(base_type::character()) value_str = value_str.slice(1,-2) } 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.last() == 'f') value_type = type_ptr(base_type::floating()) else value_type = type_ptr(base_type::double_precision()) } else { var chop = 0 if (value_str.length() > 2) { var s = value_str.slice(-3,-1) if (s == "uc") { chop = 2; value_type = type_ptr(base_type::ucharacter()); } else if (s == "us") { chop = 2; value_type = type_ptr(base_type::ushort_int()); } else if (s == "ul") { chop = 2; value_type = type_ptr(base_type::ulong_int()); } } if (chop == 0) { if (value_str.last() == 'c') { chop = 1; value_type = type_ptr(base_type::character()); } else if (value_str.last() == 's') { chop = 1; value_type = type_ptr(base_type::short_int()); } else if (value_str.last() == 'u') { chop = 1; value_type = type_ptr(base_type::uinteger()); } else if (value_str.last() == 'l') { chop = 1; value_type = type_ptr(base_type::long_int()); } } if (chop == 0) value_type = type_ptr(base_type::integer()) value_str = value_str.slice(0, -1-chop) } } return _value(value_str, value_type) } fun transform_code_block(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var new_block = _code_block() 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 = _if_comp() 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 = _passthrough() new_passthrough->simple_passthrough.passthrough_str = concat_symbol_tree(get_node("triple_quoted_string", node)).slice(3,-4) // setup passthrough params and str 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 { return transform(node->children[0], scope, template_replacements); } 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 = _ident(name, null(), scope, get_node("\"ext\"", node) != null>()) 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 = _declaration(identifier, expression) // 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)) { var factor_part = get_node("factor", node) if (factor_part->children.size == 1) { 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(str("[]="), vec(assign_to, assign_idx, to_assign), scope, template_replacements) if (possible_bracket_assign) { return possible_bracket_assign } } } } 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(str("="), vec(assign_to, to_assign), scope, template_replacements) if (possible_assign) { return possible_assign } } else if (get_node("\"\\+=\"", node)) { var possible_assign = find_and_make_any_operator_overload_call(str("+="), vec(assign_to, to_assign), scope, template_replacements) if (possible_assign) { return possible_assign } to_assign = make_operator_call("+", vec(assign_to, to_assign)) } else if (get_node("\"-=\"", node)) { var possible_assign = find_and_make_any_operator_overload_call(str("-="), vec(assign_to, to_assign), scope, template_replacements) if (possible_assign) { return possible_assign } to_assign = make_operator_call("-", vec(assign_to, to_assign)) } else if (get_node("\"\\*=\"", node)) { var possible_assign = find_and_make_any_operator_overload_call(str("*="), vec(assign_to, to_assign), scope, template_replacements) if (possible_assign) { return possible_assign } to_assign = make_operator_call("*", vec(assign_to, to_assign)) } else if (get_node("\"/=\"", node)){ var possible_assign = find_and_make_any_operator_overload_call(str("/="), vec(assign_to, to_assign), scope, template_replacements) if (possible_assign) { return possible_assign } to_assign = make_operator_call("/", vec(assign_to, to_assign)) } else if (get_node("\"^=\"", node)){ var possible_assign = find_and_make_any_operator_overload_call(str("^="), vec(assign_to, to_assign), scope, template_replacements) if (possible_assign) { return possible_assign } to_assign = make_operator_call("^", vec(assign_to, to_assign)) } var assignment = _assign(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 = _if(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 = _while(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 = _for() 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 = _return(transform(return_value, scope, template_replacements)) else to_ret = _return(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 _branch(branching_type::break_stmt()) return _branch(branching_type::continue_stmt()) } fun transform_defer_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { return _defer(transform(node->children[0], scope, template_replacements)) } fun transform_match_statement(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var to_ret = _match(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 = _case() 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, str("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))) // ADD IN ERROR CHECKING HERE 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 = _ident(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);) if (parameter_types.any_true(fun(ptype: *type): bool return !ptype;)) error(node, "One of the parameter types is null!") 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 var func_type = get_ast_type(func) if (func_type->is_object() && func_type->indirection == 0) { return make_method_call(func, "operator", parameters) } if (!(func_type->is_function() && func_type->indirection == 0)) error(node, "trying to call not a function") var f = _func_call(func, parameters) return f } fun transform_compiler_intrinsic(node: *tree, scope: *ast_node, template_replacements: map): *ast_node { var parameters = get_nodes("parameter", node).map(fun(child: *tree): *ast_node return transform(get_node("boolean_expression", child), scope, template_replacements);) var type_parameters = get_nodes("type", node).map(fun(child: *tree): *type return transform_type(child, scope, template_replacements);) var intrinsic_name = concat_symbol_tree(get_node("identifier", node)) var intrinsic_return_type: *type if (intrinsic_name == "ctce") intrinsic_return_type = get_ast_type(parameters[0]) else intrinsic_return_type = type_ptr(base_type::ulong_int()) return _compiler_intrinsic(intrinsic_name, parameters, type_parameters, intrinsic_return_type) } 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) while (!is_translation_unit(scope)) scope = get_ast_scope(scope)->get(str("~enclosing_scope"))[0] scope->translation_unit.lambdas.add(function_node) return function_node } 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 = str() var parameters = vec<*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) { var function_error_str = str() match (searching_for) { search_type::function() { function_error_str = "(" + searching_for.function.reduce(fun(n:*type, s:str):str { if (n) return s+","+n->to_string() else return s+",null" }, str()) + ")" } } error(node, concat_symbol_tree(node) + ": HAS NO POSSIBLE FUNCTION OR FUNCTION TEMPLATE SOLUTIONS\nlooking for: " + concat_symbol_tree(node->children[0]) + function_error_str) } 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 = vec(transform(node->children[0], scope, template_replacements)) } else { func_name = concat_symbol_tree(node->children[0]) parameters = vec(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 _cast(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 == "->") { var first_param_type = get_ast_type(first_param) if (!first_param_type) error(node, "Cannot get type from left side of access operation") if (!first_param_type->is_object()) error(node, "Type from left side of access operation (" + func_name + ") isn't object, is: " + first_param_type->to_string()) 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(str("~enclosing_scope"))[0] if (is_template(parent)) 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() var method_name = concat_symbol_tree(node->children[2]) 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 str: " + 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 str: " + concat_symbol_tree(node) + ", left type: " + get_ast_type(first_param)->to_string()) } } else { second_param = transform(node->children[2], scope, template_replacements) } parameters = vec(first_param, second_param) } parameters.for_each(fun(param: *ast_node) if (!is_legal_parameter_node_type(param)) error(node, "illegal node type used as parameter (perhaps name resolves to type or translation unit?)");) 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 _func_call(get_builtin_function(func_name, parameter_types, node), parameters) } fun find_and_make_any_operator_overload_call(func_name: str, parameters: vec<*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(str("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(str("~enclosing_scope"))[0] if (is_template(parent)) { 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() } possible_overload = find_or_instantiate_template_function(str("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(str("operator")+func_name, scope, parameter_types) if (!possible_overload) possible_overload = find_or_instantiate_template_function(str("operator")+func_name, null>(), scope, parameter_types, template_replacements, map()) if (possible_overload) return _func_call(possible_overload, parameters) return null() } fun find_or_instantiate_template_function(name: str, template_inst: *tree, scope: *ast_node, param_types: vec<*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 var results = scope_lookup(name, scope) var real_types = vec<*type>() var real_types_deref = vec() 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 = vec>() for (var i = 0; i < results.size; i++;) { if (is_template(results[i]) && results[i]->template.is_function) { 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 = vec<*type>() real_types_deref = vec() // 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: str, value: *type) num_satisfied_traits += value->traits.size();) // check if already instantiated var inst_func = null() 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] } replacements_base.for_each(fun(key: str, value: *type) { template_type_replacements[key] = value }) if (!satisfied_traits) { continue } if (real_types.any_true(fun(t: *type): bool return t->is_none() || t ->is_template_type();)) continue if (results[i]->template.instantiated_map.contains_key(real_types_deref)) { 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)) } } if (fitting_functions.size == 0) { 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 unify_type(template_type: *tree, param_type: *type, new_map: *map, template_replacements: map) { // 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)) { 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) { 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 { 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[str("~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) { 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_type->children[0]->data.name) println(template_type->children[0]->data.data) error(template_type, "TYPE INFERENCE NOT GOOD ENOUGH") } }