Skip to content

Commit

Permalink
Merge pull request #289 from CAD97/generator
Browse files Browse the repository at this point in the history
Generator crate to vendor bootstrap
  • Loading branch information
CAD97 authored Oct 1, 2018
2 parents 260aed2 + 81bd7b9 commit 6e6a099
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 162 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"derive",
"generator",
"grammars",
"meta",
"pest",
Expand Down
8 changes: 3 additions & 5 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "pest_derive"
description = "pest's derive macro"
version = "2.0.0"
version = "2.0.1"
authors = ["Dragoș Tiselice <[email protected]>"]
homepage = "https://pest-parser.github.io/"
repository = "https://github.com/pest-parser/pest"
Expand All @@ -16,11 +16,9 @@ name = "pest_derive"
proc-macro = true

[dependencies]
# for tests, included transitively anyway
pest = { path = "../pest", version = "2.0" }
pest_meta = { path = "../meta", version = "2.0" }
proc-macro2 = "0.4.4"
quote = "0.6.3"
syn = "0.14.1"
pest_generator = { path = "../generator", version = "2.0" }

[badges]
codecov = { repository = "pest-parser/pest" }
Expand Down
159 changes: 2 additions & 157 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,168 +265,13 @@
//! * `NEWLINE` - matches either "\n" or "\r\n" or "\r"
#![doc(html_root_url = "https://docs.rs/pest_derive")]
#![recursion_limit = "256"]

extern crate pest;
extern crate pest_meta;

extern crate pest_generator;
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;

use std::env;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;

use proc_macro::TokenStream;
use syn::{Attribute, DeriveInput, Generics, Ident, Lit, Meta};

#[macro_use]
mod macros;
mod generator;

use pest_meta::{optimizer, unwrap_or_report, validator};
use pest_meta::parser::{self, Rule};

#[proc_macro_derive(Parser, attributes(grammar))]
pub fn derive_parser(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let (name, generics, path) = parse_derive(ast);

let root = env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
let path = Path::new(&root).join("src/").join(&path);
let file_name = match path.file_name() {
Some(file_name) => file_name,
None => panic!("grammar attribute should point to a file")
};

let data = match read_file(&path) {
Ok(data) => data,
Err(error) => panic!("error opening {:?}: {}", file_name, error)
};

let pairs = match parser::parse(Rule::grammar_rules, &data) {
Ok(pairs) => pairs,
Err(error) => panic!(
"error parsing {:?}\n\n{}",
file_name,
error.renamed_rules(|rule| match *rule {
Rule::grammar_rule => "rule".to_owned(),
Rule::_push => "PUSH".to_owned(),
Rule::assignment_operator => "`=`".to_owned(),
Rule::silent_modifier => "`_`".to_owned(),
Rule::atomic_modifier => "`@`".to_owned(),
Rule::compound_atomic_modifier => "`$`".to_owned(),
Rule::non_atomic_modifier => "`!`".to_owned(),
Rule::opening_brace => "`{`".to_owned(),
Rule::closing_brace => "`}`".to_owned(),
Rule::opening_paren => "`(`".to_owned(),
Rule::positive_predicate_operator => "`&`".to_owned(),
Rule::negative_predicate_operator => "`!`".to_owned(),
Rule::sequence_operator => "`&`".to_owned(),
Rule::choice_operator => "`|`".to_owned(),
Rule::optional_operator => "`?`".to_owned(),
Rule::repeat_operator => "`*`".to_owned(),
Rule::repeat_once_operator => "`+`".to_owned(),
Rule::comma => "`,`".to_owned(),
Rule::closing_paren => "`)`".to_owned(),
Rule::quote => "`\"`".to_owned(),
Rule::insensitive_string => "`^`".to_owned(),
Rule::range_operator => "`..`".to_owned(),
Rule::single_quote => "`'`".to_owned(),
other_rule => format!("{:?}", other_rule)
})
)
};

let defaults = unwrap_or_report(validator::validate_pairs(pairs.clone()));
let ast = unwrap_or_report(parser::consume_rules(pairs));
let optimized = optimizer::optimize(ast);
let generated = generator::generate(name, &generics, &path, optimized, defaults);

generated.into()
}

fn read_file<P: AsRef<Path>>(path: P) -> io::Result<String> {
let mut file = File::open(path.as_ref())?;
let mut string = String::new();
file.read_to_string(&mut string)?;
Ok(string)
}

fn parse_derive(ast: DeriveInput) -> (Ident, Generics, String) {
let name = ast.ident;
let generics = ast.generics;

let grammar: Vec<&Attribute> = ast.attrs
.iter()
.filter(|attr| match attr.interpret_meta() {
Some(Meta::NameValue(name_value)) => name_value.ident.to_string() == "grammar",
_ => false
})
.collect();

let filename = match grammar.len() {
0 => panic!("a grammar file needs to be provided with the #[grammar = \"...\"] attribute"),
1 => get_filename(grammar[0]),
_ => panic!("only 1 grammar file can be provided")
};

(name, generics, filename)
}

fn get_filename(attr: &Attribute) -> String {
match attr.interpret_meta() {
Some(Meta::NameValue(name_value)) => match name_value.lit {
Lit::Str(filename) => filename.value(),
_ => panic!("grammar attribute must be a string")
},
_ => panic!("grammar attribute must be of the form `grammar = \"...\"`")
}
}

#[cfg(test)]
mod tests {
use super::parse_derive;
use syn;

#[test]
fn derive_ok() {
let definition = "
#[other_attr]
#[grammar = \"myfile.pest\"]
pub struct MyParser<'a, T>;
";
let ast = syn::parse_str(definition).unwrap();
let (_, _, filename) = parse_derive(ast);
assert_eq!(filename, "myfile.pest");
}

#[test]
#[should_panic(expected = "only 1 grammar file can be provided")]
fn derive_multiple_grammars() {
let definition = "
#[other_attr]
#[grammar = \"myfile1.pest\"]
#[grammar = \"myfile2.pest\"]
pub struct MyParser<'a, T>;
";
let ast = syn::parse_str(definition).unwrap();
parse_derive(ast);
}

#[test]
#[should_panic(expected = "grammar attribute must be a string")]
fn derive_wrong_arg() {
let definition = "
#[other_attr]
#[grammar = 1]
pub struct MyParser<'a, T>;
";
let ast = syn::parse_str(definition).unwrap();
parse_derive(ast);
}
pest_generator::derive_parser(input.into()).into()
}
24 changes: 24 additions & 0 deletions generator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "pest_generator"
description = "pest code generator"
version = "2.0.0"
authors = ["Dragoș Tiselice <[email protected]>"]
homepage = "https://pest-parser.github.io/"
repository = "https://github.com/pest-parser/pest"
documentation = "https://docs.rs/pest"
keywords = ["pest", "generator"]
categories = ["parsing"]
license = "MIT/Apache-2.0"
readme = "_README.md"

[dependencies]
pest = { path = "../pest", version = "2.0" }
pest_meta = { path = "../meta", version = "2.0" }
proc-macro2 = "0.4.4"
quote = "0.6.3"
syn = "0.14.1"

[badges]
codecov = { repository = "pest-parser/pest" }
maintenance = { status = "actively-developed" }
travis-ci = { repository = "pest-parser/pest" }
1 change: 1 addition & 0 deletions generator/LICENSE-APACHE
1 change: 1 addition & 0 deletions generator/LICENSE-MIT
1 change: 1 addition & 0 deletions generator/_README.md
File renamed without changes.
Loading

0 comments on commit 6e6a099

Please sign in to comment.