Add support for compound groups to grammar! macro
This commit is contained in:
+20
-10
@@ -3,7 +3,7 @@ use std::ops::RangeInclusive;
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
Ident, LitChar, LitStr, Token,
|
Ident, LitChar, LitStr, Token, parenthesized,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
parse_macro_input,
|
parse_macro_input,
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
@@ -18,9 +18,10 @@ struct Rule {
|
|||||||
definition: Term,
|
definition: Term,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum Term {
|
enum Term {
|
||||||
Choice(Vec<Term>),
|
Choice(Vec<Term>),
|
||||||
Literal(LitStr),
|
Literal(String),
|
||||||
Optional(Box<Term>),
|
Optional(Box<Term>),
|
||||||
Plus(Box<Term>),
|
Plus(Box<Term>),
|
||||||
Range(RangeInclusive<char>),
|
Range(RangeInclusive<char>),
|
||||||
@@ -60,9 +61,13 @@ impl Parse for Term {
|
|||||||
if look.peek(Ident) {
|
if look.peek(Ident) {
|
||||||
input.parse().map(Term::Rule)
|
input.parse().map(Term::Rule)
|
||||||
} else if look.peek(LitStr) {
|
} else if look.peek(LitStr) {
|
||||||
input.parse().map(Term::Literal)
|
input.parse::<LitStr>().map(|x| Term::Literal(x.value()))
|
||||||
} else if look.peek(LitChar) {
|
} else if look.peek(LitChar) {
|
||||||
parse_range(input)
|
parse_range(input)
|
||||||
|
} else if look.peek(syn::token::Paren) {
|
||||||
|
let content;
|
||||||
|
parenthesized!(content in input);
|
||||||
|
parse_choice(&content)
|
||||||
} else {
|
} else {
|
||||||
Err(look.error())
|
Err(look.error())
|
||||||
}
|
}
|
||||||
@@ -86,7 +91,7 @@ impl Parse for Term {
|
|||||||
|
|
||||||
fn parse_sequence(input: ParseStream) -> syn::Result<Term> {
|
fn parse_sequence(input: ParseStream) -> syn::Result<Term> {
|
||||||
let mut terms = vec![parse_repeat(input)?];
|
let mut terms = vec![parse_repeat(input)?];
|
||||||
while input.peek(Ident) || input.peek(LitStr) || input.peek(LitChar) {
|
while !input.is_empty() && !input.peek(Token![/]) && !input.peek(Token![;]) {
|
||||||
terms.push(parse_repeat(input)?);
|
terms.push(parse_repeat(input)?);
|
||||||
}
|
}
|
||||||
if terms.len() == 1 {
|
if terms.len() == 1 {
|
||||||
@@ -140,15 +145,20 @@ impl Term {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Term::Choice(terms) => terms
|
Term::Choice(terms) => {
|
||||||
.iter()
|
let code = terms
|
||||||
.map(|t| t.generate_code())
|
.iter()
|
||||||
.reduce(|x, y| quote! { #x || #y })
|
.map(|t| t.generate_code())
|
||||||
.unwrap(),
|
.reduce(|x, y| quote! { #x || #y })
|
||||||
|
.unwrap();
|
||||||
|
quote! {
|
||||||
|
( #code )
|
||||||
|
}
|
||||||
|
}
|
||||||
Term::Optional(term) => {
|
Term::Optional(term) => {
|
||||||
let expr = term.generate_code();
|
let expr = term.generate_code();
|
||||||
quote! {
|
quote! {
|
||||||
(#expr || true)
|
( #expr || true )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Term::Star(term) => {
|
Term::Star(term) => {
|
||||||
|
|||||||
+27
-1
@@ -64,7 +64,11 @@ mod tests {
|
|||||||
|
|
||||||
word_head = "_" / 'A'..'Z' / 'a'..'z';
|
word_head = "_" / 'A'..'Z' / 'a'..'z';
|
||||||
word_tail = word_head / '0'..'9';
|
word_tail = word_head / '0'..'9';
|
||||||
word = "(" word_head word_tail* ")";
|
plain_word = word_head word_tail*;
|
||||||
|
word = "(" plain_word ")";
|
||||||
|
|
||||||
|
words = "(" (plain_word ("," plain_word)*)? ")";
|
||||||
|
nested_choice = "(" ("a" / "b") ("c" / "d") ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse<T: Fn(&mut ParseState) -> bool>(rule: T, s: &str) -> bool {
|
fn parse<T: Fn(&mut ParseState) -> bool>(rule: T, s: &str) -> bool {
|
||||||
@@ -105,4 +109,26 @@ mod tests {
|
|||||||
assert!(!parse(word, "(123abc)"));
|
assert!(!parse(word, "(123abc)"));
|
||||||
assert!(!parse(word, "(foo-bar)"));
|
assert!(!parse(word, "(foo-bar)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_groups() {
|
||||||
|
assert!(parse(words, "()"));
|
||||||
|
assert!(parse(words, "(foo)"));
|
||||||
|
assert!(parse(words, "(foo,bar,baz)"));
|
||||||
|
assert!(!parse(words, "(foo,)"));
|
||||||
|
assert!(!parse(words, "(,bar)"));
|
||||||
|
assert!(!parse(words, "(3baz)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nested_choice() {
|
||||||
|
assert!(parse(nested_choice, "(ac)"));
|
||||||
|
assert!(parse(nested_choice, "(ad)"));
|
||||||
|
assert!(parse(nested_choice, "(bc)"));
|
||||||
|
assert!(parse(nested_choice, "(bd)"));
|
||||||
|
assert!(!parse(nested_choice, "(a)"));
|
||||||
|
assert!(!parse(nested_choice, "(b)"));
|
||||||
|
assert!(!parse(nested_choice, "(c)"));
|
||||||
|
assert!(!parse(nested_choice, "(d)"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user