2 * Copyright 2001-2006 Adrian Thurston <thurston@complang.org>
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
24 #include "cdcodegen.h"
34 using std::ostringstream
;
48 extern int numSplitPartitions
;
49 extern bool noLineDirectives
;
51 void cdLineDirective( ostream
&out
, const char *fileName
, int line
)
53 if ( noLineDirectives
)
56 /* Write the preprocessor line info for to the input file. */
57 out
<< "#line " << line
<< " \"";
58 for ( const char *pc
= fileName
; *pc
!= 0; pc
++ ) {
66 if ( noLineDirectives
)
72 void FsmCodeGen::genLineDirective( ostream
&out
)
74 std::streambuf
*sbuf
= out
.rdbuf();
75 output_filter
*filter
= static_cast<output_filter
*>(sbuf
);
76 cdLineDirective( out
, filter
->fileName
, filter
->line
+ 1 );
80 /* Init code gen with in parameters. */
81 FsmCodeGen::FsmCodeGen( ostream
&out
)
87 unsigned int FsmCodeGen::arrayTypeSize( unsigned long maxVal
)
89 long long maxValLL
= (long long) maxVal
;
90 HostType
*arrayType
= keyOps
->typeSubsumes( maxValLL
);
91 assert( arrayType
!= 0 );
92 return arrayType
->size
;
95 string
FsmCodeGen::ARRAY_TYPE( unsigned long maxVal
)
97 long long maxValLL
= (long long) maxVal
;
98 HostType
*arrayType
= keyOps
->typeSubsumes( maxValLL
);
99 assert( arrayType
!= 0 );
101 string ret
= arrayType
->data1
;
102 if ( arrayType
->data2
!= 0 ) {
104 ret
+= arrayType
->data2
;
110 /* Write out the fsm name. */
111 string
FsmCodeGen::FSM_NAME()
116 /* Emit the offset of the start state as a decimal integer. */
117 string
FsmCodeGen::START_STATE_ID()
120 ret
<< redFsm
->startState
->id
;
124 /* Write out the array of actions. */
125 std::ostream
&FsmCodeGen::ACTIONS_ARRAY()
128 int totalActions
= 1;
129 for ( GenActionTableMap::Iter act
= redFsm
->actionMap
; act
.lte(); act
++ ) {
130 /* Write out the length, which will never be the last character. */
131 out
<< act
->key
.length() << ", ";
132 /* Put in a line break every 8 */
133 if ( totalActions
++ % 8 == 7 )
136 for ( GenActionTable::Iter item
= act
->key
; item
.lte(); item
++ ) {
137 out
<< item
->value
->actionId
;
138 if ( ! (act
.last() && item
.last()) )
141 /* Put in a line break every 8 */
142 if ( totalActions
++ % 8 == 7 )
151 string
FsmCodeGen::ACCESS()
154 if ( accessExpr
!= 0 )
155 INLINE_LIST( ret
, accessExpr
, 0, false, false );
160 string
FsmCodeGen::P()
167 INLINE_LIST( ret
, pExpr
, 0, false, false );
173 string
FsmCodeGen::PE()
180 INLINE_LIST( ret
, peExpr
, 0, false, false );
186 string
FsmCodeGen::vEOF()
193 INLINE_LIST( ret
, eofExpr
, 0, false, false );
199 string
FsmCodeGen::vCS()
203 ret
<< ACCESS() << "cs";
205 /* Emit the user supplied method of retrieving the key. */
207 INLINE_LIST( ret
, csExpr
, 0, false, false );
213 string
FsmCodeGen::TOP()
217 ret
<< ACCESS() + "top";
220 INLINE_LIST( ret
, topExpr
, 0, false, false );
226 string
FsmCodeGen::STACK()
229 if ( stackExpr
== 0 )
230 ret
<< ACCESS() + "stack";
233 INLINE_LIST( ret
, stackExpr
, 0, false, false );
239 string
FsmCodeGen::ACT()
243 ret
<< ACCESS() + "act";
246 INLINE_LIST( ret
, actExpr
, 0, false, false );
252 string
FsmCodeGen::TOKSTART()
255 if ( tokstartExpr
== 0 )
256 ret
<< ACCESS() + "ts";
259 INLINE_LIST( ret
, tokstartExpr
, 0, false, false );
265 string
FsmCodeGen::TOKEND()
268 if ( tokendExpr
== 0 )
269 ret
<< ACCESS() + "te";
272 INLINE_LIST( ret
, tokendExpr
, 0, false, false );
278 string
FsmCodeGen::GET_WIDE_KEY()
280 if ( redFsm
->anyConditions() )
286 string
FsmCodeGen::GET_WIDE_KEY( RedStateAp
*state
)
288 if ( state
->stateCondList
.length() > 0 )
294 string
FsmCodeGen::GET_KEY()
297 if ( getKeyExpr
!= 0 ) {
298 /* Emit the user supplied method of retrieving the key. */
300 INLINE_LIST( ret
, getKeyExpr
, 0, false, false );
304 /* Expression for retrieving the key, use simple dereference. */
305 ret
<< "(*" << P() << ")";
310 /* Write out level number of tabs. Makes the nested binary search nice
312 string
FsmCodeGen::TABS( int level
)
315 while ( level
-- > 0 )
320 /* Write out a key from the fsm code gen. Depends on wether or not the key is
322 string
FsmCodeGen::KEY( Key key
)
325 if ( keyOps
->isSigned
|| !hostLang
->explicitUnsigned
)
328 ret
<< (unsigned long) key
.getVal() << 'u';
332 bool FsmCodeGen::isAlphTypeSigned()
334 return keyOps
->isSigned
;
337 bool FsmCodeGen::isWideAlphTypeSigned()
340 if ( redFsm
->maxKey
<= keyOps
->maxKey
)
341 return isAlphTypeSigned();
343 long long maxKeyVal
= redFsm
->maxKey
.getLongLong();
344 HostType
*wideType
= keyOps
->typeSubsumes( keyOps
->isSigned
, maxKeyVal
);
345 return wideType
->isSigned
;
349 string
FsmCodeGen::WIDE_KEY( RedStateAp
*state
, Key key
)
351 if ( state
->stateCondList
.length() > 0 ) {
353 if ( isWideAlphTypeSigned() )
356 ret
<< (unsigned long) key
.getVal() << 'u';
366 void FsmCodeGen::EXEC( ostream
&ret
, GenInlineItem
*item
, int targState
, int inFinish
)
368 /* The parser gives fexec two children. The double brackets are for D
369 * code. If the inline list is a single word it will get interpreted as a
370 * C-style cast by the D compiler. */
371 ret
<< "{" << P() << " = ((";
372 INLINE_LIST( ret
, item
->children
, targState
, inFinish
, false );
376 void FsmCodeGen::LM_SWITCH( ostream
&ret
, GenInlineItem
*item
,
377 int targState
, int inFinish
, bool csForced
)
380 " switch( " << ACT() << " ) {\n";
382 bool haveDefault
= false;
383 for ( GenInlineList::Iter lma
= *item
->children
; lma
.lte(); lma
++ ) {
384 /* Write the case label, the action and the case break. */
385 if ( lma
->lmId
< 0 ) {
386 ret
<< " default:\n";
390 ret
<< " case " << lma
->lmId
<< ":\n";
392 /* Write the block and close it off. */
394 INLINE_LIST( ret
, lma
->children
, targState
, inFinish
, csForced
);
400 if ( (hostLang
->lang
== HostLang::D
|| hostLang
->lang
== HostLang::D2
) && !haveDefault
)
401 ret
<< " default: break;";
408 void FsmCodeGen::SET_ACT( ostream
&ret
, GenInlineItem
*item
)
410 ret
<< ACT() << " = " << item
->lmId
<< ";";
413 void FsmCodeGen::SET_TOKEND( ostream
&ret
, GenInlineItem
*item
)
415 /* The tokend action sets tokend. */
416 ret
<< TOKEND() << " = " << P();
417 if ( item
->offset
!= 0 )
418 out
<< "+" << item
->offset
;
422 void FsmCodeGen::GET_TOKEND( ostream
&ret
, GenInlineItem
*item
)
427 void FsmCodeGen::INIT_TOKSTART( ostream
&ret
, GenInlineItem
*item
)
429 ret
<< TOKSTART() << " = " << NULL_ITEM() << ";";
432 void FsmCodeGen::INIT_ACT( ostream
&ret
, GenInlineItem
*item
)
434 ret
<< ACT() << " = 0;";
437 void FsmCodeGen::SET_TOKSTART( ostream
&ret
, GenInlineItem
*item
)
439 ret
<< TOKSTART() << " = " << P() << ";";
442 void FsmCodeGen::SUB_ACTION( ostream
&ret
, GenInlineItem
*item
,
443 int targState
, bool inFinish
, bool csForced
)
445 if ( item
->children
->length() > 0 ) {
446 /* Write the block and close it off. */
448 INLINE_LIST( ret
, item
->children
, targState
, inFinish
, csForced
);
454 /* Write out an inline tree structure. Walks the list and possibly calls out
455 * to virtual functions than handle language specific items in the tree. */
456 void FsmCodeGen::INLINE_LIST( ostream
&ret
, GenInlineList
*inlineList
,
457 int targState
, bool inFinish
, bool csForced
)
459 for ( GenInlineList::Iter item
= *inlineList
; item
.lte(); item
++ ) {
460 switch ( item
->type
) {
461 case GenInlineItem::Text
:
464 case GenInlineItem::Goto
:
465 GOTO( ret
, item
->targState
->id
, inFinish
);
467 case GenInlineItem::Call
:
468 CALL( ret
, item
->targState
->id
, targState
, inFinish
);
470 case GenInlineItem::Next
:
471 NEXT( ret
, item
->targState
->id
, inFinish
);
473 case GenInlineItem::Ret
:
474 RET( ret
, inFinish
);
476 case GenInlineItem::PChar
:
479 case GenInlineItem::Char
:
482 case GenInlineItem::Hold
:
485 case GenInlineItem::Exec
:
486 EXEC( ret
, item
, targState
, inFinish
);
488 case GenInlineItem::Curs
:
489 CURS( ret
, inFinish
);
491 case GenInlineItem::Targs
:
492 TARGS( ret
, inFinish
, targState
);
494 case GenInlineItem::Entry
:
495 ret
<< item
->targState
->id
;
497 case GenInlineItem::GotoExpr
:
498 GOTO_EXPR( ret
, item
, inFinish
);
500 case GenInlineItem::CallExpr
:
501 CALL_EXPR( ret
, item
, targState
, inFinish
);
503 case GenInlineItem::NextExpr
:
504 NEXT_EXPR( ret
, item
, inFinish
);
506 case GenInlineItem::LmSwitch
:
507 LM_SWITCH( ret
, item
, targState
, inFinish
, csForced
);
509 case GenInlineItem::LmSetActId
:
510 SET_ACT( ret
, item
);
512 case GenInlineItem::LmSetTokEnd
:
513 SET_TOKEND( ret
, item
);
515 case GenInlineItem::LmGetTokEnd
:
516 GET_TOKEND( ret
, item
);
518 case GenInlineItem::LmInitTokStart
:
519 INIT_TOKSTART( ret
, item
);
521 case GenInlineItem::LmInitAct
:
522 INIT_ACT( ret
, item
);
524 case GenInlineItem::LmSetTokStart
:
525 SET_TOKSTART( ret
, item
);
527 case GenInlineItem::SubAction
:
528 SUB_ACTION( ret
, item
, targState
, inFinish
, csForced
);
530 case GenInlineItem::Break
:
531 BREAK( ret
, targState
, csForced
);
536 /* Write out paths in line directives. Escapes any special characters. */
537 string
FsmCodeGen::LDIR_PATH( char *path
)
540 for ( char *pc
= path
; *pc
!= 0; pc
++ ) {
549 void FsmCodeGen::ACTION( ostream
&ret
, GenAction
*action
, int targState
,
550 bool inFinish
, bool csForced
)
552 /* Write the preprocessor line info for going into the source file. */
553 cdLineDirective( ret
, action
->loc
.fileName
, action
->loc
.line
);
555 /* Write the block and close it off. */
557 INLINE_LIST( ret
, action
->inlineList
, targState
, inFinish
, csForced
);
561 void FsmCodeGen::CONDITION( ostream
&ret
, GenAction
*condition
)
564 cdLineDirective( ret
, condition
->loc
.fileName
, condition
->loc
.line
);
565 INLINE_LIST( ret
, condition
->inlineList
, 0, false, false );
568 string
FsmCodeGen::ERROR_STATE()
571 if ( redFsm
->errState
!= 0 )
572 ret
<< redFsm
->errState
->id
;
578 string
FsmCodeGen::FIRST_FINAL_STATE()
581 if ( redFsm
->firstFinState
!= 0 )
582 ret
<< redFsm
->firstFinState
->id
;
584 ret
<< redFsm
->nextStateId
;
588 void FsmCodeGen::writeInit()
593 out
<< "\t" << vCS() << " = " << START() << ";\n";
595 /* If there are any calls, then the stack top needs initialization. */
596 if ( redFsm
->anyActionCalls() || redFsm
->anyActionRets() )
597 out
<< "\t" << TOP() << " = 0;\n";
599 if ( hasLongestMatch
) {
601 " " << TOKSTART() << " = " << NULL_ITEM() << ";\n"
602 " " << TOKEND() << " = " << NULL_ITEM() << ";\n"
603 " " << ACT() << " = 0;\n";
608 string
FsmCodeGen::DATA_PREFIX()
611 return FSM_NAME() + "_";
615 /* Emit the alphabet data type. */
616 string
FsmCodeGen::ALPH_TYPE()
618 string ret
= keyOps
->alphType
->data1
;
619 if ( keyOps
->alphType
->data2
!= 0 ) {
621 ret
+= + keyOps
->alphType
->data2
;
626 /* Emit the alphabet data type. */
627 string
FsmCodeGen::WIDE_ALPH_TYPE()
630 if ( redFsm
->maxKey
<= keyOps
->maxKey
)
633 long long maxKeyVal
= redFsm
->maxKey
.getLongLong();
634 HostType
*wideType
= keyOps
->typeSubsumes( keyOps
->isSigned
, maxKeyVal
);
635 assert( wideType
!= 0 );
637 ret
= wideType
->data1
;
638 if ( wideType
->data2
!= 0 ) {
640 ret
+= wideType
->data2
;
646 void FsmCodeGen::STATE_IDS()
648 if ( redFsm
->startState
!= 0 )
649 STATIC_VAR( "int", START() ) << " = " << START_STATE_ID() << ";\n";
652 STATIC_VAR( "int" , FIRST_FINAL() ) << " = " << FIRST_FINAL_STATE() << ";\n";
655 STATIC_VAR( "int", ERROR() ) << " = " << ERROR_STATE() << ";\n";
659 if ( entryPointNames
.length() > 0 ) {
660 for ( EntryNameVect::Iter en
= entryPointNames
; en
.lte(); en
++ ) {
661 STATIC_VAR( "int", DATA_PREFIX() + "en_" + *en
) <<
662 " = " << entryPointIds
[en
.pos()] << ";\n";
668 void FsmCodeGen::writeStart()
670 out
<< START_STATE_ID();
673 void FsmCodeGen::writeFirstFinal()
675 out
<< FIRST_FINAL_STATE();
678 void FsmCodeGen::writeError()
680 out
<< ERROR_STATE();
684 * Language specific, but style independent code generators functions.
687 string
CCodeGen::PTR_CONST()
692 string
CCodeGen::PTR_CONST_END()
697 std::ostream
&CCodeGen::OPEN_ARRAY( string type
, string name
)
699 out
<< "static const " << type
<< " " << name
<< "[] = {\n";
703 std::ostream
&CCodeGen::CLOSE_ARRAY()
705 return out
<< "};\n";
708 std::ostream
&CCodeGen::STATIC_VAR( string type
, string name
)
710 out
<< "static const " << type
<< " " << name
;
714 string
CCodeGen::UINT( )
716 return "unsigned int";
719 string
CCodeGen::ARR_OFF( string ptr
, string offset
)
721 return ptr
+ " + " + offset
;
724 string
CCodeGen::CAST( string type
)
726 return "(" + type
+ ")";
729 string
CCodeGen::NULL_ITEM()
734 string
CCodeGen::POINTER()
739 std::ostream
&CCodeGen::SWITCH_DEFAULT()
744 string
CCodeGen::CTRL_FLOW()
749 void CCodeGen::writeExports()
751 if ( exportList
.length() > 0 ) {
752 for ( ExportList::Iter ex
= exportList
; ex
.lte(); ex
++ ) {
753 out
<< "#define " << DATA_PREFIX() << "ex_" << ex
->name
<< " " <<
754 KEY(ex
->key
) << "\n";
764 string
DCodeGen::NULL_ITEM()
769 string
DCodeGen::POINTER()
771 // multiple items seperated by commas can also be pointer types.
775 string
DCodeGen::PTR_CONST()
780 string
DCodeGen::PTR_CONST_END()
785 std::ostream
&DCodeGen::OPEN_ARRAY( string type
, string name
)
787 out
<< "static const " << type
<< "[] " << name
<< " = [\n";
791 std::ostream
&DCodeGen::CLOSE_ARRAY()
793 return out
<< "];\n";
796 std::ostream
&DCodeGen::STATIC_VAR( string type
, string name
)
798 out
<< "static const " << type
<< " " << name
;
802 string
DCodeGen::ARR_OFF( string ptr
, string offset
)
804 return "&" + ptr
+ "[" + offset
+ "]";
807 string
DCodeGen::CAST( string type
)
809 return "cast(" + type
+ ")";
812 string
DCodeGen::UINT( )
817 std::ostream
&DCodeGen::SWITCH_DEFAULT()
819 out
<< " default: break;\n";
823 string
DCodeGen::CTRL_FLOW()
828 void DCodeGen::writeExports()
830 if ( exportList
.length() > 0 ) {
831 for ( ExportList::Iter ex
= exportList
; ex
.lte(); ex
++ ) {
832 out
<< "static const " << ALPH_TYPE() << " " << DATA_PREFIX() <<
833 "ex_" << ex
->name
<< " = " << KEY(ex
->key
) << ";\n";
840 * End D-specific code.
847 string
D2CodeGen::NULL_ITEM()
852 string
D2CodeGen::POINTER()
854 // multiple items seperated by commas can also be pointer types.
858 string
D2CodeGen::PTR_CONST()
863 string
D2CodeGen::PTR_CONST_END()
868 std::ostream
&D2CodeGen::OPEN_ARRAY( string type
, string name
)
870 out
<< "enum " << type
<< "[] " << name
<< " = [\n";
874 std::ostream
&D2CodeGen::CLOSE_ARRAY()
876 return out
<< "];\n";
879 std::ostream
&D2CodeGen::STATIC_VAR( string type
, string name
)
881 out
<< "enum " << type
<< " " << name
;
885 string
D2CodeGen::ARR_OFF( string ptr
, string offset
)
887 return "&" + ptr
+ "[" + offset
+ "]";
890 string
D2CodeGen::CAST( string type
)
892 return "cast(" + type
+ ")";
895 string
D2CodeGen::UINT( )
900 std::ostream
&D2CodeGen::SWITCH_DEFAULT()
902 out
<< " default: break;\n";
906 string
D2CodeGen::CTRL_FLOW()
911 void D2CodeGen::writeExports()
913 if ( exportList
.length() > 0 ) {
914 for ( ExportList::Iter ex
= exportList
; ex
.lte(); ex
++ ) {
915 out
<< "enum " << ALPH_TYPE() << " " << DATA_PREFIX() <<
916 "ex_" << ex
->name
<< " = " << KEY(ex
->key
) << ";\n";
922 void D2CodeGen::SUB_ACTION( ostream
&ret
, GenInlineItem
*item
,
923 int targState
, bool inFinish
, bool csForced
)
925 if ( item
->children
->length() > 0 ) {
926 /* Write the block and close it off. */
928 INLINE_LIST( ret
, item
->children
, targState
, inFinish
, csForced
);
933 void D2CodeGen::ACTION( ostream
&ret
, GenAction
*action
, int targState
,
934 bool inFinish
, bool csForced
)
936 /* Write the preprocessor line info for going into the source file. */
937 cdLineDirective( ret
, action
->loc
.fileName
, action
->loc
.line
);
939 /* Write the block and close it off. */
941 INLINE_LIST( ret
, action
->inlineList
, targState
, inFinish
, csForced
);
946 * End D2-specific code.
949 void FsmCodeGen::finishRagelDef()
951 if ( codeStyle
== GenGoto
|| codeStyle
== GenFGoto
||
952 codeStyle
== GenIpGoto
|| codeStyle
== GenSplit
)
954 /* For directly executable machines there is no required state
955 * ordering. Choose a depth-first ordering to increase the
956 * potential for fall-throughs. */
957 redFsm
->depthFirstOrdering();
960 /* The frontend will do this for us, but it may be a good idea to
961 * force it if the intermediate file is edited. */
962 redFsm
->sortByStateId();
965 /* Choose default transitions and the single transition. */
966 redFsm
->chooseDefaultSpan();
968 /* Maybe do flat expand, otherwise choose single. */
969 if ( codeStyle
== GenFlat
|| codeStyle
== GenFFlat
)
972 redFsm
->chooseSingle();
974 /* If any errors have occured in the input file then don't write anything. */
975 if ( gblErrorCount
> 0 )
978 if ( codeStyle
== GenSplit
)
979 redFsm
->partitionFsm( numSplitPartitions
);
981 if ( codeStyle
== GenIpGoto
|| codeStyle
== GenSplit
)
982 redFsm
->setInTrans();
984 /* Anlayze Machine will find the final action reference counts, among
985 * other things. We will use these in reporting the usage
986 * of fsm directives in action code. */
989 /* Determine if we should use indicies. */
993 ostream
&FsmCodeGen::source_warning( const InputLoc
&loc
)
995 cerr
<< sourceFileName
<< ":" << loc
.line
<< ":" << loc
.col
<< ": warning: ";
999 ostream
&FsmCodeGen::source_error( const InputLoc
&loc
)
1002 assert( sourceFileName
!= 0 );
1003 cerr
<< sourceFileName
<< ":" << loc
.line
<< ":" << loc
.col
<< ": ";