diff options
| -rw-r--r-- | boot/ir.c | 23 | ||||
| -rw-r--r-- | boot/lower.c | 256 | ||||
| -rw-r--r-- | boot/parse.c | 4 | ||||
| -rw-r--r-- | boot/transpile.c | 17 | ||||
| -rw-r--r-- | boot/visit/ir.c | 23 |
5 files changed, 276 insertions, 47 deletions
diff --git a/boot/ir.c b/boot/ir.c index 6ea055e..d8d8aa2 100644 --- a/boot/ir.c +++ b/boot/ir.c @@ -294,12 +294,21 @@ struct Expression_Sizeof_Operator struct Type_Ref target; }; +// one argument of a call, tagged with the parameter slot it fills. +struct Call_Argument +{ + struct Expression* value; + // slot decides the final emission order. + // slots that are higher than the parameter count of a function + // are always counted as variadic extras, and an error for non-variadics. + uint slot; +}; + struct Expression_Call { struct Expression* subject; - // call arguments are positional, named and out-of-order - // arguments get re-ordered until they are correct. - Array(struct Expression*) arguments; + // arguments of call in written order, the inner slot decides final ordering. + Array(struct Call_Argument) arguments; }; struct Expression_Member @@ -459,6 +468,10 @@ enum Lower_Error_Kind LOWER_ERROR_UNSUPPORTED_TOP_LEVEL, LOWER_ERROR_UNKNOWN_LOOP_STYLE, LOWER_ERROR_UNKNOWN_COMPOUND_ASSIGN, + LOWER_ERROR_UNKNOWN_NAMED_ARGUMENT, + LOWER_ERROR_DUPLICATE_ARGUMENT, + LOWER_ERROR_TOO_MANY_ARGUMENTS, + LOWER_ERROR_NAMED_ARGUMENT_ON_UNKNOWN_CALLEE, LOWER_ERROR_UNIMPLEMENTED, }; @@ -466,7 +479,7 @@ struct Lower_Error { enum Lower_Error_Kind kind; struct Span span; - + // per-error details, meaning depends on each error kind! struct String name; struct String detail; @@ -625,7 +638,7 @@ ir_make_sizeof(struct Type_Ref target, struct Span span) } struct Expression* -ir_make_call(struct Expression* subject, Array(struct Expression*) arguments, struct Span span) +ir_make_call(struct Expression* subject, Array(struct Call_Argument) arguments, struct Span span) { union Expression_Value v = { 0 }; v.call.subject = subject; diff --git a/boot/lower.c b/boot/lower.c index c72ad97..f36a124 100644 --- a/boot/lower.c +++ b/boot/lower.c @@ -161,6 +161,19 @@ lower_error_to_diagnostic(struct Lower_Error* err, struct Unit* unit, struct Sou case LOWER_ERROR_UNKNOWN_COMPOUND_ASSIGN: d.message = string_from_static_c_string("unknown compound assignment operator"); return d; + case LOWER_ERROR_UNKNOWN_NAMED_ARGUMENT: + d.message = string_format("no parameter named '%S'", STR(err->name)); + return d; + case LOWER_ERROR_DUPLICATE_ARGUMENT: + d.message = string_format("argument for '%S' supplied more than once", STR(err->name)); + return d; + case LOWER_ERROR_TOO_MANY_ARGUMENTS: + d.message = string_from_static_c_string("too many arguments"); + return d; + case LOWER_ERROR_NAMED_ARGUMENT_ON_UNKNOWN_CALLEE: + d.message = + string_from_static_c_string("named arguments only work on functions we can see"); + return d; case LOWER_ERROR_UNIMPLEMENTED: d.message = string_format("unimplemented: %S", STR(err->detail)); return d; @@ -903,32 +916,216 @@ lower_expression_binary_operation(struct Lower_Context* ctx, struct Tree_Express lower_expression(ctx, tree_expr->value.binary_operator.right_operand), tree_expr->span); } +// find the index of `name` in `names`, or `array_length(names)` if absent. +uint +lower_find_slot_by_name(Array(struct String) names, struct String name) +{ + uint count = array_length(&names); + for (uint j = 0; j < count; ++j) { + if (string_equals(*array_at(struct String, &names, j), name)) return j; + } + return count; +} + +// find the index of the leftmost unclaimed slot in `claimed`, +// or its length if no slot is free. +uint +lower_find_leftmost_unclaimed(Array(bool) claimed) +{ + uint count = array_length(&claimed); + for (uint j = 0; j < count; ++j) { + if (!*array_at(bool, &claimed, j)) return j; + } + return count; +} + +// resolve a call's argument list. named args claim their named slot, +// positional args fill the leftmost unclaimed slot. extras (positionals +// past the last slot) land in slots `>= param_count` only when the +// callee is variadic. +bool +lower_resolve_call_arguments( + struct Lower_Context* ctx, struct Span call_span, struct Tree_Argument_Group* group, + Array(struct String) param_names, bool variadic, Array(struct Call_Argument) * out_arguments) +{ + uint param_count = array_length(¶m_names); + *out_arguments = array_new(struct Call_Argument, 16); + + Array(bool) claimed = array_new(bool, param_count > 0 ? param_count : 1); + for (uint i = 0; i < param_count; ++i) { + bool f = false; + array_push(&claimed, &f); + } + uint extra_slot = param_count; + bool ok = true; + + uint i = 0; + FOR_EACH (struct Tree_Expression*, arg, group->arguments) { + struct String name = string_empty(); + if (i < array_length(&group->argument_names)) + name = *array_at(struct String, &group->argument_names, i); + ++i; + + uint slot; + if (name.length > 0) { + uint match = lower_find_slot_by_name(param_names, name); + if (match == param_count) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNKNOWN_NAMED_ARGUMENT, + .span = call_span, + .name = name }); + ok = false; + continue; + } + if (*array_at(bool, &claimed, match)) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_DUPLICATE_ARGUMENT, .span = call_span, .name = name }); + ok = false; + continue; + } + *array_at(bool, &claimed, match) = true; + slot = match; + } else { + uint match = lower_find_leftmost_unclaimed(claimed); + if (match == param_count) { + if (!variadic) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_TOO_MANY_ARGUMENTS, .span = call_span }); + ok = false; + continue; + } + slot = extra_slot++; + } else { + *array_at(bool, &claimed, match) = true; + slot = match; + } + } + + struct Call_Argument tagged = { .value = lower_expression(ctx, arg), .slot = slot }; + array_push(out_arguments, &tagged); + } + + return ok; +} + +// resolve a struct construction's field list. same rule as calls, but the +// slot here is the target field's name since the transpiler emits via c +// designated initializers and call-site order is fine. +bool +lower_resolve_construct_fields( + struct Lower_Context* ctx, struct Span call_span, struct Tree_Argument_Group* group, + Array(struct String) field_names, Array(struct Construct_Field) * out_fields) +{ + uint field_count = array_length(&field_names); + *out_fields = array_new(struct Construct_Field, 16); + + Array(bool) claimed = array_new(bool, field_count > 0 ? field_count : 1); + for (uint i = 0; i < field_count; ++i) { + bool f = false; + array_push(&claimed, &f); + } + bool ok = true; + + uint i = 0; + FOR_EACH (struct Tree_Expression*, arg, group->arguments) { + struct String name = string_empty(); + if (i < array_length(&group->argument_names)) + name = *array_at(struct String, &group->argument_names, i); + ++i; + + struct String resolved; + if (name.length > 0) { + uint match = lower_find_slot_by_name(field_names, name); + if (match == field_count) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNKNOWN_NAMED_ARGUMENT, + .span = call_span, + .name = name }); + ok = false; + continue; + } + if (*array_at(bool, &claimed, match)) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_DUPLICATE_ARGUMENT, .span = call_span, .name = name }); + ok = false; + continue; + } + *array_at(bool, &claimed, match) = true; + resolved = name; + } else { + uint match = lower_find_leftmost_unclaimed(claimed); + if (match == field_count) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_TOO_MANY_ARGUMENTS, .span = call_span }); + ok = false; + continue; + } + *array_at(bool, &claimed, match) = true; + resolved = *array_at(struct String, &field_names, match); + } + + struct Construct_Field field = { .name = resolved, .value = lower_expression(ctx, arg) }; + array_push(out_fields, &field); + } + + return ok; +} + struct Expression* lower_expression_call(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) { struct Tree_Argument_Group* group = &tree_expr->value.call.argument_group; + struct Tree_Expression* subject_tree = tree_expr->value.call.subject; + + // only catskill-side functions are in the function table, calls to libc + // and the like go through as positional with no named args allowed. + struct Function* callee = nil; + if (subject_tree && subject_tree->kind == TREE_EXPRESSION_NAME) { + Function_Id fn_id; + if (lower_function_lookup_by_name(ctx->unit, subject_tree->value.name.name, &fn_id)) + callee = *array_at(struct Function*, &ctx->unit->functions.entries, fn_id); + } - // TODO: named arguments need lookup against the callee's parameter - // table to reorder, we only handle positional arguments today. - FOR_EACH_ARRAY (struct String, name, &group->argument_names) { - if (name->length > 0) { - lower_push_error( - ctx->unit, - (struct Lower_Error){ - .kind = LOWER_ERROR_UNIMPLEMENTED, - .span = tree_expr->span, - .detail = string_from_static_c_string("named call arguments"), - }); - return nil; + struct Expression* subject = lower_expression(ctx, subject_tree); + Array(struct Call_Argument) arguments; + + if (callee) { + Array(struct String) param_names = array_new(struct String, array_length(&callee->params)); + FOR_EACH_ARRAY (struct Param, p, &callee->params) array_push(¶m_names, &p->name); + + lower_resolve_call_arguments( + ctx, tree_expr->span, group, param_names, callee->variadic, &arguments); + } else { + FOR_EACH_ARRAY (struct String, name, &group->argument_names) { + if (name->length > 0) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_NAMED_ARGUMENT_ON_UNKNOWN_CALLEE, + .span = tree_expr->span }); + return nil; + } + } + arguments = array_new(struct Call_Argument, 16); + uint slot = 0; + FOR_EACH (struct Tree_Expression*, arg, group->arguments) { + struct Call_Argument tagged = { .value = lower_expression(ctx, arg), .slot = slot++ }; + array_push(&arguments, &tagged); } } - struct Expression* subject = lower_expression(ctx, tree_expr->value.call.subject); - Array(struct Expression*) arguments = array_new(struct Expression*, 16); - FOR_EACH (struct Tree_Expression*, arg, group->arguments) { - struct Expression* lowered = lower_expression(ctx, arg); - array_push(&arguments, &lowered); - } return ir_make_call(subject, arguments, tree_expr->span); } @@ -957,20 +1154,17 @@ lower_expression_construct(struct Lower_Context* ctx, struct Tree_Expression* tr return nil; } - Array(struct Construct_Field) fields = array_new(struct Construct_Field, 16); - struct Tree_Argument_Group* group = &tree_expr->value.construct.argument_group; - uint i = 0; - FOR_EACH (struct Tree_Expression*, arg, group->arguments) { - struct String name = string_empty(); - if (i < array_length(&group->argument_names)) - name = *array_at(struct String, &group->argument_names, i); - struct Construct_Field field = { - .name = name, - .value = lower_expression(ctx, arg), - }; - array_push(&fields, &field); - ++i; + struct Type* type = *array_at(struct Type*, &ctx->unit->types.entries, type_id); + Array(struct String) field_names = array_new(struct String, 16); + if (type->kind == TYPE_STRUCTURE) { + FOR_EACH_ARRAY (struct Field, f, &type->value.structure.fields) + array_push(&field_names, &f->name); } + + Array(struct Construct_Field) fields; + lower_resolve_construct_fields( + ctx, tree_expr->span, &tree_expr->value.construct.argument_group, field_names, &fields); + return ir_make_construct(type_id, fields, tree_expr->span); } diff --git a/boot/parse.c b/boot/parse.c index 2b5d859..1cf08fd 100644 --- a/boot/parse.c +++ b/boot/parse.c @@ -407,6 +407,7 @@ parser_function_header_node(struct Parser* p, struct Parser_Error* error) CHECK_RETURN(parser_need(p, TOKEN_ROUND_OPEN, error), struct Tree_Function_Header); struct Tree_Function_Header header = { 0 }; + struct Tree_Type* tail = nil; while (!parser_probe(p, TOKEN_ROUND_CLOSE)) { // TODO: correctly output parameter spans bool variadic = false; @@ -432,7 +433,8 @@ parser_function_header_node(struct Parser* p, struct Parser_Error* error) if (!header.parameters_type_and_name) header.parameters_type_and_name = type; else - header.parameters_type_and_name->next = type; + tail->next = type; + tail = type; if (parser_probe(p, TOKEN_COMMA)) parser_next(p); } diff --git a/boot/transpile.c b/boot/transpile.c index 9d910b3..d5a718a 100644 --- a/boot/transpile.c +++ b/boot/transpile.c @@ -437,12 +437,19 @@ transpile_visit_expression_call(struct Visit* visit, struct Expression* expressi transpile_emit_expression(visit, expression->value.call.subject, TRANSPILE_PRECEDENCE_POSTFIX); TRANSPILE_WRITE("("); + Array(struct Call_Argument) args = expression->value.call.arguments; + uint max_slot = 0; + FOR_EACH_ARRAY (struct Call_Argument, a, &args) { + if (a->slot > max_slot) max_slot = a->slot; + } bool first = true; - FOR_EACH_ARRAY (struct Expression*, argument, &expression->value.call.arguments) { - if (!first) TRANSPILE_WRITE(", "); - // call arguments are comma-separated, each fresh from precedence 0. - transpile_emit_expression(visit, *argument, 0); - first = false; + for (uint s = 0; s <= max_slot; ++s) { + FOR_EACH_ARRAY (struct Call_Argument, a, &args) { + if (a->slot != s) continue; + if (!first) TRANSPILE_WRITE(", "); + transpile_emit_expression(visit, a->value, 0); + first = false; + } } TRANSPILE_WRITE(")"); } diff --git a/boot/visit/ir.c b/boot/visit/ir.c index 5317276..6748384 100644 --- a/boot/visit/ir.c +++ b/boot/visit/ir.c @@ -321,8 +321,8 @@ void walk_expression_call(struct Visit* visit, struct Expression* expr) { VISIT(visit_expression, expr->value.call.subject); - FOR_EACH_ARRAY (struct Expression*, argument, &expr->value.call.arguments) - VISIT(visit_expression, *argument); + FOR_EACH_ARRAY (struct Call_Argument, argument, &expr->value.call.arguments) + VISIT(visit_expression, argument->value); } void @@ -859,9 +859,10 @@ printer_visit_expression_call(struct Visit* visit, struct Expression* expr) PRINTER_PREAMBLE PRINT("(call "); VISIT(visit_expression, expr->value.call.subject); - FOR_EACH_ARRAY (struct Expression*, argument, &expr->value.call.arguments) { - PRINT(" "); - VISIT(visit_expression, *argument); + FOR_EACH_ARRAY (struct Call_Argument, argument, &expr->value.call.arguments) { + PRINT(" (slot %lu ", argument->slot); + VISIT(visit_expression, argument->value); + PRINT(")"); } PRINT(")"); } @@ -996,6 +997,18 @@ printer_visit_lower_error(struct Visit* visit, struct Lower_Error* error) case LOWER_ERROR_UNKNOWN_COMPOUND_ASSIGN: PRINT("(unknown-compound-assign)"); return; + case LOWER_ERROR_UNKNOWN_NAMED_ARGUMENT: + PRINT("(unknown-named-argument %s)", string_c_str(error->name)); + return; + case LOWER_ERROR_DUPLICATE_ARGUMENT: + PRINT("(duplicate-argument %s)", string_c_str(error->name)); + return; + case LOWER_ERROR_TOO_MANY_ARGUMENTS: + PRINT("(too-many-arguments)"); + return; + case LOWER_ERROR_NAMED_ARGUMENT_ON_UNKNOWN_CALLEE: + PRINT("(named-argument-on-unknown-callee)"); + return; case LOWER_ERROR_UNIMPLEMENTED: PRINT("(unimplemented \"%s\")", string_c_str(error->detail)); return; |
