diff --git a/src/common/executable.c b/src/common/executable.c index 7ac4749..14aa7e3 100644 --- a/src/common/executable.c +++ b/src/common/executable.c @@ -69,6 +69,7 @@ static const InstrInfo instr_table[] = { [OPCODE_PUSHNNE] = {"PUSHNNE", 0, NULL}, [OPCODE_PUSHFUN] = {"PUSHFUN", 2, (OperandType[]) {OPTP_INT, OPTP_INT}}, [OPCODE_PUSHLST] = {"PUSHLST", 1, (OperandType[]) {OPTP_INT}}, + [OPCODE_PUSHMAP] = {"PUSHMAP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_RETURN] = {"RETURN", 0, NULL}, diff --git a/src/common/executable.h b/src/common/executable.h index 19581b5..4e58056 100644 --- a/src/common/executable.h +++ b/src/common/executable.h @@ -48,6 +48,7 @@ typedef enum { OPCODE_PUSHNNE, OPCODE_PUSHFUN, OPCODE_PUSHLST, + OPCODE_PUSHMAP, OPCODE_RETURN, OPCODE_JUMPIFANDPOP, OPCODE_JUMPIFNOTANDPOP, diff --git a/src/compiler/ASTi.h b/src/compiler/ASTi.h index 01dee4a..379fcf9 100644 --- a/src/compiler/ASTi.h +++ b/src/compiler/ASTi.h @@ -32,6 +32,7 @@ typedef enum { EXPR_ASS, EXPR_INT, + EXPR_MAP, EXPR_CALL, EXPR_LIST, EXPR_NONE, @@ -86,6 +87,13 @@ typedef struct { int itemc; } ListExprNode; +typedef struct { + ExprNode base; + Node *keys; + Node *items; + int itemc; +} MapExprNode; + typedef struct { ExprNode base; char *val; diff --git a/src/compiler/compile.c b/src/compiler/compile.c index ecb2b2b..5a1063c 100644 --- a/src/compiler/compile.c +++ b/src/compiler/compile.c @@ -167,6 +167,37 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error) return 1; } + case EXPR_MAP: + { + MapExprNode *m = (MapExprNode*) node; + + Operand op; + + op = (Operand) { .type = OPTP_INT, .as_int = m->itemc }; + if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHMAP, &op, 1, node->offset, node->length)) + return 0; + + Node *key = m->keys; + Node *item = m->items; + + while(item) + { + if(!emit_instr_for_node(exeb, key, error)) + return 0; + + if(!emit_instr_for_node(exeb, item, error)) + return 0; + + if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT, NULL, 0, item->offset, item->length)) + return 0; + + key = key->next; + item = item->next; + } + + return 1; + } + case EXPR_CALL: { CallExprNode *p = (CallExprNode*) expr; diff --git a/src/compiler/parse.c b/src/compiler/parse.c index c9b7ac3..99d2084 100644 --- a/src/compiler/parse.c +++ b/src/compiler/parse.c @@ -848,6 +848,126 @@ static Node *parse_list_primary_expression(Context *ctx) } + +static Node *parse_map_primary_expression(Context *ctx) +{ + assert(ctx != NULL); + + if(done(ctx)) + { + Error_Report(ctx->error, 0, "Source ended where a map literal was expected"); + return NULL; + } + + if(current(ctx) != '{') + { + Error_Report(ctx->error, 0, "Got token \"%.*s\" where a map literal was expected", ctx->token->length, ctx->src + ctx->token->offset); + return NULL; + } + + int offset = current_token(ctx)->offset; + + Node *keys = NULL; + Node *items = NULL; + int itemc = 0; + + next(ctx); // Skip the '['. + + if(current(ctx) != '}') + { + Node *ktail = NULL, *tail = NULL; + + while(1) + { + Node *key = parse_expression(ctx); + + 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); + + if(item == NULL) + return NULL; + + // Append. + if(tail) + { + ktail->next = key; + tail->next = item; + } + else + { + keys = key; + items = item; + } + + ktail = key; + tail = item; + itemc += 1; + + // Get ',' or '}'. + + 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; + } + + next(ctx); // Skip the ','. + } + } + + int length = current_token(ctx)->offset + + current_token(ctx)->length + - offset; + + next(ctx); // Skip the ']'. + + MapExprNode *map; + { + map = BPAlloc_Malloc(ctx->alloc, sizeof(MapExprNode)); + + if(map == NULL) + { + Error_Report(ctx->error, 1, "No memory"); + return NULL; + } + + map->base.base.kind = NODE_EXPR; + map->base.base.next = NULL; + map->base.base.offset = offset; + map->base.base.length = length; + map->base.kind = EXPR_MAP; + map->keys = keys; + map->items = items; + map->itemc = itemc; + } + + return (Node*) map; + +} + static char *copy_token_text(Context *ctx) { char *copy = BPAlloc_Malloc(ctx->alloc, ctx->token->length + 1); @@ -905,6 +1025,9 @@ static Node *parse_primary_expresion(Context *ctx) case '[': return parse_list_primary_expression(ctx); + case '{': + return parse_map_primary_expression(ctx); + case '(': { next(ctx); // Consume the '('. diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index ca1c6a1..65165cf 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -848,6 +848,21 @@ static _Bool step(Runtime *runtime, Error *error) return 1; } + case OPCODE_PUSHMAP: + { + assert(opc == 1); + assert(ops[0].type == OPTP_INT); + + Object *obj = Object_NewMap(ops[0].as_int, runtime->heap, error); + + if(obj == NULL) + return 0; + + if(!Runtime_Push(runtime, error, obj)) + return 0; + return 1; + } + case OPCODE_RETURN: return 0;