瀏覽代碼

wip macros

txlyre 3 天之前
父節點
當前提交
68127a10df
共有 1 個文件被更改,包括 167 次插入44 次删除
  1. 167 44
      qic.c

+ 167 - 44
qic.c

@@ -59,6 +59,16 @@ list_t *list_new(void) {
   return list;
 }
 
+list_t *list_copy(list_t *list) {
+  list_t *nlist = malloc_checked(sizeof(list_t));
+  nlist->length = list->length;
+  nlist->data = realloc_checked(nlist->data, nlist->length * sizeof(void *));
+
+  memcpy(nlist->data, list->data, nlist->length * sizeof(void *));
+
+  return nlist;
+}
+
 void list_push(list_t *l, void *v) {
   size_t i = l->length++;
 
@@ -188,6 +198,31 @@ void *table_get(table_t *table, char *key) {
   return NULL;
 }
 
+int table_has(table_t *table, char *key) {
+  if (!table->used)
+    return 0;
+
+  unsigned long hash = ht_hash(key);
+  size_t index = hash % table->capacity;
+
+  size_t i = index;
+
+  while (table->entries[i].key) {
+    if (strcmp(table->entries[i].key, key) == 0)
+      return 1;
+
+    i++;
+
+    if (i >= table->capacity)
+      i = 0;
+
+    if (i == index)
+      break;
+  }
+
+  return 0;
+}
+
 static void table_entry_set(entry_t *entries, char *key, void *value, size_t capacity, size_t *used) {
   unsigned long hash = ht_hash(key);
   size_t index = hash % capacity;
@@ -1025,6 +1060,7 @@ struct _node_t {
     N_MVAR,
 
     N_MIF,
+    N_MFOR,
     N_MSET,
     N_MSTMT,
     N_MEXPR
@@ -1048,8 +1084,6 @@ struct _node_t {
 
   size_t fi;
   size_t pos;
-
-  int flag;
 };
 typedef struct _node_t node_t;
 
@@ -1060,6 +1094,13 @@ node_t *node_pos(node_t *node, size_t fi, size_t pos) {
   return node;
 }
 
+node_t *node_copy(node_t *node) {
+  node_t *nnode = malloc_checked(sizeof(node_t));
+  memcpy(nnode, node, sizeof(node_t));
+
+  return nnode;
+}
+
 node_t *nodet(int tag, token_t *t) {
   node_t *node = malloc_checked(sizeof(node_t));
 
@@ -2396,14 +2437,7 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
     return NODET(INLINE, t);
   }
 
-  node_t *n = parse_expr(tokens, pos);
-  if (n->tag == N_MVAR) {
-    n->flag = 1;
-
-    return n;
-  }
-
-  return NODE1(EXPRSTMT, n);
+  return NODE1(EXPRSTMT, parse_expr(tokens, pos));
 }
 
 #define ATM(t) (AT(MVAR) && strcmp(((token_t *)tokens->data[*pos])->text, t) == 0)
@@ -2516,12 +2550,9 @@ node_t *parse_mif(list_t *tokens, size_t *pos, int sub) {
   list_t *l = list_new();
   list_t *l2 = NULL;
 
-  while (!AT(EOF) && !ATM("else") && !ATM("elif") && !ATM("endif")) {
+  while (!AT(EOF) && !ATM("else") && !ATM("elif") && !ATM("endif"))
     list_push(l, parse_mstmt(tokens, pos));
 
-    MATCH(SEMI);
-  }
-
   if (MATCHM("elif")) {
     l2 = list_new();
 
@@ -2529,11 +2560,8 @@ node_t *parse_mif(list_t *tokens, size_t *pos, int sub) {
   } else if (MATCHM("else")) {
     l2 = list_new();
 
-    while (!AT(EOF) && !ATM("endif")) {
+    while (!AT(EOF) && !ATM("endif"))
       list_push(l2, parse_mstmt(tokens, pos));
-
-      MATCH(SEMI);
-    }
   }
 
   if (!sub && !MATCHM("endif"))
@@ -2545,7 +2573,18 @@ node_t *parse_mif(list_t *tokens, size_t *pos, int sub) {
 node_t *parse_mstmt(list_t *tokens, size_t *pos) {
   if (MATCHM("if"))
     return parse_mif(tokens, pos, 0);
-  else if (MATCHM("set")) {
+  else if (MATCHM("for")) {
+    node_t *a = parse_mexpr(tokens, pos);
+    list_t *l = list_new();
+
+    while (!AT(EOF) && !ATM("endfor"))
+      list_push(l, parse_mstmt(tokens, pos));
+
+    if (!MATCHM("endfor"))
+    PARSE_ERROR("expected $endfor");
+
+    return NODE1l(MFOR, a, l);
+  } else if (MATCHM("set")) {
     if(!AT(NAME))
       PARSE_ERROR("expected identifier");
 
@@ -2658,12 +2697,9 @@ node_t *parse_program(list_t *tokens, size_t *pos) {
       else {
         EXPECT(LCB, "{");
 
-        while (!AT(EOF) && !AT(RCB)) {
+        while (!AT(EOF) && !AT(RCB))
           list_push(body, parse_mstmt(tokens, pos));
 
-          MATCH(SEMI);
-        }
-
         EXPECT(RCB, "}");
       }
 
@@ -3494,6 +3530,7 @@ buffer_t *HBUF;
 list_t *MVARS;
 node_t *YES;
 
+#define IS_EXPR(n) ((n) && ((node_t *)(n))->tag > N_EXPRS_BEGIN && ((node_t *)(n))->tag < N_EXPRS_END)
 #define IS_NUMBER(n) ((n) && (n)->tag == N_LITERAL && (n)->t->tag == T_NUMBER)
 #define TO_DOUBLE(n) (strtod((n)->t->text, NULL))
 #define IS_STRING(n) ((n) && (n)->tag == N_LITERAL && (n)->t->tag == T_STRING)
@@ -3566,7 +3603,7 @@ node_t *mf_isList(list_t *t) {
 
 node_t *mf_newList(list_t *t) {
   for (size_t i = 0; i < t->length; i++)
-    if (!t->data[i])
+    if (!IS_EXPR(t->data[i]))
       return NULL;
 
   return nodel(N_LIST, t);
@@ -3603,7 +3640,7 @@ node_t *mf_newVar(list_t *t) {
   if (t->length == 2) {
     b = t->data[1];
 
-    if (!(b->tag > N_EXPRS_BEGIN && b->tag < N_EXPRS_END))
+    if (!IS_EXPR(b))
       return NULL;
   }
 
@@ -3818,6 +3855,11 @@ node_t *run_mnode(node_t *node) {
         r = run_mnodes(node->l2);
       break;
 
+    case N_MFOR:
+      while (run_mexpr(node->a)) 
+        if ((r = run_mnodes(node->l))) break;
+      break;
+
     case N_MSET:
       table_set(list_index(MVARS, -1), node->t->text, run_mexpr(node->a));
       break;
@@ -3858,6 +3900,98 @@ node_t *run_macro(macro_t *macro, list_t *args) {
   return r;
 }
 
+typedef struct {
+  char *mvar;
+  int code;
+} mvar_expand_err_t;
+
+node_t *_expand_mvars(node_t *node, int expr, mvar_expand_err_t *err) {
+  if (node->tag == N_LITERAL) return node;
+
+  if (node->tag == N_MVAR) {
+    char *name = node->t->text;
+    err->mvar = name;
+
+    table_t *mvars = list_index(MVARS, -1);
+
+    if (!table_has(mvars, name)) {
+      err->code = 1; return NULL;
+    }
+
+    node_t *n = table_get(mvars, name);
+
+    if (n) {
+      if (expr && !IS_EXPR(n)) {
+        err->code = 2; return NULL;
+      }
+
+      if (!expr && IS_EXPR(n))
+        n = node1(N_EXPRSTMT, n);
+    } else { err->code = 3; return NULL; }
+
+    return n;
+  }
+
+  if (node->tag == N_EXPRSTMT) {
+    node_t *n = _expand_mvars(node->a, 0, err);
+    if (err->code != 0) return NULL;
+
+    if (!IS_EXPR(n))
+      return n;
+
+    node = node_copy(node);
+    node->a = n;
+
+    return node;
+  } else if (node->tag >= N_ADD && node->tag <= N_BAND) {
+    node = node_copy(node);
+
+    node->a = _expand_mvars(node->a, 1, err);
+    if (err->code != 0) return NULL;
+
+    node->b = _expand_mvars(node->b, 1, err);
+    if (err->code != 0) return NULL;
+
+    return node;
+  } else if (node->tag == N_PROGRAM || node->tag == N_LIST) {
+    node = node_copy(node);
+    node->l = list_copy(node->l);
+
+    for (size_t i = 0; i < node->l->length; i++) {
+      node_t *n = _expand_mvars(node->l->data[i], node->tag == N_LIST, err);
+      if (err->code != 0) return NULL;
+
+      node->l->data[i] = n;
+    }
+
+    return node;
+  }
+
+  err->code = 4;
+
+  return NULL;
+}
+
+node_t *expand_mvars(node_t *node, node_t *_node, int expr) {
+  mvar_expand_err_t err = {NULL, 0};
+
+  node_t *r = _expand_mvars(node_copy(_node), expr, &err);
+  if (err.code == 1) {
+    COMPILE_ERROR("undefined macro variable: '%s'", err.mvar);
+  } else if (err.code == 2) {
+    COMPILE_ERROR("macro variable '%s' expanded to non-expression in expression context", err.mvar);
+  } else if (err.code == 3) {
+    COMPILE_ERROR("macro variable '%s' expanded to nil", err.mvar);
+  } else if (err.code == 4) {
+    COMPILE_ERROR("macro variable expansion failed because it is not yet implemented for this context");
+  }
+
+  if (!r)
+    COMPILE_ERROR("macro variable expansion failed");
+
+  return r;
+}
+
 void compile_macro_call(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, int_stack_t *lstk, int_stack_t *sstk, list_t *lbl, node_t *node, int expr) {
   size_t argc = node->l->length;
   char *name = node->t->text;
@@ -3877,14 +4011,16 @@ void compile_macro_call(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *lta
   node_t *r = run_macro(m, node->l);
 
   if (r) {
-    if (expr && !(r->tag > N_EXPRS_BEGIN && r->tag < N_EXPRS_END))
+    r = expand_mvars(node, r, expr);
+
+    if (expr && !IS_EXPR(r))
       COMPILE_ERROR("macro '%s' expanded to non-expression in expression context", name);
 
-    if (!expr && r->tag > N_EXPRS_BEGIN && r->tag < N_EXPRS_END)
+    if (!expr && IS_EXPR(r))
       r = node1(N_EXPRSTMT, r);
 
     compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, r);
-  }
+  } else COMPILE_ERROR("macro '%s' expanded to nil", name);
 
   list_pop(MVARS);
 }
@@ -3896,7 +4032,8 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, int
       break;
 
     case N_EXPRSTMT:
-      if (node->a->tag == N_MACRO_CALL) {
+      if (node->a->tag == N_LITERAL && node->a->t->tag != T_NAME) break;
+      else if (node->a->tag == N_MACRO_CALL) {
         compile_macro_call(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a, 0);
 
         break;
@@ -4818,21 +4955,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, int
       break;
     
     case N_MACRO_CALL: compile_macro_call(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node, 1); break;
-    case N_MVAR: {
-      if (!MVARS->length)
-        COMPILE_ERROR("macro variable outside of a macro");
-
-      table_t *mvars = list_index(MVARS, -1);
-      node_t *n = table_get(mvars, node->t->text);
-
-      if (!n)
-        COMPILE_ERROR("undefined macro variable: '%s'", node->t);
-
-      if (node->flag)
-        n = node1(N_EXPRSTMT, n);
-
-      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, n);
-    } break;
+    case N_MVAR: COMPILE_ERROR("macro variable outside of a macro");
 
     case N_INLINE: {
       char *text = unescape(node->t->text);