0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Filter: The for loop uses the method system for type dispatch

This commit is contained in:
Maria Matejka 2023-06-18 22:50:45 +02:00 committed by Ondrej Zajicek
parent 1d38726c64
commit 21faa54ec3
6 changed files with 85 additions and 72 deletions

View File

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

View File

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

View File

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

View File

@ -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 */

View File

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

View File

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