diff --git a/Cargo.lock b/Cargo.lock index ad13ad1..aa010b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,7 @@ name = "peg_macro" version = "0.1.0" dependencies = [ "quote", + "syn", ] [[package]] @@ -217,9 +218,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", diff --git a/peg_macro/Cargo.toml b/peg_macro/Cargo.toml index 5307907..d8360c6 100644 --- a/peg_macro/Cargo.toml +++ b/peg_macro/Cargo.toml @@ -8,3 +8,4 @@ proc-macro = true [dependencies] quote = "1.0.40" +syn = "2.0.101" diff --git a/peg_macro/src/lib.rs b/peg_macro/src/lib.rs index 548de23..9fc4e5d 100644 --- a/peg_macro/src/lib.rs +++ b/peg_macro/src/lib.rs @@ -1,8 +1,39 @@ use proc_macro::TokenStream; use quote::quote; +use syn::{ + Ident, Token, + parse::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, +}; + +struct Grammar { + rules: Punctuated, +} + +struct Rule { + name: Ident, +} + +impl Parse for Grammar { + fn parse(input: ParseStream) -> syn::Result { + Ok(Grammar { + rules: Punctuated::parse_terminated(input)?, + }) + } +} + +impl Parse for Rule { + fn parse(input: ParseStream) -> syn::Result { + let result = Self { + name: input.parse()?, + }; + Ok(result) + } +} #[proc_macro] -pub fn grammar(ts: TokenStream) -> TokenStream { +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) => { @@ -25,3 +56,24 @@ pub fn grammar(ts: TokenStream) -> TokenStream { quote! { println!("Hello, world! {:?}", #result); }.into() } + +#[proc_macro] +pub fn grammar(ts: TokenStream) -> TokenStream { + let input = parse_macro_input!(ts as Grammar); + let fns: Vec<_> = input + .rules + .iter() + .map(|r| { + let fn_name = &r.name; + quote! { + fn #fn_name() -> String { + "I'm a generated function!".into() + } + } + }) + .collect(); + quote! { + #(#fns)* + } + .into() +} diff --git a/src/main.rs b/src/main.rs index 2f9c7f3..8e73b5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,14 +6,14 @@ mod world; use anyhow::anyhow; use labels::labels::print_labels; -use peg_macro::grammar; +use peg_macro::grammar_old; use preprocess::eval::Context; use std::{env, error::Error, fs, path::PathBuf, process::exit}; use world::World; fn main() -> Result<(), Box> { - grammar!(123); - grammar!("abc"); + grammar_old!(123); + grammar_old!("abc"); let args: Vec = env::args().collect(); if args.len() != 2 { diff --git a/src/peg/peg.rs b/src/peg/peg.rs index e081159..0629f10 100644 --- a/src/peg/peg.rs +++ b/src/peg/peg.rs @@ -1,6 +1,91 @@ +use peg_macro::grammar; + #[derive(Debug)] #[allow(dead_code)] pub enum Foo { Bar(usize), Baz(String), } + +pub struct ParseState { + pub input: String, + pub offset: usize, +} + +pub struct Savepoint { + offset: usize, +} + +impl ParseState { + pub fn new(s: &str) -> Self { + Self { + input: s.into(), + offset: 0, + } + } + + pub fn save(&self) -> Savepoint { + Savepoint { + offset: self.offset, + } + } + + pub fn restore(&mut self, save: Savepoint) { + self.offset = save.offset; + } + + pub fn literal(&mut self, s: &str) -> bool { + if self.input[self.offset..].starts_with(s) { + self.offset += s.len(); + true + } else { + false + } + } +} + +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") +} + +grammar! { + generated_this; + generated_that; +} + +#[cfg(test)] +mod tests { + use insta::assert_snapshot; + + use super::*; + + #[test] + fn test_parser() { + let mut p = ParseState::new("foo"); + assert!(parse_foo(&mut p)); + let mut p = ParseState::new("fee"); + assert!(!parse_foo(&mut p)); + } + + #[test] + fn test_generated() { + assert_snapshot!(generated_this(), @"I'm a generated function!"); + assert_snapshot!(generated_that(), @"I'm a generated function!"); + } +}