Try generating named functions via proc macros

This commit is contained in:
2025-05-24 21:26:07 -07:00
parent 59392ccad2
commit 1d8c939400
5 changed files with 145 additions and 6 deletions
Generated
+3 -2
View File
@@ -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",
+1
View File
@@ -8,3 +8,4 @@ proc-macro = true
[dependencies]
quote = "1.0.40"
syn = "2.0.101"
+53 -1
View File
@@ -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<Rule, Token![;]>,
}
struct Rule {
name: Ident,
}
impl Parse for Grammar {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Grammar {
rules: Punctuated::parse_terminated(input)?,
})
}
}
impl Parse for Rule {
fn parse(input: ParseStream) -> syn::Result<Self> {
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()
}
+3 -3
View File
@@ -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<dyn Error>> {
grammar!(123);
grammar!("abc");
grammar_old!(123);
grammar_old!("abc");
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
+85
View File
@@ -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!");
}
}