summaryrefslogtreecommitdiff
path: root/abnf_to_pest
diff options
context:
space:
mode:
Diffstat (limited to 'abnf_to_pest')
-rw-r--r--abnf_to_pest/Cargo.toml10
-rw-r--r--abnf_to_pest/src/lib.rs114
2 files changed, 124 insertions, 0 deletions
diff --git a/abnf_to_pest/Cargo.toml b/abnf_to_pest/Cargo.toml
new file mode 100644
index 0000000..bfa6b6d
--- /dev/null
+++ b/abnf_to_pest/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "abnf_to_pest"
+version = "0.1.0"
+authors = ["Nadrieril <nadrieril@users.noreply.github.com>"]
+edition = "2018"
+
+[dependencies]
+abnf = { git = "https://github.com/Nadrieril/abnf" }
+itertools = "0.8.0"
+pretty = "0.5.2"
diff --git a/abnf_to_pest/src/lib.rs b/abnf_to_pest/src/lib.rs
new file mode 100644
index 0000000..cb7ba99
--- /dev/null
+++ b/abnf_to_pest/src/lib.rs
@@ -0,0 +1,114 @@
+use std::collections::HashMap;
+use itertools::Itertools;
+
+pub struct PestRuleSettings {
+ pub visible: bool,
+ pub replace: Option<String>,
+}
+
+impl Default for PestRuleSettings {
+ fn default() -> Self {
+ PestRuleSettings { visible: true, replace: None }
+ }
+}
+
+pub fn abnf_to_pest(data: &Vec<u8>, rule_settings: &HashMap<String, PestRuleSettings>) -> std::io::Result<String> {
+ use abnf::abnf::*;
+ use pretty::{Doc, BoxDoc};
+ 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" {
+ x + "_"
+ } else {
+ x
+ }
+ }
+ fn format_alternation(x: Alternation) -> Doc<'static, BoxDoc<'static, ()>> {
+ Doc::intersperse(
+ x.concatenations.into_iter().map(format_concatenation),
+ Doc::space().append(Doc::text("| "))
+ )
+ }
+ fn format_concatenation(x: Concatenation) -> Doc<'static, BoxDoc<'static, ()>> {
+ Doc::intersperse(
+ x.repetitions.into_iter().map(format_repetition),
+ Doc::text(" ~ ")
+ )
+ }
+ fn format_repetition(x: Repetition) -> Doc<'static, BoxDoc<'static, ()>> {
+ format_element(x.element)
+ .append(x.repeat.map(format_repeat).map(Doc::text).unwrap_or(Doc::nil()))
+ }
+ fn format_repeat(x: Repeat) -> String {
+ match (x.min.unwrap_or(0), x.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, ()>> {
+ use abnf::abnf::Element::*;
+ match x {
+ Rulename(s) => Doc::text(format_rulename(s)),
+ Group(g) =>
+ Doc::text("(")
+ .append(format_alternation(g.alternation).nest(4).group())
+ .append(Doc::text(")")),
+ Option(o) =>
+ Doc::text("(")
+ .append(format_alternation(o.alternation).nest(4).group())
+ .append(Doc::text(")?")),
+ CharVal(s) => Doc::text(format!("^\"{}\"", s.replace("\"", "\\\"").replace("\\", "\\\\"))),
+ NumVal(r) => Doc::text(format_range(r)),
+ ProseVal(_) => unimplemented!(),
+ }
+ }
+ fn format_range(x: Range) -> String {
+ use abnf::abnf::Range::*;
+ match x {
+ Range(x, y) => format!("'{}'..'{}'", format_char(x), format_char(y)),
+ OneOf(v) => format!("\"{}\"", v.into_iter().map(format_char).join("")),
+ }
+ }
+ fn format_char(x: u64) -> String {
+ if x <= (u8::max_value() as u64) {
+ 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)
+ }
+ let make_err = |e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e));
+
+ 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)))
+}
+