From 72bfd57f308a6b2efe7c8b9697282eab00588856 Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Tue, 1 Apr 2008 11:28:14 +1300
Subject: [PATCH 01/10] Make ebnf forgiving of whitespace at end of expression

---
 extra/peg/ebnf/ebnf.factor | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor
index 4f00edbd3c..26e5d68df8 100644
--- a/extra/peg/ebnf/ebnf.factor
+++ b/extra/peg/ebnf/ebnf.factor
@@ -320,7 +320,7 @@ M: ebnf-non-terminal (transform) ( ast -- parser )
 
 : check-parse-result ( result -- result )
   dup [
-    dup parse-result-remaining empty? [
+    dup parse-result-remaining [ blank? ] trim empty? [
       [ 
         "Unable to fully parse EBNF. Left to parse was: " %
         parse-result-remaining % 

From 122fd50d4a7fee989bdcf69dc699d7bcf4246600 Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Tue, 1 Apr 2008 14:49:20 +1300
Subject: [PATCH 02/10] Throw error when ebnf uses a non-existant non-terminal

---
 extra/peg/ebnf/ebnf.factor | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor
index 26e5d68df8..a6567ce8f3 100644
--- a/extra/peg/ebnf/ebnf.factor
+++ b/extra/peg/ebnf/ebnf.factor
@@ -310,9 +310,14 @@ M: ebnf-var (transform) ( ast -- parser )
 M: ebnf-terminal (transform) ( ast -- parser )
   symbol>> token sp ;
 
+: parser-not-found ( name -- * )
+  [
+    "Parser " % % " not found." %
+  ] "" make throw ;
+
 M: ebnf-non-terminal (transform) ( ast -- parser )
   symbol>>  [
-    , parser get , \ at , \ sp ,   
+    , \ dup , parser get , \ at , [ parser-not-found ] , \ unless* , \ sp , \ nip ,   
   ] [ ] make box ;
 
 : transform-ebnf ( string -- object )

From 6b454eed36490c35cd928e8b5b932f4e3ba2dc6d Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Wed, 2 Apr 2008 12:59:12 +1300
Subject: [PATCH 03/10] Various peg/ebnf fixes - Box parsers were broken when
 involved in left recursion detection - ebnf no longer implicitly ignores
 white space between terminates/non-terminals - ebnf now handles \t and \n in
 grammars so productions to detect white space work - reset-delegates is now
 reset-pegs

---
 extra/peg/ebnf/ebnf-tests.factor | 53 ++++++++++++++++++++++++++++++--
 extra/peg/ebnf/ebnf.factor       | 13 +++++---
 extra/peg/peg.factor             | 24 +++++++++------
 3 files changed, 74 insertions(+), 16 deletions(-)

diff --git a/extra/peg/ebnf/ebnf-tests.factor b/extra/peg/ebnf/ebnf-tests.factor
index 4f802c5207..84c492c55a 100644
--- a/extra/peg/ebnf/ebnf-tests.factor
+++ b/extra/peg/ebnf/ebnf-tests.factor
@@ -180,6 +180,55 @@ IN: peg.ebnf.tests
   { 1 2 "a" 4 } [EBNF num=. ?[ number? ]? list=list:x num:y => [[ drop x y + ]] | num EBNF] call parse-result-ast
 ] unit-test
 
+{ f } [
+  "ab" [EBNF -=" " | "\t" | "\n" foo="a" - "b" EBNF] call 
+] unit-test
+
+{ V{ "a" " " "b" } } [
+  "a b" [EBNF -=" " | "\t" | "\n" foo="a" - "b" EBNF] call parse-result-ast
+] unit-test
+
+{ V{ "a" "\t" "b" } } [
+  "a\tb" [EBNF -=" " | "\t" | "\n" foo="a" - "b" EBNF] call parse-result-ast 
+] unit-test
+
+{ V{ "a" "\n" "b" } } [
+  "a\nb" [EBNF -=" " | "\t" | "\n" foo="a" - "b" EBNF] call parse-result-ast
+] unit-test
+
+{ V{ "a" f "b" } } [
+  "ab" [EBNF -=" " | "\t" | "\n" foo="a" (-)? "b" EBNF] call parse-result-ast
+] unit-test
+
+{ V{ "a" " " "b" } } [
+  "a b" [EBNF -=" " | "\t" | "\n" foo="a" (-)? "b" EBNF] call parse-result-ast
+] unit-test
+
+
+{ V{ "a" "\t" "b" } } [
+  "a\tb" [EBNF -=" " | "\t" | "\n" foo="a" (-)? "b" EBNF] call parse-result-ast
+] unit-test
+
+{ V{ "a" "\n" "b" } } [
+  "a\nb" [EBNF -=" " | "\t" | "\n" foo="a" (-)? "b" EBNF] call parse-result-ast
+] unit-test
+
+{ V{ "a" "b" } } [
+  "ab" [EBNF -=(" " | "\t" | "\n")? => [[ drop ignore ]] foo="a" - "b" EBNF] call parse-result-ast
+] unit-test
+
+{ V{ "a" "b" } } [
+  "a\tb" [EBNF -=(" " | "\t" | "\n")? => [[ drop ignore ]] foo="a" - "b" EBNF] call parse-result-ast
+] unit-test
+
+{ V{ "a" "b" } } [
+  "a\nb" [EBNF -=(" " | "\t" | "\n")? => [[ drop ignore ]] foo="a" - "b" EBNF] call parse-result-ast
+] unit-test
+
+{ f } [
+  "axb" [EBNF -=(" " | "\t" | "\n")? => [[ drop ignore ]] foo="a" - "b" EBNF] call 
+] unit-test
+
 { V{ V{ 49 } "+" V{ 49 } } } [ 
   #! Test direct left recursion. 
   #! Using packrat, so first part of expr fails, causing 2nd choice to be used  
@@ -200,7 +249,7 @@ IN: peg.ebnf.tests
 
 EBNF: primary 
 Primary = PrimaryNoNewArray
-PrimaryNoNewArray =  ClassInstanceCreationExpression 
+PrimaryNoNewArray =  ClassInstanceCreationExpression
                    | MethodInvocation
                    | FieldAccess
                    | ArrayAccess
@@ -211,7 +260,7 @@ MethodInvocation =  Primary "." MethodName "(" ")"
                   | MethodName "(" ")"
 FieldAccess =  Primary "." Identifier
              | "super" "." Identifier  
-ArrayAccess =  Primary "[" Expression "]"
+ArrayAccess =  Primary "[" Expression "]" 
              | ExpressionName "[" Expression "]"
 ClassOrInterfaceType = ClassName | InterfaceTypeName
 ClassName = "C" | "D"
diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor
index a6567ce8f3..a4e4fe387d 100644
--- a/extra/peg/ebnf/ebnf.factor
+++ b/extra/peg/ebnf/ebnf.factor
@@ -3,7 +3,7 @@
 USING: kernel compiler.units parser words arrays strings math.parser sequences 
        quotations vectors namespaces math assocs continuations peg
        peg.parsers unicode.categories multiline combinators.lib 
-       splitting accessors effects sequences.deep ;
+       splitting accessors effects sequences.deep peg.search ;
 IN: peg.ebnf
 
 TUPLE: ebnf-non-terminal symbol ;
@@ -308,7 +308,7 @@ M: ebnf-var (transform) ( ast -- parser )
   dup vars get push [ dupd set ] curry action ;
 
 M: ebnf-terminal (transform) ( ast -- parser )
-  symbol>> token sp ;
+  symbol>> token ;
 
 : parser-not-found ( name -- * )
   [
@@ -317,7 +317,7 @@ M: ebnf-terminal (transform) ( ast -- parser )
 
 M: ebnf-non-terminal (transform) ( ast -- parser )
   symbol>>  [
-    , \ dup , parser get , \ at , [ parser-not-found ] , \ unless* , \ sp , \ nip ,   
+    , \ dup , parser get , \ at , [ parser-not-found ] , \ unless* , \ nip ,    
   ] [ ] make box ;
 
 : transform-ebnf ( string -- object )
@@ -340,10 +340,13 @@ M: ebnf-non-terminal (transform) ( ast -- parser )
   parse-result-ast transform dup dup parser [ main swap at compile ] with-variable
   [ compiled-parse ] curry [ with-scope ] curry ;
 
-: [EBNF "EBNF]" parse-multiline-string ebnf>quot nip parsed ; parsing
+: replace-escapes ( string -- string )
+  "\\t" token [ drop "\t" ] action  "\\n" token [ drop "\n" ] action 2choice replace ;
+
+: [EBNF "EBNF]" parse-multiline-string replace-escapes ebnf>quot nip parsed ; parsing
 
 : EBNF: 
   CREATE-WORD dup 
-  ";EBNF" parse-multiline-string
+  ";EBNF" parse-multiline-string replace-escapes
   ebnf>quot swapd 1 1 <effect> define-declared "ebnf-parser" set-word-prop ; parsing
 
diff --git a/extra/peg/peg.factor b/extra/peg/peg.factor
index 9e35c5b9be..ad821635d7 100755
--- a/extra/peg/peg.factor
+++ b/extra/peg/peg.factor
@@ -30,6 +30,14 @@ SYMBOL: fail
 SYMBOL: lrstack
 SYMBOL: heads
 
+: delegates ( -- cache )
+  \ delegates get-global [ H{ } clone dup \ delegates set-global ] unless* ;
+
+: reset-pegs ( -- )
+  H{ } clone \ delegates set-global ;
+
+reset-pegs 
+
 TUPLE: memo-entry ans pos ;
 C: <memo-entry> memo-entry
 
@@ -253,14 +261,6 @@ SYMBOL: id
     1 id set-global 0
   ] if* ;
 
-: delegates ( -- cache )
-  \ delegates get-global [ H{ } clone dup \ delegates set-global ] unless* ;
-
-: reset-delegates ( -- )
-  H{ } clone \ delegates set-global ;
-
-reset-delegates 
-
 : init-parser ( parser -- parser )
   #! Set the delegate for the parser. Equivalent parsers
   #! get a delegate with the same id.
@@ -590,7 +590,13 @@ PRIVATE>
   #! not a cached one. This is because the same box,
   #! compiled twice can have a different compiled word
   #! due to running at compile time.
-  box-parser construct-boa next-id f <parser> over set-delegate ;
+  #! Why the [ ] action at the end? Box parsers don't get
+  #! memoized during parsing due to all box parsers being
+  #! unique. This breaks left recursion detection during the
+  #! parse. The action adds an indirection with a parser type
+  #! that gets memoized and fixes this. Need to rethink how
+  #! to fix boxes so this isn't needed...
+  box-parser construct-boa next-id f <parser> over set-delegate [ ] action ;
 
 : PEG:
   (:) [

From 1b58ba404ec22cef9d8713369c6aa4fa47387864 Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Wed, 2 Apr 2008 13:50:29 +1300
Subject: [PATCH 04/10] Fix peg.pl0 test failures

---
 extra/peg/pl0/pl0-tests.factor | 47 +++++++++++++++++++++++++++++++++-
 extra/peg/pl0/pl0.factor       | 26 ++++++++++---------
 2 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/extra/peg/pl0/pl0-tests.factor b/extra/peg/pl0/pl0-tests.factor
index b3d2135da7..1ed528d05d 100644
--- a/extra/peg/pl0/pl0-tests.factor
+++ b/extra/peg/pl0/pl0-tests.factor
@@ -1,9 +1,54 @@
 ! Copyright (C) 2007 Chris Double.
 ! See http://factorcode.org/license.txt for BSD license.
 !
-USING: kernel tools.test peg peg.pl0 multiline sequences ;
+USING: kernel tools.test peg peg.pl0 multiline sequences words assocs ;
 IN: peg.pl0.tests
 
+{ f } [
+  "CONST foo = 1;" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+] unit-test
+
+{ f } [
+  "VAR foo;" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+] unit-test
+
+{ f } [
+  "VAR foo,bar , baz;" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+] unit-test
+
+{ f } [
+  "foo := 5;" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+] unit-test
+
+{ f } [
+  "BEGIN foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+] unit-test
+
+{ f } [
+  "IF 1=1 THEN foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+] unit-test
+
+{ f } [
+  "WHILE 1=1 DO foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+] unit-test
+
+{ f } [
+  "WHILE ODD 1=1 DO foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+] unit-test
+
+{ f } [
+  "PROCEDURE square; BEGIN squ=x*x END" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+] unit-test
+
+{ f } [
+  <"
+PROCEDURE square; 
+BEGIN 
+  squ := x * x 
+END;
+"> \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+] unit-test
+
 { t } [
   <"
 VAR x, squ;
diff --git a/extra/peg/pl0/pl0.factor b/extra/peg/pl0/pl0.factor
index f7eb3cad23..8025728285 100644
--- a/extra/peg/pl0/pl0.factor
+++ b/extra/peg/pl0/pl0.factor
@@ -7,18 +7,20 @@ IN: peg.pl0
 #! Grammar for PL/0 based on http://en.wikipedia.org/wiki/PL/0
 
 EBNF: pl0 
-block = ( "CONST" ident "=" number ( "," ident "=" number )* ";" )?
-        ( "VAR" ident ( "," ident )* ";" )?
-        ( "PROCEDURE" ident ";" ( block ";" )? )* statement 
-statement = ( ident ":=" expression | "CALL" ident |
-              "BEGIN" statement (";" statement )* "END" |
-              "IF" condition "THEN" statement |
-              "WHILE" condition "DO" statement )?
-condition = "ODD" expression |
-            expression ("=" | "#" | "<=" | "<" | ">=" | ">") expression 
-expression = ("+" | "-")? term (("+" | "-") term )* 
-term = factor (("*" | "/") factor )* 
-factor = ident | number | "(" expression ")"
+- = (" " | "\t" | "\n")+ => [[ drop ignore ]]
+_ = (" " | "\t" | "\n")* => [[ drop ignore ]]
+block = ( _ "CONST" - ident _ "=" _ number ( _ "," _ ident _ "=" _ number )* _ ";" )?
+        ( _ "VAR" - ident ( _ "," _ ident )* _ ";" )?
+        ( _ "PROCEDURE" - ident _ ";" ( _ block _ ";" )? )* _ statement
+statement = ( ident _ ":=" _ expression | "CALL" - ident |
+              "BEGIN" - statement ( _ ";" _ statement )* _ "END" |
+              "IF" - condition _ "THEN" - statement |
+              "WHILE" - condition _ "DO" - statement )?
+condition = "ODD" - expression |
+            expression _ ("=" | "#" | "<=" | "<" | ">=" | ">") _ expression
+expression = ("+" | "-")? term ( _ ("+" | "-") _ term )* 
+term = factor ( _ ("*" | "/") _ factor )* 
+factor = ident | number | "(" _ expression _ ")"
 ident = (([a-zA-Z])+) [[ >string ]]
 digit = ([0-9]) [[ digit> ]]
 number = ((digit)+) [[ unclip [ swap 10 * + ] reduce ]]

From bbcc84862f5e2ee038011886b330c3c655e754d4 Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Wed, 2 Apr 2008 15:47:21 +1300
Subject: [PATCH 05/10] Tweak ast from sequences in ebnf

---
 extra/peg/ebnf/ebnf.factor | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor
index a4e4fe387d..7c5854cd7d 100644
--- a/extra/peg/ebnf/ebnf.factor
+++ b/extra/peg/ebnf/ebnf.factor
@@ -252,7 +252,7 @@ M: ebnf-rule (transform) ( ast -- parser )
   ] keep ;
 
 M: ebnf-sequence (transform) ( ast -- parser )
-  elements>> [ (transform) ] map seq ;
+  elements>> [ (transform) ] map seq [ dup length 1 = [ first ] when ] action ;
 
 M: ebnf-choice (transform) ( ast -- parser )
   options>> [ (transform) ] map choice ;

From 34a1505d95891fd516e4f5b176d937fe4641dd8a Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Wed, 2 Apr 2008 15:47:30 +1300
Subject: [PATCH 06/10] PL0 whitespace handling improvement

---
 extra/peg/pl0/pl0-tests.factor | 36 +++++++++----------
 extra/peg/pl0/pl0.factor       | 64 +++++++++++++++++++++++++---------
 2 files changed, 65 insertions(+), 35 deletions(-)

diff --git a/extra/peg/pl0/pl0-tests.factor b/extra/peg/pl0/pl0-tests.factor
index 1ed528d05d..039f66637d 100644
--- a/extra/peg/pl0/pl0-tests.factor
+++ b/extra/peg/pl0/pl0-tests.factor
@@ -4,40 +4,40 @@
 USING: kernel tools.test peg peg.pl0 multiline sequences words assocs ;
 IN: peg.pl0.tests
 
-{ f } [
-  "CONST foo = 1;" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+{ t } [
+  "CONST foo = 1;" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty? 
 ] unit-test
 
-{ f } [
-  "VAR foo;" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+{ t } [
+  "VAR foo;" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty?
 ] unit-test
 
-{ f } [
-  "VAR foo,bar , baz;" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+{ t } [
+  "VAR foo,bar , baz;" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty? 
 ] unit-test
 
-{ f } [
-  "foo := 5;" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+{ t } [
+  "foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
 ] unit-test
 
-{ f } [
-  "BEGIN foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+{ t } [
+  "BEGIN foo := 5 END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
 ] unit-test
 
-{ f } [
-  "IF 1=1 THEN foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+{ t } [
+  "IF 1=1 THEN foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
 ] unit-test
 
-{ f } [
-  "WHILE 1=1 DO foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+{ t } [
+  "WHILE 1=1 DO foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
 ] unit-test
 
-{ f } [
-  "WHILE ODD 1=1 DO foo := 5; END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse not 
+{ t } [
+  "WHILE ODD 1 DO foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
 ] unit-test
 
-{ f } [
-  "PROCEDURE square; BEGIN squ=x*x END" \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+{ t } [
+  "PROCEDURE square; BEGIN squ:=x*x END" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty? 
 ] unit-test
 
 { f } [
diff --git a/extra/peg/pl0/pl0.factor b/extra/peg/pl0/pl0.factor
index 8025728285..1b97814ca7 100644
--- a/extra/peg/pl0/pl0.factor
+++ b/extra/peg/pl0/pl0.factor
@@ -7,22 +7,52 @@ IN: peg.pl0
 #! Grammar for PL/0 based on http://en.wikipedia.org/wiki/PL/0
 
 EBNF: pl0 
-- = (" " | "\t" | "\n")+ => [[ drop ignore ]]
 _ = (" " | "\t" | "\n")* => [[ drop ignore ]]
-block = ( _ "CONST" - ident _ "=" _ number ( _ "," _ ident _ "=" _ number )* _ ";" )?
-        ( _ "VAR" - ident ( _ "," _ ident )* _ ";" )?
-        ( _ "PROCEDURE" - ident _ ";" ( _ block _ ";" )? )* _ statement
-statement = ( ident _ ":=" _ expression | "CALL" - ident |
-              "BEGIN" - statement ( _ ";" _ statement )* _ "END" |
-              "IF" - condition _ "THEN" - statement |
-              "WHILE" - condition _ "DO" - statement )?
-condition = "ODD" - expression |
-            expression _ ("=" | "#" | "<=" | "<" | ">=" | ">") _ expression
-expression = ("+" | "-")? term ( _ ("+" | "-") _ term )* 
-term = factor ( _ ("*" | "/") _ factor )* 
-factor = ident | number | "(" _ expression _ ")"
-ident = (([a-zA-Z])+) [[ >string ]]
-digit = ([0-9]) [[ digit> ]]
-number = ((digit)+) [[ unclip [ swap 10 * + ] reduce ]]
-program = block "."
+
+BEGIN       = "BEGIN" _
+CALL        = "CALL" _
+CONST       = "CONST" _
+DO          = "DO" _
+END         = "END" _
+IF          = "IF" _
+THEN        = "THEN" _
+ODD         = "ODD" _
+PROCEDURE   = "PROCEDURE" _
+VAR         = "VAR" _
+WHILE       = "WHILE" _
+EQ          = "=" _
+LTEQ        = "<=" _
+LT          = "<" _
+GT          = ">" _
+GTEQ        = ">=" _
+NEQ         = "#" _
+COMMA       = "," _
+SEMICOLON   = ";" _
+ASSIGN      = ":=" _
+
+ADD         = "+" _
+SUBTRACT    = "-" _
+MULTIPLY    = "*" _
+DIVIDE      = "/" _
+
+LPAREN      = "(" _
+RPAREN      = ")" _
+
+block       =  ( CONST ident EQ number ( COMMA ident EQ number )* SEMICOLON )? 
+               ( VAR ident ( COMMA ident )* SEMICOLON )? 
+               ( PROCEDURE ident SEMICOLON ( block SEMICOLON )? )* statement 
+statement   =  (  ident ASSIGN expression 
+                | CALL ident 
+                | BEGIN statement ( SEMICOLON statement )* END 
+                | IF condition THEN statement 
+                | WHILE condition DO statement )?  
+condition   =  ODD expression 
+             | expression (EQ | NEQ | LTEQ | LT | GTEQ | GT) expression
+expression  = (ADD | SUBTRACT)? term ( (ADD | SUBTRACT) term )* _
+term        = factor ( (MULTIPLY | DIVIDE) factor )* 
+factor      = ident | number | LPAREN expression RPAREN
+ident       = (([a-zA-Z])+) _ => [[ >string ]]
+digit       = ([0-9])         => [[ digit> ]]
+number      = ((digit)+) _    => [[ 10 digits>integer ]]
+program     = _ block "."
 ;EBNF

From eac450bdcf28773813552170bd1091e13148202b Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Wed, 2 Apr 2008 15:55:18 +1300
Subject: [PATCH 07/10] Add ebnf rule word

---
 extra/peg/ebnf/ebnf.factor     |  3 +++
 extra/peg/pl0/pl0-tests.factor | 29 ++++++++++-------------------
 2 files changed, 13 insertions(+), 19 deletions(-)

diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor
index 7c5854cd7d..b0dfaad5b3 100644
--- a/extra/peg/ebnf/ebnf.factor
+++ b/extra/peg/ebnf/ebnf.factor
@@ -350,3 +350,6 @@ M: ebnf-non-terminal (transform) ( ast -- parser )
   ";EBNF" parse-multiline-string replace-escapes
   ebnf>quot swapd 1 1 <effect> define-declared "ebnf-parser" set-word-prop ; parsing
 
+: rule ( name word -- parser )
+  #! Given an EBNF word produced from EBNF: return the EBNF rule
+  "ebnf-parser" word-prop at ;
\ No newline at end of file
diff --git a/extra/peg/pl0/pl0-tests.factor b/extra/peg/pl0/pl0-tests.factor
index 039f66637d..88993c354b 100644
--- a/extra/peg/pl0/pl0-tests.factor
+++ b/extra/peg/pl0/pl0-tests.factor
@@ -1,52 +1,43 @@
 ! Copyright (C) 2007 Chris Double.
 ! See http://factorcode.org/license.txt for BSD license.
 !
-USING: kernel tools.test peg peg.pl0 multiline sequences words assocs ;
+USING: kernel tools.test peg peg.ebnf peg.pl0 multiline sequences ;
 IN: peg.pl0.tests
 
 { t } [
-  "CONST foo = 1;" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty? 
+  "CONST foo = 1;" "block" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [
-  "VAR foo;" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty?
+  "VAR foo;" "block" \ pl0 rule parse parse-result-remaining empty?
 ] unit-test
 
 { t } [
-  "VAR foo,bar , baz;" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty? 
+  "VAR foo,bar , baz;" "block" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [
-  "foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
+  "foo := 5" "statement" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [
-  "BEGIN foo := 5 END" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
+  "BEGIN foo := 5 END" "statement" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [
-  "IF 1=1 THEN foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
+  "IF 1=1 THEN foo := 5" "statement" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [
-  "WHILE 1=1 DO foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
+  "WHILE 1=1 DO foo := 5" "statement" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [
-  "WHILE ODD 1 DO foo := 5" \ pl0 "ebnf-parser" word-prop "statement" swap at parse parse-result-remaining empty? 
+  "WHILE ODD 1 DO foo := 5" "statement" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [
-  "PROCEDURE square; BEGIN squ:=x*x END" \ pl0 "ebnf-parser" word-prop "block" swap at parse parse-result-remaining empty? 
-] unit-test
-
-{ f } [
-  <"
-PROCEDURE square; 
-BEGIN 
-  squ := x * x 
-END;
-"> \ pl0 "ebnf-parser" word-prop "block" swap at parse not 
+  "PROCEDURE square; BEGIN squ:=x*x END" "block" \ pl0 rule parse parse-result-remaining empty? 
 ] unit-test
 
 { t } [

From 27f2992dc5eca644fb077017746243b5f34e4cf2 Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Thu, 3 Apr 2008 16:09:03 +1300
Subject: [PATCH 08/10] Add failing ebnf test

---
 extra/peg/ebnf/ebnf-tests.factor | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/extra/peg/ebnf/ebnf-tests.factor b/extra/peg/ebnf/ebnf-tests.factor
index 84c492c55a..0879ecda49 100644
--- a/extra/peg/ebnf/ebnf-tests.factor
+++ b/extra/peg/ebnf/ebnf-tests.factor
@@ -1,7 +1,7 @@
 ! Copyright (C) 2007 Chris Double.
 ! See http://factorcode.org/license.txt for BSD license.
 !
-USING: kernel tools.test peg peg.ebnf words math math.parser ;
+USING: kernel tools.test peg peg.ebnf words math math.parser sequences ;
 IN: peg.ebnf.tests
 
 { T{ ebnf-non-terminal f "abc" } } [
@@ -247,6 +247,10 @@ IN: peg.ebnf.tests
   "1+1+1" [EBNF num=([0-9])+ x=expr expr=x "+" num | num EBNF] call parse-result-ast
 ] unit-test
 
+{ t } [
+  "abcd='9' | ('8'):x => [[ drop x ]]" 'ebnf' parse parse-result-remaining empty?
+] unit-test
+
 EBNF: primary 
 Primary = PrimaryNoNewArray
 PrimaryNoNewArray =  ClassInstanceCreationExpression

From cc7d945a80273d4ce966d307424a4f66e72e32ae Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Thu, 3 Apr 2008 17:28:09 +1300
Subject: [PATCH 09/10] Change ebnf variables to not use namespaces

---
 extra/peg/ebnf/ebnf.factor | 55 +++++++++++++++++++++++++-------------
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor
index b0dfaad5b3..49c2d5a8dd 100644
--- a/extra/peg/ebnf/ebnf.factor
+++ b/extra/peg/ebnf/ebnf.factor
@@ -237,17 +237,16 @@ GENERIC: (transform) ( ast -- parser )
 
 SYMBOL: parser
 SYMBOL: main
-SYMBOL: vars
 
 : transform ( ast -- object )
-  H{ } clone dup dup [ parser set V{ } vars set swap (transform) main set ] bind ;
+  H{ } clone dup dup [ parser set swap (transform) main set ] bind ;
 
 M: ebnf (transform) ( ast -- parser )
   rules>> [ (transform) ] map peek ;
   
 M: ebnf-rule (transform) ( ast -- parser )
   dup elements>> 
-  vars get clone vars [ (transform) ] with-variable [
+  (transform) [
     swap symbol>> set
   ] keep ;
 
@@ -282,30 +281,50 @@ M: ebnf-repeat1 (transform) ( ast -- parser )
 M: ebnf-optional (transform) ( ast -- parser )
   transform-group optional ;
 
-: build-locals ( string vars -- string )
-  dup empty? [
-    drop
-  ] [
+GENERIC: build-locals ( code ast -- code )
+
+M: ebnf-sequence build-locals ( code ast -- code )
+  elements>> dup [ ebnf-var? ] subset empty? [
+    drop 
+  ] [ 
     [
-      "USING: locals namespaces ;  [let* | " %
-      [ dup % " [ \"" % % "\" get ] " % ] each
-      " | " %
-      %  
-      " ] with-locals" %     
+      "USING: locals sequences ;  [let* | " %
+        dup length swap [
+          dup ebnf-var? [
+            name>> % 
+            " [ " % # " over nth ] " %
+          ] [
+            2drop
+          ] if
+        ] 2each
+        " | " %
+        %  
+        " ] with-locals" %     
     ] "" make 
   ] if ;
 
+M: ebnf-var build-locals ( code ast -- )
+  [
+    "USING: locals kernel ;  [let* | " %
+    name>> % " [ dup ] " %
+    " | " %
+    %  
+    " ] with-locals" %     
+  ] "" make ;
+
+M: object build-locals ( code ast -- )
+  drop ;
+   
 M: ebnf-action (transform) ( ast -- parser )
-  [ parser>> (transform) ] keep
-  code>> vars get build-locals string-lines [ parse-lines ] with-compilation-unit action ;
+  [ parser>> (transform) ] [ code>> ] [ parser>> ] tri build-locals 
+  string-lines [ parse-lines ] with-compilation-unit action ;
 
 M: ebnf-semantic (transform) ( ast -- parser )
-  [ parser>> (transform) ] keep
-  code>> vars get build-locals string-lines [ parse-lines ] with-compilation-unit semantic ;
+  [ parser>> (transform) ] [ code>> ] [ parser>> ] tri build-locals 
+  string-lines [ parse-lines ] with-compilation-unit semantic ;
 
 M: ebnf-var (transform) ( ast -- parser )
-  [ parser>> (transform) ] [ name>> ] bi 
-  dup vars get push [ dupd set ] curry action ;
+  parser>> (transform) ;
 
 M: ebnf-terminal (transform) ( ast -- parser )
   symbol>> token ;

From 970f0055c266ab813c177b4c4f545e51ea203479 Mon Sep 17 00:00:00 2001
From: Chris Double <chris@bethia.(none)>
Date: Thu, 3 Apr 2008 17:33:37 +1300
Subject: [PATCH 10/10] Fix failing ebnf unit test

---
 extra/peg/ebnf/ebnf.factor | 1 +
 1 file changed, 1 insertion(+)

diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor
index 49c2d5a8dd..e5787e6cf8 100644
--- a/extra/peg/ebnf/ebnf.factor
+++ b/extra/peg/ebnf/ebnf.factor
@@ -213,6 +213,7 @@ DEFER: 'choice'
 : 'actioned-sequence' ( -- parser )
   [
     [ 'sequence' , "=>" syntax , 'action' , ] seq* [ first2 <ebnf-action> ] action ,
+    [ 'sequence' , ":" syntax , "a-zA-Z" range-pattern repeat1 [ >string ] action , "=>" syntax , 'action' , ] seq* [ first3 >r <ebnf-var> r> <ebnf-action> ] action ,
     [ 'sequence' , ":" syntax , "a-zA-Z" range-pattern repeat1 [ >string ] action , ] seq* [ first2 <ebnf-var> ] action ,
     'sequence' ,
   ] choice* ;