Not long ago, Stuart Jansen via RT proclaimed...
> I'm really glad to see you're still working on this, but I don't think
> this patch is ready to be merged yet.

You've raised several points.  Attached is a modified patch that
addresses most of them.

The largest problem with this patch is that it needs more explicit null
checking in a few places.

This version of this patch has operator support as well, and a pair of
tests, one testing the math ops directly, and one also testing some more
complicated expressions using the first twenty 'four fours' problems.
Doesn't test slurpy functions and MKAY, though.

> I don't think this is correct behavior. VISIBLE is a statement, but it
> is not an expression. I can't find a defined return value for VISIBLE,
> nor can I find any evidence that VISIBLE should set IT.
> 
> In additon, I can't find any evidence that '!' is a value. It is only a
> special syntax case for VISIBLE.

This patch parses VISIBLE as an expression because that looks like the
least awkward way of dispatching arguments to it properly.  This patch
explicitly checks for 'VISIBLE' in method statement, where the
bind-to-IT nodes are emitted to skip emitting a binding to IT on VISIBLE
statements.  I've moved the ! handing into VISIBLE.

As we discussed on IM, the alternative is explicitly emit a call to
SMOOSH and duplicate method expression inside of method VISIBLE.  I
don't like this option much, but at least it avoids the explicit bang
rules.

I can write a version using an explicit SMOOSH for comparison.

> > Function return values are a bit sketchy, but that's waiting on PAST
> > support for return statements anyway, iirc.
> 
> They can currently be accomplished using inline PIR.
> See /languages/ecmascript/src/parser/actions.pm for an example.

Yes, but that is outside the scope of this patch.  IT is properly
returned, as it has been since that was added earlier.

> > Functions can't return null values, as null is currently being used as
> > the 'end of statement' marker in expr_parse.
> 
> This will have to change. I interpret NOOB to be null. Function can
> return NOOB using GTFO, for example.

This has been fixed.  MKAY is currently passed along as a variable, as
it's a reserved word anyway, and checked with issame, so nulls can be
returned properly.

Isn't NOOB more like Undef than null, though?

> > Optional parameters to functions aren't dealt with properly.
> > 
> > Auto-assignment to IT is still a bit sketchy.  I'm unsure of the proper
> > semantics here, but the test suite passes, so it's good enough for now.
> 
> IT should only be set by a bare expression. Hence the special case in
> the grammar. The new handling for IT is not correct because bare
> expressions can consist of multiple tokens once we have proper support
> for operators.

Is a 'bare expression' any expression which is not a call to VISIBLE?
If so, this patch handles that appropriately.  That's the meaning that I
get out of my reading of the spec.

> -- 
> Stuart Jansen <[EMAIL PROTECTED]>
> Guru Labs
> 
Index: languages/lolcode/t/99-four-fours.t
===================================================================
--- languages/lolcode/t/99-four-fours.t (revision 0)
+++ languages/lolcode/t/99-four-fours.t (revision 0)
@@ -0,0 +1,49 @@
+HAI 1.2
+  VISIBLE "1..20"
+
+  BTW 1
+  VISIBLE "ok " SUM OF QUOSHUNT OF 4 AN 4 AN DIFF OF 4 AN 4
+
+  BTW 2
+  VISIBLE "ok " SUM OF QUOSHUNT OF 4 AN 4 AN QUOSHUNT OF 4 AN 4
+
+  BTW 3
+  VISIBLE "ok " QUOSHUNT OF SUM OF SUM OF 4 AN 4 AN 4 AN 4
+
+  BTW 4
+  VISIBLE "ok " SUM OF PRODUKT OF DIFF OF 4 AN 4 AN 4 AN 4
+
+  BTW 5
+  VISIBLE "ok " QUOSHUNT OF SUM OF PRODUKT OF 4 AN 4 AN 4 AN 4
+
+  BTW 6
+  VISIBLE "ok " SUM OF QUOSHUNT OF SUM OF 4 AN 4 AN 4 AN 4
+
+  BTW 7
+  VISIBLE "ok " DIFF OF SUM OF 4 AN 4 AN QUOSHUNT OF 4 AN 4
+
+  BTW 8
+  VISIBLE "ok " DIFF OF SUM OF 4 AN 4.4 AN 0.4
+
+  BTW 9
+  VISIBLE "ok " SUM OF SUM OF 4 AN 4 AN QUOSHUNT OF 4 AN 4
+
+  BTW 10
+  VISIBLE "ok " QUOSHUNT OF DIFF OF 44 AN 4 AN 4
+
+  BTW 11
+  VISIBLE "ok " SUM OF QUOSHUNT OF 4 AN 0.4 AN QUOSHUNT OF 4 AN 4
+
+  BTW 12
+  VISIBLE "ok " QUOSHUNT OF SUM OF 44 AN 4 AN 4
+
+  BTW 13
+  VISIBLE "ok " DIFF OF FAKTORIAL OF 4 AN QUOSHUNT OF 44 AN 4
+
+  BTW 14
+  VISIBLE "ok " DIFF OF PRODUKT OF 4 AN DIFF OF 4 AN 0.4 AN 0.4
+
+  BTW 15
+  VISIBLE "ok " DIFF OF PRODUKT OF 4 AN 4 AN QUOSHUNT OF 4 AN 4
+
+  BTW 16
+  VISIBLE "ok " QUOSHUNT OF PRODUKT OF PRODUKT OF 4 AN 4 AN 4 AN 4
+
+  BTW 17
+  VISIBLE "ok " SUM OF PRODUKT OF 4 AN 4 AN QUOSHUNT OF 4 AN 4
+
+  BTW 18
+  VISIBLE "ok " SUM OF PRODUKT OF 44 AN 0.4 AN 0.4
+
+  BTW 19
+  VISIBLE "ok " DIFF OF DIFF OF FAKTORIAL OF 4 AN 4 AN QUOSHUNT OF 4 AN 4
+
+  BTW 20
+  VISIBLE "ok " PRODUKT OF 4 AN SUM OF QUOSHUNT OF 4 AN 4 AN 4
+
+KTHXBYE
Index: languages/lolcode/t/05-math.t
===================================================================
--- languages/lolcode/t/05-math.t       (revision 0)
+++ languages/lolcode/t/05-math.t       (revision 0)
@@ -0,0 +1,38 @@
+HAI 1.2
+  VISIBLE "1..11"
+
+  BTW 1
+  VISIBLE "ok " SUM OF -2 AN 3
+
+  BTW 2
+  VISIBLE "ok " DIFF OF -1 AN -3
+
+  BTW 3
+  VISIBLE "ok " SUM OF 1 AN SUM OF 1 AN 1
+
+  BTW 4
+  VISIBLE "ok " PRODUKT OF 2 AN 2
+
+  BTW 5
+  VISIBLE "ok " QUOSHUNT OF 10 AN 2
+
+  BTW 6
+  VISIBLE "ok " MOD OF 27 AN 7
+
+  BTW 7
+  VISIBLE "ok " BIGGR OF 6 AN 7
+
+  BTW 8
+  VISIBLE "ok " SMALLR OF 8 AN 9
+
+  BTW 9
+  VISIBLE "ok " PRODUKT OF 4.5 AN 2
+
+  BTW 10
+  I HAS A CHEEZBURGER ITZ PRODUKT OF 5 AN 2
+  VISIBLE "ok " CHEEZBURGER
+
+  BTW 11
+  CHEEZBURGER R SUM OF CHEEZBURGER AN 1
+  VISIBLE "ok " CHEEZBURGER
+KTHXBYE
Index: languages/lolcode/config/makefiles/root.in
===================================================================
--- languages/lolcode/config/makefiles/root.in  (revision 25607)
+++ languages/lolcode/config/makefiles/root.in  (working copy)
@@ -41,6 +41,8 @@
 
 BUILTINS_PIR = \
   src/builtins/say.pir \
+  src/builtins/expr_parse.pir \
+  src/builtins/math.pir \
   src/builtins/var_or_function.pir
 
 # PMCS = lolcode
Index: languages/lolcode/src/builtins/expr_parse.pir
===================================================================
--- languages/lolcode/src/builtins/expr_parse.pir       (revision 0)
+++ languages/lolcode/src/builtins/expr_parse.pir       (revision 0)
@@ -0,0 +1,122 @@
+=head1
+
+expr_parse.pir - parse an expression and dispatch function calls with their 
appropriate arguments.
+
+=cut
+
+.namespace
+
+.sub 'setup_global_parsing_tokens' :init :load :anon
+    $P0 = new 'String'
+    $P0 = 'MKAY end of statement marker'
+    store_global 'MKAY', $P0
+.end
+
+.sub 'expr_parse'
+    .param pmc tokens :slurpy
+
+    .local pmc t_iter
+    t_iter = new 'Iterator', tokens
+
+    .local pmc sub_stack
+    .local pmc val_stack
+    .local pmc arity_stack
+
+    sub_stack = new 'ResizablePMCArray'
+    val_stack = new 'ResizablePMCArray'
+    arity_stack = new 'ResizableIntegerArray'
+
+    .local pmc mkay
+    mkay = find_name 'MKAY'
+  it_loop:
+    unless t_iter goto it_done
+      .local pmc item
+      .local pmc sub_to_call
+      .local pmc args_array
+      item = shift t_iter
+      $I0 = isntsame item, mkay
+    if $I0 goto check_type
+      has_slurpy:
+      sub_to_call = pop sub_stack
+      $I0 = shift arity_stack
+      args_array = new 'ResizablePMCArray'
+      getting_varargs:
+        $P1 = pop val_stack
+        $I0 = issame $P1, mkay
+        if $I0 goto call_the_sub
+        unshift args_array, $P1
+        goto getting_varargs
+      got_varargs:
+    check_type:
+      $I0 = isa item, 'Sub'
+    unless $I0 goto has_val
+      $P0 = inspect item
+      $I2 = $P0['pos_slurpy']
+      unless $I2 goto no_slurpy
+        push sub_stack, item
+        unshift arity_stack, -1
+        push val_stack, mkay
+      goto end_fixed
+      no_slurpy:
+        $I1 = item.arity()
+        push sub_stack, item
+        unshift arity_stack, $I1
+      end_fixed:
+      goto skip_val
+    has_val:
+      push val_stack, item
+      $I0 = arity_stack[0]
+      $I0 -= 1
+      arity_stack[0] = $I0
+    skip_val:
+    call_check:
+      $I0 = arity_stack[0]
+      unless $I0 == 0 goto skip_call
+      sub_to_call = pop sub_stack
+      $I1 = sub_to_call.arity()
+      args_array = new 'ResizablePMCArray'
+    args_loop:
+      if $I1 == 0 goto args_loop_end
+      $I1 -= 1
+      $P1 = pop val_stack
+      unshift args_array, $P1
+      goto args_loop
+    args_loop_end:
+      $I2 = shift arity_stack
+      call_the_sub:
+      $P2 = sub_to_call(args_array :flat)
+      push val_stack, $P2
+      $I0 = arity_stack[0]
+      $I0 -= 1
+      arity_stack[0] = $I0
+      goto call_check
+    skip_call:
+    goto it_loop
+it_done:
+    $I0 = elements sub_stack
+    if $I0 == 0 goto no_leftover_function
+    sub_to_call = pop sub_stack
+    args_array = new 'ResizablePMCArray'
+    $P2 = new 'Iterator', val_stack
+    getting_more_varargs:
+      $I1 = elements val_stack
+      unless $I1 goto got_more_varargs
+      $P1 = pop val_stack
+      $I0 = issame $P1, mkay
+      if $I0 goto got_more_varargs
+      unshift args_array, $P1
+      goto getting_more_varargs
+    got_more_varargs:
+    $P0 = sub_to_call(args_array :flat)
+    push val_stack, $P0
+    no_leftover_function:
+    .local pmc return_val 
+    return_val = pop val_stack
+    .return (return_val)
+.end
+# Local Variables:
+#   mode: pir
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
+
Index: languages/lolcode/src/builtins/say.pir
===================================================================
--- languages/lolcode/src/builtins/say.pir      (revision 25607)
+++ languages/lolcode/src/builtins/say.pir      (working copy)
@@ -10,14 +10,20 @@
 
 .sub 'VISIBLE'
     .param pmc args            :slurpy
-    .param int no_newline      :optional :named('no_newline')
+    .local int no_newline
+    no_newline = 0
     .local pmc iter
     iter = new 'Iterator', args
   iter_loop:
     unless iter goto iter_end
-    $P0 = shift iter
-    print $P0
+    $S0 = shift iter
+    $I0 = iseq $S0, '!'
+    if $I0 goto no_print
+    print $S0
     goto iter_loop
+    no_print:
+    no_newline = 1
+    goto iter_loop
   iter_end:
     if no_newline goto done
     print "\n"
Index: languages/lolcode/src/parser/actions.pm
===================================================================
--- languages/lolcode/src/parser/actions.pm     (revision 25607)
+++ languages/lolcode/src/parser/actions.pm     (working copy)
@@ -26,7 +26,7 @@
 
 
 method statement ($/, $key) {
-    if ($key eq 'bare_expression') {
+    if (($key eq 'expression')&&($<expression><tokens>[0]<identifier> ne 
'VISIBLE')) {
         my $it := PAST::Var.new( :name( 'IT' ), :scope('lexical'), 
:viviself('Undef'));
         my $past := PAST::Op.new( :pasttype('bind'), :node( $/ ) );
         $past.push( $it );
@@ -38,17 +38,6 @@
 }
 
 
-method visible($/) {
-    my $past := PAST::Op.new( :name('VISIBLE'), :pasttype('call'), :node( $/ ) 
);
-    if ( $<no_newline> ) {
-        $past.push( PAST::Val.new( :value( 1 ), :named( PAST::Val.new( 
:value('no_newline') ) ) ) );
-    }
-    for $<expression> {
-        $past.push( $( $_ ) );
-    }
-    make $past;
-}
-
 method declare($/) {
     if ($<expression>) {
         $($<variable>).isdecl(1);
@@ -73,28 +62,35 @@
     my $block := $( $<block> );
     $block.blocktype('declaration');
 
+    my $arglist := PAST::Stmts.new( :node($<arglist>) );
     # if there are any parameters, get the PAST for each of them and
     # adjust the scope to parameter.
-    if $<parameters> {
-        my @params := $<parameters>[0]<identifier>;
-        for @params {
-            my $param := $($_);
-            $param.scope('parameter');
-            $block.push($param);
-        }
+    $block.arity(0);
+    for $<parameters> {
+        my $param := PAST::Var.new(:name(~$_<identifier>), 
:scope('parameter'), :node($($_)));
+        $param.isdecl(1);
+        $arglist.push($param);
+        $block.arity($block.arity() + 1);
     }
 
+
     my $it := PAST::Var.new( :name( 'IT' ), :scope('lexical'), 
:viviself('Undef'), :isdecl(1));
-    $block.unshift($it);
+    $block[0].unshift($it);
 
     $it := PAST::Var.new( :name( 'IT' ), :scope('lexical'));
-    $block.push($it);
+    $block[0].push($it);
 
-    my $past := PAST::Op.new( :pasttype('bind'), :node( $/ ) );
-    $($<variable>).isdecl(1);
-    $past.push( $( $<variable> ) );
-    $past.push( $block );
-    make $past;
+    $block.unshift($arglist);
+
+    $block.name(~$<variable><identifier>);
+    make $block;
 }
 
 method ifthen($/) {
@@ -133,9 +129,11 @@
 
 method block($/) {
     my $past := PAST::Block.new( :blocktype('declaration'), :node( $/ ) );
+    my $stmts := PAST::Stmts.new( :node( $/ ) );
     for $<statement> {
-        $past.push( $( $_ ) );
+        $stmts.push( $( $_ ) );
     }
+    $past.push($stmts);
     make $past;
 }
 
@@ -143,14 +141,26 @@
     make $( $/{$key} );
 }
 
-method expression($/, $key) {
-    if ($key eq 'var_or_function') {
-        my $past := PAST::Op.new( :name('var_or_function'), :pasttype('call'), 
:node( $/ ) );
-        $past.push( $( $<variable> ) );
-        make $past;
-    } else {
-        make $( $/{$key} );
+method bang($/) {
+    make PAST::Val.new( :value( ~$/ ), :returns('String'), :node($/) );
+}
+
+method expression($/) {
+    my $past := PAST::Op.new( :name('expr_parse'), :pasttype('call'), :node( 
$/ ) );
+    for $<tokens> {
+        if($_<identifier>) {
+            my $inline := '%r = find_name "' ~ $_<identifier> ~ '"';
+            $past.push(PAST::Op.new( :inline($inline) ));
+        }
+        elsif($_ eq "MKAY"){
+            my $inline := '%r = find_name "MKAY"';
+            $past.push(PAST::Op.new( :inline($inline) ));
+        }
+        else {
+            $past.push( $( $_ ) );
+        }
     }
+    make $past;
 }
 
 method integer($/) {
@@ -174,15 +184,11 @@
 }
 
 
-method identifier($/) {
-    make PAST::Val.new( :value( ~$/ ), :node($/) );
-}
-
 method variable ($/) {
-    if ($<identifier><name> eq 'IT') {
+    if ($<identifier> eq 'IT') {
         make PAST::Var.new( :name( 'IT' ), :scope('lexical'), 
:viviself('Undef'));
     } else {
-        make PAST::Var.new( :name( ~$<identifier><name> ),
+        make PAST::Var.new( :name( $<identifier> ),
                             :scope('lexical'),
                             :viviself('Undef'),
                             :node( $/ )
Index: languages/lolcode/src/parser/grammar.pg
===================================================================
--- languages/lolcode/src/parser/grammar.pg     (revision 25607)
+++ languages/lolcode/src/parser/grammar.pg     (working copy)
@@ -19,21 +19,15 @@
 token version { \d+ [ '.' \d+ ]? }
 
 rule statement {
-    | <visible>    {*}   #= visible
     | <declare>    {*}   #= declare
     | <assign>     {*}   #= assign
     | <function>   {*}   #= function
     | <ifthen>     {*}   #= ifthen
-    | <expression> {*}   #= bare_expression
+    | <expression> {*}   #= expression
 }
 
 token statement_terminator { [ ',' | \n+ | $ ] }
 
-rule visible {
-    'VISIBLE' <expression> [ <expression> ]* [$<no_newline>='!']?
-    {*}
-}
-
 rule declare {
     'I' 'HAS' 'A' <variable> [ 'ITZ' <expression> ]?
     {*}
@@ -44,7 +38,7 @@
 }
 
 rule function {
-    'HOW' 'DUZ' 'I' <variable> <statement_terminator>
+    'HOW' 'DUZ' 'I' <variable> $<arglist>=['YR' $<parameters>=<variable>['AN' 
'YR' $<parameters>=<variable>]* ]?<statement_terminator>
     <block>
     'IF' 'U' 'SAY' 'SO'
     {*}
@@ -90,29 +84,40 @@
 }
 
 rule expression {
-    | <variable> {*}                             #= var_or_function
-    | <value> {*}                                #= value
+    [
+    | $<tokens>=<operator>
+    | $<tokens>=<variable>
+    | $<tokens>=<value>
+    | 'AN'
+    | $<tokens>='MKAY'
+    ]+ {*}
 }
 
-rule value {
+token value {
     | <float>    {*}                             #= float
     | <integer>  {*}                             #= integer
     | <boolean>  {*}                             #= boolean
     | <quote>    {*}                             #= quote
+    | <bang>     {*}                             #= bang
 }
 
-rule variable { <identifier> {*} }
+token bang {
+    '!' {*}
+}
 
-token identifier { <!keyword> $<name>=( <[a..zA..Z]> \w* ) {*} }
+token operator { $<identifier>=(<.identifier> <.ws> 'OF') {*} }
+token variable { <identifier> {*} }
 
+token identifier { <!keyword> <[a..zA..Z]> \w* }
+
 # RT #46213 : Because PGE doesn't yet know how to do longest token matching,
 # order all tokens in reverse alpha order to avoid a parsing bug.
 token keyword {
-    [ 'YR' | 'YA' | 'WTF?' | 'WIN' | 'WAI' | 'VISIBLE' | 'U' | 'SUM' | 'SO'
-    | 'SMALLR' | 'SAY' | 'RLY?' | 'RLY' | 'R' | 'QUOSHUNT' | 'PRODUKT'
-    | 'OMGWTF' | 'OMG' | 'OIC' | 'O' | 'NO' | 'MOD' | 'MEBBE' | 'KTHXBYE'
-    | 'ITZ' | 'IF' | 'I' | 'HOW' | 'HAS' | 'GTFO' | 'FOUND' | 'FAIL' | 'DIFF'
-    | 'BIGGR' | 'AN' | 'A' ] >>
+    [ 'YR' | 'YA' | 'WTF?' | 'WIN' | 'WAI' | 'U' | 'SO'
+    | 'SAY' | 'RLY?' | 'RLY' | 'R' 
+    | 'OMGWTF' | 'OMG' | 'OIC' | 'OF' | 'O' | 'NO' | 'MKAY' | 'MEBBE' | 
'KTHXBYE'
+    | 'ITZ' | 'IF' | 'I' | 'HOW' | 'HAS' | 'GTFO' | 'FOUND' | 'FAIL' 
+    | 'AN' | 'A' ] >>
 }
 
 token integer { '-'? \d+ {*} }

Reply via email to