class Squaak::Grammar::Actions;
method TOP($/, $key) { our @?BLOCK; our $?BLOCK;
if $key eq 'open' { ## create the top-level block here; any top-level variable ## declarations are entered into this block's symbol table. ## Note that TOP *must* deliver a PAST::Block with blocktype ## "declaration". $?BLOCK := PAST::Block.new( :blocktype('declaration'), :node($/) ); $?BLOCK.symbol_defaults( :scope('package') ); @?BLOCK.unshift($?BLOCK); } else { ## retrieve the block created in the "if" section in this method. my $past := @?BLOCK.shift(); for $<stat_or_def> { $past.push($($_)); } make $past; } }
method stat_or_def($/, $key) { make $( $/{$key} ); }
method statement($/, $key) { make $( $/{$key} ); }
method if_statement($/) { my $cond := $( $<expression> ); my $then := $( $<block> ); my $past := PAST::Op.new( $cond, $then, :pasttype('if'), :node($/) );
## if there's an else clause, add it to the PAST node. if $<else> { $past.push( $( $<else>[0] ) ); } make $past; }
method while_statement($/) { my $cond := $( $<expression> ); my $body := $( $<block> ); make PAST::Op.new( $cond, $body, :pasttype('while'), :node($/) ); }
## for var <ident> = <expr1> , <expr2> do <block> end ## ## translates to: ## do ## var <ident> = <expr1> ## while <ident> <= <expr2> do ## <block> ## <ident> = <ident> + 1 ## end ## end ## method for_statement($/) { our $?BLOCK; our @?BLOCK;
my $init := $( $<for_init> ); ## cache the name of the loop variable my $itername := $init.name(); ## create another PAST::Var node for the loop variable, this one is used ## for the loop condition; the node in $init has a isdecl(1) flag and a ## viviself object; $init represents the declaration of the loop var, ## $iter represents the loop variable in normal usage. my $iter := PAST::Var.new( :name($itername), :scope('lexical'), :node($/) ); ## the body of the loop consists of the statements written by the user and ## the increment instruction of the loop iterator. my $body := @?BLOCK.shift(); $?BLOCK := @?BLOCK[0]; for $<statement> { $body.push($($_)); } ## if a step was specified, use that; otherwise, use the default of +1. ## Note that a negative step will NOT work (unless YOU fix that :-) ). ## my $step; if $<step> { my $stepsize := $( $<step>[0] ); $step := PAST::Op.new( $iter, $stepsize, :pirop('add'), :node($/) ); } else { ## default is increment by 1 $step := PAST::Op.new( $iter, :pirop('inc'), :node($/) ); } $body.push($step); ## while loop iterator <= end-expression my $cond := PAST::Op.new( $iter, $( $<expression> ), :name('infix:<=') ); my $loop := PAST::Op.new( $cond, $body, :pasttype('while'), :node($/) ); make PAST::Stmts.new( $init, $loop, :node($/) ); }
method for_init($/) { our $?BLOCK; our @?BLOCK;
## create a new scope here, so that we can add the loop variable ## to this block here, which is convenient. $?BLOCK := PAST::Block.new( :blocktype('immediate'), :node($/) ); @?BLOCK.unshift($?BLOCK); my $iter := $( $<identifier> ); ## set a flag that this identifier is being declared $iter.isdecl(1); $iter.scope('lexical'); ## the identifier is initialized with this expression $iter.viviself( $( $<expression> ) ); ## enter the loop variable as a local into the symbol table. $?BLOCK.symbol($iter.name(), :scope('lexical')); make $iter; }
method try_statement($/) { ## get the try block my $try := $( $<try> );
## create a new PAST::Stmts node for the catch block; ## note that no PAST::Block is created, as this currently ## has problems with the exception object. For now this will do. my $catch := PAST::Stmts.new( :node($/) ); $catch.push( $( $<catch> ) ); ## get the exception identifier; my $exc := $( $<exception> ); $exc.isdecl(1); $exc.scope('lexical'); $exc.viviself( PAST::Val.new( :value(0) ) ); ## generate instruction to retrieve the exception objct (and the exception message, ## that is passed automatically in PIR, this is stored into $S0 (but not used). my $pir := " .get_results (%r)\n" ~ " store_lex '" ~ $exc.name() ~ "', %r"; $catch.unshift( PAST::Op.new( :inline($pir), :node($/) ) ); ## do the declaration of the exception object as a lexical here: $catch.unshift( $exc ); make PAST::Op.new( $try, $catch, :pasttype('try'), :node($/) ); }
method exception($/) { our $?BLOCK;
my $exc := $( $<identifier> ); ## the exception identifier is local to the exception handler $?BLOCK.symbol($exc.name(), :scope('lexical')); make $exc; }
method throw_statement($/) { make PAST::Op.new( $( $<expression> ), :pirop('throw'), :node($/) ); }
method block($/, $key) { our $?BLOCK; ## the current block our @?BLOCK; ## the scope stack
if $key eq 'open' { $?BLOCK := PAST::Block.new( :blocktype('immediate'), :node($/) ); @?BLOCK.unshift($?BLOCK); } else { ## retrieve the current block, remove it from the scope stack ## and restore the "current" block. my $past := @?BLOCK.shift(); $?BLOCK := @?BLOCK[0]; for $<statement> { $past.push($($_)); } make $past } }
method return_statement($/) { my $expr := $( $<expression> ); make PAST::Op.new( $expr, :pasttype('return'), :node($/) ); }
method do_block($/) { make $( $<block> ); }
method assignment($/) { my $rhs := $( $<expression> ); my $lhs := $( $<primary> ); $lhs.lvalue(1); make PAST::Op.new( $lhs, $rhs, :pasttype('bind'), :node($/) ); }
method sub_definition($/) { our @?BLOCK; our $?BLOCK;
## note that $<parameters> creates a new PAST::Block. my $past := $( $<parameters> ); my $name := $( $<identifier> ); ## set the function name $past.name( $name.name() ); for $<statement> { $past.push($($_)); } ## remove the block from the scope stack ## and restore the "current" block @?BLOCK.shift(); $?BLOCK := @?BLOCK[0]; $past.control('return_pir'); make $past; }
method variable_declaration($/) { our $?BLOCK;
my $past := $( $<identifier> ); $past.isdecl(1); $past.scope('lexical'); ## if there's an initialization value, use it to viviself the variable. if $<expression> { $past.viviself( $( $<expression>[0] ) ); } else { ## otherwise initialize to undef. $past.viviself( 'Undef' ); } ## cache this identifier's name my $name := $past.name(); ## if the symbol is already declared, emit an error. Otherwise, ## enter it into the current block's symbol table. if $?BLOCK.symbol($name) { $/.panic("Error: symbol " ~ $name ~ " was already defined\n"); } else { $?BLOCK.symbol($name, :scope('lexical')); } make $past; }
method parameters($/) { our $?BLOCK; our @?BLOCK;
my $past := PAST::Block.new( :blocktype('declaration'), :node($/) ); for $<identifier> { my $param := $( $_ ); $param.scope('parameter'); $past.push($param); ## enter the parameter as a lexical into the block's symbol table $past.symbol($param.name(), :scope('lexical')); } ## set this block as the current block, and store it on the scope stack $?BLOCK := $past; @?BLOCK.unshift($past); make $past; }
method sub_call($/) { my $invocant := $( $<primary> ); my $past := $( $<arguments> ); ## set the invocant as the first child of the PAST::Op(:pasttype('call')) node $past.unshift( $invocant ); make $past; }
method arguments($/) { my $past := PAST::Op.new( :pasttype('call'), :node($/) ); for $<expression> { $past.push($($_)); } make $past; }
method primary($/) { my $past := $( $<identifier> ); for $<postfix_expression> { my $expr := $( $_ ); ## set the current $past as the first child of $expr; ## $expr is either a key or an index; both are "keyed" ## variable access, where the first child is assumed ## to be the aggregate. $expr.unshift($past); $past := $expr; } make $past; }
method postfix_expression($/, $key) { make $( $/{$key} ); }
method key($/) { my $key := $( $<expression> );
make PAST::Var.new( $key, :scope('keyed'), :vivibase('Hash'), :viviself('Undef'), :node($/) );
}
method member($/) { my $member := $( $<identifier> ); ## x.y is syntactic sugar for x{"y"}, so stringify the identifier: my $key := PAST::Val.new( :returns('String'), :value($member.name()), :node($/) );
## the rest of this method is the same as method key() above. make PAST::Var.new( $key, :scope('keyed'), :vivibase('Hash'), :viviself('Undef'), :node($/) ); }
method index($/) { my $index := $( $<expression> );
make PAST::Var.new( $index, :scope('keyed'), :vivibase('ResizablePMCArray'), :viviself('Undef'), :node($/) ); }
method named_field($/) { my $past := $( $<expression> ); my $name := $( $<string_constant> ); ## the passed expression is in fact a named argument, ## use the named() accessor to set that name. $past.named($name); make $past; }
method array_constructor($/) { ## use the parrot calling conventions to ## create an array, ## using the "anonymous" sub !array ## (which is not a valid Squaak name) my $past := PAST::Op.new( :name('!array'), :pasttype('call'), :node($/) ); for $<expression> { $past.push($($_)); } make $past; }
method hash_constructor($/) { ## use the parrot calling conventions to ## create a hash, using the "anonymous" sub ## !hash (which is not a valid Squaak name) my $past := PAST::Op.new( :name('!hash'), :pasttype('call'), :node($/) ); for $<named_field> { $past.push($($_)); } make $past; }
method term($/, $key) { make $( $/{$key} ); }
method identifier($/) { my $name := ~$/; ## instead of ~$/, you can also write ~$<ident>, as an identifier ## uses the built-in <ident> rule to match identifiers. make PAST::Var.new( :name($name), :viviself('Undef'), :node($/) ); }
method integer_constant($/) { make PAST::Val.new( :value( ~$/ ), :returns('Integer'), :node($/) ); }
method float_constant($/) { make PAST::Val.new( :value( ~$/ ), :returns('Float'), :node($/) ); }
method string_constant($/) { make PAST::Val.new( :value( $($<string_literal>) ), :returns('String'), :node($/) ); }
## Handle the operator precedence table. method expression($/, $key) { if ($key eq 'end') { make $($<expr>); } else { my $past := PAST::Op.new( :name($<type>), :pasttype($<top><pasttype>), :pirop($<top><pirop>), :lvalue($<top><lvalue>), :node($/) ); for @($/) { $past.push( $($_) ); } make $past; } }
# Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 100 # End: # vim: expandtab shiftwidth=4: