Refactored interpreter into just functions, added a REPL to the main kraken.
This commit is contained in:
@@ -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<symbol>): *tree<symbol> {
|
||||
if (!source)
|
||||
return null<tree<symbol>>()
|
||||
if (source->data.terminal)
|
||||
return source
|
||||
if (source->children.size == 0)
|
||||
return null<tree<symbol>>()
|
||||
return get_first_terminal(source->children.first())
|
||||
}
|
||||
fun error(source: *tree<symbol>, message: *char) error(source, string(message));
|
||||
fun error(source: *tree<symbol>, 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<ast_node>()
|
||||
}
|
||||
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<ast_node>()
|
||||
}
|
||||
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<tree<symbol>>())
|
||||
fun get_builtin_function(name: string, param_types: vector<*type>): *ast_node
|
||||
return get_builtin_function(name, param_types, null<tree<symbol>>())
|
||||
fun get_builtin_function(name: string, param_types: vector<*type>, syntax: *tree<symbol>): *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<symbol>): string {
|
||||
var str.construct(): string
|
||||
if (node->data.data != "no_value")
|
||||
str += node->data.data
|
||||
node->children.for_each(fun(child: *tree<symbol>) str += concat_symbol_tree(child);)
|
||||
return str
|
||||
}
|
||||
fun get_node(lookup: *char, parent: *tree<symbol>): *tree<symbol> {
|
||||
return get_node(string(lookup), parent)
|
||||
}
|
||||
fun get_node(lookup: string, parent: *tree<symbol>): *tree<symbol> {
|
||||
var results = get_nodes(lookup, parent)
|
||||
if (results.size > 1)
|
||||
error(parent, "get node too many results!")
|
||||
if (results.size)
|
||||
return results[0]
|
||||
return null<tree<symbol>>()
|
||||
}
|
||||
fun get_nodes(lookup: *char, parent: *tree<symbol>): vector<*tree<symbol>> {
|
||||
return get_nodes(string(lookup), parent)
|
||||
}
|
||||
fun get_nodes(lookup: string, parent: *tree<symbol>): vector<*tree<symbol>> {
|
||||
return parent->children.filter(fun(node: *tree<symbol>):bool return node->data.name == lookup;)
|
||||
}
|
||||
fun add_to_scope(name: *char, to_add: *ast_node, add_to: *ast_node) {
|
||||
add_to_scope(string(name), to_add, add_to)
|
||||
}
|
||||
fun add_to_scope(name: string, to_add: *ast_node, add_to: *ast_node) {
|
||||
var add_to_map = get_ast_scope(add_to)
|
||||
if (add_to_map->contains_key(name))
|
||||
(*add_to_map)[name].add(to_add)
|
||||
else
|
||||
add_to_map->set(name, vector(to_add))
|
||||
}
|
||||
// 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)
|
||||
|
||||
Reference in New Issue
Block a user