diff --git a/stdlib/c_generator.krak b/stdlib/c_generator.krak index 588ac74..22c0ae5 100644 --- a/stdlib/c_generator.krak +++ b/stdlib/c_generator.krak @@ -196,7 +196,7 @@ obj c_generator (Object) { // start out with one stack on the stack var defer_stack = stack>>(make_pair(false, stack<*ast_node>())) - var decorated_name = generate_function(child, enclosing_object, null(), false).one_string() + var decorated_name = generate_function(child, enclosing_object, null(), false, false).one_string() backing.parameters.for_each(fun(parameter: *ast_node) { if (parameter_types != "") { parameter_types += ", "; parameters += ", ";} parameter_types += type_to_c(parameter->identifier.type) @@ -303,7 +303,7 @@ obj c_generator (Object) { } fun generate_if_comp(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple { if (node->if_comp.wanted_generator == "__C__") - return generate(node->if_comp.statement, enclosing_object, enclosing_func, defer_stack) + return generate(node->if_comp.statement, enclosing_object, enclosing_func, defer_stack, false) return code_triple() } fun generate_simple_passthrough(node: *ast_node, is_top_level: bool): string { @@ -328,7 +328,7 @@ obj c_generator (Object) { return pre + result + post return pre + "{" + result + "}" + post } - fun generate_statement(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple return generate(node->statement.child, enclosing_object, enclosing_func, defer_stack) + ";\n"; + fun generate_statement(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple return generate(node->statement.child, enclosing_object, enclosing_func, defer_stack, false) + ";\n"; fun generate_declaration_statement(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>, add_to_defer: bool): code_triple { // add destruct to defer_stack var identifier = node->declaration_statement.identifier @@ -339,44 +339,43 @@ obj c_generator (Object) { if (node->declaration_statement.expression) { if (ident_type->is_object() && has_method(ident_type->type_def, "copy_construct", vector(get_ast_type(node->declaration_statement.expression)->clone_with_increased_indirection()))) { to_ret.pre += ";\n" - to_ret += generate(ast_statement_ptr(make_method_call(identifier, "copy_construct", vector(make_operator_call("&", vector(node->declaration_statement.expression))))), enclosing_object, enclosing_func, defer_stack) + to_ret += generate(ast_statement_ptr(make_method_call(identifier, "copy_construct", vector(make_operator_call("&", vector(node->declaration_statement.expression))))), enclosing_object, enclosing_func, defer_stack, false) } else { if (ident_type->is_function()) { to_ret.pre += string(";\n") - to_ret += code_triple() + get_name(identifier) + " = " + generate(node->declaration_statement.expression, enclosing_object, enclosing_func, null>>>()) + to_ret += code_triple() + get_name(identifier) + " = " + generate(node->declaration_statement.expression, enclosing_object, enclosing_func, null>>>(), false) } else { // some shifting around to get it to work in all cases // what cases? to_ret.value = to_ret.pre to_ret.pre = "" - to_ret += code_triple() + string(" = ") + generate(node->declaration_statement.expression, enclosing_object, enclosing_func, null>>>()) - // to_ret += code_triple() + string(" = ") + generate(node->declaration_statement.expression, enclosing_object, enclosing_func, null>>>()).one_string() + to_ret += code_triple() + string(" = ") + generate(node->declaration_statement.expression, enclosing_object, enclosing_func, null>>>(), false) } } } if (node->declaration_statement.init_method_call) { to_ret.pre += ";\n" - to_ret += code_triple() + generate(node->declaration_statement.init_method_call, enclosing_object, enclosing_func, null>>>()) + to_ret += code_triple() + generate(node->declaration_statement.init_method_call, enclosing_object, enclosing_func, null>>>(), false) } if (add_to_defer && ident_type->is_object() && ident_type->indirection == 0 && has_method(ident_type->type_def, "destruct", vector<*type>())) defer_stack->top().second.push(ast_statement_ptr(make_method_call(identifier, "destruct", vector<*ast_node>()))) return to_ret } fun generate_assignment_statement(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node): code_triple { - return generate(node->assignment_statement.to, enclosing_object, enclosing_func, null>>>()) + " = " + generate(node->assignment_statement.from, enclosing_object, enclosing_func, null>>>()) + return generate(node->assignment_statement.to, enclosing_object, enclosing_func, null>>>(), false) + " = " + generate(node->assignment_statement.from, enclosing_object, enclosing_func, null>>>(), false) } fun generate_if_statement(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple { - var if_str = code_triple("if (") + generate(node->if_statement.condition, enclosing_object, enclosing_func, null>>>()) + ") {\n" + generate(node->if_statement.then_part, enclosing_object, enclosing_func, defer_stack).one_string() + "}" + var if_str = code_triple("if (") + generate(node->if_statement.condition, enclosing_object, enclosing_func, null>>>(), false) + ") {\n" + generate(node->if_statement.then_part, enclosing_object, enclosing_func, defer_stack, false).one_string() + "}" if (node->if_statement.else_part) - if_str += code_triple(" else {\n") + generate(node->if_statement.else_part, enclosing_object, enclosing_func, defer_stack).one_string() + "}" + if_str += code_triple(" else {\n") + generate(node->if_statement.else_part, enclosing_object, enclosing_func, defer_stack, false).one_string() + "}" return if_str + "\n" } fun generate_while_loop(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple { // stick another stack on defer_stack->push(make_pair(true, stack<*ast_node>())) - var condition = generate(node->while_loop.condition, enclosing_object, enclosing_func, null>>>()) + var condition = generate(node->while_loop.condition, enclosing_object, enclosing_func, null>>>(), false) var to_ret = code_triple("while (1) {\n") + condition.pre + "if(!" + condition.value + ") {" + condition.post + "break;}" + condition.post - to_ret += generate(node->while_loop.statement, enclosing_object, enclosing_func, defer_stack).one_string() + to_ret += generate(node->while_loop.statement, enclosing_object, enclosing_func, defer_stack, false).one_string() to_ret += generate_from_defer_stack(defer_stack, 1, enclosing_object, enclosing_func) defer_stack->pop() to_ret += "}\n" @@ -386,14 +385,14 @@ obj c_generator (Object) { // stick another stack on defer_stack->push(make_pair(true, stack<*ast_node>())) // gotta take off last semicolon - var init = generate(node->for_loop.init, enclosing_object, enclosing_func, null>>>()) - var cond = generate(node->for_loop.condition, enclosing_object, enclosing_func, null>>>()) - var update = generate(node->for_loop.update, enclosing_object, enclosing_func, null>>>()) + var init = generate(node->for_loop.init, enclosing_object, enclosing_func, null>>>(), false) + var cond = generate(node->for_loop.condition, enclosing_object, enclosing_func, null>>>(), false) + var update = generate(node->for_loop.update, enclosing_object, enclosing_func, null>>>(), false) var do_update_name = string("do_update") + get_id() var to_ret = string("{\n") + init.one_string() + "bool " + do_update_name + " = false;\nfor (;;) {\n" to_ret += string("if (") + do_update_name + ") {" + update.one_string() + "}\n" + do_update_name + " = true;\n" to_ret += cond.pre + "if (!" + cond.value + ") {" + cond.post + "break;}" + cond.post - to_ret += generate(node->for_loop.body, enclosing_object, enclosing_func, defer_stack).one_string() + to_ret += generate(node->for_loop.body, enclosing_object, enclosing_func, defer_stack, false).one_string() to_ret += generate_from_defer_stack(defer_stack, 1, enclosing_object, enclosing_func).one_string() defer_stack->pop() to_ret += "}/*end inner for*/}/*end for's enclosing block*/\n" @@ -420,23 +419,32 @@ obj c_generator (Object) { var to_ret = code_triple() if (return_value) { var return_value_type = get_ast_type(return_value) - // should this happen every time so that we're sure that we don't call a function after we destruct some things it depends on + // always need a return temp so we don't destruct things the return depends on before they're calculated // if we're returning an object, copy_construct a new one to return + + // if we're returning a ref, we need to account for that in the temp type + // note the temp type is a pointer, not a ref so we don't have the deref/ref thing on return + var temp_ident_type = return_value_type + if (function_return_type->is_ref) + temp_ident_type = temp_ident_type->clone_with_increased_indirection() + var temp_ident = ast_identifier_ptr(string("temporary_return")+get_id(), temp_ident_type, null()) + var declaration = ast_declaration_statement_ptr(temp_ident, null()) + // have to pass false to the declaration generator, so can't do it through generate_statement + to_ret.pre = generate_declaration_statement(declaration, enclosing_object, enclosing_func, defer_stack, false).one_string() + ";\n" if (return_value_type->is_object() && !function_return_type->is_ref && return_value_type->indirection == 0 && has_method(return_value_type->type_def, "copy_construct", vector(return_value_type->clone_with_indirection(1)))) { - var temp_ident = ast_identifier_ptr(string("temporary_return")+get_id(), return_value_type, null()) - var declaration = ast_declaration_statement_ptr(temp_ident, null()) - // have to pass false to the declaration generator, so can't do it through generate_statement - to_ret.pre = generate_declaration_statement(declaration, enclosing_object, enclosing_func, defer_stack, false).one_string() + ";\n" to_ret.pre += generate_statement(ast_statement_ptr(make_method_call(temp_ident, "copy_construct", vector(make_operator_call("&", vector(return_value))))), enclosing_object, enclosing_func, defer_stack).one_string() - // make this new identifier the new return value - return_value = temp_ident + } else { + var refamp = string() + if (function_return_type->is_ref) + refamp = "&" + to_ret.pre += (generate(temp_ident, enclosing_object, enclosing_func, defer_stack, false) + " = " + refamp + generate(return_value, enclosing_object, enclosing_func, defer_stack, false) + ";").one_string() + ";\n" } + // make this new identifier the new return value + return_value = temp_ident } to_ret += code_triple("return") - if (function_return_type->is_ref) - to_ret += code_triple(" &") if (return_value) - to_ret += code_triple(" ") + generate(return_value, enclosing_object, enclosing_func, null>>>()) + to_ret += code_triple(" ") + generate(return_value, enclosing_object, enclosing_func, null>>>(), false) // generate all in stack by passing -1, make sure added after we calculate the return value to_ret.pre += generate_from_defer_stack(defer_stack, -1, enclosing_object, enclosing_func).one_string() @@ -463,43 +471,58 @@ obj c_generator (Object) { num++ } for (var i = 0; i < num; i++;) - defer_stack->from_top(i).second.for_each_reverse(fun(node: *ast_node) to_ret += generate(node, enclosing_object, enclosing_func, null>>>());) + defer_stack->from_top(i).second.for_each_reverse(fun(node: *ast_node) to_ret += generate(node, enclosing_object, enclosing_func, null>>>(), false);) return to_ret } fun generate_defer_statement(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple { defer_stack->top().second.push(node->defer_statement.statement) return code_triple("/*defer wanna know what*/") } - fun generate_value(node: *ast_node): code_triple { + fun generate_value(node: *ast_node, need_variable: bool): code_triple { var value = node->value.string_value - if (value[0] != '"') - return code_triple(value); - var to_ret = string("\"") - if (value.slice(0,3) == "\"\"\"") - value = value.slice(3,-4) - else - value = value.slice(1,-2) - value.for_each(fun(c: char) { - if (c == '\n') - to_ret += "\\n" - else if (c == '"') - to_ret += "\\\"" - else - to_ret += c - }) - return code_triple(to_ret + "\"") + var to_ret = string() + if (value[0] != '"') { + to_ret = value; + } else { + to_ret = string("\"") + if (value.slice(0,3) == "\"\"\"") + value = value.slice(3,-4) + else + value = value.slice(1,-2) + value.for_each(fun(c: char) { + if (c == '\n') + to_ret += "\\n" + else if (c == '"') + to_ret += "\\\"" + else + to_ret += c + }) + to_ret += "\"" + } + if (need_variable) { + var temp_ident = ast_identifier_ptr(string("temporary_value")+get_id(), get_ast_type(node), null()) + var declaration = ast_declaration_statement_ptr(temp_ident, null()) + // have to pass false to the declaration generator, so can't do it through generate_statement + var trip_ret = code_triple() + // trip_ret.pre += generate_declaration_statement(declaration, enclosing_object, enclosing_func, null>>>(), false).one_string() + " = " + to_ret + ";\n" + // trip_ret.value = generate_identifier(temp_ident, enclosing_object, enclosing_func).one_string() + trip_ret.pre += generate_declaration_statement(declaration, null(), null(), null>>>(), false).one_string() + " = " + to_ret + ";\n" + trip_ret.value = generate_identifier(temp_ident, null(), null()).one_string() + return trip_ret + } + return code_triple(to_ret) } fun generate_code_block(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple { var to_ret = code_triple("{\n") // stick another stack on defer_stack->push(make_pair(false, stack<*ast_node>())) - node->code_block.children.for_each(fun(child: *ast_node) to_ret += generate(child, enclosing_object, enclosing_func, defer_stack).one_string();) + node->code_block.children.for_each(fun(child: *ast_node) to_ret += generate(child, enclosing_object, enclosing_func, defer_stack, false).one_string();) to_ret += generate_from_defer_stack(defer_stack, 1, enclosing_object, enclosing_func) defer_stack->pop() return to_ret + "}" } // this generates the function as a value, not the actual function - fun generate_function(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, as_value: bool): code_triple { + fun generate_function(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, as_value: bool, need_variable: bool): code_triple { if (as_value) { var closed_vars = node->function.closed_variables if (closed_vars.size() == 0) @@ -510,14 +533,15 @@ obj c_generator (Object) { to_ret.pre += closure_type_str + " " + temp_closure_struct + " = (" + closure_type_str + "){" closed_vars.for_each(fun(i: *ast_node) { // note that we get/have gotten rid of refs here, or maybe more accurately, everything is a ref - to_ret.pre += string(".") + get_name(i) + "=&" + generate(i, enclosing_object, enclosing_func, null>>>()).one_string() + "," + // should be a variable anyway? + to_ret.pre += string(".") + get_name(i) + "=&" + generate(i, enclosing_object, enclosing_func, null>>>(), true).one_string() + "," }) to_ret.pre += "};\n" return to_ret + string("((") + type_to_c(node->function.type) + "){(void*)&" + temp_closure_struct + ",(void*)" + get_name(node) + "})" } return code_triple(get_name(node)) } - fun generate_function_call(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node): code_triple { + fun generate_function_call(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, need_variable: bool): code_triple { var func_name = string() var call_string = code_triple() var func_return_type = get_ast_type(node) @@ -533,16 +557,18 @@ obj c_generator (Object) { // should get uglier when we have to figure out if it's just an inside lambda if (dot_style_method_call) { - func_name = generate_function(node->function_call.func->function_call.parameters[1], enclosing_object, enclosing_func, false).one_string() + func_name = generate_function(node->function_call.func->function_call.parameters[1], enclosing_object, enclosing_func, false, false).one_string() // don't add & if it was -> if (node->function_call.func->function_call.func->function.name == ".") call_string += "&" // having a null defer stack should be ok, as the only things we should get through here are identifiers and function calls // XXX should it? wouldn't function calls be a problem? - call_string += generate(node->function_call.func->function_call.parameters[0], enclosing_object, enclosing_func, null>>>()) + + // should this be true if ref? + call_string += generate(node->function_call.func->function_call.parameters[0], enclosing_object, enclosing_func, null>>>(), true) } else if (is_identifier(node->function_call.func) || is_function(node->function_call.func)) { // we handle the case when it's not this later, i.e. it's a lambda returned from another function or something - func_name = generate_function(node->function_call.func, enclosing_object, enclosing_func, false).one_string() + func_name = generate_function(node->function_call.func, enclosing_object, enclosing_func, false, false).one_string() } // handle method call from inside method of same object if (!dot_style_method_call && enclosing_object) { @@ -563,10 +589,10 @@ obj c_generator (Object) { || func_name == "<" || func_name == ">" || func_name == "<=" || func_name == ">=" || func_name == "==" || func_name == "!=" || func_name == "%" )) - return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>()) + func_name + generate(parameters[1], enclosing_object, enclosing_func, null>>>()) + string(")") + return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>(), false) + func_name + generate(parameters[1], enclosing_object, enclosing_func, null>>>(), false) + string(")") if ( parameters.size == 2 && (func_name == "||" || func_name == "&&")) { - var first = generate(parameters[0], enclosing_object, enclosing_func, null>>>()) - var second = generate(parameters[1], enclosing_object, enclosing_func, null>>>()) + var first = generate(parameters[0], enclosing_object, enclosing_func, null>>>(), false) + var second = generate(parameters[1], enclosing_object, enclosing_func, null>>>(), false) var result = code_triple() result.pre += first.pre; var temp_bool = string("temp_bool") + get_id() @@ -586,16 +612,17 @@ obj c_generator (Object) { // don't propegate enclosing function down right of access // XXX what about enclosing object? should it be the thing on the left? if (func_name == "." || func_name == "->") - return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>()) + func_name + generate(parameters[1], null(), null(), null>>>()) + string(")") + return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>(), false) + func_name + generate(parameters[1], null(), null(), null>>>(), false) + string(")") if (func_name == "[]") - return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>()) + "[" + generate(parameters[1], enclosing_object, enclosing_func, null>>>()) + string("])") + return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>(), false) + "[" + generate(parameters[1], enclosing_object, enclosing_func, null>>>(), false) + string("])") // the post ones need to be post-ed specifically, and take the p off if (func_name == "++p" || func_name == "--p") - return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>()) + ")" + func_name.slice(0,-2) + return code_triple("(") + generate(parameters[0], enclosing_object, enclosing_func, null>>>(), false) + ")" + func_name.slice(0,-2) // So we don't end up copy_constructing etc, we just handle the unary operators right here + // note also the passing down need_variable for & if (func_name == "*" || func_name == "&") - return code_triple("(") + func_name + generate(parameters[0], enclosing_object, enclosing_func, null>>>()) + ")" + return code_triple("(") + func_name + generate(parameters[0], enclosing_object, enclosing_func, null>>>(), func_name == "&") + ")" // for checking if we pass in a ref var func_type = get_ast_type(node->function_call.func) @@ -617,18 +644,20 @@ obj c_generator (Object) { // have to pass false to the declaration generator, so can't do it through generate_statement call_string.pre += generate_declaration_statement(declaration, enclosing_object, enclosing_func, null>>>(), false).one_string() + ";\n" call_string.pre += generate_statement(ast_statement_ptr(make_method_call(temp_ident, "copy_construct", vector(make_operator_call("&", vector(param))))), enclosing_object, enclosing_func, null>>>()).one_string() - call_string += generate(temp_ident, enclosing_object, enclosing_func, null>>>()) + call_string += generate(temp_ident, enclosing_object, enclosing_func, null>>>(), false) } else { - call_string += generate(param, enclosing_object, enclosing_func, null>>>()) + call_string += generate(param, enclosing_object, enclosing_func, null>>>(), in_function_param_type->is_ref) } } var pre_call = string() // we now have temporary return variables for all objects, even without destruct so we can do chained method calls // actually all non-ref returns, for chained any calls + // well, now only if we also pass in true for need_variable // XXX this should change to only if we know we need it by having an enum or bool passed down to this call... // if (func_return_type->is_object() && !func_return_type->is_ref && func_return_type->indirection == 0 && has_method(func_return_type->type_def, "destruct", vector<*type>())) { // if (func_return_type->is_object() && !func_return_type->is_ref && func_return_type->indirection == 0) { - if (!func_return_type->is_ref && !func_return_type->is_void()) { + var needs_temp_for_destruct = func_return_type->is_object() && func_return_type->indirection == 0 && has_method(func_return_type->type_def, "destruct", vector<*type>()) + if (!func_return_type->is_ref && (needs_temp_for_destruct || (!func_return_type->is_void() && need_variable)) ) { // kind of ugly combo here of var temp_ident = ast_identifier_ptr(string("temporary_return")+get_id(), func_return_type, null()) var declaration = ast_declaration_statement_ptr(temp_ident, null()) @@ -636,7 +665,7 @@ obj c_generator (Object) { call_string.pre += generate_declaration_statement(declaration, enclosing_object, enclosing_func, null>>>(), false).one_string() + ";\n" pre_call = generate_identifier(temp_ident, enclosing_object, enclosing_func).one_string() // move destruct condition inside - if (func_return_type->is_object() && func_return_type->indirection == 0 && has_method(func_return_type->type_def, "destruct", vector<*type>())) + if (needs_temp_for_destruct) call_string.post += generate_statement(ast_statement_ptr(make_method_call(temp_ident, "destruct", vector<*ast_node>())), enclosing_object, enclosing_func, null>>>()).one_string() } var ref_pre = string() @@ -656,7 +685,7 @@ obj c_generator (Object) { call_string.pre += generate_declaration_statement(declaration, enclosing_object, enclosing_func, null>>>(), false).one_string() + ";\n" pre_call = generate_identifier(temp_ident, enclosing_object, enclosing_func).one_string() } - var name_temp = generate(node->function_call.func, enclosing_object, enclosing_func, null>>>()) + var name_temp = generate(node->function_call.func, enclosing_object, enclosing_func, null>>>(), false) call_string.pre += name_temp.pre call_string.post += name_temp.post func_name = name_temp.value @@ -693,7 +722,7 @@ obj c_generator (Object) { } // for now, anyway - fun generate(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>): code_triple { + fun generate(node: *ast_node, enclosing_object: *ast_node, enclosing_func: *ast_node, defer_stack: *stack>>, need_variable: bool): code_triple { if (!node) return code_triple("/*NULL*/") match (*node) { ast_node::if_comp(backing) return generate_if_comp(node, enclosing_object, enclosing_func, defer_stack) @@ -704,13 +733,13 @@ obj c_generator (Object) { ast_node::if_statement(backing) return generate_if_statement(node, enclosing_object, enclosing_func, defer_stack) ast_node::while_loop(backing) return generate_while_loop(node, enclosing_object, enclosing_func, defer_stack) ast_node::for_loop(backing) return generate_for_loop(node, enclosing_object, enclosing_func, defer_stack) - ast_node::function(backing) return generate_function(node, enclosing_object, enclosing_func, true) - ast_node::function_call(backing) return generate_function_call(node, enclosing_object, enclosing_func) + ast_node::function(backing) return generate_function(node, enclosing_object, enclosing_func, true, need_variable) + ast_node::function_call(backing) return generate_function_call(node, enclosing_object, enclosing_func, need_variable) ast_node::code_block(backing) return generate_code_block(node, enclosing_object, enclosing_func, defer_stack) ast_node::return_statement(backing) return generate_return_statement(node, enclosing_object, enclosing_func, defer_stack) ast_node::branching_statement(backing) return generate_branching_statement(node, enclosing_object, enclosing_func, defer_stack) ast_node::defer_statement(backing) return generate_defer_statement(node, enclosing_object, enclosing_func, defer_stack) - ast_node::value(backing) return generate_value(node) + ast_node::value(backing) return generate_value(node, need_variable) ast_node::identifier(backing) return generate_identifier(node, enclosing_object, enclosing_func) } return code_triple("/* COULD NOT GENERATE */")