summaryrefslogtreecommitdiff
path: root/pest_consume
diff options
context:
space:
mode:
Diffstat (limited to 'pest_consume')
-rw-r--r--pest_consume/Cargo.toml13
-rw-r--r--pest_consume/src/lib.rs123
2 files changed, 136 insertions, 0 deletions
diff --git a/pest_consume/Cargo.toml b/pest_consume/Cargo.toml
new file mode 100644
index 0000000..b3f1db7
--- /dev/null
+++ b/pest_consume/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "pest_consume"
+version = "0.1.0" # remember to update html_root_url
+authors = ["Nadrieril <nadrieril@users.noreply.github.com>"]
+license = "MIT OR Apache-2.0"
+edition = "2018"
+description = "A framework for processing the output of a pest-generated parser"
+readme = "README.md"
+repository = "https://github.com/Nadrieril/dhall-rust"
+
+[dependencies]
+pest = "2.1"
+dhall_proc_macros = { path = "../dhall_proc_macros" }
diff --git a/pest_consume/src/lib.rs b/pest_consume/src/lib.rs
new file mode 100644
index 0000000..1cedc74
--- /dev/null
+++ b/pest_consume/src/lib.rs
@@ -0,0 +1,123 @@
+use pest::error::{Error, ErrorVariant};
+use pest::iterators::Pair;
+use pest::Span;
+
+pub use dhall_proc_macros::{make_parser, parse_children};
+
+/// Carries a pest Pair alongside custom user data.
+#[derive(Debug, Clone)]
+pub struct ParseInput<'input, 'data, Rule, Data>
+where
+ Rule: pest::RuleType,
+{
+ pair: Pair<'input, Rule>,
+ user_data: &'data Data,
+}
+
+impl<'input, 'data, Rule, Data> ParseInput<'input, 'data, Rule, Data>
+where
+ Rule: pest::RuleType,
+{
+ pub fn new(pair: Pair<'input, Rule>, user_data: &'data Data) -> Self {
+ ParseInput { pair, user_data }
+ }
+ /// Create an error that points to the span of the input.
+ pub fn error(&self, message: String) -> Error<Rule> {
+ let message = format!(
+ "{} while matching on:\n{}",
+ message,
+ debug_pair(self.pair.clone())
+ );
+ Error::new_from_span(
+ ErrorVariant::CustomError { message },
+ self.as_span(),
+ )
+ }
+ /// Reconstruct the input with a new pair, passing the user data along.
+ pub fn with_pair(&self, new_pair: Pair<'input, Rule>) -> Self {
+ ParseInput {
+ pair: new_pair,
+ user_data: self.user_data,
+ }
+ }
+ /// If the contained pair has exactly one child, return a new Self containing it.
+ pub fn single_child(&self) -> Option<Self> {
+ let mut children = self.pair.clone().into_inner();
+ if let Some(child) = children.next() {
+ if children.next().is_none() {
+ return Some(self.with_pair(child));
+ }
+ }
+ None
+ }
+
+ pub fn user_data(&self) -> &'data Data {
+ self.user_data
+ }
+ pub fn as_pair(&self) -> &Pair<'input, Rule> {
+ &self.pair
+ }
+ pub fn as_span(&self) -> Span<'input> {
+ self.pair.as_span()
+ }
+ pub fn as_str(&self) -> &'input str {
+ self.pair.as_str()
+ }
+ pub fn as_rule(&self) -> Rule {
+ self.pair.as_rule()
+ }
+}
+
+/// Used by the macros.
+pub trait PestConsumer {
+ type Rule: pest::RuleType;
+ fn rule_alias(rule: Self::Rule) -> String;
+ fn allows_shortcut(rule: Self::Rule) -> bool;
+}
+
+/// Pretty-print a pair and its nested children.
+fn debug_pair<Rule: pest::RuleType>(pair: Pair<Rule>) -> String {
+ use std::fmt::Write;
+ let mut s = String::new();
+ fn aux<Rule: pest::RuleType>(
+ s: &mut String,
+ indent: usize,
+ prefix: String,
+ pair: Pair<Rule>,
+ ) {
+ let indent_str = "| ".repeat(indent);
+ let rule = pair.as_rule();
+ let contents = pair.as_str();
+ let mut inner = pair.into_inner();
+ let mut first = true;
+ while let Some(p) = inner.next() {
+ if first {
+ first = false;
+ let last = inner.peek().is_none();
+ if last && p.as_str() == contents {
+ let prefix = format!("{}{:?} > ", prefix, rule);
+ aux(s, indent, prefix, p);
+ continue;
+ } else {
+ writeln!(
+ s,
+ r#"{}{}{:?}: "{}""#,
+ indent_str, prefix, rule, contents
+ )
+ .unwrap();
+ }
+ }
+ aux(s, indent + 1, "".into(), p);
+ }
+ if first {
+ writeln!(
+ s,
+ r#"{}{}{:?}: "{}""#,
+ indent_str, prefix, rule, contents
+ )
+ .unwrap();
+ }
+ }
+ aux(&mut s, 0, "".into(), pair);
+ s
+}