|
@@ -62,6 +62,7 @@ void list_set(list_t *l, ssize_t index, void *v) {
|
|
|
|
|
|
typedef struct {
|
|
typedef struct {
|
|
size_t *data;
|
|
size_t *data;
|
|
|
|
+
|
|
size_t length;
|
|
size_t length;
|
|
} stack_t;
|
|
} stack_t;
|
|
|
|
|
|
@@ -282,6 +283,9 @@ typedef struct {
|
|
T_IF,
|
|
T_IF,
|
|
T_ELSE,
|
|
T_ELSE,
|
|
T_ELIF,
|
|
T_ELIF,
|
|
|
|
+ T_SWITCH,
|
|
|
|
+ T_CASE,
|
|
|
|
+ T_DEFAULT,
|
|
T_FOR,
|
|
T_FOR,
|
|
T_OF,
|
|
T_OF,
|
|
T_BREAK,
|
|
T_BREAK,
|
|
@@ -563,6 +567,12 @@ token_t *next_token(char *source, size_t *pos) {
|
|
return TK(ELSE);
|
|
return TK(ELSE);
|
|
else if (strcmp(name, "elif") == 0)
|
|
else if (strcmp(name, "elif") == 0)
|
|
return TK(ELIF);
|
|
return TK(ELIF);
|
|
|
|
+ else if (strcmp(name, "switch") == 0)
|
|
|
|
+ return TK(SWITCH);
|
|
|
|
+ else if (strcmp(name, "case") == 0)
|
|
|
|
+ return TK(CASE);
|
|
|
|
+ else if (strcmp(name, "default") == 0)
|
|
|
|
+ return TK(DEFAULT);
|
|
else if (strcmp(name, "for") == 0)
|
|
else if (strcmp(name, "for") == 0)
|
|
return TK(FOR);
|
|
return TK(FOR);
|
|
else if (strcmp(name, "break") == 0)
|
|
else if (strcmp(name, "break") == 0)
|
|
@@ -770,6 +780,7 @@ struct _node_t {
|
|
N_VAR,
|
|
N_VAR,
|
|
N_LET,
|
|
N_LET,
|
|
N_IF,
|
|
N_IF,
|
|
|
|
+ N_SWITCH,
|
|
N_FOR,
|
|
N_FOR,
|
|
N_FOROF,
|
|
N_FOROF,
|
|
N_BREAK,
|
|
N_BREAK,
|
|
@@ -907,6 +918,19 @@ node_t *node2(int tag, node_t *a, node_t *b) {
|
|
|
|
|
|
#define NODE2(n, a, b) (node_pos(node2(N_##n, (a), (b)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos))
|
|
#define NODE2(n, a, b) (node_pos(node2(N_##n, (a), (b)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos))
|
|
|
|
|
|
|
|
+node_t *node2l(int tag, node_t *a, node_t *b, list_t *l) {
|
|
|
|
+ node_t *node = malloc(sizeof(node_t));
|
|
|
|
+
|
|
|
|
+ node->tag = tag;
|
|
|
|
+ node->a = a;
|
|
|
|
+ node->b = b;
|
|
|
|
+ node->l = l;
|
|
|
|
+
|
|
|
|
+ return node;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define NODE2l(n, a, b, l) (node_pos(node2l(N_##n, (a), (b), (l)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos))
|
|
|
|
+
|
|
node_t *node2t(int tag, node_t *a, node_t *b, token_t *t) {
|
|
node_t *node2t(int tag, node_t *a, node_t *b, token_t *t) {
|
|
node_t *node = malloc(sizeof(node_t));
|
|
node_t *node = malloc(sizeof(node_t));
|
|
|
|
|
|
@@ -1054,6 +1078,7 @@ size_t get_lineno(token_t *tok) {
|
|
}
|
|
}
|
|
|
|
|
|
#define CLIFF (get_lineno(((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])) != get_lineno(((token_t *)tokens->data[(*pos)>=tokens->length?tokens->length-1:(*pos)])))
|
|
#define CLIFF (get_lineno(((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])) != get_lineno(((token_t *)tokens->data[(*pos)>=tokens->length?tokens->length-1:(*pos)])))
|
|
|
|
+#define CLIFF_AHEAD (get_lineno(((token_t *)tokens->data[(*pos)>=tokens->length?tokens->length-1:(*pos)])) != get_lineno(((token_t *)tokens->data[(*pos)+1>=tokens->length?tokens->length-1:(*pos)+1])))
|
|
|
|
|
|
node_t *parse_call(list_t *tokens, size_t *pos) {
|
|
node_t *parse_call(list_t *tokens, size_t *pos) {
|
|
node_t *a = parse_primary(tokens, pos);
|
|
node_t *a = parse_primary(tokens, pos);
|
|
@@ -1595,7 +1620,54 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
|
|
return parse_var(tokens, pos, 1);
|
|
return parse_var(tokens, pos, 1);
|
|
else if (MATCH(IF))
|
|
else if (MATCH(IF))
|
|
return parse_if(tokens, pos);
|
|
return parse_if(tokens, pos);
|
|
- else if (MATCH(FOR)) {
|
|
|
|
|
|
+ else if (MATCH(SWITCH)) {
|
|
|
|
+ node_t *a = parse_expr(tokens, pos);
|
|
|
|
+
|
|
|
|
+ EXPECT(LCB, "{");
|
|
|
|
+
|
|
|
|
+ list_t *cases = list_new();
|
|
|
|
+
|
|
|
|
+ for (;;) {
|
|
|
|
+ if (AT(RCB) || AT(DEFAULT)) break;
|
|
|
|
+
|
|
|
|
+ EXPECT(CASE, "case");
|
|
|
|
+
|
|
|
|
+ node_t *expr = parse_expr(tokens, pos);
|
|
|
|
+
|
|
|
|
+ MATCH(COLON);
|
|
|
|
+
|
|
|
|
+ list_t *stmts = list_new();
|
|
|
|
+
|
|
|
|
+ while (!AT(CASE) && !AT(DEFAULT) && !AT(RCB))
|
|
|
|
+ list_push(stmts, parse_stmt(tokens, pos));
|
|
|
|
+
|
|
|
|
+ list_t *pair = list_new();
|
|
|
|
+ list_push(pair, expr);
|
|
|
|
+ list_push(pair, NODEL(PROGRAM, stmts));
|
|
|
|
+
|
|
|
|
+ list_push(cases, pair);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ node_t *b = NULL;
|
|
|
|
+
|
|
|
|
+ if (MATCH(DEFAULT)) {
|
|
|
|
+ MATCH(COLON);
|
|
|
|
+
|
|
|
|
+ list_t *stmts = list_new();
|
|
|
|
+
|
|
|
|
+ while (!AT(RCB))
|
|
|
|
+ list_push(stmts, parse_stmt(tokens, pos));
|
|
|
|
+
|
|
|
|
+ b = NODEL(PROGRAM, stmts);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ EXPECT(RCB, "}");
|
|
|
|
+
|
|
|
|
+ if (!cases->length && !b)
|
|
|
|
+ PARSE_ERROR("empty switch statement");
|
|
|
|
+
|
|
|
|
+ return NODE2l(SWITCH, a, b, cases);
|
|
|
|
+ } else if (MATCH(FOR)) {
|
|
node_t *a = NULL;
|
|
node_t *a = NULL;
|
|
node_t *b = NULL;
|
|
node_t *b = NULL;
|
|
node_t *c = NULL;
|
|
node_t *c = NULL;
|
|
@@ -1674,7 +1746,7 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
|
|
token_t *t = tokens->data[(*pos)++];
|
|
token_t *t = tokens->data[(*pos)++];
|
|
|
|
|
|
return NODET(GOTO, t);
|
|
return NODET(GOTO, t);
|
|
- } else if (AT(NAME) && ATP(COLON, 1) && !CLIFF) {
|
|
|
|
|
|
+ } else if (AT(NAME) && ATP(COLON, 1) && !CLIFF_AHEAD) {
|
|
token_t *t = tokens->data[(*pos)++];
|
|
token_t *t = tokens->data[(*pos)++];
|
|
|
|
|
|
EXPECT(COLON, ":");
|
|
EXPECT(COLON, ":");
|
|
@@ -1945,12 +2017,24 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
|
|
|
|
void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, list_t *block) {
|
|
void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, list_t *block) {
|
|
for (size_t i = 0; i < block->length; i++) {
|
|
for (size_t i = 0; i < block->length; i++) {
|
|
- node_t *n = block->data[i];
|
|
|
|
|
|
+ node_t *node = block->data[i];
|
|
|
|
|
|
- if (n->tag == N_FUNCDEF) {
|
|
|
|
- compile_func(gbuf, buf, ctx, lstk, lbl, n);
|
|
|
|
|
|
+ if (node->tag == N_FUNCDEF) {
|
|
|
|
+ compile_func(gbuf, buf, ctx, lstk, lbl, node);
|
|
|
|
|
|
EMIT("\n");
|
|
EMIT("\n");
|
|
|
|
+ } else if (node->tag == N_LABEL) {
|
|
|
|
+ char *label = node->t->text;
|
|
|
|
+
|
|
|
|
+ if (table_get(list_index(lbl, -1), label))
|
|
|
|
+ COMPILE_ERROR("duplicated label: '%s'", label);
|
|
|
|
+
|
|
|
|
+ NEWGID();
|
|
|
|
+
|
|
|
|
+ size_t *n = malloc(sizeof(size_t));
|
|
|
|
+ memcpy(n, &gid, sizeof(size_t));
|
|
|
|
+
|
|
|
|
+ table_set(list_index(lbl, -1), label, n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2215,6 +2299,50 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
EMIT("}");
|
|
EMIT("}");
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ case N_SWITCH: {
|
|
|
|
+ NEWGID();
|
|
|
|
+ char *varname = tempvar();
|
|
|
|
+
|
|
|
|
+ EMIT("qi_value_t *%s = ", varname);
|
|
|
|
+ compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
|
|
|
|
+ EMIT(";\n");
|
|
|
|
+
|
|
|
|
+ for (size_t i = 0; i < node->l->length; i++) {
|
|
|
|
+ list_t *pair = node->l->data[i];
|
|
|
|
+ char *label = tempvar();
|
|
|
|
+
|
|
|
|
+ EMIT("if (_qi_equals(state, %s, ", varname);
|
|
|
|
+ compile_node(gbuf, buf, ctx, lstk, lbl, pair->data[0]);
|
|
|
|
+ EMIT(")) goto %s;\n", label);
|
|
|
|
+
|
|
|
|
+ pair->data[0] = label;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ EMIT("goto __default%d;\n", gid);
|
|
|
|
+
|
|
|
|
+ LPUSH(gid);
|
|
|
|
+ CTXPUSH("switch");
|
|
|
|
+
|
|
|
|
+ for (size_t i = 0; i < node->l->length; i++) {
|
|
|
|
+ list_t *pair = node->l->data[i];
|
|
|
|
+ char *label = pair->data[0];
|
|
|
|
+ node_t *block = pair->data[1];
|
|
|
|
+
|
|
|
|
+ EMIT("%s:;\n", label);
|
|
|
|
+ compile_node(gbuf, buf, ctx, lstk, lbl, block);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ EMIT("__default%d:;\n", gid);
|
|
|
|
+
|
|
|
|
+ if (node->b)
|
|
|
|
+ compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
|
|
|
|
+
|
|
|
|
+ CTXPOP();
|
|
|
|
+ LPOP();
|
|
|
|
+
|
|
|
|
+ EMIT("__break%d:;\n", gid);
|
|
|
|
+ } break;
|
|
|
|
+
|
|
case N_FOR: {
|
|
case N_FOR: {
|
|
NEWGID();
|
|
NEWGID();
|
|
|
|
|
|
@@ -2278,8 +2406,8 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
} break;
|
|
} break;
|
|
|
|
|
|
case N_BREAK:
|
|
case N_BREAK:
|
|
- if (!INCTX("for"))
|
|
|
|
- COMPILE_ERROR("break outside of a loop");
|
|
|
|
|
|
+ if (!INCTX("for") && !INCTX("switch"))
|
|
|
|
+ COMPILE_ERROR("break outside of a loop or a switch");
|
|
|
|
|
|
EMIT("goto __break%d;", LID);
|
|
EMIT("goto __break%d;", LID);
|
|
break;
|
|
break;
|
|
@@ -2357,40 +2485,18 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
break;
|
|
break;
|
|
|
|
|
|
case N_LABEL: {
|
|
case N_LABEL: {
|
|
- char *label = node->t->text;
|
|
|
|
-
|
|
|
|
- table_iterate((table_t *)list_index(lbl, -1), {
|
|
|
|
- if (strcmp(entry.key, label) == 0) {
|
|
|
|
- COMPILE_ERROR("duplicated label: '%s'", label);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- NEWGID();
|
|
|
|
|
|
+ size_t *gid = table_get(list_index(lbl, -1), node->t->text);
|
|
|
|
|
|
- EMIT("__label%d:;", gid);
|
|
|
|
-
|
|
|
|
- size_t *n = malloc(sizeof(size_t));
|
|
|
|
- memcpy(n, &gid, sizeof(size_t));
|
|
|
|
-
|
|
|
|
- table_set(list_index(lbl, -1), label, n);
|
|
|
|
|
|
+ EMIT("__label%d:;", *gid);
|
|
} break;
|
|
} break;
|
|
|
|
|
|
case N_GOTO: {
|
|
case N_GOTO: {
|
|
- ssize_t gid = -1;
|
|
|
|
char *label = node->t->text;
|
|
char *label = node->t->text;
|
|
-
|
|
|
|
- table_iterate((table_t *)list_index(lbl, -1), {
|
|
|
|
- if (strcmp(entry.key, label) == 0) {
|
|
|
|
- gid = *(size_t *)entry.value;
|
|
|
|
-
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- if (gid < 0)
|
|
|
|
|
|
+ size_t *gid = table_get(list_index(lbl, -1), label);
|
|
|
|
+ if (!gid)
|
|
COMPILE_ERROR("undefined label: '%s'", label);
|
|
COMPILE_ERROR("undefined label: '%s'", label);
|
|
|
|
|
|
- EMIT("goto __label%d;", gid);
|
|
|
|
|
|
+ EMIT("goto __label%d;", *gid);
|
|
} break;
|
|
} break;
|
|
|
|
|
|
case N_REQUIRE: {
|
|
case N_REQUIRE: {
|