import symbol:* import tree:* import vec:* import map:* import util:* import str:* import mem:* import io:* import ast_nodes:* import ast_transformation:* import hash_set:* import pass_common:* fun adt_lower(name_ast_map: *map,*ast_node>>, ast_to_syntax: *map<*ast_node, *tree>) { var type_def_option_map = map<*ast_node, vec<*ast_node>>() var visited1 = hash_set<*ast_node>() var visited2 = hash_set<*ast_node>() name_ast_map->for_each(fun(name: str, syntax_ast_pair: pair<*tree,*ast_node>) { var helper_before = fun(node: *ast_node, parent_chain: *stack<*ast_node>) { match(*node) { ast_node::adt_def(backing) { /*println(backing.name + ": transforming!")*/ type_def_option_map[node] = vec<*ast_node>() var replacement = _type_def(backing.name, false); // we're going to be replacing adt_def in the same ptr, so this works replacement->type_def.self_type = node->adt_def.self_type var option_union = _type_def(backing.name + "_union", true); node->adt_def.options.for_each(fun(opt: *ast_node) { if (!opt->identifier.type->is_empty_adt_option()) option_union->type_def.variables.add(_declaration(opt, null())) type_def_option_map[node].add(opt) }) var option_union_type = type_ptr(option_union) option_union->type_def.self_type = option_union_type var option_union_ident = _ident(str("data"), option_union_type, replacement) replacement->type_def.variables.add(_declaration(option_union_ident, null())) add_to_scope("data", option_union_ident, replacement) var flag = _ident(str("flag"), type_ptr(base_type::integer()), replacement) replacement->type_def.variables.add(_declaration(flag, null())) add_to_scope("flag", flag, replacement) add_before_in(option_union, node, parent_chain) var enclosing_scope = node->adt_def.scope[str("~enclosing_scope")][0] var idx = 0 node->adt_def.option_funcs.for_each(fun(func: *ast_node) { var adt_type = replacement->type_def.self_type var block = _code_block() add_to_scope("~enclosing_scope", func, block) func->function.body_statement = block var to_ret = _ident(str("to_ret"), adt_type, block) block->code_block.children.add(_declaration(to_ret, null())) var value = _value(to_string(idx), type_ptr(base_type::integer())) block->code_block.children.add(_assign(make_operator_call(".", vec(to_ret, flag)), value)) var opt = type_def_option_map[node][idx] var lvalue = make_operator_call(".", vec(make_operator_call(".", vec(to_ret, option_union_ident)), opt)) if (func->function.parameters.size) { // do copy_construct if it should block->code_block.children.add(assign_or_copy_construct_statement(lvalue, func->function.parameters[0])) } block->code_block.children.add(_return(to_ret)) add_before_in(func, node, parent_chain) add_to_scope(func->function.name, func, enclosing_scope) add_to_scope("~enclosing_scope", enclosing_scope, func) idx++ }) node->adt_def.regular_funcs.for_each(fun(func: *ast_node) { var block = _code_block() add_to_scope("~enclosing_scope", func, block) func->function.body_statement = block var func_this = func->function.this_param if (func->function.name == "operator==") { var other = func->function.parameters[0] var if_stmt = _if(make_operator_call("!=", vec(make_operator_call("->", vec(func_this, flag)), make_operator_call(".", vec(other, flag))))) if_stmt->if_statement.then_part = _return(_value(str("false"), type_ptr(base_type::boolean()))) block->code_block.children.add(if_stmt) for (var i = 0; i < type_def_option_map[node].size; i++;) { if (get_ast_type(type_def_option_map[node][i])->is_empty_adt_option()) continue var if_stmt_inner = _if(make_operator_call("==", vec(make_operator_call("->", vec(func_this, flag)), _value(to_string(i), type_ptr(base_type::integer()))))) var option = type_def_option_map[node][i] var our_option = make_operator_call(".", vec(make_operator_call("->", vec(func_this, option_union_ident)), option)) var their_option = make_operator_call(".", vec(make_operator_call(".", vec(other, option_union_ident)), option)) if_stmt_inner->if_statement.then_part = _return(possible_object_equality(our_option, their_option)) block->code_block.children.add(if_stmt_inner) } block->code_block.children.add(_return(_value(str("true"), type_ptr(base_type::boolean())))) } else if (func->function.name == "operator!=") { var other = func->function.parameters[0] block->code_block.children.add(_return(make_operator_call("!", vec(make_method_call(func_this, "operator==", vec(other)))))) } else if (func->function.name == "construct") { var value = _value(str("-1"), type_ptr(base_type::integer())) block->code_block.children.add(_assign(make_operator_call("->", vec(func_this, flag)), value)) block->code_block.children.add(_return(func_this)) } else if (func->function.name == "copy_construct") { var other = func->function.parameters[0] block->code_block.children.add(_assign(make_operator_call("->", vec(func_this, flag)), make_operator_call("->", vec(other, flag)))) for (var i = 0; i < type_def_option_map[node].size; i++;) { if (get_ast_type(type_def_option_map[node][i])->is_empty_adt_option()) continue var if_stmt_inner = _if(make_operator_call("==", vec(make_operator_call("->", vec(func_this, flag)), _value(to_string(i), type_ptr(base_type::integer()))))) var option = type_def_option_map[node][i] var our_option = make_operator_call(".", vec(make_operator_call("->", vec(func_this, option_union_ident)), option)) var their_option = make_operator_call(".", vec(make_operator_call("->", vec(other, option_union_ident)), option)) if_stmt_inner->if_statement.then_part = assign_or_copy_construct_statement(our_option, their_option) block->code_block.children.add(if_stmt_inner) } block->code_block.children.add(_return(func_this)) } else if (func->function.name == "operator=") { var other = func->function.parameters[0] block->code_block.children.add(make_method_call(func_this, "destruct", vec<*ast_node>())) block->code_block.children.add(make_method_call(func_this, "copy_construct", vec(make_operator_call("&", vec(other))))) } else if (func->function.name == "destruct") { for (var i = 0; i < type_def_option_map[node].size; i++;) { var option = type_def_option_map[node][i] var option_type = get_ast_type(option) if (option_type->is_empty_adt_option()) continue if (option_type->indirection == 0 && option_type->is_object() && has_method(option_type->type_def, "destruct", vec<*type>())) { var if_stmt_inner = _if(make_operator_call("==", vec(make_operator_call("->", vec(func_this, flag)), _value(to_string(i), type_ptr(base_type::integer()))))) var our_option = make_operator_call(".", vec(make_operator_call("->", vec(func_this, option_union_ident)), option)) if_stmt_inner->if_statement.then_part = make_method_call(our_option, "destruct", vec<*ast_node>()) block->code_block.children.add(if_stmt_inner) } } } else error("impossible adt method") replacement->type_def.methods.add(func) add_to_scope(func->function.name, func, replacement) add_to_scope("~enclosing_scope", replacement, func) }) add_to_scope("~enclosing_scope", enclosing_scope, option_union) add_to_scope("~enclosing_scope", enclosing_scope, replacement) *node = *replacement } } } run_on_tree(helper_before, empty_pass_second_half(), syntax_ast_pair.second, &visited1) }) name_ast_map->for_each(fun(name: str, syntax_ast_pair: pair<*tree,*ast_node>) { var second_helper = fun(node: *ast_node, parent_chain: *stack<*ast_node>) { match(*node) { ast_node::match_statement(backing) { var block = _code_block() add_to_scope("~enclosing_scope", parent_chain->item_from_top_satisfying(fun(i: *ast_node): bool return is_code_block(i) || is_function(i);), block) var value = backing.value var holder = _ident(str("holder"), get_ast_type(value)->clone_with_increased_indirection(), block) block->code_block.children.add(_declaration(holder, null())) block->code_block.children.add(_assign(holder, make_operator_call("&", vec(value)))) backing.cases.for_each(fun(case_stmt: *ast_node) { var option = case_stmt->case_statement.option if (!get_ast_scope(get_ast_type(value)->type_def)->contains_key(str("flag"))) error("trying to get flag from struct without it - are you matching on not an adt? - " + get_ast_type(value)->to_string()) var flag = get_from_scope(get_ast_type(value)->type_def, "flag") var data = get_from_scope(get_ast_type(value)->type_def, "data") var option_num = -7 if (!type_def_option_map.contains_key(get_ast_type(value)->type_def)) error("trying to match on non-adt") for (var i = 0; i < type_def_option_map[get_ast_type(value)->type_def].size; i++;) if (type_def_option_map[get_ast_type(value)->type_def][i] == option) option_num = i; var condition = make_operator_call("==", vec(make_operator_call("->", vec(holder, flag)), _value(to_string(option_num), type_ptr(base_type::integer())))) var if_stmt = _if(condition) var inner_block = _code_block() add_to_scope("~enclosing_scope", block, inner_block) var unpack_ident = case_stmt->case_statement.unpack_ident if (unpack_ident) { var get_option = make_operator_call(".", vec(make_operator_call("->", vec(holder, data)), option)) get_option = make_operator_call("&", vec(get_option)) unpack_ident->identifier.type = unpack_ident->identifier.type->clone_with_ref() inner_block->code_block.children.add(_declaration(unpack_ident, get_option)) } inner_block->code_block.children.add(case_stmt->case_statement.statement) if_stmt->if_statement.then_part = inner_block block->code_block.children.add(if_stmt) }) *node = *block } ast_node::function_call(backing) { if (is_function(backing.func) && (backing.func->function.name == "." || backing.func->function.name == "->")) { var left_type = get_ast_type(backing.parameters[0]) if (left_type->is_object() && is_identifier(backing.parameters[1])) { for (var i = 0; i < left_type->type_def->type_def.variables.size; i++;) if (left_type->type_def->type_def.variables[i]->declaration_statement.identifier == backing.parameters[1]) return; /*println(backing.parameters[1]->identifier.name + " getting, adt . or -> call!")*/ var object = node->function_call.parameters[0] node->function_call.parameters[0] = make_operator_call(backing.func->function.name, vec(object, get_from_scope(left_type->type_def, "data"))) node->function_call.func = get_builtin_function(".", vec(get_ast_type(get_from_scope(left_type->type_def, "data")), get_ast_type(backing.parameters[1]))) } } } } } run_on_tree(second_helper, empty_pass_second_half(), syntax_ast_pair.second, &visited2) }) }