diff --git a/filter/config.Y b/filter/config.Y index b3a04958..495a5e5b 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -572,9 +572,9 @@ decls: /* EMPTY */ { $$ = NULL; } /* Declarations that have no ';' at the end. Beware; these are reversed. */ declsn: one_decl { $$.inst = $1; $$.count = 1; } | one_decl ';' declsn { - $$ = $3; - $$.count++; - $$.inst->next = $1; + $1->next = $3.inst; + $$.count = $3.count + 1; + $$.inst = $1; } ; @@ -861,7 +861,7 @@ function_call: ; symbol_value: - CF_SYM_CONSTANT { $$ = f_new_inst(FI_CONSTANT_INDIRECT, $1->val); } + CF_SYM_CONSTANT { $$ = f_new_inst(FI_VARIABLE, $1); } | CF_SYM_VARIABLE { $$ = f_new_inst(FI_VARIABLE, $1); } | CF_SYM_ATTRIBUTE { $$ = f_new_inst(FI_EA_GET, *$1->attribute); } ; diff --git a/filter/decl.m4 b/filter/decl.m4 index 3043f4fc..3033438d 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -14,12 +14,11 @@ m4_divert(-1)m4_dnl # 7 dump line item callers # 8 postfixify # 9 same (filter comparator) -# 1 struct f_inst_FI_... -# 2 union in struct f_inst +# 1 union in struct f_inst # 3 constructors # # Per-inst Diversions: -# 101 content of struct f_inst_FI_... +# 101 content of per-inst struct # 102 constructor arguments # 103 constructor body # 104 dump line item content @@ -35,8 +34,8 @@ m4_define(FID_END, `m4_divert(-1)') m4_dnl m4_debugmode(aceflqtx) m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */') -m4_define(FID_STRUCT, `FID_ZONE(1, Per-instruction structure)') -m4_define(FID_UNION, `FID_ZONE(2, Union member)') +m4_define(FID_INST, `FID_ZONE(1, Instruction structure for config)') +m4_define(FID_LINE, `FID_ZONE(2, Instruction structure for interpreter)') m4_define(FID_NEW, `FID_ZONE(3, Constructor)') m4_define(FID_ENUM, `FID_ZONE(4, Code enum)') m4_define(FID_ENUM_STR, `FID_ZONE(5, Code enum to string)') @@ -51,6 +50,7 @@ m4_define(FID_NEW_BODY, `m4_divert(103)') m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])') m4_define(FID_POSTFIXIFY_BODY, `m4_divert(105)m4_define([[FID_POSTFIXIFY_BODY_EXISTS]])') m4_define(FID_SAME_BODY, `m4_divert(106)') +m4_define(FID_LINE_IN, `m4_divert(107)') m4_define(FID_ALL, `m4_ifdef([[FID_CURDIV]], [[m4_divert(FID_CURDIV)m4_undefine([[FID_CURDIV]])]])') m4_define(FID_C, `m4_ifelse(TARGET, [[C]], FID_ALL, [[m4_define(FID_CURDIV, m4_divnum)m4_divert(-1)]])') @@ -62,12 +62,14 @@ FID_ENUM INST_NAME(), FID_ENUM_STR [INST_NAME()] = "INST_NAME()", -FID_STRUCT -struct f_inst_[[]]INST_NAME() { +FID_INST +struct { m4_undivert(101) -}; -FID_UNION -struct f_inst_[[]]INST_NAME() i_[[]]INST_NAME(); +} i_[[]]INST_NAME(); +FID_LINE +struct { +m4_undivert(107) +} i_[[]]INST_NAME(); FID_NEW struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code m4_undivert(102) @@ -80,8 +82,9 @@ FID_C what_->fi_code = fi_code; what_->lineno = ifs->lino; what_->size = 1; - struct f_inst_[[]]INST_NAME() *what UNUSED = &(what_->i_[[]]INST_NAME()); +#define what (&(what_->i_]]INST_NAME()[[)) m4_undivert(103) +#undef what return what_; } @@ -90,19 +93,23 @@ case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break; FID_DUMP m4_ifdef([[FID_DUMP_BODY_EXISTS]], -[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item, const int indent)]] -, +[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]], [[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]]) m4_undefine([[FID_DUMP_BODY_EXISTS]]) { +#define item (&(item_->i_]]INST_NAME()[[)) m4_undivert(104) +#undef item } FID_ALL FID_POSTFIXIFY case INST_NAME(): { -m4_ifdef([[FID_POSTFIXIFY_BODY_EXISTS]], [[const struct f_inst_]]INST_NAME()[[ *what = &(what_->i_]]INST_NAME()[[);]], [[]]) +#define what (&(what_->i_]]INST_NAME()[[)) +#define item (&(dest->items[pos].i_]]INST_NAME()[[)) m4_undivert(105) +#undef what +#undef item dest->items[pos].fi_code = what_->fi_code; dest->items[pos].lineno = what_->lineno; break; @@ -111,7 +118,11 @@ m4_undefine([[FID_POSTFIXIFY_BODY_EXISTS]]) FID_SAME case INST_NAME(): +#define f1 (&(f1_->i_]]INST_NAME()[[)) +#define f2 (&(f2_->i_]]INST_NAME()[[)) m4_undivert(106) +#undef f1 +#undef f2 break; FID_END @@ -127,6 +138,8 @@ m4_dnl comparator for same m4_dnl dump format string m4_dnl dump format args m4_define(FID_MEMBER, `m4_dnl +FID_LINE_IN +$1 $2; FID_STRUCT_IN $1 $2; FID_NEW_ARGS @@ -135,7 +148,7 @@ FID_NEW_BODY what->$2 = $2; m4_ifelse($3,,,[[ FID_POSTFIXIFY_BODY -dest->items[pos].$3 = what->$2; +item->$3 = what->$2; ]]) m4_ifelse($4,,,[[ FID_SAME_BODY @@ -147,55 +160,37 @@ debug("%s$5\n", INDENT, $6); ]]) FID_END') -m4_define(ARG, `FID_MEMBER(const struct f_inst *, f$1,,,,) +m4_define(ARG, ` +FID_STRUCT_IN +const struct f_inst * f$1; +FID_NEW_ARGS +, const struct f_inst * f$1 FID_NEW_BODY +what->f$1 = f$1; for (const struct f_inst *child = f$1; child; child = child->next) what_->size += child->size; FID_POSTFIXIFY_BODY pos = postfixify(dest, what->f$1, pos); FID_END') -m4_define(ARG_ANY, `FID_MEMBER(const struct f_inst *, f$1,,,,) +m4_define(ARG_ANY, `ARG($@)') +m4_define(LINE, ` +FID_LINE_IN +const struct f_line * fl$1; +FID_STRUCT_IN +const struct f_inst * f$1; +FID_NEW_ARGS +, const struct f_inst * f$1 FID_NEW_BODY -for (const struct f_inst *child = f$1; child; child = child->next) what_->size += child->size; -FID_POSTFIXIFY_BODY -pos = postfixify(dest, what->f$1, pos); -FID_END') -m4_define(LINE, `FID_MEMBER(const struct f_inst *, f$1,,[[!f_same(f1->lines[$2], f2->lines[$2])]],,) +what->f$1 = f$1; FID_DUMP_BODY -f_dump_line(item->lines[$2], indent + 1); +f_dump_line(item->fl$1, indent + 1); FID_POSTFIXIFY_BODY -dest->items[pos].lines[$2] = f_postfixify(what->f$1); -FID_END') -m4_define(LINEP, `FID_STRUCT_IN -const struct f_line *fl$1; -FID_DUMP_BODY -f_dump_line(item->lines[$2], indent + 1) -FID_POSTFIXIFY_BODY -dest->items[pos].lines[$2] = what->fl$1; +item->fl$1 = f_postfixify(what->f$1); FID_SAME_BODY -if (!f_same(f1->lines[$2], f2->lines[$2])) return 0; +if (!f_same(f1->fl$1, f2->fl$1)) return 0; FID_END') m4_define(SYMBOL, `FID_MEMBER(const struct symbol *, sym, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], symbol %s, item->sym->name)') -m4_define(VALI, `FID_MEMBER(struct f_val, vali, val, [[!val_same(&f1->val, &f2->val)]], value %s, val_dump(&item->val))') -m4_define(VALP, `FID_MEMBER(const struct f_val *, valp,, [[!val_same(&f1->val, &f2->val)]], value %s, val_dump(&item->val)) -FID_POSTFIXIFY_BODY -dest->items[pos].val = *(what->valp); -FID_END') -m4_define(VAR, `m4_dnl -FID_STRUCT_IN -const struct f_val *valp; -const struct symbol *sym; -FID_NEW_ARGS -, const struct symbol *sym -FID_NEW_BODY -what->valp = (what->sym = sym)->val; -FID_POSTFIXIFY_BODY -dest->items[pos].vp = (dest->items[pos].sym = what->sym)->val; -FID_SAME_BODY -if (strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)) return 0; -FID_DUMP_BODY -debug("%svariable %s with value %s\n", INDENT, item->sym->name, val_dump(item->vp)); -FID_END') +m4_define(VAL, `FID_MEMBER(struct f_val $1, val, val m4_ifelse($1,,,[0]), [[!val_same(&f1->val, &f2->val)]], value %s, val_dump(&item->val))') m4_define(FRET, `FID_MEMBER(enum filter_return, fret, fret, f1->fret != f2->fret, %s, filter_return_str(item->fret))') m4_define(ECS, `FID_MEMBER(enum ec_subtype, ecs, ecs, f1->ecs != f2->ecs, ec subtype %s, ec_subtype_str(item->ecs))') m4_define(RTC, `FID_MEMBER(const struct rtable_config *, rtc, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], route table %s, item->rtc->name)') @@ -240,11 +235,10 @@ FID_WR_PUT(3) /* Line dumpers */ #define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1) static const char f_dump_line_indent_str[] = " "; -void f_dump_line(const struct f_line *dest, int indent); FID_WR_PUT(6) -void f_dump_line(const struct f_line *dest, int indent) +void f_dump_line(const struct f_line *dest, uint indent) { if (!dest) { debug("%sNo filter line (NULL)\n", INDENT); @@ -305,17 +299,19 @@ f_same(const struct f_line *fl1, const struct f_line *fl2) if (fl1->len != fl2->len) return 0; for (uint i=0; ilen; i++) { -#define f1 (&(fl1->items[i])) -#define f2 (&(fl2->items[i])) - if (f1->fi_code != f2->fi_code) +#define f1_ (&(fl1->items[i])) +#define f2_ (&(fl2->items[i])) + if (f1_->fi_code != f2_->fi_code) return 0; - if (f1->flags != f2->flags) + if (f1_->flags != f2_->flags) return 0; - switch(f1->fi_code) { + switch(f1_->fi_code) { FID_WR_PUT(9) } } +#undef f1_ +#undef f2_ return 1; } @@ -324,16 +320,24 @@ FID_WR_DIRECT(900,H) /* Filter instruction codes */ enum f_instruction_code { FID_WR_PUT(4) -}; - -/* Per-instruction structures */ -FID_WR_PUT(1) +} PACKED; +/* Filter instruction structure for config */ struct f_inst { const struct f_inst *next; /* Next instruction */ enum f_instruction_code fi_code; /* Instruction code */ int size; /* How many instructions are underneath */ int lineno; /* Line number */ + union { + FID_WR_PUT(1) + }; +}; + +/* Filter line item */ +struct f_line_item { + enum f_instruction_code fi_code; /* What to do */ + enum f_instruction_flags flags; /* Flags, instruction-specific */ + uint lineno; /* Where */ union { FID_WR_PUT(2) }; diff --git a/filter/f-inst.c b/filter/f-inst.c index 6f563873..128b9e54 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -119,13 +119,12 @@ what->count = len; FID_END - if (vstk.cnt < what->count) /* TODO: make this check systematic */ - runtime("Construction of BGP path mask from %u elements must have at least that number of elements", what->count); + if (vstk.cnt < whati->count) /* TODO: make this check systematic */ + runtime("Construction of BGP path mask from %u elements must have at least that number of elements", whati->count); - struct f_path_mask *pm = lp_alloc(fs->pool, sizeof(struct f_path_mask) + what->count * sizeof(struct f_path_mask_item)); - for (uint i=0; icount; i++) { -//#define pv vstk.val[vstk.cnt-i-1] -#define pv vstk.val[vstk.cnt - what->count + i] + struct f_path_mask *pm = lp_alloc(fs->pool, sizeof(struct f_path_mask) + whati->count * sizeof(struct f_path_mask_item)); + for (uint i=0; icount; i++) { +#define pv vstk.val[vstk.cnt - whati->count + i] switch (pv.type) { case T_PATH_MASK_ITEM: pm->item[i] = pv.val.pmi; @@ -141,8 +140,8 @@ } } - vstk.cnt -= what->count; - pm->len = what->count; + vstk.cnt -= whati->count; + pm->len = whati->count; RESULT(T_PATH_MASK, path_mask, pm); } @@ -246,15 +245,53 @@ /* some constants have value in a[1], some in *a[0].p, strange. */ INST(FI_CONSTANT, 0, 1) { /* integer (or simple type) constant, string, set, or prefix_set */ - VALI; // res = what->val; + FID_LINE_IN + struct f_val val; + FID_STRUCT_IN + struct f_val val; + FID_NEW_ARGS + , const struct f_val val + FID_NEW_BODY + what->val = val; + FID_POSTFIXIFY_BODY + item->val = what->val; + FID_SAME_BODY + if (!val_same(&(f1->val), &(f2->val))) return 0; + FID_DUMP_BODY + debug("%svalue %s\n", INDENT, val_dump(&item->val)); + FID_END + + res = whati->val; RESULT_OK; } INST(FI_VARIABLE, 0, 1) { - VAR; - RESULT_OK; - } - INST(FI_CONSTANT_INDIRECT, 0, 1) { - VALP; + FID_STRUCT_IN + const struct symbol *sym; + FID_LINE_IN + const struct symbol *sym; + const struct f_val *valp; + FID_NEW_ARGS + , const struct symbol *sym + FID_NEW_BODY + what->sym = sym; + FID_POSTFIXIFY_BODY + item->valp = (item->sym = what->sym)->val; + FID_SAME_BODY + if (strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)) return 0; + FID_DUMP_BODY + switch (item->sym->class) { + case SYM_CONSTANT_RANGE: + debug("%sconstant %s with value %s\n", INDENT, item->sym->name, val_dump(item->valp)); + break; + case SYM_VARIABLE_RANGE: + debug("%svariable %s with current value %s\n", INDENT, item->sym->name, val_dump(item->valp)); + break; + default: + bug("Symbol %s of type %d doesn't reference a value", item->sym->name, item->sym->class); + } + FID_END + + res = *whati->valp; RESULT_OK; } INST(FI_PRINT, 1, 0) { @@ -702,36 +739,48 @@ } INST(FI_CALL, 0, 1) { - /* Do not use the symbol on execution */ - if (0) { - UNUSED SYMBOL; - } - - /* Postfixify extracts the function body from the symbol */ - FID_POSTFIXIFY_BODY - dest->items[pos].lines[0] = what->sym->function; - FID_END - - /* First push the body on stack */ - LINEX(what->lines[0]); - curline.emask |= FE_RETURN; - - /* Then push the arguments */ - LINE(1,1); - + FID_LINE_IN + const struct f_line *args; + const struct f_line *body; + struct symbol *sym; + FID_STRUCT_IN + struct symbol *sym; + const struct f_inst *args; + FID_NEW_ARGS + , struct symbol * sym + , const struct f_inst *args FID_NEW_BODY if (sym->class != SYM_FUNCTION) cf_error("You can't call something which is not a function. Really."); uint count = 0; - for (const struct f_inst *inst = f1; inst; inst = inst->next) + for (const struct f_inst *inst = args; inst; inst = inst->next) count++; if (count != sym->function->args) cf_error("Function %s takes %u arguments, got %u.", sym->name, sym->function->args, count); + + what->sym = sym; + what->args = args; + FID_DUMP_BODY + debug("%scalling %s with following args\n", INDENT, item->sym->name); + f_dump_line(item->args, indent + 1); + FID_POSTFIXIFY_BODY + item->args = f_postfixify(what->args); + item->body = (item->sym = what->sym)->function; + FID_SAME_BODY + /* To be done better */ + if (strcmp(f1->sym->name, f2->sym->name)) return 0; + if (!f_same(f1->args, f2->args)) return 0; + if (!f_same(f1->body, f2->body)) return 0; FID_END - /* FIXME: Optimization of function comparison. */ + /* First push the body on stack */ + LINEX(whati->body); + curline.emask |= FE_RETURN; + + /* Then push the arguments */ + LINEX(whati->args); } INST(FI_DROP_RESULT, 1, 0) { @@ -741,18 +790,17 @@ INST(FI_SWITCH, 1, 0) { ARG_ANY(1); TREE; - const struct f_tree *t = find_tree(what->tree, &v1); - if (!t) { + if (!tree) { v1.type = T_VOID; - t = find_tree(what->tree, &v1); - if (!t) { + tree = find_tree(tree, &v1); + if (!tree) { debug( "No else statement?\n"); break; } } /* It is actually possible to have t->data NULL */ - LINEX(t->data); + LINEX(tree->data); } INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */ diff --git a/filter/f-inst.h b/filter/f-inst.h index 201be5f8..5b397a5d 100644 --- a/filter/f-inst.h +++ b/filter/f-inst.h @@ -15,6 +15,11 @@ #include "filter/filter.h" #include "filter/data.h" +/* Flags for instructions */ +enum f_instruction_flags { + FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */ +} PACKED; + /* Include generated filter instruction declarations */ #include "filter/inst-gen.h" @@ -25,37 +30,9 @@ const char *f_instruction_name(enum f_instruction_code fi); struct f_inst *f_clear_local_vars(struct f_inst *decls); -/* Flags for instructions */ -enum f_instruction_flags { - FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */ -}; - /* Filter structures for execution */ struct f_line; -/* The single instruction item */ -struct f_line_item { - enum f_instruction_code fi_code; /* What to do */ - enum f_instruction_flags flags; /* Flags, instruction-specific */ - uint lineno; /* Where */ - union { - struct { - const struct f_val *vp; - const struct symbol *sym; - }; - struct f_val val; - const struct f_line *lines[2]; - enum filter_return fret; - struct f_static_attr sa; - struct f_dynamic_attr da; - enum ec_subtype ecs; - const char *s; - const struct f_tree *tree; - const struct rtable_config *rtc; - uint count; - }; /* Additional instruction data */ -}; - /* Line of instructions to be unconditionally executed one after another */ struct f_line { uint len; /* Line length */ @@ -68,6 +45,8 @@ struct f_line *f_postfixify_concat(const struct f_inst * const inst[], uint coun static inline struct f_line *f_postfixify(const struct f_inst *root) { return f_postfixify_concat(&root, 1); } +void f_dump_line(const struct f_line *, uint indent); + struct filter *f_new_where(const struct f_inst *); static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, u8 bit, 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, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ diff --git a/filter/filter.c b/filter/filter.c index 4249d4ee..8db5119b 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -167,6 +167,11 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) #define curline estk.item[estk.cnt-1] +#if DEBUGGING + debug("Interpreting line."); + f_dump_line(line, 1); +#endif + while (estk.cnt > 0) { while (curline.pos < curline.line->len) { const struct f_line_item *what = &(curline.line->items[curline.pos++]); diff --git a/filter/filter_test.c b/filter/filter_test.c index d83e8a8b..d0dd281a 100644 --- a/filter/filter_test.c +++ b/filter/filter_test.c @@ -74,7 +74,8 @@ bt_assert_filter(int result, const struct f_line_item *assert) bt_suit_case_result = 0; } - bt_log_suite_case_result(bt_suit_case_result, "Assertion at line %d (%s)", assert->lineno, assert->s); + bt_log_suite_case_result(bt_suit_case_result, "Assertion at line %d (%s)", + assert->lineno, assert->i_FI_ASSERT.s); } int diff --git a/filter/interpret.m4 b/filter/interpret.m4 index eb368b41..ea91e74e 100644 --- a/filter/interpret.m4 +++ b/filter/interpret.m4 @@ -13,8 +13,10 @@ m4_define(DNL, `m4_dnl') m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[ m4_divert(1) case INST_NAME(): +#define whati (&(what->i_]]INST_NAME()[[)) m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (vstk.cnt < INST_INVAL()) runtime("Stack underflow"); vstk.cnt -= INST_INVAL(); ]]) m4_undivert(2) +#undef whati break; m4_divert(-1) ]])') @@ -36,37 +38,35 @@ m4_define(LINEX, `do { } while (0)') m4_define(LINE, `do { - if (what->lines[$2]) { + if (whati->fl$1) { estk.item[estk.cnt].pos = 0; - estk.item[estk.cnt].line = what->lines[$2]; + estk.item[estk.cnt].line = whati->fl$1; estk.item[estk.cnt].ventry = vstk.cnt; estk.item[estk.cnt].emask = 0; estk.cnt++; } } while (0)') -m4_define(LINEP, LINE($1, $2)) - m4_define(ARG_ANY, `') -m4_define(SYMBOL, `const struct symbol *sym = what->sym') +m4_define(SYMBOL, `const struct symbol *sym = whati->sym') -m4_define(VALI, `res = what->val') -m4_define(VALP, `res = what->val') -m4_define(VAR, `res = *what->vp') -m4_define(FRET, `enum filter_return fret = what->fret') -m4_define(ECS, `enum ec_subtype ecs = what->ecs') -m4_define(RTC, `struct rtable *table = what->rtc->table') -m4_define(STATIC_ATTR, `struct f_static_attr sa = what->sa') -m4_define(DYNAMIC_ATTR, `struct f_dynamic_attr da = what->da') -m4_define(TREE, `') +m4_define(FRET, `enum filter_return fret = whati->fret') +m4_define(ECS, `enum ec_subtype ecs = whati->ecs') +m4_define(RTC, `struct rtable *table = whati->rtc->table') +m4_define(STATIC_ATTR, `struct f_static_attr sa = whati->sa') +m4_define(DYNAMIC_ATTR, `struct f_dynamic_attr da = whati->da') +m4_define(TREE, `const struct f_tree *tree = whati->tree') m4_define(STRING, `') m4_define(COUNT, `') m4_define(SAME, `') m4_define(FID_STRUCT_IN, `m4_divert(-1)') +m4_define(FID_LINE_IN, `m4_divert(-1)') m4_define(FID_NEW_ARGS, `m4_divert(-1)') m4_define(FID_NEW_BODY, `m4_divert(-1)') m4_define(FID_POSTFIXIFY_BODY, `m4_divert(-1)') +m4_define(FID_SAME_BODY, `m4_divert(-1)') +m4_define(FID_DUMP_BODY, `m4_divert(-1)') m4_define(FID_END, `m4_divert(2)') m4_m4wrap(` diff --git a/filter/test.conf b/filter/test.conf index 89215d1c..dc47eb7d 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -1117,6 +1117,11 @@ int i; return 0; } +function callmeagain(int a; int b; int c) +{ + return a + b + c; +} + function fifteen() { return 15; @@ -1133,6 +1138,7 @@ function t_call_function() bt_assert(callme(3, 2) = 6); bt_assert(callme(4, 4) = 16); bt_assert(callme(7, 2) = 14); + bt_assert(callmeagain(1, 2, 3) = 6); } bt_test_suite(t_call_function, "Testing calling functions");