aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/ir.c23
-rw-r--r--boot/lower.c256
-rw-r--r--boot/parse.c4
-rw-r--r--boot/transpile.c17
-rw-r--r--boot/visit/ir.c23
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(&param_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;
- // 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;
- }
+ // 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);
}
- 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);
+ 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(&param_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);
+ }
}
+
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;