summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadrieril2019-03-09 20:28:18 +0100
committerNadrieril2019-03-09 20:28:18 +0100
commitb44cbca3a3996b3c47ec0f883bd1dc087971f2d1 (patch)
tree3fa171309ef1eb218cd77aae9418e17d1fec8731
parentcb9dfc1b9421e9814dac3ba6c78bad0d271f9d67 (diff)
Refactor abnf_to_pest to make rule editing possible
Obsoletes #16
-rw-r--r--abnf_to_pest/src/lib.rs207
-rw-r--r--dhall_parser/build.rs50
2 files changed, 128 insertions, 129 deletions
diff --git a/abnf_to_pest/src/lib.rs b/abnf_to_pest/src/lib.rs
index 72746c0..7853773 100644
--- a/abnf_to_pest/src/lib.rs
+++ b/abnf_to_pest/src/lib.rs
@@ -1,146 +1,163 @@
#![allow(clippy::implicit_hasher, clippy::or_fun_call)]
use itertools::Itertools;
use std::collections::HashMap;
+use pretty::{BoxDoc, Doc};
+pub use abnf::abnf::{Alternation, Concatenation, Repetition, Repeat, Range, Element};
+use abnf::abnf::Rule;
-pub struct PestRuleSettings {
- pub visible: bool,
- pub replace: Option<String>,
-}
-impl Default for PestRuleSettings {
- fn default() -> Self {
- PestRuleSettings {
- visible: true,
- replace: None,
- }
- }
+trait Pretty {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>>;
}
-pub fn abnf_to_pest(
- data: &[u8],
- rule_settings: &HashMap<String, PestRuleSettings>,
-) -> std::io::Result<String> {
- use abnf::abnf::*;
- use pretty::{BoxDoc, Doc};
- fn format_rule(
- x: Rule,
- rule_settings: &HashMap<String, PestRuleSettings>,
- ) -> Doc<BoxDoc<()>> {
- let rulename = format_rulename(x.name);
- let default = Default::default();
- let setting = rule_settings.get(&rulename).unwrap_or(&default);
- let visible = if setting.visible { "" } else { "_" };
- let contents = match setting.replace {
- None => format_alternation(x.elements),
- Some(ref x) => Doc::text(x.clone()),
- };
- Doc::nil()
- .append(Doc::text(rulename))
- .append(Doc::text(" = "))
- .append(Doc::text(visible))
- .append(Doc::text("{"))
- .append(Doc::space().append(contents).nest(2))
- .append(Doc::space())
- .append(Doc::text("}"))
- .group()
- }
- fn format_rulename(x: String) -> String {
- let x = x.replace("-", "_");
- if x == "if"
- || x == "else"
- || x == "as"
- || x == "let"
- || x == "in"
- || x == "fn"
- // TODO: remove when https://github.com/pest-parser/pest/pull/375 gets into a release
- || x == "whitespace"
- {
- x + "_"
- } else {
- x
- }
- }
- fn format_alternation(x: Alternation) -> Doc<'static, BoxDoc<'static, ()>> {
+impl Pretty for Alternation {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>> {
Doc::intersperse(
- x.concatenations
- .into_iter()
- .map(|x| format_concatenation(x).nest(2).group()),
+ self.concatenations
+ .iter()
+ .map(|x| x.pretty().nest(2).group()),
Doc::space().append(Doc::text("| ")),
)
}
- fn format_concatenation(
- x: Concatenation,
- ) -> Doc<'static, BoxDoc<'static, ()>> {
+}
+
+impl Pretty for Concatenation {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>> {
Doc::intersperse(
- x.repetitions.into_iter().map(format_repetition),
+ self.repetitions.iter().map(Repetition::pretty),
Doc::space().append(Doc::text("~ ")),
)
}
- fn format_repetition(x: Repetition) -> Doc<'static, BoxDoc<'static, ()>> {
- format_element(x.element).append(
- x.repeat
- .map(format_repeat)
- .map(Doc::text)
+}
+
+impl Pretty for Repetition {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>> {
+ self.element.pretty().append(
+ self.repeat
+ .as_ref()
+ .map(Repeat::pretty)
.unwrap_or(Doc::nil()),
)
}
- fn format_repeat(x: Repeat) -> String {
- match (x.min.unwrap_or(0), x.max) {
+}
+
+impl Pretty for Repeat {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>> {
+ Doc::text(match (self.min.unwrap_or(0), self.max) {
(0, None) => "*".into(),
(1, None) => "+".into(),
(0, Some(1)) => "?".into(),
(min, None) => format!("{{{},}}", min),
(min, Some(max)) if min == max => format!("{{{}}}", min),
(min, Some(max)) => format!("{{{},{}}}", min, max),
- }
+ })
}
- fn format_element(x: Element) -> Doc<'static, BoxDoc<'static, ()>> {
+}
+
+impl Pretty for Element {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>> {
use abnf::abnf::Element::*;
- match x {
- Rulename(s) => Doc::text(format_rulename(s)),
+ match self {
+ Rulename(s) => Doc::text(escape_rulename(s)),
Group(g) => Doc::text("(")
- .append(format_alternation(g.alternation).nest(4).group())
+ .append((g.alternation).pretty().nest(4).group())
.append(Doc::text(")")),
Option(o) => Doc::text("(")
- .append(format_alternation(o.alternation).nest(4).group())
+ .append((o.alternation).pretty().nest(4).group())
.append(Doc::text(")?")),
CharVal(s) => Doc::text(format!(
"^\"{}\"",
s.replace("\"", "\\\"").replace("\\", "\\\\")
)),
- NumVal(r) => Doc::text(format_range(r)),
+ NumVal(r) => r.pretty(),
ProseVal(_) => unimplemented!(),
}
}
- fn format_range(x: Range) -> String {
+}
+
+impl Pretty for Range {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>> {
use abnf::abnf::Range::*;
- match x {
+ Doc::text(match self {
Range(x, y) => {
format!("'{}'..'{}'", format_char(x), format_char(y))
}
OneOf(v) => {
- format!("\"{}\"", v.into_iter().map(format_char).join(""))
+ format!("\"{}\"", v.iter().map(format_char).join(""))
}
- }
+ })
+ }
+}
+
+pub fn escape_rulename(x: &str) -> String {
+ let x = x.replace("-", "_");
+ if x == "if"
+ || x == "else"
+ || x == "as"
+ || x == "let"
+ || x == "in"
+ || x == "fn"
+ // TODO: remove when https://github.com/pest-parser/pest/pull/375 gets into a release
+ || x == "whitespace"
+ {
+ x + "_"
+ } else {
+ x.clone()
}
- fn format_char(x: u64) -> String {
- if x <= u64::from(u8::max_value()) {
- let x: u8 = x as u8;
- if x.is_ascii_graphic() {
- let x: char = x as char;
- if x != '"' && x != '\'' && x != '\\' {
- return x.to_string();
- }
+}
+
+fn format_char(x: &u64) -> String {
+ if *x <= u64::from(u8::max_value()) {
+ let x: u8 = *x as u8;
+ if x.is_ascii_graphic() {
+ let x: char = x as char;
+ if x != '"' && x != '\'' && x != '\\' {
+ return x.to_string();
}
}
- format!("\\u{{{:02X}}}", x)
}
+ format!("\\u{{{:02X}}}", x)
+}
+
+/// Allow control over some of the pest properties of the outputted rule
+pub struct PestyRule {
+ pub silent: bool,
+ pub elements: Alternation,
+}
+
+impl Pretty for (String, PestyRule) {
+ fn pretty(&self) -> Doc<'static, BoxDoc<'static, ()>> {
+ Doc::nil()
+ .append(Doc::text(self.0.clone()))
+ .append(Doc::text(" = "))
+ .append(Doc::text(if self.1.silent { "_" } else { "" }))
+ .append(Doc::text("{"))
+ .append(Doc::space().append(self.1.elements.pretty()).nest(2))
+ .append(Doc::space())
+ .append(Doc::text("}"))
+ .group()
+ }
+}
+
+
+/// Parse an abnf file. Returns a map of rules.
+pub fn parse_abnf(data: &[u8]) -> Result<HashMap<String, PestyRule>, std::io::Error> {
let make_err =
|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e));
+ let rules: Vec<Rule> = abnf::abnf::rulelist_comp(&data).map_err(make_err)?.1;
+ Ok(rules.into_iter().map(|rule| {
+ let name = escape_rulename(&rule.name);
+ (name.clone(), PestyRule {
+ silent: false,
+ elements: rule.elements.clone(),
+ })
+ }).collect())
+}
- let rules = rulelist_comp(&data).map_err(make_err)?.1;
- let formatted_rules =
- rules.into_iter().map(|x| format_rule(x, rule_settings));
- let doc: Doc<_> = Doc::intersperse(formatted_rules, Doc::newline());
- Ok(format!("{}", doc.pretty(80)))
+pub fn render_rules_to_pest<I>(rules: I) -> Doc<'static, BoxDoc<'static, ()>, ()>
+where I: IntoIterator<Item=(String, PestyRule)>,
+{
+ let pretty_rules = rules.into_iter().map(|x| x.pretty());
+ let doc: Doc<_> = Doc::intersperse(pretty_rules, Doc::newline());
+ doc
}
diff --git a/dhall_parser/build.rs b/dhall_parser/build.rs
index d30e31e..2bafba8 100644
--- a/dhall_parser/build.rs
+++ b/dhall_parser/build.rs
@@ -1,10 +1,9 @@
-use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Write};
use std::path::Path;
-use abnf_to_pest::{abnf_to_pest, PestRuleSettings};
+use abnf_to_pest::render_rules_to_pest;
fn main() -> std::io::Result<()> {
// TODO: upstream changes to grammar
@@ -20,49 +19,32 @@ fn main() -> std::io::Result<()> {
file.read_to_end(&mut data)?;
data.push('\n' as u8);
- let mut rule_settings: HashMap<String, PestRuleSettings> = HashMap::new();
+ let mut rules = abnf_to_pest::parse_abnf(&data)?;
for line in BufReader::new(File::open(visibility_path)?).lines() {
let line = line?;
if line.len() >= 2 && &line[0..2] == "# " {
- rule_settings.insert(
- line[2..].into(),
- PestRuleSettings {
- visible: false,
- ..Default::default()
- },
- );
- } else {
- rule_settings.insert(
- line,
- PestRuleSettings {
- visible: true,
- ..Default::default()
- },
- );
+ rules.get_mut(&line[2..]).map(|x| x.silent = true);
}
}
- rule_settings.insert(
- "simple_label".to_owned(),
- PestRuleSettings {
- visible: true,
- replace: Some(
- "
- keyword_raw ~ simple_label_next_char+
- | !keyword_raw ~ simple_label_first_char ~ simple_label_next_char*
- "
- .to_owned(),
- ),
- },
- );
+ rules.remove("simple_label");
let mut file = File::create(pest_path)?;
writeln!(&mut file, "// AUTO-GENERATED FILE. See build.rs.")?;
- writeln!(&mut file, "{}", abnf_to_pest(&data, &rule_settings)?)?;
+ writeln!(&mut file, "{}", render_rules_to_pest(rules).pretty(80))?;
+
+ writeln!(&mut file)?;
+ writeln!(
+ &mut file,
+ "simple_label = _{{
+ keyword_raw ~ simple_label_next_char+
+ | !keyword_raw ~ simple_label_first_char ~ simple_label_next_char*
+ }}"
+ )?;
writeln!(
&mut file,
"keyword_raw = _{{
- let_raw | in_raw | if_raw | then_raw
- | else_raw | Infinity_raw | NaN_raw
+ let_raw | in_raw | if_raw | then_raw
+ | else_raw | Infinity_raw | NaN_raw
}}"
)?;
writeln!(