From 6fee942756f4707106fbb1e3a50d1d45db52a1a5 Mon Sep 17 00:00:00 2001 From: Nathan Braswell Date: Sun, 3 Jul 2016 15:32:45 -0700 Subject: [PATCH] Refactored interpreter into just functions, added a REPL to the main kraken. --- kraken.krak | 78 +-- stdlib/ast_transformation.krak | 229 +------- stdlib/c_generator.krak | 23 +- stdlib/importer.krak | 162 +++--- stdlib/interpreter.krak | 998 +++++++++++++++------------------ stdlib/io.krak | 14 + stdlib/pass_common.krak | 230 +++++++- stdlib/util.krak | 8 + 8 files changed, 846 insertions(+), 896 deletions(-) diff --git a/kraken.krak b/kraken.krak index 3ab08ff..2492bfa 100644 --- a/kraken.krak +++ b/kraken.krak @@ -16,33 +16,7 @@ import c_line_control:* import c_generator:* import compiler_version - fun main(argc: int, argv: **char):int { - if (argc <= 1) { - error("No input file!\n Call with one argument (the input file), or two arguments (input file and output name)") - } else if (string(argv[1]) == "-v" || string(argv[1]) == "--version") { - println(compiler_version::version_string) - exit(0) - } - var input_file_offset = 1 - var interpret_instead = false - var argv1_str = string(argv[1]) - var opt_str = string("-O3") - var line_ctrl = false - if (argv1_str == "-i") { - interpret_instead = true - input_file_offset++ - } else if (argv1_str.length() > 2 && argv1_str.slice(0,2) == "-O") { - opt_str = argv1_str - input_file_offset++ - } else if (argv1_str == "-g") { - line_ctrl = true - input_file_offset++ - } - var kraken_file_name = string(argv[input_file_offset]) - var executable_name = string(".").join(kraken_file_name.split('.').slice(0,-2)) - if (argc == input_file_offset+2) - executable_name = string(argv[input_file_offset+1]) // delay construction until we either load it or copy construct it var gram: grammer var base_dir = string("/").join(string(argv[0]).split('/').slice(0,-2)) @@ -51,19 +25,44 @@ fun main(argc: int, argv: **char):int { var compiled_version = 1 var file_contents = read_file(file_name) var loaded_and_valid = false + var doing_repl = false + + if (argc <= 1) { + println("No input file!\n Call with one argument (the input file), or two arguments (input file and output name)\n Falling into REPL...") + compiled_name += ".expr" + file_contents = string("RealGoal = boolean_expression ;\n") + file_contents + doing_repl = true + } else if (string(argv[1]) == "-v" || string(argv[1]) == "--version") { + println(compiler_version::version_string) + exit(0) + } + var input_file_offset = 1 + var interpret_instead = false + var opt_str = string("-O3") + var line_ctrl = false + if (!doing_repl) { + var argv1_str = string(argv[1]) + if (argv1_str == "-i") { + interpret_instead = true + input_file_offset++ + } else if (argv1_str.length() > 2 && argv1_str.slice(0,2) == "-O") { + opt_str = argv1_str + input_file_offset++ + } else if (argv1_str == "-g") { + line_ctrl = true + input_file_offset++ + } + } if (file_exists(compiled_name)) { - /*println("cached file exists")*/ var pos = 0 var binary = read_file_binary(compiled_name) - /*println("read file!")*/ var saved_version = 0 unpack(saved_version, pos) = unserialize(binary, pos) if (saved_version == compiled_version) { var cached_contents = string() unpack(cached_contents, pos) = unserialize(binary, pos) if (cached_contents == file_contents) { - /*println("loaded_and_valid, using cached version!")*/ loaded_and_valid = true pos = gram.unserialize(binary, pos) } else println("contents different") @@ -73,7 +72,6 @@ fun main(argc: int, argv: **char):int { } if (!loaded_and_valid) { println("Not loaded_and_valid, re-generating and writing out") - /*gram = load_grammer(file_contents)*/ // since we now don't construct before hand gram.copy_construct(&load_grammer(file_contents)) println("grammer loaded, calculate_first_set") @@ -98,6 +96,22 @@ fun main(argc: int, argv: **char):int { /*var parsers = vector(parse1,parse2,parse3,parse4)*/ /*var parsers = vector(parse1,parse2,parse3,parse4,parse5,parse6)*/ /*var parsers = vector(parse1,parse2,parse3,parse4,parse5,parse6,parse7,parse8)*/ + + // This is our REPL loop + var scope = ast_translation_unit_ptr(string("stdin")) + while (doing_repl) { + var line = get_line(string("> "), 100) + if (line == "end") + return 0 + var trimmed_parse = trim(parse1.parse_input(line, string("stdin"))) + var ast_expression = ast_pass.transform_expression(trimmed_parse, scope, map()) + print_value(evaluate_constant_expression(ast_expression)) + } + + var kraken_file_name = string(argv[input_file_offset]) + var executable_name = string(".").join(kraken_file_name.split('.').slice(0,-2)) + if (argc == input_file_offset+2) + executable_name = string(argv[input_file_offset+1]) var importer.construct(parsers, ast_pass, vector(string(), base_dir + "/stdlib/")): importer importer.import(kraken_file_name) // Passes @@ -109,8 +123,7 @@ fun main(argc: int, argv: **char):int { defer_lower(&importer.name_ast_map, &importer.ast_pass.ast_to_syntax) if (interpret_instead) { printlnerr("Interpreting!") - var interpret.construct(importer.name_ast_map, importer.ast_pass.ast_to_syntax): interpreter - interpret.call_main() + call_main(importer.name_ast_map) } else { if (line_ctrl) { printlnerr("running C-specific passes") @@ -122,7 +135,6 @@ fun main(argc: int, argv: **char):int { var c_output_pair = c_generator.generate_c(importer.name_ast_map, importer.ast_pass.ast_to_syntax) var kraken_c_output_name = kraken_file_name + ".c" write_file(kraken_c_output_name, c_output_pair.first) - /*println(string("linker string: ") + c_output_pair.second)*/ var compile_string = "cc -g " + opt_str + " -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -std=c99 " + c_output_pair.second + " " + kraken_c_output_name + " -o " + executable_name printlnerr(compile_string) system(compile_string) diff --git a/stdlib/ast_transformation.krak b/stdlib/ast_transformation.krak index ce1f65b..efba91c 100644 --- a/stdlib/ast_transformation.krak +++ b/stdlib/ast_transformation.krak @@ -11,6 +11,7 @@ import os:* import importer:* import ast_nodes:* import type:* +import pass_common:* adt search_type { none, @@ -1116,74 +1117,6 @@ obj ast_transformation (Object) { return fitting_functions.max(fun(a: pair<*ast_node, int>, b: pair<*ast_node, int>): bool return a.second < b.second;).first } } -fun get_from_scope(node: *ast_node, member: *char): *ast_node - return get_from_scope(node, string(member)) -fun get_from_scope(node: *ast_node, member: string): *ast_node - return get_ast_scope(node)->get(member).first() -fun has_method(object: *ast_node, name: *char, parameter_types: vector<*type>): bool return has_method(object, string(name), parameter_types); -fun has_method(object: *ast_node, name: string, parameter_types: vector<*type>): bool { - /*println("HAS METHOD:")*/ - var to_ret = function_lookup(name, object, parameter_types) || false - /*println("HAS METHOD result:")*/ - /*println(to_ret)*/ - return to_ret -} -fun make_method_call(object_ident: *ast_node, name: *char, parameters: vector<*ast_node>): *ast_node return make_method_call(object_ident, string(name), parameters); -fun make_method_call(object_ident: *ast_node, name: string, parameters: vector<*ast_node>): *ast_node { - /*println("MAKE METHOD CALL OUT:")*/ - // note that this type_def is the adt_def if this is an adt type - var method = function_lookup(name, get_ast_type(object_ident)->type_def, parameters.map(fun(param: *ast_node): *type return get_ast_type(param);)) - /*print("Here is the Method: ")*/ - /*println(method)*/ - return make_method_call(object_ident, method, parameters) -} -fun make_method_call(object_ident: *ast_node, method: *ast_node, parameters: vector<*ast_node>): *ast_node { - /*println("MAKE METHOD CALL IN:")*/ - var access_op = "." - if (get_ast_type(object_ident)->indirection) - access_op = "->" - var method_access = ast_function_call_ptr(get_builtin_function(string(access_op), vector(get_ast_type(object_ident), get_ast_type(method))), vector(object_ident, method)) - return ast_function_call_ptr(method_access, parameters) -} -fun make_operator_call(func: *char, params: vector<*ast_node>): *ast_node return make_operator_call(string(func), params); -fun make_operator_call(func: string, params: vector<*ast_node>): *ast_node { - return ast_function_call_ptr(get_builtin_function(func, params.map(fun(p:*ast_node): *type return get_ast_type(p);)), params) -} -fun get_builtin_function(name: *char, param_types: vector<*type>): *ast_node - return get_builtin_function(string(name), param_types, null>()) -fun get_builtin_function(name: string, param_types: vector<*type>): *ast_node - return get_builtin_function(name, param_types, null>()) -fun get_builtin_function(name: string, param_types: vector<*type>, syntax: *tree): *ast_node { - // none of the builtin functions should take in references - param_types = param_types.map(fun(t: *type): *type return t->clone_without_ref();) - if (name == "==" || name == "!=" || name == ">" || name == "<" || name == "<=" || name == ">" || name == ">=" || name == "&&" || name == "||" || name == "!") - return ast_function_ptr(name, type_ptr(param_types, type_ptr(base_type::boolean())), vector<*ast_node>(), false) - if (name == "." || name == "->") { - if (name == "->" && param_types[0]->indirection == 0) - error(syntax, string("drereferencing not a pointer: ") + name) - else if (name == "." && param_types[0]->indirection != 0) - error(syntax, string("dot operator on a pointer: ") + name) - else - return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>(), false) - } - if (name == "[]") { - if (param_types[0]->indirection == 0) - error(syntax, string("drereferencing not a pointer: ") + name) - else - return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_decreased_indirection()), vector<*ast_node>(), false) - } - if (name == "&" && param_types.size == 1) - return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_increased_indirection()), vector<*ast_node>(), false) - if (name == "\*" && param_types.size == 1) { - if (param_types[0]->indirection == 0) - error(syntax, string("drereferencing not a pointer: ") + name) - else - return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_decreased_indirection()), vector<*ast_node>(), false) - } - if (param_types.size > 1 && param_types[1]->rank() > param_types[0]->rank()) - return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>(), false) - return ast_function_ptr(name, type_ptr(param_types, param_types[0]), vector<*ast_node>(), false) -} fun unify_type(template_type: *tree, param_type: *type, new_map: *map, template_replacements: map) { /*println(string("Unifying type: ") + concat_symbol_tree(template_type))*/ // first get rid of the reference if we have it - we don't care for unification @@ -1247,163 +1180,3 @@ fun unify_type(template_type: *tree, param_type: *type, new_map: *map): bool { - var func_type = get_ast_type(node) - var func_param_types = func_type->parameter_types - var param_string = string() - param_types.for_each(fun(t: *type) param_string += t->to_string() + ", ";) - if (!func_type->is_variadic && func_param_types.size != param_types.size) { - /*println(string("type sizes don't match ") + param_types.size + " with needed " + param_string)*/ - return false - } else if (param_types.size < func_param_types.size) { - return false - } - // note we iterate over the func_param_types which will stop short if function is variadic - // just like we want - for (var j = 0; j < func_param_types.size; j++;) { - // don't care about references - if (!func_param_types[j]->equality(param_types[j], false)) { - /*println(string("types don't match ") + func_param_types[j]->to_string() + " with needed " + param_types[j]->to_string())*/ - if (func_param_types[j]->to_string() == param_types[j]->to_string()) - error(string("types aren't equal, but their string rep is (and ref doesn't even matter): ") + func_param_types[j]->to_string() + " vs " + param_types[j]->to_string() ) - return false - } - } - return true -} -fun function_lookup(name: string, scope: *ast_node, param_types: vector<*type>): *ast_node { - /*println(string("doing function lookup for: ") + name)*/ - var results = scope_lookup(name, scope) - /*print(results.size); println(" number of results")*/ - for (var i = 0; i < results.size; i++;) { - if ((is_function(results[i]) || (is_identifier(results[i]) && get_ast_type(results[i])->is_function())) && function_satisfies_params(results[i], param_types)) { - return results[i] - } - } - /*println(string("function lookup failed for ") + name)*/ - return null() -} -fun identifier_lookup(name: ref string, scope: *ast_node): *ast_node { - /*println(string("doing identifier lookup for: ") + name)*/ - var results = scope_lookup(name, scope) - if (!results.size) { - /*println(string("identifier lookup failed for ") + name)*/ - return null() - } - return results[0] -} -fun scope_lookup(name: ref string, scope: *ast_node): vector<*ast_node> { - // println("*****Doing a name lookup for*****") - // println(name) - var results = vector(scope) - name.split("::").for_each(fun(i: string) { - // println(string("based on split, looking up: ") + i) - var next_results = vector<*ast_node>() - results.for_each(fun(s: *ast_node) { - // print("looking in scope: ") - // println(s) - scope_lookup_helper(i, s, set<*ast_node>()).for_each(fun (result: *ast_node) { - if (!next_results.contains(result)) - next_results.add(result) - }) - }) - results = next_results - }) - return results -} -fun scope_lookup_helper(name: ref string, scope: *ast_node, visited: set<*ast_node>): vector<*ast_node> { - // need to do properly scopded lookups - // print("scope is: ") - // get_ast_scope(scope)->for_each(fun(key: string, value: vector<*ast_node>) print(key + " ");) - // println() - var results = vector<*ast_node>() - // prevent re-checking the same one... - if (visited.contains(scope)) - return results - visited.add(scope) - if (get_ast_scope(scope)->contains_key(name)) { - // println(name + " is in scope, adding to results") - results += get_ast_scope(scope)->get(name) - } - if (get_ast_scope(scope)->contains_key(string("~enclosing_scope"))) - results += scope_lookup_helper(name, get_ast_scope(scope)->get(string("~enclosing_scope"))[0], visited) - if (is_translation_unit(scope)) { - scope->translation_unit.children.for_each(fun(child: *ast_node) { - if (is_import(child)) { - if (child->import.imported.contains(name)) { - // println(name + " is indeed imported") - results += scope_lookup_helper(name, child->import.translation_unit, visited) - } else if (child->import.starred) { - // println("import has an import *, checking along it") - results += scope_lookup_helper(name, child->import.translation_unit, visited) - } else { - // println(name + " is not imported (this time)") - // print("import imports") - // child->import.imported.for_each(fun(it: string) print(it + " ");) - } - } - }) - } - return results -} - -fun concat_symbol_tree(node: *tree): string { - var str.construct(): string - if (node->data.data != "no_value") - str += node->data.data - node->children.for_each(fun(child: *tree) str += concat_symbol_tree(child);) - return str -} - -fun get_node(lookup: *char, parent: *tree): *tree { - return get_node(string(lookup), parent) -} -fun get_node(lookup: string, parent: *tree): *tree { - var results = get_nodes(lookup, parent) - if (results.size > 1) - error(parent, "get node too many results!") - if (results.size) - return results[0] - return null>() -} -fun get_nodes(lookup: *char, parent: *tree): vector<*tree> { - return get_nodes(string(lookup), parent) -} -fun get_nodes(lookup: string, parent: *tree): vector<*tree> { - return parent->children.filter(fun(node: *tree):bool return node->data.name == lookup;) -} -fun add_to_scope(name: *char, to_add: *ast_node, add_to: *ast_node) { - add_to_scope(string(name), to_add, add_to) -} -fun add_to_scope(name: string, to_add: *ast_node, add_to: *ast_node) { - var add_to_map = get_ast_scope(add_to) - if (add_to_map->contains_key(name)) - (*add_to_map)[name].add(to_add) - else - add_to_map->set(name, vector(to_add)) -} -fun get_first_terminal(source: *tree): *tree { - if (!source) - return null>() - if (source->data.terminal) - return source - if (source->children.size == 0) - return null>() - return get_first_terminal(source->children.first()) -} -fun assert(works: bool, message: *char) { - if (!works) - error(message) -} -fun assert(works: bool, message: string) { - if (!works) - error(message) -} -fun error(source: *tree, message: *char) error(source, string(message)); -fun error(source: *tree, message: string) { - source = get_first_terminal(source) - if (source) - error(source->data.source + ": " + source->data.position + " " + message) - error(message) -} - diff --git a/stdlib/c_generator.krak b/stdlib/c_generator.krak index 2ce4eb2..f82e6c7 100644 --- a/stdlib/c_generator.krak +++ b/stdlib/c_generator.krak @@ -8,7 +8,7 @@ import tree:* import symbol:* import ast_nodes:* // for error with syntax tree -import ast_transformation:* +import pass_common:* import poset:* fun code_triple(): code_triple return code_triple(string(), string(), string()); @@ -72,27 +72,6 @@ obj code_triple (Object) { return pre+value+post } } -fun is_dot_style_method_call(node: *ast_node): bool { - return is_function_call(node->function_call.func) && - is_function(node->function_call.func->function_call.func) && - (node->function_call.func->function_call.func->function.name == "->" || node->function_call.func->function_call.func->function.name == ".") && - is_function(node->function_call.func->function_call.parameters[1]) && - (is_type_def(get_ast_scope(node->function_call.func->function_call.parameters[1])->get(string("~enclosing_scope"))[0]) || - // or if it's a templated method (yes, this has gotten uuuuugly) - is_type_def(get_ast_scope(get_ast_scope(node->function_call.func->function_call.parameters[1])->get(string("~enclosing_scope"))[0])->get(string("~enclosing_scope"))[0]) || - // or it's in an adt - is_adt_def(get_ast_scope(node->function_call.func->function_call.parameters[1])->get(string("~enclosing_scope"))[0])) - // should get uglier when we have to figure out if it's just an inside lambda -} -fun method_in_object(method: *ast_node, enclosing_object: *ast_node): bool { - var methods = enclosing_object->type_def.methods - for (var i = 0; i < methods.size; i++;) { - if (methods[i] == method || (is_template(methods[i]) && methods[i]->template.instantiated.contains(method))) { - return true - } - } - return false -} obj c_generator (Object) { var id_counter: int var ast_to_syntax: map<*ast_node, *tree> diff --git a/stdlib/importer.krak b/stdlib/importer.krak index b339f97..aa5ce34 100644 --- a/stdlib/importer.krak +++ b/stdlib/importer.krak @@ -104,89 +104,91 @@ obj importer (Object) { printlnerr("**Fourth Pass**") name_ast_map.for_each(fun(name: string, tree_pair: pair<*tree, *ast_node>) ast_pass.fourth_pass(tree_pair.first, tree_pair.second);) } - 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("\"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 = queue<*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)) { - /* - if (!node->children[i]) - println("not because null") - else { - print("not because "); println(remove.name) - } - */ - node->children.remove(i) - i--; - } else { - /*println(remove.to_string() + " not equal " + node->children[i]->data.to_string())*/ - to_process.push(node->children[i]) - } - } - } - } - fun collapse_node(remove: symbol, parse_tree: *tree) { - var to_process = queue<*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]) +fun trim(parse_tree: *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("\"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) + + return parse_tree +} +fun remove_node(remove: symbol, parse_tree: *tree) { + var to_process = queue<*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)) { + /* + if (!node->children[i]) + println("not because null") + else { + print("not because "); println(remove.name) } + */ + node->children.remove(i) + i--; + } else { + /*println(remove.to_string() + " not equal " + node->children[i]->data.to_string())*/ + to_process.push(node->children[i]) + } + } + } +} +fun collapse_node(remove: symbol, parse_tree: *tree) { + var to_process = queue<*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]) } } } } - diff --git a/stdlib/interpreter.krak b/stdlib/interpreter.krak index 2d677fb..53cecc5 100644 --- a/stdlib/interpreter.krak +++ b/stdlib/interpreter.krak @@ -1,4 +1,3 @@ -import io:* import mem:* import math:* import map:* @@ -9,12 +8,10 @@ import tree:* import symbol:* import ast_nodes:* import type:* -import ast_transformation:* +import os:* // for is_dot_style_method_call -import c_generator:* +import pass_common:* -// there is never an object literal/primitive -// they remain wrapped in a variable value adt value { boolean: bool, character: char, @@ -101,7 +98,6 @@ fun wrap_value(val: *ast_node): value { else if (value_str == "false") return value::boolean(false) else { - // should differentiate between float and double... var contains_dot = false for (var i = 0; i < value_str.length(); i++;) { if (value_str[i] == '.') { @@ -208,9 +204,9 @@ fun do_basic_op(func_name: string, a: value, b: value): value { } else if (func_name == "-") { ptr = ((av.first) cast *char - inc_in_bytes) cast *void } else { - println(string("pointer arithmatic is not + or -: ") + func_name + ", b is: ") + println(string("pointer arithmatic is not +, -, or ==: ") + func_name + ", b is: ") print_value(b) - error(string("pointer arithmatic is not + or -: ") + func_name) + error(string("pointer arithmatic is not +, -, or ==: ") + func_name) } return value::pointer(make_pair(ptr, av.second)) } @@ -336,7 +332,6 @@ fun get_real_value(v: value): value { if (var_type->indirection) return value::pointer(make_pair(*(var_ptr) cast **void, var_type)) match (var_type->base) { - // really this could just be make_pair(variable) base_type::object() return value::object_like(make_pair(var_ptr, var_type)) base_type::boolean() return value::boolean(*(var_ptr) cast *bool) base_type::character() return value::character(*(var_ptr) cast *char) @@ -360,10 +355,7 @@ fun wrap_into_variable(v: value): value { return value::variable(make_pair(v.object_like.first, v.object_like.second)) var variable_type = get_type_from_primitive_value(v) var to_ret = value::variable(make_pair(malloc(type_size(variable_type)), variable_type)) - /*if (is_function(v))*/ - /*(to_ret.variable.first) cast *pair<*ast_node,map<*ast_node,value>> ->copy_construct(&v.function)*/ - /*else*/ - store_into_variable(to_ret, v) + store_into_variable(to_ret, v) return to_ret } fun get_type_from_primitive_value(v: value): *type { @@ -408,9 +400,6 @@ fun cast_value(v: value, to_type: *type): value { } match (to_type->base) { // object_like can't be casted - /*base_type::object() return #sizeof<>*/ - /*base_type::adt() return #sizeof<>*/ - /*base_type::function() return #sizeof<>*/ base_type::boolean() return cast_value_second_half(v) base_type::character() return cast_value_second_half(v) base_type::ucharacter() return cast_value_second_half(v) @@ -510,548 +499,495 @@ fun offset_into_struct(struct_type: *type, ident: *ast_node): ulong { size += type_size(struct_type->type_def->type_def.variables[i]->declaration_statement.identifier->identifier.type) return size } +// to dereference, we basically take the pointer's value (maybe going through a variable to get it) +// and re-wrap it up into a variable value (so it can be assigned to, etc) fun dereference_pointer_into_variable(dereference_val: value): value return value::variable(make_pair(get_real_value(dereference_val).pointer.first, dereference_val.pointer.second->clone_with_decreased_indirection())) fun pop_and_free(var_stack: *stack>) { var_stack->pop().for_each(fun(k: *ast_node, v: value) { match(v) { value::variable(backing) { - /*if (backing.second->is_function())*/ - /*(backing.first) cast *pair<*ast_node,map<*ast_node,value>> ->destruct()*/ /*free(backing.first)*/ } } }) } -obj interpreter (Object) { - var ast_to_syntax: map<*ast_node, *tree> - var name_ast_map: map,*ast_node>> - var globals: map<*ast_node, value> - var id_counter: int - fun get_id(): string return to_string(id_counter++); - fun construct(name_ast_map_in: map,*ast_node>>, ast_to_syntax_in: map<*ast_node, *tree>): *interpreter { - name_ast_map.copy_construct(&name_ast_map_in) - ast_to_syntax.copy_construct(&ast_to_syntax_in) - globals.construct() - id_counter = 0 - return this - } - fun operator=(old: ref interpreter) { - destruct() - copy_construct(&old) - } - fun copy_construct(old: *interpreter) { - name_ast_map.copy_construct(&old->name_ast_map) - ast_to_syntax.copy_construct(&old->ast_to_syntax) - globals.copy_construct(&old->globals) - id_counter == old->id_counter - } - fun destruct() { - name_ast_map.destruct() - ast_to_syntax.destruct() - globals.destruct() - } - fun call_main() { - var results = vector<*ast_node>() - name_ast_map.for_each(fun(key: string, value: pair<*tree,*ast_node>) { - results += scope_lookup(string("main"), value.second) - }) - if (results.size != 1) - error(string("wrong number of mains to call: ") + results.size) - printlnerr("=============") - printlnerr("setting up globals!") - printlnerr("=============") - setup_globals(); - printlnerr("=============") - printlnerr("calling main!") - printlnerr("=============") - var var_stack = stack>() - var_stack.push(map<*ast_node,value>()) - var result = call_function(results[0], vector(), vector<*ast_node>(), &var_stack, map<*ast_node,value>(), value::void_nothing(), value::void_nothing(), null()) - printlnerr("=============") - printlnerr("Done!") - printlnerr("=============") - pop_and_free(&var_stack) - } - fun setup_globals() { - name_ast_map.for_each(fun(key: string, value: pair<*tree,*ast_node>) { - value.second->translation_unit.children.for_each(fun(child: *ast_node) { - if (is_declaration_statement(child)) { - var declaration = child->declaration_statement - var identifier = declaration.identifier->identifier - if (declaration.is_extern) { - if (identifier.name == "stderr") { - var stderr_type = type_ptr(base_type::void_return(), 1) - var stderr_pointer = malloc(type_size(stderr_type)) - *(stderr_pointer) cast **void = stderr; - globals[declaration.identifier] = value::variable(make_pair(stderr_pointer, stderr_type)) - } else { - error(string("unknown extern: ") + identifier.name) - } +fun call_main(name_ast_map: ref map,*ast_node>>) { + var results = vector<*ast_node>() + name_ast_map.for_each(fun(key: string, value: pair<*tree,*ast_node>) { + results += scope_lookup(string("main"), value.second) + }) + if (results.size != 1) + error(string("wrong number of mains to call: ") + results.size) + var globals = setup_globals(name_ast_map) + var var_stack = stack>() + var_stack.push(map<*ast_node,value>()) + var result = call_function(results[0], vector(), vector<*ast_node>(), &var_stack, map<*ast_node,value>(), value::void_nothing(), value::void_nothing(), null(), &globals) + pop_and_free(&var_stack) +} +fun evaluate_constant_expression(node: *ast_node): value { + return interpret(node, null>>(), value::void_nothing(), null(), null>()).first +} +fun setup_globals(name_ast_map: ref map,*ast_node>>): map<*ast_node, value> { + var globals = map<*ast_node, value>() + name_ast_map.for_each(fun(key: string, value: pair<*tree,*ast_node>) { + value.second->translation_unit.children.for_each(fun(child: *ast_node) { + if (is_declaration_statement(child)) { + var declaration = child->declaration_statement + var identifier = declaration.identifier->identifier + if (declaration.is_extern) { + if (identifier.name == "stderr") { + var stderr_type = type_ptr(base_type::void_return(), 1) + var stderr_pointer = malloc(type_size(stderr_type)) + *(stderr_pointer) cast **void = stderr; + globals[declaration.identifier] = value::variable(make_pair(stderr_pointer, stderr_type)) + } else if (identifier.name == "stdin") { + var stdin_type = type_ptr(base_type::void_return(), 1) + var stdin_pointer = malloc(type_size(stdin_type)) + *(stdin_pointer) cast **void = stdin; + globals[declaration.identifier] = value::variable(make_pair(stdin_pointer, stdin_type)) } else { - globals[declaration.identifier] = value::variable(make_pair(calloc(type_size(identifier.type)), identifier.type)) - if (declaration.expression) - store_into_variable(globals[declaration.identifier], get_real_value(interpret(declaration.expression, null>>(), value::void_nothing(), null()).first)) + error(string("unknown extern: ") + identifier.name) } + } else { + globals[declaration.identifier] = value::variable(make_pair(calloc(type_size(identifier.type)), identifier.type)) + if (declaration.expression) + store_into_variable(globals[declaration.identifier], get_real_value(interpret(declaration.expression, null>>(), value::void_nothing(), null(), null>()).first)) } - }) + } + }) + }) + return globals +} +fun interpret_function_call(func_call: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var func_call_parameters = func_call->function_call.parameters + var func_call_func = func_call->function_call.func + var new_enclosing_object = value::void_nothing() + var dot_style_method_call = is_dot_style_method_call(func_call) + var possible_closure_map = map<*ast_node,value>() + // test for function value + if (!dot_style_method_call && (!is_function(func_call_func) || func_call_func->function.closed_variables.size())) { + var func_value = get_real_value(interpret(func_call_func, var_stack, enclosing_object, enclosing_func, globals).first) + func_call_func = func_value.function.first + possible_closure_map = *func_value.function.second + // if the closure closes over this, put it as the enclosing object inside the closure + possible_closure_map.for_each(fun(key: *ast_node, v: value) { + if (key->identifier.name == "this") { + new_enclosing_object = get_real_value(dereference_pointer_into_variable(v)) + } }) } - fun interpret_function_call(func_call: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var func_call_parameters = func_call->function_call.parameters - var func_call_func = func_call->function_call.func - var new_enclosing_object = value::void_nothing() - var dot_style_method_call = is_dot_style_method_call(func_call) - var possible_closure_map = map<*ast_node,value>() - // test for function value - if (!dot_style_method_call && (!is_function(func_call_func) || func_call_func->function.closed_variables.size())) { - /*error("func_call_func is not a function")*/ - /*println(string("func_call_func is not a function: ") + get_ast_name(func_call_func))*/ - var func_value = get_real_value(interpret(func_call_func, var_stack, enclosing_object, enclosing_func).first) - func_call_func = func_value.function.first - possible_closure_map = *func_value.function.second - // if the closure closes over this, put it as the enclosing object inside the closure - possible_closure_map.for_each(fun(key: *ast_node, v: value) { - if (key->identifier.name == "this") { - new_enclosing_object = get_real_value(dereference_pointer_into_variable(v)) - } - }) - /*println("possible_closure_map is")*/ - /*possible_closure_map.for_each(fun(key: *ast_node, v: value) print(get_ast_name(key) + " ");)*/ - /*println()*/ - /*println("end pcm")*/ - /*println(string("calling func we got from interpreting: ") + get_ast_name(func_call_func))*/ + // note here also that this is likely not a foolproof method + if (dot_style_method_call) { + new_enclosing_object = get_real_value(interpret(func_call_func->function_call.parameters[0], var_stack, enclosing_object, enclosing_func, globals).first) + // do a dereference + if (is_pointer(new_enclosing_object)) + new_enclosing_object = get_real_value(value::variable(make_pair(new_enclosing_object.pointer.first, new_enclosing_object.pointer.second->clone_with_decreased_indirection()))) + func_call_func = func_call_func->function_call.parameters[1] + } else if (!is_void_nothing(enclosing_object)) { + if (method_in_object(func_call_func, enclosing_object.object_like.second->type_def)) { + // should maybe do something special for closure here + // copy over old enclosing object + new_enclosing_object = enclosing_object } - // note here also that this is likely not a foolproof method - if (dot_style_method_call) { - new_enclosing_object = get_real_value(interpret(func_call_func->function_call.parameters[0], var_stack, enclosing_object, enclosing_func).first) - // do a dereference - if (is_pointer(new_enclosing_object)) - new_enclosing_object = get_real_value(value::variable(make_pair(new_enclosing_object.pointer.first, new_enclosing_object.pointer.second->clone_with_decreased_indirection()))) - func_call_func = func_call_func->function_call.parameters[1] - } else if (!is_void_nothing(enclosing_object)) { - if (method_in_object(func_call_func, enclosing_object.object_like.second->type_def)) { - // should maybe do something special for closure here - // copy over old enclosing object - new_enclosing_object = enclosing_object - } - } - var func_name = func_call_func->function.name - // some of these have to be done before parameters are evaluated (&&, ||, ., ->) - if (func_name == "&&" || func_name == "||") { - error("short circuit still in interpreter") - } else if (func_name == "." || func_name == "->") { - var left_side = get_real_value(interpret(func_call_parameters[0], var_stack, enclosing_object, enclosing_func).first) - var ret_ptr = null() - if (func_name == "->") - ret_ptr = ((left_side.pointer.first) cast *char + offset_into_struct(left_side.pointer.second->clone_with_decreased_indirection(), func_call_parameters[1])) cast *void - else - ret_ptr = ((left_side.object_like.first) cast *char + offset_into_struct(left_side.object_like.second, func_call_parameters[1])) cast *void - return make_pair(value::variable(make_pair(ret_ptr, func_call_parameters[1]->identifier.type)), control_flow::nor()) - } - // so here we either do an operator, call call_func with value parameters, or call call_func with ast_expressions - // (so we can properly copy_construct if necessary) - var parameters = vector() - var parameter_sources = vector<*ast_node>() - // if we don't have to copy_construct params (is an operator, or has no object params) - if (func_name == "&" || !func_call_parameters.any_true(fun(p: *ast_node): bool return get_ast_type(p)->is_object() && get_ast_type(p)->indirection == 0;)) { - parameters = func_call_parameters.map(fun(p: *ast_node): value return interpret(p, var_stack, enclosing_object, enclosing_func).first;) - if ( parameters.size == 2 && (func_name == "+" || func_name == "-" || func_name == "*" || func_name == "/" - || func_name == "<" || func_name == ">" || func_name == "<=" || func_name == ">=" - || func_name == "==" || func_name == "!=" || func_name == "%" || func_name == "^" - || func_name == "|" || func_name == "&" - )) - return make_pair(do_basic_op(func_name, parameters[0], parameters[1]), control_flow::nor()) - // do negate by subtracting from zero - if (func_name == "-") - return make_pair(do_basic_op_second_half(string("-"), 0, parameters[0], null()), control_flow::nor()) - if (func_name == "!") - return make_pair(value::boolean(!truthy(get_real_value(parameters[0]))), control_flow::nor()) - if (func_name == "++p" || func_name == "--p") { - var to_ret = get_real_value(parameters[0]) - store_into_variable(parameters[0], do_basic_op(func_name.slice(0,1), parameters[0], value::integer(1))) - return make_pair(to_ret, control_flow::nor()) - } - if (func_name == "++" || func_name == "--") { - store_into_variable(parameters[0], do_basic_op(func_name.slice(0,1), parameters[0], value::integer(1))) - return make_pair(get_real_value(parameters[0]), control_flow::nor()) - } - if (func_name == "&") { - if (is_variable(parameters[0])) - return make_pair(value::pointer(make_pair(parameters[0].variable.first, parameters[0].variable.second->clone_with_increased_indirection())), control_flow::nor()) - else if (is_object_like(parameters[0])) - return make_pair(value::pointer(make_pair(parameters[0].object_like.first, parameters[0].object_like.second->clone_with_increased_indirection())), control_flow::nor()) - else { - print("can't take address of: ") - print_value(parameters[0]) - error("Trying to take address of not a variable or object_like") - } - } - // to dereference, we basically take the pointer's value (maybe going through a variable to get it) - // and re-wrap it up into a variable value (so it can be assigned to, etc) - if (func_name == "*" || func_name == "[]") { - var dereference_val = parameters[0] - if (func_name == "[]") - dereference_val = do_basic_op(string("+"), parameters[0], parameters[1]) - if (!is_pointer(get_real_value(parameters[0]))) - error("Trying to take dereference not a pointer") - return make_pair(dereference_pointer_into_variable(dereference_val), control_flow::nor()) - } - // check for built-in-ish externs (everything the standard library needs) - if (func_name == "printf" || func_name == "malloc" || func_name == "free" || func_name == "memmove" || func_name == "fflush" || func_name == "snprintf" || func_name == "fopen" || func_name == "fclose" || func_name == "ftell" || func_name == "fseek" || func_name == "fread" || func_name == "fwrite" || func_name == "atan" || func_name == "atan2" || func_name == "acos" || func_name == "asin" || func_name == "tan" || func_name == "cos" || func_name == "sin") - return make_pair(call_built_in_extern(func_name, parameters), control_flow::nor()) - if (!func_call_func->function.body_statement) - error(string("trying to call unsupported extern function: ") + func_name) - } else { - // not the operator & and at least one object like parameter - parameter_sources = func_call_parameters - } - return make_pair(call_function(func_call_func, parameters, parameter_sources, var_stack, possible_closure_map, enclosing_object, new_enclosing_object, enclosing_func), control_flow::nor()) } - // call_function can be called with either parameter values in parameters or ast expressions in parameter_sources - // this is to allow easy function calling if we already have the values (for main, say, or to make our job if it's not - // an operator easier), but we need to be able to be called with ast_expressions too so we can properly copy_construct once - fun call_function(func: *ast_node, parameters: vector, parameter_sources: vector<*ast_node>, var_stack: *stack>, possible_closure_map: ref map<*ast_node, value>, enclosing_object: value, new_enclosing_object: value, enclosing_func: *ast_node): value { - // will need adjustment - if (!is_function(func)) - error("Can't handle not function function calls (can do regular method, is this chained or something?)") - var func_name = func->function.name - // do regular function - // start out with the possible closure map as the highest scope (gloabals checked seperately) - var new_var_stack = stack(possible_closure_map) - new_var_stack.push(map<*ast_node,value>()) - /*println("stack after pcm and new m is")*/ - /*for (var i = 0; i < new_var_stack.size(); i++;) {*/ - /*println(string("level: ") + i)*/ - /*new_var_stack.from_top(i).for_each(fun(key: *ast_node, v: value) print(get_ast_name(key) + " ");)*/ - /*println()*/ - /*}*/ - /*println("end stack after")*/ - // if this is a value based call, pull from parameters - if (parameter_sources.size == 0) { - /*println(func_name + " being called with parameter values")*/ - if (parameters.size != func->function.parameters.size) - error(string("calling function ") + func->function.name + " with wrong number of parameters (values)") - for (var i = 0; i < parameters.size; i++;) { - var param_type = get_ast_type(func)->parameter_types[i] - var param_ident = func->function.parameters[i] - if (param_type->is_ref) { - if (is_variable(parameters[i])) - new_var_stack.top()[param_ident] = parameters[i] - else - new_var_stack.top()[param_ident] = wrap_into_variable(parameters[i]) - } else { - new_var_stack.top()[param_ident] = value::variable(make_pair(malloc(type_size(param_type)), param_type)) - //HERE - /*if (param_type->indirection == 0 && param_type->is_function())*/ - /*(new_var_stack.top()[param_ident].variable.first) cast *pair<*ast_node,map<*ast_node,value>> ->construct()*/ - store_into_variable(new_var_stack.top()[param_ident], get_real_value(parameters[i])) - } - } - } else { - // on this side we construct temps in the old var stack, then move it over to the new one so that references resolve correctly - var_stack->push(map<*ast_node,value>()) - /*println(func_name + " being called with parameter sources")*/ - // need to pull from parameter_sources instead - if (parameter_sources.size != func->function.parameters.size) - error(string("calling function ") + func->function.name + " with wrong number of parameters (sources)") - for (var i = 0; i < parameter_sources.size; i++;) { - var param_type = get_ast_type(func)->parameter_types[i] - var param_ident = func->function.parameters[i] - if (param_type->is_ref) { - var param = interpret(parameter_sources[i], var_stack, enclosing_object, enclosing_func).first - if (is_variable(param)) - new_var_stack.top()[param_ident] = param - else - new_var_stack.top()[param_ident] = wrap_into_variable(param) - } else { - new_var_stack.top()[param_ident] = value::variable(make_pair(malloc(type_size(param_type)), param_type)) - store_into_variable(new_var_stack.top()[param_ident], get_real_value(interpret(parameter_sources[i], var_stack, enclosing_object, enclosing_func).first)) - } - } - } - var to_ret = interpret(func->function.body_statement, &new_var_stack, new_enclosing_object, func).first - // to_ret is on the new_var_stack, likely - /*pop_and_free(&new_var_stack)*/ - if (parameter_sources.size) { - // pop off the temporaries if we needed to, but only after destructing any params we needed to - pop_and_free(var_stack) - } - return to_ret - } - fun call_built_in_extern(func_name: string, parameters: vector): value { - for (var i = 0; i < parameters.size; i++;) - parameters[i] = get_real_value(parameters[i]) - if (func_name == "printf") { - assert(parameters.size == 2 && is_pointer(parameters[0]) && is_pointer(parameters[1]), "Calling printf with wrong params") - printf((parameters[0].pointer.first) cast *char, (parameters[1].pointer.first) cast *char) - return value::integer(0) - } else if (func_name == "malloc") { - assert(parameters.size == 1 && is_ulong_int(parameters[0]), "Calling malloc with wrong params") - return value::pointer(make_pair(malloc(parameters[0].ulong_int), type_ptr(base_type::void_return())->clone_with_increased_indirection())) - } else if (func_name == "free") { - assert(parameters.size == 1 && is_pointer(parameters[0]), "Calling free with wrong params") - free(parameters[0].pointer.first) - } else if (func_name == "memmove") { - assert(parameters.size == 3 && is_pointer(parameters[0]) && is_pointer(parameters[1]) && is_ulong_int(parameters[2]), "Calling memmove with wrong params") - return value::pointer(make_pair(memmove((parameters[0].pointer.first) cast *void, (parameters[1].pointer.first) cast *void, parameters[2].ulong_int), type_ptr(base_type::void_return(), 1))) - } else if (func_name == "fflush") { - assert(parameters.size == 1 && is_integer(parameters[0]), "Calling fflush with wrong params") - fflush(parameters[0].integer) - } else if (func_name == "snprintf") { - assert(parameters.size == 4 && is_pointer(parameters[0]) && is_ulong_int(parameters[1]) && is_pointer(parameters[2]) && is_double_precision(parameters[3]), "Calling snprintf with wrong params") - return value::integer(snprintf((parameters[0].pointer.first) cast *char, parameters[1].ulong_int, (parameters[2].pointer.first) cast *char, parameters[3].double_precision)) - } else if (func_name == "fopen") { - assert(parameters.size == 2 && is_pointer(parameters[0]) && is_pointer(parameters[1]), "Calling fopen with wrong params") - return value::pointer(make_pair(fopen((parameters[0].pointer.first) cast *char, (parameters[1].pointer.first) cast *char), type_ptr(base_type::void_return(), 1))) - } else if (func_name == "fclose") { - assert(parameters.size == 1 && is_pointer(parameters[0]), "Calling fclose with wrong params") - return value::integer(fclose((parameters[0].pointer.first) cast *void)) - } else if (func_name == "ftell") { - assert(parameters.size == 1 && is_pointer(parameters[0]), "Calling ftell with wrong params") - return value::long_int(ftell((parameters[0].pointer.first) cast *void)) - } else if (func_name == "fseek") { - assert(parameters.size == 3 && is_pointer(parameters[0]) && is_long_int(parameters[1]) && is_integer(parameters[2]), "Calling fseek with wrong params") - return value::integer(fseek((parameters[0].pointer.first) cast *void, parameters[1].long_int, parameters[2].integer)) - } else if (func_name == "fread") { - assert(parameters.size == 4 && is_pointer(parameters[0]) && is_ulong_int(parameters[1]) && is_ulong_int(parameters[2]) && is_pointer(parameters[3]), "Calling fread with wrong params") - return value::ulong_int(fread((parameters[0].pointer.first) cast *void, parameters[1].ulong_int, parameters[2].ulong_int, parameters[3].pointer.first)) - } else if (func_name == "fwrite") { - assert(parameters.size == 4 && is_pointer(parameters[0]) && is_ulong_int(parameters[1]) && is_ulong_int(parameters[2]) && is_pointer(parameters[3]), "Calling fwrite with wrong params") - return value::ulong_int(fwrite((parameters[0].pointer.first) cast *void, parameters[1].ulong_int, parameters[2].ulong_int, parameters[3].pointer.first)) - } else if (func_name == "exit") { - assert(parameters.size == 1 && is_integer(parameters[0]), "Calling exit with wrong params") - exit(parameters[0].integer) - } else if (func_name == "atan") { - assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling atan with wrong params") - return value::double_precision(atan(parameters[0].double_precision)) - } else if (func_name == "atan2") { - assert(parameters.size == 2 && is_double_precision(parameters[0]) && is_double_precision(parameters[1]), "Calling atan2 with wrong params") - return value::double_precision(atan2(parameters[0].double_precision, parameters[1].double_precision)) - } else if (func_name == "acos") { - assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling acos with wrong params") - return value::double_precision(acos(parameters[0].double_precision)) - } else if (func_name == "asin") { - assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling asin with wrong params") - return value::double_precision(asin(parameters[0].double_precision)) - } else if (func_name == "tan") { - assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling tan with wrong params") - return value::double_precision(tan(parameters[0].double_precision)) - } else if (func_name == "cos") { - assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling cos with wrong params") - return value::double_precision(cos(parameters[0].double_precision)) - } else if (func_name == "sin") { - assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling sin with wrong params") - return value::double_precision(sin(parameters[0].double_precision)) - } else { - error(string("trying to call invalid func: ") + func_name) - } - return value::void_nothing() - } - fun interpret_function(function: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var possible_closure_map = new>()->construct() - /*println("trying to close")*/ - function->function.closed_variables.for_each(fun(v: *ast_node) { - (*possible_closure_map)[v] = interpret_identifier(v, var_stack, enclosing_object, enclosing_func).first - /*println(string("closed over ") + get_ast_name(v))*/ - }) - /*println("in interpret function possible_closure_map is")*/ - /*possible_closure_map->for_each(fun(key: *ast_node, v: value) print(get_ast_name(key) + " ");)*/ - /*println()*/ - /*println("end in inteprret function pcm")*/ - return make_pair(value::function(make_pair(function, possible_closure_map)), control_flow::nor()) - } - fun interpret_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - return interpret(stmt->statement.child, var_stack, enclosing_object, enclosing_func) - } - fun interpret_if_statement(if_stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var_stack->push(map<*ast_node,value>()) - var value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) - if (truthy(interpret(if_stmt->if_statement.condition, var_stack, enclosing_object, enclosing_func).first)) { - value_from_inside = interpret(if_stmt->if_statement.then_part, var_stack, enclosing_object, enclosing_func) - } else if (if_stmt->if_statement.else_part) { - value_from_inside = interpret(if_stmt->if_statement.else_part, var_stack, enclosing_object, enclosing_func) - } - pop_and_free(var_stack) - return value_from_inside - } - fun interpret_while_loop(while_loop: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var_stack->push(map<*ast_node,value>()) - var value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) - var going = true - while (going && truthy(interpret(while_loop->while_loop.condition, var_stack, enclosing_object, enclosing_func).first)) { - value_from_inside = interpret(while_loop->while_loop.statement, var_stack, enclosing_object, enclosing_func) - if (value_from_inside.second == control_flow::ret() || value_from_inside.second == control_flow::bre()) - going = false - if (value_from_inside.second == control_flow::bre() || value_from_inside.second == control_flow::con()) - value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) - } - pop_and_free(var_stack) - return value_from_inside - } - fun interpret_for_loop(for_loop: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var_stack->push(map<*ast_node,value>()) - var value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) - var going = true - if (for_loop->for_loop.init) - interpret(for_loop->for_loop.init, var_stack, enclosing_object, enclosing_func) - while (going && (!for_loop->for_loop.condition || truthy(interpret(for_loop->for_loop.condition, var_stack, enclosing_object, enclosing_func).first))) { - value_from_inside = interpret(for_loop->for_loop.body, var_stack, enclosing_object, enclosing_func) - if (value_from_inside.second == control_flow::ret() || value_from_inside.second == control_flow::bre()) - going = false - if (value_from_inside.second == control_flow::bre() || value_from_inside.second == control_flow::con()) - value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) - - // only run update if we're not breaking or continuing - if (going && for_loop->for_loop.update) - interpret(for_loop->for_loop.update, var_stack, enclosing_object, enclosing_func) - } - pop_and_free(var_stack) - return value_from_inside - } - fun interpret_branching_statement(bstatement: *ast_node): pair { - match (bstatement->branching_statement.b_type) { - branching_type::break_stmt() return make_pair(value::void_nothing(), control_flow::bre()) - branching_type::continue_stmt() return make_pair(value::void_nothing(), control_flow::con()) - } - error("bad branch type") - } - fun interpret_code_block(block: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var_stack->push(map<*ast_node,value>()) - for (var i = 0; i < block->code_block.children.size; i++;) { - var statement = interpret(block->code_block.children[i], var_stack, enclosing_object, enclosing_func) - match (statement.second) { - control_flow::con() { - pop_and_free(var_stack) - return make_pair(value::void_nothing(), control_flow::con()) - } - control_flow::bre() { - pop_and_free(var_stack) - return make_pair(value::void_nothing(), control_flow::bre()) - } - control_flow::ret() { - pop_and_free(var_stack) - return statement - } - } - // if nor, continue on - } - pop_and_free(var_stack) - return make_pair(value::void_nothing(), control_flow::nor()) - } - fun interpret_return_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - if (stmt->return_statement.return_value == null()) - return make_pair(value::void_nothing(), control_flow::ret()) - var return_expression = stmt->return_statement.return_value - var return_type = get_ast_type(return_expression) - var to_ret.construct(): value - if (get_ast_type(enclosing_func)->return_type->is_ref) { - to_ret = interpret(return_expression, var_stack, enclosing_object, enclosing_func).first - if (!is_variable(to_ret)) { - print("here is: ") - print_value(to_ret) - error("interpreter returning reference is not variable") - } - } else { - to_ret = interpret(return_expression, var_stack, enclosing_object, enclosing_func).first - } - return make_pair(to_ret, control_flow::ret()) - } - fun interpret_declaration_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var ident = stmt->declaration_statement.identifier - var ident_type = ident->identifier.type - var_stack->top()[ident] = value::variable(make_pair(malloc(type_size(ident_type)),ident_type)) - // NOTE: store_into_variable takes to in as a ref because it might change it in the special case ref = ptr - if (stmt->declaration_statement.expression) { - store_into_variable(var_stack->top()[ident], get_real_value(interpret(stmt->declaration_statement.expression, var_stack, enclosing_object, enclosing_func).first)) - } else if (stmt->declaration_statement.init_method_call) { - interpret(stmt->declaration_statement.init_method_call, var_stack, enclosing_object, enclosing_func) - } - return make_pair(value::void_nothing(), control_flow::nor()) - } - fun interpret_assignment_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - var to = interpret(stmt->assignment_statement.to, var_stack, enclosing_object, enclosing_func).first - var from = interpret(stmt->assignment_statement.from, var_stack, enclosing_object, enclosing_func).first - assert(is_variable(to), "assigning into not a variable") - // always do cast now to make our best effort at assignment (assign into a double from a float, etc) - // unless it's an object - var from_real = get_real_value(from) - // NOTE: store_into_variable takes to in as a ref because it might change it in the special case ref = ptr - if (is_object_like(from_real) || is_function(from_real)) - store_into_variable(to, from_real) + var func_name = func_call_func->function.name + // some of these have to be done before parameters are evaluated (&&, ||, ., ->) + if (func_name == "&&" || func_name == "||") { + error("short circuit still in interpreter") + } else if (func_name == "." || func_name == "->") { + var left_side = get_real_value(interpret(func_call_parameters[0], var_stack, enclosing_object, enclosing_func, globals).first) + var ret_ptr = null() + if (func_name == "->") + ret_ptr = ((left_side.pointer.first) cast *char + offset_into_struct(left_side.pointer.second->clone_with_decreased_indirection(), func_call_parameters[1])) cast *void else - store_into_variable(to, cast_value(from_real, to.variable.second)) - return make_pair(value::void_nothing(), control_flow::nor()) + ret_ptr = ((left_side.object_like.first) cast *char + offset_into_struct(left_side.object_like.second, func_call_parameters[1])) cast *void + return make_pair(value::variable(make_pair(ret_ptr, func_call_parameters[1]->identifier.type)), control_flow::nor()) } - fun interpret_identifier(ident: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - for (var i = 0; i < var_stack->size(); i++;) - if (var_stack->from_top(i).contains_key(ident)) - return make_pair(var_stack->from_top(i)[ident], control_flow::nor()) - // check for object member / this - if (is_object_like(enclosing_object)) { - if (ident->identifier.name == "this") - return make_pair(value::pointer(make_pair(enclosing_object.object_like.first, enclosing_object.object_like.second->clone_with_increased_indirection())), control_flow::nor()) - var object_def = enclosing_object.object_like.second->type_def - for (var i = 0; i < object_def->type_def.variables.size; i++;) { - if (object_def->type_def.variables[i]->declaration_statement.identifier == ident) { - var ret_ptr = ((enclosing_object.object_like.first) cast *char + offset_into_struct(enclosing_object.object_like.second, ident)) cast *void - return make_pair(value::variable(make_pair(ret_ptr, ident->identifier.type)), control_flow::nor()) - } + // so here we either do an operator, call call_func with value parameters, or call call_func with ast_expressions + // (so we can properly copy_construct if necessary) + var parameters = vector() + var parameter_sources = vector<*ast_node>() + // if we don't have to copy_construct params (is an operator, or has no object params) + if (func_name == "&" || !func_call_parameters.any_true(fun(p: *ast_node): bool return get_ast_type(p)->is_object() && get_ast_type(p)->indirection == 0;)) { + parameters = func_call_parameters.map(fun(p: *ast_node): value return interpret(p, var_stack, enclosing_object, enclosing_func, globals).first;) + if ( parameters.size == 2 && (func_name == "+" || func_name == "-" || func_name == "*" || func_name == "/" + || func_name == "<" || func_name == ">" || func_name == "<=" || func_name == ">=" + || func_name == "==" || func_name == "!=" || func_name == "%" || func_name == "^" + || func_name == "|" || func_name == "&" + )) + return make_pair(do_basic_op(func_name, parameters[0], parameters[1]), control_flow::nor()) + // do negate by subtracting from zero + if (func_name == "-") + return make_pair(do_basic_op_second_half(string("-"), 0, parameters[0], null()), control_flow::nor()) + if (func_name == "!") + return make_pair(value::boolean(!truthy(get_real_value(parameters[0]))), control_flow::nor()) + if (func_name == "++p" || func_name == "--p") { + var to_ret = get_real_value(parameters[0]) + store_into_variable(parameters[0], do_basic_op(func_name.slice(0,1), parameters[0], value::integer(1))) + return make_pair(to_ret, control_flow::nor()) + } + if (func_name == "++" || func_name == "--") { + store_into_variable(parameters[0], do_basic_op(func_name.slice(0,1), parameters[0], value::integer(1))) + return make_pair(get_real_value(parameters[0]), control_flow::nor()) + } + if (func_name == "&") { + if (is_variable(parameters[0])) + return make_pair(value::pointer(make_pair(parameters[0].variable.first, parameters[0].variable.second->clone_with_increased_indirection())), control_flow::nor()) + else if (is_object_like(parameters[0])) + return make_pair(value::pointer(make_pair(parameters[0].object_like.first, parameters[0].object_like.second->clone_with_increased_indirection())), control_flow::nor()) + else { + print("can't take address of: ") + print_value(parameters[0]) + error("Trying to take address of not a variable or object_like") } } - // check for global - if (globals.contains_key(ident)) - return make_pair(globals[ident], control_flow::nor()) - println("couldn't find it in interpret identifier, scope:") - for (var i = 0; i < var_stack->size(); i++;) { - println(string("level: ") + i) - var_stack->from_top(i).for_each(fun(key: *ast_node, v: value) print(get_ast_name(key) + " ");) - println() + if (func_name == "*" || func_name == "[]") { + var dereference_val = parameters[0] + if (func_name == "[]") + dereference_val = do_basic_op(string("+"), parameters[0], parameters[1]) + if (!is_pointer(get_real_value(parameters[0]))) + error("Trying to take dereference not a pointer") + return make_pair(dereference_pointer_into_variable(dereference_val), control_flow::nor()) } - if (is_object_like(enclosing_object)) { - println("object scope:") - var object_def = enclosing_object.object_like.second->type_def - for (var i = 0; i < object_def->type_def.variables.size; i++;) { - print(get_ast_name(object_def->type_def.variables[i]) + " ") + // check for built-in-ish externs (everything the standard library needs) + if (func_name == "printf" || func_name == "malloc" || func_name == "free" || func_name == "memmove" || func_name == "fflush" || func_name == "snprintf" || func_name == "fopen" || func_name == "fclose" || func_name == "ftell" || func_name == "fseek" || func_name == "fread" || func_name == "fwrite" || func_name == "atan" || func_name == "atan2" || func_name == "acos" || func_name == "asin" || func_name == "tan" || func_name == "cos" || func_name == "sin") + return make_pair(call_built_in_extern(func_name, parameters), control_flow::nor()) + if (!func_call_func->function.body_statement) + error(string("trying to call unsupported extern function: ") + func_name) + } else { + // not the operator & and at least one object like parameter + parameter_sources = func_call_parameters + } + return make_pair(call_function(func_call_func, parameters, parameter_sources, var_stack, possible_closure_map, enclosing_object, new_enclosing_object, enclosing_func, globals), control_flow::nor()) +} +// call_function can be called with either parameter values in parameters or ast expressions in parameter_sources +// this is to allow easy function calling if we already have the values (for main, say, or to make our job if it's not +// an operator easier), but we need to be able to be called with ast_expressions too so we can properly copy_construct once +fun call_function(func: *ast_node, parameters: vector, parameter_sources: vector<*ast_node>, var_stack: *stack>, possible_closure_map: ref map<*ast_node, value>, enclosing_object: value, new_enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): value { + // will need adjustment + if (!is_function(func)) + error("Can't handle not function function calls (can do regular method, is this chained or something?)") + var func_name = func->function.name + // do regular function + // start out with the possible closure map as the highest scope (gloabals checked seperately) + var new_var_stack = stack(possible_closure_map) + new_var_stack.push(map<*ast_node,value>()) + // if this is a value based call, pull from parameters + if (parameter_sources.size == 0) { + /*println(func_name + " being called with parameter values")*/ + if (parameters.size != func->function.parameters.size) + error(string("calling function ") + func->function.name + " with wrong number of parameters (values)") + for (var i = 0; i < parameters.size; i++;) { + var param_type = get_ast_type(func)->parameter_types[i] + var param_ident = func->function.parameters[i] + if (param_type->is_ref) { + if (is_variable(parameters[i])) + new_var_stack.top()[param_ident] = parameters[i] + else + new_var_stack.top()[param_ident] = wrap_into_variable(parameters[i]) + } else { + new_var_stack.top()[param_ident] = value::variable(make_pair(malloc(type_size(param_type)), param_type)) + store_into_variable(new_var_stack.top()[param_ident], get_real_value(parameters[i])) } - } else { - print("no object scope: ") - print_value(enclosing_object) } - error(string("Cannot find variable: ") + ident->identifier.name) - } - fun interpret_cast(node: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - return make_pair(cast_value(interpret(node->cast.value, var_stack, enclosing_object, enclosing_func).first, node->cast.to_type), control_flow::nor()) - } - fun interpret_compiler_intrinsic(node: *ast_node, var_stack: *stack>): pair { - var intrinsic_name = node->compiler_intrinsic.intrinsic - if (intrinsic_name == "sizeof") - return make_pair(value::ulong_int(type_size(node->compiler_intrinsic.type_parameters[0])), control_flow::nor()) - error(string("bad intrinsic: ") + intrinsic_name) - } - fun interpret_value(val: *ast_node): pair - return make_pair(wrap_value(val), control_flow::nor()) - fun interpret(node: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node): pair { - if (!node) error("cannot interpret null node!") - match (*node) { - ast_node::function_call(backing) return interpret_function_call(node, var_stack, enclosing_object, enclosing_func) - ast_node::function(backing) return interpret_function(node, var_stack, enclosing_object, enclosing_func) - ast_node::statement(backing) return interpret_statement(node, var_stack, enclosing_object, enclosing_func) - ast_node::if_statement(backing) return interpret_if_statement(node, var_stack, enclosing_object, enclosing_func) - ast_node::while_loop(backing) return interpret_while_loop(node, var_stack, enclosing_object, enclosing_func) - ast_node::for_loop(backing) return interpret_for_loop(node, var_stack, enclosing_object, enclosing_func) - ast_node::branching_statement(backing) return interpret_branching_statement(node) - ast_node::code_block(backing) return interpret_code_block(node, var_stack, enclosing_object, enclosing_func) - ast_node::return_statement(backing) return interpret_return_statement(node, var_stack, enclosing_object, enclosing_func) - ast_node::declaration_statement(backing) return interpret_declaration_statement(node, var_stack, enclosing_object, enclosing_func) - ast_node::assignment_statement(backing) return interpret_assignment_statement(node, var_stack, enclosing_object, enclosing_func) - ast_node::identifier(backing) return interpret_identifier(node, var_stack, enclosing_object, enclosing_func) - ast_node::cast(backing) return interpret_cast(node, var_stack, enclosing_object, enclosing_func) - ast_node::compiler_intrinsic(backing) return interpret_compiler_intrinsic(node, var_stack) - ast_node::value(backing) return interpret_value(node) + } else { + // on this side we construct temps in the old var stack, then move it over to the new one so that references resolve correctly + var_stack->push(map<*ast_node,value>()) + /*println(func_name + " being called with parameter sources")*/ + // need to pull from parameter_sources instead + if (parameter_sources.size != func->function.parameters.size) + error(string("calling function ") + func->function.name + " with wrong number of parameters (sources)") + for (var i = 0; i < parameter_sources.size; i++;) { + var param_type = get_ast_type(func)->parameter_types[i] + var param_ident = func->function.parameters[i] + if (param_type->is_ref) { + var param = interpret(parameter_sources[i], var_stack, enclosing_object, enclosing_func, globals).first + if (is_variable(param)) + new_var_stack.top()[param_ident] = param + else + new_var_stack.top()[param_ident] = wrap_into_variable(param) + } else { + new_var_stack.top()[param_ident] = value::variable(make_pair(malloc(type_size(param_type)), param_type)) + store_into_variable(new_var_stack.top()[param_ident], get_real_value(interpret(parameter_sources[i], var_stack, enclosing_object, enclosing_func, globals).first)) + } } - error(string("Cannot interpret node: ") + get_ast_name(node)) } + var to_ret = interpret(func->function.body_statement, &new_var_stack, new_enclosing_object, func, globals).first + // to_ret is on the new_var_stack, likely + /*pop_and_free(&new_var_stack)*/ + if (parameter_sources.size) { + // pop off the temporaries if we needed to, but only after destructing any params we needed to + pop_and_free(var_stack) + } + return to_ret +} +fun call_built_in_extern(func_name: string, parameters: vector): value { + for (var i = 0; i < parameters.size; i++;) + parameters[i] = get_real_value(parameters[i]) + if (func_name == "printf") { + assert(parameters.size == 2 && is_pointer(parameters[0]) && is_pointer(parameters[1]), "Calling printf with wrong params") + printf((parameters[0].pointer.first) cast *char, (parameters[1].pointer.first) cast *char) + return value::integer(0) + } else if (func_name == "malloc") { + assert(parameters.size == 1 && is_ulong_int(parameters[0]), "Calling malloc with wrong params") + return value::pointer(make_pair(malloc(parameters[0].ulong_int), type_ptr(base_type::void_return())->clone_with_increased_indirection())) + } else if (func_name == "free") { + assert(parameters.size == 1 && is_pointer(parameters[0]), "Calling free with wrong params") + free(parameters[0].pointer.first) + } else if (func_name == "memmove") { + assert(parameters.size == 3 && is_pointer(parameters[0]) && is_pointer(parameters[1]) && is_ulong_int(parameters[2]), "Calling memmove with wrong params") + return value::pointer(make_pair(memmove((parameters[0].pointer.first) cast *void, (parameters[1].pointer.first) cast *void, parameters[2].ulong_int), type_ptr(base_type::void_return(), 1))) + } else if (func_name == "fflush") { + assert(parameters.size == 1 && is_integer(parameters[0]), "Calling fflush with wrong params") + fflush(parameters[0].integer) + } else if (func_name == "snprintf") { + assert(parameters.size == 4 && is_pointer(parameters[0]) && is_ulong_int(parameters[1]) && is_pointer(parameters[2]) && is_double_precision(parameters[3]), "Calling snprintf with wrong params") + return value::integer(snprintf((parameters[0].pointer.first) cast *char, parameters[1].ulong_int, (parameters[2].pointer.first) cast *char, parameters[3].double_precision)) + } else if (func_name == "fopen") { + assert(parameters.size == 2 && is_pointer(parameters[0]) && is_pointer(parameters[1]), "Calling fopen with wrong params") + return value::pointer(make_pair(fopen((parameters[0].pointer.first) cast *char, (parameters[1].pointer.first) cast *char), type_ptr(base_type::void_return(), 1))) + } else if (func_name == "fclose") { + assert(parameters.size == 1 && is_pointer(parameters[0]), "Calling fclose with wrong params") + return value::integer(fclose((parameters[0].pointer.first) cast *void)) + } else if (func_name == "ftell") { + assert(parameters.size == 1 && is_pointer(parameters[0]), "Calling ftell with wrong params") + return value::long_int(ftell((parameters[0].pointer.first) cast *void)) + } else if (func_name == "fseek") { + assert(parameters.size == 3 && is_pointer(parameters[0]) && is_long_int(parameters[1]) && is_integer(parameters[2]), "Calling fseek with wrong params") + return value::integer(fseek((parameters[0].pointer.first) cast *void, parameters[1].long_int, parameters[2].integer)) + } else if (func_name == "fread") { + assert(parameters.size == 4 && is_pointer(parameters[0]) && is_ulong_int(parameters[1]) && is_ulong_int(parameters[2]) && is_pointer(parameters[3]), "Calling fread with wrong params") + return value::ulong_int(fread((parameters[0].pointer.first) cast *void, parameters[1].ulong_int, parameters[2].ulong_int, parameters[3].pointer.first)) + } else if (func_name == "fwrite") { + assert(parameters.size == 4 && is_pointer(parameters[0]) && is_ulong_int(parameters[1]) && is_ulong_int(parameters[2]) && is_pointer(parameters[3]), "Calling fwrite with wrong params") + return value::ulong_int(fwrite((parameters[0].pointer.first) cast *void, parameters[1].ulong_int, parameters[2].ulong_int, parameters[3].pointer.first)) + } else if (func_name == "exit") { + assert(parameters.size == 1 && is_integer(parameters[0]), "Calling exit with wrong params") + exit(parameters[0].integer) + } else if (func_name == "atan") { + assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling atan with wrong params") + return value::double_precision(atan(parameters[0].double_precision)) + } else if (func_name == "atan2") { + assert(parameters.size == 2 && is_double_precision(parameters[0]) && is_double_precision(parameters[1]), "Calling atan2 with wrong params") + return value::double_precision(atan2(parameters[0].double_precision, parameters[1].double_precision)) + } else if (func_name == "acos") { + assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling acos with wrong params") + return value::double_precision(acos(parameters[0].double_precision)) + } else if (func_name == "asin") { + assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling asin with wrong params") + return value::double_precision(asin(parameters[0].double_precision)) + } else if (func_name == "tan") { + assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling tan with wrong params") + return value::double_precision(tan(parameters[0].double_precision)) + } else if (func_name == "cos") { + assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling cos with wrong params") + return value::double_precision(cos(parameters[0].double_precision)) + } else if (func_name == "sin") { + assert(parameters.size == 1 && is_double_precision(parameters[0]), "Calling sin with wrong params") + return value::double_precision(sin(parameters[0].double_precision)) + } else { + error(string("trying to call invalid func: ") + func_name) + } + return value::void_nothing() +} +fun interpret_function(function: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var possible_closure_map = new>()->construct() + function->function.closed_variables.for_each(fun(v: *ast_node) { + (*possible_closure_map)[v] = interpret_identifier(v, var_stack, enclosing_object, enclosing_func, globals).first + }) + return make_pair(value::function(make_pair(function, possible_closure_map)), control_flow::nor()) +} +fun interpret_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + return interpret(stmt->statement.child, var_stack, enclosing_object, enclosing_func, globals) +} +fun interpret_if_statement(if_stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var_stack->push(map<*ast_node,value>()) + var value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) + if (truthy(interpret(if_stmt->if_statement.condition, var_stack, enclosing_object, enclosing_func, globals).first)) { + value_from_inside = interpret(if_stmt->if_statement.then_part, var_stack, enclosing_object, enclosing_func, globals) + } else if (if_stmt->if_statement.else_part) { + value_from_inside = interpret(if_stmt->if_statement.else_part, var_stack, enclosing_object, enclosing_func, globals) + } + pop_and_free(var_stack) + return value_from_inside +} +fun interpret_while_loop(while_loop: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var_stack->push(map<*ast_node,value>()) + var value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) + var going = true + while (going && truthy(interpret(while_loop->while_loop.condition, var_stack, enclosing_object, enclosing_func, globals).first)) { + value_from_inside = interpret(while_loop->while_loop.statement, var_stack, enclosing_object, enclosing_func, globals) + if (value_from_inside.second == control_flow::ret() || value_from_inside.second == control_flow::bre()) + going = false + if (value_from_inside.second == control_flow::bre() || value_from_inside.second == control_flow::con()) + value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) + } + pop_and_free(var_stack) + return value_from_inside +} +fun interpret_for_loop(for_loop: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var_stack->push(map<*ast_node,value>()) + var value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) + var going = true + if (for_loop->for_loop.init) + interpret(for_loop->for_loop.init, var_stack, enclosing_object, enclosing_func, globals) + while (going && (!for_loop->for_loop.condition || truthy(interpret(for_loop->for_loop.condition, var_stack, enclosing_object, enclosing_func, globals).first))) { + value_from_inside = interpret(for_loop->for_loop.body, var_stack, enclosing_object, enclosing_func, globals) + if (value_from_inside.second == control_flow::ret() || value_from_inside.second == control_flow::bre()) + going = false + if (value_from_inside.second == control_flow::bre() || value_from_inside.second == control_flow::con()) + value_from_inside = make_pair(value::void_nothing(), control_flow::nor()) + + // only run update if we're not breaking or continuing + if (going && for_loop->for_loop.update) + interpret(for_loop->for_loop.update, var_stack, enclosing_object, enclosing_func, globals) + } + pop_and_free(var_stack) + return value_from_inside +} +fun interpret_branching_statement(bstatement: *ast_node): pair { + match (bstatement->branching_statement.b_type) { + branching_type::break_stmt() return make_pair(value::void_nothing(), control_flow::bre()) + branching_type::continue_stmt() return make_pair(value::void_nothing(), control_flow::con()) + } + error("bad branch type") +} +fun interpret_code_block(block: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var_stack->push(map<*ast_node,value>()) + for (var i = 0; i < block->code_block.children.size; i++;) { + var statement = interpret(block->code_block.children[i], var_stack, enclosing_object, enclosing_func, globals) + match (statement.second) { + control_flow::con() { + pop_and_free(var_stack) + return make_pair(value::void_nothing(), control_flow::con()) + } + control_flow::bre() { + pop_and_free(var_stack) + return make_pair(value::void_nothing(), control_flow::bre()) + } + control_flow::ret() { + pop_and_free(var_stack) + return statement + } + } + // if nor, continue on + } + pop_and_free(var_stack) + return make_pair(value::void_nothing(), control_flow::nor()) +} +fun interpret_return_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + if (stmt->return_statement.return_value == null()) + return make_pair(value::void_nothing(), control_flow::ret()) + var return_expression = stmt->return_statement.return_value + var return_type = get_ast_type(return_expression) + var to_ret.construct(): value + if (get_ast_type(enclosing_func)->return_type->is_ref) { + to_ret = interpret(return_expression, var_stack, enclosing_object, enclosing_func, globals).first + if (!is_variable(to_ret)) { + print("here is: ") + print_value(to_ret) + error("interpreter returning reference is not variable") + } + } else { + to_ret = interpret(return_expression, var_stack, enclosing_object, enclosing_func, globals).first + } + return make_pair(to_ret, control_flow::ret()) +} +fun interpret_declaration_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var ident = stmt->declaration_statement.identifier + var ident_type = ident->identifier.type + var_stack->top()[ident] = value::variable(make_pair(malloc(type_size(ident_type)),ident_type)) + // NOTE: store_into_variable takes to in as a ref because it might change it in the special case ref = ptr + if (stmt->declaration_statement.expression) { + store_into_variable(var_stack->top()[ident], get_real_value(interpret(stmt->declaration_statement.expression, var_stack, enclosing_object, enclosing_func, globals).first)) + } else if (stmt->declaration_statement.init_method_call) { + interpret(stmt->declaration_statement.init_method_call, var_stack, enclosing_object, enclosing_func, globals) + } + return make_pair(value::void_nothing(), control_flow::nor()) +} +fun interpret_assignment_statement(stmt: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + var to = interpret(stmt->assignment_statement.to, var_stack, enclosing_object, enclosing_func, globals).first + var from = interpret(stmt->assignment_statement.from, var_stack, enclosing_object, enclosing_func, globals).first + assert(is_variable(to), "assigning into not a variable") + // always do cast now to make our best effort at assignment (assign into a double from a float, etc) + // unless it's an object + var from_real = get_real_value(from) + // NOTE: store_into_variable takes to in as a ref because it might change it in the special case ref = ptr + if (is_object_like(from_real) || is_function(from_real)) + store_into_variable(to, from_real) + else + store_into_variable(to, cast_value(from_real, to.variable.second)) + return make_pair(value::void_nothing(), control_flow::nor()) +} +fun interpret_identifier(ident: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + for (var i = 0; i < var_stack->size(); i++;) + if (var_stack->from_top(i).contains_key(ident)) + return make_pair(var_stack->from_top(i)[ident], control_flow::nor()) + // check for object member / this + if (is_object_like(enclosing_object)) { + if (ident->identifier.name == "this") + return make_pair(value::pointer(make_pair(enclosing_object.object_like.first, enclosing_object.object_like.second->clone_with_increased_indirection())), control_flow::nor()) + var object_def = enclosing_object.object_like.second->type_def + for (var i = 0; i < object_def->type_def.variables.size; i++;) { + if (object_def->type_def.variables[i]->declaration_statement.identifier == ident) { + var ret_ptr = ((enclosing_object.object_like.first) cast *char + offset_into_struct(enclosing_object.object_like.second, ident)) cast *void + return make_pair(value::variable(make_pair(ret_ptr, ident->identifier.type)), control_flow::nor()) + } + } + } + // check for global + if (globals->contains_key(ident)) + return make_pair((*globals)[ident], control_flow::nor()) + println("couldn't find it in interpret identifier, scope:") + for (var i = 0; i < var_stack->size(); i++;) { + println(string("level: ") + i) + var_stack->from_top(i).for_each(fun(key: *ast_node, v: value) print(get_ast_name(key) + " ");) + println() + } + if (is_object_like(enclosing_object)) { + println("object scope:") + var object_def = enclosing_object.object_like.second->type_def + for (var i = 0; i < object_def->type_def.variables.size; i++;) { + print(get_ast_name(object_def->type_def.variables[i]) + " ") + } + } else { + print("no object scope: ") + print_value(enclosing_object) + } + error(string("Cannot find variable: ") + ident->identifier.name) +} +fun interpret_cast(node: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + return make_pair(cast_value(interpret(node->cast.value, var_stack, enclosing_object, enclosing_func, globals).first, node->cast.to_type), control_flow::nor()) +} +fun interpret_compiler_intrinsic(node: *ast_node, var_stack: *stack>): pair { + var intrinsic_name = node->compiler_intrinsic.intrinsic + if (intrinsic_name == "sizeof") + return make_pair(value::ulong_int(type_size(node->compiler_intrinsic.type_parameters[0])), control_flow::nor()) + error(string("bad intrinsic: ") + intrinsic_name) +} +fun interpret_value(val: *ast_node): pair + return make_pair(wrap_value(val), control_flow::nor()) +fun interpret(node: *ast_node, var_stack: *stack>, enclosing_object: value, enclosing_func: *ast_node, globals: *map<*ast_node, value>): pair { + if (!node) error("cannot interpret null node!") + match (*node) { + ast_node::function_call(backing) return interpret_function_call(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::function(backing) return interpret_function(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::statement(backing) return interpret_statement(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::if_statement(backing) return interpret_if_statement(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::while_loop(backing) return interpret_while_loop(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::for_loop(backing) return interpret_for_loop(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::branching_statement(backing) return interpret_branching_statement(node) + ast_node::code_block(backing) return interpret_code_block(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::return_statement(backing) return interpret_return_statement(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::declaration_statement(backing) return interpret_declaration_statement(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::assignment_statement(backing) return interpret_assignment_statement(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::identifier(backing) return interpret_identifier(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::cast(backing) return interpret_cast(node, var_stack, enclosing_object, enclosing_func, globals) + ast_node::compiler_intrinsic(backing) return interpret_compiler_intrinsic(node, var_stack) + ast_node::value(backing) return interpret_value(node) + } + error(string("Cannot interpret node: ") + get_ast_name(node)) } diff --git a/stdlib/io.krak b/stdlib/io.krak index 5769601..b9ff77e 100644 --- a/stdlib/io.krak +++ b/stdlib/io.krak @@ -7,7 +7,21 @@ ext fun fprintf(file: *void, format: *char, ...): int ext fun snprintf(to_str: *char, num: ulong, format: *char, ...): int ext fun fflush(file: int): int ext var stderr: *void +ext fun fgets(buff: *char, size: int, file: *void): *char +ext var stdin: *void +// dead simple stdin +fun get_line(prompt: string::string, line_size: int): string::string { + print(prompt) + return get_line(line_size) +} +fun get_line(line_size: int): string::string { + var buff = new(line_size) + fgets(buff, line_size, stdin) + var to_ret = string::string(buff) + delete(buff) + return to_ret.slice(0,-2) // remove '\n' +} fun printlnerr(toPrint: T) : void { printerr(toPrint) printerr("\n") diff --git a/stdlib/pass_common.krak b/stdlib/pass_common.krak index d2267d1..34a7982 100644 --- a/stdlib/pass_common.krak +++ b/stdlib/pass_common.krak @@ -1,5 +1,4 @@ import ast_nodes:* -import ast_transformation:* import mem:* import util:* import vector:* @@ -9,6 +8,200 @@ import string:* fun make_this_noncached(object: *ast_node): *ast_node { return ast_identifier_ptr("this", object->type_def.self_type->clone_with_indirection(1), object) } +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, string(message)); +fun error(source: *tree, message: string) { + source = get_first_terminal(source) + if (source) + error(source->data.source + ": " + source->data.position + " " + message) + error(message) +} +fun method_in_object(method: *ast_node, enclosing_object: *ast_node): bool { + var methods = enclosing_object->type_def.methods + for (var i = 0; i < methods.size; i++;) { + if (methods[i] == method || (is_template(methods[i]) && methods[i]->template.instantiated.contains(method))) { + return true + } + } + return false +} +fun is_dot_style_method_call(node: *ast_node): bool { + return is_function_call(node->function_call.func) && + is_function(node->function_call.func->function_call.func) && + (node->function_call.func->function_call.func->function.name == "->" || node->function_call.func->function_call.func->function.name == ".") && + is_function(node->function_call.func->function_call.parameters[1]) && + (is_type_def(get_ast_scope(node->function_call.func->function_call.parameters[1])->get(string("~enclosing_scope"))[0]) || + // or if it's a templated method (yes, this has gotten uuuuugly) + is_type_def(get_ast_scope(get_ast_scope(node->function_call.func->function_call.parameters[1])->get(string("~enclosing_scope"))[0])->get(string("~enclosing_scope"))[0]) || + // or it's in an adt + is_adt_def(get_ast_scope(node->function_call.func->function_call.parameters[1])->get(string("~enclosing_scope"))[0])) + // should get uglier when we have to figure out if it's just an inside lambda +} +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 { + var results = scope_lookup(name, scope) + 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] + } + } + return null() +} +fun identifier_lookup(name: ref string, scope: *ast_node): *ast_node { + /*println(string("doing identifier lookup for: ") + name)*/ + var results = scope_lookup(name, scope) + if (!results.size) { + /*println(string("identifier lookup failed for ") + name)*/ + return null() + } + return results[0] +} +fun scope_lookup(name: ref string, scope: *ast_node): vector<*ast_node> { + // println("*****Doing a name lookup for*****") + // println(name) + var results = vector(scope) + name.split("::").for_each(fun(i: string) { + // println(string("based on split, looking up: ") + i) + var next_results = vector<*ast_node>() + results.for_each(fun(s: *ast_node) { + // print("looking in scope: ") + // println(s) + scope_lookup_helper(i, s, set<*ast_node>()).for_each(fun (result: *ast_node) { + if (!next_results.contains(result)) + next_results.add(result) + }) + }) + results = next_results + }) + return results +} +fun scope_lookup_helper(name: ref string, scope: *ast_node, visited: set<*ast_node>): vector<*ast_node> { + // need to do properly scopded lookups + // print("scope is: ") + // get_ast_scope(scope)->for_each(fun(key: string, value: vector<*ast_node>) print(key + " ");) + // println() + var results = vector<*ast_node>() + // prevent re-checking the same one... + if (visited.contains(scope)) + return results + visited.add(scope) + if (get_ast_scope(scope)->contains_key(name)) { + // println(name + " is in scope, adding to results") + results += get_ast_scope(scope)->get(name) + } + if (get_ast_scope(scope)->contains_key(string("~enclosing_scope"))) + results += scope_lookup_helper(name, get_ast_scope(scope)->get(string("~enclosing_scope"))[0], visited) + if (is_translation_unit(scope)) { + scope->translation_unit.children.for_each(fun(child: *ast_node) { + if (is_import(child)) { + if (child->import.imported.contains(name)) { + // println(name + " is indeed imported") + results += scope_lookup_helper(name, child->import.translation_unit, visited) + } else if (child->import.starred) { + // println("import has an import *, checking along it") + results += scope_lookup_helper(name, child->import.translation_unit, visited) + } else { + // println(name + " is not imported (this time)") + // print("import imports") + // child->import.imported.for_each(fun(it: string) print(it + " ");) + } + } + }) + } + return results +} +fun 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 { + var to_ret = function_lookup(name, object, parameter_types) || false + return to_ret +} +fun get_from_scope(node: *ast_node, member: *char): *ast_node + return get_from_scope(node, string(member)) +fun get_from_scope(node: *ast_node, member: string): *ast_node + return get_ast_scope(node)->get(member).first() +fun 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 { + // 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);)) + 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 { + var access_op = "." + if (get_ast_type(object_ident)->indirection) + access_op = "->" + var method_access = ast_function_call_ptr(get_builtin_function(string(access_op), vector(get_ast_type(object_ident), get_ast_type(method))), vector(object_ident, method)) + return ast_function_call_ptr(method_access, parameters) +} +fun make_operator_call(func: *char, params: vector<*ast_node>): *ast_node return make_operator_call(string(func), params); +fun make_operator_call(func: string, params: vector<*ast_node>): *ast_node { + return ast_function_call_ptr(get_builtin_function(func, params.map(fun(p:*ast_node): *type return get_ast_type(p);)), params) +} +fun get_builtin_function(name: *char, param_types: vector<*type>): *ast_node + return get_builtin_function(string(name), param_types, null>()) +fun get_builtin_function(name: string, param_types: vector<*type>): *ast_node + return get_builtin_function(name, param_types, null>()) +fun get_builtin_function(name: string, param_types: vector<*type>, syntax: *tree): *ast_node { + // none of the builtin functions should take in references + param_types = param_types.map(fun(t: *type): *type return t->clone_without_ref();) + if (name == "==" || name == "!=" || name == ">" || name == "<" || name == "<=" || name == ">" || name == ">=" || name == "&&" || name == "||" || name == "!") + return ast_function_ptr(name, type_ptr(param_types, type_ptr(base_type::boolean())), vector<*ast_node>(), false) + if (name == "." || name == "->") { + if (name == "->" && param_types[0]->indirection == 0) + error(syntax, string("drereferencing not a pointer: ") + name) + else if (name == "." && param_types[0]->indirection != 0) + error(syntax, string("dot operator on a pointer: ") + name) + else + return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>(), false) + } + if (name == "[]") { + if (param_types[0]->indirection == 0) + error(syntax, string("drereferencing not a pointer: ") + name) + else + return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_decreased_indirection()), vector<*ast_node>(), false) + } + if (name == "&" && param_types.size == 1) + return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_increased_indirection()), vector<*ast_node>(), false) + if (name == "\*" && param_types.size == 1) { + if (param_types[0]->indirection == 0) + error(syntax, string("drereferencing not a pointer: ") + name) + else + return ast_function_ptr(name, type_ptr(param_types, param_types[0]->clone_with_decreased_indirection()), vector<*ast_node>(), false) + } + if (param_types.size > 1 && param_types[1]->rank() > param_types[0]->rank()) + return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>(), false) + return ast_function_ptr(name, type_ptr(param_types, param_types[0]), vector<*ast_node>(), false) +} fun possible_object_equality(lvalue: *ast_node, rvalue: *ast_node): *ast_node { var ltype = get_ast_type(lvalue) var rtype = get_ast_type(rvalue) @@ -19,7 +212,40 @@ fun possible_object_equality(lvalue: *ast_node, rvalue: *ast_node): *ast_node { return ast_value_ptr(string("false"), type_ptr(base_type::boolean())) return make_operator_call("==", vector(lvalue, rvalue)) } - +fun concat_symbol_tree(node: *tree): string { + var str.construct(): string + if (node->data.data != "no_value") + str += node->data.data + node->children.for_each(fun(child: *tree) str += concat_symbol_tree(child);) + return str +} +fun get_node(lookup: *char, parent: *tree): *tree { + return get_node(string(lookup), parent) +} +fun get_node(lookup: string, parent: *tree): *tree { + var results = get_nodes(lookup, parent) + if (results.size > 1) + error(parent, "get node too many results!") + if (results.size) + return results[0] + return null>() +} +fun get_nodes(lookup: *char, parent: *tree): vector<*tree> { + return get_nodes(string(lookup), parent) +} +fun get_nodes(lookup: string, parent: *tree): vector<*tree> { + return parent->children.filter(fun(node: *tree):bool return node->data.name == lookup;) +} +fun add_to_scope(name: *char, to_add: *ast_node, add_to: *ast_node) { + add_to_scope(string(name), to_add, add_to) +} +fun add_to_scope(name: string, to_add: *ast_node, add_to: *ast_node) { + var add_to_map = get_ast_scope(add_to) + if (add_to_map->contains_key(name)) + (*add_to_map)[name].add(to_add) + else + add_to_map->set(name, vector(to_add)) +} // for now, needs source to already be in a variable for copy_constructing fun assign_or_copy_construct_statement(lvalue: *ast_node, rvalue: *ast_node): *ast_node { var ltype = get_ast_type(lvalue) diff --git a/stdlib/util.krak b/stdlib/util.krak index d3e0a74..dca1361 100644 --- a/stdlib/util.krak +++ b/stdlib/util.krak @@ -20,6 +20,14 @@ fun error(message: string::string) { io::printlnerr(message) os::exit(-1) } +fun assert(works: bool, message: *char) { + if (!works) + error(message) +} +fun assert(works: bool, message: string::string) { + if (!works) + error(message) +} fun deref_equality(a: *T, b: *T): bool { if ( (a && b && !(*a == *b)) || (a && !b) || (!a && b) )