Generate actual parsing code in grammar! macro
This commit is contained in:
Generated
+16
@@ -62,6 +62,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -81,6 +87,15 @@ dependencies = [
|
|||||||
"similar",
|
"similar",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@@ -131,6 +146,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|||||||
name = "peg_macro"
|
name = "peg_macro"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ edition = "2024"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
itertools = "0.14.0"
|
||||||
proc-macro2 = "1.0.95"
|
proc-macro2 = "1.0.95"
|
||||||
quote = "1.0.40"
|
quote = "1.0.40"
|
||||||
syn = "2.0.101"
|
syn = "2.0.101"
|
||||||
|
|||||||
+57
-20
@@ -1,3 +1,4 @@
|
|||||||
|
use itertools::{Itertools, Position};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
@@ -68,16 +69,52 @@ impl Term {
|
|||||||
fn generate_code(&self) -> proc_macro2::TokenStream {
|
fn generate_code(&self) -> proc_macro2::TokenStream {
|
||||||
match self {
|
match self {
|
||||||
Term::Rule(ident) => quote! {
|
Term::Rule(ident) => quote! {
|
||||||
println!("call subrule");
|
#ident(p)
|
||||||
// call subrule
|
|
||||||
},
|
},
|
||||||
Term::Literal(lit_str) => quote! {
|
Term::Literal(lit_str) => quote! {
|
||||||
println!("parse literal");
|
p.literal(#lit_str)
|
||||||
// call p.literal()
|
|
||||||
},
|
},
|
||||||
Term::Sequence(terms) => quote! {
|
Term::Sequence(terms) => {
|
||||||
println!("sequence");
|
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| {
|
.map(|r| {
|
||||||
let fn_name = &r.name;
|
let fn_name = &r.name;
|
||||||
|
|
||||||
// Generate a string for debugging
|
// // Generate a string for debugging
|
||||||
fn term2str(t: &Term) -> String {
|
// fn term2str(t: &Term) -> String {
|
||||||
match t {
|
// match t {
|
||||||
Term::Rule(ident) => format!("rule {}", ident.to_string()),
|
// Term::Rule(ident) => format!("rule {}", ident.to_string()),
|
||||||
Term::Literal(lit_str) => format!("literal \"{}\"", lit_str.value()),
|
// Term::Literal(lit_str) => format!("literal \"{}\"", lit_str.value()),
|
||||||
Term::Sequence(terms) => {
|
// Term::Sequence(terms) => {
|
||||||
let parts: Vec<_> = terms.iter().map(term2str).collect();
|
// let parts: Vec<_> = terms.iter().map(term2str).collect();
|
||||||
format!("sequence [{}]", parts.join(", "))
|
// format!("sequence [{}]", parts.join(", "))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
let text = format!("I'm generated! ({})", term2str(&r.definition));
|
// let text = format!("I'm generated! ({})", term2str(&r.definition));
|
||||||
|
|
||||||
let generated = r.definition.generate_code();
|
let generated = r.definition.generate_code();
|
||||||
quote! {
|
quote! {
|
||||||
fn #fn_name(p: &mut crate::peg::ParseState) -> String {
|
fn #fn_name(p: &mut crate::peg::ParseState) -> bool {
|
||||||
|
// println!(#text.into());
|
||||||
#generated
|
#generated
|
||||||
#text.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
+11
-11
@@ -1,5 +1,3 @@
|
|||||||
use peg_macro::grammar;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Foo {
|
pub enum Foo {
|
||||||
@@ -64,17 +62,18 @@ fn parse_oo(p: &mut ParseState) -> bool {
|
|||||||
p.literal("oo")
|
p.literal("oo")
|
||||||
}
|
}
|
||||||
|
|
||||||
grammar! {
|
|
||||||
generated_this = foo;
|
|
||||||
generated_that = "bar" baz;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use insta::assert_snapshot;
|
use peg_macro::grammar;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
grammar! {
|
||||||
|
fake_csv = line "\n" line;
|
||||||
|
line = foo "," foo;
|
||||||
|
foo = "foo";
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parser() {
|
fn test_parser() {
|
||||||
let mut p = ParseState::new("foo");
|
let mut p = ParseState::new("foo");
|
||||||
@@ -85,8 +84,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generated() {
|
fn test_generated() {
|
||||||
let mut p = ParseState::new("");
|
let mut p = ParseState::new("foo,foo\nfoo,foo");
|
||||||
assert_snapshot!(generated_this(&mut p), @"I'm generated! (rule foo)");
|
assert!(fake_csv(&mut p));
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user