Skip to content

Commit

Permalink
Merge pull request #290 from CAD97/bootstrap
Browse files Browse the repository at this point in the history
Bootstrap pest_meta with pest:2.0
  • Loading branch information
dragostis authored Oct 2, 2018
2 parents e7e19d4 + d045774 commit 0f72cb0
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 133 deletions.
2 changes: 2 additions & 0 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[alias]
bootstrap = "run --package pest_bootstrap"
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ notifications:
script:
# Need a custom script because we are using cargo workspaces.
- |
cargo bootstrap && # dynamic build dependency of pest_meta
cargo build --all --verbose &&
cargo test --all --verbose &&
cargo doc --all --verbose
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"bootstrap",
"derive",
"generator",
"grammars",
Expand Down
19 changes: 19 additions & 0 deletions bootstrap/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "pest_bootstrap"
description = "pest bootstrap script"
version = "0.0.0"
authors = ["Dragoș Tiselice <dragostiselice@gmail.com>"]
homepage = "https://pest-parser.github.io/"
repository = "https://github.com/pest-parser/pest"
documentation = "https://docs.rs/pest"
publish = false
license = "MIT/Apache-2.0"

[dependencies]
pest_generator = "2.0" # Use the crates-io version, which (should be) known-good
quote = "0.6.8"

[badges]
codecov = { repository = "pest-parser/pest" }
maintenance = { status = "actively-developed" }
travis-ci = { repository = "pest-parser/pest" }
30 changes: 30 additions & 0 deletions bootstrap/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#[macro_use]
extern crate quote;
extern crate pest_generator;

use pest_generator::derive_parser;
use std::{fs::File, io::prelude::*, path::Path};

fn main() {
let pest = Path::new(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../meta/src/grammar.pest"
));
let rs = Path::new(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../meta/src/grammar.rs"
));

let derived = {
let path = pest.to_string_lossy();
let pest = quote! {
#[grammar = #path]
pub struct PestParser;
};
derive_parser(pest, false)
};

let mut file = File::create(rs).unwrap();

writeln!(file, "pub struct PestParser;\n{}", derived,).unwrap();
}
1 change: 1 addition & 0 deletions meta/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/src/grammar.rs
7 changes: 5 additions & 2 deletions meta/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ keywords = ["pest", "parser", "meta", "optimizer"]
categories = ["parsing"]
license = "MIT/Apache-2.0"
readme = "_README.md"
exclude = ["src/grammar.pest"]

[dependencies]
maplit = "1.0"
pest = "1.0"
pest_derive = "1.0"
pest = { path = "../pest", version = "2.0" }

[badges]
codecov = { repository = "pest-parser/pest" }
maintenance = { status = "actively-developed" }
travis-ci = { repository = "pest-parser/pest" }

[build-dependencies]
sha-1 = "0.7.0"
69 changes: 69 additions & 0 deletions meta/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
extern crate sha1;

use sha1::{Digest, Sha1};
use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::{Command};

fn display_digest(digest: &[u8]) -> String {
digest.iter()
.map(|byte| format!("{:02x}", byte))
.collect()
}

fn main() {
println!("rerun-if-changed=src/grammar.pest");

// Yes; build.rs is supposed to treat `src` as read-only; however:
// We want to publish `grammar.rs` and not `grammar.pest`,
// so putting it in `src` is the simplest way to do so.
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let grammar_pest_path = manifest_dir.join("src/grammar.pest");
let grammar_rs_path = manifest_dir.join("src/grammar.rs");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let hash_path = out_dir.join("pest_hash.sha1");

// If `grammar.pest` exists (we're building from git sources)
if grammar_pest_path.exists() {
let mut sha = Sha1::default();

let old_hash = File::open(&hash_path).ok().map(|mut file| {
let mut s = String::new();
file.read_to_string(&mut s).unwrap();
s
});
let current_grammar = fs::read_to_string(grammar_pest_path).unwrap();
sha.input(current_grammar.as_bytes());
let current_hash = display_digest(&sha.result());

// If `grammar.pest` has changed
if !grammar_rs_path.exists()
|| old_hash.as_ref().map(|it| it.trim()) != Some(current_hash.trim())
{
println!("Bootstrapping `meta/src/grammar.rs`");

let mut hash_file = File::create(hash_path).unwrap();
writeln!(hash_file, "{}", current_hash).unwrap();

// This "dynamic linking" is probably so fragile I don't even want to hear it
let status = Command::new(manifest_dir.join("../target/debug/pest_bootstrap"))
.spawn().unwrap_or_else(|_| {
panic!(
"Bootstrap failed because no bootstrap executable was found. \
Please run `cargo build --package pest_bootstrap` or `cargo bootstrap` \
and then try again.",
)
})
.wait().unwrap();
if !status.success() {
panic!("Bootstrap failed");
}
} else {
println!(" Fresh `meta/src/grammar.rs`");
}
} else {
assert!(grammar_rs_path.exists(), "package is broken; does not contain grammar.rs");
}
}
10 changes: 5 additions & 5 deletions meta/src/grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

grammar_rules = _{ soi ~ grammar_rule+ ~ eoi }
grammar_rules = _{ SOI ~ grammar_rule+ ~ EOI }

grammar_rule = {
identifier ~ assignment_operator ~ modifier? ~
Expand Down Expand Up @@ -77,8 +77,8 @@ insensitive_string = { "^" ~ string }
range = { character ~ range_operator ~ character }
character = ${ single_quote ~ inner_chr ~ single_quote }

inner_str = @{ (!("\"" | "\\") ~ any)* ~ (escape ~ inner_str)? }
inner_chr = @{ escape | any }
inner_str = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ inner_str)? }
inner_chr = @{ escape | ANY }
escape = @{ "\\" ~ ("\"" | "\\" | "r" | "n" | "t" | "0" | "'" | code | unicode) }
code = @{ "x" ~ hex_digit{2} }
unicode = @{ "u" ~ opening_brace ~ hex_digit{2, 6} ~ closing_brace }
Expand All @@ -89,5 +89,5 @@ single_quote = { "'" }
range_operator = { ".." }

newline = _{ "\n" | "\r\n" }
whitespace = _{ " " | "\t" | newline }
comment = _{ "//" ~ (!newline ~ any)* }
WHITESPACE = _{ " " | "\t" | newline }
COMMENT = _{ "//" ~ (!newline ~ ANY)* }
2 changes: 0 additions & 2 deletions meta/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ extern crate maplit;
extern crate pest;
#[cfg(not(test))]
extern crate pest;
#[macro_use]
extern crate pest_derive;

use std::fmt::Display;

Expand Down
102 changes: 53 additions & 49 deletions meta/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,15 @@
use std::char;
use std::iter::Peekable;

use pest::{Error, Parser};
use pest::Span;
use pest::iterators::{Pair, Pairs};
use pest::prec_climber::{Assoc, Operator, PrecClimber};
use pest::{Span, Parser};
use pest::error::{Error, ErrorVariant};

use ast::{Expr, Rule as AstRule, RuleType};
use validator;

#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct PestParser;
include!("grammar.rs");

pub fn parse<'i>(rule: Rule, data: &'i str) -> Result<Pairs<Rule>, Error<Rule>> {
PestParser::parse(rule, data)
Expand Down Expand Up @@ -164,7 +162,7 @@ fn convert_node<'i>(node: ParserNode<'i>) -> Expr {
}
}

pub fn consume_rules<'i>(pairs: Pairs<'i, Rule>) -> Result<Vec<AstRule>, Vec<Error<'i, Rule>>> {
pub fn consume_rules<'i>(pairs: Pairs<'i, Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule>>> {
let rules = consume_rules_with_spans(pairs)?;
let errors = validator::validate_ast(&rules);
if errors.len() == 0 {
Expand All @@ -176,7 +174,7 @@ pub fn consume_rules<'i>(pairs: Pairs<'i, Rule>) -> Result<Vec<AstRule>, Vec<Err

fn consume_rules_with_spans<'i>(
pairs: Pairs<'i, Rule>
) -> Result<Vec<ParserRule<'i>>, Vec<Error<'i, Rule>>> {
) -> Result<Vec<ParserRule<'i>>, Vec<Error<Rule>>> {
let climber = PrecClimber::new(vec![
Operator::new(Rule::choice_operator, Assoc::Left),
Operator::new(Rule::sequence_operator, Assoc::Left),
Expand Down Expand Up @@ -221,11 +219,11 @@ fn consume_rules_with_spans<'i>(
fn consume_expr<'i>(
pairs: Peekable<Pairs<'i, Rule>>,
climber: &PrecClimber<Rule>
) -> Result<ParserNode<'i>, Vec<Error<'i, Rule>>> {
) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
fn unaries<'i>(
mut pairs: Peekable<Pairs<'i, Rule>>,
climber: &PrecClimber<Rule>
) -> Result<ParserNode<'i>, Vec<Error<'i, Rule>>> {
) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
let pair = pairs.next().unwrap();

let node = match pair.as_rule() {
Expand Down Expand Up @@ -314,7 +312,7 @@ fn consume_expr<'i>(

pairs.fold(
Ok(node),
|node: Result<ParserNode<'i>, Vec<Error<'i, Rule>>>, pair| {
|node: Result<ParserNode<'i>, Vec<Error<Rule>>>, pair| {
let node = node?;

let node = match pair.as_rule() {
Expand Down Expand Up @@ -348,19 +346,21 @@ fn consume_expr<'i>(
let num = if let Ok(num) = number.as_str().parse::<u32>() {
num
} else {
return Err(vec![
Error::CustomErrorSpan {
message: "number cannot overflow u32".to_owned(),
span: number.into_span()
return Err(vec![Error::new_from_span(
ErrorVariant::CustomError {
message: "number cannot overflow u32".to_owned()
},
]);
number.into_span()
)]);
};

if num == 0 {
let error: Error<Rule> = Error::CustomErrorSpan {
message: "cannot repeat 0 times".to_owned(),
span: number.into_span()
};
let error: Error<Rule> = Error::new_from_span(
ErrorVariant::CustomError {
message: "cannot repeat 0 times".to_owned()
},
number.into_span()
);

return Err(vec![error]);
}
Expand All @@ -380,12 +380,12 @@ fn consume_expr<'i>(
let min = if let Ok(min) = min_number.as_str().parse::<u32>() {
min
} else {
return Err(vec![
Error::CustomErrorSpan {
message: "number cannot overflow u32".to_owned(),
span: min_number.into_span()
return Err(vec![Error::new_from_span(
ErrorVariant::CustomError {
message: "number cannot overflow u32".to_owned()
},
]);
min_number.into_span()
)]);
};

let start = node.span.start_pos();
Expand All @@ -404,19 +404,21 @@ fn consume_expr<'i>(
let max = if let Ok(max) = max_number.as_str().parse::<u32>() {
max
} else {
return Err(vec![
Error::CustomErrorSpan {
message: "number cannot overflow u32".to_owned(),
span: max_number.into_span()
return Err(vec![Error::new_from_span(
ErrorVariant::CustomError {
message: "number cannot overflow u32".to_owned()
},
]);
max_number.into_span()
)]);
};

if max == 0 {
let error: Error<Rule> = Error::CustomErrorSpan {
message: "cannot repeat 0 times".to_owned(),
span: max_number.into_span()
};
let error: Error<Rule> = Error::new_from_span(
ErrorVariant::CustomError {
message: "cannot repeat 0 times".to_owned()
},
max_number.into_span()
);

return Err(vec![error]);
}
Expand All @@ -436,12 +438,12 @@ fn consume_expr<'i>(
let min = if let Ok(min) = min_number.as_str().parse::<u32>() {
min
} else {
return Err(vec![
Error::CustomErrorSpan {
message: "number cannot overflow u32".to_owned(),
span: min_number.into_span()
return Err(vec![Error::new_from_span(
ErrorVariant::CustomError {
message: "number cannot overflow u32".to_owned()
},
]);
min_number.into_span()
)]);
};

inner.next().unwrap(); // comma
Expand All @@ -450,19 +452,21 @@ fn consume_expr<'i>(
let max = if let Ok(max) = max_number.as_str().parse::<u32>() {
max
} else {
return Err(vec![
Error::CustomErrorSpan {
message: "number cannot overflow u32".to_owned(),
span: max_number.into_span()
return Err(vec![Error::new_from_span(
ErrorVariant::CustomError {
message: "number cannot overflow u32".to_owned()
},
]);
max_number.into_span()
)]);
};

if max == 0 {
let error: Error<Rule> = Error::CustomErrorSpan {
message: "cannot repeat 0 times".to_owned(),
span: max_number.into_span()
};
let error: Error<Rule> = Error::new_from_span(
ErrorVariant::CustomError {
message: "cannot repeat 0 times".to_owned()
},
max_number.into_span()
);

return Err(vec![error]);
}
Expand Down Expand Up @@ -494,9 +498,9 @@ fn consume_expr<'i>(
}

let term = |pair: Pair<'i, Rule>| unaries(pair.into_inner().peekable(), climber);
let infix = |lhs: Result<ParserNode<'i>, Vec<Error<'i, Rule>>>,
let infix = |lhs: Result<ParserNode<'i>, Vec<Error<Rule>>>,
op: Pair<'i, Rule>,
rhs: Result<ParserNode<'i>, Vec<Error<'i, Rule>>>| match op.as_rule(
rhs: Result<ParserNode<'i>, Vec<Error<Rule>>>| match op.as_rule(
) {
Rule::sequence_operator => {
let lhs = lhs?;
Expand Down
Loading

0 comments on commit 0f72cb0

Please sign in to comment.