1 # Copyright (C) 2008, The Perl Foundation.
7 Squaak::Grammar::Actions - ast transformations for Squaak
9 This file contains the methods that are used by the parse grammar
10 to build the PAST representation of an Squaak program.
11 Each method below corresponds to a rule in F<src/parser/grammar.pg>,
12 and is invoked at the point where C<{*}> appears in the rule,
13 with the current match object as the first argument. If the
14 line containing C<{*}> also has a C<#= key> comment, then the
15 value of the comment is passed as the second argument to the method.
19 class Squaak::Grammar::Actions;
21 method TOP($/, $key) {
26 ## create the top-level block here; any top-level variable
27 ## declarations are entered into this block's symbol table.
28 ## Note that TOP *must* deliver a PAST::Block with blocktype
30 $?BLOCK := PAST::Block.new( :blocktype('declaration'), :node($/) );
31 $?BLOCK.symbol_defaults( :scope('package') );
32 @?BLOCK.unshift($?BLOCK);
35 ## retrieve the block created in the "if" section in this method.
36 my $past := @?BLOCK.shift();
45 method stat_or_def($/, $key) {
49 method statement($/, $key) {
53 method if_statement($/) {
54 my $cond := $( $<expression> );
55 my $then := $( $<block> );
56 my $past := PAST::Op.new( $cond, $then, :pasttype('if'), :node($/) );
58 ## if there's an else clause, add it to the PAST node.
60 $past.push( $( $<else>[0] ) );
65 method while_statement($/) {
66 my $cond := $( $<expression> );
67 my $body := $( $<block> );
68 make PAST::Op.new( $cond, $body, :pasttype('while'), :node($/) );
71 ## for var <ident> = <expr1> , <expr2> do <block> end
75 ## var <ident> = <expr1>
76 ## while <ident> <= <expr2> do
78 ## <ident> = <ident> + 1
82 method for_statement($/) {
86 my $init := $( $<for_init> );
88 ## cache the name of the loop variable
89 my $itername := $init.name();
91 ## create another PAST::Var node for the loop variable, this one is used
92 ## for the loop condition; the node in $init has a isdecl(1) flag and a
93 ## viviself object; $init represents the declaration of the loop var,
94 ## $iter represents the loop variable in normal usage.
95 my $iter := PAST::Var.new( :name($itername), :scope('lexical'), :node($/) );
97 ## the body of the loop consists of the statements written by the user and
98 ## the increment instruction of the loop iterator.
100 my $body := @?BLOCK.shift();
101 $?BLOCK := @?BLOCK[0];
106 ## if a step was specified, use that; otherwise, use the default of +1.
107 ## Note that a negative step will NOT work (unless YOU fix that :-) ).
111 my $stepsize := $( $<step>[0] );
112 $step := PAST::Op.new( $iter, $stepsize, :pirop('add'), :node($/) );
114 else { ## default is increment by 1
115 $step := PAST::Op.new( $iter, :pirop('inc'), :node($/) );
119 ## while loop iterator <= end-expression
120 my $cond := PAST::Op.new( $iter, $( $<expression> ), :name('infix:<=') );
121 my $loop := PAST::Op.new( $cond, $body, :pasttype('while'), :node($/) );
123 make PAST::Stmts.new( $init, $loop, :node($/) );
126 method for_init($/) {
130 ## create a new scope here, so that we can add the loop variable
131 ## to this block here, which is convenient.
132 $?BLOCK := PAST::Block.new( :blocktype('immediate'), :node($/) );
133 @?BLOCK.unshift($?BLOCK);
135 my $iter := $( $<identifier> );
136 ## set a flag that this identifier is being declared
138 $iter.scope('lexical');
139 ## the identifier is initialized with this expression
140 $iter.viviself( $( $<expression> ) );
142 ## enter the loop variable as a local into the symbol table.
143 $?BLOCK.symbol($iter.name(), :scope('lexical'));
148 method try_statement($/) {
150 my $try := $( $<try> );
152 ## create a new PAST::Stmts node for the catch block;
153 ## note that no PAST::Block is created, as this currently
154 ## has problems with the exception object. For now this will do.
155 my $catch := PAST::Stmts.new( :node($/) );
156 $catch.push( $( $<catch> ) );
158 ## get the exception identifier;
159 my $exc := $( $<exception> );
160 $exc.isdecl( PAST::Val.new( :value(1) ) );
161 $exc.scope('lexical');
162 $exc.viviself( PAST::Val.new( :value(0) ) );
164 ## generate instruction to retrieve the exception objct (and the exception message,
165 ## that is passed automatically in PIR, this is stored into $S0 (but not used).
166 my $pir := " .get_results (%r, $S0)\n"
167 ~ " store_lex '" ~ $exc.name() ~ "', %r";
169 $catch.unshift( PAST::Op.new( :inline($pir), :node($/) ) );
170 ## do the declaration of the exception object as a lexical here:
171 $catch.unshift( $exc );
173 make PAST::Op.new( $try, $catch, :pasttype('try'), :node($/) );
176 method exception($/) {
179 my $exc := $( $<identifier> );
180 ## the exception identifier is local to the exception handler
181 $?BLOCK.symbol($exc.name(), :scope('lexical'));
185 method throw_statement($/) {
186 make PAST::Op.new( $( $<expression> ), :pirop('throw'), :node($/) );
189 method block($/, $key) {
190 our $?BLOCK; ## the current block
191 our @?BLOCK; ## the scope stack
194 $?BLOCK := PAST::Block.new( :blocktype('immediate'), :node($/) );
195 @?BLOCK.unshift($?BLOCK);
198 ## retrieve the current block, remove it from the scope stack
199 ## and restore the "current" block.
200 my $past := @?BLOCK.shift();
201 $?BLOCK := @?BLOCK[0];
210 method return_statement($/) {
211 my $expr := $( $<expression> );
212 make PAST::Op.new( $expr, :pasttype('return'), :node($/) );
215 method do_block($/) {
219 method assignment($/) {
220 my $rhs := $( $<expression> );
221 my $lhs := $( $<primary> );
223 make PAST::Op.new( $lhs, $rhs, :pasttype('bind'), :node($/) );
226 method sub_definition($/) {
230 ## note that $<parameters> creates a new PAST::Block.
231 my $past := $( $<parameters> );
232 my $name := $( $<identifier> );
234 ## set the function name
235 $past.name( $name.name() );
240 ## remove the block from the scope stack
241 ## and restore the "current" block
243 $?BLOCK := @?BLOCK[0];
245 $past.control('return_pir');
249 method variable_declaration($/) {
252 my $past := $( $<identifier> );
254 $past.scope('lexical');
256 ## if there's an initialization value, use it to viviself the variable.
258 $past.viviself( $( $<expression>[0] ) );
260 else { ## otherwise initialize to undef.
261 $past.viviself( 'Undef' );
264 ## cache this identifier's name
265 my $name := $past.name();
267 ## if the symbol is already declared, emit an error. Otherwise,
268 ## enter it into the current block's symbol table.
269 if $?BLOCK.symbol($name) {
270 $/.panic("Error: symbol " ~ $name ~ " was already defined\n");
273 $?BLOCK.symbol($name, :scope('lexical'));
278 method parameters($/) {
282 my $past := PAST::Block.new( :blocktype('declaration'), :node($/) );
284 my $param := $( $_ );
285 $param.scope('parameter');
288 ## enter the parameter as a lexical into the block's symbol table
289 $past.symbol($param.name(), :scope('lexical'));
292 ## set this block as the current block, and store it on the scope stack
294 @?BLOCK.unshift($past);
299 method sub_call($/) {
300 my $invocant := $( $<primary> );
301 my $past := $( $<arguments> );
302 ## set the invocant as the first child of the PAST::Op(:pasttype('call')) node
303 $past.unshift( $invocant );
307 method arguments($/) {
308 my $past := PAST::Op.new( :pasttype('call'), :node($/) );
316 my $past := $( $<identifier> );
317 for $<postfix_expression> {
319 ## set the current $past as the first child of $expr;
320 ## $expr is either a key or an index; both are "keyed"
321 ## variable access, where the first child is assumed
322 ## to be the aggregate.
323 $expr.unshift($past);
329 method postfix_expression($/, $key) {
334 my $key := $( $<expression> );
336 make PAST::Var.new( $key, :scope('keyed'),
344 my $member := $( $<identifier> );
345 ## x.y is syntactic sugar for x{"y"}, so stringify the identifier:
346 my $key := PAST::Val.new( :returns('String'), :value($member.name()), :node($/) );
348 ## the rest of this method is the same as method key() above.
349 make PAST::Var.new( $key, :scope('keyed'),
356 my $index := $( $<expression> );
358 make PAST::Var.new( $index, :scope('keyed'),
359 :vivibase('ResizablePMCArray'),
364 method named_field($/) {
365 my $past := $( $<expression> );
366 my $name := $( $<string_constant> );
367 ## the passed expression is in fact a named argument,
368 ## use the named() accessor to set that name.
373 method array_constructor($/) {
374 ## use the parrot calling conventions to
376 ## using the "anonymous" sub !array
377 ## (which is not a valid Squaak name)
378 my $past := PAST::Op.new( :name('!array'), :pasttype('call'), :node($/) );
385 method hash_constructor($/) {
386 ## use the parrot calling conventions to
387 ## create a hash, using the "anonymous" sub
388 ## !hash (which is not a valid Squaak name)
389 my $past := PAST::Op.new( :name('!hash'), :pasttype('call'), :node($/) );
396 method term($/, $key) {
400 method identifier($/) {
402 ## instead of ~$/, you can also write ~$<ident>, as an identifier
403 ## uses the built-in <ident> rule to match identifiers.
404 make PAST::Var.new( :name($name), :viviself('Undef'), :node($/) );
407 method integer_constant($/) {
408 make PAST::Val.new( :value( ~$/ ), :returns('Integer'), :node($/) );
411 method float_constant($/) {
412 make PAST::Val.new( :value( ~$/ ), :returns('Float'), :node($/) );
415 method string_constant($/) {
416 make PAST::Val.new( :value( $($<string_literal>) ), :returns('String'), :node($/) );
419 ## Handle the operator precedence table.
420 method expression($/, $key) {
425 my $past := PAST::Op.new( :name($<type>),
426 :pasttype($<top><pasttype>),
427 :pirop($<top><pirop>),
428 :lvalue($<top><lvalue>),
442 # cperl-indent-level: 4
445 # vim: expandtab shiftwidth=4: