summaryrefslogtreecommitdiff
path: root/pest_consume/src/lib.rs
blob: 6e4b2e39d8d77c305c4eaf4fb6f08a8d4422da58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use pest::error::{Error, ErrorVariant};
use pest::iterators::Pair;
use pest::Span;

pub use pest_consume_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
}