peg.ebnf: add rule~ syntax to discard a matching subrule from the AST; document (grouping) and {grouping} syntax
parent
e4b1417dd2
commit
27f5f07dbc
|
@ -94,6 +94,28 @@ ARTICLE: "peg.ebnf.sequence" "Sequence"
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
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"
|
ARTICLE: "peg.ebnf.choice" "Choice"
|
||||||
"Any rule element separated by a pipe character (|) is considered a choice. Choices "
|
"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 "
|
"are matched against the input stream in order. If a match succeeds then the remaining "
|
||||||
|
@ -117,6 +139,18 @@ ARTICLE: "peg.ebnf.choice" "Choice"
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
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"
|
ARTICLE: "peg.ebnf.option" "Option"
|
||||||
"Any rule element followed by a question mark (?) is considered optional. The "
|
"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. "
|
"rule is tested against the input. If it succeeds the result is stored in the AST. "
|
||||||
|
@ -452,7 +486,9 @@ ARTICLE: "peg.ebnf" "EBNF"
|
||||||
{ $subsections "peg.ebnf.strings"
|
{ $subsections "peg.ebnf.strings"
|
||||||
"peg.ebnf.any"
|
"peg.ebnf.any"
|
||||||
"peg.ebnf.sequence"
|
"peg.ebnf.sequence"
|
||||||
|
"peg.ebnf.grouping"
|
||||||
"peg.ebnf.choice"
|
"peg.ebnf.choice"
|
||||||
|
"peg.ebnf.ignore"
|
||||||
"peg.ebnf.option"
|
"peg.ebnf.option"
|
||||||
"peg.ebnf.one-or-more"
|
"peg.ebnf.one-or-more"
|
||||||
"peg.ebnf.zero-or-more"
|
"peg.ebnf.zero-or-more"
|
||||||
|
|
|
@ -84,6 +84,26 @@ IN: peg.ebnf.tests
|
||||||
"one ((two | three) four)*" 'choice' parse
|
"one ((two | three) four)*" 'choice' parse
|
||||||
] unit-test
|
] 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
|
T{ ebnf-sequence f
|
||||||
V{
|
V{
|
||||||
|
@ -304,6 +324,26 @@ main = Primary
|
||||||
"abc" [EBNF a="a" "b" foo=(a "c") EBNF]
|
"abc" [EBNF a="a" "b" foo=(a "c") EBNF]
|
||||||
] unit-test
|
] 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" } } [
|
{ V{ V{ "a" "b" } "c" } } [
|
||||||
"abc" [EBNF a="a" "b" foo={a "c"} EBNF]
|
"abc" [EBNF a="a" "b" foo={a "c"} EBNF]
|
||||||
] unit-test
|
] unit-test
|
||||||
|
@ -360,6 +400,14 @@ main = Primary
|
||||||
"ab c ab c" [EBNF a="a" "b" foo={a "c"}* EBNF]
|
"ab c ab c" [EBNF a="a" "b" foo={a "c"}* EBNF]
|
||||||
] unit-test
|
] 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{ } } [
|
{ V{ } } [
|
||||||
"ab c ab c" [EBNF a="a" "b" foo=(a "c")* EBNF]
|
"ab c ab c" [EBNF a="a" "b" foo=(a "c")* EBNF]
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
|
@ -61,6 +61,7 @@ TUPLE: ebnf-ensure group ;
|
||||||
TUPLE: ebnf-ensure-not group ;
|
TUPLE: ebnf-ensure-not group ;
|
||||||
TUPLE: ebnf-choice options ;
|
TUPLE: ebnf-choice options ;
|
||||||
TUPLE: ebnf-sequence elements ;
|
TUPLE: ebnf-sequence elements ;
|
||||||
|
TUPLE: ebnf-ignore group ;
|
||||||
TUPLE: ebnf-repeat0 group ;
|
TUPLE: ebnf-repeat0 group ;
|
||||||
TUPLE: ebnf-repeat1 group ;
|
TUPLE: ebnf-repeat1 group ;
|
||||||
TUPLE: ebnf-optional group ;
|
TUPLE: ebnf-optional group ;
|
||||||
|
@ -81,6 +82,7 @@ C: <ebnf-ensure> ebnf-ensure
|
||||||
C: <ebnf-ensure-not> ebnf-ensure-not
|
C: <ebnf-ensure-not> ebnf-ensure-not
|
||||||
C: <ebnf-choice> ebnf-choice
|
C: <ebnf-choice> ebnf-choice
|
||||||
C: <ebnf-sequence> ebnf-sequence
|
C: <ebnf-sequence> ebnf-sequence
|
||||||
|
C: <ebnf-ignore> ebnf-ignore
|
||||||
C: <ebnf-repeat0> ebnf-repeat0
|
C: <ebnf-repeat0> ebnf-repeat0
|
||||||
C: <ebnf-repeat1> ebnf-repeat1
|
C: <ebnf-repeat1> ebnf-repeat1
|
||||||
C: <ebnf-optional> ebnf-optional
|
C: <ebnf-optional> ebnf-optional
|
||||||
|
@ -215,6 +217,7 @@ PEG: escaper ( string -- ast )
|
||||||
'range-parser' ,
|
'range-parser' ,
|
||||||
'any-character' ,
|
'any-character' ,
|
||||||
] choice*
|
] choice*
|
||||||
|
[ dup , "~" token hide , ] seq* [ first <ebnf-ignore> ] action ,
|
||||||
[ dup , "*" token hide , ] seq* [ first <ebnf-repeat0> ] action ,
|
[ dup , "*" token hide , ] seq* [ first <ebnf-repeat0> ] action ,
|
||||||
[ dup , "+" token hide , ] seq* [ first <ebnf-repeat1> ] action ,
|
[ dup , "+" token hide , ] seq* [ first <ebnf-repeat1> ] action ,
|
||||||
[ dup , "?[" token ensure-not , "?" token hide , ] seq* [ first <ebnf-optional> ] action ,
|
[ dup , "?[" token ensure-not , "?" token hide , ] seq* [ first <ebnf-optional> ] action ,
|
||||||
|
@ -257,11 +260,15 @@ DEFER: 'choice'
|
||||||
: 'group' ( -- parser )
|
: 'group' ( -- parser )
|
||||||
#! A grouping with no suffix. Used for precedence.
|
#! A grouping with no suffix. Used for precedence.
|
||||||
[ ] [
|
[ ] [
|
||||||
|
"~" token sp ensure-not ,
|
||||||
"*" token sp ensure-not ,
|
"*" token sp ensure-not ,
|
||||||
"+" token sp ensure-not ,
|
"+" token sp ensure-not ,
|
||||||
"?" token sp ensure-not ,
|
"?" token sp ensure-not ,
|
||||||
] seq* hide grouped ;
|
] seq* hide grouped ;
|
||||||
|
|
||||||
|
: 'ignore' ( -- parser )
|
||||||
|
[ <ebnf-ignore> ] "~" syntax grouped ;
|
||||||
|
|
||||||
: 'repeat0' ( -- parser )
|
: 'repeat0' ( -- parser )
|
||||||
[ <ebnf-repeat0> ] "*" syntax grouped ;
|
[ <ebnf-repeat0> ] "*" syntax grouped ;
|
||||||
|
|
||||||
|
@ -305,6 +312,7 @@ DEFER: 'choice'
|
||||||
'ensure' sp ,
|
'ensure' sp ,
|
||||||
'element' sp ,
|
'element' sp ,
|
||||||
'group' sp ,
|
'group' sp ,
|
||||||
|
'ignore' sp ,
|
||||||
'repeat0' sp ,
|
'repeat0' sp ,
|
||||||
'repeat1' sp ,
|
'repeat1' sp ,
|
||||||
'optional' sp ,
|
'optional' sp ,
|
||||||
|
@ -425,6 +433,9 @@ M: ebnf-ensure (transform) ( ast -- parser )
|
||||||
M: ebnf-ensure-not (transform) ( ast -- parser )
|
M: ebnf-ensure-not (transform) ( ast -- parser )
|
||||||
transform-group ensure-not ;
|
transform-group ensure-not ;
|
||||||
|
|
||||||
|
M: ebnf-ignore (transform) ( ast -- parser )
|
||||||
|
transform-group [ drop ignore ] action ;
|
||||||
|
|
||||||
M: ebnf-repeat0 (transform) ( ast -- parser )
|
M: ebnf-repeat0 (transform) ( ast -- parser )
|
||||||
transform-group repeat0 ;
|
transform-group repeat0 ;
|
||||||
|
|
||||||
|
@ -532,7 +543,7 @@ M: ebnf-non-terminal (transform) ( ast -- parser )
|
||||||
dup remaining>> [ blank? ] trim [
|
dup remaining>> [ blank? ] trim [
|
||||||
[
|
[
|
||||||
"Unable to fully parse EBNF. Left to parse was: " %
|
"Unable to fully parse EBNF. Left to parse was: " %
|
||||||
remaining>> %
|
%
|
||||||
] "" make throw
|
] "" make throw
|
||||||
] unless-empty
|
] unless-empty
|
||||||
] [
|
] [
|
||||||
|
|
Loading…
Reference in New Issue