Author: pmichaud
Date: Wed May 30 10:11:10 2007
New Revision: 18690
Added:
trunk/languages/abc/src/abc-2a.txt
Modified:
trunk/MANIFEST
trunk/languages/abc/src/abc-2.pg
Log:
(abc/PAST):
* More thoughts about a non-PIR based parse->AST transformation.
Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST (original)
+++ trunk/MANIFEST Wed May 30 10:11:10 2007
@@ -1022,6 +1022,7 @@
languages/abc/config/makefiles/root.in [abc]
languages/abc/src/PASTGrammar.tg [abc]
languages/abc/src/abc-2.pg [abc]
+languages/abc/src/abc-2a.txt [abc]
languages/abc/src/abc.pg [abc]
languages/abc/src/builtins.pir [abc]
languages/abc/t/01-tests.t [abc]
Modified: trunk/languages/abc/src/abc-2.pg
==============================================================================
--- trunk/languages/abc/src/abc-2.pg (original)
+++ trunk/languages/abc/src/abc-2.pg Wed May 30 10:11:10 2007
@@ -1,5 +1,7 @@
grammar ABC::Grammar;
+## DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT
+
## an example 'bc' grammar
## This version uses a Perl 6 subset to build the abstract
## syntax tree as the program is being parsed, instead of a separate
@@ -11,38 +13,21 @@
token TOP { <program> { return $<program> }; }
## recognize terms
-token float {
- \d+ '.' \d* | '.' \d+
- { return PAST::Val.new( :node($/), :vtype('.Float'), :name($/); }
-}
+token float { [ \d+ '.' \d* | '.' \d+ ] {*} }
-token integer {
- \d+
- { return PAST::Val.new( :node($/), :vtype('.Integer'), :name($/); }
-}
+token integer { \d+ {*} }
## FIXME: still need to handle lvalue variables
-token variable {
- <[a..z]><[_a..z0..9]>*
- { return PAST::Var.new( :node($/), :name($/), :viviself('.Integer'); }
-}
+token variable { <[a..z]><[_a..z0..9]>* {*} }
## FIXME: still need to handle lvalue array
token term {
- | <float> { return $<float>; }
- | <integer> { return $<integer>; }
+ | <float> {*#<float>}
+ | <integer> {*#<integer>}
| <variable>
- [ '[' <expression> ']'
- { return PAST::Var( $<variable>, $<expression>,
- :node($/),
- :name($<variable>.text ~ '_array'),
- :viviself('.Integer') ); }
- | '(' <expression> ')'
- { return PAST::Op( $<expression>
- :node($/),
- :name($<variable>.text),
- :pasttype('call') ); }
- | <null> { return $<variable>; }
+ [ '[' <expression> ']' {*#<array>}
+ | '(' <expression> ')' {*#<call>}
+ | <null> {*#<variable>}
]
}
@@ -57,120 +42,113 @@
}
rule compound_statement {
- '{' <statement_list> '}' { return $<statement_list>; }
+ '{' <statement_list> '}' {*}
}
rule if_statement {
'if' '(' <expression> ')' <statement>
[ 'else' <statement> ]?
- { $x = PAST::Op.new( $<expression>, $<statement>[0],
- :node($/), :pasttype('if') );
- if ($<statement>[1]) { $x.push($<statement>[1]); }
- return $x;
- }
+ {*}
}
rule while_statement {
'while' '(' <expression> ')' <statement>
- { return PAST::Op.new( $<expression>, $<statement>,
- :node($/), :pasttype('while') );
- }
+ {*}
}
rule for_statement {
'for' '(' <expression> ; <expression> ; <expression> ')' <statement>
- { $iter = PAST::Stmts.new( $<statement>, $<expression>[2] );
- $loop = PAST::Op.new( $<expression>[1], $iter, :pasttype('while') );
- return PAST::Stmts.new( $<expression>[0], $loop );
- }
+ {*}
}
rule statement {
- | <if_statement> { return $<if_statement> ; }
- | <while_statement> { return $<while_statement>; }
- | <for_statement> { return $<for_statement>; }
- | <compound_statement> { return $<compound_statement>; }
+ | <if_statement> {*#<if>}
+ | <while_statement> {*#<while>}
+ | <for_statement> {*#<for>}
+ | <compound_statement> {*#<compound>}
| \" <string> \"
| break
| continue
| halt
| return [ \( <expression> \) ]?
- | <expression> { return $<expression>; }
+ | <expression> {*#<expression> }
}
rule statement_list {
<statement>? [ ; <statement>? ]*
- { $stmts = PAST::Stmts.new( :node($/) );
- for @$<statement> { $stmts.push( $_ ); }
- return $stmts;
- }
+ {*}
}
rule program {
^ <statement_list> [ $ | <die: parse error> ]
- { return PAST::Block.new( :node($/), $<statement_list> ); }
+ {*}
}
rule pseudo_statement { limits | quit | warranty }
## FIXME : still don't handle $<expression> as PAST
## create operator precedence parser
-rule 'expression' is optable { ... }
-
-## terms
-proto 'term:' is precedence('=')
- is parsed(&term)
- { ... }
+rule expression {
+ <EXPR>
+ {*}
+}
-proto 'circumfix:( )' is equiv('term:')
+token circumfix:sym<( )> is equiv(&term)
is pirop('set')
{ ... }
## autoincrement
-proto 'postfix:++' is looser('term:') is lvalue(1) { ... }
-proto 'postfix:--' is equiv('postfix:++') is lvalue(1) { ... }
-proto 'prefix:++' is equiv('postfix:++') is lvalue(1) { ... }
-proto 'prefix:--' is equiv('postfix:++') is lvalue(1) { ... }
+token postfix:sym<++> is looser(&circumfix:<( )>)
+ is lvalue(1)
+ { ... }
+token postfix:sym<--> is equiv(&postfix:<++>) is lvalue(1) { ... }
+token prefix:sym<++> is equiv(&postfix:<++>) is lvalue(1) { ... }
+token prefix:sym<--> is equiv(&postfix:<++>) is lvalue(1) { ... }
## negation
-proto 'prefix:-' is looser('postfix:++')
+token prefix:sym<-> is looser(&postfix:<++>)
is pirop('neg')
{ ... }
## exponentiation
-proto 'infix:^' is looser('prefix:-') { ... }
+token infix:sym<^> is looser(&prefix:<->) { ... }
## multiplicative
-proto 'infix:*' is looser('infix:^')
+token infix:sym<*> is looser(&infix:<^>)
is pirop('mul')
{ ... }
-proto 'infix:/' is equiv('infix:*')
+token infix:sym</> is equiv(&infix:<*>)
is pirop('div')
{ ... }
-proto 'infix:%' is equiv('infix:*')
+token infix:sym<%> is equiv(&infix:<*>)
is pirop('mod')
{ ... }
+
## additive
-proto 'infix:+' is looser('infix:*')
+token infix:sym<+> is looser(&infix:<*>)
is pirop('add')
{ ... }
-proto 'infix:-' is equiv('infix:+')
+token infix:sym<-> is equiv(&infix:<+>)
is pirop('sub')
{ ... }
## assignment
-proto 'infix:=' is looser('infix:+')
+token infix:sym<=> is looser(&infix:<+>)
is assoc('right')
is past('assign')
is lvalue(1)
{ ... }
## relational
+token infix:sym<==> is looser(&infix:<=>)
+ is assoc('non')
+ {...}
+
proto 'infix:==' is looser('infix:=') is assoc('non') { ... }
proto 'infix:!=' is equiv('infix:==') { ... }
proto 'infix:<' is equiv('infix:==') { ... }
Added: trunk/languages/abc/src/abc-2a.txt
==============================================================================
--- (empty file)
+++ trunk/languages/abc/src/abc-2a.txt Wed May 30 10:11:10 2007
@@ -0,0 +1,126 @@
+class ABC::Grammar::ASTBuilder;
+
+## DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT
+
+## This is a draft concept of building an abstract syntax
+## tree (using PAST-pm) using methods in a parse tree (abc-2.pg)
+## instead of as a separate TGE parse->PAST phase. The advantage
+## of this approach is that the conversions are easier to write,
+## as they use a fairly simple subset of Perl 6.
+
+## each method here corresponds to a rule of the same name
+## in ABC::Grammar. The method is invoked automatically wherever a
+## {*} appears in the parse rule. So, for example, where abc-2.pg
+## has a regex like:
+##
+## token float { [ \d+ '.' \d* | '.' \d+ ] {*} }
+##
+## The 'float' method of this class will be invoked at the point
+## of the {*}, with the current match object in the C< $/ > lexical
+## variable. For regexes that have multiple points of invocation,
+## the grammar can specify a $key parameter that is passed to the AST
+## method here.
+##
+## token term {
+## | <float> {*#<float>}
+## | <integer> {*#<integer>}
+## ...
+## }
+##
+## This is just a draft/concept file at the moment, the actual
+## syntax and operation is likely to change.
+
+
+method TOP {
+ return $<program>;
+}
+
+method float {
+ return PAST::Val.new( :node($/), :vtype('.Float'), :name($/) );
+}
+
+method integer {
+ return PAST::Val.new( :node($/), :vtype('.Integer'), :name($/) );
+}
+
+method variable {
+ return PAST::Var.new( :node($/), :name($/), :viviself('.Integer'),
+ :lvalue($+lvalue) );
+}
+
+method term(:$key) {
+ if $key eq 'float' { return $<float>; }
+ if $key eq 'integer' { return $<integer>; }
+ if $key eq 'variable' { return $<variable>; }
+ if $key eq 'array' {
+ return PAST::Var.new( $<variable>, $<expression>,
+ :node($/),
+ :name($<variable>.text ~ '_array'),
+ :viviself('.Integer') );
+ }
+ if $key eq 'call' {
+ return PAST::Op.new( $<expression>,
+ :node($/),
+ :name($<variable>.text),
+ :pasttype('call') );
+ }
+}
+
+method compound_statement {
+ return $<statement_list>;
+}
+
+method if_statement {
+ my $x := PAST::Op.new( $<expression>, $<statement>[0],
+ :node($/), :pasttype('if') );
+ if ($<statement>[1]) { $x.push($<statement>[1]); }
+ return $x;
+}
+
+method while_statement {
+ return PAST::Op.new( $<expression>, $<statement>,
+ :node($/), :pasttype('while') );
+}
+
+method for_statement {
+ my $iter := PAST::Stmts.new( $<statement>, $<expression>[2] );
+ my $loop := PAST::Op.new( $<expression>[1], $iter, :pasttype('while') );
+ return PAST::Stmts.new( $<expression>[0], $loop );
+}
+
+method statement(:$key) {
+ if $key eq 'if' { return $<if_statement>; }
+ if $key eq 'while' { return $<while_statement>; }
+ if $key eq 'for' { return $<for_statement>; }
+ if $key eq 'compound' { return $<compound_statement>; }
+ if $key eq 'expression' { return $<expression>; }
+}
+
+
+method statement_list {
+ my $stmts := PAST::Stmts.new( :node($/) );
+ for @$<statement> { $stmts.push( $_ ); }
+ return $stmts;
+}
+
+method program {
+ return PAST::Block.new( :node($/), $<statement_list> );
+}
+
+
+method expression {
+ return $<EXPR>;
+}
+
+
+method EXPR(:$key) {
+ if ($key eq 'reduce') {
+ my $op := PAST::Op.new( :node($/),
+ :name($<top><category> ~ ':' ~ $<top><sym>),
+ :pasttype($<top><pasttype>),
+ :pirop($<top><pirop>),
+ :returns($<top><returns>) );
+ foreach @$/ { $op.push($/); }
+ return $op;
+ }
+}