]>
Commit | Line | Data |
---|---|---|
4f082dfa MM |
1 | m4_divert(-1)m4_dnl |
2 | # | |
3 | # BIRD -- Construction of per-instruction structures | |
4 | # | |
5 | # (c) 2018 Maria Matejka <mq@jmq.cz> | |
6 | # | |
7 | # Can be freely distributed and used under the terms of the GNU GPL. | |
8 | # | |
550a6488 MM |
9 | # THIS IS A M4 MACRO FILE GENERATING 3 FILES ALTOGETHER. |
10 | # KEEP YOUR HANDS OFF UNLESS YOU KNOW WHAT YOU'RE DOING. | |
11 | # EDITING AND DEBUGGING THIS FILE MAY DAMAGE YOUR BRAIN SERIOUSLY. | |
4f082dfa | 12 | # |
550a6488 MM |
13 | # But you're welcome to read and edit and debug if you aren't scared. |
14 | # | |
15 | # Uncomment the following line to get exhaustive debug output. | |
16 | # m4_debugmode(aceflqtx) | |
17 | # | |
18 | # How it works: | |
19 | # 1) Instruction to code conversion (uses diversions 100..199) | |
20 | # 2) Code wrapping (uses diversions 1..99) | |
21 | # 3) Final preparation (uses diversions 200..299) | |
22 | # 4) Shipout | |
23 | # | |
24 | # See below for detailed description. | |
25 | # | |
26 | # | |
27 | # 1) Instruction to code conversion | |
28 | # The code provided in f-inst.c between consecutive INST() calls | |
29 | # is interleaved for many different places. It is here processed | |
30 | # and split into separate instances where split-by-instruction | |
31 | # happens. These parts are stored in temporary diversions listed: | |
4f082dfa | 32 | # |
ea4f55e3 | 33 | # 101 content of per-inst struct |
04160812 MM |
34 | # 102 constructor arguments |
35 | # 103 constructor body | |
de12cd18 | 36 | # 104 dump line item content |
550a6488 MM |
37 | # (there may be nothing in dump-line content and |
38 | # it must be handled specially in phase 2) | |
23e3b1e6 | 39 | # 105 linearize body |
132529ce | 40 | # 106 comparator body |
d1039926 MM |
41 | # 107 struct f_line_item content |
42 | # 108 interpreter body | |
04160812 | 43 | # |
550a6488 | 44 | # Here are macros to allow you to _divert to the right directions. |
4f082dfa MM |
45 | m4_define(FID_STRUCT_IN, `m4_divert(101)') |
46 | m4_define(FID_NEW_ARGS, `m4_divert(102)') | |
47 | m4_define(FID_NEW_BODY, `m4_divert(103)') | |
dd4d4095 | 48 | m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])') |
550a6488 | 49 | m4_define(FID_LINEARIZE_BODY, `m4_divert(105)') |
132529ce | 50 | m4_define(FID_SAME_BODY, `m4_divert(106)') |
ea4f55e3 | 51 | m4_define(FID_LINE_IN, `m4_divert(107)') |
d1039926 | 52 | m4_define(FID_INTERPRET_BODY, `m4_divert(108)') |
4f082dfa | 53 | |
550a6488 MM |
54 | # Sometimes you want slightly different code versions in different |
55 | # outputs. | |
56 | # Use FID_HIC(code for inst-gen.h, code for inst-gen.c, code for inst-interpret.c) | |
57 | # and put it into [[ ]] quotes if it shall contain commas. | |
b40c0f02 MM |
58 | m4_define(FID_HIC, `m4_ifelse(TARGET, [[H]], [[$1]], TARGET, [[I]], [[$2]], TARGET, [[C]], [[$3]])') |
59 | ||
550a6488 | 60 | # In interpreter code, this is quite common. |
b40c0f02 MM |
61 | m4_define(FID_INTERPRET_EXEC, `FID_HIC(,[[FID_INTERPRET_BODY()]],[[m4_divert(-1)]])') |
62 | m4_define(FID_INTERPRET_NEW, `FID_HIC(,[[m4_divert(-1)]],[[FID_INTERPRET_BODY()]])') | |
550a6488 MM |
63 | |
64 | # If the instruction is never converted to constant, the interpret | |
65 | # code is not produced at all for constructor | |
b40c0f02 MM |
66 | m4_define(NEVER_CONSTANT, `m4_define([[INST_NEVER_CONSTANT]])') |
67 | m4_define(FID_IFCONST, `m4_ifdef([[INST_NEVER_CONSTANT]],[[$2]],[[$1]])') | |
87bd7cd7 | 68 | |
550a6488 MM |
69 | # If the instruction has some attributes (here called members), |
70 | # these are typically carried with the instruction from constructor | |
71 | # to interpreter. This yields a line of code everywhere on the path. | |
72 | # FID_MEMBER is a macro to help with this task. | |
4f082dfa | 73 | m4_define(FID_MEMBER, `m4_dnl |
84c58aab MM |
74 | FID_LINE_IN()m4_dnl |
75 | $1 $2; | |
76 | FID_STRUCT_IN()m4_dnl | |
77 | $1 $2; | |
78 | FID_NEW_ARGS()m4_dnl | |
79 | , $1 $2 | |
263fa2c4 | 80 | FID_NEW_BODY()m4_dnl |
63f49457 | 81 | whati->$2 = $2; |
263fa2c4 | 82 | FID_LINEARIZE_BODY()m4_dnl |
b40c0f02 MM |
83 | item->$2 = whati->$2; |
84 | m4_ifelse($3,,,[[ | |
263fa2c4 | 85 | FID_SAME_BODY()m4_dnl |
b40c0f02 | 86 | if ($3) return 0; |
132529ce | 87 | ]]) |
b40c0f02 | 88 | m4_ifelse($4,,,[[ |
263fa2c4 | 89 | FID_DUMP_BODY()m4_dnl |
b40c0f02 | 90 | debug("%s$4\n", INDENT, $5); |
d1039926 | 91 | ]]) |
263fa2c4 | 92 | FID_INTERPRET_EXEC()m4_dnl |
b40c0f02 | 93 | const $1 $2 = whati->$2 |
550a6488 | 94 | FID_INTERPRET_BODY') |
4f082dfa | 95 | |
550a6488 MM |
96 | # Instruction arguments are needed only until linearization is done. |
97 | # This puts the arguments into the filter line to be executed before | |
98 | # the instruction itself. | |
99 | # | |
100 | # To achieve this, ARG_ANY must be called before anything writes into | |
101 | # the instruction line as it moves the instruction pointer forward. | |
d1039926 | 102 | m4_define(ARG_ANY, ` |
84c58aab MM |
103 | FID_STRUCT_IN()m4_dnl |
104 | struct f_inst * f$1; | |
105 | FID_NEW_ARGS()m4_dnl | |
106 | , struct f_inst * f$1 | |
4f082dfa | 107 | FID_NEW_BODY |
63f49457 | 108 | whati->f$1 = f$1; |
b40c0f02 MM |
109 | for (const struct f_inst *child = f$1; child; child = child->next) { |
110 | what->size += child->size; | |
111 | FID_IFCONST([[ | |
112 | if (child->fi_code != FI_CONSTANT) | |
113 | constargs = 0; | |
114 | ]]) | |
115 | } | |
23e3b1e6 | 116 | FID_LINEARIZE_BODY |
b40c0f02 | 117 | pos = linearize(dest, whati->f$1, pos); |
550a6488 | 118 | FID_INTERPRET_BODY()') |
d1039926 | 119 | |
550a6488 | 120 | # Some arguments need to check their type. After that, ARG_ANY is called. |
d1039926 | 121 | m4_define(ARG, `ARG_ANY($1) |
263fa2c4 | 122 | FID_INTERPRET_EXEC()m4_dnl |
d1039926 | 123 | if (v$1.type != $2) runtime("Argument $1 of instruction %s must be of type $2, got 0x%02x", f_instruction_name(what->fi_code), v$1.type)m4_dnl |
550a6488 | 124 | FID_INTERPRET_BODY()') |
d1039926 | 125 | |
550a6488 MM |
126 | # Executing another filter line. This replaces the recursion |
127 | # that was needed in the former implementation. | |
128 | m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()') | |
b40c0f02 | 129 | m4_define(LINEX_, `do { |
1757a6fc MM |
130 | fstk->estk[fstk->ecnt].pos = 0; |
131 | fstk->estk[fstk->ecnt].line = $1; | |
132 | fstk->estk[fstk->ecnt].ventry = fstk->vcnt; | |
133 | fstk->estk[fstk->ecnt].vbase = fstk->estk[fstk->ecnt-1].vbase; | |
134 | fstk->estk[fstk->ecnt].emask = 0; | |
135 | fstk->ecnt++; | |
b40c0f02 | 136 | } while (0)') |
d1039926 | 137 | |
ea4f55e3 | 138 | m4_define(LINE, ` |
84c58aab MM |
139 | FID_LINE_IN()m4_dnl |
140 | const struct f_line * fl$1; | |
141 | FID_STRUCT_IN()m4_dnl | |
142 | struct f_inst * f$1; | |
143 | FID_NEW_ARGS()m4_dnl | |
144 | , struct f_inst * f$1 | |
263fa2c4 | 145 | FID_NEW_BODY()m4_dnl |
63f49457 | 146 | whati->f$1 = f$1; |
263fa2c4 | 147 | FID_DUMP_BODY()m4_dnl |
ea4f55e3 | 148 | f_dump_line(item->fl$1, indent + 1); |
263fa2c4 | 149 | FID_LINEARIZE_BODY()m4_dnl |
63f49457 | 150 | item->fl$1 = f_linearize(whati->f$1); |
263fa2c4 | 151 | FID_SAME_BODY()m4_dnl |
ea4f55e3 | 152 | if (!f_same(f1->fl$1, f2->fl$1)) return 0; |
263fa2c4 | 153 | FID_INTERPRET_EXEC()m4_dnl |
d1039926 | 154 | do { if (whati->fl$1) { |
b40c0f02 MM |
155 | LINEX_(whati->fl$1); |
156 | } } while(0) | |
263fa2c4 | 157 | FID_INTERPRET_NEW()m4_dnl |
b40c0f02 | 158 | return whati->f$1 |
550a6488 | 159 | FID_INTERPRET_BODY()') |
d1039926 | 160 | |
550a6488 MM |
161 | # Some of the instructions have a result. These constructions |
162 | # state the result and put it to the right place. | |
a84b8b6e | 163 | m4_define(RESULT, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])') |
b40c0f02 MM |
164 | m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; fstk->vcnt++; } while (0)]], |
165 | [[return fi_constant(what, $1)]])') | |
f74d1976 | 166 | m4_define(RESULT_VOID, `RESULT_VAL([[ (struct f_val) { .type = T_VOID } ]])') |
d1039926 | 167 | |
550a6488 | 168 | # Some common filter instruction members |
84c58aab | 169 | m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], symbol %s, item->sym->name)') |
b40c0f02 MM |
170 | m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], route table %s, item->rtc->name)') |
171 | m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f2->sa.sa_code,,)') | |
172 | m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)') | |
173 | m4_define(ACCESS_RTE, `NEVER_CONSTANT()') | |
de12cd18 | 174 | |
550a6488 MM |
175 | # 2) Code wrapping |
176 | # The code produced in 1xx temporary diversions is a raw code without | |
177 | # any auxiliary commands and syntactical structures around. When the | |
178 | # instruction is done, INST_FLUSH is called. More precisely, it is called | |
179 | # at the beginning of INST() call and at the end of file. | |
180 | # | |
181 | # INST_FLUSH picks all the temporary diversions, wraps their content | |
182 | # into appropriate headers and structures and saves them into global | |
183 | # diversions listed: | |
184 | # | |
185 | # 4 enum fi_code | |
186 | # 5 enum fi_code to string | |
187 | # 6 dump line item | |
188 | # 7 dump line item callers | |
189 | # 8 linearize | |
190 | # 9 same (filter comparator) | |
191 | # 1 union in struct f_inst | |
192 | # 3 constructors + interpreter | |
193 | # | |
194 | # These global diversions contain blocks of code that can be directly | |
195 | # put into the final file, yet it still can't be written out now as | |
196 | # every instruction writes to all of these diversions. | |
197 | ||
84c58aab MM |
198 | # Code wrapping diversion names. Here we want an explicit newline |
199 | # after the C comment. | |
200 | m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */ | |
201 | ') | |
550a6488 MM |
202 | m4_define(FID_INST, `FID_ZONE(1, Instruction structure for config)') |
203 | m4_define(FID_LINE, `FID_ZONE(2, Instruction structure for interpreter)') | |
204 | m4_define(FID_NEW, `FID_ZONE(3, Constructor)') | |
205 | m4_define(FID_ENUM, `FID_ZONE(4, Code enum)') | |
206 | m4_define(FID_ENUM_STR, `FID_ZONE(5, Code enum to string)') | |
207 | m4_define(FID_DUMP, `FID_ZONE(6, Dump line)') | |
208 | m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)') | |
209 | m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)') | |
210 | m4_define(FID_SAME, `FID_ZONE(9, Comparison)') | |
211 | ||
212 | # This macro does all the code wrapping. See inline comments. | |
213 | m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[ | |
84c58aab MM |
214 | FID_ENUM()m4_dnl Contents of enum fi_code { ... } |
215 | INST_NAME(), | |
216 | FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code | |
217 | [INST_NAME()] = "INST_NAME()", | |
218 | FID_INST()m4_dnl Anonymous structure inside struct f_inst | |
219 | struct { | |
220 | m4_undivert(101)m4_dnl | |
221 | } i_[[]]INST_NAME(); | |
222 | FID_LINE()m4_dnl Anonymous structure inside struct f_line_item | |
223 | struct { | |
224 | m4_undivert(107)m4_dnl | |
225 | } i_[[]]INST_NAME(); | |
226 | FID_NEW()m4_dnl Constructor and interpreter code together | |
550a6488 | 227 | FID_HIC( |
84c58aab | 228 | [[m4_dnl Public declaration of constructor in H file |
550a6488 | 229 | struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code |
84c58aab | 230 | m4_undivert(102)m4_dnl |
550a6488 | 231 | );]], |
84c58aab | 232 | [[m4_dnl The one case in The Big Switch inside interpreter |
550a6488 MM |
233 | case INST_NAME(): |
234 | #define whati (&(what->i_]]INST_NAME()[[)) | |
235 | m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]]) | |
263fa2c4 | 236 | m4_undivert(108)m4_dnl |
550a6488 MM |
237 | #undef whati |
238 | break; | |
239 | ]], | |
84c58aab | 240 | [[m4_dnl Constructor itself |
550a6488 | 241 | struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code |
263fa2c4 | 242 | m4_undivert(102)m4_dnl |
550a6488 MM |
243 | ) |
244 | { | |
245 | /* Allocate the structure */ | |
246 | struct f_inst *what = fi_new(fi_code); | |
247 | FID_IFCONST([[uint constargs = 1;]]) | |
248 | ||
249 | /* Initialize all the members */ | |
250 | #define whati (&(what->i_]]INST_NAME()[[)) | |
263fa2c4 | 251 | m4_undivert(103)m4_dnl |
550a6488 MM |
252 | |
253 | /* If not constant, return the instruction itself */ | |
254 | FID_IFCONST([[if (!constargs)]]) | |
255 | return what; | |
256 | ||
257 | /* Try to pre-calculate the result */ | |
263fa2c4 | 258 | FID_IFCONST([[m4_undivert(108)]])m4_dnl |
550a6488 MM |
259 | #undef whati |
260 | } | |
261 | ]]) | |
262 | ||
84c58aab | 263 | FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping (debug) |
550a6488 MM |
264 | case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break; |
265 | ||
84c58aab | 266 | FID_DUMP()m4_dnl The dumper itself |
550a6488 MM |
267 | m4_ifdef([[FID_DUMP_BODY_EXISTS]], |
268 | [[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]], | |
269 | [[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]]) | |
270 | m4_undefine([[FID_DUMP_BODY_EXISTS]]) | |
271 | { | |
272 | #define item (&(item_->i_]]INST_NAME()[[)) | |
263fa2c4 | 273 | m4_undivert(104)m4_dnl |
550a6488 MM |
274 | #undef item |
275 | } | |
276 | ||
84c58aab | 277 | FID_LINEARIZE()m4_dnl The linearizer |
550a6488 MM |
278 | case INST_NAME(): { |
279 | #define whati (&(what->i_]]INST_NAME()[[)) | |
280 | #define item (&(dest->items[pos].i_]]INST_NAME()[[)) | |
263fa2c4 | 281 | m4_undivert(105)m4_dnl |
550a6488 MM |
282 | #undef whati |
283 | #undef item | |
284 | dest->items[pos].fi_code = what->fi_code; | |
285 | dest->items[pos].lineno = what->lineno; | |
286 | break; | |
287 | } | |
288 | ||
84c58aab | 289 | FID_SAME()m4_dnl This code compares two f_line"s while reconfiguring |
550a6488 MM |
290 | case INST_NAME(): |
291 | #define f1 (&(f1_->i_]]INST_NAME()[[)) | |
292 | #define f2 (&(f2_->i_]]INST_NAME()[[)) | |
263fa2c4 | 293 | m4_undivert(106)m4_dnl |
550a6488 MM |
294 | #undef f1 |
295 | #undef f2 | |
296 | break; | |
297 | ||
84c58aab | 298 | m4_divert(-1)FID_FLUSH(101,200)m4_dnl And finally this flushes all the unused diversions |
550a6488 MM |
299 | ]])') |
300 | ||
301 | m4_define(INST, `m4_dnl This macro is called on beginning of each instruction. | |
302 | INST_FLUSH()m4_dnl First, old data is flushed | |
303 | m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name, | |
304 | m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count | |
305 | m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl and reset NEVER_CONSTANT trigger. | |
84c58aab | 306 | FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code. |
550a6488 MM |
307 | ') |
308 | ||
309 | # 3) Final preparation | |
310 | # | |
311 | # Now we prepare all the code around the global diversions. | |
312 | # It must be here, not in m4wrap, as we want M4 to mark the code | |
313 | # by #line directives correctly, not to claim that every single line | |
314 | # is at the beginning of the m4wrap directive. | |
315 | # | |
316 | # This part is split by the final file. | |
317 | # H for inst-gen.h | |
318 | # I for inst-interpret.c | |
319 | # C for inst-gen.c | |
320 | # | |
321 | # So we in cycle: | |
322 | # A. open a diversion | |
323 | # B. send there some code | |
324 | # C. close that diversion | |
325 | # D. flush a global diversion | |
326 | # E. open another diversion and goto B. | |
327 | # | |
328 | # Final diversions | |
329 | # 200+ completed text before it is flushed to output | |
330 | ||
331 | # This is a list of output diversions | |
de12cd18 | 332 | m4_define(FID_WR_PUT_LIST) |
550a6488 MM |
333 | |
334 | # This macro does the steps C to E, see before. | |
64bb1346 | 335 | m4_define(FID_WR_PUT_ALSO, `m4_define([[FID_WR_PUT_LIST]],FID_WR_PUT_LIST()[[FID_WR_DPUT(]]FID_WR_DIDX[[)FID_WR_DPUT(]]$1[[)]])m4_define([[FID_WR_DIDX]],m4_eval(FID_WR_DIDX+1))m4_divert(FID_WR_DIDX)') |
de12cd18 | 336 | |
550a6488 | 337 | # These macros do the splitting between H/I/C |
64bb1346 MM |
338 | m4_define(FID_WR_DIRECT, `m4_ifelse(TARGET,[[$1]],[[FID_WR_INIT()]],[[FID_WR_STOP()]])') |
339 | m4_define(FID_WR_INIT, `m4_define([[FID_WR_DIDX]],200)m4_define([[FID_WR_PUT]],[[FID_WR_PUT_ALSO($]][[@)]])m4_divert(200)') | |
340 | m4_define(FID_WR_STOP, `m4_define([[FID_WR_PUT]])m4_divert(-1)') | |
4f082dfa | 341 | |
550a6488 MM |
342 | # Here is the direct code to be put into the output files |
343 | # together with the undiversions, being hidden under FID_WR_PUT() | |
344 | ||
04160812 | 345 | m4_changequote([[,]]) |
64bb1346 | 346 | FID_WR_DIRECT(I) |
236828d0 | 347 | FID_WR_PUT(3) |
64bb1346 | 348 | FID_WR_DIRECT(C) |
87bd7cd7 MM |
349 | #include "nest/bird.h" |
350 | #include "filter/filter.h" | |
351 | #include "filter/f-inst.h" | |
b256f241 | 352 | |
b256f241 MM |
353 | /* Instruction codes to string */ |
354 | static const char * const f_instruction_name_str[] = { | |
355 | FID_WR_PUT(5) | |
356 | }; | |
357 | ||
358 | const char * | |
359 | f_instruction_name(enum f_instruction_code fi) | |
360 | { | |
361 | if (fi < (sizeof(f_instruction_name_str) / sizeof(f_instruction_name_str[0]))) | |
362 | return f_instruction_name_str[fi]; | |
363 | else | |
364 | bug("Got unknown instruction code: %d", fi); | |
365 | } | |
366 | ||
4212c0e7 MM |
367 | static inline struct f_inst * |
368 | fi_new(enum f_instruction_code fi_code) | |
369 | { | |
370 | struct f_inst *what = cfg_allocz(sizeof(struct f_inst)); | |
371 | what->lineno = ifs->lino; | |
372 | what->size = 1; | |
373 | what->fi_code = fi_code; | |
374 | return what; | |
375 | } | |
376 | ||
b40c0f02 MM |
377 | static inline struct f_inst * |
378 | fi_constant(struct f_inst *what, struct f_val val) | |
379 | { | |
380 | what->fi_code = FI_CONSTANT; | |
381 | what->i_FI_CONSTANT.val = val; | |
382 | return what; | |
383 | } | |
384 | ||
385 | #define v1 whati->f1->i_FI_CONSTANT.val | |
386 | #define v2 whati->f2->i_FI_CONSTANT.val | |
387 | #define v3 whati->f3->i_FI_CONSTANT.val | |
388 | #define runtime(fmt, ...) cf_error("filter preevaluation, line %d: " fmt, ifs->lino, ##__VA_ARGS__) | |
389 | #define fpool cfg_mem | |
390 | #define falloc(size) cfg_alloc(size) | |
de12cd18 MM |
391 | /* Instruction constructors */ |
392 | FID_WR_PUT(3) | |
b40c0f02 MM |
393 | #undef v1 |
394 | #undef v2 | |
395 | #undef v3 | |
de12cd18 MM |
396 | |
397 | /* Line dumpers */ | |
398 | #define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1) | |
399 | static const char f_dump_line_indent_str[] = " "; | |
de12cd18 MM |
400 | |
401 | FID_WR_PUT(6) | |
402 | ||
ea4f55e3 | 403 | void f_dump_line(const struct f_line *dest, uint indent) |
de12cd18 MM |
404 | { |
405 | if (!dest) { | |
406 | debug("%sNo filter line (NULL)\n", INDENT); | |
407 | return; | |
408 | } | |
409 | debug("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len); | |
410 | for (uint i=0; i<dest->len; i++) { | |
411 | const struct f_line_item *item = &dest->items[i]; | |
412 | debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name(item->fi_code), item->lineno); | |
413 | switch (item->fi_code) { | |
414 | FID_WR_PUT(7) | |
415 | default: bug("Unknown instruction %x in f_dump_line", item->fi_code); | |
416 | } | |
417 | } | |
418 | debug("%sFilter line %p dump done\n", INDENT, dest); | |
419 | } | |
420 | ||
23e3b1e6 | 421 | /* Linearize */ |
dd4d4095 | 422 | static uint |
63f49457 | 423 | linearize(struct f_line *dest, const struct f_inst *what, uint pos) |
dd4d4095 | 424 | { |
63f49457 MM |
425 | for ( ; what; what = what->next) { |
426 | switch (what->fi_code) { | |
dd4d4095 MM |
427 | FID_WR_PUT(8) |
428 | } | |
429 | pos++; | |
430 | } | |
431 | return pos; | |
432 | } | |
433 | ||
434 | struct f_line * | |
23e3b1e6 | 435 | f_linearize_concat(const struct f_inst * const inst[], uint count) |
dd4d4095 MM |
436 | { |
437 | uint len = 0; | |
438 | for (uint i=0; i<count; i++) | |
439 | for (const struct f_inst *what = inst[i]; what; what = what->next) | |
440 | len += what->size; | |
441 | ||
442 | struct f_line *out = cfg_allocz(sizeof(struct f_line) + sizeof(struct f_line_item)*len); | |
443 | ||
444 | for (uint i=0; i<count; i++) | |
23e3b1e6 | 445 | out->len = linearize(out, inst[i], out->len); |
dd4d4095 MM |
446 | |
447 | #if DEBUGGING | |
448 | f_dump_line(out, 0); | |
449 | #endif | |
450 | return out; | |
451 | } | |
452 | ||
132529ce MM |
453 | /* Filter line comparison */ |
454 | int | |
455 | f_same(const struct f_line *fl1, const struct f_line *fl2) | |
456 | { | |
457 | if ((!fl1) && (!fl2)) | |
458 | return 1; | |
459 | if ((!fl1) || (!fl2)) | |
460 | return 0; | |
461 | if (fl1->len != fl2->len) | |
462 | return 0; | |
463 | for (uint i=0; i<fl1->len; i++) { | |
ea4f55e3 MM |
464 | #define f1_ (&(fl1->items[i])) |
465 | #define f2_ (&(fl2->items[i])) | |
466 | if (f1_->fi_code != f2_->fi_code) | |
132529ce | 467 | return 0; |
ea4f55e3 | 468 | if (f1_->flags != f2_->flags) |
132529ce MM |
469 | return 0; |
470 | ||
ea4f55e3 | 471 | switch(f1_->fi_code) { |
132529ce MM |
472 | FID_WR_PUT(9) |
473 | } | |
474 | } | |
ea4f55e3 MM |
475 | #undef f1_ |
476 | #undef f2_ | |
132529ce MM |
477 | return 1; |
478 | } | |
479 | ||
480 | ||
64bb1346 | 481 | FID_WR_DIRECT(H) |
de12cd18 MM |
482 | /* Filter instruction codes */ |
483 | enum f_instruction_code { | |
84c58aab | 484 | FID_WR_PUT(4)m4_dnl |
ea4f55e3 | 485 | } PACKED; |
4f082dfa | 486 | |
ea4f55e3 | 487 | /* Filter instruction structure for config */ |
4f082dfa | 488 | struct f_inst { |
96d757c1 | 489 | struct f_inst *next; /* Next instruction */ |
4f082dfa MM |
490 | enum f_instruction_code fi_code; /* Instruction code */ |
491 | int size; /* How many instructions are underneath */ | |
492 | int lineno; /* Line number */ | |
ea4f55e3 | 493 | union { |
84c58aab | 494 | FID_WR_PUT(1)m4_dnl |
ea4f55e3 MM |
495 | }; |
496 | }; | |
497 | ||
498 | /* Filter line item */ | |
499 | struct f_line_item { | |
500 | enum f_instruction_code fi_code; /* What to do */ | |
501 | enum f_instruction_flags flags; /* Flags, instruction-specific */ | |
502 | uint lineno; /* Where */ | |
4f082dfa | 503 | union { |
84c58aab | 504 | FID_WR_PUT(2)m4_dnl |
4f082dfa MM |
505 | }; |
506 | }; | |
507 | ||
508 | /* Instruction constructors */ | |
04160812 MM |
509 | FID_WR_PUT(3) |
510 | m4_divert(-1) | |
550a6488 MM |
511 | |
512 | # 4) Shipout | |
513 | # | |
514 | # Everything is prepared in FID_WR_PUT_LIST now. Let's go! | |
515 | ||
04160812 MM |
516 | m4_changequote(`,') |
517 | ||
550a6488 | 518 | # Flusher auxiliary macro |
b40c0f02 | 519 | m4_define(FID_FLUSH, `m4_ifelse($1,$2,,[[m4_undivert($1)FID_FLUSH(m4_eval($1+1),$2)]])') |
550a6488 MM |
520 | |
521 | # Defining the macro used in FID_WR_PUT_LIST | |
64bb1346 MM |
522 | m4_define(FID_WR_DPUT, `m4_undivert($1)') |
523 | ||
550a6488 | 524 | # After the code is read and parsed, we: |
b40c0f02 | 525 | m4_m4wrap(`INST_FLUSH()m4_divert(0)FID_WR_PUT_LIST()m4_divert(-1)FID_FLUSH(1,200)') |
4f082dfa MM |
526 | |
527 | m4_changequote([[,]]) | |
550a6488 MM |
528 | # And now M4 is going to parse f-inst.c, fill the diversions |
529 | # and after the file is done, the content of m4_m4wrap (see before) | |
530 | # is executed. |