Files
kraken/stdlib/ast_transformation.krak
2016-05-19 23:34:29 -07:00

1392 lines
84 KiB
Plaintext

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<symbol>>
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<symbol>, importer: *importer): *ast_node {
var translation_unit = ast_translation_unit_ptr(file_name)
importer->register(file_name, parse_tree, translation_unit)
parse_tree->children.for_each(fun(child: *tree<symbol>) {
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 (done second so that if it imports this translation_unit,
// this one already has all its types defined
parse_tree->children.for_each(fun(child: *tree<symbol>) {
if (child->data.name == "import") {
var import_identifier_children = get_nodes("identifier", child)
var name = concat_symbol_tree(import_identifier_children[0])
var outside_translation_unit = importer->import_first_pass(name + ".krak")
add_to_scope(name, outside_translation_unit, translation_unit)
var import_node = ast_import_ptr(name, outside_translation_unit)
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<symbol>):string return concat_symbol_tree(ident);))
if (get_node("\"\\*\"", child))
import_node->import.starred = true
}
})
return translation_unit
}
fun transform_traits(traits_node: *tree<symbol>): set<string> {
if (!traits_node)
return set<string>()
return from_vector(get_nodes("scoped_identifier", traits_node).map(fun(s: *tree<symbol>): string return concat_symbol_tree(s);))
}
fun first_pass_type_def(child: *tree<symbol>, 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<string>()
var template_type_replacements = map<string, *type>()
// XXX add traits
get_nodes("template_param", template_dec).for_each(fun(template_param: *tree<symbol>) {
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 {
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<symbol>, 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<symbol>) {
if (child->data.name == "function") {
// also handles templated function
var function_node = second_pass_function(child, translation_unit, map<string, *type>(), 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<string, *type>()))
} else if (child->data.name == "compiler_intrinsic") {
translation_unit->translation_unit.children.add(transform_compiler_intrinsic(child, translation_unit, map<string, *type>()))
}
})
// 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<string, *type>())
ast_node::adt_def(backing) second_pass_adt_def(ast_to_syntax[node], node, translation_unit, map<string, *type>())
}
})
}
fun second_pass_type_def(type_def_syntax: *tree<symbol>, node: *ast_node, scope: *ast_node, template_replacements: map<string, *type>) {
type_def_syntax->children.for_each(fun(child: *tree<symbol>) {
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<symbol>, node: *ast_node, scope: *ast_node, template_replacements: map<string, *type>) {
get_nodes("adt_option", adt_def_syntax).for_each(fun(adt_option: *tree<symbol>) {
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<ast_node>()
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<symbol>, scope: *ast_node, template_replacements: map<string, *type>, 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<string>()
var template_type_replacements = map<string, *type>()
get_nodes("template_param", template_dec).for_each(fun(template_param: *tree<symbol>) {
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<type>()
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<symbol>) {
// note the temporary null<ast_node>() 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<ast_node>()))
})
var is_variadic = get_node("\"...\"", node) != null<tree<symbol>>()
// 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<tree<symbol>>(), 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<symbol>, 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<string, *type>())
})
}
}
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<string, *type>())
}
}
})
}
// The fourth pass generates the class templates that have not yet been generated in a worklist loop
fun fourth_pass(parse_tree: *tree<symbol>, 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<string, *type>()
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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *type {
// check for references and step down
// always get to pre-reffed level
var is_ref = get_node("\"ref\"", node) != null<tree<symbol>>()
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<symbol>): *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<pair<*ast_node, int>>()
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<ast_node>()
// 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<type>, 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<type>()
}
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<symbol>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *ast_node return transform(node, scope, search_type::none(), template_replacements)
fun transform(node: *tree<symbol>, scope: *ast_node, searching_for: search_type, template_replacements: map<string, *type>): *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<ast_node>()
}
fun transform_all(nodes: vector<*tree<symbol>>, scope: *ast_node, template_replacements: map<string, *type>): vector<*ast_node> {
return nodes.map(fun(node: *tree<symbol>): *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<symbol>, 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<ast_node>()
}
fun transform_value(node: *tree<symbol>, scope: *ast_node): *ast_node {
var value_str = concat_symbol_tree(node)
var value_type = null<type>()
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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>, 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<string, *type>())
return new_if_comp
}
fun transform_simple_passthrough(node: *tree<symbol>, 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<symbol>) {
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<symbol>) {
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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *ast_node return ast_statement_ptr(transform(node->children[0], scope, template_replacements));
fun transform_declaration_statement(node: *tree<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<ast_node>()
// 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<type>(), 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)
// ok, deal with the possible init position method call
if (identifiers.size == 2) {
var parameters = get_nodes("parameter", node).map(fun(child: *tree<symbol>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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_node>())
ast_to_syntax.set(to_ret, node)
return to_ret
}
fun transform_branching_statement(node: *tree<symbol>, 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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *ast_node {
return ast_defer_statement_ptr(transform(node->children[0], scope, template_replacements))
}
fun transform_match_statement(node: *tree<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>) to_ret->match_statement.cases.add(transform_case_statement(syntax, scope, template_replacements));)
return to_ret
}
fun transform_case_statement(node: *tree<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<string>()
var type_parameters = vector<*type>()
if (get_nodes("intrinsic_parameter", node).size)
parameters = get_nodes("intrinsic_parameter", node).map(fun(child: *tree<symbol>): string return concat_symbol_tree(child);)
if (get_nodes("type", node).size)
type_parameters = get_nodes("type", node).map(fun(child: *tree<symbol>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *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<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *ast_node return transform_expression(node, scope, search_type::none(), template_replacements)
fun transform_expression(node: *tree<symbol>, scope: *ast_node, searching_for: search_type, template_replacements: map<string, *type>): *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<tree<symbol>>(), scope, type_vec, template_replacements, map<string, *type>());
}
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<ast_node>()
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<string, *type>())
}
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<ast_node>()
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<string, *type>()
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), parameters)
}
fun find_and_make_any_operator_overload_call(func_name: string, parameters: vector<*ast_node>, scope: *ast_node, template_replacements: map<string, *type>): *ast_node {
var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);)
var possible_overload = null<ast_node>()
if ((parameter_types[0]->is_adt() || 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<string, *type>()
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<tree<symbol>>(), 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<tree<symbol>>(), scope, parameter_types, template_replacements, map<string, *type>())
if (possible_overload)
return ast_function_call_ptr(possible_overload, parameters)
return null<ast_node>()
}
fun find_or_instantiate_template_function(name: string, template_inst: *tree<symbol>, scope: *ast_node, param_types: vector<*type>, template_replacements: map<string, *type>, replacements_base: map<string, *type>): *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<type>()
var had_real_types = false
if (template_inst) {
real_types = get_nodes("type", template_inst).map(fun(t: *tree<symbol>): *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<pair<*ast_node, int>>()
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<type>()
// Template Function Instance Inference time
var typed_params = get_nodes("typed_parameter", results[i]->template.syntax_node).map(fun(t: *tree<symbol>): *tree<symbol> 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<ast_node>()
/*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<ast_node>()
}
return fitting_functions.max(fun(a: pair<*ast_node, int>, b: pair<*ast_node, int>): bool return a.second < b.second;).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 method_access = ast_function_call_ptr(get_builtin_function(string("."), 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: string, param_types: vector<*type>): *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(string("drereferencing not 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(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(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<symbol>, param_type: *type, new_map: *map<string, *type>, template_replacements: map<string, *type>) {
/*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<ast_node>()
}
fun identifier_lookup(name: 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<ast_node>()
}
return results[0]
}
fun scope_lookup(name: 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: 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<symbol>): string {
var str.construct(): string
if (node->data.data != "no_value")
str += node->data.data
node->children.for_each(fun(child: *tree<symbol>) str += concat_symbol_tree(child);)
return str
}
fun get_node(lookup: *char, parent: *tree<symbol>): *tree<symbol> {
return get_node(string(lookup), parent)
}
fun get_node(lookup: string, parent: *tree<symbol>): *tree<symbol> {
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<tree<symbol>>()
}
fun get_nodes(lookup: *char, parent: *tree<symbol>): vector<*tree<symbol>> {
return get_nodes(string(lookup), parent)
}
fun get_nodes(lookup: string, parent: *tree<symbol>): vector<*tree<symbol>> {
return parent->children.filter(fun(node: *tree<symbol>):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<symbol>): *tree<symbol> {
if (!source)
return null<tree<symbol>>()
if (source->data.terminal)
return source
if (source->children.size == 0)
return null<tree<symbol>>()
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(message: *char) error(string(message));
fun error(source: *tree<symbol>, message: *char) error(source, string(message));
fun error(message: string) error(null<tree<symbol>>(), message);
fun error(source: *tree<symbol>, message: string) {
println("****ERROR****")
source = get_first_terminal(source)
if (source) {
print(source->data.source + ": " + source->data.position + " ")
}
println(message)
exit(-1)
/*while (true){}*/
}