RPA Toolkit
Modified documentation and exaples. Removed RPA_RECORD_INVALID_UID.
[rpatk.git] / rpa / rpadbex.c
1 /**
2  *\file rpadbex.c
3  */
4
5 #include "rpa/rpacompiler.h"
6 #include "rpa/rpadbex.h"
7 #include "rpa/rpastatpriv.h"
8 #include "rpa/rpaparser.h"
9 #include "rpa/rpaoptimization.h"
10 #include "rlib/rmem.h"
11 #include "rlib/rutf.h"
12
13 typedef rinteger (*rpa_dbex_recordhandler)(rpadbex_t *dbex, rlong rec);
14
15 #define RPA_RULEINFO_NONE 0
16 #define RPA_RULEINFO_NAMEDRULE 1
17 #define RPA_RULEINFO_ANONYMOUSRULE 2
18 #define RPA_RULEINFO_DIRECTIVE 3
19
20 #define RPA_DBEX_SETERRINFO_CODE(__d__, __e__) do { (__d__)->err.code = __e__; (__d__)->err.mask |= RPA_ERRINFO_CODE; } while (0)
21 #define RPA_DBEX_SETERRINFO_OFFSET(__d__, __o__) do { (__d__)->err.offset = __o__; (__d__)->err.mask |= RPA_ERRINFO_OFFSET; } while (0)
22 #define RPA_DBEX_SETERRINFO_LINE(__d__, __l__) do { (__d__)->err.line = __l__; (__d__)->err.mask |= RPA_ERRINFO_LINE; } while (0)
23 #define RPA_DBEX_SETERRINFO_RULEID(__d__, __r__) do { (__d__)->err.ruleid = __r__; (__d__)->err.mask |= RPA_ERRINFO_RULEID; } while (0)
24 #define RPA_DBEX_SETERRINFO_NAME(__d__, __n__, __s__) do { \
25         (__d__)->err.mask |= RPA_ERRINFO_NAME; \
26         r_memset((__d__)->err.name, 0, sizeof((__d__)->err.name)); \
27         r_strncpy((__d__)->err.name, __n__, R_MIN(__s__, (sizeof((__d__)->err.name) - 1)));  } while (0)
28
29
30 typedef struct rpa_ruleinfo_s {
31         rlong startrec;
32         rlong sizerecs;
33         rlong codeoff;
34         rlong codesiz;
35         rulong type;
36 } rpa_ruleinfo_t;
37
38
39 struct rpadbex_s {
40         rpa_compiler_t *co;
41         rpa_parser_t *pa;
42         rarray_t *records;
43         rarray_t *temprecords;
44         rharray_t *rules;
45         rarray_t *recstack;
46         rarray_t *inlinestack;
47         rarray_t *text;
48         rpa_dbex_recordhandler *handlers;
49         rpa_errinfo_t err;
50         rulong headoff;
51         rulong optimizations:1;
52         rulong debug:1;
53         rulong compiled:1;
54 };
55
56 static rparecord_t *rpa_dbex_rulerecord(rpadbex_t *dbex, rparule_t rid);
57 static rparecord_t *rpa_dbex_record(rpadbex_t *dbex, rlong rec);
58 static rinteger rpa_dbex_rulename(rpadbex_t *dbex, rlong rec, const rchar **name, rsize_t *namesize);
59 static rinteger rpa_parseinfo_loopdetect(rpadbex_t *dbex, rlong parent, rlong loopto);
60 static rlong rpa_dbex_firstinlined(rpadbex_t *dbex);
61 static rinteger rpa_dbex_findinlined(rpadbex_t *dbex, rlong startrec);
62 static rinteger rpa_dbex_playchildrecords(rpadbex_t *dbex, rlong rec);
63 static rinteger rpa_dbex_playreversechildrecords(rpadbex_t *dbex, rlong rec);
64 static rinteger rpa_dbex_playrecord(rpadbex_t *dbex, rlong rec);
65 static rinteger rpa_dbex_rh_default(rpadbex_t *dbex, rlong rec);
66
67
68 void rpa_dbex_debug_recordhead(rpadbex_t *dbex, rlong rec)
69 {
70         if (dbex->debug) {
71                 rarray_t *records = dbex->records;
72                 rparecord_t *prec = (rparecord_t *) r_array_slot(records, rec);
73                 dbex->headoff = rvm_codegen_getcodesize(dbex->co->cg);
74                 if (prec->type & RPA_RECORD_START) {
75                         rpa_record_dump(records, rec);
76                 }
77         }
78
79 }
80
81
82 void rpa_dbex_debug_recordtail(rpadbex_t *dbex, rlong rec)
83 {
84         if (dbex->debug) {
85                 rarray_t *records = dbex->records;
86                 rparecord_t *prec = (rparecord_t *) r_array_slot(records, rec);
87                 rvm_asm_dump(rvm_codegen_getcode(dbex->co->cg, dbex->headoff), rvm_codegen_getcodesize(dbex->co->cg) - dbex->headoff);
88                 if (prec->type & RPA_RECORD_END) {
89                         rpa_record_dump(records, rec);
90                 }
91         }
92 }
93
94
95 static rinteger rpa_dbex_rh_default(rpadbex_t *dbex, rlong rec)
96 {
97         rarray_t *records = dbex->records;
98         rparecord_t *prec;
99
100         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
101         prec = rpa_dbex_record(dbex, rec);
102         R_ASSERT(prec);
103         rpa_dbex_debug_recordhead(dbex, rec);
104         rpa_dbex_debug_recordtail(dbex, rec);
105         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
106                 return -1;
107         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
108         prec = rpa_dbex_record(dbex, rec);
109         R_ASSERT(prec);
110         rpa_dbex_debug_recordhead(dbex, rec);
111         rpa_dbex_debug_recordtail(dbex, rec);
112         return 0;
113 }
114
115
116
117 static rinteger rpa_dbex_playrecord(rpadbex_t *dbex, rlong rec)
118 {
119         rarray_t *records = dbex->records;
120         rparecord_t *prec = (rparecord_t *)r_array_slot(records, rec);
121
122         if (prec->ruleuid >= 0 && prec->ruleuid < RPA_PRODUCTION_COUNT && dbex->handlers[prec->ruleuid]) {
123                 return dbex->handlers[prec->ruleuid](dbex, rec);
124         }
125         return rpa_dbex_rh_default(dbex, rec);
126 }
127
128
129 static rinteger rpa_dbex_playchildrecords(rpadbex_t *dbex, rlong rec)
130 {
131         rlong child;
132         rarray_t *records = dbex->records;
133
134         for (child = rpa_recordtree_firstchild(records, rec, RPA_RECORD_START); child >= 0; child = rpa_recordtree_next(records, child, RPA_RECORD_START)) {
135                 if (rpa_dbex_playrecord(dbex, child) < 0)
136                         return -1;
137         }
138         return 0;
139 }
140
141
142 static rinteger rpa_dbex_playreversechildrecords(rpadbex_t *dbex, rlong rec)
143 {
144         rlong child;
145         rarray_t *records = dbex->records;
146
147         for (child = rpa_recordtree_lastchild(records, rec, RPA_RECORD_START); child >= 0; child = rpa_recordtree_prev(records, child, RPA_RECORD_START)) {
148                 if (rpa_dbex_playrecord(dbex, child) < 0)
149                         return -1;
150         }
151
152         return 0;
153 }
154
155
156 static rlong rpa_dbex_getmatchchr(rulong matchtype)
157 {
158         switch (matchtype & RPA_MATCH_MASK) {
159         default:
160         case RPA_MATCH_NONE:
161                 return RPA_MATCHCHR_NAN;
162                 break;
163         case RPA_MATCH_MULTIPLE:
164                 return RPA_MATCHCHR_MUL;
165                 break;
166         case RPA_MATCH_OPTIONAL:
167                 return RPA_MATCHCHR_OPT;
168                 break;
169         case RPA_MATCH_MULTIOPT:
170                 return RPA_MATCHCHR_MOP;
171                 break;
172         };
173         return RPA_MATCHCHR_NAN;
174 }
175
176
177 static rlong rpa_dbex_getmatchspecialchr(rulong matchtype)
178 {
179         switch (matchtype & RPA_MATCH_MASK) {
180         default:
181         case RPA_MATCH_NONE:
182                 return RPA_MATCHSPCHR_NAN;
183                 break;
184         case RPA_MATCH_MULTIPLE:
185                 return RPA_MATCHSPCHR_MUL;
186                 break;
187         case RPA_MATCH_OPTIONAL:
188                 return RPA_MATCHSPCHR_OPT;
189                 break;
190         case RPA_MATCH_MULTIOPT:
191                 return RPA_MATCHSPCHR_MOP;
192                 break;
193         };
194         return RPA_MATCHSPCHR_NAN;
195 }
196
197
198 static rinteger rpa_record2long(rparecord_t *prec, ruint32 *num)
199 {
200         rchar *endptr = NULL;
201         rchar buffer[64];
202
203         if (!prec || !num || prec->inputsiz == 0 || prec->inputsiz >= sizeof(buffer))
204                 return -1;
205         r_memset(buffer, 0, sizeof(buffer));
206         r_memcpy(buffer, prec->input, prec->inputsiz);
207         if (prec->ruleuid == RPA_PRODUCTION_HEX) {
208                 *num = (ruint32)r_strtoul(prec->input, &endptr, 16);
209         } else if (prec->ruleuid == RPA_PRODUCTION_DEC) {
210                 *num = (ruint32)r_strtoul(prec->input, &endptr, 10);
211         } else {
212                 return -1;
213         }
214         return 0;
215 }
216
217
218 static rinteger rpa_dbex_rh_uid(rpadbex_t *dbex, rlong rec)
219 {
220         const rchar *name = NULL;
221         rsize_t namesize;
222         ruint32 uid = 0;
223         rparecord_t *pnumrec;
224         rarray_t *records = dbex->records;
225         rparecord_t *prec;
226
227         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
228         prec = rpa_dbex_record(dbex, rec);
229         R_ASSERT(prec);
230         rpa_dbex_debug_recordhead(dbex, rec);
231         if (rpa_dbex_rulename(dbex, rec, &name, &namesize) < 0) {
232                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_SYNTAXERROR);
233                 return -1;
234         }
235         pnumrec = rpa_dbex_record(dbex, rpa_recordtree_lastchild(dbex->records, rec, RPA_RECORD_END));
236         if (!pnumrec) {
237                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_SYNTAXERROR);
238                 return -1;
239         }
240         if (rpa_record2long(pnumrec, &uid) < 0) {
241                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_SYNTAXERROR);
242                 return -1;
243         }
244         rpa_compiler_rulepref_set_ruleuid(dbex->co, name, namesize, uid);
245         rpa_compiler_rulepref_set_flag(dbex->co, name, namesize, RPA_RFLAG_EMITRECORD);
246         rpa_dbex_debug_recordtail(dbex, rec);
247         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
248                 return -1;
249         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
250         prec = rpa_dbex_record(dbex, rec);
251         R_ASSERT(prec);
252         rpa_dbex_debug_recordhead(dbex, rec);
253         rpa_dbex_debug_recordtail(dbex, rec);
254         return 0;
255 }
256
257
258 static rinteger rpa_dbex_rh_abort(rpadbex_t *dbex, rlong rec)
259 {
260         const rchar *name = NULL;
261         rsize_t namesize;
262         rarray_t *records = dbex->records;
263         rparecord_t *prec;
264
265         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
266         prec = rpa_dbex_record(dbex, rec);
267         R_ASSERT(prec);
268         rpa_dbex_debug_recordhead(dbex, rec);
269         if (rpa_dbex_rulename(dbex, rec, &name, &namesize) < 0) {
270                 return -1;
271         }
272         rpa_compiler_rulepref_set_flag(dbex->co, name, namesize, RPA_RFLAG_ABORTONFAIL);
273         rpa_dbex_debug_recordtail(dbex, rec);
274         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
275                 return -1;
276         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
277         prec = rpa_dbex_record(dbex, rec);
278         R_ASSERT(prec);
279         rpa_dbex_debug_recordhead(dbex, rec);
280         rpa_dbex_debug_recordtail(dbex, rec);
281         return 0;
282 }
283
284
285 static rinteger rpa_dbex_rh_emit(rpadbex_t *dbex, rlong rec)
286 {
287         const rchar *name = NULL;
288         rsize_t namesize;
289         rarray_t *records = dbex->records;
290         rparecord_t *prec;
291
292         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
293         prec = rpa_dbex_record(dbex, rec);
294         R_ASSERT(prec);
295         rpa_dbex_debug_recordhead(dbex, rec);
296         if (rpa_dbex_rulename(dbex, rec, &name, &namesize) < 0) {
297                 return -1;
298         }
299         rpa_compiler_rulepref_set_flag(dbex->co, name, namesize, RPA_RFLAG_EMITRECORD);
300         rpa_dbex_debug_recordtail(dbex, rec);
301         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
302                 return -1;
303         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
304         prec = rpa_dbex_record(dbex, rec);
305         R_ASSERT(prec);
306         rpa_dbex_debug_recordhead(dbex, rec);
307         rpa_dbex_debug_recordtail(dbex, rec);
308         return 0;
309 }
310
311
312 static rinteger rpa_dbex_rh_noemit(rpadbex_t *dbex, rlong rec)
313 {
314         const rchar *name = NULL;
315         rsize_t namesize;
316         rarray_t *records = dbex->records;
317         rparecord_t *prec;
318
319         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
320         prec = rpa_dbex_record(dbex, rec);
321         R_ASSERT(prec);
322         rpa_dbex_debug_recordhead(dbex, rec);
323         if (rpa_dbex_rulename(dbex, rec, &name, &namesize) < 0) {
324                 return -1;
325         }
326         rpa_compiler_rulepref_clear_flag(dbex->co, name, namesize, RPA_RFLAG_EMITRECORD);
327         rpa_dbex_debug_recordtail(dbex, rec);
328         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
329                 return -1;
330         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
331         prec = rpa_dbex_record(dbex, rec);
332         R_ASSERT(prec);
333         rpa_dbex_debug_recordhead(dbex, rec);
334         rpa_dbex_debug_recordtail(dbex, rec);
335         return 0;
336 }
337
338
339 static rinteger rpa_dbex_setemit(rpadbex_t *dbex, rboolean emit)
340 {
341         rlong i;
342         rpa_ruleinfo_t *info;
343
344         for (i = 0; i < r_array_length(dbex->rules->names); i++) {
345                 rstr_t *name = r_array_index(dbex->rules->names, i, rstr_t*);
346                 info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, i);
347                 if (info->type == RPA_RULEINFO_NAMEDRULE) {
348                         if (emit) {
349                                 rpa_compiler_rulepref_set_flag(dbex->co, name->str, name->size, RPA_RFLAG_EMITRECORD);
350                         } else {
351                                 rpa_compiler_rulepref_clear_flag(dbex->co, name->str, name->size, RPA_RFLAG_EMITRECORD);
352                         }
353                 }
354         }
355         return 0;
356 }
357
358
359 static rinteger rpa_dbex_rh_emitall(rpadbex_t *dbex, rlong rec)
360 {
361         rarray_t *records = dbex->records;
362         rparecord_t *prec;
363
364         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
365         prec = rpa_dbex_record(dbex, rec);
366         R_ASSERT(prec);
367         rpa_dbex_debug_recordhead(dbex, rec);
368         rpa_dbex_setemit(dbex, TRUE);
369         rpa_dbex_debug_recordtail(dbex, rec);
370         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
371                 return -1;
372         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
373         prec = rpa_dbex_record(dbex, rec);
374         R_ASSERT(prec);
375         rpa_dbex_debug_recordhead(dbex, rec);
376         rpa_dbex_debug_recordtail(dbex, rec);
377         return 0;
378 }
379
380
381 static rinteger rpa_dbex_rh_emitnone(rpadbex_t *dbex, rlong rec)
382 {
383         rarray_t *records = dbex->records;
384         rparecord_t *prec;
385
386         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
387         prec = rpa_dbex_record(dbex, rec);
388         R_ASSERT(prec);
389         rpa_dbex_debug_recordhead(dbex, rec);
390         rpa_dbex_setemit(dbex, FALSE);
391         rpa_dbex_debug_recordtail(dbex, rec);
392         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
393                 return -1;
394         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
395         prec = rpa_dbex_record(dbex, rec);
396         R_ASSERT(prec);
397         rpa_dbex_debug_recordhead(dbex, rec);
398         rpa_dbex_debug_recordtail(dbex, rec);
399         return 0;
400 }
401
402
403 static rinteger rpa_dbex_rh_namedrule(rpadbex_t *dbex, rlong rec)
404 {
405         const rchar *name = NULL;
406         rsize_t namesize;
407         rarray_t *records = dbex->records;
408         rparecord_t *prec = (rparecord_t *) r_array_slot(dbex->records, rec);
409
410         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
411         prec = rpa_dbex_record(dbex, rec);
412         R_ASSERT(prec);
413         rpa_dbex_debug_recordhead(dbex, rec);
414         if (rpa_dbex_rulename(dbex, rec, &name, &namesize) < 0) {
415
416                 return -1;
417         }
418         if (!r_array_empty(dbex->inlinestack)) {
419                 rpa_compiler_inlinerule_begin(dbex->co, name, namesize, 0);
420         } else {
421                 rvm_codegen_addins(dbex->co->cg, rvm_asm(RPA_SHIFT, XX, XX, XX, 0));
422                 rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_BL, DA, XX, XX, 3));
423                 rvm_codegen_addins(dbex->co->cg, rvm_asm(RPA_EMITTAIL, XX, XX, XX, 0));
424                 rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_EXT, XX, XX, XX, 0));
425
426                 if ((prec->usertype & RPA_LOOP_PATH)) {
427                         rpa_compiler_loop_begin(dbex->co, name, namesize);
428                 } else {
429                         rpa_compiler_rule_begin(dbex->co, name, namesize);
430                 }
431         }
432         r_array_add(dbex->inlinestack, &rec);
433         rpa_dbex_debug_recordtail(dbex, rec);
434         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
435                 return -1;
436         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
437         prec = rpa_dbex_record(dbex, rec);
438         R_ASSERT(prec);
439         rpa_dbex_debug_recordhead(dbex, rec);
440         r_array_removelast(dbex->inlinestack);
441         if (!r_array_empty(dbex->inlinestack)) {
442                 rpa_compiler_inlinerule_end(dbex->co);
443         } else {
444                 if ((prec->usertype & RPA_LOOP_PATH)) {
445                         rpa_compiler_loop_end(dbex->co);
446                 } else {
447                         rpa_compiler_rule_end(dbex->co);
448                 }
449         }
450         rpa_dbex_debug_recordtail(dbex, rec);
451         return 0;
452 }
453
454
455 static rinteger rpa_dbex_rh_anonymousrule(rpadbex_t *dbex, rlong rec)
456 {
457         rarray_t *records = dbex->records;
458         rparecord_t *prec = (rparecord_t *) r_array_slot(dbex->records, rec);
459
460         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
461         prec = rpa_dbex_record(dbex, rec);
462         R_ASSERT(prec);
463         rpa_dbex_debug_recordhead(dbex, rec);
464         rvm_codegen_addins(dbex->co->cg, rvm_asm(RPA_SHIFT, XX, XX, XX, 0));
465         rpa_compiler_exp_begin(dbex->co, RPA_MATCH_NONE);
466         rpa_dbex_debug_recordtail(dbex, rec);
467         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
468                 return -1;
469         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
470         prec = rpa_dbex_record(dbex, rec);
471         R_ASSERT(prec);
472         rpa_dbex_debug_recordhead(dbex, rec);
473         rpa_compiler_exp_end(dbex->co);
474         rvm_codegen_addins(dbex->co->cg, rvm_asm(RPA_EMITTAIL, XX, XX, XX, 0));
475         rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_EXT, XX, XX, XX, 0));
476         rpa_dbex_debug_recordtail(dbex, rec);
477
478         return 0;
479 }
480
481
482 static rinteger rpa_dbex_rh_char(rpadbex_t *dbex, rlong rec)
483 {
484         rparecord_t *prec;
485         rarray_t *records = dbex->records;
486         ruint32 wc = 0;
487
488         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
489         prec = rpa_dbex_record(dbex, rec);
490         R_ASSERT(prec);
491         rpa_dbex_debug_recordhead(dbex, rec);
492         rpa_dbex_debug_recordtail(dbex, rec);
493         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
494                 return -1;
495         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
496         prec = rpa_dbex_record(dbex, rec);
497         R_ASSERT(prec);
498         rpa_dbex_debug_recordhead(dbex, rec);
499         if (r_utf8_mbtowc(&wc, (const ruchar*) prec->input, (const ruchar*)prec->input + prec->inputsiz) < 0) {
500
501                 return -1;
502         }
503         rvm_codegen_addins(dbex->co->cg, rvm_asm(rpa_dbex_getmatchchr(prec->usertype & RPA_MATCH_MASK), DA, XX, XX, wc));
504         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
505         rpa_dbex_debug_recordtail(dbex, rec);
506         return 0;
507 }
508
509
510 static rinteger rpa_dbex_rh_specialchar(rpadbex_t *dbex, rlong rec)
511 {
512         ruint32 wc = 0;
513         rarray_t *records = dbex->records;
514         rparecord_t *prec;
515
516         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
517         prec = rpa_dbex_record(dbex, rec);
518         R_ASSERT(prec);
519         rpa_dbex_debug_recordhead(dbex, rec);
520         rpa_dbex_debug_recordtail(dbex, rec);
521         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
522                 return -1;
523         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
524         prec = rpa_dbex_record(dbex, rec);
525         R_ASSERT(prec);
526         rpa_dbex_debug_recordhead(dbex, rec);
527         if (r_utf8_mbtowc(&wc, (const ruchar*) prec->input, (const ruchar*)prec->input + prec->inputsiz) < 0) {
528
529                 return -1;
530         }
531         rvm_codegen_addins(dbex->co->cg, rvm_asm(rpa_dbex_getmatchspecialchr(prec->usertype & RPA_MATCH_MASK), DA, XX, XX, wc));
532         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
533         rpa_dbex_debug_recordtail(dbex, rec);
534         return 0;
535 }
536
537
538 static rinteger rpa_dbex_rh_cls(rpadbex_t *dbex, rlong rec)
539 {
540         rarray_t *records = dbex->records;
541         rparecord_t *prec;
542
543         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
544         prec = rpa_dbex_record(dbex, rec);
545         R_ASSERT(prec);
546         rpa_dbex_debug_recordhead(dbex, rec);
547         rpa_compiler_class_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
548         rpa_dbex_debug_recordtail(dbex, rec);
549         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
550                 return -1;
551         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
552         prec = rpa_dbex_record(dbex, rec);
553         R_ASSERT(prec);
554         rpa_dbex_debug_recordhead(dbex, rec);
555         rpa_compiler_class_end(dbex->co);
556         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
557         rpa_dbex_debug_recordtail(dbex, rec);
558         return 0;
559 }
560
561
562 static rinteger rpa_dbex_rh_clschar(rpadbex_t *dbex, rlong rec)
563 {
564         ruint32 wc = 0;
565         rarray_t *records = dbex->records;
566         rparecord_t *prec;
567
568         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
569         prec = rpa_dbex_record(dbex, rec);
570         R_ASSERT(prec);
571         rpa_dbex_debug_recordhead(dbex, rec);
572         rpa_dbex_debug_recordtail(dbex, rec);
573         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
574                 return -1;
575         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
576         prec = rpa_dbex_record(dbex, rec);
577         R_ASSERT(prec);
578         rpa_dbex_debug_recordhead(dbex, rec);
579         if (r_utf8_mbtowc(&wc, (const ruchar*) prec->input, (const ruchar*)prec->input + prec->inputsiz) < 0) {
580
581                 return -1;
582         }
583         rvm_codegen_addins(dbex->co->cg, rvm_asm(RPA_MATCHCHR_NAN, DA, XX, XX, wc));
584         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BGRE, DA, XX, XX, 0));
585         rpa_dbex_debug_recordtail(dbex, rec);
586         return 0;
587 }
588
589
590 static rinteger rpa_dbex_rh_minexp(rpadbex_t *dbex, rlong rec)
591 {
592         rarray_t *records = dbex->records;
593         rparecord_t *prec;
594
595         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
596         prec = rpa_dbex_record(dbex, rec);
597         R_ASSERT(prec);
598         rpa_dbex_debug_recordhead(dbex, rec);
599         rpa_compiler_exp_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
600         rpa_dbex_debug_recordtail(dbex, rec);
601         if (rpa_dbex_playreversechildrecords(dbex, rec) < 0)
602                 return -1;
603         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
604         prec = rpa_dbex_record(dbex, rec);
605         R_ASSERT(prec);
606         rpa_dbex_debug_recordhead(dbex, rec);
607         rpa_compiler_exp_end(dbex->co);
608         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
609         rpa_dbex_debug_recordtail(dbex, rec);
610         return 0;
611 }
612
613
614 static rinteger rpa_dbex_rh_exp(rpadbex_t *dbex, rlong rec)
615 {
616         rarray_t *records = dbex->records;
617         rparecord_t *prec;
618
619         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
620         prec = rpa_dbex_record(dbex, rec);
621         R_ASSERT(prec);
622         rpa_dbex_debug_recordhead(dbex, rec);
623         rpa_compiler_exp_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
624         rpa_dbex_debug_recordtail(dbex, rec);
625         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
626                 return -1;
627         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
628         prec = rpa_dbex_record(dbex, rec);
629         R_ASSERT(prec);
630         rpa_dbex_debug_recordhead(dbex, rec);
631         rpa_compiler_exp_end(dbex->co);
632         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
633         rpa_dbex_debug_recordtail(dbex, rec);
634         return 0;
635 }
636
637
638 static rinteger rpa_dbex_rh_orop(rpadbex_t *dbex, rlong rec)
639 {
640         rarray_t *records = dbex->records;
641         rparecord_t *prec;
642
643         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
644         prec = rpa_dbex_record(dbex, rec);
645         R_ASSERT(prec);
646         rpa_dbex_debug_recordhead(dbex, rec);
647         rpa_compiler_altexp_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
648         rpa_dbex_debug_recordtail(dbex, rec);
649         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
650                 return -1;
651         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
652         prec = rpa_dbex_record(dbex, rec);
653         R_ASSERT(prec);
654         rpa_dbex_debug_recordhead(dbex, rec);
655         rpa_compiler_altexp_end(dbex->co);
656         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
657         rpa_dbex_debug_recordtail(dbex, rec);
658         return 0;
659 }
660
661
662 static rinteger rpa_dbex_rh_norop(rpadbex_t *dbex, rlong rec)
663 {
664         rarray_t *records = dbex->records;
665         rparecord_t *prec;
666
667         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
668         prec = rpa_dbex_record(dbex, rec);
669         R_ASSERT(prec);
670         rpa_dbex_debug_recordhead(dbex, rec);
671         rpa_compiler_altexp_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
672         rpa_dbex_debug_recordtail(dbex, rec);
673         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
674                 return -1;
675         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
676         prec = rpa_dbex_record(dbex, rec);
677         R_ASSERT(prec);
678         rpa_dbex_debug_recordhead(dbex, rec);
679         rpa_compiler_altexp_end(dbex->co);
680         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BGRE, DA, XX, XX, 0));
681         rpa_dbex_debug_recordtail(dbex, rec);
682         return 0;
683 }
684
685
686 static rinteger rpa_dbex_rh_notop(rpadbex_t *dbex, rlong rec)
687 {
688         rarray_t *records = dbex->records;
689         rparecord_t *prec;
690
691         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
692         prec = rpa_dbex_record(dbex, rec);
693         R_ASSERT(prec);
694         rpa_dbex_debug_recordhead(dbex, rec);
695         rpa_compiler_notexp_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
696         rpa_dbex_debug_recordtail(dbex, rec);
697         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
698                 return -1;
699         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
700         prec = rpa_dbex_record(dbex, rec);
701         R_ASSERT(prec);
702         rpa_dbex_debug_recordhead(dbex, rec);
703         rpa_compiler_notexp_end(dbex->co);
704         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
705         rpa_dbex_debug_recordtail(dbex, rec);
706         return 0;
707 }
708
709
710 static rinteger rpa_dbex_rh_range(rpadbex_t *dbex, rlong rec)
711 {
712         rarray_t *records = dbex->records;
713         rparecord_t *prec;
714
715         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
716         prec = rpa_dbex_record(dbex, rec);
717         R_ASSERT(prec);
718         rpa_dbex_debug_recordhead(dbex, rec);
719         dbex->co->currange.p1 = 0;
720         dbex->co->currange.p2 = 0;
721         rpa_dbex_debug_recordtail(dbex, rec);
722         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
723                 return -1;
724         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
725         prec = rpa_dbex_record(dbex, rec);
726         R_ASSERT(prec);
727         rpa_dbex_debug_recordhead(dbex, rec);
728         if (dbex->co->currange.p1 < dbex->co->currange.p2)
729                 rvm_codegen_addins(dbex->co->cg, rvm_asm2(RPA_MATCHRNG_NAN, DA, XX, XX, dbex->co->currange.p1, dbex->co->currange.p2));
730         else
731                 rvm_codegen_addins(dbex->co->cg, rvm_asm2(RPA_MATCHRNG_NAN, DA, XX, XX, dbex->co->currange.p2, dbex->co->currange.p1));
732         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BGRE, DA, XX, XX, 0));
733         rpa_dbex_debug_recordtail(dbex, rec);
734         return 0;
735 }
736
737
738 static rinteger rpa_dbex_rh_numrange(rpadbex_t *dbex, rlong rec)
739 {
740         rarray_t *records = dbex->records;
741         rparecord_t *prec;
742         rparecord_t *child;
743         /*
744          * Fix me: probably we don't need to access the children from here. There should be a way just to
745          * play them a regular records!
746          */
747
748         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
749         prec = rpa_dbex_record(dbex, rec);
750         R_ASSERT(prec);
751         rpa_dbex_debug_recordhead(dbex, rec);
752         child = rpa_dbex_record(dbex, rpa_recordtree_firstchild(dbex->records, rec, RPA_RECORD_END));
753         if (rpa_record2long(child, &dbex->co->currange.p1) < 0)
754                 return -1;
755         child = rpa_dbex_record(dbex, rpa_recordtree_lastchild(dbex->records, rec, RPA_RECORD_END));
756         if (rpa_record2long(child, &dbex->co->currange.p2) < 0)
757                 return -1;
758         rpa_dbex_debug_recordtail(dbex, rec);
759         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
760                 return -1;
761         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
762         prec = rpa_dbex_record(dbex, rec);
763         R_ASSERT(prec);
764         rpa_dbex_debug_recordhead(dbex, rec);
765         if (dbex->co->currange.p1 < dbex->co->currange.p2)
766                 rvm_codegen_addins(dbex->co->cg, rvm_asm2(RPA_MATCHRNG_NAN, DA, XX, XX, dbex->co->currange.p1, dbex->co->currange.p2));
767         else
768                 rvm_codegen_addins(dbex->co->cg, rvm_asm2(RPA_MATCHRNG_NAN, DA, XX, XX, dbex->co->currange.p2, dbex->co->currange.p1));
769         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BGRE, DA, XX, XX, 0));
770         rpa_dbex_debug_recordtail(dbex, rec);
771         return 0;
772 }
773
774
775 static rinteger rpa_dbex_rh_clsnum(rpadbex_t *dbex, rlong rec)
776 {
777         rarray_t *records = dbex->records;
778         rparecord_t *prec;
779         rparecord_t *child;
780         ruint32 wc;
781
782         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
783         prec = rpa_dbex_record(dbex, rec);
784         R_ASSERT(prec);
785         rpa_dbex_debug_recordhead(dbex, rec);
786         rpa_dbex_debug_recordtail(dbex, rec);
787         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
788                 return -1;
789         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
790         prec = rpa_dbex_record(dbex, rec);
791         R_ASSERT(prec);
792         rpa_dbex_debug_recordhead(dbex, rec);
793         child = rpa_dbex_record(dbex, rpa_recordtree_firstchild(dbex->records, rec, RPA_RECORD_END));
794         if (rpa_record2long(child, &wc) < 0)
795                 return -1;
796         rvm_codegen_addins(dbex->co->cg, rvm_asm(rpa_dbex_getmatchchr(prec->usertype & RPA_MATCH_MASK), DA, XX, XX, wc));
797         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BGRE, DA, XX, XX, 0));
798         rpa_dbex_debug_recordtail(dbex, rec);
799         return 0;
800 }
801
802
803 static rinteger rpa_dbex_rh_beginchar(rpadbex_t *dbex, rlong rec)
804 {
805         rarray_t *records = dbex->records;
806         rparecord_t *prec;
807         ruint32 wc = 0;
808
809         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
810         prec = rpa_dbex_record(dbex, rec);
811         R_ASSERT(prec);
812         rpa_dbex_debug_recordhead(dbex, rec);
813         rpa_dbex_debug_recordtail(dbex, rec);
814         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
815                 return -1;
816         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
817         prec = rpa_dbex_record(dbex, rec);
818         R_ASSERT(prec);
819         rpa_dbex_debug_recordhead(dbex, rec);
820         if (r_utf8_mbtowc(&wc, (const ruchar*) prec->input, (const ruchar*)prec->input + prec->inputsiz) < 0) {
821
822                 return -1;
823         }
824         dbex->co->currange.p1 = wc;
825         rpa_dbex_debug_recordtail(dbex, rec);
826         return 0;
827 }
828
829
830 static rinteger rpa_dbex_rh_endchar(rpadbex_t *dbex, rlong rec)
831 {
832         rarray_t *records = dbex->records;
833         rparecord_t *prec;
834         ruint32 wc = 0;
835
836         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
837         prec = rpa_dbex_record(dbex, rec);
838         R_ASSERT(prec);
839         rpa_dbex_debug_recordhead(dbex, rec);
840         rpa_dbex_debug_recordtail(dbex, rec);
841         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
842                 return -1;
843         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
844         prec = rpa_dbex_record(dbex, rec);
845         R_ASSERT(prec);
846         rpa_dbex_debug_recordhead(dbex, rec);
847         if (r_utf8_mbtowc(&wc, (const ruchar*) prec->input, (const ruchar*)prec->input + prec->inputsiz) < 0) {
848
849                 return -1;
850         }
851         dbex->co->currange.p2 = wc;
852         rpa_dbex_debug_recordtail(dbex, rec);
853         return 0;
854 }
855
856
857 static rinteger rpa_dbex_rh_branch(rpadbex_t *dbex, rlong rec)
858 {
859         rarray_t *records = dbex->records;
860         rparecord_t *prec;
861
862         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
863         prec = rpa_dbex_record(dbex, rec);
864         R_ASSERT(prec);
865         rpa_dbex_debug_recordhead(dbex, rec);
866         if (prec->usertype & RPA_NONLOOP_PATH) {
867                 rpa_compiler_nonloopybranch_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
868         } else {
869                 rpa_compiler_branch_begin(dbex->co, prec->usertype & RPA_MATCH_MASK);
870         }
871         rpa_dbex_debug_recordtail(dbex, rec);
872         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
873                 return -1;
874         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
875         prec = rpa_dbex_record(dbex, rec);
876         R_ASSERT(prec);
877         rpa_dbex_debug_recordhead(dbex, rec);
878         if (prec->usertype & RPA_NONLOOP_PATH) {
879                 rpa_compiler_nonloopybranch_end(dbex->co);
880         } else {
881                 rpa_compiler_branch_end(dbex->co);
882         }
883         rpa_dbex_debug_recordtail(dbex, rec);
884         return 0;
885 }
886
887
888 static void rpa_dbex_rh_loopref(rpadbex_t *dbex, rparecord_t *prec)
889 {
890         /*
891          * We ignore, it doesn't make sense for loops:
892          * RPA_MATCH_MULTIPLE
893          */
894         rpa_compiler_exp_begin(dbex->co, (prec->usertype & RPA_MATCH_OPTIONAL));
895         rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_CMP, R_LOO, DA, XX, 0));
896         rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_BGRE, DA, XX, XX, 3));
897         rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_MOVS, R0, DA, XX, -1));
898         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
899         rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_ADD, R_TOP, R_TOP, R_LOO, 0));
900         rvm_codegen_addins(dbex->co->cg, rvm_asm(RVM_MOVS, R0, R_LOO, XX, 0));
901         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
902         rpa_compiler_exp_end(dbex->co);
903 }
904
905
906 static rinteger rpa_dbex_rh_aref(rpadbex_t *dbex, rlong rec)
907 {
908         const rchar *name = NULL;
909         rsize_t namesize;
910         rpa_ruleinfo_t *info;
911         rarray_t *records = dbex->records;
912         rparecord_t *prec;
913
914         rec = rpa_recordtree_get(records, rec, RPA_RECORD_START);
915         prec = rpa_dbex_record(dbex, rec);
916         R_ASSERT(prec);
917         rpa_dbex_debug_recordhead(dbex, rec);
918         if (rpa_dbex_rulename(dbex, rec, &name, &namesize) < 0) {
919
920                 return -1;
921         }
922
923         if ((prec->usertype & RPA_LOOP_PATH) && rpa_parseinfo_loopdetect(dbex, rec, rpa_dbex_firstinlined(dbex))) {
924                 info = (rpa_ruleinfo_t *) r_harray_get(dbex->rules, rpa_dbex_lookup(dbex, name, namesize));
925                 if (!info) {
926                         RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_UNRESOLVEDSYMBOL);
927                         RPA_DBEX_SETERRINFO_NAME(dbex, name, namesize);
928                         return -1;
929                 }
930                 if (rpa_dbex_findinlined(dbex, info->startrec)) {
931                         rpa_dbex_rh_loopref(dbex, prec);
932                 } else {
933                         if (prec->usertype & RPA_MATCH_OPTIONAL) {
934                                 /*
935                                  * Most probably this is useless case - loop refs shouldn't have quantitative modifiers
936                                  * but in case they do we wrap the inlined production rule in quantitative expression.
937                                  * The inlined named rule can take the quantitative argument, but I just don't have
938                                  * a clean way to pass it from here - so, lets play the records inside an expression that
939                                  * has the right quantitative argument.
940                                  * We ignore, it doesn't make sense for loops:
941                                  * RPA_MATCH_MULTIPLE
942                                  */
943                                 rpa_compiler_exp_begin(dbex->co, RPA_MATCH_OPTIONAL);
944                                 rpa_dbex_playrecord(dbex, info->startrec);
945                                 rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
946                                 rpa_compiler_exp_end(dbex->co);
947                         } else {
948                                 rpa_dbex_playrecord(dbex, info->startrec);
949                         }
950                 }
951         } else {
952                 rpa_compiler_reference(dbex->co, name, namesize, (prec->usertype & RPA_MATCH_MASK));
953         }
954         rvm_codegen_index_addrelocins(dbex->co->cg, RVM_RELOC_BRANCH, RPA_COMPILER_CURRENTEXP(dbex->co)->endidx, rvm_asm(RVM_BLES, DA, XX, XX, 0));
955         rpa_dbex_debug_recordtail(dbex, rec);
956         if (rpa_dbex_playchildrecords(dbex, rec) < 0)
957                 return -1;
958         rec = rpa_recordtree_get(records, rec, RPA_RECORD_END);
959         prec = rpa_dbex_record(dbex, rec);
960         R_ASSERT(prec);
961         rpa_dbex_debug_recordhead(dbex, rec);
962         rpa_dbex_debug_recordtail(dbex, rec);
963         return 0;
964 }
965
966
967 rpadbex_t *rpa_dbex_create(void)
968 {
969         rpadbex_t *dbex = (rpadbex_t *) r_zmalloc(sizeof(*dbex));
970
971         dbex->co = rpa_compiler_create();
972         dbex->pa = rpa_parser_create();
973         dbex->text = r_array_create(sizeof(rchar *));
974         dbex->records = r_array_create(sizeof(rparecord_t));
975         dbex->temprecords = r_array_create(sizeof(rparecord_t));
976         dbex->rules = r_harray_create(sizeof(rpa_ruleinfo_t));
977         dbex->recstack = r_array_create(sizeof(rulong));
978         dbex->inlinestack = r_array_create(sizeof(rulong));
979         dbex->handlers = r_zmalloc(sizeof(rpa_dbex_recordhandler) * RPA_PRODUCTION_COUNT);
980         rpa_dbex_cfgset(dbex, RPA_DBEXCFG_OPTIMIZATIONS, 1);
981
982         dbex->handlers[RPA_PRODUCTION_NONE] = rpa_dbex_rh_default;
983         dbex->handlers[RPA_PRODUCTION_NAMEDRULE] = rpa_dbex_rh_namedrule;
984         dbex->handlers[RPA_PRODUCTION_ANONYMOUSRULE] = rpa_dbex_rh_anonymousrule;
985         dbex->handlers[RPA_PRODUCTION_CLS] = rpa_dbex_rh_cls;
986         dbex->handlers[RPA_PRODUCTION_CHAR] = rpa_dbex_rh_char;
987         dbex->handlers[RPA_PRODUCTION_SPECIALCHAR] = rpa_dbex_rh_specialchar;
988         dbex->handlers[RPA_PRODUCTION_CLSCHAR] = rpa_dbex_rh_clschar;
989         dbex->handlers[RPA_PRODUCTION_AREF] = rpa_dbex_rh_aref;
990         dbex->handlers[RPA_PRODUCTION_CREF] = rpa_dbex_rh_aref;
991         dbex->handlers[RPA_PRODUCTION_BRACKETEXP] = rpa_dbex_rh_exp;
992         dbex->handlers[RPA_PRODUCTION_OROP] = rpa_dbex_rh_orop;
993         dbex->handlers[RPA_PRODUCTION_NOTOP] = rpa_dbex_rh_notop;
994         dbex->handlers[RPA_PRODUCTION_ALTBRANCH] = rpa_dbex_rh_branch;
995         dbex->handlers[RPA_PRODUCTION_NEGBRANCH] = rpa_dbex_rh_branch;
996         dbex->handlers[RPA_PRODUCTION_CHARRNG] = rpa_dbex_rh_range;
997         dbex->handlers[RPA_PRODUCTION_NUMRNG] = rpa_dbex_rh_numrange;
998         dbex->handlers[RPA_PRODUCTION_CLSNUM] = rpa_dbex_rh_clsnum;
999         dbex->handlers[RPA_PRODUCTION_BEGINCHAR] = rpa_dbex_rh_beginchar;
1000         dbex->handlers[RPA_PRODUCTION_ENDCHAR] = rpa_dbex_rh_endchar;
1001         dbex->handlers[RPA_PRODUCTION_NOROP] = rpa_dbex_rh_norop;
1002         dbex->handlers[RPA_PRODUCTION_REQOP] = rpa_dbex_rh_exp;
1003         dbex->handlers[RPA_PRODUCTION_MINOP] = rpa_dbex_rh_minexp;
1004         dbex->handlers[RPA_PRODUCTION_DIRECTIVEEMIT] = rpa_dbex_rh_emit;
1005         dbex->handlers[RPA_PRODUCTION_DIRECTIVEABORT] = rpa_dbex_rh_abort;
1006         dbex->handlers[RPA_PRODUCTION_DIRECTIVENOEMIT] = rpa_dbex_rh_noemit;
1007         dbex->handlers[RPA_PRODUCTION_DIRECTIVEEMITALL] = rpa_dbex_rh_emitall;
1008         dbex->handlers[RPA_PRODUCTION_DIRECTIVEEMITNONE] = rpa_dbex_rh_emitnone;
1009         dbex->handlers[RPA_PRODUCTION_DIRECTIVEEMITID] = rpa_dbex_rh_uid;
1010
1011         return dbex;
1012 }
1013
1014
1015 void rpa_dbex_destroy(rpadbex_t *dbex)
1016 {
1017         int i;
1018         if (dbex) {
1019                 for (i = 0; i < r_array_length(dbex->text); i++)
1020                         r_free(r_array_index(dbex->text, i, rchar*));
1021                 rpa_compiler_destroy(dbex->co);
1022                 rpa_parser_destroy(dbex->pa);
1023                 r_harray_destroy(dbex->rules);
1024                 r_array_destroy(dbex->records);
1025                 r_array_destroy(dbex->temprecords);
1026                 r_array_destroy(dbex->recstack);
1027                 r_array_destroy(dbex->inlinestack);
1028                 r_array_destroy(dbex->text);
1029                 r_free(dbex->handlers);
1030                 r_free(dbex);
1031         }
1032 }
1033
1034
1035 static rinteger rpa_parseinfo_loopdetect_do(rpadbex_t *dbex, rlong parent, rlong loopto, rinteger inderction)
1036 {
1037         rsize_t namesiz;
1038         const rchar *name;
1039         rlong i;
1040         rinteger ret = 0;
1041         rparecord_t *prec;
1042
1043         if (parent == loopto && inderction > 0)
1044                 return 1;
1045         for (i = 0; i < r_array_length(dbex->recstack); i++) {
1046                 if (parent == r_array_index(dbex->recstack, i, rlong))
1047                         return 0;
1048         }
1049         r_array_add(dbex->recstack, &parent);
1050
1051         if (!(prec = (rparecord_t *)r_array_slot(dbex->records, parent)))
1052                 return 0;
1053         if (prec->ruleuid == RPA_PRODUCTION_AREF || prec->ruleuid == RPA_PRODUCTION_CREF)
1054                 i = parent;
1055         else
1056                 i = rpa_recordtree_firstchild(dbex->records, parent, RPA_RECORD_START);
1057         for (; i >= 0; i = rpa_recordtree_next(dbex->records, i, RPA_RECORD_START)) {
1058                 prec = (rparecord_t *)r_array_slot(dbex->records, i);
1059                 if (prec->ruleuid == RPA_PRODUCTION_RULENAME)
1060                         continue;
1061                 if (prec->ruleuid == RPA_PRODUCTION_AREF || prec->ruleuid == RPA_PRODUCTION_CREF) {
1062                         rpa_ruleinfo_t *info;
1063                         if ((inderction > 0 || i != parent) && i == loopto) {
1064                                 /*
1065                                  * We found what we are looking for
1066                                  */
1067                                 ret = 1;
1068                                 break;
1069                         }
1070                         if (rpa_dbex_rulename(dbex, i, &name, &namesiz) < 0)
1071                                 R_ASSERT(0);
1072                         info = (rpa_ruleinfo_t *) r_harray_get(dbex->rules, rpa_dbex_lookup(dbex, name, namesiz));
1073                         if (!info)
1074                                 continue;
1075                         if ((ret = rpa_parseinfo_loopdetect_do(dbex, info->startrec, loopto, inderction + 1)) > 0)
1076                                 break;
1077                 } else {
1078                         if ((ret = rpa_parseinfo_loopdetect_do(dbex, i, loopto, inderction + 1)) > 0)
1079                                 break;
1080                 }
1081
1082                 if ((prec->usertype & RPA_MATCH_OPTIONAL) == 0 && (prec->ruleuid == RPA_PRODUCTION_CREF || prec->ruleuid == RPA_PRODUCTION_AREF ||
1083                                 prec->ruleuid == RPA_PRODUCTION_CHAR || prec->ruleuid == RPA_PRODUCTION_CLS || prec->ruleuid == RPA_PRODUCTION_SPECIALCHAR))
1084                         break;
1085
1086         }
1087
1088         r_array_removelast(dbex->recstack);
1089         return ret;
1090 }
1091
1092
1093 static rinteger rpa_parseinfo_loopdetect(rpadbex_t *dbex, rlong parent, rlong loopto)
1094 {
1095         if (parent != loopto) {
1096                 /*
1097                  * Make sure we are dealing with a loop first
1098                  */
1099                 if (!rpa_parseinfo_loopdetect_do(dbex, loopto, parent, 0))
1100                         return 0;
1101         }
1102
1103         return (rpa_parseinfo_loopdetect_do(dbex, parent, loopto, 0)) ? 1 : 0;
1104 }
1105
1106
1107 static void rpa_parseinfo_marklooppath(rpadbex_t *dbex, rlong parent)
1108 {
1109         rlong i;
1110
1111         if (rpa_parseinfo_loopdetect(dbex, parent, parent) > 0) {
1112                 rpa_record_setusertype(dbex->records, parent, RPA_LOOP_PATH, RVALSET_OR);
1113                 for (i = rpa_recordtree_firstchild(dbex->records, parent, RPA_RECORD_START); i >= 0; i = rpa_recordtree_next(dbex->records, i, RPA_RECORD_START)) {
1114                         rpa_parseinfo_marklooppath(dbex, i);
1115                 }
1116         }
1117 }
1118
1119
1120 static rinteger rpa_parseinfo_rule_checkforloop(rpadbex_t *dbex, const char *name, rsize_t namesize, rlong loopto)
1121 {
1122         rpa_ruleinfo_t *info = info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, rpa_dbex_lookup(dbex, name, namesize));
1123
1124         if (!info)
1125                 return 0;
1126         return rpa_parseinfo_loopdetect(dbex, info->startrec, loopto);
1127 }
1128
1129
1130 static void rpa_dbex_buildloopinfo(rpadbex_t *dbex)
1131 {
1132         ruinteger i, p;
1133         rharray_t *rules = dbex->rules;
1134         rpa_ruleinfo_t *info;
1135
1136         for (i = 0; i < r_array_length(rules->members); i++) {
1137                 if ((info = (rpa_ruleinfo_t *)r_harray_get(rules, i)) != NULL)
1138                         rpa_parseinfo_marklooppath(dbex, info->startrec);
1139         }
1140
1141         /*
1142          * Mark the non-loop branches.
1143          */
1144         for (i = 0; i < r_array_length(dbex->records); i++) {
1145                 rparecord_t *prec = (rparecord_t *)r_array_slot(dbex->records, i);
1146                 if (prec->type == RPA_RECORD_START &&
1147                         (prec->ruleuid == RPA_PRODUCTION_ALTBRANCH) &&
1148                         (prec->usertype & RPA_LOOP_PATH) == 0) {
1149                         p = rpa_recordtree_parent(dbex->records, i, RPA_RECORD_START);
1150                         if (p >= 0) {
1151                                 prec = (rparecord_t *)r_array_slot(dbex->records, p);
1152                                 if (prec && (prec->usertype & RPA_LOOP_PATH))
1153                                         rpa_record_setusertype(dbex->records, i, RPA_NONLOOP_PATH, RVALSET_OR);
1154                         }
1155                 }
1156         }
1157 }
1158
1159
1160 static void rpa_dbex_buildruleinfo(rpadbex_t *dbex)
1161 {
1162         rparecord_t *rec;
1163         rpa_ruleinfo_t info;
1164         ruinteger nrecords;
1165         rlong i;
1166         const rchar *name = NULL;
1167         rsize_t namesize = 0;
1168
1169         if (dbex->rules) {
1170                 r_object_destroy((robject_t *)dbex->rules);
1171                 dbex->rules = NULL;
1172         }
1173         dbex->rules = r_harray_create(sizeof(rpa_ruleinfo_t));
1174
1175         for (i = 0, nrecords = r_array_length(dbex->records); i < nrecords; i++) {
1176                 if (!(rec = rpa_dbex_record(dbex, i)))
1177                         continue;
1178                 if ((rec->ruleuid == RPA_PRODUCTION_NAMEDRULE) && (rec->type & RPA_RECORD_START)) {
1179                         r_memset(&info, 0, sizeof(info));
1180                         info.type = RPA_RULEINFO_NAMEDRULE;
1181                         info.startrec = i;
1182                         info.sizerecs = rpa_recordtree_size(dbex->records, i);
1183                         if (info.sizerecs < 0)
1184                                 continue;
1185                         if (rpa_dbex_rulename(dbex, i, &name, &namesize) < 0) {
1186                                 continue;
1187                         }
1188                         r_harray_add(dbex->rules, name, namesize, &info);
1189                         i += info.sizerecs - 1;
1190                 } else if ((rec->ruleuid == RPA_PRODUCTION_ANONYMOUSRULE) && (rec->type & RPA_RECORD_START)) {
1191                         r_memset(&info, 0, sizeof(info));
1192                         info.type = RPA_RULEINFO_ANONYMOUSRULE;
1193                         info.startrec = i;
1194                         info.sizerecs = rpa_recordtree_size(dbex->records, i);
1195                         if (info.sizerecs < 0)
1196                                 continue;
1197                         if ((rec = rpa_dbex_record(dbex, rpa_recordtree_get(dbex->records, i, RPA_RECORD_END))))
1198                                 r_harray_add(dbex->rules, rec->input, rec->inputsiz, &info);
1199                         i += info.sizerecs - 1;
1200                 } else if ((rec->type & RPA_RECORD_START) && (rec->ruleuid >= RPA_PRODUCTION_DIRECTIVEEMIT) && (rec->ruleuid <= RPA_PRODUCTION_DIRECTIVEEMITID)) {
1201                         r_memset(&info, 0, sizeof(info));
1202                         info.type = RPA_RULEINFO_DIRECTIVE;
1203                         info.startrec = i;
1204                         info.sizerecs = rpa_recordtree_size(dbex->records, i);
1205                         if (info.sizerecs < 0)
1206                                 continue;
1207                         if ((rec = rpa_dbex_record(dbex, rpa_recordtree_get(dbex->records, i, RPA_RECORD_END))))
1208                                 r_harray_add(dbex->rules, rec->input, rec->inputsiz, &info);
1209                         i += info.sizerecs - 1;
1210                 }
1211
1212         }
1213 }
1214
1215
1216 static rlong rpa_dbex_copy_handler(rarray_t *records, rlong rec, rpointer userdata)
1217 {
1218         rpadbex_t *dbex = (rpadbex_t *)userdata;
1219         rlong index;
1220
1221         rparecord_t *prec = (rparecord_t *)r_array_slot(records, rec);
1222         if (prec->ruleuid == RPA_PRODUCTION_OCCURENCE && (prec->type & RPA_RECORD_START)) {
1223                 /*
1224                  * Ignore it
1225                  */
1226         } else if (prec->ruleuid == RPA_PRODUCTION_OCCURENCE && (prec->type & (RPA_RECORD_END))) {
1227                 ruint32 usertype = RPA_MATCH_NONE;
1228                 rlong lastrec = 0;
1229                 /*
1230                  * Don't copy it but set the usertype of the previous record accordingly.
1231                  */
1232                 switch (*prec->input) {
1233                 case '?':
1234                         usertype = RPA_MATCH_OPTIONAL;
1235                         break;
1236                 case '+':
1237                         usertype = RPA_MATCH_MULTIPLE;
1238                         break;
1239                 case '*':
1240                         usertype = RPA_MATCH_MULTIOPT;
1241                         break;
1242                 default:
1243                         usertype = RPA_MATCH_NONE;
1244                 };
1245                 lastrec = r_array_length(dbex->records) - 1;
1246                 if (lastrec >= 0)
1247                         rpa_record_setusertype(dbex->records, lastrec, usertype, RVALSET_OR);
1248         } else if (prec->ruleuid) {
1249                 index = r_array_add(dbex->records, prec);
1250                 /*
1251                  * Optimizations. Lets apply the optimizations while we copy the records.
1252                  * This is probably not the most clean way to apply optimizations. In the future
1253                  * we should probably think of optimization pass right before compiling.
1254                  */
1255                 if (dbex->optimizations) {
1256                         if (prec->ruleuid == RPA_PRODUCTION_OROP && (prec->type & RPA_RECORD_END)) {
1257                                 rpa_optimiztion_orop(dbex->records, rpa_recordtree_get(dbex->records, index, RPA_RECORD_START));
1258                         }
1259                 }
1260         }
1261
1262         return 0;
1263 }
1264
1265
1266 static void rpa_dbex_copyrecords(rpadbex_t *dbex)
1267 {
1268         rinteger i;
1269         rarray_t *records = dbex->temprecords;
1270
1271         for (i = rpa_recordtree_get(records, 0, RPA_RECORD_START); i >= 0; i = rpa_recordtree_next(records, i, RPA_RECORD_START))
1272                 rpa_recordtree_walk(records, i, 0, rpa_dbex_copy_handler, dbex);
1273 }
1274
1275
1276 static rparecord_t *rpa_dbex_record(rpadbex_t *dbex, rlong rec)
1277 {
1278         rparecord_t *prec;
1279
1280         if (!dbex || !dbex->rules)
1281                 return NULL;
1282         if (rec < 0 || rec >= r_array_length(dbex->records))
1283                 return NULL;
1284         prec = (rparecord_t *)r_array_slot(dbex->records, rec);
1285         return prec;
1286
1287 }
1288
1289
1290 static rparecord_t *rpa_dbex_rulerecord(rpadbex_t *dbex, rparule_t rid)
1291 {
1292         rparecord_t *prec;
1293         rpa_ruleinfo_t *info;
1294         rlong rec;
1295
1296         if (!dbex || !dbex->rules)
1297                 return NULL;
1298         info = r_harray_get(dbex->rules, rid);
1299         if (!info)
1300                 return NULL;
1301         rec = info->startrec + info->sizerecs - 1;
1302         if (rec < 0 || rec >= r_array_length(dbex->records))
1303                 return NULL;
1304         prec = (rparecord_t *)r_array_slot(dbex->records, rec);
1305         return prec;
1306 }
1307
1308
1309 static rinteger rpa_dbex_rulename(rpadbex_t *dbex, rlong rec, const rchar **name, rsize_t *namesize)
1310 {
1311         rparecord_t *pnamerec = rpa_dbex_record(dbex, rpa_recordtree_firstchild(dbex->records, rpa_recordtree_get(dbex->records, rec, RPA_RECORD_START), RPA_RECORD_END));
1312         if (!pnamerec || !(pnamerec->ruleuid & RPA_PRODUCTION_RULENAME))
1313                 return -1;
1314         *name = pnamerec->input;
1315         *namesize = pnamerec->inputsiz;
1316         return 0;
1317 }
1318
1319
1320 rinteger rpa_dbex_open(rpadbex_t *dbex)
1321 {
1322         if (!dbex)
1323                 return -1;
1324         if (dbex->rules) {
1325                 r_object_destroy((robject_t *)dbex->rules);
1326                 dbex->rules = NULL;
1327         }
1328         dbex->compiled = 0;
1329         return 0;
1330 }
1331
1332
1333 void rpa_dbex_close(rpadbex_t *dbex)
1334 {
1335         if (!dbex)
1336                 return;
1337         rpa_dbex_buildruleinfo(dbex);
1338         rpa_dbex_buildloopinfo(dbex);
1339 }
1340
1341
1342 rlong rpa_dbex_load(rpadbex_t *dbex, const rchar *rules, rsize_t size)
1343 {
1344         rlong ret;
1345         rchar *text;
1346
1347         if (!dbex)
1348                 return -1;
1349         if (dbex->rules) {
1350                 /*
1351                  * Dbex is not open
1352                  */
1353                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTOPEN);
1354                 return -1;
1355         }
1356
1357         text = r_strndup(rules, size);
1358         R_ASSERT(text);
1359         r_array_add(dbex->text, &text);
1360         r_array_setlength(dbex->temprecords, 0);
1361         if ((ret = rpa_parser_load(dbex->pa, text, size, dbex->temprecords)) < 0) {
1362
1363                 return -1;
1364         }
1365         if (ret != size) {
1366                 rlong line = 1;
1367                 rchar *ptext = text;
1368                 ptext += ret;
1369                 for (line = 1; ptext >= text; --ptext) {
1370                         if (*ptext == '\n')
1371                                 line += 1;
1372                 }
1373                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_SYNTAXERROR);
1374                 RPA_DBEX_SETERRINFO_OFFSET(dbex, ret);
1375                 RPA_DBEX_SETERRINFO_LINE(dbex, line);
1376                 return -1;
1377         }
1378         rpa_dbex_copyrecords(dbex);
1379         return ret;
1380 }
1381
1382
1383 rlong rpa_dbex_load_s(rpadbex_t *dbex, const rchar *rules)
1384 {
1385         return rpa_dbex_load(dbex, rules, r_strlen(rules));
1386 }
1387
1388
1389 void rpa_dbex_dumpindented(rpadbex_t *dbex, rlong rec, rinteger level, const rchar *rulelabel)
1390 {
1391         rchar buffer[1024];
1392         rinteger i, size;
1393         rparecord_t *prec = rpa_dbex_record(dbex, rpa_recordtree_get(dbex->records, rec, RPA_RECORD_END));
1394
1395         if (!prec)
1396                 return;
1397         r_memset(buffer, 0, sizeof(buffer));
1398         for (i = 0; i < level + 1; i++)
1399                 r_printf("   ");
1400         r_printf("(");
1401         r_printf("%s, %c, %c", rulelabel, rpa_record_optchar(prec, 'x'), rpa_record_loopchar(prec, 'x'));
1402         r_printf(")");
1403         size = R_MIN(prec->inputsiz, sizeof(buffer) - 1);
1404         r_strncpy(buffer, prec->input, size);
1405
1406         if (size == (sizeof(buffer) - 1))
1407                 r_printf(" %s ...\n", buffer);
1408         else
1409                 r_printf(" %s\n", buffer);
1410         return;
1411 }
1412
1413
1414 static rlong rpa_dbex_firstinlined(rpadbex_t *dbex)
1415 {
1416         rlong ret = r_array_empty(dbex->inlinestack) ? -1 : r_array_index(dbex->inlinestack, 0, rlong);
1417         return ret;
1418 }
1419
1420
1421 static rinteger rpa_dbex_findinlined(rpadbex_t *dbex, rlong startrec)
1422 {
1423         rlong i;
1424         for (i = 0; i < r_array_length(dbex->inlinestack); i++) {
1425                 if (r_array_index(dbex->inlinestack, i, rlong) == startrec)
1426                         return 1;
1427         }
1428         return 0;
1429 }
1430
1431
1432 static void rpa_dbex_dumptree_do(rpadbex_t *dbex, rlong rec, rinteger level)
1433 {
1434         rparecord_t *prec = rpa_dbex_record(dbex, rec);
1435         if (prec && prec->ruleuid == RPA_PRODUCTION_RULENAME)
1436                 return;
1437         if (prec && (prec->ruleuid == RPA_PRODUCTION_AREF || prec->ruleuid == RPA_PRODUCTION_CREF)) {
1438                 const rchar *name = NULL;
1439                 rsize_t namesize = 0;
1440                 rinteger loop = 0;
1441                 rpa_ruleinfo_t *info;
1442
1443                 if (rpa_dbex_rulename(dbex, rec, &name, &namesize) >= 0) {
1444                         loop = rpa_parseinfo_rule_checkforloop(dbex, name, namesize, rpa_dbex_firstinlined(dbex));
1445                         info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, rpa_dbex_lookup(dbex, name, namesize));
1446                         if (loop && info){
1447                                 if (!rpa_dbex_findinlined(dbex, info->startrec)) {
1448                                         /*
1449                                          * Temporary set the quantitative flags for the inlined rule to the parent
1450                                          * reference, so they are printed correctly. After the printing is done
1451                                          * restore the original flags.
1452                                          */
1453                                         rparecord_t *prulestart = rpa_dbex_record(dbex, rpa_recordtree_get(dbex->records, info->startrec, RPA_RECORD_START));
1454                                         rparecord_t *pruleend = rpa_dbex_record(dbex, rpa_recordtree_get(dbex->records, info->startrec, RPA_RECORD_END));
1455                                         rulong optional = (prulestart->usertype & RPA_MATCH_OPTIONAL);
1456                                         prulestart->usertype |= (prec->usertype & RPA_MATCH_OPTIONAL);
1457                                         pruleend->usertype |= (prec->usertype & RPA_MATCH_OPTIONAL);
1458                                         r_array_add(dbex->inlinestack, &info->startrec);
1459                                         rpa_dbex_dumptree_do(dbex, info->startrec, level);
1460                                         r_array_removelast(dbex->inlinestack);
1461                                         if (!optional) {
1462                                                 prulestart->usertype &= ~RPA_MATCH_OPTIONAL;
1463                                                 pruleend->usertype &= ~RPA_MATCH_OPTIONAL;
1464                                         }
1465                                 } else {
1466                                         rpa_dbex_dumpindented(dbex, rpa_recordtree_get(dbex->records, rec, RPA_RECORD_END), level, "loopref");
1467                                 }
1468                                 return;
1469                         }
1470                 }
1471         }
1472         rpa_dbex_dumpindented(dbex, rpa_recordtree_get(dbex->records, rec, RPA_RECORD_END), level, prec->rule);
1473         for (rec = rpa_recordtree_firstchild(dbex->records, rec, RPA_RECORD_START); rec >= 0; rec = rpa_recordtree_next(dbex->records, rec, RPA_RECORD_START)) {
1474                 rpa_dbex_dumptree_do(dbex, rec, level + 1);
1475         }
1476 }
1477
1478
1479 rinteger rpa_dbex_dumptree(rpadbex_t *dbex, rparule_t rid)
1480 {
1481         rpa_ruleinfo_t *info;
1482
1483         if (!dbex)
1484                 return -1;
1485         if (rid < 0) {
1486                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_PARAM);
1487                 return -1;
1488         }
1489         if (!dbex->rules) {
1490                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1491                 return -1;
1492         }
1493         if (!(info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, rid))) {
1494                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTFOUND);
1495                 return -1;
1496         }
1497         r_array_add(dbex->inlinestack, &info->startrec);
1498         rpa_dbex_dumptree_do(dbex, info->startrec, 0);
1499         r_array_removelast(dbex->inlinestack);
1500         return 0;
1501 }
1502
1503
1504 rinteger rpa_dbex_dumpproductions(rpadbex_t *dbex)
1505 {
1506         rinteger ret = 0;
1507         rparule_t rid;
1508         rchar buffer[512];
1509
1510         if (!dbex)
1511                 return -1;
1512         if (!dbex->rules) {
1513                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1514                 return -1;
1515         }
1516         for (rid = rpa_dbex_first(dbex); rid >= 0; rid = rpa_dbex_next(dbex, rid)) {
1517                 ret = rpa_dbex_strncpy(dbex, buffer, rid, sizeof(buffer));
1518                 if ( ret >= 0) {
1519                         if (ret == sizeof(buffer))
1520                                 r_printf("   %s ...\n", buffer);
1521                         else
1522                                 r_printf("   %s\n", buffer);
1523                 }
1524
1525         }
1526         return ret;
1527 }
1528
1529
1530 rinteger rpa_dbex_dumprecords(rpadbex_t *dbex)
1531 {
1532         rlong i;
1533
1534         if (!dbex)
1535                 return -1;
1536         if (!dbex->rules) {
1537                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1538                 return -1;
1539         }
1540         for (i = 0; i < r_array_length(dbex->records); i++) {
1541                 rpa_record_dump(dbex->records, i);
1542         }
1543         return 0;
1544 }
1545
1546
1547 rinteger rpa_dbex_dumpinfo(rpadbex_t *dbex)
1548 {
1549         rlong i;
1550         rpa_ruleinfo_t *info;
1551
1552         if (!dbex)
1553                 return -1;
1554         if (!dbex->rules) {
1555                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1556                 return -1;
1557         }
1558         for (i = 0; i < r_array_length(dbex->rules->names); i++) {
1559                 rstr_t *name = r_array_index(dbex->rules->names, i, rstr_t*);
1560                 info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, i);
1561                 switch (info->type) {
1562                 case RPA_RULEINFO_NAMEDRULE:
1563                         r_printf("N ");
1564                         break;
1565                 case RPA_RULEINFO_ANONYMOUSRULE:
1566                         r_printf("A ");
1567                         break;
1568                 case RPA_RULEINFO_DIRECTIVE:
1569                         r_printf("D ");
1570                         break;
1571                 default:
1572                         r_printf("  ");
1573                         break;
1574                 };
1575                 r_printf("(%7d, %4d, code: %7ld, %5ld) : %s\n", info->startrec, info->sizerecs, info->codeoff, info->codesiz, name->str);
1576         }
1577         return 0;
1578 }
1579
1580
1581 rinteger rpa_dbex_dumpuids(rpadbex_t *dbex)
1582 {
1583         rlong i;
1584         rlong rec;
1585         rpa_ruleinfo_t *info;
1586         rchar *buffer = r_zmalloc(32 * sizeof(rchar));
1587
1588         if (!dbex)
1589                 return -1;
1590         if (!dbex->rules) {
1591                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1592                 return -1;
1593         }
1594         for (i = 0; i < r_array_length(dbex->rules->names); i++) {
1595                 info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, i);
1596                 if (info->type == RPA_RULEINFO_DIRECTIVE) {
1597                         rparecord_t *prec = rpa_dbex_record(dbex, rpa_recordtree_get(dbex->records, info->startrec, RPA_RECORD_END));
1598                         if (prec->ruleuid == RPA_PRODUCTION_DIRECTIVEEMITID && prec->inputsiz) {
1599                                 rec = rpa_recordtree_firstchild(dbex->records, info->startrec, RPA_RECORD_START);
1600                                 while (rec >= 0) {
1601                                         prec = rpa_dbex_record(dbex, rpa_recordtree_get(dbex->records, rec, RPA_RECORD_END));
1602                                         if (prec->ruleuid == RPA_PRODUCTION_ALIASNAME) {
1603                                                 ruint32 dec;
1604                                                 if (rpa_record2long(rpa_dbex_record(dbex, rpa_recordtree_next(dbex->records, rec, RPA_RECORD_END)), &dec) < 0)
1605                                                         break;
1606                                                 buffer = r_realloc(buffer, prec->inputsiz + 1);
1607                                                 r_memset(buffer, 0, prec->inputsiz + 1);
1608                                                 r_memcpy(buffer, prec->input, prec->inputsiz);
1609                                                 r_printf("#define %s %d\n", buffer, dec);
1610                                                 break;
1611                                         }
1612                                         rec = rpa_recordtree_next(dbex->records, rec, RPA_RECORD_START);
1613                                 }
1614                         }
1615                 }
1616         }
1617         r_free(buffer);
1618         return 0;
1619 }
1620
1621
1622 rinteger rpa_dbex_dumpcode(rpadbex_t* dbex, rparule_t rid)
1623 {
1624         rpa_ruleinfo_t *info;
1625         if (!dbex)
1626                 return -1;
1627         if (rid < 0) {
1628                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_PARAM);
1629                 return -1;
1630         }
1631         if (!dbex->rules) {
1632                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1633                 return -1;
1634         }
1635         info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, rid);
1636         if (!info)
1637                 return -1;
1638         rvm_asm_dump(rvm_codegen_getcode(dbex->co->cg, info->codeoff), info->codesiz);
1639         return 0;
1640 }
1641
1642
1643 rsize_t rpa_dbex_strlen(rpadbex_t *dbex, rparule_t rid)
1644 {
1645         rparecord_t *prec;
1646         rsize_t size;
1647
1648         if (!dbex)
1649                 return -1;
1650         if ((prec = rpa_dbex_rulerecord(dbex, rid)) == NULL) {
1651                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTFOUND);
1652                 return -1;
1653         }
1654         size = prec->inputsiz;
1655         return size;
1656 }
1657
1658
1659 rsize_t rpa_dbex_strncpy(rpadbex_t *dbex, rchar *dst, rparule_t rid, rsize_t n)
1660 {
1661         rparecord_t *prec;
1662         rsize_t size;
1663
1664         if (!dbex)
1665                 return -1;
1666         if ((prec = rpa_dbex_rulerecord(dbex, rid)) == NULL) {
1667                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTFOUND);
1668                 return -1;
1669         }
1670         size = prec->inputsiz;
1671         if (n <= size)
1672                 size = n - 1;
1673         r_memset(dst, 0, n);
1674         r_strncpy(dst, prec->input, size);
1675         return size + 1;
1676 }
1677
1678
1679 const rchar *rpa_dbex_name(rpadbex_t *dbex, rparule_t rid)
1680 {
1681         rstr_t *name;
1682
1683         if (!dbex)
1684                 return NULL;
1685         if (!dbex->rules) {
1686                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1687                 return NULL;
1688         }
1689         if (rid >= r_array_length(dbex->rules->members))
1690                 return NULL;
1691         name = r_array_index(dbex->rules->names, rid, rstr_t*);
1692         return name->str;
1693 }
1694
1695
1696 rparule_t rpa_dbex_first(rpadbex_t *dbex)
1697 {
1698         if (!dbex)
1699                 return -1;
1700         if (!dbex->rules) {
1701                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1702                 return -1;
1703         }
1704
1705         if (r_array_length(dbex->rules->members) <= 0) {
1706                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTFOUND);
1707                 return -1;
1708         }
1709         return 0;
1710 }
1711
1712
1713 rparule_t rpa_dbex_last(rpadbex_t *dbex)
1714 {
1715         if (!dbex)
1716                 return -1;
1717         if (!dbex->rules) {
1718                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1719                 return -1;
1720         }
1721
1722         if (r_array_length(dbex->rules->members) <= 0) {
1723                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTFOUND);
1724                 return -1;
1725         }
1726         return r_array_length(dbex->rules->members) - 1;
1727 }
1728
1729
1730 rparule_t rpa_dbex_lookup(rpadbex_t *dbex, const rchar *name, rsize_t namesize)
1731 {
1732         rparule_t ret;
1733
1734         if (!dbex) {
1735                 return -1;
1736         }
1737         if (!dbex->rules) {
1738                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1739                 return -1;
1740         }
1741
1742         ret = (rparule_t) r_harray_taillookup(dbex->rules, name, namesize);
1743         if (ret < 0) {
1744                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTFOUND);
1745         }
1746         return ret;
1747 }
1748
1749
1750 rparule_t rpa_dbex_lookup_s(rpadbex_t *dbex, const rchar *name)
1751 {
1752         return rpa_dbex_lookup(dbex, name, r_strlen(name));
1753 }
1754
1755
1756 rparule_t rpa_dbex_next(rpadbex_t *dbex, rparule_t rid)
1757 {
1758         if (!dbex)
1759                 return -1;
1760         if (!dbex->rules) {
1761                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1762                 return -1;
1763         }
1764
1765         ++rid;
1766         if (rid < r_array_length(dbex->rules->members))
1767                 return rid;
1768         return -1;
1769 }
1770
1771
1772 rparule_t rpa_dbex_prev(rpadbex_t *dbex, rparule_t rid)
1773 {
1774         if (!dbex)
1775                 return -1;
1776         if (!dbex->rules) {
1777                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1778                 return -1;
1779         }
1780         --rid;
1781         if (rid >= 0)
1782                 return rid;
1783         return -1;
1784 }
1785
1786
1787 rlong rpa_dbex_lasterror(rpadbex_t *dbex)
1788 {
1789         if (!dbex)
1790                 return -1;
1791         return dbex->err.code;
1792 }
1793
1794
1795 rlong rpa_dbex_lasterrorinfo(rpadbex_t *dbex, rpa_errinfo_t *errinfo)
1796 {
1797         if (!dbex || !errinfo)
1798                 return -1;
1799         r_memcpy(errinfo, &dbex->err, sizeof(rpa_errinfo_t));
1800         return 0;
1801 }
1802
1803
1804 const rchar *rpa_dbex_version()
1805 {
1806         return "2.0";
1807 }
1808
1809
1810 static rinteger rpa_dbex_compile_rule(rpadbex_t *dbex, rparule_t rid)
1811 {
1812         rlong codeoff;
1813         rpa_ruleinfo_t *info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, rid);
1814
1815         if (!info)
1816                 return -1;
1817         codeoff = rvm_codegen_getcodesize(dbex->co->cg);
1818         /*
1819          * Set the rid in the rulepref, so the compiler associates this rule
1820          * with the correct rid.
1821          */
1822         rpa_compiler_rulepref_set_ruleid_s(dbex->co, rpa_dbex_name(dbex, rid), rid);
1823         if (rpa_dbex_playrecord(dbex, info->startrec) < 0)
1824                 return -1;
1825         info->codeoff = codeoff;
1826         info->codesiz = rvm_codegen_getcodesize(dbex->co->cg) - codeoff;
1827         return 0;
1828 }
1829
1830
1831 rinteger rpa_dbex_compile(rpadbex_t *dbex)
1832 {
1833         rparule_t rid;
1834         rvm_codelabel_t *labelerr;
1835
1836         if (!dbex)
1837                 return -1;
1838         if (!dbex->rules) {
1839                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1840                 return -1;
1841         }
1842         /*
1843          * By default all production rules emit
1844          */
1845         if (dbex->co)
1846                 rpa_compiler_destroy(dbex->co);
1847         dbex->co = rpa_compiler_create();
1848         rpa_dbex_setemit(dbex, TRUE);
1849
1850         for (rid = rpa_dbex_first(dbex); rid >= 0; rid = rpa_dbex_next(dbex, rid)) {
1851                 if (rpa_dbex_compile_rule(dbex, rid) < 0) {
1852                         RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_COMPILE);
1853                         return -1;
1854                 }
1855         }
1856
1857         if (rvm_codegen_relocate(dbex->co->cg, &labelerr) < 0) {
1858                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_UNRESOLVEDSYMBOL);
1859                 RPA_DBEX_SETERRINFO_NAME(dbex, labelerr->name->str, labelerr->name->size);
1860                 return -1;
1861         }
1862         dbex->compiled = 1;
1863         return 0;
1864 }
1865
1866
1867 rvm_asmins_t *rpa_dbex_executable(rpadbex_t *dbex)
1868 {
1869         if (!dbex)
1870                 return NULL;
1871         if (!dbex->rules) {
1872                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1873                 return NULL;
1874         }
1875         if (!dbex->compiled || rvm_codegen_getcodesize(dbex->co->cg) == 0) {
1876                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCOMPILED);
1877                 return NULL;
1878         }
1879         return rvm_codegen_getcode(dbex->co->cg, 0);
1880 }
1881
1882
1883 rlong rpa_dbex_executableoffset(rpadbex_t *dbex, rparule_t rid)
1884 {
1885         rpa_ruleinfo_t *info;
1886
1887         if (!dbex)
1888                 return -1;
1889         if (!dbex->rules) {
1890                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCLOSED);
1891                 return -1;
1892         }
1893         if (!dbex->compiled) {
1894                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTCOMPILED);
1895                 return -1;
1896         }
1897         info = (rpa_ruleinfo_t *)r_harray_get(dbex->rules, rid);
1898         if (!info) {
1899                 RPA_DBEX_SETERRINFO_CODE(dbex, RPA_E_NOTFOUND);
1900                 return -1;
1901         }
1902         return info->codeoff;
1903 }
1904
1905
1906 rlong rpa_dbex_cfgset(rpadbex_t *dbex, rulong cfg, rulong val)
1907 {
1908         if (!dbex)
1909                 return -1;
1910         if (cfg == RPA_DBEXCFG_OPTIMIZATIONS) {
1911                 dbex->optimizations = val;
1912                 return 0;
1913         } else if(cfg == RPA_DBEXCFG_DEBUG) {
1914                 dbex->debug = val;
1915                 return 0;
1916         }
1917         return -1;
1918 }
1919
1920
1921 rlong rpa_dbex_cfgget(rpadbex_t *dbex, rulong cfg)
1922 {
1923         if (!dbex)
1924                 return -1;
1925         if (cfg == RPA_DBEXCFG_OPTIMIZATIONS) {
1926                 return dbex->optimizations;
1927         } else if(cfg == RPA_DBEXCFG_DEBUG) {
1928                 return dbex->debug;
1929         }
1930         return -1;
1931 }
1932