|
@@ -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);
|