The action of the <ragel_def> tag was made into a direct function call.
[external/ragel.git] / ragel / xmlcodegen.cpp
1 /*
2  *  Copyright 2005-2007 Adrian Thurston <thurston@complang.org>
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
23 #include "ragel.h"
24 #include "xmlcodegen.h"
25 #include "xmlparse.h"
26 #include "parsedata.h"
27 #include "fsmgraph.h"
28 #include <string.h>
29
30 using namespace std;
31
32 XMLCodeGen::XMLCodeGen( char *fsmName, ParseData *pd, FsmAp *fsm, 
33                 std::ostream &out, XmlParser &xmlParser )
34 :
35         fsmName(fsmName),
36         pd(pd),
37         fsm(fsm),
38         out(out),
39         xmlParser(xmlParser),
40         nextActionTableId(0)
41 {
42 }
43
44
45 void XMLCodeGen::writeActionList()
46 {
47         /* Determine which actions to write. */
48         int nextActionId = 0;
49         for ( ActionList::Iter act = pd->actionList; act.lte(); act++ ) {
50                 if ( act->numRefs() > 0 || act->numCondRefs > 0 )
51                         act->actionId = nextActionId++;
52         }
53
54         /* Write the list. */
55         out << "    <action_list length=\"" << nextActionId << "\">\n";
56         for ( ActionList::Iter act = pd->actionList; act.lte(); act++ ) {
57                 if ( act->actionId >= 0 )
58                         writeAction( act );
59         }
60         out << "    </action_list>\n";
61 }
62
63 void XMLCodeGen::writeActionTableList()
64 {
65         /* Must first order the action tables based on their id. */
66         int numTables = nextActionTableId;
67         RedActionTable **tables = new RedActionTable*[numTables];
68         for ( ActionTableMap::Iter at = actionTableMap; at.lte(); at++ )
69                 tables[at->id] = at;
70
71         out << "    <action_table_list length=\"" << numTables << "\">\n";
72         for ( int t = 0; t < numTables; t++ ) {
73                 out << "      <action_table id=\"" << t << "\" length=\"" << 
74                                 tables[t]->key.length() << "\">";
75                 for ( ActionTable::Iter atel = tables[t]->key; atel.lte(); atel++ ) {
76                         out << atel->value->actionId;
77                         if ( ! atel.last() )
78                                 out << " ";
79                 }
80                 out << "</action_table>\n";
81         }
82         out << "    </action_table_list>\n";
83
84         delete[] tables;
85 }
86
87 void XMLCodeGen::reduceActionTables()
88 {
89         /* Reduce the actions tables to a set. */
90         for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) {
91                 RedActionTable *actionTable = 0;
92
93                 /* Reduce To State Actions. */
94                 if ( st->toStateActionTable.length() > 0 ) {
95                         if ( actionTableMap.insert( st->toStateActionTable, &actionTable ) )
96                                 actionTable->id = nextActionTableId++;
97                 }
98
99                 /* Reduce From State Actions. */
100                 if ( st->fromStateActionTable.length() > 0 ) {
101                         if ( actionTableMap.insert( st->fromStateActionTable, &actionTable ) )
102                                 actionTable->id = nextActionTableId++;
103                 }
104
105                 /* Reduce EOF actions. */
106                 if ( st->eofActionTable.length() > 0 ) {
107                         if ( actionTableMap.insert( st->eofActionTable, &actionTable ) )
108                                 actionTable->id = nextActionTableId++;
109                 }
110
111                 /* Loop the transitions and reduce their actions. */
112                 for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) {
113                         if ( trans->actionTable.length() > 0 ) {
114                                 if ( actionTableMap.insert( trans->actionTable, &actionTable ) )
115                                         actionTable->id = nextActionTableId++;
116                         }
117                 }
118         }
119 }
120
121 void XMLCodeGen::appendTrans( TransListVect &outList, Key lowKey, 
122                 Key highKey, TransAp *trans )
123 {
124         if ( trans->toState != 0 || trans->actionTable.length() > 0 )
125                 outList.append( TransEl( lowKey, highKey, trans ) );
126 }
127
128 void XMLCodeGen::writeKey( Key key )
129 {
130         if ( keyOps->isSigned )
131                 out << key.getVal();
132         else
133                 out << (unsigned long) key.getVal();
134 }
135
136 void XMLCodeGen::writeTrans( Key lowKey, Key highKey, TransAp *trans )
137 {
138         /* First reduce the action. */
139         RedActionTable *actionTable = 0;
140         if ( trans->actionTable.length() > 0 )
141                 actionTable = actionTableMap.find( trans->actionTable );
142
143         /* Write the transition. */
144         out << "        <t>";
145         writeKey( lowKey );
146         out << " ";
147         writeKey( highKey );
148
149         if ( trans->toState != 0 )
150                 out << " " << trans->toState->alg.stateNum;
151         else
152                 out << " x";
153
154         if ( actionTable != 0 )
155                 out << " " << actionTable->id;
156         else
157                 out << " x";
158         out << "</t>\n";
159 }
160
161 void XMLCodeGen::writeTransList( StateAp *state )
162 {
163         TransListVect outList;
164
165         /* If there is only are no ranges the task is simple. */
166         if ( state->outList.length() > 0 ) {
167                 /* Loop each source range. */
168                 for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
169                         /* Reduce the transition. If it reduced to anything then add it. */
170                         appendTrans( outList, trans->lowKey, trans->highKey, trans );
171                 }
172         }
173
174         out << "      <trans_list length=\"" << outList.length() << "\">\n";
175         for ( TransListVect::Iter tvi = outList; tvi.lte(); tvi++ )
176                 writeTrans( tvi->lowKey, tvi->highKey, tvi->value );
177         out << "      </trans_list>\n";
178 }
179
180 void XMLCodeGen::writeEofTrans( StateAp *state )
181 {
182         RedActionTable *eofActions = 0;
183         if ( state->eofActionTable.length() > 0 )
184                 eofActions = actionTableMap.find( state->eofActionTable );
185         
186         /* The <eof_t> is used when there is an eof target, otherwise the eof
187          * action goes into state actions. */
188         if ( state->eofTarget != 0 ) {
189                 out << "      <eof_t>" << state->eofTarget->alg.stateNum;
190
191                 if ( eofActions != 0 )
192                         out << " " << eofActions->id;
193                 else
194                         out << " x"; 
195
196                 out << "</eof_t>" << endl;
197         }
198 }
199
200 void XMLCodeGen::writeText( InlineItem *item )
201 {
202         if ( item->prev == 0 || item->prev->type != InlineItem::Text )
203                 out << "<text>";
204         xmlEscapeHost( out, item->data, strlen(item->data) );
205         if ( item->next == 0 || item->next->type != InlineItem::Text )
206                 out << "</text>";
207 }
208
209 void XMLCodeGen::writeGoto( InlineItem *item )
210 {
211         if ( pd->generatingSectionSubset )
212                 out << "<goto>-1</goto>";
213         else {
214                 EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
215                 out << "<goto>" << targ->value->alg.stateNum << "</goto>";
216         }
217 }
218
219 void XMLCodeGen::writeCall( InlineItem *item )
220 {
221         if ( pd->generatingSectionSubset )
222                 out << "<call>-1</call>";
223         else {
224                 EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
225                 out << "<call>" << targ->value->alg.stateNum << "</call>";
226         }
227 }
228
229 void XMLCodeGen::writeNext( InlineItem *item )
230 {
231         if ( pd->generatingSectionSubset )
232                 out << "<next>-1</next>";
233         else {
234                 EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
235                 out << "<next>" << targ->value->alg.stateNum << "</next>";
236         }
237 }
238
239 void XMLCodeGen::writeGotoExpr( InlineItem *item )
240 {
241         out << "<goto_expr>";
242         writeInlineList( item->children );
243         out << "</goto_expr>";
244 }
245
246 void XMLCodeGen::writeCallExpr( InlineItem *item )
247 {
248         out << "<call_expr>";
249         writeInlineList( item->children );
250         out << "</call_expr>";
251 }
252
253 void XMLCodeGen::writeNextExpr( InlineItem *item )
254 {
255         out << "<next_expr>";
256         writeInlineList( item->children );
257         out << "</next_expr>";
258 }
259
260 void XMLCodeGen::writeEntry( InlineItem *item )
261 {
262         if ( pd->generatingSectionSubset )
263                 out << "<entry>-1</entry>";
264         else {
265                 EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
266                 out << "<entry>" << targ->value->alg.stateNum << "</entry>";
267         }
268 }
269
270 void XMLCodeGen::writeActionExec( InlineItem *item )
271 {
272         out << "<exec>";
273         writeInlineList( item->children );
274         out << "</exec>";
275 }
276
277 void XMLCodeGen::writeLmOnLast( InlineItem *item )
278 {
279         out << "<set_tokend>1</set_tokend>";
280
281         if ( item->longestMatchPart->action != 0 ) {
282                 out << "<sub_action>";
283                 writeInlineList( item->longestMatchPart->action->inlineList );
284                 out << "</sub_action>";
285         }
286 }
287
288 void XMLCodeGen::writeLmOnNext( InlineItem *item )
289 {
290         out << "<set_tokend>0</set_tokend>";
291         out << "<hold></hold>";
292
293         if ( item->longestMatchPart->action != 0 ) {
294                 out << "<sub_action>";
295                 writeInlineList( item->longestMatchPart->action->inlineList );
296                 out << "</sub_action>";
297         }
298 }
299
300 void XMLCodeGen::writeLmOnLagBehind( InlineItem *item )
301 {
302         out << "<exec><get_tokend></get_tokend></exec>";
303
304         if ( item->longestMatchPart->action != 0 ) {
305                 out << "<sub_action>";
306                 writeInlineList( item->longestMatchPart->action->inlineList );
307                 out << "</sub_action>";
308         }
309 }
310
311 void XMLCodeGen::writeLmSwitch( InlineItem *item )
312 {
313         LongestMatch *longestMatch = item->longestMatch;
314         out << "<lm_switch>\n";
315
316         /* We can't put the <exec> here because we may need to handle the error
317          * case and in that case p should not be changed. Instead use a default
318          * label in the switch to adjust p when user actions are not set. An id of
319          * -1 indicates the default. */
320
321         if ( longestMatch->lmSwitchHandlesError ) {
322                 /* If the switch handles error then we should have also forced the
323                  * error state. */
324                 assert( fsm->errState != 0 );
325
326                 out << "        <sub_action id=\"0\">";
327                 out << "<goto>" << fsm->errState->alg.stateNum << "</goto>";
328                 out << "</sub_action>\n";
329         }
330         
331         bool needDefault = false;
332         for ( LmPartList::Iter lmi = *longestMatch->longestMatchList; lmi.lte(); lmi++ ) {
333                 if ( lmi->inLmSelect ) {
334                         if ( lmi->action == 0 )
335                                 needDefault = true;
336                         else {
337                                 /* Open the action. Write it with the context that sets up _p 
338                                  * when doing control flow changes from inside the machine. */
339                                 out << "        <sub_action id=\"" << lmi->longestMatchId << "\">";
340                                 out << "<exec><get_tokend></get_tokend></exec>";
341                                 writeInlineList( lmi->action->inlineList );
342                                 out << "</sub_action>\n";
343                         }
344                 }
345         }
346
347         if ( needDefault ) {
348                 out << "        <sub_action id=\"-1\"><exec><get_tokend>"
349                                 "</get_tokend></exec></sub_action>\n";
350         }
351
352         out << "    </lm_switch>";
353 }
354
355 void XMLCodeGen::writeInlineList( InlineList *inlineList )
356 {
357         for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
358                 switch ( item->type ) {
359                 case InlineItem::Text:
360                         writeText( item );
361                         break;
362                 case InlineItem::Goto:
363                         writeGoto( item );
364                         break;
365                 case InlineItem::GotoExpr:
366                         writeGotoExpr( item );
367                         break;
368                 case InlineItem::Call:
369                         writeCall( item );
370                         break;
371                 case InlineItem::CallExpr:
372                         writeCallExpr( item );
373                         break;
374                 case InlineItem::Next:
375                         writeNext( item );
376                         break;
377                 case InlineItem::NextExpr:
378                         writeNextExpr( item );
379                         break;
380                 case InlineItem::Break:
381                         out << "<break></break>";
382                         break;
383                 case InlineItem::Ret: 
384                         out << "<ret></ret>";
385                         break;
386                 case InlineItem::PChar:
387                         out << "<pchar></pchar>";
388                         break;
389                 case InlineItem::Char: 
390                         out << "<char></char>";
391                         break;
392                 case InlineItem::Curs: 
393                         out << "<curs></curs>";
394                         break;
395                 case InlineItem::Targs: 
396                         out << "<targs></targs>";
397                         break;
398                 case InlineItem::Entry:
399                         writeEntry( item );
400                         break;
401
402                 case InlineItem::Hold:
403                         out << "<hold></hold>";
404                         break;
405                 case InlineItem::Exec:
406                         writeActionExec( item );
407                         break;
408
409                 case InlineItem::LmSetActId:
410                         out << "<set_act>" << 
411                                         item->longestMatchPart->longestMatchId << 
412                                         "</set_act>";
413                         break;
414                 case InlineItem::LmSetTokEnd:
415                         out << "<set_tokend>1</set_tokend>";
416                         break;
417
418                 case InlineItem::LmOnLast:
419                         writeLmOnLast( item );
420                         break;
421                 case InlineItem::LmOnNext:
422                         writeLmOnNext( item );
423                         break;
424                 case InlineItem::LmOnLagBehind:
425                         writeLmOnLagBehind( item );
426                         break;
427                 case InlineItem::LmSwitch: 
428                         writeLmSwitch( item );
429                         break;
430
431                 case InlineItem::LmInitAct:
432                         out << "<init_act></init_act>";
433                         break;
434                 case InlineItem::LmInitTokStart:
435                         out << "<init_tokstart></init_tokstart>";
436                         break;
437                 case InlineItem::LmSetTokStart:
438                         out << "<set_tokstart></set_tokstart>";
439                         break;
440                 }
441         }
442 }
443
444 void XMLCodeGen::writeAction( Action *action )
445 {
446         out << "      <action id=\"" << action->actionId << "\"";
447         if ( action->name != 0 ) 
448                 out << " name=\"" << action->name << "\"";
449         out << " line=\"" << action->loc.line << "\" col=\"" << action->loc.col << "\">";
450         writeInlineList( action->inlineList );
451         out << "</action>\n";
452 }
453
454 void xmlEscapeHost( std::ostream &out, char *data, long len )
455 {
456         char *end = data + len;
457         while ( data != end ) {
458                 switch ( *data ) {
459                 case '<': out << "&lt;"; break;
460                 case '>': out << "&gt;"; break;
461                 case '&': out << "&amp;"; break;
462                 default: out << *data; break;
463                 }
464                 data += 1;
465         }
466 }
467
468 void XMLCodeGen::writeStateActions( StateAp *state )
469 {
470         RedActionTable *toStateActions = 0;
471         if ( state->toStateActionTable.length() > 0 )
472                 toStateActions = actionTableMap.find( state->toStateActionTable );
473
474         RedActionTable *fromStateActions = 0;
475         if ( state->fromStateActionTable.length() > 0 )
476                 fromStateActions = actionTableMap.find( state->fromStateActionTable );
477
478         /* EOF actions go out here only if the state has no eof target. If it has
479          * an eof target then an eof transition will be used instead. */
480         RedActionTable *eofActions = 0;
481         if ( state->eofTarget == 0 && state->eofActionTable.length() > 0 )
482                 eofActions = actionTableMap.find( state->eofActionTable );
483         
484         if ( toStateActions != 0 || fromStateActions != 0 || eofActions != 0 ) {
485                 out << "      <state_actions>";
486                 if ( toStateActions != 0 )
487                         out << toStateActions->id;
488                 else
489                         out << "x";
490
491                 if ( fromStateActions != 0 )
492                         out << " " << fromStateActions->id;
493                 else
494                         out << " x";
495
496                 if ( eofActions != 0 )
497                         out << " " << eofActions->id;
498                 else
499                         out << " x";
500
501                 out << "</state_actions>\n";
502         }
503 }
504
505 void XMLCodeGen::writeStateConditions( StateAp *state )
506 {
507         if ( state->stateCondList.length() > 0 ) {
508                 out << "      <cond_list length=\"" << state->stateCondList.length() << "\">\n";
509                 for ( StateCondList::Iter scdi = state->stateCondList; scdi.lte(); scdi++ ) {
510                         out << "        <c>";
511                         writeKey( scdi->lowKey );
512                         out << " ";
513                         writeKey( scdi->highKey );
514                         out << " ";
515                         out << scdi->condSpace->condSpaceId;
516                         out << "</c>\n";
517                 }
518                 out << "      </cond_list>\n";
519         }
520 }
521
522 void XMLCodeGen::writeStateList()
523 {
524         /* Write the list of states. */
525         out << "    <state_list length=\"" << fsm->stateList.length() << "\">\n";
526         for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) {
527                 out << "      <state id=\"" << st->alg.stateNum << "\"";
528                 if ( st->isFinState() )
529                         out << " final=\"t\"";
530                 out << ">\n";
531
532                 writeStateActions( st );
533                 writeEofTrans( st );
534                 writeStateConditions( st );
535                 writeTransList( st );
536
537                 out << "      </state>\n";
538
539                 if ( !st.last() )
540                         out << "\n";
541         }
542         out << "    </state_list>\n";
543 }
544
545 bool XMLCodeGen::writeNameInst( NameInst *nameInst )
546 {
547         bool written = false;
548         if ( nameInst->parent != 0 )
549                 written = writeNameInst( nameInst->parent );
550         
551         if ( nameInst->name != 0 ) {
552                 if ( written )
553                         out << '_';
554                 out << nameInst->name;
555                 written = true;
556         }
557
558         return written;
559 }
560
561 void XMLCodeGen::writeEntryPoints()
562 {
563         /* List of entry points other than start state. */
564         if ( fsm->entryPoints.length() > 0 || pd->lmRequiresErrorState ) {
565                 out << "    <entry_points";
566                 if ( pd->lmRequiresErrorState )
567                         out << " error=\"t\"";
568                 out << ">\n";
569                 for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) {
570                         /* Get the name instantiation from nameIndex. */
571                         NameInst *nameInst = pd->nameIndex[en->key];
572                         StateAp *state = en->value;
573                         out << "      <entry name=\"";
574                         writeNameInst( nameInst );
575                         out << "\">" << state->alg.stateNum << "</entry>\n";
576                 }
577                 out << "    </entry_points>\n";
578         }
579 }
580
581 void XMLCodeGen::writeMachine()
582 {
583         /* Open the machine. */
584         out << "  <machine>\n"; 
585         
586         /* Action tables. */
587         reduceActionTables();
588
589         writeActionList();
590         writeActionTableList();
591         writeConditions();
592
593         /* Start state. */
594         out << "    <start_state>" << fsm->startState->alg.stateNum << 
595                         "</start_state>\n";
596         
597         /* Error state. */
598         if ( fsm->errState != 0 ) {
599                 out << "    <error_state>" << fsm->errState->alg.stateNum << 
600                         "</error_state>\n";
601         }
602
603         writeEntryPoints();
604         writeStateList();
605
606         out << "  </machine>\n";
607 }
608
609
610 void XMLCodeGen::writeConditions()
611 {
612         if ( condData->condSpaceMap.length() > 0 ) {
613                 long nextCondSpaceId = 0;
614                 for ( CondSpaceMap::Iter cs = condData->condSpaceMap; cs.lte(); cs++ )
615                         cs->condSpaceId = nextCondSpaceId++;
616
617                 out << "    <cond_space_list length=\"" << condData->condSpaceMap.length() << "\">\n";
618                 for ( CondSpaceMap::Iter cs = condData->condSpaceMap; cs.lte(); cs++ ) {
619                         out << "      <cond_space id=\"" << cs->condSpaceId << 
620                                 "\" length=\"" << cs->condSet.length() << "\">";
621                         writeKey( cs->baseKey );
622                         for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ )
623                                 out << " " << (*csi)->actionId;
624                         out << "</cond_space>\n";
625                 }
626                 out << "    </cond_space_list>\n";
627         }
628 }
629
630 void XMLCodeGen::writeExports()
631 {
632         if ( pd->exportList.length() > 0 ) {
633                 out << "  <exports>\n";
634                 for ( ExportList::Iter exp = pd->exportList; exp.lte(); exp++ ) {
635                         out << "    <ex name=\"" << exp->name << "\">";
636                         writeKey( exp->key );
637                         out << "</ex>\n";
638                 }
639                 out << "  </exports>\n";
640         }
641 }
642
643 void XMLCodeGen::writeXML()
644 {
645         /* Open the definition. */
646         xmlParser.open_ragel_def( fsmName );
647         out << "<ragel_def>\n";
648
649         /* Alphabet type. */
650         out << "  <alphtype>" << keyOps->alphType->internalName << "</alphtype>\n";
651         
652         /* Getkey expression. */
653         if ( pd->getKeyExpr != 0 ) {
654                 out << "  <getkey>";
655                 writeInlineList( pd->getKeyExpr );
656                 out << "</getkey>\n";
657         }
658
659         /* Access expression. */
660         if ( pd->accessExpr != 0 ) {
661                 out << "  <access>";
662                 writeInlineList( pd->accessExpr );
663                 out << "</access>\n";
664         }
665
666         /* PrePush expression. */
667         if ( pd->prePushExpr != 0 ) {
668                 out << "  <prepush>";
669                 writeInlineList( pd->prePushExpr );
670                 out << "</prepush>\n";
671         }
672
673         /* PostPop expression. */
674         if ( pd->postPopExpr != 0 ) {
675                 out << "  <postpop>";
676                 writeInlineList( pd->postPopExpr );
677                 out << "</postpop>\n";
678         }
679
680         /*
681          * Variable expressions.
682          */
683
684         if ( pd->pExpr != 0 ) {
685                 out << "  <p_expr>";
686                 writeInlineList( pd->pExpr );
687                 out << "</p_expr>\n";
688         }
689         
690         if ( pd->peExpr != 0 ) {
691                 out << "  <pe_expr>";
692                 writeInlineList( pd->peExpr );
693                 out << "</pe_expr>\n";
694         }
695
696         if ( pd->eofExpr != 0 ) {
697                 out << "  <eof_expr>";
698                 writeInlineList( pd->eofExpr );
699                 out << "</eof_expr>\n";
700         }
701         
702         if ( pd->csExpr != 0 ) {
703                 out << "  <cs_expr>";
704                 writeInlineList( pd->csExpr );
705                 out << "</cs_expr>\n";
706         }
707         
708         if ( pd->topExpr != 0 ) {
709                 out << "  <top_expr>";
710                 writeInlineList( pd->topExpr );
711                 out << "</top_expr>\n";
712         }
713         
714         if ( pd->stackExpr != 0 ) {
715                 out << "  <stack_expr>";
716                 writeInlineList( pd->stackExpr );
717                 out << "</stack_expr>\n";
718         }
719         
720         if ( pd->actExpr != 0 ) {
721                 out << "  <act_expr>";
722                 writeInlineList( pd->actExpr );
723                 out << "</act_expr>\n";
724         }
725         
726         if ( pd->tokstartExpr != 0 ) {
727                 out << "  <tokstart_expr>";
728                 writeInlineList( pd->tokstartExpr );
729                 out << "</tokstart_expr>\n";
730         }
731         
732         if ( pd->tokendExpr != 0 ) {
733                 out << "  <tokend_expr>";
734                 writeInlineList( pd->tokendExpr );
735                 out << "</tokend_expr>\n";
736         }
737         
738         if ( pd->dataExpr != 0 ) {
739                 out << "  <data_expr>";
740                 writeInlineList( pd->dataExpr );
741                 out << "</data_expr>\n";
742         }
743         
744         writeExports();
745         
746         writeMachine();
747
748         out <<
749                 "</ragel_def>\n";
750 }
751