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 "gocodegen.h"
34 using std::ostringstream
;
51 void goLineDirective( ostream
&out
, const char *fileName
, int line
)
53 out
<< "//line " << fileName
<< ":" << line
<< endl
;
56 void GoCodeGen::genLineDirective( ostream
&out
)
58 std::streambuf
*sbuf
= out
.rdbuf();
59 output_filter
*filter
= static_cast<output_filter
*>(sbuf
);
60 goLineDirective( out
, filter
->fileName
, filter
->line
+ 1 );
63 unsigned int GoCodeGen::arrayTypeSize( unsigned long maxVal
)
65 long long maxValLL
= (long long) maxVal
;
66 HostType
*arrayType
= keyOps
->typeSubsumes( maxValLL
);
67 assert( arrayType
!= 0 );
68 return arrayType
->size
;
71 string
GoCodeGen::ARRAY_TYPE( unsigned long maxVal
)
73 long long maxValLL
= (long long) maxVal
;
74 HostType
*arrayType
= keyOps
->typeSubsumes( maxValLL
);
75 assert( arrayType
!= 0 );
77 string ret
= arrayType
->data1
;
78 if ( arrayType
->data2
!= 0 ) {
80 ret
+= arrayType
->data2
;
86 /* Write out the fsm name. */
87 string
GoCodeGen::FSM_NAME()
92 /* Emit the offset of the start state as a decimal integer. */
93 string
GoCodeGen::START_STATE_ID()
96 ret
<< redFsm
->startState
->id
;
100 /* Write out the array of actions. */
101 std::ostream
&GoCodeGen::ACTIONS_ARRAY()
104 int totalActions
= 1;
105 for ( GenActionTableMap::Iter act
= redFsm
->actionMap
; act
.lte(); act
++ ) {
106 /* Write out the length, which will never be the last character. */
107 out
<< act
->key
.length() << ", ";
108 if ( totalActions
++ % IALL
== 0 )
111 for ( GenActionTable::Iter item
= act
->key
; item
.lte(); item
++ ) {
112 out
<< item
->value
->actionId
<< ", ";
113 if ( ! (act
.last() && item
.last()) ) {
114 if ( totalActions
++ % IALL
== 0 )
124 string
GoCodeGen::ACCESS()
127 if ( accessExpr
!= 0 )
128 INLINE_LIST( ret
, accessExpr
, 0, false, false );
133 string
GoCodeGen::P()
140 INLINE_LIST( ret
, pExpr
, 0, false, false );
146 string
GoCodeGen::PE()
153 INLINE_LIST( ret
, peExpr
, 0, false, false );
159 string
GoCodeGen::vEOF()
166 INLINE_LIST( ret
, eofExpr
, 0, false, false );
172 string
GoCodeGen::vCS()
176 ret
<< ACCESS() << "cs";
178 /* Emit the user supplied method of retrieving the key. */
180 INLINE_LIST( ret
, csExpr
, 0, false, false );
186 string
GoCodeGen::TOP()
190 ret
<< ACCESS() + "top";
193 INLINE_LIST( ret
, topExpr
, 0, false, false );
199 string
GoCodeGen::STACK()
202 if ( stackExpr
== 0 )
203 ret
<< ACCESS() + "stack";
206 INLINE_LIST( ret
, stackExpr
, 0, false, false );
212 string
GoCodeGen::ACT()
216 ret
<< ACCESS() + "act";
219 INLINE_LIST( ret
, actExpr
, 0, false, false );
225 string
GoCodeGen::TOKSTART()
228 if ( tokstartExpr
== 0 )
229 ret
<< ACCESS() + "ts";
232 INLINE_LIST( ret
, tokstartExpr
, 0, false, false );
238 string
GoCodeGen::TOKEND()
241 if ( tokendExpr
== 0 )
242 ret
<< ACCESS() + "te";
245 INLINE_LIST( ret
, tokendExpr
, 0, false, false );
251 string
GoCodeGen::GET_WIDE_KEY()
253 if ( redFsm
->anyConditions() )
259 string
GoCodeGen::GET_WIDE_KEY( RedStateAp
*state
)
261 if ( state
->stateCondList
.length() > 0 )
267 string
GoCodeGen::GET_KEY()
270 if ( getKeyExpr
!= 0 ) {
271 /* Emit the user supplied method of retrieving the key. */
273 INLINE_LIST( ret
, getKeyExpr
, 0, false, false );
277 /* Expression for retrieving the key, use simple dereference. */
278 ret
<< DATA() << "[" << P() << "]";
283 /* Write out level number of tabs. Makes the nested binary search nice
285 string
GoCodeGen::TABS( int level
)
288 while ( level
-- > 0 )
293 /* Write out a key from the fsm code gen. Depends on wether or not the key is
295 string
GoCodeGen::KEY( Key key
)
298 if ( keyOps
->isSigned
|| !hostLang
->explicitUnsigned
)
301 ret
<< (unsigned long) key
.getVal() << 'u';
305 bool GoCodeGen::isAlphTypeSigned()
307 return keyOps
->isSigned
;
310 bool GoCodeGen::isWideAlphTypeSigned()
313 if ( redFsm
->maxKey
<= keyOps
->maxKey
)
314 return isAlphTypeSigned();
316 long long maxKeyVal
= redFsm
->maxKey
.getLongLong();
317 HostType
*wideType
= keyOps
->typeSubsumes( keyOps
->isSigned
, maxKeyVal
);
318 return wideType
->isSigned
;
322 string
GoCodeGen::WIDE_KEY( RedStateAp
*state
, Key key
)
324 if ( state
->stateCondList
.length() > 0 ) {
326 if ( isWideAlphTypeSigned() )
329 ret
<< (unsigned long) key
.getVal() << 'u';
339 void GoCodeGen::EXEC( ostream
&ret
, GenInlineItem
*item
, int targState
, int inFinish
)
341 /* The parser gives fexec two children. The double brackets are for D
342 * code. If the inline list is a single word it will get interpreted as a
343 * C-style cast by the D compiler. */
344 ret
<< P() << " = (";
345 INLINE_LIST( ret
, item
->children
, targState
, inFinish
, false );
346 ret
<< ") - 1" << endl
;
349 void GoCodeGen::LM_SWITCH( ostream
&ret
, GenInlineItem
*item
,
350 int targState
, int inFinish
, bool csForced
)
353 " switch " << ACT() << " {" << endl
;
355 for ( GenInlineList::Iter lma
= *item
->children
; lma
.lte(); lma
++ ) {
356 /* Write the case label, the action and the case break. */
357 if ( lma
->lmId
< 0 ) {
358 ret
<< " default:" << endl
;
361 ret
<< " case " << lma
->lmId
<< ":" << endl
;
363 /* Write the block and close it off. */
365 INLINE_LIST( ret
, lma
->children
, targState
, inFinish
, csForced
);
374 void GoCodeGen::SET_ACT( ostream
&ret
, GenInlineItem
*item
)
376 ret
<< ACT() << " = " << item
->lmId
<< ";";
379 void GoCodeGen::SET_TOKEND( ostream
&ret
, GenInlineItem
*item
)
381 /* The tokend action sets tokend. */
382 ret
<< TOKEND() << " = " << P();
383 if ( item
->offset
!= 0 )
384 out
<< "+" << item
->offset
;
388 void GoCodeGen::GET_TOKEND( ostream
&ret
, GenInlineItem
*item
)
393 void GoCodeGen::INIT_TOKSTART( ostream
&ret
, GenInlineItem
*item
)
395 ret
<< TOKSTART() << " = " << NULL_ITEM() << endl
;
398 void GoCodeGen::INIT_ACT( ostream
&ret
, GenInlineItem
*item
)
400 ret
<< ACT() << " = 0" << endl
;
403 void GoCodeGen::SET_TOKSTART( ostream
&ret
, GenInlineItem
*item
)
405 ret
<< TOKSTART() << " = " << P() << endl
;
408 void GoCodeGen::SUB_ACTION( ostream
&ret
, GenInlineItem
*item
,
409 int targState
, bool inFinish
, bool csForced
)
411 if ( item
->children
->length() > 0 ) {
412 /* Write the block and close it off. */
414 INLINE_LIST( ret
, item
->children
, targState
, inFinish
, csForced
);
420 /* Write out an inline tree structure. Walks the list and possibly calls out
421 * to virtual functions than handle language specific items in the tree. */
422 void GoCodeGen::INLINE_LIST( ostream
&ret
, GenInlineList
*inlineList
,
423 int targState
, bool inFinish
, bool csForced
)
425 for ( GenInlineList::Iter item
= *inlineList
; item
.lte(); item
++ ) {
426 switch ( item
->type
) {
427 case GenInlineItem::Text
:
430 case GenInlineItem::Goto
:
431 GOTO( ret
, item
->targState
->id
, inFinish
);
433 case GenInlineItem::Call
:
434 CALL( ret
, item
->targState
->id
, targState
, inFinish
);
436 case GenInlineItem::Next
:
437 NEXT( ret
, item
->targState
->id
, inFinish
);
439 case GenInlineItem::Ret
:
440 RET( ret
, inFinish
);
442 case GenInlineItem::PChar
:
445 case GenInlineItem::Char
:
448 case GenInlineItem::Hold
:
449 ret
<< P() << "--" << endl
;
451 case GenInlineItem::Exec
:
452 EXEC( ret
, item
, targState
, inFinish
);
454 case GenInlineItem::Curs
:
455 CURS( ret
, inFinish
);
457 case GenInlineItem::Targs
:
458 TARGS( ret
, inFinish
, targState
);
460 case GenInlineItem::Entry
:
461 ret
<< item
->targState
->id
;
463 case GenInlineItem::GotoExpr
:
464 GOTO_EXPR( ret
, item
, inFinish
);
466 case GenInlineItem::CallExpr
:
467 CALL_EXPR( ret
, item
, targState
, inFinish
);
469 case GenInlineItem::NextExpr
:
470 NEXT_EXPR( ret
, item
, inFinish
);
472 case GenInlineItem::LmSwitch
:
473 LM_SWITCH( ret
, item
, targState
, inFinish
, csForced
);
475 case GenInlineItem::LmSetActId
:
476 SET_ACT( ret
, item
);
478 case GenInlineItem::LmSetTokEnd
:
479 SET_TOKEND( ret
, item
);
481 case GenInlineItem::LmGetTokEnd
:
482 GET_TOKEND( ret
, item
);
484 case GenInlineItem::LmInitTokStart
:
485 INIT_TOKSTART( ret
, item
);
487 case GenInlineItem::LmInitAct
:
488 INIT_ACT( ret
, item
);
490 case GenInlineItem::LmSetTokStart
:
491 SET_TOKSTART( ret
, item
);
493 case GenInlineItem::SubAction
:
494 SUB_ACTION( ret
, item
, targState
, inFinish
, csForced
);
496 case GenInlineItem::Break
:
497 BREAK( ret
, targState
, csForced
);
502 /* Write out paths in line directives. Escapes any special characters. */
503 string
GoCodeGen::LDIR_PATH( char *path
)
506 for ( char *pc
= path
; *pc
!= 0; pc
++ ) {
515 void GoCodeGen::ACTION( ostream
&ret
, GenAction
*action
, int targState
,
516 bool inFinish
, bool csForced
)
518 /* Write the preprocessor line info for going into the source file. */
519 goLineDirective( ret
, action
->loc
.fileName
, action
->loc
.line
);
521 /* Write the block and close it off. */
522 INLINE_LIST( ret
, action
->inlineList
, targState
, inFinish
, csForced
);
526 void GoCodeGen::CONDITION( ostream
&ret
, GenAction
*condition
)
528 INLINE_LIST( ret
, condition
->inlineList
, 0, false, false );
531 string
GoCodeGen::ERROR_STATE()
534 if ( redFsm
->errState
!= 0 )
535 ret
<< redFsm
->errState
->id
;
541 string
GoCodeGen::FIRST_FINAL_STATE()
544 if ( redFsm
->firstFinState
!= 0 )
545 ret
<< redFsm
->firstFinState
->id
;
547 ret
<< redFsm
->nextStateId
;
551 void GoCodeGen::writeInit()
556 out
<< " " << vCS() << " = " << START() << endl
;
558 /* If there are any calls, then the stack top needs initialization. */
559 if ( redFsm
->anyActionCalls() || redFsm
->anyActionRets() )
560 out
<< " " << TOP() << " = 0" << endl
;
562 if ( hasLongestMatch
) {
564 " " << TOKSTART() << " = " << NULL_ITEM() << endl
<<
565 " " << TOKEND() << " = " << NULL_ITEM() << endl
<<
566 " " << ACT() << " = 0" << endl
;
571 string
GoCodeGen::DATA()
575 ret
<< ACCESS() + "data";
578 INLINE_LIST( ret
, dataExpr
, 0, false, false );
584 string
GoCodeGen::DATA_PREFIX()
587 return FSM_NAME() + "_";
591 /* Emit the alphabet data type. */
592 string
GoCodeGen::ALPH_TYPE()
594 string ret
= keyOps
->alphType
->data1
;
595 if ( keyOps
->alphType
->data2
!= 0 ) {
597 ret
+= + keyOps
->alphType
->data2
;
602 /* Emit the alphabet data type. */
603 string
GoCodeGen::WIDE_ALPH_TYPE()
606 if ( redFsm
->maxKey
<= keyOps
->maxKey
)
609 long long maxKeyVal
= redFsm
->maxKey
.getLongLong();
610 HostType
*wideType
= keyOps
->typeSubsumes( keyOps
->isSigned
, maxKeyVal
);
611 assert( wideType
!= 0 );
613 ret
= wideType
->data1
;
614 if ( wideType
->data2
!= 0 ) {
616 ret
+= wideType
->data2
;
622 void GoCodeGen::STATE_IDS()
624 if ( redFsm
->startState
!= 0 )
625 CONST( "int", START() ) << " = " << START_STATE_ID() << endl
;
628 CONST( "int" , FIRST_FINAL() ) << " = " << FIRST_FINAL_STATE() << endl
;
631 CONST( "int", ERROR() ) << " = " << ERROR_STATE() << endl
;
635 if ( entryPointNames
.length() > 0 ) {
636 for ( EntryNameVect::Iter en
= entryPointNames
; en
.lte(); en
++ ) {
637 CONST( "int", DATA_PREFIX() + "en_" + *en
) <<
638 " = " << entryPointIds
[en
.pos()] << endl
;
644 void GoCodeGen::writeStart()
646 out
<< START_STATE_ID();
649 void GoCodeGen::writeFirstFinal()
651 out
<< FIRST_FINAL_STATE();
654 void GoCodeGen::writeError()
656 out
<< ERROR_STATE();
659 void GoCodeGen::finishRagelDef()
661 if ( codeStyle
== GenGoto
|| codeStyle
== GenFGoto
||
662 codeStyle
== GenIpGoto
|| codeStyle
== GenSplit
)
664 /* For directly executable machines there is no required state
665 * ordering. Choose a depth-first ordering to increase the
666 * potential for fall-throughs. */
667 redFsm
->depthFirstOrdering();
670 /* The frontend will do this for us, but it may be a good idea to
671 * force it if the intermediate file is edited. */
672 redFsm
->sortByStateId();
675 /* Choose default transitions and the single transition. */
676 redFsm
->chooseDefaultSpan();
678 /* Maybe do flat expand, otherwise choose single. */
679 if ( codeStyle
== GenFlat
|| codeStyle
== GenFFlat
)
682 redFsm
->chooseSingle();
684 /* If any errors have occured in the input file then don't write anything. */
685 if ( gblErrorCount
> 0 )
688 if ( codeStyle
== GenSplit
)
689 redFsm
->partitionFsm( numSplitPartitions
);
691 if ( codeStyle
== GenIpGoto
|| codeStyle
== GenSplit
)
692 redFsm
->setInTrans();
694 /* Anlayze Machine will find the final action reference counts, among
695 * other things. We will use these in reporting the usage
696 * of fsm directives in action code. */
699 /* Determine if we should use indicies. */
703 ostream
&GoCodeGen::source_warning( const InputLoc
&loc
)
705 cerr
<< sourceFileName
<< ":" << loc
.line
<< ":" << loc
.col
<< ": warning: ";
709 ostream
&GoCodeGen::source_error( const InputLoc
&loc
)
712 assert( sourceFileName
!= 0 );
713 cerr
<< sourceFileName
<< ":" << loc
.line
<< ":" << loc
.col
<< ": ";
723 std::ostream
&GoCodeGen::OPEN_ARRAY( string type
, string name
)
725 out
<< "var " << name
<< " []" << type
<< " = []" << type
<< "{" << endl
;
729 std::ostream
&GoCodeGen::CLOSE_ARRAY()
731 return out
<< "}" << endl
;
734 std::ostream
&GoCodeGen::STATIC_VAR( string type
, string name
)
736 out
<< "var " << name
<< " " << type
;
740 std::ostream
&GoCodeGen::CONST( string type
, string name
)
742 out
<< "const " << name
<< " " << type
;
746 string
GoCodeGen::UINT( )
751 string
GoCodeGen::INT()
756 string
GoCodeGen::CAST( string type
, string expr
)
758 return type
+ "(" + expr
+ ")";
761 string
GoCodeGen::NULL_ITEM()
766 void GoCodeGen::writeExports()
768 if ( exportList
.length() > 0 ) {
769 for ( ExportList::Iter ex
= exportList
; ex
.lte(); ex
++ ) {
770 out
<< "const " << DATA_PREFIX() << "ex_" << ex
->name
<< " = " <<
771 KEY(ex
->key
) << endl
;