|
@@ -1061,6 +1061,7 @@ struct _node_t {
|
|
|
|
|
|
N_MIF,
|
|
N_MIF,
|
|
N_MFOR,
|
|
N_MFOR,
|
|
|
|
+ N_MREPEAT,
|
|
N_MSET,
|
|
N_MSET,
|
|
N_MPUSH,
|
|
N_MPUSH,
|
|
N_MSTMT,
|
|
N_MSTMT,
|
|
@@ -1308,6 +1309,8 @@ node_t *nodeb(buffer_t *buf) {
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
typedef struct {
|
|
|
|
+ char *name;
|
|
|
|
+
|
|
list_t *params;
|
|
list_t *params;
|
|
list_t *body;
|
|
list_t *body;
|
|
int variable;
|
|
int variable;
|
|
@@ -2613,6 +2616,17 @@ node_t *parse_mstmt(list_t *tokens, size_t *pos) {
|
|
PARSE_ERROR("expected $endfor");
|
|
PARSE_ERROR("expected $endfor");
|
|
|
|
|
|
return NODE1l(MFOR, a, l);
|
|
return NODE1l(MFOR, a, l);
|
|
|
|
+ } else if (MATCHM("repeat")) {
|
|
|
|
+ node_t *a = parse_mexpr(tokens, pos);
|
|
|
|
+ list_t *l = list_new();
|
|
|
|
+
|
|
|
|
+ while (!AT(EOF) && !ATM("endrep"))
|
|
|
|
+ list_push(l, parse_mstmt(tokens, pos));
|
|
|
|
+
|
|
|
|
+ if (!MATCHM("endrep"))
|
|
|
|
+ PARSE_ERROR("expected $endrep");
|
|
|
|
+
|
|
|
|
+ return NODE1l(MREPEAT, a, l);
|
|
} else if (MATCHM("set")) {
|
|
} else if (MATCHM("set")) {
|
|
if(!AT(NAME))
|
|
if(!AT(NAME))
|
|
PARSE_ERROR("expected identifier");
|
|
PARSE_ERROR("expected identifier");
|
|
@@ -2652,9 +2666,12 @@ node_t *parse_mstmt(list_t *tokens, size_t *pos) {
|
|
} else if (MATCHM(":"))
|
|
} else if (MATCHM(":"))
|
|
return NODE1(MEXPR, parse_mexpr(tokens, pos));
|
|
return NODE1(MEXPR, parse_mexpr(tokens, pos));
|
|
|
|
|
|
- PARSE_ERROR("expected m-statement");
|
|
|
|
|
|
+ node_t *a = parse_stmt(tokens, pos);
|
|
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ if (a->tag == N_EXPRSTMT)
|
|
|
|
+ a = a->a;
|
|
|
|
+
|
|
|
|
+ return a;
|
|
}
|
|
}
|
|
|
|
|
|
list_t *macros_get(char *name) {
|
|
list_t *macros_get(char *name) {
|
|
@@ -2758,6 +2775,7 @@ node_t *parse_program(list_t *tokens, size_t *pos) {
|
|
}
|
|
}
|
|
|
|
|
|
macro_t *m = malloc_checked(sizeof(macro_t));
|
|
macro_t *m = malloc_checked(sizeof(macro_t));
|
|
|
|
+ m->name = name;
|
|
m->params = params;
|
|
m->params = params;
|
|
m->body = body;
|
|
m->body = body;
|
|
m->variable = variable;
|
|
m->variable = variable;
|
|
@@ -2817,6 +2835,7 @@ node_t *parse(char *source) {
|
|
});\
|
|
});\
|
|
}
|
|
}
|
|
#define COMPILE_ERROR(fmt, ...) { format_error(GETFNAME(node->fi), GETSRC(node->fi), node->pos, fmt, ##__VA_ARGS__); exit(1); }
|
|
#define COMPILE_ERROR(fmt, ...) { format_error(GETFNAME(node->fi), GETSRC(node->fi), node->pos, fmt, ##__VA_ARGS__); exit(1); }
|
|
|
|
+#define COMPILE_NOTE(fmt, ...) { format_error(GETFNAME(node->fi), GETSRC(node->fi), node->pos, fmt, ##__VA_ARGS__); }
|
|
|
|
|
|
void compile_node(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);
|
|
void compile_node(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);
|
|
|
|
|
|
@@ -3758,6 +3777,16 @@ node_t *mf_newList(list_t *t) {
|
|
return nodel(N_LIST, t);
|
|
return nodel(N_LIST, t);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+node_t *mf_newBlock(list_t *t) {
|
|
|
|
+ for (size_t i = 0; i < t->length; i++)
|
|
|
|
+ if (!t->data[i])
|
|
|
|
+ return NULL;
|
|
|
|
+ else if (IS_EXPR(t->data[i]))
|
|
|
|
+ t->data[i] = node1(N_EXPRSTMT, t->data[i]);
|
|
|
|
+
|
|
|
|
+ return nodel(N_PROGRAM, t);
|
|
|
|
+}
|
|
|
|
+
|
|
node_t *mf_newName(list_t *t) {
|
|
node_t *mf_newName(list_t *t) {
|
|
if (t->length != 1)
|
|
if (t->length != 1)
|
|
return NULL;
|
|
return NULL;
|
|
@@ -3860,6 +3889,7 @@ struct {
|
|
|
|
|
|
{ "newName", mf_newName },
|
|
{ "newName", mf_newName },
|
|
{ "newList", mf_newList },
|
|
{ "newList", mf_newList },
|
|
|
|
+ { "newBlock", mf_newBlock },
|
|
{ "newVar", mf_newVar },
|
|
{ "newVar", mf_newVar },
|
|
|
|
|
|
{ "len", mf_len },
|
|
{ "len", mf_len },
|
|
@@ -3903,7 +3933,7 @@ macro_t *find_macro(char *name, size_t argc, int *res) {
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
typedef struct {
|
|
- char *mvar;
|
|
|
|
|
|
+ node_t *mvar;
|
|
int code;
|
|
int code;
|
|
} mvar_expand_err_t;
|
|
} mvar_expand_err_t;
|
|
|
|
|
|
@@ -4052,16 +4082,40 @@ node_t *run_mnode(node_t *node) {
|
|
if ((r = run_mnodes(node->l))) break;
|
|
if ((r = run_mnodes(node->l))) break;
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ case N_MREPEAT: {
|
|
|
|
+ list_t *l = list_new();
|
|
|
|
+
|
|
|
|
+ while (run_mexpr(node->a)) {
|
|
|
|
+ node_t *n = run_mnodes(node->l);
|
|
|
|
+
|
|
|
|
+ if (!n) return NULL;
|
|
|
|
+
|
|
|
|
+ if (IS_EXPR(n))
|
|
|
|
+ n = node1(N_EXPRSTMT, n);
|
|
|
|
+
|
|
|
|
+ list_push(l, n);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ r = nodel(N_PROGRAM, l);
|
|
|
|
+ } break;
|
|
|
|
+
|
|
case N_MSET:
|
|
case N_MSET:
|
|
table_set(list_index(MVARS, -1), node->t->text, run_mexpr(node->a));
|
|
table_set(list_index(MVARS, -1), node->t->text, run_mexpr(node->a));
|
|
break;
|
|
break;
|
|
|
|
|
|
case N_MPUSH: {
|
|
case N_MPUSH: {
|
|
node_t *l = table_get(list_index(MVARS, -1), node->t->text);
|
|
node_t *l = table_get(list_index(MVARS, -1), node->t->text);
|
|
- if (l && l->tag == N_LIST) {
|
|
|
|
|
|
+ if (l && (l->tag == N_LIST || l->tag == N_PROGRAM)) {
|
|
node_t *n = run_mexpr(node->a);
|
|
node_t *n = run_mexpr(node->a);
|
|
- if (IS_EXPR(n))
|
|
|
|
- list_push(l->l, n);
|
|
|
|
|
|
+ if (!n) break;
|
|
|
|
+
|
|
|
|
+ if (IS_EXPR(n)) {
|
|
|
|
+ if (l->tag == N_PROGRAM)
|
|
|
|
+ n = node1(N_EXPRSTMT, n);
|
|
|
|
+ else break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ list_push(l->l, n);
|
|
}
|
|
}
|
|
} break;
|
|
} break;
|
|
|
|
|
|
@@ -4074,13 +4128,22 @@ node_t *run_mnode(node_t *node) {
|
|
break;
|
|
break;
|
|
|
|
|
|
default:
|
|
default:
|
|
- COMPILE_ERROR("not yet implemented");
|
|
|
|
|
|
+ r = node;
|
|
}
|
|
}
|
|
|
|
|
|
return r;
|
|
return r;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+list_t *macro_stk;
|
|
|
|
+size_t expand_depth = 0;
|
|
|
|
+size_t macro_rlimit = 100;
|
|
|
|
+
|
|
node_t *run_macro(macro_t *macro, list_t *args) {
|
|
node_t *run_macro(macro_t *macro, list_t *args) {
|
|
|
|
+ if (macro_stk->length >= macro_rlimit)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ list_push(macro_stk, macro);
|
|
|
|
+
|
|
table_t *mvars = list_index(MVARS, -1);
|
|
table_t *mvars = list_index(MVARS, -1);
|
|
table_set(mvars, "nil", NULL);
|
|
table_set(mvars, "nil", NULL);
|
|
table_set(mvars, "*", nodel(N_LIST, args));
|
|
table_set(mvars, "*", nodel(N_LIST, args));
|
|
@@ -4097,6 +4160,8 @@ node_t *run_macro(macro_t *macro, list_t *args) {
|
|
if (r) break;
|
|
if (r) break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ list_pop(macro_stk);
|
|
|
|
+
|
|
return r;
|
|
return r;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4105,7 +4170,7 @@ node_t *_expand_mvars(node_t *node, int expr, mvar_expand_err_t *err) {
|
|
|
|
|
|
if (node->tag == N_MVAR) {
|
|
if (node->tag == N_MVAR) {
|
|
char *name = node->t->text;
|
|
char *name = node->t->text;
|
|
- err->mvar = name;
|
|
|
|
|
|
+ err->mvar = node;
|
|
|
|
|
|
table_t *mvars = list_index(MVARS, -1);
|
|
table_t *mvars = list_index(MVARS, -1);
|
|
|
|
|
|
@@ -4159,6 +4224,56 @@ node_t *_expand_mvars(node_t *node, int expr, mvar_expand_err_t *err) {
|
|
node->l->data[i] = n;
|
|
node->l->data[i] = n;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ return node;
|
|
|
|
+ } else if (node->tag == N_IF) {
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ if (node->c) {
|
|
|
|
+ node->c = _expand_mvars(node->c, 1, err);
|
|
|
|
+ if (err->code != 0) return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return node;
|
|
|
|
+ } else if (node->tag == N_CALL) {
|
|
|
|
+ node = node_copy(node);
|
|
|
|
+
|
|
|
|
+ node->a = _expand_mvars(node->a, 1, err);
|
|
|
|
+ if (err->code != 0) return NULL;
|
|
|
|
+
|
|
|
|
+ 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], 1, err);
|
|
|
|
+ if (err->code != 0) return NULL;
|
|
|
|
+
|
|
|
|
+ node->l->data[i] = n;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return node;
|
|
|
|
+ } else if (node->tag == N_MACRO_CALL) {
|
|
|
|
+ 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], 1, err);
|
|
|
|
+ if (err->code != 0) return NULL;
|
|
|
|
+
|
|
|
|
+ node->l->data[i] = n;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return node;
|
|
|
|
+ } else if (node->tag == N_INC || node->tag == N_DEC) {
|
|
|
|
+ node = node_copy(node);
|
|
|
|
+
|
|
|
|
+ node->a = _expand_mvars(node->a, 1, err);
|
|
|
|
+ if (err->code != 0) return NULL;
|
|
|
|
+
|
|
return node;
|
|
return node;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4171,12 +4286,18 @@ node_t *expand_mvars(node_t *node, node_t *_node, int expr) {
|
|
mvar_expand_err_t err = {NULL, 0};
|
|
mvar_expand_err_t err = {NULL, 0};
|
|
|
|
|
|
node_t *r = _expand_mvars(_node, expr, &err);
|
|
node_t *r = _expand_mvars(_node, expr, &err);
|
|
|
|
+ if ((!r || err.code != 0) && err.mvar) {
|
|
|
|
+ COMPILE_NOTE("macro expansion failed");
|
|
|
|
+
|
|
|
|
+ node = err.mvar;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (err.code == 1) {
|
|
if (err.code == 1) {
|
|
- COMPILE_ERROR("undefined macro variable: '%s'", err.mvar);
|
|
|
|
|
|
+ COMPILE_ERROR("undefined macro variable: '%s'", err.mvar->t->text);
|
|
} else if (err.code == 2) {
|
|
} else if (err.code == 2) {
|
|
- COMPILE_ERROR("macro variable '%s' expanded to non-expression in expression context", err.mvar);
|
|
|
|
|
|
+ COMPILE_ERROR("macro variable '%s' expanded to non-expression in expression context", err.mvar->t->text);
|
|
} else if (err.code == 3) {
|
|
} else if (err.code == 3) {
|
|
- COMPILE_ERROR("macro variable '%s' expanded to nil", err.mvar);
|
|
|
|
|
|
+ COMPILE_ERROR("macro variable '%s' expanded to nil", err.mvar->t->text);
|
|
} else if (err.code == 4) {
|
|
} else if (err.code == 4) {
|
|
COMPILE_ERROR("macro variable expansion failed because it is not yet implemented for this context");
|
|
COMPILE_ERROR("macro variable expansion failed because it is not yet implemented for this context");
|
|
}
|
|
}
|
|
@@ -4203,11 +4324,29 @@ void compile_macro_call(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *lta
|
|
|
|
|
|
list_push(MVARS, table_new());
|
|
list_push(MVARS, table_new());
|
|
|
|
|
|
|
|
+ if (macro_stk->length >= macro_rlimit)
|
|
|
|
+ COMPILE_ERROR("recursion depth limit exceeded while expanding macro: '%s'", name);
|
|
|
|
+
|
|
node_t *r = run_macro(m, node->l);
|
|
node_t *r = run_macro(m, node->l);
|
|
|
|
|
|
if (r) {
|
|
if (r) {
|
|
r = expand_mvars(node, r, expr);
|
|
r = expand_mvars(node, r, expr);
|
|
|
|
|
|
|
|
+ if (r->tag == N_MACRO_CALL) {
|
|
|
|
+ list_pop(MVARS);
|
|
|
|
+
|
|
|
|
+ if (expand_depth >= macro_rlimit)
|
|
|
|
+ COMPILE_ERROR("recursion depth limit exceeded while expanding macro: '%s'", name);
|
|
|
|
+
|
|
|
|
+ expand_depth++;
|
|
|
|
+
|
|
|
|
+ compile_macro_call(gbuf, buf, ctx, ltab, lstk, sstk, lbl, r, expr);
|
|
|
|
+
|
|
|
|
+ expand_depth--;
|
|
|
|
+
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (expr && !IS_EXPR(r))
|
|
if (expr && !IS_EXPR(r))
|
|
COMPILE_ERROR("macro '%s' expanded to non-expression in expression context", name);
|
|
COMPILE_ERROR("macro '%s' expanded to non-expression in expression context", name);
|
|
|
|
|
|
@@ -5297,6 +5436,8 @@ int main(int argc, char **argv) {
|
|
MACROS = list_new();
|
|
MACROS = list_new();
|
|
list_push(MACROS, table_new());
|
|
list_push(MACROS, table_new());
|
|
|
|
|
|
|
|
+ macro_stk = list_new();
|
|
|
|
+
|
|
HBUF = buffer_new();
|
|
HBUF = buffer_new();
|
|
MVARS = list_new();
|
|
MVARS = list_new();
|
|
|
|
|