2 * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
3 * 2004 Erich Ocean <eric.ocean@ampede.com>
4 * 2005 Alan West <alan@alanz.com>
7 /* This file is part of Ragel.
9 * Ragel is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * Ragel is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with Ragel; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "fsmcodegen.h"
34 using std::ostringstream
;
39 void cdLineDirective( ostream
&out
, const char *fileName
, int line
)
41 if ( noLineDirectives
)
44 /* Write the preprocessor line info for to the input file. */
45 out
<< "#line " << line
<< " \"";
46 for ( const char *pc
= fileName
; *pc
!= 0; pc
++ ) {
54 if ( noLineDirectives
)
60 void FsmCodeGen::genLineDirective( ostream
&out
)
62 std::streambuf
*sbuf
= out
.rdbuf();
63 output_filter
*filter
= static_cast<output_filter
*>(sbuf
);
64 cdLineDirective( out
, filter
->fileName
, filter
->line
+ 1 );
68 /* Init code gen with in parameters. */
69 FsmCodeGen::FsmCodeGen( ostream
&out
)
75 unsigned int FsmCodeGen::arrayTypeSize( unsigned long maxVal
)
77 long long maxValLL
= (long long) maxVal
;
78 HostType
*arrayType
= keyOps
->typeSubsumes( maxValLL
);
79 assert( arrayType
!= 0 );
80 return arrayType
->size
;
83 string
FsmCodeGen::ARRAY_TYPE( unsigned long maxVal
)
85 long long maxValLL
= (long long) maxVal
;
86 HostType
*arrayType
= keyOps
->typeSubsumes( maxValLL
);
87 assert( arrayType
!= 0 );
89 string ret
= arrayType
->data1
;
90 if ( arrayType
->data2
!= 0 ) {
92 ret
+= arrayType
->data2
;
98 /* Write out the fsm name. */
99 string
FsmCodeGen::FSM_NAME()
104 /* Emit the offset of the start state as a decimal integer. */
105 string
FsmCodeGen::START_STATE_ID()
108 ret
<< redFsm
->startState
->id
;
112 /* Write out the array of actions. */
113 std::ostream
&FsmCodeGen::ACTIONS_ARRAY()
116 int totalActions
= 1;
117 for ( ActionTableMap::Iter act
= redFsm
->actionMap
; act
.lte(); act
++ ) {
118 /* Write out the length, which will never be the last character. */
119 out
<< act
->key
.length() << ", ";
120 /* Put in a line break every 8 */
121 if ( totalActions
++ % 8 == 7 )
124 for ( ActionTable::Iter item
= act
->key
; item
.lte(); item
++ ) {
125 out
<< item
->value
->actionId
;
126 if ( ! (act
.last() && item
.last()) )
129 /* Put in a line break every 8 */
130 if ( totalActions
++ % 8 == 7 )
139 string
FsmCodeGen::ACCESS()
142 if ( accessExpr
!= 0 )
143 INLINE_LIST( ret
, accessExpr
, 0, false, false );
148 string
FsmCodeGen::P()
155 INLINE_LIST( ret
, pExpr
, 0, false, false );
161 string
FsmCodeGen::PE()
168 INLINE_LIST( ret
, peExpr
, 0, false, false );
174 string
FsmCodeGen::EOFV()
181 INLINE_LIST( ret
, eofExpr
, 0, false, false );
187 string
FsmCodeGen::CS()
191 ret
<< ACCESS() << "cs";
193 /* Emit the user supplied method of retrieving the key. */
195 INLINE_LIST( ret
, csExpr
, 0, false, false );
201 string
FsmCodeGen::TOP()
205 ret
<< ACCESS() + "top";
208 INLINE_LIST( ret
, topExpr
, 0, false, false );
214 string
FsmCodeGen::STACK()
217 if ( stackExpr
== 0 )
218 ret
<< ACCESS() + "stack";
221 INLINE_LIST( ret
, stackExpr
, 0, false, false );
227 string
FsmCodeGen::ACT()
231 ret
<< ACCESS() + "act";
234 INLINE_LIST( ret
, actExpr
, 0, false, false );
240 string
FsmCodeGen::TOKSTART()
243 if ( tokstartExpr
== 0 )
244 ret
<< ACCESS() + "ts";
247 INLINE_LIST( ret
, tokstartExpr
, 0, false, false );
253 string
FsmCodeGen::TOKEND()
256 if ( tokendExpr
== 0 )
257 ret
<< ACCESS() + "te";
260 INLINE_LIST( ret
, tokendExpr
, 0, false, false );
266 string
FsmCodeGen::GET_WIDE_KEY()
268 if ( redFsm
->anyConditions() )
274 string
FsmCodeGen::GET_WIDE_KEY( RedStateAp
*state
)
276 if ( state
->stateCondList
.length() > 0 )
282 string
FsmCodeGen::GET_KEY()
285 if ( getKeyExpr
!= 0 ) {
286 /* Emit the user supplied method of retrieving the key. */
288 INLINE_LIST( ret
, getKeyExpr
, 0, false, false );
292 /* Expression for retrieving the key, use simple dereference. */
293 ret
<< "(*" << P() << ")";
298 /* Write out level number of tabs. Makes the nested binary search nice
300 string
FsmCodeGen::TABS( int level
)
303 while ( level
-- > 0 )
308 /* Write out a key from the fsm code gen. Depends on wether or not the key is
310 string
FsmCodeGen::KEY( Key key
)
313 if ( keyOps
->isSigned
|| !hostLang
->explicitUnsigned
)
316 ret
<< (unsigned long) key
.getVal() << 'u';
320 void FsmCodeGen::EXEC( ostream
&ret
, GenInlineItem
*item
, int targState
, int inFinish
)
322 /* The parser gives fexec two children. The double brackets are for D
323 * code. If the inline list is a single word it will get interpreted as a
324 * C-style cast by the D compiler. */
325 ret
<< "{" << P() << " = ((";
326 INLINE_LIST( ret
, item
->children
, targState
, inFinish
, false );
330 void FsmCodeGen::LM_SWITCH( ostream
&ret
, GenInlineItem
*item
,
331 int targState
, int inFinish
, bool csForced
)
334 " switch( " << ACT() << " ) {\n";
336 bool haveDefault
= false;
337 for ( GenInlineList::Iter lma
= *item
->children
; lma
.lte(); lma
++ ) {
338 /* Write the case label, the action and the case break. */
339 if ( lma
->lmId
< 0 ) {
340 ret
<< " default:\n";
344 ret
<< " case " << lma
->lmId
<< ":\n";
346 /* Write the block and close it off. */
348 INLINE_LIST( ret
, lma
->children
, targState
, inFinish
, csForced
);
354 if ( hostLang
->lang
== HostLang::D
&& !haveDefault
)
355 ret
<< " default: break;";
362 void FsmCodeGen::SET_ACT( ostream
&ret
, GenInlineItem
*item
)
364 ret
<< ACT() << " = " << item
->lmId
<< ";";
367 void FsmCodeGen::SET_TOKEND( ostream
&ret
, GenInlineItem
*item
)
369 /* The tokend action sets tokend. */
370 ret
<< TOKEND() << " = " << P();
371 if ( item
->offset
!= 0 )
372 out
<< "+" << item
->offset
;
376 void FsmCodeGen::GET_TOKEND( ostream
&ret
, GenInlineItem
*item
)
381 void FsmCodeGen::INIT_TOKSTART( ostream
&ret
, GenInlineItem
*item
)
383 ret
<< TOKSTART() << " = " << NULL_ITEM() << ";";
386 void FsmCodeGen::INIT_ACT( ostream
&ret
, GenInlineItem
*item
)
388 ret
<< ACT() << " = 0;";
391 void FsmCodeGen::SET_TOKSTART( ostream
&ret
, GenInlineItem
*item
)
393 ret
<< TOKSTART() << " = " << P() << ";";
396 void FsmCodeGen::SUB_ACTION( ostream
&ret
, GenInlineItem
*item
,
397 int targState
, bool inFinish
, bool csForced
)
399 if ( item
->children
->length() > 0 ) {
400 /* Write the block and close it off. */
402 INLINE_LIST( ret
, item
->children
, targState
, inFinish
, csForced
);
408 /* Write out an inline tree structure. Walks the list and possibly calls out
409 * to virtual functions than handle language specific items in the tree. */
410 void FsmCodeGen::INLINE_LIST( ostream
&ret
, GenInlineList
*inlineList
,
411 int targState
, bool inFinish
, bool csForced
)
413 for ( GenInlineList::Iter item
= *inlineList
; item
.lte(); item
++ ) {
414 switch ( item
->type
) {
415 case GenInlineItem::Text
:
418 case GenInlineItem::Goto
:
419 GOTO( ret
, item
->targState
->id
, inFinish
);
421 case GenInlineItem::Call
:
422 CALL( ret
, item
->targState
->id
, targState
, inFinish
);
424 case GenInlineItem::Next
:
425 NEXT( ret
, item
->targState
->id
, inFinish
);
427 case GenInlineItem::Ret
:
428 RET( ret
, inFinish
);
430 case GenInlineItem::PChar
:
433 case GenInlineItem::Char
:
436 case GenInlineItem::Hold
:
439 case GenInlineItem::Exec
:
440 EXEC( ret
, item
, targState
, inFinish
);
442 case GenInlineItem::Curs
:
443 CURS( ret
, inFinish
);
445 case GenInlineItem::Targs
:
446 TARGS( ret
, inFinish
, targState
);
448 case GenInlineItem::Entry
:
449 ret
<< item
->targState
->id
;
451 case GenInlineItem::GotoExpr
:
452 GOTO_EXPR( ret
, item
, inFinish
);
454 case GenInlineItem::CallExpr
:
455 CALL_EXPR( ret
, item
, targState
, inFinish
);
457 case GenInlineItem::NextExpr
:
458 NEXT_EXPR( ret
, item
, inFinish
);
460 case GenInlineItem::LmSwitch
:
461 LM_SWITCH( ret
, item
, targState
, inFinish
, csForced
);
463 case GenInlineItem::LmSetActId
:
464 SET_ACT( ret
, item
);
466 case GenInlineItem::LmSetTokEnd
:
467 SET_TOKEND( ret
, item
);
469 case GenInlineItem::LmGetTokEnd
:
470 GET_TOKEND( ret
, item
);
472 case GenInlineItem::LmInitTokStart
:
473 INIT_TOKSTART( ret
, item
);
475 case GenInlineItem::LmInitAct
:
476 INIT_ACT( ret
, item
);
478 case GenInlineItem::LmSetTokStart
:
479 SET_TOKSTART( ret
, item
);
481 case GenInlineItem::SubAction
:
482 SUB_ACTION( ret
, item
, targState
, inFinish
, csForced
);
484 case GenInlineItem::Break
:
485 BREAK( ret
, targState
, csForced
);
490 /* Write out paths in line directives. Escapes any special characters. */
491 string
FsmCodeGen::LDIR_PATH( char *path
)
494 for ( char *pc
= path
; *pc
!= 0; pc
++ ) {
503 void FsmCodeGen::ACTION( ostream
&ret
, Action
*action
, int targState
,
504 bool inFinish
, bool csForced
)
506 /* Write the preprocessor line info for going into the source file. */
507 cdLineDirective( ret
, sourceFileName
, action
->loc
.line
);
509 /* Write the block and close it off. */
511 INLINE_LIST( ret
, action
->inlineList
, targState
, inFinish
, csForced
);
515 void FsmCodeGen::CONDITION( ostream
&ret
, Action
*condition
)
518 cdLineDirective( ret
, sourceFileName
, condition
->loc
.line
);
519 INLINE_LIST( ret
, condition
->inlineList
, 0, false, false );
522 string
FsmCodeGen::ERROR_STATE()
525 if ( redFsm
->errState
!= 0 )
526 ret
<< redFsm
->errState
->id
;
532 string
FsmCodeGen::FIRST_FINAL_STATE()
535 if ( redFsm
->firstFinState
!= 0 )
536 ret
<< redFsm
->firstFinState
->id
;
538 ret
<< redFsm
->nextStateId
;
542 void FsmCodeGen::writeInit()
547 out
<< "\t" << CS() << " = " << START() << ";\n";
549 /* If there are any calls, then the stack top needs initialization. */
550 if ( redFsm
->anyActionCalls() || redFsm
->anyActionRets() )
551 out
<< "\t" << TOP() << " = 0;\n";
553 if ( hasLongestMatch
) {
555 " " << TOKSTART() << " = " << NULL_ITEM() << ";\n"
556 " " << TOKEND() << " = " << NULL_ITEM() << ";\n"
557 " " << ACT() << " = 0;\n";
562 string
FsmCodeGen::DATA_PREFIX()
565 return FSM_NAME() + "_";
569 /* Emit the alphabet data type. */
570 string
FsmCodeGen::ALPH_TYPE()
572 string ret
= keyOps
->alphType
->data1
;
573 if ( keyOps
->alphType
->data2
!= 0 ) {
575 ret
+= + keyOps
->alphType
->data2
;
580 /* Emit the alphabet data type. */
581 string
FsmCodeGen::WIDE_ALPH_TYPE()
584 if ( redFsm
->maxKey
<= keyOps
->maxKey
)
587 long long maxKeyVal
= redFsm
->maxKey
.getLongLong();
588 HostType
*wideType
= keyOps
->typeSubsumes( keyOps
->isSigned
, maxKeyVal
);
589 assert( wideType
!= 0 );
591 ret
= wideType
->data1
;
592 if ( wideType
->data2
!= 0 ) {
594 ret
+= wideType
->data2
;
600 void FsmCodeGen::STATE_IDS()
602 if ( redFsm
->startState
!= 0 )
603 STATIC_VAR( "int", START() ) << " = " << START_STATE_ID() << ";\n";
605 if ( writeFirstFinal
)
606 STATIC_VAR( "int" , FIRST_FINAL() ) << " = " << FIRST_FINAL_STATE() << ";\n";
609 STATIC_VAR( "int", ERROR() ) << " = " << ERROR_STATE() << ";\n";
613 if ( entryPointNames
.length() > 0 ) {
614 for ( EntryNameVect::Iter en
= entryPointNames
; en
.lte(); en
++ ) {
615 STATIC_VAR( "int", DATA_PREFIX() + "en_" + *en
) <<
616 " = " << entryPointIds
[en
.pos()] << ";\n";
624 * Language specific, but style independent code generators functions.
627 string
CCodeGen::PTR_CONST()
632 std::ostream
&CCodeGen::OPEN_ARRAY( string type
, string name
)
634 out
<< "static const " << type
<< " " << name
<< "[] = {\n";
638 std::ostream
&CCodeGen::CLOSE_ARRAY()
640 return out
<< "};\n";
643 std::ostream
&CCodeGen::STATIC_VAR( string type
, string name
)
645 out
<< "static const " << type
<< " " << name
;
649 string
CCodeGen::UINT( )
651 return "unsigned int";
654 string
CCodeGen::ARR_OFF( string ptr
, string offset
)
656 return ptr
+ " + " + offset
;
659 string
CCodeGen::CAST( string type
)
661 return "(" + type
+ ")";
664 string
CCodeGen::NULL_ITEM()
669 string
CCodeGen::POINTER()
674 std::ostream
&CCodeGen::SWITCH_DEFAULT()
679 string
CCodeGen::CTRL_FLOW()
684 void CCodeGen::writeExports()
686 if ( exportList
.length() > 0 ) {
687 for ( ExportList::Iter ex
= exportList
; ex
.lte(); ex
++ ) {
688 out
<< "#define " << DATA_PREFIX() << "ex_" << ex
->name
<< " " <<
689 KEY(ex
->key
) << "\n";
699 string
DCodeGen::NULL_ITEM()
704 string
DCodeGen::POINTER()
706 // multiple items seperated by commas can also be pointer types.
710 string
DCodeGen::PTR_CONST()
715 std::ostream
&DCodeGen::OPEN_ARRAY( string type
, string name
)
717 out
<< "static const " << type
<< "[] " << name
<< " = [\n";
721 std::ostream
&DCodeGen::CLOSE_ARRAY()
723 return out
<< "];\n";
726 std::ostream
&DCodeGen::STATIC_VAR( string type
, string name
)
728 out
<< "static const " << type
<< " " << name
;
732 string
DCodeGen::ARR_OFF( string ptr
, string offset
)
734 return "&" + ptr
+ "[" + offset
+ "]";
737 string
DCodeGen::CAST( string type
)
739 return "cast(" + type
+ ")";
742 string
DCodeGen::UINT( )
747 std::ostream
&DCodeGen::SWITCH_DEFAULT()
749 out
<< " default: break;\n";
753 string
DCodeGen::CTRL_FLOW()
758 void DCodeGen::writeExports()
760 if ( exportList
.length() > 0 ) {
761 for ( ExportList::Iter ex
= exportList
; ex
.lte(); ex
++ ) {
762 out
<< "static const " << ALPH_TYPE() << " " << DATA_PREFIX() <<
763 "ex_" << ex
->name
<< " = " << KEY(ex
->key
) << ";\n";
770 * End D-specific code.
773 void FsmCodeGen::finishRagelDef()
775 if ( codeStyle
== GenGoto
|| codeStyle
== GenFGoto
||
776 codeStyle
== GenIpGoto
|| codeStyle
== GenSplit
)
778 /* For directly executable machines there is no required state
779 * ordering. Choose a depth-first ordering to increase the
780 * potential for fall-throughs. */
781 redFsm
->depthFirstOrdering();
784 /* The frontend will do this for us, but it may be a good idea to
785 * force it if the intermediate file is edited. */
786 redFsm
->sortByStateId();
789 /* Choose default transitions and the single transition. */
790 redFsm
->chooseDefaultSpan();
792 /* Maybe do flat expand, otherwise choose single. */
793 if ( codeStyle
== GenFlat
|| codeStyle
== GenFFlat
)
796 redFsm
->chooseSingle();
798 /* If any errors have occured in the input file then don't write anything. */
799 if ( gblErrorCount
> 0 )
802 if ( codeStyle
== GenSplit
)
803 redFsm
->partitionFsm( numSplitPartitions
);
805 if ( codeStyle
== GenIpGoto
|| codeStyle
== GenSplit
)
806 redFsm
->setInTrans();
808 /* Anlayze Machine will find the final action reference counts, among
809 * other things. We will use these in reporting the usage
810 * of fsm directives in action code. */
813 /* Determine if we should use indicies. */
817 ostream
&FsmCodeGen::source_warning( const GenInputLoc
&loc
)
819 cerr
<< sourceFileName
<< ":" << loc
.line
<< ":" << loc
.col
<< ": warning: ";
823 ostream
&FsmCodeGen::source_error( const GenInputLoc
&loc
)
826 assert( sourceFileName
!= 0 );
827 cerr
<< sourceFileName
<< ":" << loc
.line
<< ":" << loc
.col
<< ": ";