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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! Diagnostic errors produced by the parser.

use crate::diagnostics::{DiagnosticRecord, DiagnosticRegistry};

macro_rules! define_errors {
    ($($(#[doc = $doc:expr])+ $code:ident: $error:ident $gen_macro:tt)*) => {$(
        $(#[doc = $doc])+
        pub(crate) struct $error;

        impl DiagnosticRecord for $error {
            const CODE: &'static str = stringify!($code);
            const EXPLANATION: &'static str = concat!($($doc, "\n"),+);
        })*

        pub struct ParseErrors;

        impl DiagnosticRegistry for ParseErrors {
            fn codes_with_explanations() -> Vec<(&'static str, &'static str)> {
                let mut vec = Vec::new();
                $(vec.push(($error::CODE, $error::EXPLANATION));)*
                vec
            }
        }

        $(
            macro_rules! $error $gen_macro
        )*
    };
}

define_errors! {
    ///This error fires on tokens that are not connected to the rest of a primary statement in a
    ///slide program.
    ///
    ///For example, in the program
    ///
    ///```text
    ///1 + 2 3 + 4
    ///      ^^^^^- offending tokens
    ///```
    ///
    ///`3 + 4` are not connected to the primary expression statement `1 + 2`, and slide does not
    ///know how this is intended to be evaluated.
    ///
    ///In the future, statement that are separated by a newline will not emit this error. The
    ///following are examples of programs that currently emit this error, but in the future should not:
    ///
    ///```text
    ///a = 1
    ///b = 2 - in the future, parsed as two assignment statements
    ///```
    ///
    ///```text
    ///1 + 2
    ///3 + 4 - in the future, parsed as two expression statements
    ///```
    P0001: ExtraTokens {
        ($span:expr) => {
            Diagnostic::span_err(
                $span,
                "Unexpected extra tokens",
                ExtraTokens::CODE,
                "not connected to a primary statement".to_string(),
            )
        }
    }

    ///This error fires on token sequences that are expected to parse as an expression, but do not.
    ///
    ///The following are examples of slide programs that emit this error:
    ///
    ///```text
    ///1 + +
    ///    ^- offending token
    ///```
    ///
    ///```text
    ///1 / )
    ///    ^- offending token
    ///```
    ///
    ///```text
    ///1 /
    ///   ^- offending token: end of file
    ///```
    ///
    ///In all cases such programs are malformed and should be refactored to include complete
    ///expressions.
    P0002: ExpectedExpr {
        ($span:expr, $found:expr) => {
            Diagnostic::span_err(
                $span,
                format!("Expected an expression, found {}", $found),
                ExpectedExpr::CODE,
                "expected an expression".to_string(),
            )
        }
    }

    ///All opening delimiters with closing pairs must have that closing delimiter as a
    ///correctly-ordered complement in a slide program. In particular,
    ///
    ///  - `(` and `)` are complements (parantheses)
    ///  - `[` and `]` are complements (brackets)
    ///
    ///The most obvious case for a mismatch is when an incorrect complement is used, for example in
    ///`(1+2]` or `[1+2)`.
    ///
    ///A complement to this is that nesting order must be obeyed. That is, `([1 + 2])` is valid but
    ///`([1 + 2)]` is not.
    ///
    ///Finally, a more subtle case may be when one set of delimiters is not properly closed, as in
    ///the case
    ///
    ///```text
    ///([1 + 2)
    ///       ^- expected closing `]`
    ///```
    P0003: MismatchedClosingDelimiter {
        (expected $expected:expr, at $cur_span:expr, due to $opener:expr, at $open_span:expr; found $found:expr) => {
            Diagnostic::span_err(
                $cur_span,
                format!("Mismatched closing delimiter `{}`", $found),
                MismatchedClosingDelimiter::CODE,
                format!("expected closing `{}`", $expected),
            )
            .with_spanned_help($open_span, format!("opening `{}` here", $opener))
        }
    }

    ///Patterns are illegal in a "regular" slide program; i.e. a program including a standard
    ///expression.
    ///
    ///In most cases, this error is fired because you intended to run an expression pattern through
    ///slide, or wrote a variable in the form of a pattern.
    ///
    ///Because patterns are abstractions over expressions, they cannot be evaluated in the way an
    ///expression can without first being filled in by an expression. As an analogy, saying you
    ///have "eaten groceries" does not provide concrete information about what you have eaten
    ///without first defining what the groceries are.
    P0004: IllegalPattern {
        ($span:expr, $pat_name:expr) => {
            Diagnostic::span_err(
                $span,
                "Patterns cannot be used in an expression",
                IllegalPattern::CODE,
                "unexpected pattern".to_string(),
            )
            .with_help(format!(
                r#"consider using "{cut_name}" as a variable"#,
                cut_name = $pat_name.substring(1, $pat_name.len() - 1)
            ))
        }
    }

    ///Variables are illegal in a slide expression pattern.
    ///
    ///In most cases, this error is fired because you intended to evaluate an expression with
    ///slide, or wrote a variable in place of a variable pattern.
    ///
    ///Because expression patterns are meant to abstract over and match expressions, there is
    ///generally not a need to explicitly define the name of a variable to be matched by an
    ///expression pattern. Rather, the concern is generally with the shape of the variable; that
    ///is, that it is actually a variable. For this use case, the "${name}" pattern (where "{name}"
    ///is a text placeholder) serves as a variable-matching pattern.
    ///
    ///As a concrete example, the expression pattern `$a + $b + $a` matches both the expressions
    ///`a + b + a` and `b + a + b`. Both expressions are lowered the same way despite having
    ///different variable names, so variable patterns permit abstraction and common representation
    ///over the names.
    P0005: IllegalVariable {
        ($span:expr, $var_name:expr) => {
            Diagnostic::span_err(
                $span,
                "Variables cannot be used in an expression pattern",
                IllegalVariable::CODE,
                Some("unexpected variable".into()),
            )
            .with_help(format!(
                r##"consider using "${name}", "#{name}", or "_{name}" as a pattern"##,
                name = $var_name,
            ))
        }
    }

    ///All closing delimiters with opening pairs must have that opening delimiter as a complement in
    ///a slide program. In particular,
    ///
    ///  - `)` and `(` are complements (parantheses)
    ///  - `]` and `[` are complements (brackets)
    ///
    ///An unmatched closing delimiter error occurs when corresponding opening delimiters are not
    ///present earlier in the slide program. Some examples include:
    ///
    ///```text
    ///1 + 2 )
    ///      ^ unmatched closing delimiter
    ///```
    ///
    ///```text
    ///1 + 2
    ///)]
    ///^ unmatched closing delimiter
    /// ^ unmatched closing delimiter
    ///```
    P0006: UnmatchedClosingDelimiter {
        ($span:expr, $found:expr) => {
            Diagnostic::span_err(
                $span,
                format!(r#"Unmatched closing delimiter "{}""#, $found),
                UnmatchedClosingDelimiter::CODE,
                format!(r#"has no matching opener "{}""#, $found.matcher()),
            )
        }
    }
}