Add grammar! support for choice operator
This commit is contained in:
+29
-6
@@ -18,6 +18,7 @@ struct Rule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Term {
|
enum Term {
|
||||||
|
Choice(Vec<Term>),
|
||||||
Literal(LitStr),
|
Literal(LitStr),
|
||||||
Rule(Ident),
|
Rule(Ident),
|
||||||
Sequence(Vec<Term>),
|
Sequence(Vec<Term>),
|
||||||
@@ -42,8 +43,7 @@ impl Parse for Rule {
|
|||||||
|
|
||||||
impl Parse for Term {
|
impl Parse for Term {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let mut terms = vec![];
|
fn parse_atom(input: ParseStream) -> syn::Result<Term> {
|
||||||
let parse_one = || {
|
|
||||||
let look = input.lookahead1();
|
let look = input.lookahead1();
|
||||||
if look.peek(Ident) {
|
if look.peek(Ident) {
|
||||||
input.parse().map(Term::Rule)
|
input.parse().map(Term::Rule)
|
||||||
@@ -52,10 +52,25 @@ impl Parse for Term {
|
|||||||
} else {
|
} else {
|
||||||
Err(look.error())
|
Err(look.error())
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
terms.push(parse_one()?);
|
|
||||||
|
fn parse_term(input: ParseStream) -> syn::Result<Term> {
|
||||||
|
let mut choices = vec![parse_atom(input)?];
|
||||||
|
while input.peek(Token![/]) {
|
||||||
|
input.parse::<Token![/]>()?;
|
||||||
|
choices.push(parse_atom(input)?);
|
||||||
|
}
|
||||||
|
if choices.len() == 1 {
|
||||||
|
Ok(choices.pop().unwrap())
|
||||||
|
} else {
|
||||||
|
Ok(Term::Choice(choices))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_sequence(input: ParseStream) -> syn::Result<Term> {
|
||||||
|
let mut terms = vec![parse_term(input)?];
|
||||||
while !input.is_empty() && !input.peek(Token![;]) {
|
while !input.is_empty() && !input.peek(Token![;]) {
|
||||||
terms.push(parse_one()?);
|
terms.push(parse_term(input)?);
|
||||||
}
|
}
|
||||||
if terms.len() == 1 {
|
if terms.len() == 1 {
|
||||||
Ok(terms.pop().unwrap())
|
Ok(terms.pop().unwrap())
|
||||||
@@ -63,6 +78,9 @@ impl Parse for Term {
|
|||||||
Ok(Term::Sequence(terms))
|
Ok(Term::Sequence(terms))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parse_sequence(input)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Term {
|
impl Term {
|
||||||
@@ -103,13 +121,18 @@ impl Term {
|
|||||||
#(#parts)*
|
#(#parts)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Term::Choice(terms) => terms
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.generate_wrapped_code())
|
||||||
|
.reduce(|x, y| quote! { #x || #y })
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_wrapped_code(&self) -> proc_macro2::TokenStream {
|
fn generate_wrapped_code(&self) -> proc_macro2::TokenStream {
|
||||||
let code = self.generate_code();
|
let code = self.generate_code();
|
||||||
match self {
|
match self {
|
||||||
Term::Sequence(_) => quote! {
|
Term::Sequence(_) | Term::Choice(_) => quote! {
|
||||||
(|| {
|
(|| {
|
||||||
#code
|
#code
|
||||||
})()
|
})()
|
||||||
|
|||||||
+3
-3
@@ -70,8 +70,8 @@ mod tests {
|
|||||||
|
|
||||||
grammar! {
|
grammar! {
|
||||||
fake_csv = line "\n" line;
|
fake_csv = line "\n" line;
|
||||||
line = foo "," foo;
|
line = item "," item;
|
||||||
foo = "foo";
|
item = "foo" / "bar";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -84,7 +84,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generated() {
|
fn test_generated() {
|
||||||
let mut p = ParseState::new("foo,foo\nfoo,foo");
|
let mut p = ParseState::new("foo,bar\nbar,foo");
|
||||||
assert!(fake_csv(&mut p));
|
assert!(fake_csv(&mut p));
|
||||||
let mut p = ParseState::new("foo,foo\nfoo;foo");
|
let mut p = ParseState::new("foo,foo\nfoo;foo");
|
||||||
assert!(!fake_csv(&mut p));
|
assert!(!fake_csv(&mut p));
|
||||||
|
|||||||
Reference in New Issue
Block a user