diff --git a/examples/arrow.noja b/examples/arrow.noja new file mode 100644 index 0000000..8664788 --- /dev/null +++ b/examples/arrow.noja @@ -0,0 +1,12 @@ + + +fun sayHello(self, other) + print("Hello ", other, " from ", self.name); + +person = { + name: "Francesco", + sayHello: sayHello +}; + +person->sayHello("Giovanni"); + diff --git a/examples/http.noja b/examples/http.noja index dc64c94..d1df0d2 100644 --- a/examples/http.noja +++ b/examples/http.noja @@ -1,3 +1,4 @@ + scan = import("scan.noja"); newScanner = scan.newScanner; @@ -7,14 +8,16 @@ consume = scan.consume; current = scan.current; isDigit = scan.isDigit; consumeSpaces = scan.consumeSpaces; +ord = string.ord; +cat = string.cat; fun isAlpha(char: String) - return (string.ord(char) >= string.ord('a') and string.ord(char) <= string.ord('z')) - or (string.ord(char) >= string.ord('A') and string.ord(char) <= string.ord('Z')); + return (ord(char) >= ord('a') and ord(char) <= ord('z')) + or (ord(char) >= ord('A') and ord(char) <= ord('Z')); fun isUpper(char: String) - return string.ord(char) >= string.ord('A') - and string.ord(char) <= string.ord('Z'); + return ord(char) >= ord('A') + and ord(char) <= ord('Z'); fun isUnreserved(char: String) return isAlpha(char) @@ -46,7 +49,7 @@ fun parseMethod(scan: Scanner) { method = ""; do { - method = string.cat(method, char); + method = cat(method, char); consume(scan); char = current(scan); } while char != none and isUpper(char); @@ -65,8 +68,8 @@ fun parsePort(scan: Scanner) { port = 0; do { - digit = string.ord(char) - - string.ord("0"); + digit = ord(char) + - ord("0"); port = port * 10 + digit; consume(scan); char = current(scan); @@ -90,7 +93,7 @@ fun parseHost(scan: Scanner) { name = ""; do { - name = string.cat(name, char); + name = cat(name, char); consume(scan); char = current(scan); } while char != none and isHostname(char); @@ -104,7 +107,7 @@ fun parsePath(scan: Scanner) { path = ""; char = current(scan); if char != none and char == "/": { - path = string.cat(path, char); + path = cat(path, char); consume(scan); char = current(scan); } else { @@ -114,13 +117,13 @@ fun parsePath(scan: Scanner) { while char != none and isPChar(char): { do { - path = string.cat(path, char); + path = cat(path, char); consume(scan); char = current(scan); } while char != none and isPChar(char); if char == none or char != "/": break; - path = string.cat(path, char); + path = cat(path, char); consume(scan); char = current(scan); } @@ -143,7 +146,7 @@ fun parseQuery(scan: Scanner) { consume(scan); # Consume the "?" query = ""; while isQuery(char = current(scan)): { - query = string.cat(query, char); + query = cat(query, char); consume(scan); } } else @@ -165,7 +168,7 @@ fun parseFragment(scan: Scanner) { consume(scan); # Consume the "#" fragment = ""; while isFragment(char = current(scan)): { - fragment = string.cat(fragment, char); + fragment = cat(fragment, char); consume(scan); } } else @@ -174,12 +177,15 @@ fun parseFragment(scan: Scanner) { return fragment; } -Host = { name: String, port: ?int }; +Host = { + name: String, + port: ?int +}; URL = { schema: ?String, host: ?Host, - path: ?String, + path: String, query: ?String, fragment: ?String }; @@ -204,7 +210,7 @@ fun parseSchema(scan: Scanner) { start = scan.i; schema = ""; do { - schema = string.cat(schema, char); + schema = cat(schema, char); consume(scan); char = current(scan); } while isSchema(char); @@ -253,7 +259,6 @@ fun parseURL(scan: Scanner) { query = parseQuery(scan); fragment = parseFragment(scan); - print(scan); return { host : host, @@ -272,13 +277,13 @@ fun parseVersion(scan: Scanner) { return none, "Invalid version token"; consume(scan, 5); - major = string.ord(current(scan)) - - string.ord("0"); + major = ord(current(scan)) + - ord("0"); if hint(scan, 1) != "." and isDigit(hint(scan, 2)): { consume(scan, 2); - minor = string.ord(current(scan)) - - string.ord("0"); + minor = ord(current(scan)) + - ord("0"); } else minor = 0; consume(scan); @@ -343,7 +348,7 @@ fun parse(src: String) { # Header name until the next ":" name = ""; do { - name = string.cat(name, char); + name = cat(name, char); consume(scan); char = current(scan); } while char != ":" and char != none; @@ -357,7 +362,7 @@ fun parse(src: String) { # Header body until \r body = ""; do { - body = string.cat(body, char); + body = cat(body, char); consume(scan); char = current(scan); } while char != "\r" and char != none; @@ -371,7 +376,7 @@ fun parse(src: String) { return newRequest(method, url, version, headers); } -sample = string.cat( +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", diff --git a/src/lib/common/executable.c b/src/lib/common/executable.c index eb9738c..f9887f2 100644 --- a/src/lib/common/executable.c +++ b/src/lib/common/executable.c @@ -99,6 +99,7 @@ static const InstrInfo instr_table[] = { INSTR(CHECKTYPE, OPTP_INT, OPTP_STRING) INSTR(CALL, OPTP_INT, OPTP_INT) INSTR(SELECT) + INSTR(SELECT2) INSTR(INSERT) INSTR(INSERT2) INSTR(PUSHINT, OPTP_INT) @@ -520,6 +521,13 @@ _Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand * const InstrInfo *info = Executable_GetInstrByOpcode(opcode); +#ifndef NDEBUG + if (info == NULL) { + Error_Report(error, 1, "Missing instruction table entry for opcode"); + return 0; + } +#endif + if(opc != info->opcount) { // ERROR: Too many operands were provided. diff --git a/src/lib/common/executable.h b/src/lib/common/executable.h index da258b2..9e00ed0 100644 --- a/src/lib/common/executable.h +++ b/src/lib/common/executable.h @@ -74,6 +74,7 @@ typedef enum { OPCODE_POP, OPCODE_CALL, OPCODE_SELECT, + OPCODE_SELECT2, OPCODE_INSERT, OPCODE_INSERT2, OPCODE_PUSHINT, diff --git a/src/lib/compiler/ASTi.h b/src/lib/compiler/ASTi.h index 2891110..7290b44 100644 --- a/src/lib/compiler/ASTi.h +++ b/src/lib/compiler/ASTi.h @@ -65,6 +65,7 @@ typedef enum { EXPR_OR, EXPR_NOT, + EXPR_ARW, EXPR_ASS, EXPR_INT, EXPR_MAP, diff --git a/src/lib/compiler/codegen.c b/src/lib/compiler/codegen.c index 0bde788..04504af 100644 --- a/src/lib/compiler/codegen.c +++ b/src/lib/compiler/codegen.c @@ -198,19 +198,34 @@ static void emitInstrForFuncCallNode(CodegenContext *ctx, CallExprNode *expr, Label *label_break, int returns) { Node *arg = expr->argv; - while(arg) { emitInstrForNode(ctx, arg, label_break); arg = arg->next; } - emitInstrForNode(ctx, expr->func, label_break); + int argc; + if (expr->func->kind == NODE_EXPR && ((ExprNode*) expr->func)->kind == EXPR_ARW) { + IndexSelectionExprNode *selection = (IndexSelectionExprNode*) expr->func; + Node *idx = selection->idx; + Node *set = selection->set; + emitInstrForNode(ctx, set, label_break); + emitInstrForNode(ctx, idx, label_break); + CodegenContext_EmitInstr(ctx, OPCODE_SELECT2, NULL, 0, + expr->func->offset, + expr->func->length); + argc = expr->argc+1; + } else { + emitInstrForNode(ctx, expr->func, label_break); + argc = expr->argc; + } Operand ops[2]; - ops[0] = (Operand) { .type = OPTP_INT, .as_int = expr->argc }; + ops[0] = (Operand) { .type = OPTP_INT, .as_int = argc }; ops[1] = (Operand) { .type = OPTP_INT, .as_int = returns }; - CodegenContext_EmitInstr(ctx, OPCODE_CALL, ops, 2, expr->base.base.offset, expr->base.base.length); + CodegenContext_EmitInstr(ctx, OPCODE_CALL, ops, 2, + expr->base.base.offset, + expr->base.base.length); } static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, @@ -394,6 +409,11 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, return; } + case EXPR_ARW: + CodegenContext_ReportErrorAndJump(ctx, 0, "Operator -> out of a function call"); + UNREACHABLE; + return; + case EXPR_AND: { OperExprNode *oper = (OperExprNode*) expr; diff --git a/src/lib/compiler/parse.c b/src/lib/compiler/parse.c index 3ef5c97..da3a7ac 100644 --- a/src/lib/compiler/parse.c +++ b/src/lib/compiler/parse.c @@ -52,6 +52,7 @@ ** +--------------------------------------------------------------------------+ */ +#include #include #include #include @@ -112,6 +113,7 @@ typedef enum { TNQL, TLEQ, TGEQ, + TARW, } TokenKind; @@ -331,6 +333,7 @@ static Token *tokenize(Source *src, BPAlloc *alloc, Error *error) { TCOMMA, 1, "," }, { TSMT, 1, "|" }, { TMOD, 1, "%" }, + { TARW, 2, "->" }, }; _Bool found = 0; @@ -1364,7 +1367,7 @@ static Node *parse_primary_expresion(Context *ctx) return NULL; } -static Node *makeIndexSelectionExprNode(Context *ctx, Node *set, Node *idx) +static Node *makeIndexOrArrowSelectionExprNode(Context *ctx, bool arrow, Node *set, Node *idx) { IndexSelectionExprNode *sel = BPAlloc_Malloc(ctx->alloc, sizeof(IndexSelectionExprNode)); @@ -1378,7 +1381,7 @@ static Node *makeIndexSelectionExprNode(Context *ctx, Node *set, Node *idx) sel->base.base.next = NULL; sel->base.base.offset = -1; sel->base.base.length = -1; - sel->base.kind = EXPR_SELECT; + sel->base.kind = arrow ? EXPR_ARW : EXPR_SELECT; sel->set = set; sel->idx = idx; return (Node*) sel; @@ -1398,20 +1401,23 @@ static Node *parse_postfix_expression(Context *ctx) switch(current(ctx)) { case '.': + case TARW: { + bool arrow = (current(ctx) == TARW); + next(ctx); - // We expect an identifier after the dot. + // We expect an identifier after the dot or arrow if(done(ctx)) { - Error_Report(ctx->error, 0, "Source ended after dot of dot selection expression"); + Error_Report(ctx->error, 0, "Source ended after dot or arrow"); return NULL; } if(current(ctx) != TIDENT) { - Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" after fot of dot selection expression where an identifier was expected", ctx->token->length, ctx->src + ctx->token->offset); + Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" after dot or arrow, where an identifier was expected", ctx->token->length, ctx->src + ctx->token->offset); return NULL; } @@ -1419,7 +1425,7 @@ static Node *parse_postfix_expression(Context *ctx) if(idx == NULL) return NULL; - Node *sel = makeIndexSelectionExprNode(ctx, node, idx); + Node *sel = makeIndexOrArrowSelectionExprNode(ctx, arrow, node, idx); if(sel == NULL) return NULL; @@ -1446,7 +1452,7 @@ static Node *parse_postfix_expression(Context *ctx) return NULL; } - Node *sel = makeIndexSelectionExprNode(ctx, node, ls->itemc == 1 ? ls->items : (Node*) ls); + Node *sel = makeIndexOrArrowSelectionExprNode(ctx, false, node, ls->itemc == 1 ? ls->items : (Node*) ls); if(sel == NULL) return NULL; @@ -1653,7 +1659,7 @@ static inline int precedenceof(Token *tok) case ',': return 7; - + default: return -100000000; } @@ -1723,7 +1729,6 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec, _Bo case '=': temp->base.kind = EXPR_ASS; break; case '|': temp->base.kind = EXPR_SUMTYPE; break; case ',': temp->base.kind = EXPR_PAIR; break; - default: UNREACHABLE; break; diff --git a/src/lib/runtime/runtime.c b/src/lib/runtime/runtime.c index 247fe21..70f2e91 100644 --- a/src/lib/runtime/runtime.c +++ b/src/lib/runtime/runtime.c @@ -978,12 +978,18 @@ static _Bool step(Runtime *runtime, Error *error) } case OPCODE_SELECT: + case OPCODE_SELECT2: { ASSERT(opc == 0); - if(runtime->frame->used < 2) + int to_be_popped = (opcode == OPCODE_SELECT) ? 2 : 1; + + if(runtime->frame->used < to_be_popped) { - Error_Report(error, 1, "Frame has not enough values on the stack to run SELECT instruction"); + const char *name = "SELECT"; + if (opcode == OPCODE_SELECT2) + name = "SELECT2"; + Error_Report(error, 1, "Frame has not enough values on the stack to run %s instruction", name); return 0; } @@ -992,7 +998,7 @@ static _Bool step(Runtime *runtime, Error *error) ASSERT(col != NULL && key != NULL); - if(!Runtime_Pop(runtime, error, 2)) + if(!Runtime_Pop(runtime, error, to_be_popped)) return 0; ASSERT(error->occurred == 0); @@ -1007,7 +1013,6 @@ static _Bool step(Runtime *runtime, Error *error) Error_Free(&dummy); val = Object_NewNone(runtime->heap, error); - if(val == NULL) return 0; }