Implemented an export feature, for exporting single-character machine
[external/ragel.git] / redfsm / xmlparse.kl
1 /*
2  *  Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
3  */
4
5 /*  This file is part of Ragel.
6  *
7  *  Ragel is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  * 
12  *  Ragel is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  * 
17  *  You should have received a copy of the GNU General Public License
18  *  along with Ragel; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
20  */
21
22 #include "xmlparse.h"
23 #include "common.h"
24 #include "gendata.h"
25 #include <iostream>
26 #include <malloc.h>
27
28 using std::cout;
29 using std::ostream;
30 using std::istream;
31 using std::cerr;
32 using std::endl;
33
34 Key readKey( char *td, char **end );
35 long readOffsetPtr( char *td, char **end );
36 unsigned long readLength( char *td );
37
38 %%{
39
40 parser Parser;
41
42 include "xmlparse.kh";
43
44 start: tag_ragel;
45 start:
46         final {
47                 /* If we get no input the assumption is that the frontend died and
48                  * emitted an error. */ 
49                 errCount += 1;
50         };
51
52 tag_ragel: tag_ragel_head ragel_def_list host_or_write_list '/' TAG_ragel;
53
54 tag_ragel_head: TAG_ragel
55         final {
56                 Attribute *fileNameAttr = $1->tag->findAttr( "filename" );
57                 if ( fileNameAttr == 0 ) {
58                         error($1->loc) << "tag <ragel> requires a filename attribute" << endl;
59                         exit(1);
60                 }
61                 else {
62                         sourceFileName = fileNameAttr->value;
63
64                         Attribute *langAttr = $1->tag->findAttr( "lang" );
65                         if ( langAttr == 0 )
66                                 error($1->loc) << "tag <ragel> requires a lang attribute" << endl;
67                         else {
68                                 if ( strcmp( langAttr->value, "C" ) == 0 ) {
69                                         hostLangType = CCode;
70                                         hostLang = &hostLangC;
71                                 }
72                                 else if ( strcmp( langAttr->value, "D" ) == 0 ) {
73                                         hostLangType = DCode;
74                                         hostLang = &hostLangD;
75                                 }
76                                 else if ( strcmp( langAttr->value, "Java" ) == 0 ) {
77                                         hostLangType = JavaCode;
78                                         hostLang = &hostLangJava;
79                                 }
80                                 else if ( strcmp( langAttr->value, "Ruby" ) == 0 ) {
81                                         hostLangType = RubyCode;
82                                         hostLang = &hostLangRuby;
83                                 }
84                                 else {
85                                         error($1->loc) << "expecting lang attribute to be "
86                                                         "one of C, D, Java or Ruby" << endl;
87                                 }
88
89                                 outStream = openOutput( sourceFileName );
90                         }
91                 }
92         };
93
94 ragel_def_list: ragel_def_list ragel_def;
95 ragel_def_list: ;
96
97 host_or_write_list: host_or_write_list host_or_write;
98 host_or_write_list: ;
99
100 host_or_write: tag_host;
101 host_or_write: tag_write;
102
103 tag_host: 
104         TAG_host '/' TAG_host
105         final {
106                 Attribute *lineAttr = $1->tag->findAttr( "line" );
107                 if ( lineAttr == 0 )
108                         error($1->loc) << "tag <host> requires a line attribute" << endl;
109                 else {
110                         int line = atoi( lineAttr->value );
111                         if ( outputActive )
112                                 lineDirective( *outStream, sourceFileName, line );
113                 }
114
115                 if ( outputActive )
116                         *outStream << $3->tag->content;
117         };
118
119 ragel_def: 
120         tag_ragel_def_head ragel_def_item_list '/' TAG_ragel_def
121         final {
122                 /* Do this before distributing transitions out to singles and defaults
123                  * makes life easier. */
124                 cgd->redFsm->maxKey = cgd->findMaxKey();
125
126                 cgd->redFsm->assignActionLocs();
127
128                 /* Find the first final state (The final state with the lowest id). */
129                 cgd->redFsm->findFirstFinState();
130
131                 /* Call the user's callback. */
132                 cgd->finishRagelDef();
133         };
134
135 tag_ragel_def_head: TAG_ragel_def 
136         final {
137                 char *fsmName = 0;
138                 Attribute *nameAttr = $1->tag->findAttr( "name" );
139                 if ( nameAttr != 0 ) {
140                         fsmName = nameAttr->value;
141
142                         CodeGenMapEl *mapEl = codeGenMap.find( fsmName );
143                         if ( mapEl != 0 )
144                                 cgd = mapEl->value;
145                         else {
146                                 cgd = makeCodeGen( sourceFileName, fsmName, *outStream, wantComplete );
147                                 codeGenMap.insert( fsmName, cgd );
148                         }
149                 }
150                 else {
151                         cgd = makeCodeGen( sourceFileName, fsmName, 
152                                         *outStream, wantComplete );
153                 }
154
155                 ::keyOps = &cgd->thisKeyOps;
156         };
157
158 ragel_def_item_list: ragel_def_item_list ragel_def_item;
159 ragel_def_item_list: ;
160
161 ragel_def_item: tag_alph_type;
162 ragel_def_item: tag_getkey_expr;
163 ragel_def_item: tag_access_expr;
164 ragel_def_item: tag_curstate_expr;
165 ragel_def_item: tag_export_list;
166 ragel_def_item: tag_machine;
167
168 tag_export_list: TAG_exports export_list '/' TAG_exports;
169
170 export_list: export_list tag_export;
171 export_list: ;
172
173 tag_export: TAG_ex '/' TAG_ex
174         final {
175                 Attribute *nameAttr = $1->tag->findAttr( "name" );
176                 if ( nameAttr == 0 )
177                         error($1->loc) << "tag <ex> requires a name attribute" << endl;
178                 else {
179                         char *td = $3->tag->content;
180                         Key exportKey = readKey( td, &td );
181                         cgd->exportList.append( new Export( nameAttr->value, exportKey ) );
182                 }
183         };
184
185 tag_alph_type: TAG_alphtype '/' TAG_alphtype
186         final {
187                 if ( ! cgd->setAlphType( $3->tag->content ) )
188                         error($1->loc) << "tag <alphtype> specifies unknown alphabet type" << endl;
189         };
190
191 tag_getkey_expr: TAG_getkey inline_list '/' TAG_getkey
192         final {
193                 cgd->getKeyExpr = $2->inlineList;
194         };
195
196 tag_access_expr: TAG_access inline_list '/' TAG_access
197         final {
198                 cgd->accessExpr = $2->inlineList;
199         };
200
201 tag_curstate_expr: TAG_curstate inline_list '/' TAG_curstate
202         final {
203                 cgd->curStateExpr = $2->inlineList;
204         };
205
206 tag_write: tag_write_head write_option_list '/' TAG_write
207         final {
208                 /* Terminate the options list and call the write statement handler. */
209                 writeOptions.append(0);
210                 cgd->writeStatement( $1->loc, writeOptions.length()-1, writeOptions.data );
211
212                 /* CodeGenData may have issued an error. */
213                 errCount += cgd->codeGenErrCount;
214
215                 /* Clear the options in prep for the next write statement. */
216                 writeOptions.empty();
217         };
218
219 nonterm tag_write_head
220 {
221         InputLoc loc;
222 };
223
224 tag_write_head: TAG_write
225         final {
226                 Attribute *nameAttr = $1->tag->findAttr( "def_name" );
227                 Attribute *lineAttr = $1->tag->findAttr( "line" );
228                 Attribute *colAttr = $1->tag->findAttr( "col" );
229
230                 if ( nameAttr == 0 )
231                         error($1->loc) << "tag <write> requires a def_name attribute" << endl;
232                 if ( lineAttr == 0 )
233                         error($1->loc) << "tag <write> requires a line attribute" << endl;
234                 if ( colAttr == 0 )
235                         error($1->loc) << "tag <write> requires a col attribute" << endl;
236
237                 if ( nameAttr != 0 && lineAttr != 0 && colAttr != 0 ) {
238                         CodeGenMapEl *mapEl = codeGenMap.find( nameAttr->value );
239                         if ( mapEl == 0 )
240                                 error($1->loc) << "internal error: cannot find codeGen" << endl;
241                         else {
242                                 cgd = mapEl->value;
243                                 ::keyOps = &cgd->thisKeyOps;
244                         }
245
246                         $$->loc.line = atoi(lineAttr->value);
247                         $$->loc.col = atoi(colAttr->value);
248                 }
249         };
250
251
252 write_option_list: write_option_list tag_arg;
253 write_option_list: ;
254
255 nonterm tag_arg
256 {
257         char *option;
258 };
259
260 tag_arg: TAG_arg '/' TAG_arg
261         final {
262                 writeOptions.append( $3->tag->content );
263         };
264
265 tag_machine: tag_machine_head machine_item_list '/' TAG_machine
266         final {
267                 cgd->closeMachine();
268         };
269
270 tag_machine_head: TAG_machine
271         final {
272                 cgd->createMachine();
273         };
274
275 machine_item_list: machine_item_list machine_item;
276 machine_item_list: ;
277
278 machine_item: tag_start_state;
279 machine_item: tag_error_state;
280 machine_item: tag_entry_points;
281 machine_item: tag_state_list;
282 machine_item: tag_action_list;
283 machine_item: tag_action_table_list;
284 machine_item: tag_cond_space_list;
285
286 #
287 # States.
288 #
289
290 tag_start_state: TAG_start_state '/' TAG_start_state
291         final {
292                 unsigned long startState = strtoul( $3->tag->content, 0, 10 );
293                 cgd->setStartState( startState );
294         };
295
296 tag_error_state: TAG_error_state '/' TAG_error_state
297         final {
298                 unsigned long errorState = strtoul( $3->tag->content, 0, 10 );
299                 cgd->setErrorState( errorState );
300         };
301
302 tag_entry_points: TAG_entry_points entry_point_list '/' TAG_entry_points
303         final {
304                 Attribute *errorAttr = $1->tag->findAttr( "error" );
305                 if ( errorAttr != 0 )
306                         cgd->setForcedErrorState();
307         };
308
309 entry_point_list: entry_point_list tag_entry;
310 entry_point_list: ;
311
312 tag_entry: TAG_entry '/' TAG_entry
313         final {
314                 Attribute *nameAttr = $1->tag->findAttr( "name" );
315                 if ( nameAttr == 0 ) {
316                         error($1->loc) << "tag <entry_points>::<entry> "
317                                         "requires a name attribute" << endl;
318                 }
319                 else {
320                         char *data = $3->tag->content;
321                         unsigned long entry = strtoul( data, &data, 10 );
322                         cgd->addEntryPoint( nameAttr->value, entry );
323                 }
324         };
325
326 tag_state_list: tag_state_list_head state_list '/' TAG_state_list;
327
328 tag_state_list_head: TAG_state_list
329         final {
330                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
331                 if ( lengthAttr == 0 )
332                         error($1->loc) << "tag <state_list> requires a length attribute" << endl;
333                 else {
334                         unsigned long length = strtoul( lengthAttr->value, 0, 10 );
335                         cgd->initStateList( length );
336                         curState = 0;
337                 }
338         };
339
340 state_list: state_list tag_state;
341 state_list: ;
342
343 tag_state: TAG_state state_item_list '/' TAG_state
344         final {
345                 Attribute *idAttr = $1->tag->findAttr( "id" );
346                 if ( idAttr == 0 )
347                         error($1->loc) << "tag <state> requires an id attribute" << endl;
348                 else {
349                         int id = atoi( idAttr->value );
350                         cgd->setId( curState, id );
351                 }
352
353                 Attribute *lengthAttr = $1->tag->findAttr( "final" );
354                 if ( lengthAttr != 0 )
355                         cgd->setFinal( curState );
356                 curState += 1;
357         };
358
359 state_item_list: state_item_list state_item;
360 state_item_list: ;
361
362 state_item: tag_state_actions;
363 state_item: tag_state_cond_list;
364 state_item: tag_trans_list;
365
366 tag_state_actions: TAG_state_actions '/' TAG_state_actions
367         final {
368                 char *ad = $3->tag->content;
369
370                 long toStateAction = readOffsetPtr( ad, &ad );
371                 long fromStateAction = readOffsetPtr( ad, &ad );
372                 long eofAction = readOffsetPtr( ad, &ad );
373
374                 cgd->setStateActions( curState, toStateAction,
375                                 fromStateAction, eofAction );
376         };
377
378 tag_state_cond_list: tag_state_cond_list_head state_cond_list '/' TAG_cond_list;
379
380 tag_state_cond_list_head: TAG_cond_list
381         final {
382                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
383                 if ( lengthAttr == 0 )
384                         error($1->loc) << "tag <cond_list> requires a length attribute" << endl;
385                 else {
386                         ulong length = readLength( lengthAttr->value );
387                         cgd->initStateCondList( curState, length );
388                         curStateCond = 0;
389                 }
390         };
391
392 state_cond_list: state_cond_list state_cond;
393 state_cond_list: ;
394
395 state_cond: TAG_c '/' TAG_c
396         final {
397                 char *td = $3->tag->content;
398                 Key lowKey = readKey( td, &td );
399                 Key highKey = readKey( td, &td );
400                 long condId = readOffsetPtr( td, &td );
401                 cgd->addStateCond( curState, lowKey, highKey, condId );
402         };
403
404 tag_trans_list: tag_trans_list_head trans_list '/' TAG_trans_list
405         final {
406                 cgd->finishTransList( curState );
407         };
408
409 tag_trans_list_head: TAG_trans_list
410         final {
411                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
412                 if ( lengthAttr == 0 )
413                         error($1->loc) << "tag <trans_list> requires a length attribute" << endl;
414                 else {
415                         unsigned long length = strtoul( lengthAttr->value, 0, 10 );
416                         cgd->initTransList( curState, length );
417                         curTrans = 0;
418                 }
419         };
420
421 trans_list: trans_list tag_trans;
422 trans_list: ;
423
424 tag_trans: TAG_t '/' TAG_t
425         final {
426                 char *td = $3->tag->content;
427                 Key lowKey = readKey( td, &td );
428                 Key highKey = readKey( td, &td );
429                 long targ = readOffsetPtr( td, &td );
430                 long action = readOffsetPtr( td, &td );
431
432                 cgd->newTrans( curState, curTrans++, lowKey, highKey, targ, action );
433         };
434
435 #
436 # Action Lists.
437 #
438
439 tag_action_list: tag_action_list_head action_list '/' TAG_action_list;
440
441 tag_action_list_head: TAG_action_list
442         final {
443                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
444                 if ( lengthAttr == 0 )
445                         error($1->loc) << "tag <action_list> requires a length attribute" << endl;
446                 else {
447                         unsigned long length = strtoul( lengthAttr->value, 0, 10 );
448                         cgd->initActionList( length );
449                         curAction = 0;
450                 }
451         };
452
453 action_list: action_list tag_action;
454 action_list: ;
455
456 #
457 # Actions.
458 #
459
460 tag_action: TAG_action inline_list '/' TAG_action
461         final {
462                 Attribute *lineAttr = $1->tag->findAttr( "line" );
463                 Attribute *colAttr = $1->tag->findAttr( "col" );
464                 Attribute *nameAttr = $1->tag->findAttr( "name" );
465                 if ( lineAttr == 0 || colAttr == 0)
466                         error($1->loc) << "tag <action> requires a line and col attributes" << endl;
467                 else {
468                         unsigned long line = strtoul( lineAttr->value, 0, 10 );
469                         unsigned long col = strtoul( colAttr->value, 0, 10 );
470
471                         char *name = 0;
472                         if ( nameAttr != 0 )
473                                 name = nameAttr->value;
474
475                         cgd->newAction( curAction++, name, line, col, $2->inlineList );
476                 }
477         };
478
479 nonterm inline_list
480 {
481         InlineList *inlineList;
482 };
483
484
485 inline_list: inline_list inline_item
486         final {
487                 /* Append the item to the list, return the list. */
488                 $1->inlineList->append( $2->inlineItem );
489                 $$->inlineList = $1->inlineList;
490         };
491
492 inline_list: 
493         final {
494                 /* Start with empty list. */
495                 $$->inlineList = new InlineList;
496         };
497
498 nonterm inline_item_type
499 {
500         InlineItem *inlineItem;
501 };
502
503 nonterm inline_item uses inline_item_type;
504
505 inline_item: tag_text final { $$->inlineItem = $1->inlineItem; };
506 inline_item: tag_goto final { $$->inlineItem = $1->inlineItem; };
507 inline_item: tag_call final { $$->inlineItem = $1->inlineItem; };
508 inline_item: tag_next final { $$->inlineItem = $1->inlineItem; };
509 inline_item: tag_goto_expr final { $$->inlineItem = $1->inlineItem; };
510 inline_item: tag_call_expr final { $$->inlineItem = $1->inlineItem; };
511 inline_item: tag_next_expr final { $$->inlineItem = $1->inlineItem; };
512 inline_item: tag_ret final { $$->inlineItem = $1->inlineItem; };
513 inline_item: tag_break final { $$->inlineItem = $1->inlineItem; };
514 inline_item: tag_pchar final { $$->inlineItem = $1->inlineItem; };
515 inline_item: tag_char final { $$->inlineItem = $1->inlineItem; };
516 inline_item: tag_hold final { $$->inlineItem = $1->inlineItem; };
517 inline_item: tag_exec final { $$->inlineItem = $1->inlineItem; };
518 inline_item: tag_holdte final { $$->inlineItem = $1->inlineItem; };
519 inline_item: tag_execte final { $$->inlineItem = $1->inlineItem; };
520 inline_item: tag_curs final { $$->inlineItem = $1->inlineItem; };
521 inline_item: tag_targs final { $$->inlineItem = $1->inlineItem; };
522 inline_item: tag_il_entry final { $$->inlineItem = $1->inlineItem; };
523 inline_item: tag_init_tokstart final { $$->inlineItem = $1->inlineItem; };
524 inline_item: tag_init_act final { $$->inlineItem = $1->inlineItem; };
525 inline_item: tag_get_tokend final { $$->inlineItem = $1->inlineItem; };
526 inline_item: tag_set_tokstart final { $$->inlineItem = $1->inlineItem; };
527 inline_item: tag_set_tokend final { $$->inlineItem = $1->inlineItem; };
528 inline_item: tag_set_act final { $$->inlineItem = $1->inlineItem; };
529 inline_item: tag_sub_action final { $$->inlineItem = $1->inlineItem; };
530 inline_item: tag_lm_switch final { $$->inlineItem = $1->inlineItem; };
531
532 nonterm tag_text uses inline_item_type;
533 nonterm tag_goto uses inline_item_type;
534 nonterm tag_call uses inline_item_type;
535 nonterm tag_next uses inline_item_type;
536 nonterm tag_goto_expr uses inline_item_type;
537 nonterm tag_call_expr uses inline_item_type;
538 nonterm tag_next_expr uses inline_item_type;
539 nonterm tag_ret uses inline_item_type;
540 nonterm tag_break uses inline_item_type;
541 nonterm tag_pchar uses inline_item_type;
542 nonterm tag_char uses inline_item_type;
543 nonterm tag_hold uses inline_item_type;
544 nonterm tag_exec uses inline_item_type;
545 nonterm tag_holdte uses inline_item_type;
546 nonterm tag_execte uses inline_item_type;
547 nonterm tag_curs uses inline_item_type;
548 nonterm tag_targs uses inline_item_type;
549 nonterm tag_il_entry uses inline_item_type;
550 nonterm tag_init_tokstart uses inline_item_type;
551 nonterm tag_init_act uses inline_item_type;
552 nonterm tag_get_tokend uses inline_item_type;
553 nonterm tag_set_tokstart uses inline_item_type;
554 nonterm tag_set_tokend uses inline_item_type;
555 nonterm tag_set_act uses inline_item_type;
556 nonterm tag_sub_action uses inline_item_type;
557 nonterm tag_lm_switch uses inline_item_type;
558
559 tag_text: TAG_text '/' TAG_text
560         final {
561                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Text );
562                 $$->inlineItem->data = $3->tag->content;
563         };
564
565 tag_goto: TAG_goto '/' TAG_goto
566         final {
567                 int targ = strtol( $3->tag->content, 0, 10 );
568                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Goto );
569                 $$->inlineItem->targId = targ;
570         };
571
572 tag_call: TAG_call '/' TAG_call
573         final {
574                 int targ = strtol( $3->tag->content, 0, 10 );
575                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Call );
576                 $$->inlineItem->targId = targ;
577         };
578
579 tag_next: TAG_next '/' TAG_next
580         final {
581                 int targ = strtol( $3->tag->content, 0, 10 );
582                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Next );
583                 $$->inlineItem->targId = targ;
584         };
585
586 tag_goto_expr: TAG_goto_expr inline_list '/' TAG_goto_expr
587         final {
588                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::GotoExpr );
589                 $$->inlineItem->children = $2->inlineList;
590         };
591
592 tag_call_expr: TAG_call_expr inline_list '/' TAG_call_expr
593         final {
594                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::CallExpr );
595                 $$->inlineItem->children = $2->inlineList;
596         };
597
598 tag_next_expr: TAG_next_expr inline_list '/' TAG_next_expr
599         final {
600                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::NextExpr );
601                 $$->inlineItem->children = $2->inlineList;
602         };
603
604 tag_ret: TAG_ret '/' TAG_ret
605         final {
606                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Ret );
607         };
608
609 tag_break: TAG_break '/' TAG_break
610         final {
611                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Break );
612         };
613
614 tag_pchar: TAG_pchar '/' TAG_pchar
615         final {
616                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::PChar );
617         };
618
619 tag_char: TAG_char '/' TAG_char
620         final {
621                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Char );
622         };
623
624 tag_hold: TAG_hold '/' TAG_hold
625         final {
626                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Hold );
627         };
628
629 tag_exec: TAG_exec inline_list '/' TAG_exec
630         final {
631                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Exec );
632                 $$->inlineItem->children = $2->inlineList;
633         };
634
635 tag_holdte: TAG_holdte '/' TAG_holdte
636         final {
637                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::HoldTE );
638         };
639
640 tag_execte: TAG_execte inline_list '/' TAG_execte
641         final {
642                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::ExecTE );
643                 $$->inlineItem->children = $2->inlineList;
644         };
645
646 tag_curs: TAG_curs '/' TAG_curs
647         final {
648                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Curs );
649         };
650
651 tag_targs: TAG_targs '/' TAG_targs
652         final {
653                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Targs );
654         };
655
656 tag_il_entry: TAG_entry '/' TAG_entry
657         final {
658                 int targ = strtol( $3->tag->content, 0, 10 );
659                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::Entry );
660                 $$->inlineItem->targId = targ;
661         };
662
663 tag_init_tokstart: TAG_init_tokstart '/' TAG_init_tokstart
664         final {
665                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::LmInitTokStart );
666         };
667
668 tag_init_act: TAG_init_act '/' TAG_init_act
669         final {
670                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::LmInitAct );
671         };
672
673 tag_get_tokend: TAG_get_tokend '/' TAG_get_tokend
674         final {
675                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::LmGetTokEnd );
676         };
677
678 tag_set_tokstart: TAG_set_tokstart '/' TAG_set_tokstart
679         final {
680                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSetTokStart );
681                 cgd->hasLongestMatch = true;
682         };
683
684 tag_set_tokend: TAG_set_tokend '/' TAG_set_tokend
685         final {
686                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSetTokEnd );
687                 $$->inlineItem->offset = strtol( $3->tag->content, 0, 10 );
688         };
689
690 tag_set_act: TAG_set_act '/' TAG_set_act
691         final {
692                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSetActId );
693                 $$->inlineItem->lmId = strtol( $3->tag->content, 0, 10 );
694         };
695
696 tag_sub_action: TAG_sub_action inline_list '/' TAG_sub_action
697         final {
698                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::SubAction );
699                 $$->inlineItem->children = $2->inlineList;
700         };
701
702 # Action switches.
703 tag_lm_switch: TAG_lm_switch lm_action_list '/' TAG_lm_switch
704         final {
705                 bool handlesError = false;
706                 Attribute *handlesErrorAttr = $1->tag->findAttr( "handles_error" );
707                 if ( handlesErrorAttr != 0 )
708                         handlesError = true;
709
710                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSwitch );
711                 $$->inlineItem->children = $2->inlineList;
712                 $$->inlineItem->handlesError = handlesError;
713         };
714
715 nonterm lm_action_list
716 {
717         InlineList *inlineList;
718 };
719
720 lm_action_list: lm_action_list tag_inline_action
721         final {
722                 $$->inlineList = $1->inlineList;
723                 $$->inlineList->append( $2->inlineItem );
724         };
725 lm_action_list:
726         final {
727                 $$->inlineList = new InlineList;
728         };
729
730 nonterm tag_inline_action uses inline_item_type;
731
732 tag_inline_action: TAG_sub_action inline_list '/' TAG_sub_action
733         final {
734                 $$->inlineItem = new InlineItem( InputLoc(), InlineItem::SubAction );
735                 $$->inlineItem->children = $2->inlineList;
736
737                 Attribute *idAttr = $1->tag->findAttr( "id" );
738                 if ( idAttr != 0 ) {
739                         unsigned long id = strtoul( idAttr->value, 0, 10 );
740                         $$->inlineItem->lmId = id;
741                 }
742         };
743
744 #
745 # Lists of Actions.
746 #
747
748 tag_action_table_list: 
749         tag_action_table_list_head action_table_list '/' TAG_action_table_list;
750
751 tag_action_table_list_head: TAG_action_table_list
752         final {
753                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
754                 if ( lengthAttr == 0 ) {
755                         error($1->loc) << "tag <action_table_list> requires "
756                                         "a length attribute" << endl;
757                 }
758                 else {
759                         unsigned long length = strtoul( lengthAttr->value, 0, 10 );
760                         cgd->initActionTableList( length );
761                         curActionTable = 0;
762                 }
763         };
764
765 action_table_list: action_table_list tag_action_table;
766 action_table_list: ;
767
768 tag_action_table: TAG_action_table '/' TAG_action_table
769         final {
770                 /* Find the length of the action table. */
771                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
772                 if ( lengthAttr == 0 )
773                         error($1->loc) << "tag <at> requires a length attribute" << endl;
774                 else {
775                         unsigned long length = strtoul( lengthAttr->value, 0, 10 );
776
777                         /* Collect the action table. */
778                         RedAction *redAct = cgd->allActionTables + curActionTable;
779                         redAct->actListId = curActionTable;
780                         redAct->key.setAsNew( length );
781                         char *ptr = $3->tag->content;
782                         int pos = 0;
783                         while ( *ptr != 0 ) {
784                                 unsigned long actionId = strtoul( ptr, &ptr, 10 );
785                                 redAct->key[pos].key = 0;
786                                 redAct->key[pos].value = cgd->allActions+actionId;
787                                 pos += 1;
788                         }
789
790                         /* Insert into the action table map. */
791                         cgd->redFsm->actionMap.insert( redAct );
792                 }
793
794                 curActionTable += 1;
795         };
796
797 #
798 # Conditions.
799 #
800
801 tag_cond_space_list: tag_cond_space_list_head cond_space_list '/' TAG_cond_space_list;
802
803 tag_cond_space_list_head: TAG_cond_space_list
804         final {
805                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
806                 if ( lengthAttr == 0 ) {
807                         error($1->loc) << "tag <cond_space_list> "
808                                         "requires a length attribute" << endl;
809                 }
810                 else {
811                         ulong length = readLength( lengthAttr->value );
812                         cgd->initCondSpaceList( length );
813                         curCondSpace = 0;
814                 }
815         };
816
817 cond_space_list: cond_space_list tag_cond_space;
818 cond_space_list: tag_cond_space;
819
820 tag_cond_space: TAG_cond_space '/' TAG_cond_space
821         final {
822                 Attribute *lengthAttr = $1->tag->findAttr( "length" );
823                 Attribute *idAttr = $1->tag->findAttr( "id" );
824                 if ( lengthAttr == 0 )
825                         error($1->loc) << "tag <cond_space> requires a length attribute" << endl;
826                 else {
827                         if ( lengthAttr == 0 )
828                                 error($1->loc) << "tag <cond_space> requires an id attribute" << endl;
829                         else {
830                                 unsigned long condSpaceId = strtoul( idAttr->value, 0, 10 );
831                                 ulong length = readLength( lengthAttr->value );
832
833                                 char *td = $3->tag->content;
834                                 Key baseKey = readKey( td, &td );
835
836                                 cgd->newCondSpace( curCondSpace, condSpaceId, baseKey );
837                                 for ( ulong a = 0; a < length; a++ ) {
838                                         long actionOffset = readOffsetPtr( td, &td );
839                                         cgd->condSpaceItem( curCondSpace, actionOffset );
840                                 }
841                                 curCondSpace += 1;
842                         }
843                 }
844         };
845
846 }%%
847
848 %%{
849         write types;
850         write data;
851 }%%
852
853 void Parser::init()
854 {
855         %% write init;
856 }
857
858 int Parser::parseLangEl( int type, const Token *token )
859 {
860         %% write exec;
861         return errCount == 0 ? 0 : -1;
862 }
863
864
865 unsigned long readLength( char *td )
866 {
867         return strtoul( td, 0, 10 );
868 }
869
870 Key readKey( char *td, char **end )
871 {
872         if ( keyOps->isSigned )
873                 return Key( strtol( td, end, 10 ) );
874         else
875                 return Key( strtoul( td, end, 10 ) );
876 }
877
878 long readOffsetPtr( char *td, char **end )
879 {
880         while ( *td == ' ' || *td == '\t' )
881                 td++;
882
883         if ( *td == 'x' ) {
884                 if ( end != 0 )
885                         *end = td + 1;
886                 return -1;
887         }
888
889         return strtol( td, end, 10 );
890 }
891
892 ostream &Parser::warning( const InputLoc &loc )
893 {
894         cerr << fileName << ":" << loc.line << ":" << loc.col << ": warning: ";
895         return cerr;
896 }
897
898 ostream &Parser::error( const InputLoc &loc )
899 {
900         errCount += 1;
901         assert( fileName != 0 );
902         cerr << fileName << ":" << loc.line << ":" << loc.col << ": ";
903         return cerr;
904 }
905
906
907 ostream &Parser::parser_error( int tokId, Token &token )
908 {
909         errCount += 1;
910         assert( fileName != 0 );
911         cerr << fileName << ":" << token.loc.line << ":" << token.loc.col;
912         if ( token.tag != 0 ) {
913                 if ( token.tag->tagId == 0 )
914                         cerr << ": at unknown tag";
915                 else
916                         cerr << ": at tag <" << token.tag->tagId->name << ">";
917         }
918         cerr << ": ";
919         
920         return cerr;
921 }
922
923 int Parser::token( int tokenId, Token &tok )
924 {
925         int res = parseLangEl( tokenId, &tok );
926         if ( res < 0 ) {
927                 parser_error( tokenId, tok ) << "parse error" << endl;
928                 exit(1);
929         }
930         return res;
931 }
932
933 int Parser::token( int tokenId, int col, int line )
934 {
935         Token tok;
936         tok.loc.col = col;
937         tok.loc.line = line;
938         tok.tag = 0;
939         return token( tokenId, tok );
940 }
941
942 int Parser::token( XMLTag *tag, int col, int line )
943 {
944         Token tok;
945         tok.loc.col = col;
946         tok.loc.line = line;
947         tok.tag = tag;
948         
949         if ( tag->type == XMLTag::Close ) {
950                 int res = token( '/', tok );
951                 if ( res < 0 )
952                         return res;
953         }
954
955         tok.tag = tag;
956         return token( tag->tagId != 0 ? tag->tagId->id : TAG_unknown, tok );
957 }