diff --git a/Cargo.lock b/Cargo.lock index efa3af0..31df6bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "encode_unicode" version = "1.0.0" @@ -81,6 +87,15 @@ dependencies = [ "similar", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -131,6 +146,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" name = "peg_macro" version = "0.1.0" dependencies = [ + "itertools", "proc-macro2", "quote", "syn", diff --git a/peg_macro/Cargo.toml b/peg_macro/Cargo.toml index 9207d01..d0d7b71 100644 --- a/peg_macro/Cargo.toml +++ b/peg_macro/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" proc-macro = true [dependencies] +itertools = "0.14.0" proc-macro2 = "1.0.95" quote = "1.0.40" syn = "2.0.101" diff --git a/peg_macro/src/lib.rs b/peg_macro/src/lib.rs index 56afc95..aa49fdb 100644 --- a/peg_macro/src/lib.rs +++ b/peg_macro/src/lib.rs @@ -1,3 +1,4 @@ +use itertools::{Itertools, Position}; use proc_macro::TokenStream; use quote::quote; use syn::{ @@ -68,16 +69,52 @@ impl Term { fn generate_code(&self) -> proc_macro2::TokenStream { match self { Term::Rule(ident) => quote! { - println!("call subrule"); - // call subrule + #ident(p) }, Term::Literal(lit_str) => quote! { - println!("parse literal"); - // call p.literal() + p.literal(#lit_str) }, - Term::Sequence(terms) => quote! { - println!("sequence"); + Term::Sequence(terms) => { + let parts: Vec<_> = terms + .iter() + .with_position() + .map(|(pos, term)| { + let code = term.generate_wrapped_code(); + match pos { + Position::First => quote! { + let result = #code; + if !result { + return false; + } + }, + Position::Middle => quote! { + let result = #code; + if !result { + p.restore(save); + return false; + } + }, + _ => code, + } + }) + .collect(); + quote! { + let save = p.save(); + #(#parts)* + } + } + } + } + + fn generate_wrapped_code(&self) -> proc_macro2::TokenStream { + let code = self.generate_code(); + match self { + Term::Sequence(_) => quote! { + (|| { + #code + })() }, + _ => code, } } } @@ -116,24 +153,24 @@ pub fn grammar(ts: TokenStream) -> TokenStream { .map(|r| { let fn_name = &r.name; - // Generate a string for debugging - fn term2str(t: &Term) -> String { - match t { - Term::Rule(ident) => format!("rule {}", ident.to_string()), - Term::Literal(lit_str) => format!("literal \"{}\"", lit_str.value()), - Term::Sequence(terms) => { - let parts: Vec<_> = terms.iter().map(term2str).collect(); - format!("sequence [{}]", parts.join(", ")) - } - } - } - let text = format!("I'm generated! ({})", term2str(&r.definition)); + // // Generate a string for debugging + // fn term2str(t: &Term) -> String { + // match t { + // Term::Rule(ident) => format!("rule {}", ident.to_string()), + // Term::Literal(lit_str) => format!("literal \"{}\"", lit_str.value()), + // Term::Sequence(terms) => { + // let parts: Vec<_> = terms.iter().map(term2str).collect(); + // format!("sequence [{}]", parts.join(", ")) + // } + // } + // } + // let text = format!("I'm generated! ({})", term2str(&r.definition)); let generated = r.definition.generate_code(); quote! { - fn #fn_name(p: &mut crate::peg::ParseState) -> String { + fn #fn_name(p: &mut crate::peg::ParseState) -> bool { + // println!(#text.into()); #generated - #text.into() } } }) diff --git a/src/peg/peg.rs b/src/peg/peg.rs index 59740a0..3f3ee9f 100644 --- a/src/peg/peg.rs +++ b/src/peg/peg.rs @@ -1,5 +1,3 @@ -use peg_macro::grammar; - #[derive(Debug)] #[allow(dead_code)] pub enum Foo { @@ -64,17 +62,18 @@ fn parse_oo(p: &mut ParseState) -> bool { p.literal("oo") } -grammar! { - generated_this = foo; - generated_that = "bar" baz; -} - #[cfg(test)] mod tests { - use insta::assert_snapshot; + use peg_macro::grammar; use super::*; + grammar! { + fake_csv = line "\n" line; + line = foo "," foo; + foo = "foo"; + } + #[test] fn test_parser() { let mut p = ParseState::new("foo"); @@ -85,8 +84,9 @@ mod tests { #[test] fn test_generated() { - let mut p = ParseState::new(""); - assert_snapshot!(generated_this(&mut p), @"I'm generated! (rule foo)"); - assert_snapshot!(generated_that(&mut p), @r#"I'm generated! (sequence [literal "bar", rule baz])"#); + let mut p = ParseState::new("foo,foo\nfoo,foo"); + assert!(fake_csv(&mut p)); + let mut p = ParseState::new("foo,foo\nfoo;foo"); + assert!(!fake_csv(&mut p)); } }