From 1d38726c646ee3a4b6431d55f921fa6ede92fca1 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Sat, 17 Jun 2023 13:05:23 +0200 Subject: [PATCH 1/4] Removing unused terminals from filter config --- filter/config.Y | 4 ---- 1 file changed, 4 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index c15814fd..ff7348f1 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -370,10 +370,6 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, FROM_HEX, BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT) -CF_METHODS(IS_V4, TYPE, IP, RD, LEN, MAXLEN, ASN, SRC, DST, MASK, - FIRST, LAST, LAST_NONAGGREGATED, DATA, DATA1, DATA2, MIN, MAX, - EMPTY, PREPEND, ADD, DELETE, FILTER) - %nonassoc THEN %nonassoc ELSE From 21faa54ec3251cb730a23a663ebf7775834ee7a8 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Sun, 18 Jun 2023 22:50:45 +0200 Subject: [PATCH 2/4] Filter: The for loop uses the method system for type dispatch --- filter/config.Y | 3 +- filter/decl.m4 | 8 +++- filter/f-inst.c | 109 ++++++++++++++++++------------------------------ filter/f-inst.h | 2 + filter/f-util.c | 34 +++++++++++++++ filter/filter.c | 1 + 6 files changed, 85 insertions(+), 72 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index ff7348f1..08c53367 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -1008,8 +1008,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 46429f6a..8fcbe874 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 16d01e4d..34e18ca4 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -581,92 +581,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 0913ace6..55272737 100644 --- a/filter/f-inst.h +++ b/filter/f-inst.h @@ -96,6 +96,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 d080cadc..65fb92a4 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."); From 6d411fc7bdd82ee40a96fee1ded228fa102b62c9 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Mon, 19 Jun 2023 15:49:51 +0200 Subject: [PATCH 3/4] Filter: Shortened method declarations --- filter/decl.m4 | 15 +++++ filter/f-inst.c | 143 ++++++++++++------------------------------------ 2 files changed, 51 insertions(+), 107 deletions(-) diff --git a/filter/decl.m4 b/filter/decl.m4 index 8fcbe874..d292f65b 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -292,6 +292,21 @@ m4_define([[INST_IS_METHOD]]) m4_define([[INST_METHOD_NAME]],$1) FID_INTERPRET_BODY()') +# Short method constructor +# $1 = type +# $2 = name +# $3 = method inputs +# method outputs are always 1 +# $4 = code +m4_define(METHOD, `m4_dnl +INST([[FI_METHOD__]]$1[[__]]$2, m4_eval($3 + 1), 1) { + ARG(1, $1); + $4 + METHOD_CONSTRUCTOR("$2"); +}') + +m4_define(METHOD_R, `METHOD($1, $2, $3, [[ RESULT($4, $5, $6) ]])') + # 2) Code wrapping # The code produced in 1xx temporary diversions is a raw code without # any auxiliary commands and syntactical structures around. When the diff --git a/filter/f-inst.c b/filter/f-inst.c index 34e18ca4..f2519efb 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -101,6 +101,11 @@ * * Other code is just copied into the interpreter part. * + * It's also possible to declare type methods in a short way: + * + * m4_dnl METHOD(type, method name, argument count, code) + * m4_dnl METHOD_R(type, method name, argument count, result type, union-field, value) + * * The filter language uses a simple type system, where values have types * (constants T_*) and also terms (instructions) are statically typed. Our * static typing is partial (some terms do not declare types of arguments @@ -491,17 +496,8 @@ RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); } - INST(FI_NET_TYPE, 1, 1) { - ARG(1, T_NET); - METHOD_CONSTRUCTOR("type"); - RESULT(T_ENUM_NETTYPE, i, v1.val.net->type); - } - - INST(FI_IS_V4, 1, 1) { - ARG(1, T_IP); - METHOD_CONSTRUCTOR("is_v4"); - RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip)); - } + METHOD_R(T_NET, type, 0, T_ENUM_NETTYPE, i, v1.val.net->type); + METHOD_R(T_IP, is_v4, 0, T_BOOL, i, ipa_is_ip4(v1.val.ip)); /* Add initialized variable */ INST(FI_VAR_INIT, 1, 0) { @@ -557,29 +553,10 @@ RESULT_VAL(val); } - INST(FI_PATH_EMPTY, 1, 1) { - ARG(1, T_PATH); - METHOD_CONSTRUCTOR("empty"); - RESULT(T_PATH, ad, &null_adata); - } - - INST(FI_CLIST_EMPTY, 1, 1) { - ARG(1, T_CLIST); - METHOD_CONSTRUCTOR("empty"); - RESULT(T_CLIST, ad, &null_adata); - } - - INST(FI_ECLIST_EMPTY, 1, 1) { - ARG(1, T_ECLIST); - METHOD_CONSTRUCTOR("empty"); - RESULT(T_ECLIST, ad, &null_adata); - } - - INST(FI_LCLIST_EMPTY, 1, 1) { - ARG(1, T_LCLIST); - METHOD_CONSTRUCTOR("empty"); - RESULT(T_LCLIST, ad, &null_adata); - } + METHOD_R(T_PATH, empty, 0, T_PATH, ad, &null_adata); + METHOD_R(T_CLIST, empty, 0, T_CLIST, ad, &null_adata); + METHOD_R(T_ECLIST, empty, 0, T_ECLIST, ad, &null_adata); + METHOD_R(T_LCLIST, empty, 0, T_LCLIST, ad, &null_adata); /* Common loop begin instruction, always created by f_for_cycle() */ INST(FI_FOR_LOOP_START, 0, 3) { @@ -969,35 +946,12 @@ ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code); } - INST(FI_NET_LENGTH, 1, 1) { /* Get length of */ - ARG(1, T_NET); - METHOD_CONSTRUCTOR("len"); - RESULT(T_INT, i, net_pxlen(v1.val.net)); - } - - INST(FI_PATH_LENGTH, 1, 1) { /* Get length of */ - ARG(1, T_PATH); - METHOD_CONSTRUCTOR("len"); - RESULT(T_INT, i, as_path_getlen(v1.val.ad)); - } - - INST(FI_CLIST_LENGTH, 1, 1) { /* Get length of */ - ARG(1, T_CLIST); - METHOD_CONSTRUCTOR("len"); - RESULT(T_INT, i, int_set_get_size(v1.val.ad)); - } - - INST(FI_ECLIST_LENGTH, 1, 1) { /* Get length of */ - ARG(1, T_ECLIST); - METHOD_CONSTRUCTOR("len"); - RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); - } - - INST(FI_LCLIST_LENGTH, 1, 1) { /* Get length of */ - ARG(1, T_LCLIST); - METHOD_CONSTRUCTOR("len"); - RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); - } + /* Get length of */ + METHOD_R(T_NET, len, 0, T_INT, i, net_pxlen(v1.val.net)); + METHOD_R(T_PATH, len, 0, T_INT, i, as_path_getlen(v1.val.ad)); + METHOD_R(T_CLIST, len, 0, T_INT, i, int_set_get_size(v1.val.ad)); + METHOD_R(T_ECLIST, len, 0, T_INT, i, ec_set_get_size(v1.val.ad)); + METHOD_R(T_LCLIST, len, 0, T_INT, i, lc_set_get_size(v1.val.ad)); INST(FI_NET_SRC, 1, 1) { /* Get src prefix */ ARG(1, T_NET); @@ -1071,45 +1025,32 @@ RESULT(T_NET, net, dst); } - INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */ - ARG(1, T_NET); - METHOD_CONSTRUCTOR("maxlen") + /* Get ROA max prefix length */ + METHOD(T_NET, maxlen, 0, [[ if (!net_is_roa(v1.val.net)) runtime( "ROA expected" ); RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? ((net_addr_roa4 *) v1.val.net)->max_pxlen : ((net_addr_roa6 *) v1.val.net)->max_pxlen); - } + ]]); - INST(FI_NET_ASN, 1, 1) { /* Get ROA ASN or community ASN part */ - ARG(1, T_NET); - METHOD_CONSTRUCTOR("asn"); + /* Get ROA ASN or community ASN part */ + METHOD_R(T_PAIR, asn, 0, T_INT, i, v1.val.i >> 16); + METHOD_R(T_LC, asn, 0, T_INT, i, v1.val.lc.asn); + + METHOD(T_NET, asn, 0, [[ if (!net_is_roa(v1.val.net)) runtime( "ROA expected" ); RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? ((net_addr_roa4 *) v1.val.net)->asn : ((net_addr_roa6 *) v1.val.net)->asn); - } + ]]); - INST(FI_PAIR_ASN, 1, 1) { /* Get ROA ASN or community ASN part */ - ARG(1, T_PAIR); - METHOD_CONSTRUCTOR("asn"); - RESULT(T_INT, i, v1.val.i >> 16); - } - INST(FI_LC_ASN, 1, 1) { /* Get ROA ASN or community ASN part */ - ARG(1, T_LC); - METHOD_CONSTRUCTOR("asn"); - RESULT(T_INT, i, v1.val.lc.asn); - } - - INST(FI_NET_IP, 1, 1) { /* Convert prefix to ... */ - ARG(1, T_NET); - METHOD_CONSTRUCTOR("ip"); - RESULT(T_IP, ip, net_prefix(v1.val.net)); - } + /* Convert prefix to IP */ + METHOD_R(T_NET, ip, 0, T_IP, ip, net_prefix(v1.val.net)); INST(FI_ROUTE_DISTINGUISHER, 1, 1) { ARG(1, T_NET); @@ -1135,29 +1076,17 @@ RESULT(T_INT, i, as); } - INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */ - ARG(1, T_PATH); - METHOD_CONSTRUCTOR("last_nonaggregated"); - RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); - } + /* Get last ASN from non-aggregated part of AS PATH */ + METHOD_R(T_PATH, last_nonaggregated, 0, T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); - INST(FI_PAIR_DATA, 1, 1) { /* Get data part from the standard community */ - ARG(1, T_PAIR); - METHOD_CONSTRUCTOR("data"); - RESULT(T_INT, i, v1.val.i & 0xFFFF); - } + /* Get data part from the standard community */ + METHOD_R(T_PAIR, data, 0, T_INT, i, v1.val.i & 0xFFFF); - INST(FI_LC_DATA1, 1, 1) { /* Get data1 part from the large community */ - ARG(1, T_LC); - METHOD_CONSTRUCTOR("data1"); - RESULT(T_INT, i, v1.val.lc.ldp1); - } + /* Get data1 part from the large community */ + METHOD_R(T_LC, data1, 0, T_INT, i, v1.val.lc.ldp1); - INST(FI_LC_DATA2, 1, 1) { /* Get data2 part from the large community */ - ARG(1, T_LC); - METHOD_CONSTRUCTOR("data2"); - RESULT(T_INT, i, v1.val.lc.ldp2); - } + /* Get data2 part from the large community */ + METHOD_R(T_LC, data2, 0, T_INT, i, v1.val.lc.ldp2); INST(FI_CLIST_MIN, 1, 1) { /* Get minimum element from list */ ARG(1, T_CLIST); From fdd39c81bd4cb04dc9f0f5e5cc8db95909b76cbf Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Mon, 19 Jun 2023 17:24:30 +0200 Subject: [PATCH 4/4] Filter: Print instructions take only one value (simplification) --- filter/config.Y | 33 +++++++-------------------------- filter/f-inst.c | 9 ++++----- filter/f-inst.h | 1 + filter/f-util.c | 25 +++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index 08c53367..1f5d2bc5 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -374,7 +374,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc ELSE %type cmds_int cmd_prep -%type term term_bs cmd cmd_var cmds cmds_scoped constant constructor print_list var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont +%type term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont %type dynamic_attr %type static_attr %type filter where_filter @@ -963,16 +963,6 @@ break_command: | ERROR { $$ = F_ERROR; } ; -print_list: /* EMPTY */ { $$ = NULL; } - | term { $$ = $1; } - | term ',' print_list { - ASSERT($1); - ASSERT($1->next == NULL); - $1->next = $3; - $$ = $1; - } - ; - var: type symbol '=' term ';' { struct symbol *sym = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); @@ -1049,23 +1039,14 @@ cmd: | UNSET '(' dynamic_attr ')' ';' { $$ = f_new_inst(FI_EA_UNSET, $3); } - | break_command print_list ';' { - struct f_inst *breaker = f_new_inst(FI_DIE, $1); - if ($2) { - struct f_inst *printer = f_new_inst(FI_PRINT, $2); - struct f_inst *flusher = f_new_inst(FI_FLUSH); - printer->next = flusher; - flusher->next = breaker; - $$ = printer; - } else - $$ = breaker; + | break_command var_list ';' { + $$ = f_print($2, !!$2, $1); } - | PRINT print_list ';' { - $$ = f_new_inst(FI_PRINT, $2); - $$->next = f_new_inst(FI_FLUSH); + | PRINT var_list ';' { + $$ = f_print($2, 1, F_NOP); } - | PRINTN print_list ';' { - $$ = f_new_inst(FI_PRINT, $2); + | PRINTN var_list ';' { + $$ = f_print($2, 0, F_NOP); } | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); } | CASE term '{' switch_body '}' { diff --git a/filter/f-inst.c b/filter/f-inst.c index f2519efb..6977f656 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -625,13 +625,12 @@ LINE(3,0); } - INST(FI_PRINT, 0, 0) { + INST(FI_PRINT, 1, 0) { NEVER_CONSTANT; - VARARG; + ARG_ANY(1); - if (whati->varcount && !(fs->flags & FF_SILENT)) - for (uint i=0; ivarcount; i++) - val_format(&(vv(i)), &fs->buf); + if (!(fs->flags & FF_SILENT)) + val_format(&v1, &fs->buf); } INST(FI_FLUSH, 0, 0) { diff --git a/filter/f-inst.h b/filter/f-inst.h index 55272737..8c304de5 100644 --- a/filter/f-inst.h +++ b/filter/f-inst.h @@ -97,6 +97,7 @@ 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); +struct f_inst *f_print(struct f_inst *vars, int flush, enum filter_return fret); 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 */ diff --git a/filter/f-util.c b/filter/f-util.c index ab5b9094..98b7d1a8 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -75,6 +75,31 @@ f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block) return ms->method->new_inst(term, loop_start); } +struct f_inst * +f_print(struct f_inst *vars, int flush, enum filter_return fret) +{ +#define AX(...) do { struct f_inst *_tmp = f_new_inst(__VA_ARGS__); _tmp->next = output; output = _tmp; } while (0) + struct f_inst *output = NULL; + if (fret != F_NOP) + AX(FI_DIE, fret); + + if (flush) + AX(FI_FLUSH); + + while (vars) + { + struct f_inst *tmp = vars; + vars = vars->next; + tmp->next = NULL; + + AX(FI_PRINT, tmp); + } + + return output; +#undef AX +} + + #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))