Try generating named functions via proc macros
This commit is contained in:
Generated
+3
-2
@@ -132,6 +132,7 @@ name = "peg_macro"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -217,9 +218,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.100"
|
version = "2.0.101"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ proc-macro = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1.0.40"
|
quote = "1.0.40"
|
||||||
|
syn = "2.0.101"
|
||||||
|
|||||||
+53
-1
@@ -1,8 +1,39 @@
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
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]
|
#[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 token = ts.into_iter().next().expect("must have at least one token");
|
||||||
let result = match token {
|
let result = match token {
|
||||||
proc_macro::TokenTree::Literal(literal) => {
|
proc_macro::TokenTree::Literal(literal) => {
|
||||||
@@ -25,3 +56,24 @@ pub fn grammar(ts: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
quote! { println!("Hello, world! {:?}", #result); }.into()
|
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
@@ -6,14 +6,14 @@ mod world;
|
|||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use labels::labels::print_labels;
|
use labels::labels::print_labels;
|
||||||
use peg_macro::grammar;
|
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!(123);
|
grammar_old!(123);
|
||||||
grammar!("abc");
|
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 {
|
||||||
|
|||||||
@@ -1,6 +1,91 @@
|
|||||||
|
use peg_macro::grammar;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Foo {
|
pub enum Foo {
|
||||||
Bar(usize),
|
Bar(usize),
|
||||||
Baz(String),
|
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!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user