From d9fd452b8baa82143a2300f3851a1b4884361ed8 Mon Sep 17 00:00:00 2001 From: cozis Date: Mon, 15 Aug 2022 19:33:46 +0200 Subject: [PATCH] implemented default arguments --- examples/func.noja | 9 ----- src/lib/common/executable.c | 1 + src/lib/common/executable.h | 1 + src/lib/compiler/ASTi.h | 1 + src/lib/compiler/codegen.c | 26 ++++++++++++ src/lib/compiler/parse.c | 77 ++++++++++++++++++++---------------- src/lib/runtime/runtime.c | 20 ++++++++-- tests/suite/Testcase_012_000 | 25 ++++++++++++ tests/suite/Testcase_012_001 | 27 +++++++++++++ tests/suite/Testcase_012_002 | 27 +++++++++++++ tests/suite/Testcase_012_003 | 38 ++++++++++++++++++ 11 files changed, 205 insertions(+), 47 deletions(-) delete mode 100644 examples/func.noja create mode 100644 tests/suite/Testcase_012_000 create mode 100644 tests/suite/Testcase_012_001 create mode 100644 tests/suite/Testcase_012_002 create mode 100644 tests/suite/Testcase_012_003 diff --git a/examples/func.noja b/examples/func.noja deleted file mode 100644 index 3f3c6b2..0000000 --- a/examples/func.noja +++ /dev/null @@ -1,9 +0,0 @@ - -fun add(a: int | float = 1, b = 8) - return a + b; - -c = add(3, 4); - -assert(c == 7); - -print(c); \ No newline at end of file diff --git a/src/lib/common/executable.c b/src/lib/common/executable.c index 6fcc148..517abed 100644 --- a/src/lib/common/executable.c +++ b/src/lib/common/executable.c @@ -102,6 +102,7 @@ static const InstrInfo instr_table[] = { [OPCODE_PUSHMAP] = {"PUSHMAP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_PUSHTYP] = {"PUSHTYP", 0, NULL}, [OPCODE_PUSHTYPTYP] = {"PUSHTYPTYP", 0, NULL}, + [OPCODE_PUSHNNETYP] = {"PUSHNNETYP", 0, NULL}, [OPCODE_RETURN] = {"RETURN", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_ERROR] = {"ERROR", 1, (OperandType[]) {OPTP_STRING}}, [OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, (OperandType[]) {OPTP_IDX}}, diff --git a/src/lib/common/executable.h b/src/lib/common/executable.h index b08d336..73a2148 100644 --- a/src/lib/common/executable.h +++ b/src/lib/common/executable.h @@ -87,6 +87,7 @@ typedef enum { OPCODE_PUSHMAP, OPCODE_PUSHTYP, OPCODE_PUSHTYPTYP, + OPCODE_PUSHNNETYP, OPCODE_RETURN, OPCODE_ERROR, OPCODE_JUMPIFANDPOP, diff --git a/src/lib/compiler/ASTi.h b/src/lib/compiler/ASTi.h index de5ee4b..42bfb38 100644 --- a/src/lib/compiler/ASTi.h +++ b/src/lib/compiler/ASTi.h @@ -194,5 +194,6 @@ typedef struct { char *name; int typec; Node *typev; + Node *value; } ArgumentNode; #endif \ No newline at end of file diff --git a/src/lib/compiler/codegen.c b/src/lib/compiler/codegen.c index 3e4ae90..db7b3ef 100644 --- a/src/lib/compiler/codegen.c +++ b/src/lib/compiler/codegen.c @@ -157,6 +157,11 @@ static void emitInstr_PUSHTYPTYP(CodegenContext *ctx, int off, int len) CodegenContext_EmitInstr(ctx, OPCODE_PUSHTYPTYP, NULL, 0, off, len); } +static void emitInstr_PUSHNNETYP(CodegenContext *ctx, int off, int len) +{ + CodegenContext_EmitInstr(ctx, OPCODE_PUSHNNETYP, NULL, 0, off, len); +} + static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break); static Opcode exprkind_to_opcode(ExprKind kind) @@ -208,6 +213,14 @@ static void emitInstrForFuncCallNode(CodegenContext *ctx, CallExprNode *expr, static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int argidx) { /* + * // If a default value for the argument was specified + * PUSHTYP; + * PUSHNNETYP; + * EQL; + * JUMPIFNOTANDPOP default_handled; + * POP 1; + * + * default_handled: * // Push the type of the argument * PUSHTYP; * @@ -255,6 +268,19 @@ static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int * POP 1; */ + if(arg->value != NULL) { + /* Emit bytecode for the default argument */ + Label *label_default_handled = Label_New(ctx); + emitInstr_PUSHTYP(ctx, arg->value->offset, arg->value->length); + emitInstr_PUSHNNETYP(ctx, arg->value->offset, arg->value->length); + emitInstr_EQL(ctx, arg->value->offset, arg->value->length); + emitInstr_JUMPIFNOTANDPOP(ctx, label_default_handled, arg->value->offset, arg->value->length); + emitInstr_POP1(ctx, arg->value->offset, arg->value->length); + emitInstrForNode(ctx, arg->value, NULL); + Label_SetHere(label_default_handled, ctx); + Label_Free(label_default_handled); + } + if(arg->typev != NULL) { /* Emit checks for the argument type */ diff --git a/src/lib/compiler/parse.c b/src/lib/compiler/parse.c index f13d13b..a3b3efb 100644 --- a/src/lib/compiler/parse.c +++ b/src/lib/compiler/parse.c @@ -127,7 +127,7 @@ typedef struct { } Context; static Node *parse_statement(Context *ctx); -static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples); +static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples, _Bool allow_assignments); static Node *parse_expression_statement(Context *ctx); static Node *parse_ifelse_statement(Context *ctx); static Node *parse_compound_statement(Context *ctx, TokenKind end); @@ -616,7 +616,7 @@ static Node *parse_statement(Context *ctx) next(ctx); // Consume the "return" keyword. - Node *val = parse_expression(ctx, 1); + Node *val = parse_expression(ctx, 1, 1); if(val == NULL) return NULL; @@ -664,7 +664,7 @@ static Node *parse_expression_statement(Context *ctx) { assert(ctx != NULL); - Node *expr = parse_expression(ctx, 1); + Node *expr = parse_expression(ctx, 1, 1); if(expr == NULL) return NULL; @@ -855,7 +855,7 @@ static Node *parse_list_primary_expression(Context *ctx) while(1) { // Parse. - Node *item = parse_expression(ctx, 0); + Node *item = parse_expression(ctx, 0, 1); if(item == NULL) return NULL; @@ -975,7 +975,7 @@ static Node *parse_map_primary_expression(Context *ctx) } else { - key = parse_expression(ctx, 0); + key = parse_expression(ctx, 0, 1); } if(key == NULL) @@ -1002,7 +1002,7 @@ static Node *parse_map_primary_expression(Context *ctx) next(ctx); // Parse. - Node *item = parse_expression(ctx, 0); + Node *item = parse_expression(ctx, 0, 1); if(item == NULL) return NULL; @@ -1144,7 +1144,7 @@ static Node *parse_primary_expresion(Context *ctx) { next(ctx); // Consume the '('. - Node *node = parse_expression(ctx, 1); + Node *node = parse_expression(ctx, 1, 1); if(node == NULL) return NULL; @@ -1463,7 +1463,7 @@ static Node *parse_postfix_expression(Context *ctx) while(1) { // Parse. - Node *arg = parse_expression(ctx, 0); + Node *arg = parse_expression(ctx, 0, 1); if(arg == NULL) return NULL; @@ -1648,13 +1648,13 @@ static inline int precedenceof(Token *tok) return -100000000; } -static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec, _Bool allow_toplev_tuples) +static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec, _Bool allow_toplev_tuples, _Bool allow_assignments) { while(isbinop(ctx->token) && precedenceof(ctx->token) >= min_prec) { Token *op = ctx->token; - if(op->kind == ',' && allow_toplev_tuples == 0) + if((op->kind == ',' && allow_toplev_tuples == 0) || (op->kind == '=' && allow_assignments == 0)) break; next(ctx); @@ -1666,7 +1666,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec, _Bo while(isbinop(ctx->token) && (precedenceof(ctx->token) > precedenceof(op) || (precedenceof(ctx->token) == precedenceof(op) && isrightassoc(ctx->token)))) { - right_expr = parse_expression_2(ctx, right_expr, precedenceof(op) + 1, allow_toplev_tuples); + right_expr = parse_expression_2(ctx, right_expr, precedenceof(op) + 1, allow_toplev_tuples, allow_assignments); if(right_expr == NULL) return NULL; @@ -1724,7 +1724,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec, _Bo return left_expr; } -static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples) +static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples, _Bool allow_assignments) { Node *left_expr = parse_prefix_expression(ctx); @@ -1734,7 +1734,7 @@ static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples) if(done(ctx)) return left_expr; - return parse_expression_2(ctx, left_expr, -1000000000, allow_toplev_tuples); + return parse_expression_2(ctx, left_expr, -1000000000, allow_toplev_tuples, allow_assignments); } static Node *parse_ifelse_statement(Context *ctx) @@ -1758,7 +1758,7 @@ static Node *parse_ifelse_statement(Context *ctx) next(ctx); // Consume the "if" keyword. - Node *condition = parse_expression(ctx, 1); + Node *condition = parse_expression(ctx, 1, 1); if(condition == NULL) return NULL; @@ -1865,7 +1865,7 @@ static Node *parse_compound_statement(Context *ctx, TokenKind end) return (Node*) node; } -static Node *parse_function_arguments(Context *ctx, int *argc_) +static _Bool parse_function_arguments(Context *ctx, int *argc_, Node **argv_) { #warning "What if the source ends here?" @@ -1875,7 +1875,7 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) Error_Report(ctx->error, 0, "Source ended where a function argument list was expected"); else Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a function argument list was expected", ctx->token->length, ctx->src + ctx->token->offset); - return NULL; + return 0; } Node *argv = NULL; @@ -1890,13 +1890,13 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) if(done(ctx)) { Error_Report(ctx->error, 0, "Source ended inside a function argument list"); - return NULL; + return 0; } if(current(ctx) != TIDENT) { Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a function argument name was expected", ctx->token->length, ctx->src + ctx->token->offset); - return NULL; + return 0; } char *arg_name = copy_token_text(ctx); @@ -1904,7 +1904,7 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) if(arg_name == NULL) { Error_Report(ctx->error, 1, "No memory"); - return NULL; + return 0; } int typec; @@ -1913,17 +1913,17 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) if(next(ctx) == ':') { next(ctx); // Skip the ':'. - Node *type = parse_expression(ctx, 0); + Node *type = parse_expression(ctx, 0, 0); if(type == NULL) - return NULL; + return 0; typev = type; typec = 1; Node *tail = type; while(current(ctx) == '|') { next(ctx); - type = parse_expression(ctx, 0); + type = parse_expression(ctx, 0, 0); if(type == NULL) - return NULL; + return 0; tail->next = type; tail = type; typec += 1; @@ -1931,6 +1931,15 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) } else typec = 0; + Node *defarg; // Default argument (or NULL if there isn't one) + if(current(ctx) == '=') { + next(ctx); // Skip the '='. + defarg = parse_expression(ctx, 0, 1); + if(defarg == NULL) + return 0; + } else + defarg = NULL; + ArgumentNode *arg; { // Make argument node. @@ -1939,7 +1948,7 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) if(arg == NULL) { Error_Report(ctx->error, 1, "No memory"); - return NULL; + return 0; } arg->base.kind = NODE_ARG; @@ -1949,6 +1958,7 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) arg->name = arg_name; arg->typev = typev; arg->typec = typec; + arg->value = defarg; } // Add it to the list. @@ -1961,7 +1971,7 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) if(done(ctx)) { Error_Report(ctx->error, 0, "Source ended inside a function argument list"); - return NULL; + return 0; } if(current(ctx) == ')') @@ -1970,7 +1980,7 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) if(current(ctx) != ',') { Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" inside function argument list, where either ',' or ')' were expected", ctx->token->length, ctx->src + ctx->token->offset); - return NULL; + return 0; } // Now prepare for the next identifier. @@ -1980,10 +1990,9 @@ static Node *parse_function_arguments(Context *ctx, int *argc_) next(ctx); // Consume the ')'. - if(argc_ != NULL) - *argc_ = argc; - - return argv; + if(argc_ != NULL) *argc_ = argc; + if(argv_ != NULL) *argv_ = argv; + return 1; } static Node *parse_function_definition(Context *ctx) @@ -2022,8 +2031,8 @@ static Node *parse_function_definition(Context *ctx) } int argc = 0; // Initialization for the warning. - Node *argv = parse_function_arguments(ctx, &argc); - if(argv == NULL) + Node *argv; + if(!parse_function_arguments(ctx, &argc, &argv)) return NULL; if(done(ctx)) @@ -2082,7 +2091,7 @@ static Node *parse_while_statement(Context *ctx) next(ctx); // Consume the "while" keyword. - Node *condition = parse_expression(ctx, 1); + Node *condition = parse_expression(ctx, 1, 1); if(condition == NULL) return NULL; @@ -2168,7 +2177,7 @@ static Node *parse_dowhile_statement(Context *ctx) next(ctx); // Consume the "while" keyword. - Node *condition = parse_expression(ctx, 1); + Node *condition = parse_expression(ctx, 1, 1); if(condition == NULL) return NULL; diff --git a/src/lib/runtime/runtime.c b/src/lib/runtime/runtime.c index dd8c4f9..39b9c20 100644 --- a/src/lib/runtime/runtime.c +++ b/src/lib/runtime/runtime.c @@ -1099,7 +1099,7 @@ static _Bool step(Runtime *runtime, Error *error) case OPCODE_PUSHFUN: { assert(opc == 2); - assert(ops[0].type == OPTP_INT); + assert(ops[0].type == OPTP_IDX); assert(ops[1].type == OPTP_INT); Object *closure = Object_NewClosure(runtime->frame->closure, runtime->frame->locals, Runtime_GetHeap(runtime), error); @@ -1159,6 +1159,18 @@ static _Bool step(Runtime *runtime, Error *error) return 1; } + case OPCODE_PUSHNNETYP: + { + assert(opc == 0); + + Object *obj = (Object*) Object_GetNoneType(); + assert(obj != NULL); + + if(!Runtime_Push(runtime, error, obj)) + return 0; + return 1; + } + case OPCODE_PUSHTYP: { assert(opc == 0); @@ -1201,14 +1213,14 @@ static _Bool step(Runtime *runtime, Error *error) case OPCODE_JUMP: assert(opc == 1); - assert(ops[0].type == OPTP_INT); + assert(ops[0].type == OPTP_IDX); runtime->frame->index = ops[0].as_int; return 1; case OPCODE_JUMPIFANDPOP: { assert(opc == 1); - assert(ops[0].type == OPTP_INT); + assert(ops[0].type == OPTP_IDX); long long int target = ops[0].as_int; @@ -1240,7 +1252,7 @@ static _Bool step(Runtime *runtime, Error *error) case OPCODE_JUMPIFNOTANDPOP: { assert(opc == 1); - assert(ops[0].type == OPTP_INT); + assert(ops[0].type == OPTP_IDX); long long int target = ops[0].as_int; diff --git a/tests/suite/Testcase_012_000 b/tests/suite/Testcase_012_000 new file mode 100644 index 0000000..6f69dc5 --- /dev/null +++ b/tests/suite/Testcase_012_000 @@ -0,0 +1,25 @@ + +#source + + fun nop(a = 1) + {} + +#bytecode + + PUSHFUN nop, 1; + ASS "nop"; + POP 1; + JUMP nop_end; +nop: + PUSHTYP; + PUSHNNETYP; + EQL; + JUMPIFNOTANDPOP not_none; + POP 1; + PUSHINT 1; +not_none: + ASS "a"; + POP 1; + RETURN 0; +nop_end: + RETURN 0; diff --git a/tests/suite/Testcase_012_001 b/tests/suite/Testcase_012_001 new file mode 100644 index 0000000..8f2ace8 --- /dev/null +++ b/tests/suite/Testcase_012_001 @@ -0,0 +1,27 @@ + +#source + + fun nop(a = 1, b) + {} + +#bytecode + + PUSHFUN nop, 2; + ASS "nop"; + POP 1; + JUMP nop_end; +nop: + ASS "b"; + POP 1; + PUSHTYP; + PUSHNNETYP; + EQL; + JUMPIFNOTANDPOP not_none; + POP 1; + PUSHINT 1; +not_none: + ASS "a"; + POP 1; + RETURN 0; +nop_end: + RETURN 0; \ No newline at end of file diff --git a/tests/suite/Testcase_012_002 b/tests/suite/Testcase_012_002 new file mode 100644 index 0000000..780ac81 --- /dev/null +++ b/tests/suite/Testcase_012_002 @@ -0,0 +1,27 @@ + +#source + + fun nop(a, b = true) + {} + +#bytecode + + PUSHFUN nop, 2; + ASS "nop"; + POP 1; + JUMP nop_end; +nop: + PUSHTYP; + PUSHNNETYP; + EQL; + JUMPIFNOTANDPOP not_none; + POP 1; + PUSHTRU; +not_none: + ASS "b"; + POP 1; + ASS "a"; + POP 1; + RETURN 0; +nop_end: + RETURN 0; \ No newline at end of file diff --git a/tests/suite/Testcase_012_003 b/tests/suite/Testcase_012_003 new file mode 100644 index 0000000..b9f9b1b --- /dev/null +++ b/tests/suite/Testcase_012_003 @@ -0,0 +1,38 @@ + +#source + + fun nop(a: int = 1) + {} + +#bytecode + + PUSHFUN nop, 1; + ASS "nop"; + POP 1; + JUMP nop_end; +nop: + PUSHTYP; + PUSHNNETYP; + EQL; + JUMPIFNOTANDPOP not_none; + POP 1; + PUSHINT 1; +not_none: + + PUSHTYP; + PUSHVAR "int"; + PUSHTYP; + PUSHTYPTYP; + EQL; + JUMPIFNOTANDPOP annot_not_ok; + EQL; + JUMPIFANDPOP type_ok; + ERROR "Bad type for argument 0"; +annot_not_ok: + ERROR "Argument 0 type annotation 0 is not a type"; +type_ok: + ASS "a"; + POP 1; + RETURN 0; +nop_end: + RETURN 0;