txlyre 1 день тому
батько
коміт
b169bf87f7
1 змінених файлів з 140 додано та 34 видалено
  1. 140 34
      qic.c

+ 140 - 34
qic.c

@@ -62,6 +62,7 @@ void list_set(list_t *l, ssize_t index, void *v) {
 
 typedef struct {
   size_t *data;
+
   size_t length;
 } stack_t;
 
@@ -282,6 +283,9 @@ typedef struct {
     T_IF,
     T_ELSE,
     T_ELIF,
+    T_SWITCH,
+    T_CASE,
+    T_DEFAULT,
     T_FOR,
     T_OF,
     T_BREAK,
@@ -563,6 +567,12 @@ token_t *next_token(char *source, size_t *pos) {
       return TK(ELSE);
     else if (strcmp(name, "elif") == 0)
       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)
       return TK(FOR);
     else if (strcmp(name, "break") == 0)
@@ -770,6 +780,7 @@ struct _node_t {
     N_VAR,
     N_LET,
     N_IF,
+    N_SWITCH,
     N_FOR,
     N_FOROF,
     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))
 
+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 *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_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 *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);
   else if (MATCH(IF))
     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 *b = 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)++];
 
     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)++];
 
     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) {
   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");
+    } 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("}");
       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: {
       NEWGID();
 
@@ -2278,8 +2406,8 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       } 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);
       break;
@@ -2357,40 +2485,18 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       break;
 
     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;
 
     case N_GOTO: {
-      ssize_t gid = -1;
       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);
 
-      EMIT("goto __label%d;", gid);
+      EMIT("goto __label%d;", *gid);
       } break;
 
     case N_REQUIRE: {