The new 2.51 parser design (ditching yacc and relying on perl to handle
operator precedence) is excellent!  It allows expressions in all the
right places, avoids problems with reserved words as variables and
lots of other nice things, which resolves numerous latent issues
in 2.0x.  Nice work!

I have browsed through the code and I am proposing some new features
plus a few bug fixes.  Sorry in advance for the long email.

The significant changes I am proposing are:

  - moving "( expression )" from parse_expression to parse_term.
  - moving dot-operator processing from parse_node to parse_term.
  - adding "+" and "-" as unary operators.
  - figuring out exact line numbers in multi-line directive error
    messages (experimental).

The first two changes allow any type of term (eg: literal, qw[], [], {},
(expr)) to be used in a dotted expression, eg:

    foo.qw[a b c]      # -> [foo.a, foo.b, foo.c]
    foo.(1+2)          # -> foo.3
    foo.(bar.baz)      # -> foo.${bar.baz}
    "string".length    # -> 6

Using the (expr) form with the dot operator has the same effect as
${expr} interpolation, but might be slightly more efficient and is
perhaps more natural.  The relevant snippets of the new grammar look
like:

    expression: UNARYOP expression      # UNARYOP is [-+!]
            |   expression ? expression : expression
            |   term BINOP expression
            |   term

    term:       ufloat
            |   literal 
            |   quoted
            |   ( expression )
            |   ( assignment )
            |   [ list ]
            |   qw[ list ]          # or qw( ), qw< >, qw{ }, qw| |
            |   variable
            |   { hash }
            |   resource
            |   term ( . term )+    # only first term does ufloat

    variable:   node
            |   node ( args )

    node:       integer
            |   ident
            |   $ident
            |   ${directive}

    integer:    [-+]?\d+                # signed decimal
            |   [-+]?0[xX][\dA-Fa-f]+   # signed hex

    ufloat:     \d+(\.\d+)?([eE][+-]?\d+)?  # integer or float+exponent
            |   0[xX][\dA-Fa-f]+        # hex

(There will be some changes necessary elsewhere (eg: stash) to handle
these new operands to the dot operator... Note also that one material
change is that calling parse_variable now only handles a single
node[(args)].)

One subtle bug, fixed below, relates to floating point numbers.  Currently,
x.1.23 will incorrectly parse as x.(1.23), rather than x.(1).(23) (ie: it
should be two array indices, 1 followed by 23, not a single number, 1.23).

I've fixed this by splitting a number into two types: float and integer.
Float is the default in parse_term, and float is disabled on the RHS of
the dot operator. So you should be able to write 1.23.list.0 and get the
floating point number 1.23, since 1.23 is parsed as a single float.

Attached are patches to lib/Template/Parser.pm, lib/Parser/Handler.pm,
t/parse02.t and docs/xml/modules/Template/Parser.xml.

Here is a summary of the changes and miscellaneous bug fixes:

  - Parser/Handler.pm: whitespace before ";" causes error:
        -       if ($dir =~ / \G \s* ([^;]+) /gcx) {
        +       if ($dir =~ / \G \s* ([^\s;]+) /gcx) {

  - Parser/Handler.pm: text after newlines in directive is lost:
        -       if ($dir =~ / \G \s* ; \s* (.+) /gx) {
        +       if ($dir =~ / \G \s* ; \s* (.+) /gsx) {

  - Parser/Handler.pm: couple of other experimental changes to track
    line numbers within multiline directives, so that error messages
    display the correct absolute line number.

  - Template/Parser.pm $REGEXEN: added hex numbers, optional signed
    exponent, changed number (signed) to ufloat (unsigned float),
    added integer and added unaryop ([-+!]).

  - Template/Parser.pm $PARENS: added '{' => '\}'

  - Template/Parser.pm: variable pushback is problematic since not every
    rule checks for presence of a pushback variable.  For example,
    "(foo !(bar))" does not produce a syntax error and incorrectly
    parses as "(! (variable<literal<'bar'>, 0>))" (ie: the foo is pushed
    back, but never recovered).  I took the liberty of removing pushback,
    relying on rewind. (I assume pushback was only an optimization to
    avoid reparsing the variable...  if not then removing pushback is
    a big mistake.  Sorry.)

  - Template/Parser.pm: added unary operators "-" and "+", grouped
    together with "!".  Removed optional sign from number (now ufloat),
    since it is handled by the unary ops.  Unary "-" and "+" allow
    expressions like "foo(-x)" or "x = -y", which didn't work before
    (and don't work in 2.0x).

  - Template/Parser.pm: moved "( expression )" rule from parse_expression
    to parse_term.  Discussed above.

  - Template/Parser.pm: added do_dotop option to parse_term.  Set on
    recursive calls to parse_term for RHS of dotop.  Used to turn off
    variable compile, ufloat matching and dotop processing on RHS of dotop.

  - Template/Parser.pm: moved parse_node dotop processing into
    parse_term.

  - Template/Parser.pm: collapsed parse_node into parse_variable. Note
    that parse_variable now handles only a single variable[args], since
    dotop has moved to parse_term. parse_variable is only called in a
    couple of places where the argument is just a simple ident (\w+), so
    this should be ok.  But if you should call parse_term instead of
    parse_variable is you want to parse a dotted variable.

    In fact: parse_assign now calls parse_term instead of parse_variable
    for the LHS.  This allows lists to appear on the LHS.  But it also
    now allows illegal assignments (eg: "foo" = 1) that will have to be
    checked for later.  (Oops, I didn't update this changed in the
    Parser.xml doc.)

  - Template/Parser.pm: changed \s+ to \s* to allow optional (rather
    than required) white space after commas in hash, list and args.

  - Template/Parser.pm: change to parse_filename to fix possible
    bug (but didn't test old or new version...)

  - Template/Parser.pm: fixed the same $pre bug as 2.03 on interpolate
    "0$foo".

  - Template/Parser.pm: changes to sub location() to allow it to return
    correct error line number in multi-line directives.  Experimental,
    not cleaned up and not well tested.

  - Template/Parser.pm: needs same fix as 2.0[34] for not removing
    last trailing newline with -%] tag.

Regards,
Craig

--- Template-Toolkit-2.51/docs/xml/modules/Template/Parser.xml  Mon Jun 25 01:58:03 
2001
+++ Template-Toolkit-2.51.new/docs/xml/modules/Template/Parser.xml      Sun Jul  1 
+22:06:59 2001
@@ -307,8 +307,9 @@
     <method name="parse_term">
         <args>$text</args>
         <args>\$text</args>
-       Parse a term which can be a number, literal or interpolated string, a variable,
-        hash, list or quoted list.
+       Parse a term which can be an integer, floating point number,
+       literal or interpolated string, a variable, hash, list,
+       quoted list, or an expression in parentheses.
 <template>
 [% 3.14 %]
 [% 'Hello World' %]
@@ -325,24 +326,13 @@
     <method name="parse_variable">
         <args>$text</args>
         <args>\$text</args>
-       Parse a variable.
+       Parse the single node of a variable.
 <template>
 [% foo %]
 [% foo(10).bar(20) %]
 </template>
     </method>
 
-    <method name="parse_node">
-        <args>$text</args>
-        <args>\$text</args>
-       Parse a single node of a variable.
-<template>
-[% foo %]
-[% foo(10) %]
-[% bar(20) %]
-</template>
-    </method>
-
     <method name="parse_hash">
         <args>$text</args>
         <args>\$text</args>
@@ -406,8 +396,7 @@
 
     <method name="location">
        Returns the line number of the current parser position,
-       e.g. "23", or in the case of directives that span multiple
-       lines, a string of the form "n-m", e.g. "23-26".
+       e.g. "23".
     </method>
 
     <method name="parse_error" args="$error, $directive">
@@ -459,27 +448,27 @@
 
 assignment: variable =>? expression
 
-expression: ! expression
-       |   ( expression )
-        |   ( assignment )
+expression: UNARYOP expression      # UNARYOP is [-+!]
        |   expression ? expression : expression            
         |   term BINOP expression
        |   term
 
-term:      number
+term:      ufloat
        |   literal
        |   quoted
-       |   variable
-       |   qw[ list ]          # or qw( ), qw&lt; &gt;, qw{ }, qw| |
+       |   ( expression )
+        |   ( assignment )
        |   [ list ]
+       |   qw[ list ]          # or qw( ), qw&lt; &gt;, qw{ }, qw| |
+       |   variable
        |   { hash }
+       |   resource
+        |   term ( . term )+    # only first term does ufloat
 
-variable:   varnode ( . varnode )*
-
-varnode:    node
+variable:   node
        |   node ( args )
 
-node:      number
+node:      integer
        |   ident
        |   $ident
        |   ${directive}
@@ -504,8 +493,14 @@
 
 ident:     \w+
 
-number:            [-+]\d+(\.\d+)?
+integer:    [-+]?\d+                    # signed decimal
+        |   [-+]?0[xX][\dA-Fa-f]+       # signed hex
+
+ufloat:     \d+(\.\d+)?([eE][+-]?\d+)?  # integer or float+exponent
+        |   0[xX][\dA-Fa-f]+            # hex
+
+resource:   \w+:[\w\.\/:]+
 </pre>
     </p>
   </section>
-</module>
\ No newline at end of file
+</module>
--- Template-Toolkit-2.51/lib/Template/Parser/Handler.pm        Wed Jun 20 05:37:34 
2001
+++ Template-Toolkit-2.51.new/lib/Template/Parser/Handler.pm    Sun Jul  1 21:43:31 
+2001
@@ -124,7 +124,7 @@
 
 sub text {
     my ($self, $parser, $text) = @_;
-    my $line = $parser->location();
+    my $line = $parser->location(\$text);
     $self->DEBUG($self->ID, "->text('", $self->_inspect_text($text), "') at line 
$line\n")
        if $DEBUG;
     push(@{ $self->{ CONTENT } }, $text);
@@ -146,7 +146,7 @@
     my ($self, $parser, $dir) = @_;
     my ($keyword, $handler, $object, $method, $args, $result, $gap);
 
-    my $line = $parser->location();
+    my $line = $parser->location(\$dir);
     $self->DEBUG($self->ID, "->directive('", $self->_inspect_text($dir), "') at line 
$line\n")
        if $DEBUG;
 
@@ -222,11 +222,16 @@
            push(@{ $self->{ CONTENT } }, $result);
        }
 
-       if ($dir =~ / \G \s* ([^;]+) /gcx) {
+       if ($dir =~ / \G \s* ([^\s;]+) /gcx) {
            $parser->parse_warning("ignored trailing cruft in directive: $1");
        }
 
-       if ($dir =~ / \G \s* ; \s* (.+) /gx) {
+       if ($dir =~ / \G \s* ; \s* /gx) {
+            $parser->location(\$dir, 1);    # update current line count
+       } else {
+            last;
+       }
+       if ($dir =~ / \G (.+) /gsx) {
            $dir = $1;
        }
        else {
--- Template-Toolkit-2.51/lib/Template/Parser.pm        Mon Jun 25 02:00:58 2001
+++ Template-Toolkit-2.51.new/lib/Template/Parser.pm    Sun Jul  1 22:03:43 2001
@@ -67,7 +67,9 @@
 };
 
 my $REGEXEN = {
-    number   => qr/ \G \s* ([+-]? \d+ (?:\.\d+)? ) /x,
+    ufloat   => qr/ \G \s* ( 0[xX][\dA-Fa-f]+
+                           | \d+ (?:\.\d+)? (?:[eE][+-]?\d+)? ) /x,
+    integer  => qr/ \G \s* ( [+-]? (?: 0[xX][\dA-Fa-f]+ | \d+) ) /x,
     literal  => qr/ \G \s* ' ( (?:\\'|[^'])* ) ' /x,
     quoted   => qr/ \G \s* " ( (\\\\|\\"|.|\n)*? ) " /x,
     ident    => qr/ \G (\w+) /x,
@@ -75,7 +77,8 @@
     embed    => qr/ \G \${ \s* ([^}]*?) \s* } /x,
     filename => qr/ \G ([\w\.\/:]+) /x,
     resource => qr/ \G (\w+):([\w\.\/:]+) /x,
-    binop    => qr/ \G \s* ( [\-\+\*\/%] 
+    unaryop  => qr/ \G \s* ( [-+!] ) /x,
+    binop    => qr/ \G \s* ( [-+*\/%] 
                            | [=!<>]= | [<>] | eq | ne | [lg][et] 
                            | &&? | \|\| | or | and  ) \s* /x,
     text     => qr/ \G ( (?: \\. | [^\$] )+ ) /x,
@@ -86,10 +89,9 @@
     '[' => '\]',
     '<' => '\>',
     '|' => '\|',
+    '{' => '\}',
 };
 
-
-
 #------------------------------------------------------------------------
 # init(\%config)
 #
@@ -175,7 +177,7 @@
     # skip any leading whitespace
     $$textref =~ / \G \s* /gcx;
 
-    if (defined ($stmt = $self->parse_assign($textref, pushback => 1, no_compile => 
1))) {
+    if (defined ($stmt = $self->parse_assign($textref, no_compile => 1))) {
 
        $self->DEBUG("stmt got an assign: [ $stmt->[0], $stmt->[1] ]\n")
            if $DEBUG;
@@ -217,27 +219,16 @@
     # skip any leading whitespace
     $$textref =~ / \G \s* /gcx;
 
-    # ! expression
-    if ($$textref =~ / \G ! \s* /gcx) {
+    # unaryop expression
+    if ($$textref =~ /$REGEXEN->{ unaryop }/gc ) {
+       $op = $1;
+       $self->DEBUG("expr got a unaryop: $op\n")
+           if $DEBUG;
        defined ($expr = $self->parse_expression($textref))
            || return;
-       $expr = "! $expr";
+       $expr = "$op $expr";
        return $expr;
     }
-    # ( expression )
-    elsif ($$textref =~ / \G \( \s* /gcx) {
-       if (defined ($term = $self->parse_assign($textref, pushback => 1))) {
-           $self->DEBUG("expr got an assign: $term\n")
-               if $DEBUG;
-           $self->throw('no terminating )')
-               unless $$textref =~ / \G \s* \) /gcx;
-       }
-       elsif (defined ($expr = $self->parse_expression($textref))) {
-           $self->throw('no terminating )')
-               unless $$textref =~ / \G \s* \) /gcx;
-           $term = "($expr)";
-       }
-    }
     elsif (defined ($term = $self->parse_term($textref))) {
        $self->DEBUG("expr got a term: $term\n")
            if $DEBUG;
@@ -278,7 +269,7 @@
     }
 
     return $expr;
-}      
+}
 
 
 #------------------------------------------------------------------------
@@ -300,7 +291,7 @@
     # save string position in case we need to backtrack
     my $pos = pos $$textref;
 
-    if (defined ($var = $self->parse_variable($textref))) {
+    if (defined ($var = $self->parse_term($textref, in_assign => 1))) {
        if ($$textref =~ / \G \s* (=>?) \s* /gcx) {
            $op = $1;
 
@@ -330,21 +321,10 @@
            return $assign;
        }
        else {
-           # we check for following whitespace to ensure this really can
-           # be passed off as a variable and not something like qw[ 
-           # where 'qw' has been matched but shouldn't have
-           if ($options->{ pushback } && $$textref =~ / \G \s+ /gcx) { 
-               # cache variable
-#              $self->DEBUG("not an assign, CACHING\n")
-#                  if $DEBUG;
-               $self->{ _PUSHBACK_VAR } = $var;
-           }
-           else {
-               # rewind string position (backtrack)
-#              $self->DEBUG("not an assign, REWINDING\n")
-#                  if $DEBUG;
-               pos $$textref = $pos;
-           }
+            # rewind string position (backtrack)
+            $self->DEBUG("not an assign, REWINDING\n")
+                if $DEBUG;
+            pos $$textref = $pos;
        }
     }
     return undef;
@@ -395,20 +375,11 @@
     my $textref = ref $text ? $text : \$text;
     my $compiler = $self->{ _COMPILER } 
        || return $self->throw('no compiler defined');
-    my ($term, $args);
-
-    # check variable cache for any variable read-ahead (e.g. 
-    # by looking ahead for an '=' assignment)
-    if ($self->{ _PUSHBACK_VAR } 
-       && defined ($term = $self->parse_variable($textref))) {
-       defined ($term = $compiler->variable($term))
-           || $self->throw($compiler->error());
-       $self->DEBUG("got pushback variable: $term\n") if $DEBUG;
-       return $term;
-    }
+    my ($term, $args, $gotVariable);
 
-    # term can be an number such as: 5...
-    if ($$textref =~ /$REGEXEN->{ number }/gc) {
+    # term can be an unsigned (possibly floating point) number such
+    # as: 5 or 1.61803...
+    if (!$options->{in_dotop} && $$textref =~ /$REGEXEN->{ ufloat }/gc) {
        $self->DEBUG("got number: $1\n") if $DEBUG;
        defined($term = $compiler->number($1))
            || return $self->error($compiler->error());
@@ -425,6 +396,20 @@
        defined($term = $self->parse_interpolate($1))
            || return;
     }
+    # ...or an expression in parens, like ( foo + bar )...
+    elsif ($$textref =~ / \G \( \s* /gcx) {
+       if (defined ($term = $self->parse_assign($textref))) {
+            $self->DEBUG("term got an assign: $term\n")
+               if $DEBUG;
+           $self->throw('no terminating )')
+               unless $$textref =~ / \G \s* \) /gcx;
+       }
+       elsif (defined ($term = $self->parse_expression($textref))) {
+           $self->parse_error('no terminating )')
+               unless $$textref =~ / \G \s* \) /gcx;
+           $term = "($term)";
+       }
+    }
     # ...or a list: [ 1, two, '3' ]
     elsif ($$textref =~ / \G \[ \s* /gcx) {
        $term = $self->parse_list($textref);
@@ -463,23 +448,39 @@
     # ...or a resource: http://www.example.com/blah.xml
     elsif ($$textref =~ /$REGEXEN->{ resource }/gc) {
        $self->DEBUG("got resource: ($1) ($2)\n") if $DEBUG;
-       return "$1_resource($2)";
+       $term = "$1_resource($2)";
     }
-    # ...or a variable like: foo.bar(baz)
-    elsif (defined ($term = $self->parse_variable($textref))) {
-       defined ($term = $compiler->variable($term))
-           || $self->throw($compiler->error());
-       $self->DEBUG("got variable: $term\n") if $DEBUG;
+    # ...or a variable or function call like: foo or foo(bar)...
+    elsif (defined($term = $self->parse_variable($textref))) {
+       $gotVariable = 1;
     }
     # ...or it's not a term
     else {
        return undef;
     }
-
+    # look for a following '.' then expect another term
+    if ( !$options->{in_dotop} && $$textref =~ / \G (?=\.[^\.]) /x ) {
+        $term = [$term, 0] unless $gotVariable;
+        $gotVariable = 1;
+        while ($$textref =~ / \G \.(?!\.) /gcx) {
+            my $nextTerm = $self->parse_term($textref, in_dotop => 1);
+            $self->throw("expected term after '.'")
+                unless defined $nextTerm;
+            push(@$term, @$nextTerm);
+        }
+    }
+    if ( $options->{in_dotop} ) {
+        return [$term, 0] if ( !$gotVariable );
+        return $term;
+    } elsif ( $gotVariable && !$options->{in_assign} ) {
+       # compile the variable last time through, unless we're doing an assign
+       defined ($term = $compiler->variable($term))
+           || $self->throw($compiler->error());
+       $self->DEBUG("got variable: $term\n") if $DEBUG;
+    }
     return $term;
 }
 
-
 #------------------------------------------------------------------------
 # parse_variable($self, $text)
 #
@@ -493,56 +494,16 @@
     my $text = shift;
     my $options = ref $_[0] eq 'HASH' ? shift : { @_ };
     my $textref = ref $text ? $text : \$text;
-
-    if (my $pushback = $self->{ _PUSHBACK_VAR }) {
-       $self->{ _PUSHBACK_VAR } = undef;
-#      $self->DEBUG("parse_variable() returning pushed back variable: ",
-#                   $self->_inspect_ref($pushback), "\n")
-#          if $DEBUG;
-       return $pushback;
-    }
+    my ($ident, $args);
     my $compiler = $self->{ _COMPILER } 
        || return $self->throw('no compiler defined');
-    my ($node, @nodes);
 
     # skip any leading whitespace
     $$textref =~ / \G \s* /gcx;
 
-    # parse the first node
-    $node = $self->parse_node($textref);
-    return undef unless defined $node;
-    push(@nodes, @$node);
-
-    # look for a following '.' then expect another node
-    while ($$textref =~ / \G \.(?!\.) /gcx) {
-       $node = $self->parse_node($textref);
-       $self->throw("expected node after '.'")
-           unless defined $node;
-       push(@nodes, @$node);
-    }
-
-    return \@nodes;
-}
-
-
-#------------------------------------------------------------------------
-# parse_node($self, \$text)
-#
-# Parse a TT variable node containing an identifier with an optional 
-# argument list in parentheseis, e.g. 'foo' or 'foo(x, y, z(a))'.
-#------------------------------------------------------------------------
- 
-sub parse_node {
-    my $self = shift;
-    my $text = shift;
-    my $options = ref $_[0] eq 'HASH' ? shift : { @_ };
-    my $textref = ref $text ? $text : \$text;
-    my $compiler = $self->{ _COMPILER } 
-       || return $self->throw('no compiler defined');
-    my ($ident, $args);
-
-    # node can be a number like '4'...
-    if ($$textref =~ /$REGEXEN->{ number }/gc) {
+    # variable is a node, followed by optional arguments
+    # node can be a signed integer number like '4'...
+    if ($$textref =~ /$REGEXEN->{ integer }/gc) {
        $self->DEBUG("got number: $1\n") if $DEBUG;
        defined($ident = $compiler->number($1))
            || return $self->throw($compiler->error());
@@ -587,7 +548,6 @@
     return [ $ident, $args ];
 }
 
-
 #------------------------------------------------------------------------
 # parse_list($self, \$text)
 #
@@ -653,7 +613,7 @@
            push(@hash, $key, $value);
            
            # skip comma and/or whitespace
-           $$textref =~ / \G (?:\s*,)? \s+ /gcx;
+           $$textref =~ / \G (?:\s*,)? \s* /gcx;
        }
        else {
            $self->throw("missing '=>' after '$key'");
@@ -729,7 +689,7 @@
     }
     continue {
        # skip comma and/or whitespace
-       $$textref =~ / \G (?:\s*,)? \s+ /gcx;
+       $$textref =~ / \G (?:\s*,)? \s* /gcx;
     }
 
     # trailing comma/whitespace
@@ -772,7 +732,7 @@
        defined ($filename = $compiler->literal($1))
            || $self->throw($compiler->error());
     }
-    elsif ($$textref =~ / \G \$ /gcx) {
+    elsif ($$textref =~ /$REGEXEN->{ interp }/gc) {
        defined ($filename = $self->parse_variable($1))
            || return;
        defined ($filename = $compiler->variable($filename))
@@ -810,11 +770,11 @@
     /gx ) {
 
        # preceding text
-       if ($1) {
+       if (defined($1)) {
            push(@tokens, "\"$1\"");
        }
        # $variable reference
-        elsif ($2) {
+        elsif (defined($2)) {
            push(@tokens, $self->parse_embedded($2) || return);
        }
        else {
@@ -1230,17 +1190,35 @@
 
 
 #------------------------------------------------------------------------
-# location()
+# location($textref, $updateDelta)
 #
-# Return current parse location in terms of a file number or 'n-m' range.
+# Return current parse location in terms of a line number.
+# Optional arguments specify which text is being parsed, and
+# how many lines to count in case we jump over newlines.
 #------------------------------------------------------------------------
 
 sub location {
-    my $self = shift;
+    my($self, $textref, $updateDelta) = @_;
     my ($line, $linespan) = @$self{ qw( _LINE _LINESPAN ) };
+    my($pos, $deltaLines);
+
+    if ( defined($pos = pos ${$self->{_LINETEXT}}) ) {
+        $deltaLines = (substr(${$self->{_LINETEXT}}, 0, $pos) =~ tr/\n//);
+    }
+    if ( defined($textref) ) {
+        $self->{_LINETEXT} = $textref;
+        if ( defined($updateDelta) && defined($deltaLines) ) {
+            # note: only works for case where $self->{_LINE} is a ref.
+            ${$self->{_LINE}} += $deltaLines;
+            $deltaLines = 0;
+        }
+    }
     $line = ref($line) ? $$line : $line;
     $linespan = ref($linespan) ? $$linespan : $linespan;
-    unless (defined $line) {
+    if ( defined $line && defined $deltaLines ) {
+        $line += $deltaLines;
+    }
+    elsif (!defined $line) {
        $line = 'unknown';
     }
     elsif ($linespan) {
--- Template-Toolkit-2.51/t/parse02.t   Sat Jun 23 02:22:06 2001
+++ Template-Toolkit-2.51.new/t/parse02.t       Sun Jul  1 22:04:53 2001
@@ -114,7 +114,7 @@
 #------------------------------------------------------------------------
 # test numbers
 -1
-number<-1>
+- number<1>
 
 0
 number<0>
@@ -123,16 +123,16 @@
 number<1>
 
 +2
-number<+2>
++ number<2>
 
 2.718
 number<2.718>
 
 -2.718
-number<-2.718>
+- number<2.718>
 
 +3.14159
-number<+3.14159>
++ number<3.14159>
 
 #------------------------------------------------------------------------
 # test literal strings
@@ -254,3 +254,81 @@
 
 http://www.foo.com/bar.xml
 http_resource(//www.foo.com/bar.xml)
+
+-x
+- variable<literal<'x'>, 0>
+
+!x
+! variable<literal<'x'>, 0>
+
+----x
+- - - - variable<literal<'x'>, 0>
+
+-1
+- number<1>
+
+foo.bar.$zzz + !!!! -1
+variable<literal<'foo'>, 0, literal<'bar'>, 0, variable<literal<'zzz'>, 0>, 0> + ! ! 
+! ! - number<1>
+
+1 + 2 + 3 + 4 + 5
+number<1> + number<2> + number<3> + number<4> + number<5>
+
+1 ? 2 : 3 ? 4 : 5 ? 6 : 7
+number<1> ? number<2> : number<3> ? number<4> : number<5> ? number<6> : number<7>
+
+foo.1.2
+variable<literal<'foo'>, 0, number<1>, 0, number<2>, 0>
+
+"1$foo"
+quoted<"1", variable<literal<'foo'>, 0>>
+
+"00$foo"
+quoted<"00", variable<literal<'foo'>, 0>>
+
+"0$foo"
+quoted<"0", variable<literal<'foo'>, 0>>
+
+"$foo"
+quoted<variable<literal<'foo'>, 0>>
+
+"xxxx".length
+variable<quoted<"xxxx">, 0, literal<'length'>, 0>
+
+qw{a b c}.1.2
+variable<[ qw( a b c ) ], 0, number<1>, 0, number<2>, 0>
+
+1.23.list.list.4.5
+variable<number<1.23>, 0, literal<'list'>, 0, literal<'list'>, 0, number<4>, 0, 
+number<5>, 0>
+
+foo.qw[a b c]
+variable<literal<'foo'>, 0, [ qw( a b c ) ], 0>
+
+(foo+!(aaa))
+(variable<literal<'foo'>, 0> + ! (variable<literal<'aaa'>, 0>))
+
+-foo
+- variable<literal<'foo'>, 0>
+
+foo + (!bar)
+variable<literal<'foo'>, 0> + (! variable<literal<'bar'>, 0>)
+
+(foo.aaa.bbb + bar)
+(variable<literal<'foo'>, 0, literal<'aaa'>, 0, literal<'bbb'>, 0> + 
+variable<literal<'bar'>, 0>)
+
+foo.$bar.aaa
+variable<literal<'foo'>, 0, variable<literal<'bar'>, 0>, 0, literal<'aaa'>, 0>
+
+foo(1,2).$bar(3,4)
+variable<literal<'foo'>, [ number<1>, number<2> ], variable<literal<'bar'>, 0>, [ 
+number<3>, number<4> ]>
+
+foo.(1+2)
+variable<literal<'foo'>, 0, (number<1> + number<2>), 0>
+
+foo(1,2).(bar(3 4))
+variable<literal<'foo'>, [ number<1>, number<2> ], (variable<literal<'bar'>, [ 
+number<3>, number<4> ]>), 0>
+
+[12e4, 12.3e-4, 12.3e+4, 0x0, 0xaBcDeF]
+[ number<12e4>, number<12.3e-4>, number<12.3e+4>, number<0x0>, number<0xaBcDeF> ]
+
+foo(-x, +y)
+variable<literal<'foo'>, [ - variable<literal<'x'>, 0>, + variable<literal<'y'>, 0> ]>


Reply via email to