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