Clean up old code, add option/star/plus ops
This commit is contained in:
+66
-55
@@ -19,8 +19,11 @@ struct Rule {
|
|||||||
enum Term {
|
enum Term {
|
||||||
Choice(Vec<Term>),
|
Choice(Vec<Term>),
|
||||||
Literal(LitStr),
|
Literal(LitStr),
|
||||||
|
Optional(Box<Term>),
|
||||||
Rule(Ident),
|
Rule(Ident),
|
||||||
Sequence(Vec<Term>),
|
Sequence(Vec<Term>),
|
||||||
|
Star(Box<Term>),
|
||||||
|
Plus(Box<Term>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Grammar {
|
impl Parse for Grammar {
|
||||||
@@ -53,23 +56,26 @@ impl Parse for Term {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_term(input: ParseStream) -> syn::Result<Term> {
|
fn parse_repeat(input: ParseStream) -> syn::Result<Term> {
|
||||||
let mut choices = vec![parse_atom(input)?];
|
let mut result = parse_atom(input)?;
|
||||||
while input.peek(Token![/]) {
|
loop {
|
||||||
input.parse::<Token![/]>()?;
|
if input.parse::<Token![?]>().is_ok() {
|
||||||
choices.push(parse_atom(input)?);
|
result = Term::Optional(Box::new(result));
|
||||||
}
|
} else if input.parse::<Token![+]>().is_ok() {
|
||||||
if choices.len() == 1 {
|
result = Term::Plus(Box::new(result));
|
||||||
Ok(choices.pop().unwrap())
|
} else if input.parse::<Token![*]>().is_ok() {
|
||||||
} else {
|
result = Term::Star(Box::new(result));
|
||||||
Ok(Term::Choice(choices))
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_sequence(input: ParseStream) -> syn::Result<Term> {
|
fn parse_sequence(input: ParseStream) -> syn::Result<Term> {
|
||||||
let mut terms = vec![parse_term(input)?];
|
let mut terms = vec![parse_repeat(input)?];
|
||||||
while !input.is_empty() && !input.peek(Token![;]) {
|
while input.peek(Ident) || input.peek(LitStr) {
|
||||||
terms.push(parse_term(input)?);
|
terms.push(parse_repeat(input)?);
|
||||||
}
|
}
|
||||||
if terms.len() == 1 {
|
if terms.len() == 1 {
|
||||||
Ok(terms.pop().unwrap())
|
Ok(terms.pop().unwrap())
|
||||||
@@ -78,7 +84,20 @@ impl Parse for Term {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_sequence(input)
|
fn parse_choice(input: ParseStream) -> syn::Result<Term> {
|
||||||
|
let mut choices = vec![parse_sequence(input)?];
|
||||||
|
while input.peek(Token![/]) {
|
||||||
|
input.parse::<Token![/]>()?;
|
||||||
|
choices.push(parse_sequence(input)?);
|
||||||
|
}
|
||||||
|
if choices.len() == 1 {
|
||||||
|
Ok(choices.pop().unwrap())
|
||||||
|
} else {
|
||||||
|
Ok(Term::Choice(choices))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_choice(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,61 +113,53 @@ impl Term {
|
|||||||
Term::Sequence(terms) => {
|
Term::Sequence(terms) => {
|
||||||
let expr = terms
|
let expr = terms
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.generate_wrapped_code())
|
.map(|t| t.generate_code())
|
||||||
.reduce(|x, y| quote! { #x && #y })
|
.reduce(|x, y| quote! { #x && #y })
|
||||||
.unwrap();
|
.unwrap();
|
||||||
quote! {
|
quote! {
|
||||||
let save = p.save();
|
{
|
||||||
if #expr {
|
let save = p.save();
|
||||||
true
|
if #expr {
|
||||||
} else {
|
true
|
||||||
p.restore(save);
|
} else {
|
||||||
false
|
p.restore(save);
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Term::Choice(terms) => terms
|
Term::Choice(terms) => terms
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.generate_wrapped_code())
|
.map(|t| t.generate_code())
|
||||||
.reduce(|x, y| quote! { #x || #y })
|
.reduce(|x, y| quote! { #x || #y })
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
}
|
Term::Optional(term) => {
|
||||||
}
|
let expr = term.generate_code();
|
||||||
|
quote! {
|
||||||
fn generate_wrapped_code(&self) -> proc_macro2::TokenStream {
|
(#expr || true)
|
||||||
let code = self.generate_code();
|
|
||||||
match self {
|
|
||||||
Term::Sequence(_) | Term::Choice(_) => quote! {
|
|
||||||
{ #code }
|
|
||||||
},
|
|
||||||
_ => code,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn grammar_old(ts: TokenStream) -> TokenStream {
|
|
||||||
let token = ts.into_iter().next().expect("must have at least one token");
|
|
||||||
let result = match token {
|
|
||||||
proc_macro::TokenTree::Literal(literal) => {
|
|
||||||
let s = literal.to_string();
|
|
||||||
let c = s.chars().next().unwrap_or('\0');
|
|
||||||
match c {
|
|
||||||
'0'..'9' => {
|
|
||||||
let val: usize = s.parse().unwrap();
|
|
||||||
quote! { Some(crate::peg::Foo::Bar(#val)) }
|
|
||||||
}
|
}
|
||||||
'"' => {
|
}
|
||||||
let inner = &s[1..s.len() - 1];
|
Term::Star(term) => {
|
||||||
quote! { Some(crate::peg::Foo::Baz(#inner.into())) }
|
let expr = term.generate_code();
|
||||||
|
quote! {
|
||||||
|
{ while #expr {}; true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Plus(term) => {
|
||||||
|
let expr = term.generate_code();
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
let mut closure = || #expr;
|
||||||
|
if !closure() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while closure() {}
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => quote! { None },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => quote! { None },
|
}
|
||||||
};
|
|
||||||
|
|
||||||
quote! { println!("Hello, world! {:?}", #result); }.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
|
|||||||
@@ -6,15 +6,11 @@ mod world;
|
|||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use labels::labels::print_labels;
|
use labels::labels::print_labels;
|
||||||
use peg_macro::grammar_old;
|
|
||||||
use preprocess::eval::Context;
|
use preprocess::eval::Context;
|
||||||
use std::{env, error::Error, fs, path::PathBuf, process::exit};
|
use std::{env, error::Error, fs, path::PathBuf, process::exit};
|
||||||
use world::World;
|
use world::World;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
grammar_old!(123);
|
|
||||||
grammar_old!("abc");
|
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
eprintln!("Usage: {} WORLD_FILE", args[0]);
|
eprintln!("Usage: {} WORLD_FILE", args[0]);
|
||||||
|
|||||||
+25
-33
@@ -1,10 +1,3 @@
|
|||||||
#[derive(Debug)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum Foo {
|
|
||||||
Bar(usize),
|
|
||||||
Baz(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ParseState {
|
pub struct ParseState {
|
||||||
pub input: String,
|
pub input: String,
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
@@ -42,26 +35,6 @@ impl ParseState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_foo(p: &mut ParseState) -> bool {
|
|
||||||
let save = p.save();
|
|
||||||
if !parse_f(p) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if !parse_oo(p) {
|
|
||||||
p.restore(save);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_f(p: &mut ParseState) -> bool {
|
|
||||||
p.literal("f")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_oo(p: &mut ParseState) -> bool {
|
|
||||||
p.literal("oo")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use peg_macro::grammar;
|
use peg_macro::grammar;
|
||||||
@@ -72,14 +45,15 @@ mod tests {
|
|||||||
fake_csv = line "\n" line;
|
fake_csv = line "\n" line;
|
||||||
line = item "," item;
|
line = item "," item;
|
||||||
item = "foo" / "bar";
|
item = "foo" / "bar";
|
||||||
|
|
||||||
|
option = "(" "x"? ")";
|
||||||
|
plus = "(" "x"+ ")";
|
||||||
|
star = "(" "x"* ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn parse<T: Fn(&mut ParseState) -> bool>(rule: T, s: &str) -> bool {
|
||||||
fn test_parser() {
|
let mut p = ParseState::new(s);
|
||||||
let mut p = ParseState::new("foo");
|
rule(&mut p)
|
||||||
assert!(parse_foo(&mut p));
|
|
||||||
let mut p = ParseState::new("fee");
|
|
||||||
assert!(!parse_foo(&mut p));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -89,4 +63,22 @@ mod tests {
|
|||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_repetition_suffixes() {
|
||||||
|
let zero = "()";
|
||||||
|
assert!(parse(option, zero));
|
||||||
|
assert!(parse(star, zero));
|
||||||
|
assert!(!parse(plus, zero));
|
||||||
|
|
||||||
|
let one = "(x)";
|
||||||
|
assert!(parse(option, one));
|
||||||
|
assert!(parse(star, one));
|
||||||
|
assert!(parse(plus, one));
|
||||||
|
|
||||||
|
let many = "(xxx)";
|
||||||
|
assert!(!parse(option, many));
|
||||||
|
assert!(parse(star, many));
|
||||||
|
assert!(parse(plus, many));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user