Clean up old code, add option/star/plus ops

This commit is contained in:
2025-05-26 19:59:31 -07:00
parent ff9d471e00
commit 0994054291
3 changed files with 91 additions and 92 deletions
+56 -45
View File
@@ -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() {
result = Term::Star(Box::new(result));
} else { } else {
Ok(Term::Choice(choices)) 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,10 +113,11 @@ 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(); let save = p.save();
if #expr { if #expr {
true true
@@ -107,48 +127,39 @@ impl Term {
} }
} }
} }
}
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! {
(#expr || true)
} }
} }
Term::Star(term) => {
fn generate_wrapped_code(&self) -> proc_macro2::TokenStream { let expr = term.generate_code();
let code = self.generate_code(); quote! {
match self { { while #expr {}; true }
Term::Sequence(_) | Term::Choice(_) => quote! { }
{ #code } }
}, Term::Plus(term) => {
_ => code, let expr = term.generate_code();
quote! {
{
let mut closure = || #expr;
if !closure() {
return false;
}
while closure() {}
true
} }
} }
} }
#[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];
quote! { Some(crate::peg::Foo::Baz(#inner.into())) }
}
_ => quote! { None },
} }
} }
_ => quote! { None },
};
quote! { println!("Hello, world! {:?}", #result); }.into()
} }
#[proc_macro] #[proc_macro]
-4
View File
@@ -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
View File
@@ -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));
}
} }