" Keywords
" FIXME: Enable the range keyword post 5.17.
" syntax keyword rlKeywords machine action context include range contained
-syntax keyword rlKeywords machine action context include import export contained
+syntax keyword rlKeywords machine action context include import export prepush postpop contained
syntax keyword rlExprKeywords when inwhen outwhen err lerr eof from to contained
" Case Labels
alphTypeSet(false),
getKeyExpr(0),
accessExpr(0),
+ prePushExpr(0),
+ postPopExpr(0),
pExpr(0),
peExpr(0),
csExpr(0),
InlineList *getKeyExpr;
InlineList *accessExpr;
+ /* Stack management */
+ InlineList *prePushExpr;
+ InlineList *postPopExpr;
+
/* Overriding variables. */
InlineList *pExpr;
InlineList *peExpr;
token KW_Machine, KW_Include, KW_Import, KW_Write, KW_Action, KW_AlphType,
KW_Range, KW_GetKey, KW_Include, KW_Write, KW_Machine, KW_InWhen,
KW_When, KW_OutWhen, KW_Eof, KW_Err, KW_Lerr, KW_To, KW_From,
- KW_Export;
+ KW_Export, KW_PrePush, KW_PostPop;
# Specials in code blocks.
token KW_Break, KW_Exec, KW_Hold, KW_PChar, KW_Char, KW_Goto, KW_Call,
statement: access_spec commit;
statement: variable_spec commit;
statement: export_block commit;
+statement: pre_push_spec commit;
+statement: post_pop_spec commit;
+
+pre_push_spec:
+ KW_PrePush '{' inline_block '}'
+ final {
+ if ( pd->prePushExpr != 0 ) {
+ /* Recover by just ignoring the duplicate. */
+ error($2->loc) << "pre_push code already defined" << endl;
+ }
+
+ pd->prePushExpr = $3->inlineList;
+ };
+
+
+post_pop_spec:
+ KW_PostPop '{' inline_block '}'
+ final {
+ if ( pd->postPopExpr != 0 ) {
+ /* Recover by just ignoring the duplicate. */
+ error($2->loc) << "post_pop code already defined" << endl;
+ }
+
+ pd->postPopExpr = $3->inlineList;
+ };
+
export_open: KW_Export
final {
};
'action' => { token( KW_Action ); };
'alphtype' => { token( KW_AlphType ); };
+ 'prepush' => { token( KW_PrePush ); };
+ 'postpop' => { token( KW_PostPop ); };
# FIXME: Enable this post 5.17.
# 'range' => { token( KW_Range ); };
out << "</access>\n";
}
+ /* PrePush expression. */
+ if ( pd->prePushExpr != 0 ) {
+ out << " <prepush>";
+ writeInlineList( pd->prePushExpr );
+ out << "</prepush>\n";
+ }
+
+ /* PostPop expression. */
+ if ( pd->postPopExpr != 0 ) {
+ out << " <postpop>";
+ writeInlineList( pd->postPopExpr );
+ out << "</postpop>\n";
+ }
+
/*
* Variable expressions.
*/
errState(-1),
getKeyExpr(0),
accessExpr(0),
+ prePushExpr(0),
+ postPopExpr(0),
pExpr(0),
peExpr(0),
csExpr(0),
CondSpaceList condSpaceList;
InlineList *getKeyExpr;
InlineList *accessExpr;
+ InlineList *prePushExpr;
+ InlineList *postPopExpr;
/* Overriding variables. */
InlineList *pExpr;
token TAG_p_expr, TAG_pe_expr, TAG_cs_expr, TAG_top_expr,
TAG_stack_expr, TAG_act_expr, TAG_tokstart_expr, TAG_tokend_expr,
- TAG_data_expr;
+ TAG_data_expr, TAG_prepush, TAG_postpop;
}%%
%% write instance_data;
ragel_def_item: tag_alph_type;
ragel_def_item: tag_getkey_expr;
ragel_def_item: tag_access_expr;
+ragel_def_item: tag_prepush_expr;
+ragel_def_item: tag_postpop_expr;
ragel_def_item: tag_export_list;
ragel_def_item: tag_machine;
ragel_def_item: tag_p_expr;
cgd->accessExpr = $2->inlineList;
};
+tag_prepush_expr: TAG_prepush inline_list '/' TAG_prepush
+ final {
+ cgd->prePushExpr = $2->inlineList;
+ };
+
+tag_postpop_expr: TAG_postpop inline_list '/' TAG_postpop
+ final {
+ cgd->postPopExpr = $2->inlineList;
+ };
+
tag_p_expr: TAG_p_expr inline_list '/' TAG_p_expr
final { cgd->pExpr = $2->inlineList; };
tag_pe_expr: TAG_pe_expr inline_list '/' TAG_pe_expr
tokstart_expr, TAG_tokstart_expr
tokend_expr, TAG_tokend_expr
data_expr, TAG_data_expr
+prepush, TAG_prepush
+postpop, TAG_postpop
void FlatCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
callDest << "; " << CTRL_FLOW() << "goto _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void FlatCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
INLINE_LIST( ret, ilItem->children, targState, inFinish );
ret << "); " << CTRL_FLOW() << "goto _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void FlatCodeGen::RET( ostream &ret, bool inFinish )
{
- ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "]; " <<
- CTRL_FLOW() << "goto _again;}";
+ ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "];";
+
+ if ( postPopExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, postPopExpr, 0, false );
+ ret << "}";
+ }
+
+ ret << CTRL_FLOW() << "goto _again;}";
}
void FlatCodeGen::BREAK( ostream &ret, int targState )
void GotoCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
callDest << "; " << CTRL_FLOW() << "goto _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void GotoCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
INLINE_LIST( ret, ilItem->children, targState, inFinish );
ret << "); " << CTRL_FLOW() << "goto _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void GotoCodeGen::RET( ostream &ret, bool inFinish )
{
- ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "]; " <<
- CTRL_FLOW() << "goto _again;}";
+ ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "];";
+
+ if ( postPopExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, postPopExpr, 0, false );
+ ret << "}";
+ }
+
+ ret << CTRL_FLOW() << "goto _again;}";
}
void GotoCodeGen::BREAK( ostream &ret, int targState )
void IpGotoCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << targState <<
"; " << CTRL_FLOW() << "goto st" << callDest << ";}";
-}
-void IpGotoCodeGen::RET( ostream &ret, bool inFinish )
-{
- ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "]; " <<
- CTRL_FLOW() << "goto _again;}";
+ if ( prePushExpr != 0 )
+ ret << "}";
}
-void IpGotoCodeGen::GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+void IpGotoCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
{
- ret << "{" << CS() << " = (";
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << targState << "; " << CS() << " = (";
INLINE_LIST( ret, ilItem->children, 0, inFinish );
ret << "); " << CTRL_FLOW() << "goto _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
-void IpGotoCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
+void IpGotoCodeGen::RET( ostream &ret, bool inFinish )
{
- ret << "{" << STACK() << "[" << TOP() << "++] = " << targState << "; " << CS() << " = (";
+ ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "];";
+
+ if ( postPopExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, postPopExpr, 0, false );
+ ret << "}";
+ }
+
+ ret << CTRL_FLOW() << "goto _again;}";
+}
+
+void IpGotoCodeGen::GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << "{" << CS() << " = (";
INLINE_LIST( ret, ilItem->children, 0, inFinish );
ret << "); " << CTRL_FLOW() << "goto _again;}";
}
void TabCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
callDest << "; " << CTRL_FLOW() << "goto _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void TabCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
INLINE_LIST( ret, ilItem->children, targState, inFinish );
ret << "); " << CTRL_FLOW() << "goto _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void TabCodeGen::RET( ostream &ret, bool inFinish )
{
ret << "{" << CS() << " = " << STACK() << "[--" <<
- TOP() << "]; " << CTRL_FLOW() << "goto _again;}";
+ TOP() << "]; ";
+
+ if ( postPopExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, postPopExpr, 0, false );
+ ret << "}";
+ }
+
+ ret << CTRL_FLOW() << "goto _again;}";
}
void TabCodeGen::BREAK( ostream &ret, int targState )
void JavaTabCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
callDest << "; " << CTRL_FLOW() << "break _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void JavaTabCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
INLINE_LIST( ret, ilItem->children, targState, inFinish );
ret << "); " << CTRL_FLOW() << "break _again;}";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void JavaTabCodeGen::RET( ostream &ret, bool inFinish )
{
- ret << "{" << CS() << " = " << STACK() << "[--" << TOP()
- << "]; " << CTRL_FLOW() << "break _again;}";
+ ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "];";
+
+ if ( postPopExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, postPopExpr, 0, false );
+ ret << "}";
+ }
+
+ ret << CTRL_FLOW() << "break _again;}";
}
void JavaTabCodeGen::BREAK( ostream &ret, int targState )
void RbxGotoCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "begin\n"
<< STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
callDest << "; ";
rbxGoto(ret, "_again") <<
"\nend\n";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void RbxGotoCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, prePushExpr, 0, false );
+ }
+
ret << "begin\n" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
INLINE_LIST( ret, ilItem->children, targState, inFinish );
ret << "); ";
rbxGoto(ret, "_again") <<
"\nend\n";
+
+ if ( prePushExpr != 0 )
+ ret << "}";
}
void RbxGotoCodeGen::RET( ostream &ret, bool inFinish )
{
ret << "begin\n" << CS() << " = " << STACK() << "[--" << TOP() << "]; " ;
+
+ if ( postPopExpr != 0 ) {
+ ret << "{";
+ INLINE_LIST( ret, postPopExpr, 0, false );
+ ret << "}";
+ }
+
rbxGoto(ret, "_again") <<
"\nend\n";
}
void RubyFlatCodeGen::CALL( ostream &out, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ out << "begin\n";
+ INLINE_LIST( out, prePushExpr, 0, false );
+ }
+
out <<
" begin\n"
" " << STACK() << "[" << TOP() << "] = " << CS() << "\n"
" _break_again = true\n"
" break\n" // break _again
" end\n";
+
+ if ( prePushExpr != 0 )
+ out << "end\n";
+}
+
+void RubyFlatCodeGen::CALL_EXPR(ostream &out, InlineItem *ilItem, int targState, bool inFinish )
+{
+ if ( prePushExpr != 0 ) {
+ out << "begin\n";
+ INLINE_LIST( out, prePushExpr, 0, false );
+ }
+
+ out <<
+ " begin\n"
+ " " << STACK() << "[" << TOP() << "] = " << CS() << "\n"
+ " " << TOP() << " += 1\n"
+ " " << CS() << " = (";
+ INLINE_LIST( out, ilItem->children, targState, inFinish );
+ out << ")\n";
+
+ out <<
+ " _break_again = true\n"
+ " break\n" // break _again
+ " end\n";
+
+ if ( prePushExpr != 0 )
+ out << "end\n";
+}
+
+void RubyFlatCodeGen::RET( ostream &out, bool inFinish )
+{
+ out <<
+ " begin\n"
+ " " << TOP() << " -= 1\n"
+ " " << CS() << " = " << STACK() << "[" << TOP() << "]\n";
+
+ if ( postPopExpr != 0 ) {
+ out << "begin\n";
+ INLINE_LIST( out, postPopExpr, 0, false );
+ out << "end\n";
+ }
+
+ out <<
+ " _break_again = true\n"
+ " break\n" // break _again
+ " end\n";
}
void RubyFlatCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
}
-void RubyFlatCodeGen::CALL_EXPR(ostream &out, InlineItem *ilItem, int targState, bool inFinish )
-{
- out <<
- " begin\n"
- " " << STACK() << "[" << TOP() << "] = " << CS() << "\n"
- " " << TOP() << " += 1\n"
- " " << CS() << " = (";
- INLINE_LIST( out, ilItem->children, targState, inFinish );
- out << ")\n";
-
- out <<
- " _break_again = true\n"
- " break\n" // break _again
- " end\n";
-}
-
void RubyFlatCodeGen::CURS( ostream &ret, bool inFinish )
{
ret << "(_ps)";
ret << "(" << CS() << ")";
}
-void RubyFlatCodeGen::RET( ostream &out, bool inFinish )
-{
- out <<
- " begin\n"
- " " << TOP() << " -= 1\n"
- " " << CS() << " = " << STACK() << "[" << TOP() << "]\n"
- " _break_again = true\n"
- " break\n" // break _again
- " end\n";
-}
-
void RubyFlatCodeGen::BREAK( ostream &out, int targState )
{
out <<
void RubyTabCodeGen::CALL( ostream &out, int callDest, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ out << "begin\n";
+ INLINE_LIST( out, prePushExpr, 0, false );
+ }
+
out <<
" begin\n"
" " << STACK() << "[" << TOP() << "] = " << CS() << "\n"
" _break_again = true\n"
" break\n" // break _again
" end\n";
+
+ if ( prePushExpr != 0 )
+ out << "end\n";
}
void RubyTabCodeGen::CALL_EXPR(ostream &out, InlineItem *ilItem, int targState, bool inFinish )
{
+ if ( prePushExpr != 0 ) {
+ out << "begin\n";
+ INLINE_LIST( out, prePushExpr, 0, false );
+ }
+
out <<
" begin\n"
" " << STACK() << "[" << TOP() << "] = " << CS() << "\n"
" _break_again = true\n"
" break\n" // break _again
" end\n";
+
+ if ( prePushExpr != 0 )
+ out << "end\n";
}
void RubyTabCodeGen::RET( ostream &out, bool inFinish )
out <<
" begin\n"
" " << TOP() << " -= 1\n"
- " " << CS() << " = " << STACK() << "[" << TOP() << "]\n"
+ " " << CS() << " = " << STACK() << "[" << TOP() << "]\n";
+
+ if ( postPopExpr != 0 ) {
+ out << "begin\n";
+ INLINE_LIST( out, postPopExpr, 0, false );
+ out << "end\n";
+ }
+
+ out <<
" _break_again = true\n"
" break\n" // break _again
" end\n";
--- /dev/null
+/*
+ * @LANG: c
+ * Test growable stack.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+%%{
+ machine recdescent;
+
+ prepush {
+ if ( top == stack_size ) {
+ printf( "growing stack\n" );
+ stack_size = top * 2;
+ stack = (int*)realloc( stack, sizeof(int)*stack_size );
+ }
+ }
+
+ postpop {
+ if ( stack_size > (top * 4) ) {
+ stack_size = top * 2;
+ stack = (int*)realloc( stack, sizeof(int)*stack_size );
+ printf( "shrinking stack\n" );
+ }
+ }
+
+ action item_start { item = p; }
+
+ action item_finish
+ {
+ printf( "item: " );
+ fwrite( item, 1, p-item, stdout );
+ printf( "\n" );
+ }
+
+ action call_main
+ {
+ printf( "calling main\n" );
+ fcall main;
+ }
+
+ action return_main
+ {
+ if ( top == 0 ) {
+ printf( "STRAY CLOSE\n" );
+ fbreak;
+ }
+
+ printf( "returning from main\n" );
+ fhold;
+ fret;
+ }
+
+ id = [a-zA-Z_]+;
+ number = [0-9]+;
+ ws = [ \t\n]+;
+
+ main := (
+ ws |
+ ( number | id ) >item_start %item_finish |
+
+ '{' @call_main '}' |
+
+ '}' @return_main
+ )**;
+}%%
+
+%% write data;
+
+void test( char *buf )
+{
+ int cs;
+ int *stack;
+ int top, stack_size;
+ char *p, *pe, *item = 0;
+
+ int len = strlen( buf );
+
+ %% write init;
+
+ stack_size = 1;
+ stack = (int*)malloc( sizeof(int) * stack_size );
+
+ p = buf;
+ pe = buf + len;
+
+ %% write exec;
+ %% write eof;
+
+ if ( cs == recdescent_error ) {
+ /* Machine failed before finding a token. */
+ printf( "PARSE ERROR\n" );
+ }
+}
+
+int main()
+{
+ test( "88 foo { 99 {{{{}}}}{ } }");
+ test( "76 } sadf");
+ return 0;
+}
+
+#ifdef _____OUTPUT_____
+item: 88
+item: foo
+calling main
+item: 99
+calling main
+growing stack
+calling main
+growing stack
+calling main
+calling main
+growing stack
+returning from main
+returning from main
+returning from main
+returning from main
+shrinking stack
+calling main
+returning from main
+returning from main
+shrinking stack
+item: 76
+STRAY CLOSE
+#endif
--- /dev/null
+/*
+ * @LANG: java
+ */
+
+class recdescent2
+{
+ %%{
+ machine recdescent;
+
+ prepush {
+ if ( top == stack_size ) {
+ System.out.print( "growing stack\n" );
+ stack_size = top * 2;
+ // Don't actually bother to resize here, but we do print messages.
+ //stack = (int*)realloc( stack, sizeof(int)*stack_size );
+ }
+ }
+
+ postpop {
+ if ( stack_size > (top * 4) ) {
+ stack_size = top * 2;
+ // Don't actually bother to resize here, but we do print messages.
+ //stack = (int*)realloc( stack, sizeof(int)*stack_size );
+ System.out.print( "shrinking stack\n" );
+ }
+ }
+
+ action item_start { item = p; }
+
+ action item_finish
+ {
+ String item_data = new String ( data, item, p-item );
+ System.out.print( "item: " );
+ System.out.print( item_data );
+ System.out.print( "\n" );
+ }
+
+ action call_main
+ {
+ System.out.print( "calling main\n" );
+ fcall main;
+ }
+
+ action return_main
+ {
+ if ( top == 0 ) {
+ System.out.print( "STRAY CLOSE\n" );
+ fbreak;
+ }
+
+ System.out.print( "returning from main\n" );
+ fhold;
+ fret;
+ }
+
+ id = [a-zA-Z_]+;
+ number = [0-9]+;
+ ws = [ \t\n]+;
+
+ main := (
+ ws |
+ ( number | id ) >item_start %item_finish |
+
+ '{' @call_main '}' |
+
+ '}' @return_main
+ )**;
+ }%%
+
+ %% write data;
+
+ static void test( char data[] )
+ {
+ int cs, p = 0, pe = data.length, item = 0;
+ int stack[] = new int[1024];
+ int stack_size = 1;
+ int top;
+
+ %% write init;
+ %% write exec;
+ %% write eof;
+
+ if ( cs == recdescent_error )
+ System.out.println( "SCANNER ERROR" );
+ }
+
+ public static void main( String args[] )
+ {
+ test( "88 foo { 99 {{{{}}}}{ } }".toCharArray() );
+ test( "76 } sadf".toCharArray() );
+ }
+}
+
+/* _____OUTPUT_____
+item: 88
+item: foo
+calling main
+item: 99
+calling main
+growing stack
+calling main
+growing stack
+calling main
+calling main
+growing stack
+returning from main
+returning from main
+returning from main
+returning from main
+shrinking stack
+calling main
+returning from main
+returning from main
+shrinking stack
+item: 76
+STRAY CLOSE
+*/
--- /dev/null
+#
+# @LANG: ruby
+#
+
+%%{
+ machine recdescent3;
+
+ prepush {
+ if top == stack_size
+ print( "growing stack\n" );
+ stack_size = top * 2;
+ # Don't actually bother to resize here, but we do print messages.
+ # stack = (int*)realloc( stack, sizeof(int)*stack_size );
+ end
+ }
+
+ postpop {
+ if stack_size > (top * 4)
+ print( "shrinking stack\n" );
+ stack_size = top * 2;
+ # Don't actually bother to resize here, but we do print messages.
+ # stack = (int*)realloc( stack, sizeof(int)*stack_size );
+ end
+ }
+
+ action item_start { item = p; }
+
+ action item_finish
+ {
+ print( "item: " );
+ print( data[item..p-1] );
+ print( "\n" );
+ }
+
+ action call_main
+ {
+ print( "calling main\n" );
+ fcall main;
+ }
+
+ action return_main
+ {
+ if top == 0
+ print( "STRAY CLOSE\n" );
+ fbreak;
+ end
+
+ print( "returning from main\n" );
+ fhold;
+ fret;
+ }
+
+ id = [a-zA-Z_]+;
+ number = [0-9]+;
+ ws = [ \t\n]+;
+
+ main := (
+ ws |
+ ( number | id ) >item_start %item_finish |
+
+ '{' @call_main '}' |
+
+ '}' @return_main
+ )**;
+}%%
+
+%% write data;
+
+def run_machine( data )
+ item = 0;
+ p = 0;
+ pe = data.length;
+ cs = 0;
+ stack = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+ stack_size = 1;
+ top = 0;
+
+ %% write init;
+ %% write exec;
+ %% write eof;
+
+ if cs == recdescent3_error
+ puts "SCANNER_ERROR"
+ end
+end
+
+inp = [
+ "88 foo { 99 {{{{}}}}{ } }",
+ "76 } sadf"
+]
+
+inp.each { |str| run_machine(str) }
+
+=begin _____OUTPUT_____
+item: 88
+item: foo
+calling main
+item: 99
+calling main
+growing stack
+calling main
+growing stack
+calling main
+calling main
+growing stack
+returning from main
+returning from main
+returning from main
+returning from main
+shrinking stack
+calling main
+returning from main
+returning from main
+shrinking stack
+item: 76
+STRAY CLOSE
+=end _____OUTPUT_____