diff --git a/extra/peg/ebnf/ebnf-tests.factor b/extra/peg/ebnf/ebnf-tests.factor index c9b9f5d977..dea549eb37 100644 --- a/extra/peg/ebnf/ebnf-tests.factor +++ b/extra/peg/ebnf/ebnf-tests.factor @@ -142,4 +142,32 @@ IN: peg.ebnf.tests { f } [ "Z" [EBNF foo=[^A-Z] EBNF] call -] unit-test \ No newline at end of file +] unit-test + +[ + #! Test direct left recursion. Currently left recursion should cause a + #! failure of that parser. + #! Not using packrat, so recursion causes data stack overflow + "1+1" [EBNF num=([0-9])+ expr=expr "+" num | num EBNF] call +] must-fail + +{ V{ 49 } } [ + #! Test direct left recursion. Currently left recursion should cause a + #! failure of that parser. + #! Using packrat, so first part of expr fails, causing 2nd choice to be used + "1+1" [ [EBNF num=([0-9])+ expr=expr "+" num | num EBNF] call ] with-packrat parse-result-ast +] unit-test + +[ + #! Test indirect left recursion. Currently left recursion should cause a + #! failure of that parser. + #! Not using packrat, so recursion causes data stack overflow + "1+1" [EBNF num=([0-9])+ x=expr expr=x "+" num | num EBNF] call +] must-fail + +{ V{ 49 } } [ + #! Test indirect left recursion. Currently left recursion should cause a + #! failure of that parser. + #! Using packrat, so first part of expr fails, causing 2nd choice to be used + "1+1" [ [EBNF num=([0-9])+ x=expr expr=x "+" num | num EBNF] call ] with-packrat parse-result-ast +] unit-test diff --git a/extra/peg/ebnf/ebnf.factor b/extra/peg/ebnf/ebnf.factor index 11e1e2ea64..be4beab3f1 100644 --- a/extra/peg/ebnf/ebnf.factor +++ b/extra/peg/ebnf/ebnf.factor @@ -266,7 +266,7 @@ M: ebnf-non-terminal (transform) ( ast -- parser ) ] [ ] make delay sp ; : transform-ebnf ( string -- object ) - 'ebnf' parse parse-result-ast transform ; + 'ebnf' [ parse ] packrat-parse parse-result-ast transform ; : check-parse-result ( result -- result ) dup [ @@ -281,7 +281,7 @@ M: ebnf-non-terminal (transform) ( ast -- parser ) ] if ; : ebnf>quot ( string -- hashtable quot ) - 'ebnf' parse check-parse-result + 'ebnf' [ parse ] with-packrat check-parse-result parse-result-ast transform dup main swap at compile 1quotation ; : [EBNF "EBNF]" parse-multiline-string ebnf>quot nip parsed ; parsing diff --git a/extra/peg/peg-tests.factor b/extra/peg/peg-tests.factor index bd8abb63e6..cd95bd3b93 100644 --- a/extra/peg/peg-tests.factor +++ b/extra/peg/peg-tests.factor @@ -179,4 +179,20 @@ IN: peg.tests [ "1+1" swap parse parse-result-ast ] with-packrat -] unit-test \ No newline at end of file +] unit-test + +: expr ( -- parser ) + #! Test direct left recursion. Currently left recursion should cause a + #! failure of that parser. + [ expr ] delay "+" token "1" token 3seq "1" token 2choice ; + +[ + #! Not using packrat, so recursion causes data stack overflow + "1+1" expr parse parse-result-ast +] must-fail + +{ "1" } [ + #! Using packrat, so expr fails, causing the 2nd choice to be used. + "1+1" expr [ parse ] with-packrat parse-result-ast +] unit-test + diff --git a/extra/peg/peg.factor b/extra/peg/peg.factor index 1361f9fdbd..e5632d645c 100755 --- a/extra/peg/peg.factor +++ b/extra/peg/peg.factor @@ -39,7 +39,11 @@ GENERIC: (compile) ( parser -- quot ) #! from the input cache. If the item is not in the cache, #! call 'quot' with 'input' on the stack to get the result #! and store that in the cache and return it. - n input-cache [ drop input quot call ] cache ; inline + n input-cache [ + drop + f n input-cache set-at + input quot call + ] cache ; inline :: run-packrat-parser ( input quot c -- result ) input input-from