From 828f36daabc2e641fb239e72c89175831510000e Mon Sep 17 00:00:00 2001 From: Nathan Braswell Date: Sat, 21 May 2016 11:20:29 -0700 Subject: [PATCH] Adding in proper copy_construct into function calls and destruct afterwards. Doesn't correctly destruct return values yet. --- LICENCE.md | 2 +- stdlib/ast_transformation.krak | 2 + stdlib/interpreter.krak | 146 +++++++++++++++++++++------------ stdlib/io.krak | 2 +- stdlib/type.krak | 2 + 5 files changed, 100 insertions(+), 54 deletions(-) diff --git a/LICENCE.md b/LICENCE.md index 4717d3f..3b756d5 100644 --- a/LICENCE.md +++ b/LICENCE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Nathan Christopher Braswell +Copyright (c) 2014-2016 Nathan Christopher Braswell, Google Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/stdlib/ast_transformation.krak b/stdlib/ast_transformation.krak index 6e4438d..a7bc45b 100644 --- a/stdlib/ast_transformation.krak +++ b/stdlib/ast_transformation.krak @@ -1139,6 +1139,8 @@ fun get_builtin_function(name: string, param_types: vector<*type>): *ast_node { if (name == "." || name == "->") { if (name == "->" && param_types[0]->indirection == 0) error(string("drereferencing not a pointer: ") + name) + else if (name == "." && param_types[0]->indirection != 0) + error(string("dot operator on a pointer: ") + name) else return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>(), false) } diff --git a/stdlib/interpreter.krak b/stdlib/interpreter.krak index 5fc2380..9b60cb4 100644 --- a/stdlib/interpreter.krak +++ b/stdlib/interpreter.krak @@ -476,7 +476,8 @@ obj interpreter (Object) { println("calling main!") println("=============") var var_stack = stack>() - var result = call_function(results[0], vector(), &var_stack, value::void_nothing()) + var defer_stack = stack<*ast_node>() + var result = call_function(results[0], vector(), vector<*ast_node>(), &var_stack, &defer_stack, value::void_nothing()) println("=============") println("Main returned: ") print_value(result) @@ -518,63 +519,104 @@ obj interpreter (Object) { 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()) } - var parameters = func_call_parameters.map(fun(p: *ast_node): value return interpret(p, var_stack, enclosing_object, defer_stack).first;) - return make_pair(call_function(func_call_func, parameters, var_stack, new_enclosing_object), 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_like() && get_ast_type(p)->indirection == 0;)) { + parameters = func_call_parameters.map(fun(p: *ast_node): value return interpret(p, var_stack, enclosing_object, defer_stack).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 == "++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])) + error("Trying to take address of not a variable") + return make_pair(value::pointer(make_pair(parameters[0].variable.first, parameters[0].variable.second->clone_with_increased_indirection())), control_flow::nor()) + } + // 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(value::variable(make_pair(get_real_value(dereference_val).pointer.first, dereference_val.pointer.second->clone_with_decreased_indirection())), 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 == "fflush" || func_name == "snprintf") + 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, defer_stack, new_enclosing_object), control_flow::nor()) } - fun call_function(func: *ast_node, parameters: vector, var_stack: *stack>, enclosing_object: value): value { + // 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>, defer_stack: *stack<*ast_node>, enclosing_object: value): value { // will need adjustment var func_name = func->function.name - 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 do_basic_op(func_name, parameters[0], parameters[1]) - // do negate by subtracting from zero - if (func_name == "-") - return do_basic_op_second_half(string("-"), 0, parameters[0], null()) - 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 to_ret - } - if (func_name == "++" || func_name == "--") { - store_into_variable(parameters[0], do_basic_op(func_name.slice(0,1), parameters[0], value::integer(1))) - return get_real_value(parameters[0]) - } - if (func_name == "&") { - if (!is_variable(parameters[0])) - error("Trying to take address of not a variable") - return value::pointer(make_pair(parameters[0].variable.first, parameters[0].variable.second->clone_with_increased_indirection())) - } - // 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 value::variable(make_pair(get_real_value(dereference_val).pointer.first, dereference_val.pointer.second->clone_with_decreased_indirection())) - } - // check for built-in-ish externs (everything the standard library needs) - if (func_name == "printf" || func_name == "malloc" || func_name == "free" || func_name == "fflush" || func_name == "snprintf") - return call_built_in_extern(func_name, parameters) - if (!func->function.body_statement) - error(string("trying to call unsupported extern function: ") + func_name) // do regular function var new_var_stack = stack>() - new_var_stack.push(map()) - if (parameters.size != func->function.parameters.size) - error(string("calling function ") + func->function.name + " with wrong number of parameters") - for (var i = 0; i < parameters.size; i++;) { - var param_type = get_ast_type(func)->parameter_types[i] - new_var_stack.top()[func->function.parameters[i]->identifier.name] = value::variable(make_pair(malloc(type_size(param_type)), param_type)) - store_into_variable(new_var_stack.top()[func->function.parameters[i]->identifier.name], get_real_value(parameters[i])) + // the new defer stack takes care of destructing parameters that were copy_constructed + var new_defer_stack = stack<*ast_node>() + // if this is a value based call, pull from parameters + if (parameter_sources.size == 0) { + new_var_stack.push(map()) + /*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] + new_var_stack.top()[param_ident->identifier.name] = value::variable(make_pair(malloc(type_size(param_type)), param_type)) + store_into_variable(new_var_stack.top()[param_ident->identifier.name], get_real_value(parameters[i])) + } + } else { + // on this side we construct in the old var stack, then move it over to the new one so that references resolve correctly + var_stack->push(map()) + /*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] + /*println(param_ident->identifier.name + " is the name of the identifier being added")*/ + var_stack->top()[param_ident->identifier.name] = value::variable(make_pair(malloc(type_size(param_type)), param_type)) + if (param_type->indirection == 0 && (param_type->is_adt() || (param_type->is_object() && has_method(param_type->type_def, "copy_construct", vector(param_type->clone_with_increased_indirection()))))) { + interpret(ast_statement_ptr(make_method_call(param_ident, "copy_construct", vector(make_operator_call("&", vector(parameter_sources[i]))))), var_stack, enclosing_object, defer_stack) + new_defer_stack.push(ast_statement_ptr(make_method_call(param_ident, "destruct", vector<*ast_node>()))) + } else { + store_into_variable(var_stack->top()[param_ident->identifier.name], get_real_value(interpret(parameter_sources[i], var_stack, enclosing_object, defer_stack).first)) + } + } + // swap the params over + new_var_stack.push(var_stack->pop()) } - // our defer stack to keep track of what needs to be evaluated later at runtime - var defer_stack = stack<*ast_node>() - var to_ret = interpret(func->function.body_statement, &new_var_stack, enclosing_object, &defer_stack).first + var to_ret = interpret(func->function.body_statement, &new_var_stack, enclosing_object, &new_defer_stack).first + // handle destructing params + interpret_from_defer_stack(&new_defer_stack, &new_var_stack, enclosing_object) // need to handle copying out object before pop_and_free deletes them pop_and_free(&new_var_stack) return to_ret diff --git a/stdlib/io.krak b/stdlib/io.krak index 8c3797d..80f07e9 100644 --- a/stdlib/io.krak +++ b/stdlib/io.krak @@ -47,7 +47,7 @@ fun print(toPrint: double) { var int_str = new(how_much+2) snprintf(int_str, (how_much+1) cast ulong, "%f", toPrint) print(int_str) - /*delete(int_str)*/ + delete(int_str) } fun print(toPrint: T): void print(string::to_string(toPrint)) diff --git a/stdlib/type.krak b/stdlib/type.krak index 0e9bd1b..b4aaf79 100644 --- a/stdlib/type.krak +++ b/stdlib/type.krak @@ -225,6 +225,8 @@ obj type (Object) { to_ret->is_ref = is_ref_in return to_ret } + fun is_object_like(): bool + return is_object() || is_adt() fun is_object(): bool { match (base) { base_type::object() return true