From 920b88c0063f87539ee9907bb90be3d519ada71c Mon Sep 17 00:00:00 2001 From: Francesco Cozzuto Date: Fri, 13 Jan 2023 03:02:28 +0100 Subject: [PATCH] map can have function members by placing function declaration between the curly braces --- examples/http.noja | 22 +++-- examples/json.noja | 4 +- examples/scan.noja | 68 +++++++------ examples/test.noja | 15 +++ src/lib/builtins/start.noja | 3 +- src/lib/compiler/ASTi.h | 19 ++-- src/lib/compiler/codegen.c | 20 ++-- src/lib/compiler/parse.c | 188 ++++++++++++++++++++++-------------- 8 files changed, 208 insertions(+), 131 deletions(-) create mode 100644 examples/test.noja diff --git a/examples/http.noja b/examples/http.noja index 339ea2d..2c20168 100644 --- a/examples/http.noja +++ b/examples/http.noja @@ -352,13 +352,17 @@ fun parse(src: String) { return newRequest(method, url, version, headers); } -sample = cat( - "GET /search?client=firefox-b-d&q=http+request+dataset HTTP/2\r\n", - "Host: www.google.com\r\n", - "user-agent: curl/7.84.0\r\n", - "accept: */*\r\n", - "\r\n"); +fun test() { + sample = cat( + "GET /search?client=firefox-b-d&q=http+request+dataset HTTP/2\r\n", + "Host: www.google.com\r\n", + "user-agent: curl/7.84.0\r\n", + "accept: */*\r\n", + "\r\n"); -request, error = parse(sample); -print("request=", request, "\n"); -print("error=", error, "\n"); + request, error = parse(sample); + print("request=", request, "\n"); + print("error=", error, "\n"); +} + +return {parse: parse, test: test}; diff --git a/examples/json.noja b/examples/json.noja index 23f8277..e52288f 100644 --- a/examples/json.noja +++ b/examples/json.noja @@ -328,4 +328,6 @@ fun test() { print("passed: ", passed, "\n"); print("failed: ", failed, "\n"); print(" total: ", total, "\n"); -} \ No newline at end of file +} + +return {parse: parse, test: test}; \ No newline at end of file diff --git a/examples/scan.noja b/examples/scan.noja index 424b581..ff963a3 100644 --- a/examples/scan.noja +++ b/examples/scan.noja @@ -8,38 +8,40 @@ Scanner = { consumeSpaces: Callable }; -fun hint(scan: Scanner, n: int = 1) { - k = scan.i + n; - if k < count(scan.src): - return scan.src[k]; - return none; -} - -fun current(scan: Scanner) - return hint(scan, 0); - -fun consume(scan: Scanner, n: int = 1) { - assert(n > 0); - limit = count(scan.src); - scan.i = min(scan.i + n, limit); - return current(scan); -} - -fun consumeSpaces(scan: Scanner) { - char = current(scan); - while isSpace(char): - char = consume(scan); - return char; -} - fun newScanner(src: String) { + + fun isSpace(c: String) + return c == ' ' + or c == '\t' + or c == '\n'; + scan = { src: src, i: 0, - hint: hint, - consume: consume, - current: current, - consumeSpaces: consumeSpaces + + fun hint(scan: Scanner, n: int = 1) { + k = scan.i + n; + if k < count(scan.src): + return scan.src[k]; + return none; + } + + fun current(scan: Scanner) + return scan->hint(0); + + fun consume(scan: Scanner, n: int = 1) { + assert(n > 0); + limit = count(scan.src); + scan.i = min(scan.i + n, limit); + return scan->current(); + } + + fun consumeSpaces(scan: Scanner) { + char = scan->current(); + while isSpace(char): + char = scan->consume(); + return char; + } }; assert(istypeof(Scanner, scan)); return scan; @@ -48,11 +50,7 @@ fun newScanner(src: String) { fun isDigit(char: ?String) { if char == none: return false; - return string.ord(char) >= string.ord('0') - and string.ord(char) <= string.ord('9'); + ord = string.ord; + return ord(char) >= ord('0') + and ord(char) <= ord('9'); } - -fun isSpace(c: String) - return c == ' ' - or c == '\t' - or c == '\n'; diff --git a/examples/test.noja b/examples/test.noja new file mode 100644 index 0000000..1bfeda1 --- /dev/null +++ b/examples/test.noja @@ -0,0 +1,15 @@ + +Person = {name: String, age: int, sayHello: Callable}; + +fun newPerson(name: String, age: int) + return { + name: name, + age: age, + fun sayHello(self) + print("Hello from ", self.name, "!\n"); + }; + +me = newPerson("Francesco", 24); +me->sayHello(); + +assert(istypeof(Person, me)); diff --git a/src/lib/builtins/start.noja b/src/lib/builtins/start.noja index f700859..895fe01 100644 --- a/src/lib/builtins/start.noja +++ b/src/lib/builtins/start.noja @@ -56,7 +56,7 @@ fun stringFromInteger(n: int) { temp = abs(n); # Temporary copy of the input text = ""; # The output we're about to compute - + while power >= 1: { # Pop the leftmost digit from [temp]. digit = temp / power; # Get the leftmost digit @@ -78,6 +78,7 @@ fun floatFromInteger(n: int) return 1.0 * n; fun integerFromDigit(char: String) { + ord = string.ord; res = ord(char) - ord('0'); if res < 0 or res > 9: error("String isn't a digit"); diff --git a/src/lib/compiler/ASTi.h b/src/lib/compiler/ASTi.h index 7290b44..c5efcfc 100644 --- a/src/lib/compiler/ASTi.h +++ b/src/lib/compiler/ASTi.h @@ -70,6 +70,7 @@ typedef enum { EXPR_INT, EXPR_MAP, EXPR_CALL, + EXPR_FUNC, EXPR_PAIR, EXPR_LIST, EXPR_NONE, @@ -157,6 +158,14 @@ typedef struct { Node *set; } IndexSelectionExprNode; +#warning "temp" +typedef struct { + ExprNode base; + Node *argv; + int argc; + Node *body; +} FuncExprNode; + typedef struct { Node base; Node *condition; @@ -187,12 +196,10 @@ typedef struct { } ReturnNode; typedef struct { - Node base; - char *name; - Node *argv; - int argc; - Node *body; -} FunctionNode; + Node base; + StringExprNode *name; + FuncExprNode *expr; +} FuncDeclNode; typedef struct { Node base; diff --git a/src/lib/compiler/codegen.c b/src/lib/compiler/codegen.c index 04504af..096e618 100644 --- a/src/lib/compiler/codegen.c +++ b/src/lib/compiler/codegen.c @@ -255,7 +255,7 @@ static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int emitInstr_POP1(ctx, arg->base.offset, arg->base.length); } -static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func) +static void emitInstrForFuncExprNode(CodegenContext *ctx, FuncExprNode *func) { Label *label_func = Label_New(ctx); Label *label_jump = Label_New(ctx); @@ -266,11 +266,9 @@ static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func) { .type = OPTP_PROMISE, .as_promise = Label_ToPromise(label_func) }, { .type = OPTP_INT, .as_int = func->argc }, }; - CodegenContext_EmitInstr(ctx, OPCODE_PUSHFUN, ops, 2, func->base.offset, func->base.length); + CodegenContext_EmitInstr(ctx, OPCODE_PUSHFUN, ops, 2, func->base.base.offset, func->base.base.length); } - emitInstr_ASS(ctx, func->name, func->base.offset, func->base.length); // Assign variable - emitInstr_POP1(ctx, func->base.offset, func->base.length); // Pop function object - emitInstr_JUMP(ctx, label_jump, func->base.offset, func->base.length); // Jump after the function code + emitInstr_JUMP(ctx, label_jump, func->base.base.offset, func->base.base.length); // Jump after the function code Label_SetHere(label_func, ctx); // This is the function code index. // Compile the function body. @@ -302,6 +300,12 @@ static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func) Label_Free(label_jump); } +static void emitInstrForFuncDeclNode(CodegenContext *ctx, FuncDeclNode *func) +{ + emitInstrForFuncExprNode(ctx, func->expr); + emitInstr_ASS(ctx, func->name->val, func->base.offset, func->base.length); // Assign variable + emitInstr_POP1(ctx, func->base.offset, func->base.length); // Pop function object +} static void flattenTupleTree(CodegenContext *ctx, ExprNode *root, ExprNode *tuple[], int max, int *count) { @@ -569,6 +573,10 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, emitInstrForFuncCallNode(ctx, (CallExprNode*) expr, label_break, 1); return; + case EXPR_FUNC: + emitInstrForFuncExprNode(ctx, (FuncExprNode*) expr); + return; + case EXPR_SELECT: { IndexSelectionExprNode *sel = (IndexSelectionExprNode*) expr; @@ -723,7 +731,7 @@ static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break } case NODE_FUNC: - emitInstrForFuncNode(ctx, (FunctionNode*) node); + emitInstrForFuncDeclNode(ctx, (FuncDeclNode*) node); return; default: diff --git a/src/lib/compiler/parse.c b/src/lib/compiler/parse.c index da3a7ac..7295df0 100644 --- a/src/lib/compiler/parse.c +++ b/src/lib/compiler/parse.c @@ -136,7 +136,7 @@ static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples, _Bool all static Node *parse_expression_statement(Context *ctx); static Node *parse_ifelse_statement(Context *ctx); static Node *parse_compound_statement(Context *ctx, TokenKind end); -static Node *parse_function_definition(Context *ctx); +static FuncDeclNode *parse_function_definition(Context *ctx); static Node *parse_postfix_expression(Context *ctx); static Node *parse_prefix_expression(Context *ctx); static Node *parse_while_statement(Context *ctx); @@ -656,7 +656,7 @@ static Node *parse_statement(Context *ctx) } case TKWFUN: - return parse_function_definition(ctx); + return (Node*) parse_function_definition(ctx); case TKWWHILE: return parse_while_statement(ctx); @@ -965,58 +965,69 @@ static Node *parse_map_primary_expression(Context *ctx) while(1) { Node *key; - _Bool key_is_a_single_ident; + Node *item; + + bool item_is_func_decl = false; + + if (current(ctx) == TKWFUN) { - _Bool key_starts_with_an_ident = current(ctx) == TIDENT; - - next(ctx); - - key_is_a_single_ident = key_starts_with_an_ident && (current(ctx) == TDONE || current(ctx) == ':'); - - prev(ctx); - } - - if(key_is_a_single_ident) - { - assert(current(ctx) == TIDENT); - key = makeStringExprNode(ctx, ctx->src + ctx->token->offset, ctx->token->length); - - next(ctx); + FuncDeclNode *func = parse_function_definition(ctx); + if (func == NULL) return NULL; + key = (Node*) func->name; + item = (Node*) func->expr; + item_is_func_decl = true; } else { - key = parse_expression(ctx, 0, 1); + _Bool key_is_a_single_ident; + { + _Bool key_starts_with_an_ident = current(ctx) == TIDENT; + + next(ctx); + + key_is_a_single_ident = key_starts_with_an_ident && (current(ctx) == TDONE || current(ctx) == ':'); + + prev(ctx); + } + + if(key_is_a_single_ident) + { + assert(current(ctx) == TIDENT); + key = makeStringExprNode(ctx, ctx->src + ctx->token->offset, ctx->token->length); + + next(ctx); + } + else + key = parse_expression(ctx, 0, 1); + + if(key == NULL) + return NULL; + + if(done(ctx)) + { + Error_Report(ctx->error, 0, + "Source ended where a map key-value " + "separator ':' was expected"); + return NULL; + } + + if(current(ctx) != ':') + { + Error_Report(ctx->error, 0, + "Got token \"%.*s\" where a map key-value " + "separator ':' was expected", + ctx->token->length, + ctx->src + ctx->token->offset); + return NULL; + } + + next(ctx); + + // Parse. + item = parse_expression(ctx, 0, 1); + if(item == NULL) return NULL; } - if(key == NULL) - return NULL; - - if(done(ctx)) - { - Error_Report(ctx->error, 0, - "Source ended where a map key-value " - "separator ':' was expected"); - return NULL; - } - - if(current(ctx) != ':') - { - Error_Report(ctx->error, 0, - "Got token \"%.*s\" where a map key-value " - "separator ':' was expected", - ctx->token->length, - ctx->src + ctx->token->offset); - return NULL; - } - - next(ctx); - - // Parse. - Node *item = parse_expression(ctx, 0, 1); - - if(item == NULL) - return NULL; - // Append. if(tail) { @@ -1038,19 +1049,20 @@ static Node *parse_map_primary_expression(Context *ctx) if(current(ctx) == '}') break; - if(current(ctx) != ',') - { - if(current(ctx) == TDONE) - Error_Report(ctx->error, 0, "Source ended inside a map literal"); - else - Error_Report(ctx->error, 0, - "Got unexpected token \"%.*s\" inside " - "map literal, where ',' or '}' were expected", - ctx->token->length, ctx->src + ctx->token->offset); - return NULL; + if (!item_is_func_decl) { + if(current(ctx) != ',') + { + if(current(ctx) == TDONE) + Error_Report(ctx->error, 0, "Source ended inside a map literal"); + else + Error_Report(ctx->error, 0, + "Got unexpected token \"%.*s\" inside " + "map literal, where ',' or '}' were expected", + ctx->token->length, ctx->src + ctx->token->offset); + return NULL; + } + next(ctx); // Skip the ','. } - - next(ctx); // Skip the ','. } } @@ -2001,7 +2013,7 @@ static _Bool parse_function_arguments(Context *ctx, int *argc_, Node **argv_) return 1; } -static Node *parse_function_definition(Context *ctx) +static FuncDeclNode *parse_function_definition(Context *ctx) { assert(ctx != NULL); @@ -2028,13 +2040,15 @@ static Node *parse_function_definition(Context *ctx) return NULL; } - char *name = copy_token_text(ctx); - - if(name == NULL) + char *name_val = copy_token_text(ctx); + if(name_val == NULL) { Error_Report(ctx->error, 1, "No memory"); return NULL; } + int name_len = strlen(name_val); + int name_offset = current_token(ctx)->offset; + int name_length = current_token(ctx)->length; int argc = 0; // Initialization for the warning. Node *argv; @@ -2052,28 +2066,56 @@ static Node *parse_function_definition(Context *ctx) if(body == NULL) return NULL; - FunctionNode *func; + FuncDeclNode *func; { - // Make argument node. - func = BPAlloc_Malloc(ctx->alloc, sizeof(FunctionNode)); + int length = body->offset + + body->length + - offset; + FuncExprNode *expr = BPAlloc_Malloc(ctx->alloc, sizeof(FuncExprNode)); + if (expr == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + expr->base.base.kind = NODE_EXPR; + expr->base.base.next = NULL; + expr->base.base.offset = offset; + expr->base.base.length = length; + expr->base.kind = EXPR_FUNC; + expr->argv = argv; + expr->argc = argc; + expr->body = body; + + StringExprNode *name = BPAlloc_Malloc(ctx->alloc, sizeof(StringExprNode)); + if (name == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + name->base.base.kind = NODE_EXPR; + name->base.base.next = NULL; + name->base.base.offset = name_offset; + name->base.base.length = name_length; + name->base.kind = EXPR_STRING; + name->val = name_val; + name->len = name_len; + + func = BPAlloc_Malloc(ctx->alloc, sizeof(FuncDeclNode)); if(func == NULL) { Error_Report(ctx->error, 1, "No memory"); return NULL; } - func->base.kind = NODE_FUNC; func->base.next = NULL; func->base.offset = offset; - func->base.length = body->offset + body->length - offset; + func->base.length = length; func->name = name; - func->argv = argv; - func->argc = argc; - func->body = body; + func->expr = expr; } - return (Node*) func; + return func; } static Node *parse_while_statement(Context *ctx)