}
;
+ARTICLE: "peg.ebnf.grouping" "Group"
+"Any sequence of rules may be grouped using parentheses (" { $snippet "()" } "). "
+"The parenthesized sequence can then be modified as a group. Parentheses also "
+"delimit sets of choices separated by pipe (|) characters."
+$nl
+"A group can also be delimited with curly braces (" { $snippet "{}" } "), in "
+"which case an implicit optional whitespace-matching rule will be inserted between "
+"rules sequenced within the braces."
+{ $examples
+ { $example
+ "USING: prettyprint peg.ebnf ;"
+ "\"abcca\" [EBNF rule=\"a\" (\"b\" | \"c\")* \"a\" EBNF] ."
+ "V{ \"a\" V{ \"b\" \"c\" \"c\" } \"a\" }"
+ }
+ { $example
+ "USING: prettyprint peg.ebnf ;"
+ "\"ab c\nd \" [EBNF rule={\"a\" \"b\" \"c\" \"d\"} EBNF] ."
+ "V{ \"a\" \"b\" \"c\" \"d\" }"
+ }
+}
+;
+
ARTICLE: "peg.ebnf.choice" "Choice"
"Any rule element separated by a pipe character (|) is considered a choice. Choices "
"are matched against the input stream in order. If a match succeeds then the remaining "
}
;
+ARTICLE: "peg.ebnf.ignore" "Ignore"
+"Any rule element followed by a tilde (~) will be matched, and its results "
+"discarded from the AST."
+{ $examples
+ { $example
+ "USING: prettyprint peg.ebnf ;"
+ "\"abc\" [EBNF rule=\"a\" \"b\"~ \"c\" EBNF] ."
+ "V{ \"a\" \"c\" }"
+ }
+}
+;
+
ARTICLE: "peg.ebnf.option" "Option"
"Any rule element followed by a question mark (?) is considered optional. The "
"rule is tested against the input. If it succeeds the result is stored in the AST. "
{ $subsections "peg.ebnf.strings"
"peg.ebnf.any"
"peg.ebnf.sequence"
+"peg.ebnf.grouping"
"peg.ebnf.choice"
+"peg.ebnf.ignore"
"peg.ebnf.option"
"peg.ebnf.one-or-more"
"peg.ebnf.zero-or-more"
"one ((two | three) four)*" 'choice' parse
] unit-test
+{
+ T{ ebnf-sequence f
+ V{
+ T{ ebnf-non-terminal f "one" }
+ T{ ebnf-ignore f
+ T{ ebnf-sequence f
+ V{
+ T{ ebnf-choice f
+ V{ T{ ebnf-non-terminal f "two" } T{ ebnf-non-terminal f "three" } }
+ }
+ T{ ebnf-non-terminal f "four" }
+ }
+ }
+ }
+ }
+ }
+} [
+ "one ((two | three) four)~" 'choice' parse
+] unit-test
+
{
T{ ebnf-sequence f
V{
"abc" [EBNF a="a" "b" foo=(a "c") EBNF]
] unit-test
+{ V{ "a" "c" } } [
+ "abc" [EBNF a="a" "b"~ foo=(a "c") EBNF]
+] unit-test
+
+{ V{ V{ "a" V{ "b" "b" } } "c" } } [
+ "abbc" [EBNF a=("a" "b"*) foo=(a "c") EBNF]
+] unit-test
+
+{ V{ "a" "c" } } [
+ "abc" [EBNF a=("a" ("b")~) foo=(a "c") EBNF]
+] unit-test
+
+{ V{ "a" "c" } } [
+ "abc" [EBNF a=("a" "b"~) foo=(a "c") EBNF]
+] unit-test
+
+{ "c" } [
+ "abc" [EBNF a=("a" "b")~ foo=(a "c") EBNF]
+] unit-test
+
{ V{ V{ "a" "b" } "c" } } [
"abc" [EBNF a="a" "b" foo={a "c"} EBNF]
] unit-test
"ab c ab c" [EBNF a="a" "b" foo={a "c"}* EBNF]
] unit-test
+{ V{ V{ "a" "c" } V{ "a" "c" } } } [
+ "ab c ab c" [EBNF a="a" "b"~ foo={a "c"}* EBNF]
+] unit-test
+
+{ V{ } } [
+ "ab c ab c" [EBNF a="a" "b" foo=(a "c")* EBNF]
+] unit-test
+
{ V{ } } [
"ab c ab c" [EBNF a="a" "b" foo=(a "c")* EBNF]
] unit-test
TUPLE: ebnf-ensure-not group ;\r
TUPLE: ebnf-choice options ;\r
TUPLE: ebnf-sequence elements ;\r
+TUPLE: ebnf-ignore group ;\r
TUPLE: ebnf-repeat0 group ;\r
TUPLE: ebnf-repeat1 group ;\r
TUPLE: ebnf-optional group ;\r
C: <ebnf-ensure-not> ebnf-ensure-not\r
C: <ebnf-choice> ebnf-choice\r
C: <ebnf-sequence> ebnf-sequence\r
+C: <ebnf-ignore> ebnf-ignore\r
C: <ebnf-repeat0> ebnf-repeat0\r
C: <ebnf-repeat1> ebnf-repeat1\r
C: <ebnf-optional> ebnf-optional\r
'range-parser' ,\r
'any-character' ,\r
] choice* \r
+ [ dup , "~" token hide , ] seq* [ first <ebnf-ignore> ] action ,\r
[ dup , "*" token hide , ] seq* [ first <ebnf-repeat0> ] action ,\r
[ dup , "+" token hide , ] seq* [ first <ebnf-repeat1> ] action ,\r
[ dup , "?[" token ensure-not , "?" token hide , ] seq* [ first <ebnf-optional> ] action ,\r
: 'group' ( -- parser )\r
#! A grouping with no suffix. Used for precedence.\r
[ ] [\r
+ "~" token sp ensure-not ,\r
"*" token sp ensure-not ,\r
"+" token sp ensure-not ,\r
"?" token sp ensure-not ,\r
] seq* hide grouped ; \r
\r
+: 'ignore' ( -- parser )\r
+ [ <ebnf-ignore> ] "~" syntax grouped ;\r
+\r
: 'repeat0' ( -- parser )\r
[ <ebnf-repeat0> ] "*" syntax grouped ;\r
\r
'ensure' sp ,\r
'element' sp ,\r
'group' sp , \r
+ 'ignore' sp ,\r
'repeat0' sp ,\r
'repeat1' sp ,\r
'optional' sp , \r
M: ebnf-ensure-not (transform) ( ast -- parser )\r
transform-group ensure-not ;\r
\r
+M: ebnf-ignore (transform) ( ast -- parser )\r
+ transform-group [ drop ignore ] action ;\r
+\r
M: ebnf-repeat0 (transform) ( ast -- parser )\r
transform-group repeat0 ;\r
\r
dup remaining>> [ blank? ] trim [\r
[ \r
"Unable to fully parse EBNF. Left to parse was: " %\r
- remaining>> % \r
+ % \r
] "" make throw\r
] unless-empty\r
] [\r