diff --git a/filter/config.Y b/filter/config.Y index a71f6e0a..0014b4bd 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -1000,8 +1000,7 @@ cmd: { new_config->current_scope->active = 0; } term { new_config->current_scope->active = 1; } DO cmd { cf_pop_block_scope(new_config); - $$ = f_new_inst(FI_FOR_INIT, $6, $3); - $$->next = f_new_inst(FI_FOR_NEXT, $3, $9); + $$ = f_for_cycle($3, $6, $9); } | symbol_known '=' term ';' { switch ($1->class) { diff --git a/filter/decl.m4 b/filter/decl.m4 index e38cef23..db5c0ff5 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -130,7 +130,7 @@ m4_ifelse($1,1,,[[FID_NEW_METHOD()m4_dnl struct f_inst *arg$1 = args; if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */ args = args->next; - FID_METHOD_CALL() , arg$1]]) +FID_METHOD_CALL() , arg$1]]) FID_LINEARIZE_BODY()m4_dnl pos = linearize(dest, whati->f$1, pos); FID_INTERPRET_BODY()') @@ -230,6 +230,12 @@ FID_NEW_ARGS()m4_dnl , struct f_inst * f$1 FID_NEW_BODY()m4_dnl whati->f$1 = f$1; +m4_define([[INST_METHOD_NUM_ARGS]],m4_eval($1-1))m4_dnl +FID_NEW_METHOD()m4_dnl + struct f_inst *arg$1 = args; + if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */ + args = NULL; /* The rest is the line itself */ +FID_METHOD_CALL() , arg$1 FID_DUMP_BODY()m4_dnl f_dump_line(item->fl$1, indent + 1); FID_LINEARIZE_BODY()m4_dnl diff --git a/filter/f-inst.c b/filter/f-inst.c index 452419ee..fb73ca06 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -569,92 +569,63 @@ RESULT(T_LCLIST, ad, &null_adata); } - INST(FI_FOR_INIT, 1, 0) { + /* Common loop begin instruction, always created by f_for_cycle() */ + INST(FI_FOR_LOOP_START, 0, 3) { NEVER_CONSTANT; - ARG_ANY(1); SYMBOL; - FID_NEW_BODY() - ASSERT((sym->class & ~0xff) == SYM_VARIABLE); + /* Repeat the instruction which called us */ + ASSERT_DIE(fstk->ecnt > 1); + prevline.pos--; - /* Static type check */ - if (f1->type) - { - enum f_type t_var = (sym->class & 0xff); - enum f_type t_arg = f_type_element_type(f1->type); - if (!t_arg) - cf_error("Value of expression in FOR must be iterable, got %s", - f_type_name(f1->type)); - if (t_var != t_arg) - cf_error("Loop variable '%s' in FOR must be %s, is %s", - sym->name, f_type_name(t_arg), f_type_name(t_var)); - } + /* There should be exactly three items on the value stack to be taken care of */ + fstk->vcnt += 3; - FID_INTERPRET_BODY() + /* And these should also stay there after we finish for the caller instruction */ + curline.ventry += 3; - /* Dynamic type check */ - if ((sym->class & 0xff) != f_type_element_type(v1.type)) - runtime("Mismatched argument and variable type"); + /* Assert the iterator variable positioning */ + ASSERT_DIE(curline.vbase + sym->offset == fstk->vcnt - 1); - /* Setup the index */ - v2 = (struct f_val) { .type = T_INT, .val.i = 0 }; - - /* Keep v1 and v2 on the stack */ - fstk->vcnt += 2; + /* The result type declaration makes no sense here but is needed */ + RESULT_TYPE(T_VOID); } - INST(FI_FOR_NEXT, 2, 0) { + /* Type-specific for_next iterators */ + INST(FI_PATH_FOR_NEXT, 3, 0) { NEVER_CONSTANT; - SYMBOL; + ARG(1, T_PATH); + if (as_path_walk(v1.val.ad, &v2.val.i, &v3.val.i)) + LINE(2,0); - /* Type checks are done in FI_FOR_INIT */ + METHOD_CONSTRUCTOR("!for_next"); + } - /* Loop variable */ - struct f_val *var = &fstk->vstk[curline.vbase + sym->offset]; - int step = 0; + INST(FI_CLIST_FOR_NEXT, 3, 0) { + NEVER_CONSTANT; + ARG(1, T_CLIST); + if (int_set_walk(v1.val.ad, &v2.val.i, &v3.val.i)) + LINE(2,0); - switch(v1.type) - { - case T_PATH: - var->type = T_INT; - step = as_path_walk(v1.val.ad, &v2.val.i, &var->val.i); - break; + METHOD_CONSTRUCTOR("!for_next"); + } - case T_CLIST: - var->type = T_PAIR; - step = int_set_walk(v1.val.ad, &v2.val.i, &var->val.i); - break; + INST(FI_ECLIST_FOR_NEXT, 3, 0) { + NEVER_CONSTANT; + ARG(1, T_ECLIST); + if (ec_set_walk(v1.val.ad, &v2.val.i, &v3.val.ec)) + LINE(2,0); - case T_ECLIST: - var->type = T_EC; - step = ec_set_walk(v1.val.ad, &v2.val.i, &var->val.ec); - break; + METHOD_CONSTRUCTOR("!for_next"); + } - case T_LCLIST: - var->type = T_LC; - step = lc_set_walk(v1.val.ad, &v2.val.i, &var->val.lc); - break; + INST(FI_LCLIST_FOR_NEXT, 3, 0) { + NEVER_CONSTANT; + ARG(1, T_LCLIST); + if (lc_set_walk(v1.val.ad, &v2.val.i, &v3.val.lc)) + LINE(2,0); - default: - runtime( "Clist or lclist expected" ); - } - - if (step) - { - /* Keep v1 and v2 on the stack */ - fstk->vcnt += 2; - - /* Repeat this instruction */ - curline.pos--; - - /* Execute the loop body */ - LINE(1, 0); - - /* Space for loop variable, may be unused */ - fstk->vcnt += 1; - } - else - var->type = T_VOID; + METHOD_CONSTRUCTOR("!for_next"); } INST(FI_CONDITION, 1, 0) { diff --git a/filter/f-inst.h b/filter/f-inst.h index 67309a90..faee95b0 100644 --- a/filter/f-inst.h +++ b/filter/f-inst.h @@ -95,6 +95,8 @@ void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit); struct filter *f_new_where(struct f_inst *); +struct f_inst *f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block); + static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ { return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ diff --git a/filter/f-util.c b/filter/f-util.c index d814493e..ab5b9094 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -41,6 +41,40 @@ struct filter *f_new_where(struct f_inst *where) return f; } +struct f_inst * +f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block) +{ + ASSERT((var->class & ~0xff) == SYM_VARIABLE); + ASSERT(term->next == NULL); + + /* Static type check */ + if (term->type == T_VOID) + cf_error("Couldn't infer the type of FOR expression, please assign it to a variable."); + + enum f_type el_type = f_type_element_type(term->type); + struct sym_scope *scope = el_type ? f_type_method_scope(term->type) : NULL; + struct symbol *ms = scope ? cf_find_symbol_scope(scope, "!for_next") : NULL; + + if (!ms) + cf_error("Type %s is not iterable, can't be used in FOR", f_type_name(term->type)); + + if (var->class != (SYM_VARIABLE | el_type)) + cf_error("Loop variable '%s' in FOR must be of type %s, got %s", + var->name, f_type_name(el_type), f_type_name(var->class & 0xff)); + + /* Push the iterator auxiliary value onto stack */ + struct f_inst *iter = term->next = f_new_inst(FI_CONSTANT, (struct f_val) {}); + + /* Initialize the iterator variable */ + iter->next = f_new_inst(FI_CONSTANT, (struct f_val) { .type = el_type }); + + /* Prepend the loop block with loop beginning instruction */ + struct f_inst *loop_start = f_new_inst(FI_FOR_LOOP_START, var); + loop_start->next = block; + + return ms->method->new_inst(term, loop_start); +} + #define CA_KEY(n) n->name, n->fda.type #define CA_NEXT(n) n->next #define CA_EQ(na,ta,nb,tb) (!strcmp(na,nb) && (ta == tb)) diff --git a/filter/filter.c b/filter/filter.c index 20a380dc..74fc904e 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -174,6 +174,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) fstk->estk[0].pos = 0; #define curline fstk->estk[fstk->ecnt-1] +#define prevline fstk->estk[fstk->ecnt-2] #ifdef LOCAL_DEBUG debug("Interpreting line.");