added map expressions
This commit is contained in:
@@ -69,6 +69,7 @@ static const InstrInfo instr_table[] = {
|
|||||||
[OPCODE_PUSHNNE] = {"PUSHNNE", 0, NULL},
|
[OPCODE_PUSHNNE] = {"PUSHNNE", 0, NULL},
|
||||||
[OPCODE_PUSHFUN] = {"PUSHFUN", 2, (OperandType[]) {OPTP_INT, OPTP_INT}},
|
[OPCODE_PUSHFUN] = {"PUSHFUN", 2, (OperandType[]) {OPTP_INT, OPTP_INT}},
|
||||||
[OPCODE_PUSHLST] = {"PUSHLST", 1, (OperandType[]) {OPTP_INT}},
|
[OPCODE_PUSHLST] = {"PUSHLST", 1, (OperandType[]) {OPTP_INT}},
|
||||||
|
[OPCODE_PUSHMAP] = {"PUSHMAP", 1, (OperandType[]) {OPTP_INT}},
|
||||||
|
|
||||||
[OPCODE_RETURN] = {"RETURN", 0, NULL},
|
[OPCODE_RETURN] = {"RETURN", 0, NULL},
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ typedef enum {
|
|||||||
OPCODE_PUSHNNE,
|
OPCODE_PUSHNNE,
|
||||||
OPCODE_PUSHFUN,
|
OPCODE_PUSHFUN,
|
||||||
OPCODE_PUSHLST,
|
OPCODE_PUSHLST,
|
||||||
|
OPCODE_PUSHMAP,
|
||||||
OPCODE_RETURN,
|
OPCODE_RETURN,
|
||||||
OPCODE_JUMPIFANDPOP,
|
OPCODE_JUMPIFANDPOP,
|
||||||
OPCODE_JUMPIFNOTANDPOP,
|
OPCODE_JUMPIFNOTANDPOP,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ typedef enum {
|
|||||||
|
|
||||||
EXPR_ASS,
|
EXPR_ASS,
|
||||||
EXPR_INT,
|
EXPR_INT,
|
||||||
|
EXPR_MAP,
|
||||||
EXPR_CALL,
|
EXPR_CALL,
|
||||||
EXPR_LIST,
|
EXPR_LIST,
|
||||||
EXPR_NONE,
|
EXPR_NONE,
|
||||||
@@ -86,6 +87,13 @@ typedef struct {
|
|||||||
int itemc;
|
int itemc;
|
||||||
} ListExprNode;
|
} ListExprNode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ExprNode base;
|
||||||
|
Node *keys;
|
||||||
|
Node *items;
|
||||||
|
int itemc;
|
||||||
|
} MapExprNode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ExprNode base;
|
ExprNode base;
|
||||||
char *val;
|
char *val;
|
||||||
|
|||||||
@@ -167,6 +167,37 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
return 1;
|
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:
|
case EXPR_CALL:
|
||||||
{
|
{
|
||||||
CallExprNode *p = (CallExprNode*) expr;
|
CallExprNode *p = (CallExprNode*) expr;
|
||||||
|
|||||||
@@ -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)
|
static char *copy_token_text(Context *ctx)
|
||||||
{
|
{
|
||||||
char *copy = BPAlloc_Malloc(ctx->alloc, ctx->token->length + 1);
|
char *copy = BPAlloc_Malloc(ctx->alloc, ctx->token->length + 1);
|
||||||
@@ -905,6 +1025,9 @@ static Node *parse_primary_expresion(Context *ctx)
|
|||||||
case '[':
|
case '[':
|
||||||
return parse_list_primary_expression(ctx);
|
return parse_list_primary_expression(ctx);
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
return parse_map_primary_expression(ctx);
|
||||||
|
|
||||||
case '(':
|
case '(':
|
||||||
{
|
{
|
||||||
next(ctx); // Consume the '('.
|
next(ctx); // Consume the '('.
|
||||||
|
|||||||
@@ -848,6 +848,21 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
return 1;
|
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:
|
case OPCODE_RETURN:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user