import io:* import grammer:* import lexer:* import parser:* import str:* import serialize:* import os:* import set:* import vec:* import vec_literals:* import poset:* import util:* import ast:* import type2:* import tree:* import symbol:* fun main(argc: int, argv: **char): int { // delay construction until we either load it or copy construct it var gram: grammer var base_dir = str("/").join(str(argv[0]).split('/').slice(0,-2)) var import_paths = vec(str(), base_dir + "/stdlib/") var file_name = base_dir + "/krakenGrammer.kgm" var compiled_name = file_name + str(".comp_new") var compiled_version = 1 var file_contents = read_file(file_name) var loaded_and_valid = false if (argc <= 1) { println("No input file!\n Call with one argument (the input file), or two arguments (input file and output name)") exit(1) } else if (str(argv[1]) == "-v" || str(argv[1]) == "--version") { println("0.0 pre") exit(0) } var opt_str = str("-O2") var compile_c = true var positional_args = vec() for (var i = 1; i < argc; i++;) { var arg_str = str(argv[i]) if (arg_str.length() > 2 && arg_str.slice(0,2) == "-O") { opt_str = arg_str } else if (arg_str == "--no-c-compile") { compile_c = false } else { positional_args.add(arg_str) } } if (file_exists(compiled_name)) { var pos = 0 var binary = read_file_binary(compiled_name) var saved_version = 0 unpack(saved_version, pos) = unserialize(binary, pos) if (saved_version == compiled_version) { var cached_contents = str() unpack(cached_contents, pos) = unserialize(binary, pos) if (cached_contents == file_contents) { loaded_and_valid = true pos = gram.unserialize(binary, pos) } else println("contents different") } else println("version number different") } else { println("cached file does not exist") } if (!loaded_and_valid) { println("Not loaded_and_valid, re-generating and writing out") gram.copy_construct(&load_grammer(file_contents)) println("grammer loaded, calculate_first_set") gram.calculate_first_set() println("grammer loaded, calculate_state_automaton") gram.calculate_state_automaton() println("calculated, writing out") write_file_binary(compiled_name, serialize(compiled_version) + serialize(file_contents) + serialize(gram)) println("done writing") } var lex = lexer(gram.terminals) var parse.construct(&gram, &lex): parser var kraken_file_name = positional_args[0] var executable_name = str(".").join(kraken_file_name.split('.').slice(0,-2)) if (positional_args.size > 1) executable_name = positional_args[1] var pass_poset = poset, str>>() var name_ast_map = map>() var passes = map): void>() // resolves all binding possibilities for one top level item passes[str("name_possibility_resolve")] = fun(item: *tree) { println("Running name possibility resolver?") var scope_lookup: fun(*tree, str, bool): vec<*tree> = fun(scope: *tree, name: str, is_type: bool): vec<*tree> { var to_ret = vec<*tree>() for (var i = 0; i < scope->children.size; i++;) { match(scope->children[i]->data) { ast::_import(b) if b.second.contains(name) || b.second.contains(str("*")) { if !ast_bound(b.first) { // Import / parse file if not already var file_path = ast_binding_str(b.first) if (!name_ast_map.contains_key(file_path)) { printerr(file_path + ", ") var parse_tree = parse.parse_input(read_file(file_path), file_path) trim(parse_tree) name_ast_map[file_path] = syntax_to_ast(file_path, parse_tree, import_paths) printlnerr("syntax_to_ast " + file_path + ":") print_tree(name_ast_map[file_path], 1) } set_ast_binding(b.first, name_ast_map[file_path]) } to_ret += scope_lookup(get_ast_binding(b.first), name, is_type) } ast::_type_def(b) if (is_type && b == name) to_ret += scope->children[i] ast::_adt_def(b) if (is_type && b == name) to_ret += scope->children[i] ast::_function(b) if (!is_type && b.first == name) to_ret += scope->children[i] ast::_template(b) if (!is_type && b.first == name) to_ret += scope->children[i] ast::_identifier(b) if (!is_type && b.first == name) to_ret += scope->children[i] ast::_declaration() if (!is_type && scope->children[i]->children[0]->data._identifier.first == name) to_ret += scope->children[i]->children[0] } } if (scope->parent != null>()) return to_ret + scope_lookup(scope->parent, name, is_type) return to_ret } var try_binding = fun(binding: *tree, start_scope: *tree, type_binding: bool) { if !ast_bound(binding) { var options = scope_lookup(start_scope, binding->data._binding.first, type_binding) if (options.size < 1) error("Could not find any options for scope lookup of " + binding->data._binding.first) set_ast_binding(binding, options[0]) } } var handle_type: fun(*binding, *tree): void = fun(t: *binding, n: *tree) { match(t->bound_to->base) { base_type::_obj(b) try_binding(b, n, true) base_type::_fun(b) { b.first.first.for_each(fun(it: *binding) { handle_type(it, n) }) handle_type(b.first.second, n) } } } var traverse_for_bindings: fun(*tree): void = fun(t: *tree) { match (t->data) { // TODO: Handle type binding lookup ast::_identifier(b) handle_type(b.second, t) /*_binding: triple, *tree>,*/ ast::_function(b) handle_type(b.second, t) ast::_compiler_intrinsic(b) b.second.for_each(fun(tb: *binding) { handle_type(tb, t) }) ast::_cast(b) handle_type(b, t) /*_value: pair*/ ast::_binding(b) try_binding(t, t, false) } t->children.for_each(traverse_for_bindings) } traverse_for_bindings(item) } // resolves all binding possibilities for one top level item passes[str("name_type_resolve")] = fun(item: *tree) { println("Running name type resolver?") // just temp pass_poset.add_close_dep(make_pair(item, str("name_type_resolve")), make_pair(item, str("name_possibility_resolve"))) } // emit C var C_str = str() var C_declaration_str = str() passes[str("emit_C")] = fun(item: *tree) { if !pass_poset.done(make_pair(item, str("name_type_resolve"))) { pass_poset.add_open_dep(make_pair(item, str("emit_C")), make_pair(item, str("name_type_resolve"))) return } var to_c_type = fun(tb: *binding): str { var t = tb->bound_to var ind = str("*") * t->indirection if (t->is_ref) error("type is ref in to_c_type") match(t->base) { base_type::_unknown() error("unknown in to_c_type") base_type::_void() return "void" + ind base_type::_obj(b) error("ob in to_c_type unimplemented") base_type::_fun(b) error("fun in to_c_type unimplemented") base_type::_template_placeholder() error("template_placeholder in to_c_type") base_type::_bool() return "bool" + ind base_type::_char() return "char" + ind base_type::_uchar() return "usigned char" + ind base_type::_short() return "short" + ind base_type::_ushort() return "unsigned short" + ind base_type::_int() return "int" + ind base_type::_uint() return "unsigned int" + ind base_type::_long() return "long" + ind base_type::_ulong() return "unsigned long" + ind base_type::_float() return "float" + ind base_type::_double() return "double" + ind } error("fell through to_c_type") } var get_c_name = fun(x: *tree): str { match(x->data) { ast::_identifier(b) { return b.first; } ast::_type_def(b) { return b; } ast::_function(b) { return b.first; } } error("cannot get_c_name of thing: " + to_string(x->data)) } var emit_C: fun(*tree, int): void = fun(t: *tree, level: int) { var idt = str("\t") * level match (t->data) { ast::_translation_unit(b) { t->children.for_each(fun(c: *tree) { emit_C(c, 0) C_str += ";\n" }) } ast::_import(b) { } ast::_identifier(b) { C_str += idt + b.first; } ast::_binding(b) { var bound_to = b.second->bound_to if (is_top_level_item(bound_to)) pass_poset.add_close_dep(make_pair(item, str("emit_C")), make_pair(bound_to, str("emit_C"))) else if (is_identifier(bound_to) && is_declaration(bound_to->parent) && is_top_level_item(bound_to->parent)) pass_poset.add_close_dep(make_pair(item, str("emit_C")), make_pair(bound_to->parent, str("emit_C"))) C_str += get_c_name(bound_to) } ast::_type_def(b) { error("type_def gen unimplemented"); } ast::_adt_def(b) { error("no adt_def should remain at C emit"); } ast::_function(b) { var fun_name = b.first var fun_type = b.second->bound_to var is_ext = b.third var return_type = fun_type->base._fun.first.second var parameter_types = fun_type->base._fun.first.first var is_variadic = fun_type->base._fun.second var is_raw = fun_type->base._fun.third // TODO check is_ext for name mangling C_str += to_c_type(return_type) + " " + fun_name + "(" C_declaration_str += to_c_type(return_type) + " " + fun_name + "(" for (var i = 0; i < parameter_types.size; i++;) { if (i != 0) { C_str += ", " C_declaration_str += ", " } C_str += to_c_type(parameter_types[i]) + " " C_declaration_str += to_c_type(parameter_types[i]) emit_C(t->children[i], 0) } if (is_variadic) { if (parameter_types.size != 0) { C_str += ", " C_declaration_str += ", " } C_str += "..." C_declaration_str += "..." } C_str += ") {\n" C_declaration_str += ");\n" for (var i = parameter_types.size; i < t->children.size; i++;) { emit_C(t->children[i], level+1) C_str += ";\n" } C_str += "}\n" } ast::_template(b) { /* template should be ignored */ } ast::_declaration() { C_str += idt + to_c_type(t->children[0]->data._identifier.second) + " " + get_c_name(t->children[0]) if (t->children.size > 1) { C_str += " = " emit_C(t->children[1], 0) } if (is_top_level_item(t)) { C_str += ";\n" C_declaration_str += idt + to_c_type(t->children[0]->data._identifier.second) + " " + get_c_name(t->children[0]) + ";\n" } } ast::_assignment() { error("assignment gen unimplemented"); } ast::_block() { C_str += idt + "{\n" t->children.for_each(fun(c: *tree) { emit_C(c, level+1) C_str += ";\n" }) C_str += idt + "}" } ast::_if() { C_str += idt + "if (" emit_C(t->children[0], 0) C_str += ") {\n" emit_C(t->children[1], level + 1) C_str += ";\n" + idt + "}" if t->children.size > 2 { C_str += " else {\n" emit_C(t->children[2], level + 1) C_str += ";\n" + idt + "}" } } ast::_match() { error("no match should remain at C emit"); } ast::_case() { error("no case should remain at C emit"); } ast::_while() { error("while gen unimplemented"); } ast::_for() { error("for gen unimplemented"); } ast::_return() { C_str += idt + "return" if (t->children.size == 1) { C_str += " " emit_C(t->children[0], 0) } } ast::_break() { C_str += idt + "break"; } ast::_continue() { C_str += idt + "continue"; } ast::_defer() { error("no defer should remain at C emit"); } ast::_call() { emit_C(t->children[0], level) C_str += "(" for (var i = 1; i < t->children.size; i++;) { if (i != 1) C_str += ", " emit_C(t->children[i], 0) } C_str += ")" } ast::_compiler_intrinsic(b) { error("compiler_intrinsic gen unimplemented"); } ast::_cast(b) { error("cast gen unimplemented"); } ast::_value(b) { C_str += idt + b.first; } } } emit_C(item, 0) } // We construct our real main entry function and add an emit_C pass for it, // starting generation of the entire program var real_main = _function( str("main"), binding(type(base_type::_fun(make_triple(make_pair(vec( binding(type(base_type::_int(), 0, false)), binding(type(base_type::_char(), 2, false)) ), binding(type(base_type::_int(), 0, false)) ), false, false)), 0, false)), true, vec( _identifier(str("argc"), binding(type(base_type::_int(), 0, false))), _identifier(str("argv"), binding(type(base_type::_char(), 2, false))), _return(vec(_call(vec(make_ast_binding("fmain"), make_ast_binding("argc"), make_ast_binding("argv"))))) ) ) var top_unit = _translation_unit(str(), vec( _import(make_ast_binding(kraken_file_name), set(str("*")), vec( _identifier(kraken_file_name, binding(type(base_type::_void(), 0, false))) )), real_main )) pass_poset.add_job(make_pair(real_main, str("emit_C"))) pass_poset.run(fun(file_pass: pair<*tree, str>) { printlnerr("doing pass new style " + file_pass.second + " on " + to_string(file_pass.first->data)) passes[file_pass.second](file_pass.first) }) C_str = "#include \n" + C_declaration_str + "\n" + C_str println() println() println("Finished with trees:") name_ast_map.for_each(fun(key: str, value: *tree) { printlnerr(key + ":") print_tree(value, 1) printlnerr("done") }) var kraken_c_output_name = kraken_file_name + ".c" println(C_str) write_file(kraken_c_output_name, C_str) var c_flags = str("") if (compile_c) { var compile_string = "cc -g " + opt_str + " -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -Wno-incompatible-pointer-types -std=c99 " + c_flags + " " + kraken_c_output_name + " -o " + executable_name printlnerr(compile_string) system(compile_string) } return 0 } fun parse_type(syntax: *tree): *binding { var is_ref = get_node("\"ref\"", syntax) != null>() var indr = 0 syntax = get_node("pre_reffed", syntax) var next = get_node("pre_reffed", syntax) while(next != null>()) { indr++ syntax = next next = get_node("pre_reffed", syntax) } var ident = get_node("scoped_identifier", syntax) var func = get_node("function_type", syntax) var first_child_name = syntax->children[0]->data.name if (ident != null>()) { var template_inst = get_node("template_inst", syntax) if (template_inst != null>()) { return binding(type(base_type::_obj(make_ast_binding(concat(ident) + "")), indr, is_ref)) } else { return binding(type(base_type::_obj(make_ast_binding(concat(ident))), indr, is_ref)) } } else if (func != null>()) { var param_types = vec<*binding>() var return_type = binding(type(base_type::_void(), 0, false)) var variadic = false var raw = false return binding(type(base_type::_fun(make_triple(make_pair(param_types, return_type), variadic, raw)), indr, is_ref)) } else if (first_child_name == "\"void\"") { return binding(type(base_type::_void(), indr, is_ref)) } else if (first_child_name == "\"char\"") { return binding(type(base_type::_char(), indr, is_ref)) } else if (first_child_name == "\"uchar\"") { return binding(type(base_type::_uchar(), indr, is_ref)) } else if (first_child_name == "\"short\"") { return binding(type(base_type::_short(), indr, is_ref)) } else if (first_child_name == "\"ushort\"") { return binding(type(base_type::_ushort(), indr, is_ref)) } else if (first_child_name == "\"int\"") { return binding(type(base_type::_int(), indr, is_ref)) } else if (first_child_name == "\"uint\"") { return binding(type(base_type::_uint(), indr, is_ref)) } else if (first_child_name == "\"long\"") { return binding(type(base_type::_long(), indr, is_ref)) } else if (first_child_name == "\"ulong\"") { return binding(type(base_type::_ulong(), indr, is_ref)) } else if (first_child_name == "\"float\"") { return binding(type(base_type::_float(), indr, is_ref)) } else if (first_child_name == "\"double\"") { return binding(type(base_type::_double(), indr, is_ref)) } error(syntax, "could not parse type " + first_child_name) } fun syntax_to_ast(file_name: str, syntax: *tree, import_paths: ref vec): *tree { var resolve_import_file = fun(file_name: str): str { var file_path = str() for (var i = 0; i < import_paths.size; i++;) { if (file_exists(import_paths[i] + file_name)) { if (file_path != "") error("File: " + file_name + ", found in multiple import paths - at least two of [" + str(",").join(import_paths) + "]") file_path = import_paths[i] + file_name } } if (file_path == "") error("File: " + file_name + ", not found in any import path - none of [" + str(",").join(import_paths) + "]") return file_path } var syntax_to_ast_helper: fun(*tree): *tree = fun(syntax: *tree): *tree { if (syntax->data.name == "import") { return _import(make_ast_binding(resolve_import_file(concat(syntax->children[1]) + ".krak")), from_vector(syntax->children.slice(2,-1).filter(fun(s:*tree):bool { return s->data.name == "identifier" || s->data.data == "*" }).map(concat)), vec(syntax_to_ast_helper(syntax->children[1]))) } else if (syntax->data.name == "function") { var parameters = get_nodes("typed_parameter", syntax).map(syntax_to_ast_helper) var body = syntax_to_ast_helper(get_node("statement", syntax)) var return_type = null>() var return_type_node = get_node("typed_return", syntax) if (return_type_node != null>()) return_type = parse_type(get_node("type", return_type_node)) else return_type = binding(type(base_type::_void(), 0, false)) var function_type = binding(type(base_type::_fun(make_triple(make_pair(parameters.map(fun(i: *tree): *binding return i->data._identifier.second;), return_type), false, false)), 0, false)) var n = _function(concat(get_node("func_identifier", syntax)), function_type, false, parameters + body) var template = get_node("template_dec", syntax) if (template == null>()) { return n } else { return _template(n->data._function.first, from_vector(get_nodes("template_param", template).map(concat)), vec(n)) } } else if (syntax->data.name == "typed_parameter") return _identifier(concat(get_node("identifier", syntax)), parse_type(get_node("type", syntax))) else if (syntax->data.name == "type_def") { var n = _type_def(concat(get_node("identifier", syntax)), get_nodes("declaration_statement", syntax).map(syntax_to_ast_helper)) var template = get_node("template_dec", syntax) if (template == null>()) { return n } else { return _template(n->data._type_def, from_vector(get_nodes("template_param", template).map(concat)), vec(n)) } } else if (syntax->data.name == "adt_def") { var n = _adt_def(concat(get_node("identifier", syntax)), get_nodes("adt_option", syntax).map(fun(s: *tree): *tree { var option_type = get_node("type", s) if (option_type != null>()) return _identifier(concat(get_node("identifier", s)), parse_type(option_type)) else return _identifier(concat(get_node("identifier", s)), binding(type(base_type::_void(), 0, false))) })) var template = get_node("template_dec", syntax) if (template == null>()) { return n } else { return _template(n->data._adt_def, from_vector(get_nodes("template_param", template).map(concat)), vec(n)) } } else if (syntax->data.name == "statement") return syntax_to_ast_helper(syntax->children[0]) else if (syntax->data.name == "code_block") return _block(syntax->children.map(syntax_to_ast_helper)) else if (syntax->data.name == "if_statement") return _if(syntax->children.map(syntax_to_ast_helper)) else if (syntax->data.name == "return_statement") return _return(syntax->children.map(syntax_to_ast_helper)) else if (syntax->data.name == "defer_statement") return _defer(syntax->children.map(syntax_to_ast_helper)) else if (syntax->data.name == "match_statement") { return _match(vec(syntax_to_ast_helper(get_node("boolean_expression", syntax))) + get_nodes("case_statement", syntax).map(fun(s: *tree): *tree { return _case(s->children.map(syntax_to_ast_helper)) })) } else if (syntax->data.name == "declaration_statement") { var t = binding(type(base_type::_unknown(), 0, false)) var type_syntax = get_node("type", syntax) if type_syntax != null>() t = parse_type(type_syntax) var children = vec(_identifier(concat(get_node("identifier", syntax)), t)) children += get_nodes("boolean_expression", syntax).map(syntax_to_ast_helper) return _declaration(children) } else if (syntax->data.name == "assignment_statement") return _assignment(vec(make_ast_binding(concat(syntax->children[1])), syntax_to_ast_helper(syntax->children[0]), syntax_to_ast_helper(syntax->children[2]))) else if (syntax->data.name == "function_call") return _call(vec(syntax_to_ast_helper(syntax->children[0])) + get_nodes("parameter", syntax).map(fun(s: *tree): *tree { return syntax_to_ast_helper(s->children[0]) })) else if (syntax->data.name == "boolean_expression" || syntax->data.name == "and_boolean_expression" || syntax->data.name == "bitwise_or" || syntax->data.name == "bitwise_xor" || syntax->data.name == "bitwise_and" || syntax->data.name == "bool_exp" || syntax->data.name == "expression" || syntax->data.name == "shiftand" || syntax->data.name == "term" || syntax->data.name == "factor" || syntax->data.name == "unarad" || syntax->data.name == "access_operation") { if (syntax->children.size == 1) { return syntax_to_ast_helper(syntax->children[0]) } else if (syntax->children.size == 2) { var template_inst = get_node("template_inst", syntax) if (template_inst != null>()) { if (syntax->children[0]->data.name != "scoped_identifier") error(syntax, "Unexpected template instantiation (not on an identifier)") return make_ast_binding(concat(syntax->children[0]) + "") } else if (syntax->children[0]->data.terminal) { return _call(vec(make_ast_binding(concat(syntax->children[0])), syntax_to_ast_helper(syntax->children[1]))) } else { return _call(vec(make_ast_binding(concat(syntax->children[1])), syntax_to_ast_helper(syntax->children[0]))) } } else { return _call(vec(make_ast_binding(concat(syntax->children[1])), syntax_to_ast_helper(syntax->children[0]), syntax_to_ast_helper(syntax->children[2]))) } } else if (syntax->data.name == "number") return _value(concat(syntax), binding(type(base_type::_int(), 0, false))) else if (syntax->data.name == "bool") return _value(concat(syntax), binding(type(base_type::_bool(), 0, false))) else if (syntax->data.name == "scoped_identifier" || syntax->data.name == "identifier") return make_ast_binding(concat(syntax)) else { error(syntax, "Cannot transform") return null>() } } var result = _translation_unit(file_name, syntax->children.map(syntax_to_ast_helper)) return result } fun print_tree(t: *tree, level: int) { printlnerr("\t" * level + to_string(t->data)) for (var i = 0; i < t->children.size; i++;) if (t->children[i]) print_tree(t->children[i], level+1) else printlnerr("\t" * (level + 1) + "null!") } fun get_node(lookup: *char, parent: *tree): *tree { return get_node(str(lookup), parent) } fun get_node(lookup: str, parent: *tree): *tree { var results = get_nodes(lookup, parent) if (results.size > 1) error(parent, "get node too many results!") if (results.size) return results[0] return null>() } fun get_nodes(lookup: *char, parent: *tree): vec<*tree> { return get_nodes(str(lookup), parent) } fun get_nodes(lookup: str, parent: *tree): vec<*tree> { return parent->children.filter(fun(node: *tree):bool return node->data.name == lookup;) } fun concat(node: *tree): str { var str.construct(): str if (node->data.data != "no_value") str += node->data.data node->children.for_each(fun(child: *tree) str += concat(child);) return str } fun get_first_terminal(source: *tree): *tree { if (!source) return null>() if (source->data.terminal) return source if (source->children.size == 0) return null>() return get_first_terminal(source->children.first()) } fun error(source: *tree, message: *char) error(source, str(message)); fun error(source: *tree, message: str) { var first = get_first_terminal(source) if (first) error("***error |" + concat(source) + "| *** " + first->data.source + ": " + first->data.position + " " + message) error(message) } fun trim(parse_tree: *tree) { remove_node(symbol("$NULL$", false), parse_tree) remove_node(symbol("WS", false), parse_tree) // the terminals have " around them, which we have to escape remove_node(symbol("\"\\(\"", true), parse_tree) remove_node(symbol("\"\\)\"", true), parse_tree) remove_node(symbol("\"template\"", true), parse_tree) remove_node(symbol("\"return\"", true), parse_tree) remove_node(symbol("\"defer\"", true), parse_tree) remove_node(symbol("\";\"", true), parse_tree) remove_node(symbol("line_end", false), parse_tree) remove_node(symbol("\"{\"", true), parse_tree) remove_node(symbol("\"}\"", true), parse_tree) remove_node(symbol("\"(\"", true), parse_tree) remove_node(symbol("\")\"", true), parse_tree) remove_node(symbol("\"if\"", true), parse_tree) remove_node(symbol("\"else\"", true), parse_tree) remove_node(symbol("\"while\"", true), parse_tree) remove_node(symbol("\"__if_comp__\"", true), parse_tree) remove_node(symbol("\"comp_simple_passthrough\"", true), parse_tree) /*remove_node(symbol("obj_nonterm", false), parse_tree)*/ remove_node(symbol("adt_nonterm", false), parse_tree) collapse_node(symbol("case_statement_list", false), parse_tree) collapse_node(symbol("opt_param_assign_list", false), parse_tree) collapse_node(symbol("param_assign_list", false), parse_tree) collapse_node(symbol("opt_typed_parameter_list", false), parse_tree) collapse_node(symbol("opt_parameter_list", false), parse_tree) collapse_node(symbol("intrinsic_parameter_list", false), parse_tree) collapse_node(symbol("identifier_list", false), parse_tree) collapse_node(symbol("adt_option_list", false), parse_tree) collapse_node(symbol("statement_list", false), parse_tree) collapse_node(symbol("parameter_list", false), parse_tree) collapse_node(symbol("typed_parameter_list", false), parse_tree) collapse_node(symbol("unorderd_list_part", false), parse_tree) collapse_node(symbol("if_comp_pred", false), parse_tree) collapse_node(symbol("declaration_block", false), parse_tree) collapse_node(symbol("type_list", false), parse_tree) collapse_node(symbol("opt_type_list", false), parse_tree) collapse_node(symbol("template_param_list", false), parse_tree) collapse_node(symbol("trait_list", false), parse_tree) collapse_node(symbol("dec_type", false), parse_tree) } fun remove_node(remove: symbol, parse_tree: *tree) { var to_process = stack<*tree>() to_process.push(parse_tree) while(!to_process.empty()) { var node = to_process.pop() for (var i = 0; i < node->children.size; i++;) { if (!node->children[i] || node->children[i]->data.equal_wo_data(remove)) { node->children.remove(i) i--; } else { to_process.push(node->children[i]) } } } } fun collapse_node(remove: symbol, parse_tree: *tree) { var to_process = stack<*tree>() to_process.push(parse_tree) while(!to_process.empty()) { var node = to_process.pop() for (var i = 0; i < node->children.size; i++;) { if (node->children[i]->data.equal_wo_data(remove)) { var add_children = node->children[i]->children; // stick child's children between the current children divided // on i, without including i node->children = node->children.slice(0,i) + add_children + node->children.slice(i+1,-1) i--; } else { to_process.push(node->children[i]) } } } }