diff options
| author | Mel <mel@rnrd.eu> | 2026-05-26 04:15:12 +0200 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2026-05-26 04:15:12 +0200 |
| commit | bf82cbd2a7d4e234313e253cb6f7dd351b2c5c5a (patch) | |
| tree | 42dcf198deee1dae3c9b3586dc37e6dd23938244 /boot | |
| parent | 900b412b052a0f745f0853d6703e4bafbb0a3ce5 (diff) | |
| download | catskill-bf82cbd2a7d4e234313e253cb6f7dd351b2c5c5a.tar.zst catskill-bf82cbd2a7d4e234313e253cb6f7dd351b2c5c5a.zip | |
Locate and pass through captured locals to stateful closures
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/ir.c | 101 | ||||
| -rw-r--r-- | boot/lower.c | 171 | ||||
| -rw-r--r-- | boot/transpile.c | 116 | ||||
| -rw-r--r-- | boot/visit/ir.c | 51 |
4 files changed, 348 insertions, 91 deletions
diff --git a/boot/ir.c b/boot/ir.c index 164d9a4..f86d63f 100644 --- a/boot/ir.c +++ b/boot/ir.c @@ -137,6 +137,13 @@ struct Param struct Type_Ref type; }; +// one captured local in a closing function. +struct Capture +{ + struct String name; + struct Type_Ref type; // referenced by capture +}; + // minimal lowered statement set. enum Statement_Kind { @@ -239,7 +246,7 @@ enum Expression_Kind EXPRESSION_FLOAT_LITERAL, EXPRESSION_STRING_LITERAL, EXPRESSION_BOOLEAN_LITERAL, - EXPRESSION_NAME, + EXPRESSION_REFERENCE, EXPRESSION_UNARY_OPERATION, EXPRESSION_BINARY_OPERATION, EXPRESSION_SIZEOF_OPERATION, @@ -249,7 +256,6 @@ enum Expression_Kind EXPRESSION_CAST, EXPRESSION_CONSTRUCT, EXPRESSION_INCREMENT_DECREMENT, - EXPRESSION_FUNCTION_REF, }; struct Expression_Integer_Literal @@ -272,11 +278,56 @@ struct Expression_Bool_Literal bool value; }; -struct Expression_Name +enum Reference_Kind +{ + REFERENCE_NONE, + REFERENCE_NAME, + REFERENCE_FUNCTION, + REFERENCE_CAPTURE, +}; + +// a simple, by-name reference to some language object, +// requiring no special treatment. +struct Reference_Name { struct String name; }; +// a reference to a function, either a named, thin one, +// or a fat, synthetic one. +// when a reference points to a thin named function (basically any +// normal function that's not a closure) and is not a simple, direct call, +// we will have to call out to a wrapping thunk, to match the fat +// calling convention of closures. +struct Reference_Function +{ + Function_Id function_id; + // matching closure type for the function's signature. + Type_Id closure_type_id; +}; + +// a reference to a captured local from another, enclosing +// scope within a closure function. +// will be gathered from the closure state. +struct Reference_Capture +{ + struct String name; +}; + +union Reference_Value +{ + struct Reference_Name name; + struct Reference_Function function; + struct Reference_Capture capture; +}; + +// a reference to some other language object. +struct Expression_Reference +{ + enum Reference_Kind kind; + union Reference_Value value; +}; + struct Expression_Unary_Operator { enum Unary_Operation operation; @@ -356,21 +407,13 @@ struct Expression_Increment_Decrement struct Expression* subject; }; -// reference to a known catskill function in a value context. -struct Expression_Function_Ref -{ - Function_Id function_id; - // matching synthetic closure type for the function's signature. - Type_Id closure_type_id; -}; - union Expression_Value { struct Expression_Integer_Literal integer_literal; struct Expression_Float_Literal float_literal; struct Expression_String_Literal string_literal; struct Expression_Bool_Literal bool_literal; - struct Expression_Name name; + struct Expression_Reference reference; struct Expression_Unary_Operator unary_operator; struct Expression_Binary_Operator binary_operator; struct Expression_Sizeof_Operator sizeof_operator; @@ -380,7 +423,6 @@ union Expression_Value struct Expression_Cast cast; struct Expression_Construct construct; struct Expression_Increment_Decrement increment_decrement; - struct Expression_Function_Ref function_ref; }; struct Expression @@ -420,6 +462,12 @@ struct Function // synthesized lazily on first reference in value context, or 0. Type_Id closure_type_id; + // locals captured from enclosing function scopes, filled during body + // lowering. (only valid for stateful lambdas) + Array(struct Capture) captures; + // synthetic structure containing all captured locals. (only stateful lambdas) + Type_Id capture_state_type_id; + // tree-side header which produced this function node. // required for synthesizing the closure type after function is lowered. struct Tree_Function_Header* ast_header; @@ -428,6 +476,11 @@ struct Function // to collect all top-declarations. struct Tree_Block* ast_body; struct Block* body; + + // has this function been processed to completion already? + // synthetic lambdas get their bodies lowered directly when encountered, + // unlike other function declarations, to get access to the current local stack. + bool completed; }; struct Type_Hash_To_Id @@ -561,6 +614,7 @@ function_new(Function_Id id, struct String name) *function = (struct Function){ .id = id, .name = name, + .captures = array_new(struct Capture, 4), }; return function; } @@ -636,8 +690,9 @@ struct Expression* ir_make_name(struct String name, struct Span span) { union Expression_Value v = { 0 }; - v.name.name = name; - return expression_new(EXPRESSION_NAME, v, span); + v.reference.kind = REFERENCE_NAME; + v.reference.value.name.name = name; + return expression_new(EXPRESSION_REFERENCE, v, span); } struct Expression* @@ -729,12 +784,22 @@ ir_make_increment_decrement( } struct Expression* +ir_make_capture_ref(struct String name, struct Span span) +{ + union Expression_Value v = { 0 }; + v.reference.kind = REFERENCE_CAPTURE; + v.reference.value.capture.name = name; + return expression_new(EXPRESSION_REFERENCE, v, span); +} + +struct Expression* ir_make_function_ref(Function_Id function_id, Type_Id closure_type_id, struct Span span) { union Expression_Value v = { 0 }; - v.function_ref.function_id = function_id; - v.function_ref.closure_type_id = closure_type_id; - return expression_new(EXPRESSION_FUNCTION_REF, v, span); + v.reference.kind = REFERENCE_FUNCTION; + v.reference.value.function.function_id = function_id; + v.reference.value.function.closure_type_id = closure_type_id; + return expression_new(EXPRESSION_REFERENCE, v, span); } struct Type_Ref diff --git a/boot/lower.c b/boot/lower.c index ba83b39..81f39a5 100644 --- a/boot/lower.c +++ b/boot/lower.c @@ -33,6 +33,18 @@ struct Local_Variable struct Type_Ref type; // TODO: type-check & fill out empty }; +// result of looking up a local that may turn out to be a capture for +// the function we are currently inside. +struct Lower_Local_Lookup_Capture +{ + // was the local found in an enclosing function? + bool captured; + // the captured local's declared type + struct Type_Ref type; + // which function does this capture belong to? + struct Function* captured_into; +}; + // what kind of scope is this? // declared on every lexical scope so we can make sure that the scopes are balanced. enum Scope_Type @@ -65,6 +77,14 @@ struct Scope { enum Scope_Type type; Array(struct Local_Variable) variables; + + // function this scope is lexically contained in. + // for function scopes, references the function itself, + // for block scopes, references the enclosing function. + // used to check whether references cross function scope boundary, + // signifying a closure capture. + struct Function* containing_function; + // TODO: add local closure functions & local types. }; @@ -668,6 +688,46 @@ lower_function_closure_type(struct Lower_Context* ctx, struct Function* fn) return ref.type_id; } +// synthesize the per-lambda state struct that carries pointers to each +// captured local. +// each captured local is stored by reference. +// NOTE(mel): the transpiler pass will output this as a normal structure, +// does it make sense to distinguish these state types as something else? +// it muddles distinguishing a type that the user wrote, and a type we synthesized. +Type_Id +lower_function_capture_state_type(struct Lower_Context* ctx, struct Function* fn) +{ + if (fn->capture_state_type_id != 0) return fn->capture_state_type_id; + if (array_length(&fn->captures) == 0) return 0; + + Type_Id id = array_length(&ctx->unit->types.entries); + struct String name = lower_synthesize_type_name(ctx); + struct Type* type = type_new(id, TYPE_STRUCTURE, name, fn->ast_header->span); + type->synthetic = true; + type->depends_on = array_new(Type_Id, 8); + type->value.structure.fields = array_new(struct Field, 8); + + FOR_EACH_ARRAY (struct Capture, cap, &fn->captures) { + // each field carries a pointer to the captured local. peel any + // existing modifiers off the captured type and prepend a reference. + // TODO: correctly nest all modifiers here! + struct Type_Ref ptr = { + .type_id = cap->type.type_id, + .mods = array_new(enum Type_Modifier, 4), + }; + enum Type_Modifier ref_mod = TYPE_MOD_REFERENCE; + array_push(&ptr.mods, &ref_mod); + FOR_EACH_ARRAY (enum Type_Modifier, mod, &cap->type.mods) array_push(&ptr.mods, mod); + + struct Field f = { .name = cap->name, .type = ptr }; + array_push(&type->value.structure.fields, &f); + } + + array_push(&ctx->unit->types.entries, &type); + fn->capture_state_type_id = id; + return id; +} + void lower_fill_type_body(struct Lower_Context* ctx, struct Type* type, struct Tree_Type* tree_type) { @@ -858,6 +918,12 @@ struct Block* lower_block(struct Lower_Context* ctx, struct Tree_Block* tree_blo struct Statement* lower_statement(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt); struct Expression* lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr); bool lower_name_is_function_typed_local(struct Lower_Context* ctx, struct String name); +struct Lower_Local_Lookup_Capture +lower_local_lookup_capturing(struct Lower_Context* ctx, struct String name); +void lower_record_capture(struct Function* fn, struct String name, struct Type_Ref type); +void lower_push_scope_function(struct Lower_Context* ctx, struct Function* owner); +void lower_pop_scope(struct Lower_Context* ctx, enum Scope_Type expected); +void lower_declare_local(struct Lower_Context* ctx, struct String name, struct Type_Ref type); struct Expression* lower_expression_integer_literal(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) @@ -890,16 +956,31 @@ lower_expression_boolean_literal(struct Lower_Context* ctx, struct Tree_Expressi struct Expression* lower_expression_name(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) { + struct String name = tree_expr->value.name.name; + // names that resolve to a catskill function become a function reference. Function_Id fn_id; - if (lower_function_lookup_by_name(ctx->unit, tree_expr->value.name.name, &fn_id)) { + if (lower_function_lookup_by_name(ctx->unit, name, &fn_id)) { // since this path is only taken outside direct call-positions, // the function will need a closure type to wrap it as a value. struct Function* fn = *array_at(struct Function*, &ctx->unit->functions.entries, fn_id); Type_Id closure_type_id = lower_function_closure_type(ctx, fn); return ir_make_function_ref(fn_id, closure_type_id, tree_expr->span); } - return ir_make_name(tree_expr->value.name.name, tree_expr->span); + + // locals that were defined in an upper function scope are captured. + struct Lower_Local_Lookup_Capture lookup = lower_local_lookup_capturing(ctx, name); + if (lookup.captured) { + lower_record_capture(lookup.captured_into, name, lookup.type); + return ir_make_capture_ref(name, tree_expr->span); + } + + // unresolved names will continue being bare names. + // this is required to account for all non-language objects that might + // come from c, or the runtime, or some c-header include we can't see. + // the backend will note any name reference which is still unresolved + // after taking every source into account. + return ir_make_name(name, tree_expr->span); } // any groups are discarded in the lowered representation, their presence @@ -1239,9 +1320,22 @@ lower_expression_function(struct Lower_Context* ctx, struct Tree_Expression* tre fn->synthetic = true; lower_fill_function_signature(ctx, fn, tree_expr); + // lower the body inline so name lookups inside see the enclosing + // function's scope and can detect captures. + lower_push_scope_function(ctx, fn); + FOR_EACH_ARRAY (struct Param, param, &fn->params) + lower_declare_local(ctx, param->name, param->type); + fn->body = lower_block(ctx, fn->ast_body); + lower_pop_scope(ctx, SCOPE_TYPE_FUNCTION); + + fn->completed = true; // mark the function as fully lowered. + // a function literal is always in value context, we can synthesize // the closure type eagerly right here. Type_Id closure_type_id = lower_function_closure_type(ctx, fn); + // if the body captured anything, also synthesize the state type. + lower_function_capture_state_type(ctx, fn); + return ir_make_function_ref(id, closure_type_id, tree_expr->span); } @@ -1312,6 +1406,15 @@ lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) } } +// look up the function whose body we are currently inside. (if there is one) +struct Function* +lower_current_function(struct Lower_Context* ctx) +{ + if (ctx->scope_stack.length == 0) return nil; + struct Scope* top = array_at(struct Scope, &ctx->scope_stack, ctx->scope_stack.length - 1); + return top->containing_function; +} + // push a fresh lexical scope onto the lowering stack. // do not forget to also pop this scope once we leave it! void @@ -1320,6 +1423,21 @@ lower_push_scope(struct Lower_Context* ctx, enum Scope_Type type) struct Scope s = { .type = type, .variables = array_new(struct Local_Variable, 16), + // block scopes inherit the function from their upper scope + .containing_function = lower_current_function(ctx), + }; + array_push(&ctx->scope_stack, &s); +} + +// push a function scope. +void +lower_push_scope_function(struct Lower_Context* ctx, struct Function* owner) +{ + struct Scope s = { + .type = SCOPE_TYPE_FUNCTION, + .variables = array_new(struct Local_Variable, 16), + // the owner of this scope is the function that created it + .containing_function = owner, }; array_push(&ctx->scope_stack, &s); } @@ -1387,6 +1505,44 @@ lower_local_lookup(struct Lower_Context* ctx, struct String name, struct Type_Re return false; } +// walk the scope stack looking for a local, which could be captured. +// if the local was declared in some enclosing function that's not us, +// the local becomes captured by our closure function. +struct Lower_Local_Lookup_Capture +lower_local_lookup_capturing(struct Lower_Context* ctx, struct String name) +{ + struct Function* current = lower_current_function(ctx); + + for (uint i = ctx->scope_stack.length; i > 0; --i) { + struct Scope* scope = array_at(struct Scope, &ctx->scope_stack, i - 1); + FOR_EACH_ARRAY (struct Local_Variable, var, &scope->variables) { + if (!string_equals(var->name, name)) continue; + + bool from_enclosing = current != nil && scope->containing_function != current; + return (struct Lower_Local_Lookup_Capture){ + .captured = from_enclosing, + .type = var->type, + .captured_into = from_enclosing ? current : nil, + }; + } + } + + // no capture. + return (struct Lower_Local_Lookup_Capture){ 0 }; +} + +// add a captured name to a function's capture list. +void +lower_record_capture(struct Function* fn, struct String name, struct Type_Ref type) +{ + // prevent duplication, closed-over locals are often referenced multiple times. + FOR_EACH_ARRAY (struct Capture, c, &fn->captures) { + if (string_equals(c->name, name)) return; + } + struct Capture cap = { .name = name, .type = type }; + array_push(&fn->captures, &cap); +} + // is the named local a function-typed value? // (used to decide direct vs. indirect call dispatch when the call subject is a bare name.) bool @@ -1735,18 +1891,21 @@ void lower_pass_2(struct Lower_Context* ctx) { FOR_EACH_ARRAY (struct Function*, fn, &ctx->unit->functions.entries) { + // skip functions which have already been processed. + // synthetic lambdas get lowered eagerly when encountered to gain + // access to the local stack at their location. + if ((*fn)->completed) continue; if (!(*fn)->ast_body) continue; - // push a function-level scope on top of the persistent top-level + // push a function-level scope on top of the persistent top-level unit // scope, holding the parameter names. - // the function body will also push a new block scope, - // separating the parameters and the local declarations. - lower_push_scope(ctx, SCOPE_TYPE_FUNCTION); + lower_push_scope_function(ctx, *fn); FOR_EACH_ARRAY (struct Param, param, &(*fn)->params) { lower_declare_local(ctx, param->name, param->type); } (*fn)->body = lower_block(ctx, (*fn)->ast_body); + (*fn)->completed = true; lower_pop_scope(ctx, SCOPE_TYPE_FUNCTION); } diff --git a/boot/transpile.c b/boot/transpile.c index 9b8f420..ac480cb 100644 --- a/boot/transpile.c +++ b/boot/transpile.c @@ -331,12 +331,15 @@ transpile_function_emit_signature(struct Visit* visit, struct Function* function { TRANSPILE_PREAMBLE + bool has_captures = function->capture_state_type_id != 0; + VISIT(visit_type_ref, &function->return_type); TRANSPILE_WRITE(" %s(", transpile_function_c_name(function)); bool need_separator = false; if (function->synthetic) { - // synthetic functions implicitly take in state - TRANSPILE_WRITE("void* state"); + // synthetic functions implicitly take in state. + // TODO(mel): can we avoid state_void? + TRANSPILE_WRITE("void* %s", has_captures ? "state_void" : "state"); need_separator = true; } @@ -386,13 +389,18 @@ transpile_visit_function_body(struct Visit* visit, struct Function* function) return; } - // add a void cast for the implicit state argument, in case synthetic - // function does not need to use it if (function->synthetic) { TRANSPILE_WRITE(" {\n"); transpiler->indent_level++; transpile_emit_indent(transpiler); - TRANSPILE_WRITE("(void)state;\n"); + if (function->capture_state_type_id != 0) { + // TODO(mel): can we avoid this? + struct Type* state_type = + transpile_type_at(transpiler, function->capture_state_type_id); + TRANSPILE_WRITE("struct %s* state = state_void;\n", string_c_str(state_type->name)); + } else { + TRANSPILE_WRITE("(void)state;\n"); + } FOR_EACH_ARRAY (struct Statement*, statement, &function->body->statements) VISIT(visit_statement, *statement); transpiler->indent_level--; @@ -459,11 +467,65 @@ transpile_visit_expression_boolean_literal(struct Visit* visit, struct Expressio TRANSPILE_WRITE("%s", expression->value.bool_literal.value ? "true" : "false"); } +// emit a reference. +// the reference's inner kind. callers in subject-of-call position +// short-circuit FUNCTION references before reaching this handler. void -transpile_visit_expression_name(struct Visit* visit, struct Expression* expression) +transpile_visit_expression_reference(struct Visit* visit, struct Expression* expression) { TRANSPILE_PREAMBLE - TRANSPILE_WRITE("%s", string_c_str(expression->value.name.name)); + struct Expression_Reference* ref = &expression->value.reference; + switch (ref->kind) { + case REFERENCE_NAME: + TRANSPILE_WRITE("%s", string_c_str(ref->value.name.name)); + return; + + case REFERENCE_CAPTURE: + TRANSPILE_WRITE("(*state->%s)", string_c_str(ref->value.capture.name)); + return; + + case REFERENCE_FUNCTION: { + struct Function* fn = *array_at( + struct Function*, &transpiler->unit->functions.entries, + ref->value.function.function_id); + struct Type* closure_type = + transpile_type_at(transpiler, ref->value.function.closure_type_id); + + const ascii* call_name; + struct String thunk_name = string_empty(); + if (fn->synthetic) { + // synthetic lambdas already carry the fat signature + call_name = transpile_function_c_name(fn); + } else { + // named functions go through their thunk converting their + // thin calling convention, to the fat lambda convention. + thunk_name = transpile_function_thunk_name(fn); + call_name = string_c_str(thunk_name); + } + + TRANSPILE_WRITE("(struct %s){ .state = ", string_c_str(closure_type->name)); + if (fn->capture_state_type_id != 0) { + // closure with closed-over state, construct a state value. + struct Type* state_type = transpile_type_at(transpiler, fn->capture_state_type_id); + TRANSPILE_WRITE("&(struct %s){ ", string_c_str(state_type->name)); + bool first = true; + FOR_EACH_ARRAY (struct Capture, cap, &fn->captures) { + if (!first) TRANSPILE_WRITE(", "); + TRANSPILE_WRITE(".%s = &%s", string_c_str(cap->name), string_c_str(cap->name)); + first = false; + } + TRANSPILE_WRITE(" }"); + } else { + TRANSPILE_WRITE("nil"); + } + TRANSPILE_WRITE(", .call = %s }", call_name); + return; + } + + case REFERENCE_NONE: + TRANSPILE_WRITE("/* invalid reference */"); + return; + } } void @@ -533,11 +595,14 @@ transpile_visit_expression_call(struct Visit* visit, struct Expression* expressi struct Expression* subject = expression->value.call.subject; bool is_indirect = expression->value.call.is_indirect; - if (!is_indirect && subject && subject->kind == EXPRESSION_FUNCTION_REF) { + bool subject_is_function_ref = + subject && subject->kind == EXPRESSION_REFERENCE + && subject->value.reference.kind == REFERENCE_FUNCTION; + if (!is_indirect && subject_is_function_ref) { // direct calls emit the subject bare struct Function* fn = *array_at( struct Function*, &transpiler->unit->functions.entries, - subject->value.function_ref.function_id); + subject->value.reference.value.function.function_id); TRANSPILE_WRITE("%s", transpile_function_c_name(fn)); } else if (is_indirect) { // route through the fat closure value's call slot, passing state @@ -620,36 +685,6 @@ transpile_visit_expression_construct(struct Visit* visit, struct Expression* exp } void -transpile_visit_expression_function_ref(struct Visit* visit, struct Expression* expression) -{ - TRANSPILE_PREAMBLE - - struct Function* fn = *array_at( - struct Function*, &transpiler->unit->functions.entries, - expression->value.function_ref.function_id); - - // we only get here when the function reference is explicitly a *value*, - // any direct calls are filtered out earlier. - struct Type* closure_type = - transpile_type_at(transpiler, expression->value.function_ref.closure_type_id); - - const ascii* call_name; - struct String thunk_name = string_empty(); - if (fn->synthetic) { - // synthetic functions are already fat-by-default, can call directly - call_name = transpile_function_c_name(fn); - } else { - // thin named functions go through their generated thunk to match the - // fat calling convention - thunk_name = transpile_function_thunk_name(fn); - call_name = string_c_str(thunk_name); - } - - TRANSPILE_WRITE( - "(struct %s){ .state = nil, .call = %s }", string_c_str(closure_type->name), call_name); -} - -void transpile_visit_expression_increment_decrement(struct Visit* visit, struct Expression* expression) { TRANSPILE_PREAMBLE @@ -839,7 +874,7 @@ struct Visit_Table transpile_body_visit_functions = { .visit_expression_float_literal = transpile_visit_expression_float_literal, .visit_expression_string_literal = transpile_visit_expression_string_literal, .visit_expression_boolean_literal = transpile_visit_expression_boolean_literal, - .visit_expression_name = transpile_visit_expression_name, + .visit_expression_reference = transpile_visit_expression_reference, .visit_expression_unary_operation = transpile_visit_expression_unary_operation, .visit_expression_binary_operation = transpile_visit_expression_binary_operation, .visit_expression_sizeof_operation = transpile_visit_expression_sizeof_operation, @@ -849,7 +884,6 @@ struct Visit_Table transpile_body_visit_functions = { .visit_expression_cast = transpile_visit_expression_cast, .visit_expression_construct = transpile_visit_expression_construct, .visit_expression_increment_decrement = transpile_visit_expression_increment_decrement, - .visit_expression_function_ref = transpile_visit_expression_function_ref, }; // walk a lowered translation unit and emit c source into the transpiler's output. diff --git a/boot/visit/ir.c b/boot/visit/ir.c index fa17d9c..ca32a1e 100644 --- a/boot/visit/ir.c +++ b/boot/visit/ir.c @@ -53,7 +53,7 @@ struct Visit_Table void (*visit_expression_float_literal)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_string_literal)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_boolean_literal)(struct Visit* visitor, struct Expression* expr); - void (*visit_expression_name)(struct Visit* visitor, struct Expression* expr); + void (*visit_expression_reference)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_unary_operation)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_binary_operation)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_sizeof_operation)(struct Visit* visitor, struct Expression* expr); @@ -63,7 +63,6 @@ struct Visit_Table void (*visit_expression_cast)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_construct)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_increment_decrement)(struct Visit* visitor, struct Expression* expr); - void (*visit_expression_function_ref)(struct Visit* visitor, struct Expression* expr); void (*visit_block)(struct Visit* visitor, struct Block* block); void (*visit_type_ref)(struct Visit* visitor, struct Type_Ref* ref); @@ -260,8 +259,8 @@ walk_expression(struct Visit* visit, struct Expression* expr) case EXPRESSION_BOOLEAN_LITERAL: VISIT(visit_expression_boolean_literal, expr); break; - case EXPRESSION_NAME: - VISIT(visit_expression_name, expr); + case EXPRESSION_REFERENCE: + VISIT(visit_expression_reference, expr); break; case EXPRESSION_UNARY_OPERATION: VISIT(visit_expression_unary_operation, expr); @@ -290,9 +289,6 @@ walk_expression(struct Visit* visit, struct Expression* expr) case EXPRESSION_INCREMENT_DECREMENT: VISIT(visit_expression_increment_decrement, expr); break; - case EXPRESSION_FUNCTION_REF: - VISIT(visit_expression_function_ref, expr); - break; case EXPRESSION_NONE: break; default: @@ -304,7 +300,7 @@ WALK_LEAF_FUNCTION(walk_expression_integer_literal, struct Expression*); WALK_LEAF_FUNCTION(walk_expression_float_literal, struct Expression*); WALK_LEAF_FUNCTION(walk_expression_string_literal, struct Expression*); WALK_LEAF_FUNCTION(walk_expression_boolean_literal, struct Expression*); -WALK_LEAF_FUNCTION(walk_expression_name, struct Expression*); +WALK_LEAF_FUNCTION(walk_expression_reference, struct Expression*); void walk_expression_unary_operation(struct Visit* visit, struct Expression* expr) @@ -366,8 +362,6 @@ walk_expression_increment_decrement(struct Visit* visit, struct Expression* expr VISIT(visit_expression, expr->value.increment_decrement.subject); } -WALK_LEAF_FUNCTION(walk_expression_function_ref, struct Expression*); - void walk_block(struct Visit* visit, struct Block* block) { @@ -409,7 +403,7 @@ struct Visit_Table walk_functions = { .visit_expression_float_literal = walk_expression_float_literal, .visit_expression_string_literal = walk_expression_string_literal, .visit_expression_boolean_literal = walk_expression_boolean_literal, - .visit_expression_name = walk_expression_name, + .visit_expression_reference = walk_expression_reference, .visit_expression_unary_operation = walk_expression_unary_operation, .visit_expression_binary_operation = walk_expression_binary_operation, .visit_expression_sizeof_operation = walk_expression_sizeof_operation, @@ -419,7 +413,6 @@ struct Visit_Table walk_functions = { .visit_expression_cast = walk_expression_cast, .visit_expression_construct = walk_expression_construct, .visit_expression_increment_decrement = walk_expression_increment_decrement, - .visit_expression_function_ref = walk_expression_function_ref, .visit_block = walk_block, .visit_type_ref = walk_type_ref, @@ -836,10 +829,27 @@ printer_visit_expression_boolean_literal(struct Visit* visit, struct Expression* } void -printer_visit_expression_name(struct Visit* visit, struct Expression* expr) +printer_visit_expression_reference(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE - PRINT("(name %s)", string_c_str(expr->value.name.name)); + struct Expression_Reference* ref = &expr->value.reference; + switch (ref->kind) { + case REFERENCE_NAME: + PRINT("(name %s)", string_c_str(ref->value.name.name)); + break; + case REFERENCE_FUNCTION: { + struct Function* fn = *array_at( + struct Function*, &p->unit->functions.entries, ref->value.function.function_id); + PRINT("(fn-ref %s)", string_c_str(fn->name)); + break; + } + case REFERENCE_CAPTURE: + PRINT("(capture %s)", string_c_str(ref->value.capture.name)); + break; + case REFERENCE_NONE: + PRINT("(reference none)"); + break; + } } void @@ -950,16 +960,6 @@ printer_visit_expression_increment_decrement(struct Visit* visit, struct Express } void -printer_visit_expression_function_ref(struct Visit* visit, struct Expression* expr) -{ - PRINTER_PREAMBLE - - struct Function* fn = *array_at( - struct Function*, &p->unit->functions.entries, expr->value.function_ref.function_id); - PRINT("(fn-ref %s)", string_c_str(fn->name)); -} - -void printer_visit_block(struct Visit* visit, struct Block* block) { PRINTER_PREAMBLE @@ -1087,7 +1087,7 @@ struct Visit_Table printer_visit_functions = { .visit_expression_float_literal = printer_visit_expression_float_literal, .visit_expression_string_literal = printer_visit_expression_string_literal, .visit_expression_boolean_literal = printer_visit_expression_boolean_literal, - .visit_expression_name = printer_visit_expression_name, + .visit_expression_reference = printer_visit_expression_reference, .visit_expression_unary_operation = printer_visit_expression_unary_operation, .visit_expression_binary_operation = printer_visit_expression_binary_operation, .visit_expression_sizeof_operation = printer_visit_expression_sizeof_operation, @@ -1097,7 +1097,6 @@ struct Visit_Table printer_visit_functions = { .visit_expression_cast = printer_visit_expression_cast, .visit_expression_construct = printer_visit_expression_construct, .visit_expression_increment_decrement = printer_visit_expression_increment_decrement, - .visit_expression_function_ref = printer_visit_expression_function_ref, .visit_block = printer_visit_block, .visit_type_ref = printer_visit_type_ref, |
