Commits

Anonymous committed 5fa9ed5

Closure support for inner defs when returned by function.
When returning from a defined function, any subroutines returned need a
'newclosure' generated. This is so that inner defs will generate closures
properly. This may not be the best way to solve this problem, and it also
does not handle assignment statements.

Comments (0)

Files changed (5)

+2009-08-02  Bradley M. Kuhn  <bkuhn@ebb.org>
+
+	* src/builtins/helper.pir: Created file.
+	(is_type_sub): Wrote sub.
+
+	* setup.py (BUILTINS_PIR): Added helper.pir to list.
+
+	* Grammar.pg (return_expression_list): Added rule.
+
+	* Actions.nqp (return_expression_list): Wrote method.
+	(do_newclosure_if_sub): Wrote sub.
+	(return_stmt): use return_expression_list instead of
+	expression_list.
+
 2009-08-01  Bradley M. Kuhn  <bkuhn@ebb.org>
 
 	* Actions.nqp (lambda_form): generate newclosure 'pirop' for

Grammar/Actions.nqp

 
 method return_stmt($/) {
     my $past := PAST::Op.new( :pasttype('return'), :node($/) );
-    if $<expression_list> {
-        my $retvals := $( $<expression_list>[0] );
+    if $<return_expression_list> {
+        my $retvals := $( $<return_expression_list>[0] );
         $past.push($retvals);
     }
     make $past;
     make $past;
 }
 
+sub do_newclosure_if_sub($expression) {
+  my $block := PAST::Block.new(:blocktype("immediate"));
+  # Save the expression in 'exprval'.
+  $block.push(PAST::Op.new(:pasttype('bind'),
+                           PAST::Var.new(:name('exprval'),
+                                         :scope('register'), :isdecl(1)),
+                           $expression.ast));
+  # We now have the if statement
+  $block.push(PAST::Op.new(
+                           # this inline Op is the conditional statement
+                           # of the if.  It tests to see if the type of
+                           # the 'exprval' variable is "Sub", using the
+                           # is_type_sub
+                           PAST::Op.new(:pasttype('call'), :name("is_type_sub"),
+                                        PAST::Var.new( :name('exprval'),
+                                                       :scope('register'))),
+                           # If it turns out to be a subroutine, we want
+                           # to force a newclosure on it.
+                           PAST::Op.new(:inline('    newclosure %r, %0'),
+                                        PAST::Var.new( :name('exprval'),
+                                                       :scope('register'))),
+                           # Otherwise, return the value of expression
+                           PAST::Op.new(:inline('   %r = %0'),
+                                        PAST::Var.new( :name('exprval'),
+                                          :scope('register'))),
+                           :pasttype('if')));
+  return $block;
+}
+
+# return_expression_list makes sure that when return any functions, they
+# contain a closure (using newclosuere, see do_newclosure_if_sub above)
+# for any functions returned.  We do this on the return since we are about
+# to leave scope.
+
+method return_expression_list($/) {
+    my $past;
+    if (+$<expression> == 1) {
+      $past := do_newclosure_if_sub($<expression>[0]);
+    }
+    else {
+        $past := PAST::Op.new( :name('listmaker'), :node($/) );
+        for $<expression> {
+            $past.push( do_newclosure_if_sub($($_)) );
+        }
+    }
+    make $past;
+}
+
 
 method identifier($/) {
     make PAST::Var.new( :name( ~$/ ),

Grammar/Grammar.pg

 }
 
 rule return_stmt {
-    'return' <expression_list>?
+    'return' <return_expression_list>?
     {*}
 }
 
      <expression> [',' <expression> ]* ','? {*}
 }
 
+rule return_expression_list {
+     <expression> [',' <expression> ]* ','? {*}
+}
+
 rule tuple_or_scalar {
     | <tuple_constructor> {*} #= tuple_constructor
     | <expression> {*}        #= expression
 
 BUILTINS_PIR = [
   'src/builtins/funcs.pir', 
+  'src/builtins/helper.pir', 
   'src/builtins/io.pir', 
   'src/builtins/lists.pir', 
   'src/builtins/oper.pir',

src/builtins/helper.pir

+# Copyright (C) 2009, Bradley M. Kuhn
+# License: Artistic-2.0-or-later
+
+=head1 NAME
+
+src/builtins/helper.pir - helper functions
+
+=head1 Functions
+
+=over 4
+
+=cut
+
+=item is_type_sub(val)
+
+Returns true iff. the typeof the argument, val, is "Sub".
+
+=cut
+
+.sub 'is_type_sub'
+    .param pmc val
+    .local pmc valtype
+    typeof valtype, val
+    eq valtype, "Sub", return_true
+    .return(0)
+return_true:
+    .return(1)
+.end