arithmetic_parser/grammars/
traits.rs

1use core::{fmt, marker::PhantomData};
2
3use anyhow::anyhow;
4use bitflags::bitflags;
5use nom::{Err as NomErr, Input};
6
7use crate::{
8    parser::{statements, streaming_statements},
9    Block, Error, ErrorKind, InputSpan, NomResult,
10};
11
12bitflags! {
13    /// Parsing features used to configure [`Parse`] implementations.
14    #[derive(Debug, Clone, Copy)]
15    pub struct Features: u64 {
16        /// Enables parsing tuples.
17        const TUPLES = 1;
18        /// Enables parsing type annotations.
19        const TYPE_ANNOTATIONS = 2;
20        /// Enables parsing function definitions.
21        const FN_DEFINITIONS = 4;
22        /// Enables parsing blocks.
23        const BLOCKS = 8;
24        /// Enables parsing methods.
25        const METHODS = 16;
26        /// Enables parsing equality comparisons (`==`, `!=`), the `!` unary op and
27        /// `&&`, `||` binary ops.
28        const BOOLEAN_OPS_BASIC = 32;
29        /// Enables parsing order comparisons (`>`, `<`, `>=`, `<=`).
30        const ORDER_COMPARISONS = 64;
31        /// Enables parsing objects.
32        const OBJECTS = 128;
33
34        /// Enables all Boolean operations.
35        const BOOLEAN_OPS = Self::BOOLEAN_OPS_BASIC.bits() | Self::ORDER_COMPARISONS.bits();
36    }
37}
38
39impl Features {
40    /// Creates a copy of these `Features` without any of the flags in `other`.
41    #[must_use]
42    pub const fn without(self, other: Self) -> Self {
43        Self::from_bits_truncate(self.bits() & !other.bits())
44    }
45}
46
47/// Encapsulates parsing literals in a grammar.
48///
49/// # Examples
50///
51/// If your grammar does not need to support type annotations, you can define a `ParseLiteral` impl
52/// and wrap it into [`Untyped`] to get a [`Grammar`] / [`Parse`]:
53///
54/// ```
55/// use arithmetic_parser::{
56///     grammars::{Features, ParseLiteral, Parse, Untyped},
57///     ErrorKind, InputSpan, NomResult,
58/// };
59///
60/// /// Grammar that parses `u64` numbers.
61/// #[derive(Debug)]
62/// struct IntegerGrammar;
63///
64/// impl ParseLiteral for IntegerGrammar {
65///     type Lit = u64;
66///
67///     // We use the `nom` crate to construct necessary parsers.
68///     fn parse_literal(input: InputSpan<'_>) -> NomResult<'_, Self::Lit> {
69///         use nom::{character::complete::digit1, combinator::map_res, Parser};
70///         let parser = |s: InputSpan<'_>| {
71///             s.fragment().parse().map_err(ErrorKind::literal)
72///         };
73///         map_res(digit1, parser).parse(input)
74///     }
75/// }
76///
77/// // Here's how a grammar can be used.
78/// # fn main() -> anyhow::Result<()> {
79/// let program = "
80///     x = 1 + 2 * 3 + sin(a^3 / b^2);
81///     some_function = |a, b| (a + b, a - b);
82///     other_function = |x| {
83///         r = min(rand(), 0);
84///         r * x
85///     };
86///     (y, z) = some_function({ x = x - 1; x }, x);
87///     other_function(y - z)
88/// ";
89/// let parsed = Untyped::<IntegerGrammar>::parse_statements(program)?;
90/// println!("{:#?}", parsed);
91/// # Ok(())
92/// # }
93/// ```
94pub trait ParseLiteral: 'static {
95    /// Type of the literal used in the grammar.
96    type Lit: Clone + fmt::Debug;
97
98    /// Attempts to parse a literal.
99    ///
100    /// Literals should follow these rules:
101    ///
102    /// - A literal must be distinguished from other constructs, in particular,
103    ///   variable identifiers.
104    /// - If a literal may end with `.` and methods are enabled in [`Parse::FEATURES`],
105    ///   care should be taken for cases when `.` is a part of a call, rather than a part
106    ///   of a literal. For example, a parser for real-valued literals should interpret `1.abs()`
107    ///   as a call of the `abs` method on receiver `1`, rather than `1.` followed by
108    ///   ineligible `abs()`.
109    ///
110    /// If a literal may start with `-` or `!` (in general, unary ops), these ops will be
111    /// consumed as a part of the literal, rather than `Expr::Unary`, *unless* the literal
112    /// is followed by an eligible higher-priority operation (i.e., a method call) *and*
113    /// the literal without a preceding unary op is still eligible. That is, if `-1` and `1`
114    /// are both valid literals, then `-1.abs()` will be parsed as negation applied to `1.abs()`.
115    /// On the other hand, if `!foo!` is a valid literal, but `foo!` isn't, `!foo!.bar()` will
116    /// be parsed as method `bar()` called on `!foo!`.
117    ///
118    /// # Return value
119    ///
120    /// The output should follow `nom` conventions on errors / failures.
121    fn parse_literal(input: InputSpan<'_>) -> NomResult<'_, Self::Lit>;
122}
123
124/// Extension of `ParseLiteral` that parses type annotations.
125///
126/// # Examples
127///
128/// Use a [`Typed`] wrapper to create a parser from the grammar, which will support all
129/// parsing [`Features`]:
130///
131/// ```
132/// # use arithmetic_parser::{
133/// #     grammars::{Features, ParseLiteral, Grammar, Parse, Typed},
134/// #     ErrorKind, InputSpan, NomResult,
135/// # };
136/// /// Grammar that parses `u64` numbers and has a single type annotation, `Num`.
137/// #[derive(Debug)]
138/// struct IntegerGrammar;
139///
140/// impl ParseLiteral for IntegerGrammar {
141///     type Lit = u64;
142///
143///     fn parse_literal(input: InputSpan<'_>) -> NomResult<'_, Self::Lit> {
144///         use nom::{character::complete::digit1, combinator::map_res, Parser};
145///         let parser = |s: InputSpan<'_>| {
146///             s.fragment().parse().map_err(ErrorKind::literal)
147///         };
148///         map_res(digit1, parser).parse(input)
149///     }
150/// }
151///
152/// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
153/// struct Num;
154///
155/// impl Grammar for IntegerGrammar {
156///     type Type<'a> = Num;
157///
158///     fn parse_type(input: InputSpan<'_>) -> NomResult<'_, Self::Type<'_>> {
159///         use nom::{bytes::complete::tag, combinator::map, Parser};
160///         map(tag("Num"), |_| Num).parse(input)
161///     }
162/// }
163///
164/// // Here's how a grammar can be used.
165/// # fn main() -> anyhow::Result<()> {
166/// let program = "
167///     x = 1 + 2 * 3 + sin(a^3 / b^2);
168///     some_function = |a, b: Num| (a + b, a - b);
169///     other_function = |x| {
170///         r = min(rand(), 0);
171///         r * x
172///     };
173///     (y, z: Num) = some_function({ x = x - 1; x }, x);
174///     other_function(y - z)
175/// ";
176/// let parsed = Typed::<IntegerGrammar>::parse_statements(program)?;
177/// println!("{:#?}", parsed);
178/// # Ok(())
179/// # }
180/// ```
181pub trait Grammar: ParseLiteral {
182    /// Type of the type annotation used in the grammar. This type may be parametric by the input lifetime.
183    type Type<'a>: 'a + Clone + fmt::Debug;
184
185    /// Attempts to parse a type annotation.
186    ///
187    /// # Return value
188    ///
189    /// The output should follow `nom` conventions on errors / failures.
190    fn parse_type(input: InputSpan<'_>) -> NomResult<'_, Self::Type<'_>>;
191}
192
193/// Helper trait allowing `Parse` to accept multiple types as inputs.
194pub trait IntoInputSpan<'a> {
195    /// Converts input into a span.
196    fn into_input_span(self) -> InputSpan<'a>;
197}
198
199impl<'a> IntoInputSpan<'a> for InputSpan<'a> {
200    fn into_input_span(self) -> InputSpan<'a> {
201        self
202    }
203}
204
205impl<'a> IntoInputSpan<'a> for &'a str {
206    fn into_input_span(self) -> InputSpan<'a> {
207        InputSpan::new(self)
208    }
209}
210
211/// Unites all necessary parsers to form a complete grammar definition.
212///
213/// The two extension points for a `Grammar` are *literals* and *type annotations*. They are
214/// defined via [`Base`](Self::Base) type.
215/// A `Grammar` also defines a set of supported [`Features`]. This allows to customize which
216/// constructs are parsed.
217///
218/// Most common sets of `Features` are covered by [`Typed`] and [`Untyped`] wrappers;
219/// these types allow to not declare `Parse` impl explicitly. It is still possible
220/// to define custom `Parse` implementations, as shown in the example below.
221///
222/// # Examples
223///
224/// ```
225/// # use arithmetic_parser::{
226/// #     grammars::{Features, ParseLiteral, Parse, Untyped},
227/// #     ErrorKind, InputSpan, NomResult,
228/// # };
229/// #[derive(Debug)]
230/// struct IntegerGrammar;
231///
232/// impl ParseLiteral for IntegerGrammar {
233///     // Implementation skipped...
234/// #   type Lit = u64;
235/// #   fn parse_literal(input: InputSpan<'_>) -> NomResult<'_, Self::Lit> {
236/// #       use nom::{character::complete::digit1, combinator::map_res, Parser};
237/// #       let parser = |s: InputSpan<'_>| s.fragment().parse().map_err(ErrorKind::literal);
238/// #       map_res(digit1, parser).parse(input)
239/// #   }
240/// }
241///
242/// impl Parse for IntegerGrammar {
243///     type Base = Untyped<Self>;
244///     const FEATURES: Features = Features::empty();
245/// }
246///
247/// // Here's how a grammar can be used.
248/// # fn main() -> anyhow::Result<()> {
249/// let program = "x = 1 + 2 * 3 + sin(a^3 / b^2);";
250/// let parsed = IntegerGrammar::parse_statements(program)?;
251/// println!("{:#?}", parsed);
252/// # Ok(())
253/// # }
254/// ```
255pub trait Parse {
256    /// Base for the grammar providing the literal and type annotation parsers.
257    type Base: Grammar;
258    /// Features supported by this grammar.
259    const FEATURES: Features;
260
261    /// Parses a list of statements.
262    fn parse_statements<'a, I>(input: I) -> Result<Block<'a, Self::Base>, Error>
263    where
264        I: IntoInputSpan<'a>,
265        Self: Sized,
266    {
267        statements::<Self>(input.into_input_span())
268    }
269
270    /// Parses a potentially incomplete list of statements.
271    fn parse_streaming_statements<'a, I>(input: I) -> Result<Block<'a, Self::Base>, Error>
272    where
273        I: IntoInputSpan<'a>,
274        Self: Sized,
275    {
276        streaming_statements::<Self>(input.into_input_span())
277    }
278}
279
280/// Wrapper for [`ParseLiteral`] types that allows to use them as a [`Grammar`] or [`Parse`]r.
281///
282/// When used as a `Grammar`, type annotations are not supported; any use of an annotation will
283/// lead to a parsing error. When used as a `Parse`r, all [`Features`] are on except for
284/// type annotations.
285///
286/// # Examples
287///
288/// See [`ParseLiteral`] docs for an example of usage.
289#[derive(Debug)]
290pub struct Untyped<T>(PhantomData<T>);
291
292impl<T: ParseLiteral> ParseLiteral for Untyped<T> {
293    type Lit = T::Lit;
294
295    #[inline]
296    fn parse_literal(input: InputSpan<'_>) -> NomResult<'_, Self::Lit> {
297        T::parse_literal(input)
298    }
299}
300
301impl<T: ParseLiteral> Grammar for Untyped<T> {
302    type Type<'a> = ();
303
304    #[inline]
305    fn parse_type(input: InputSpan<'_>) -> NomResult<'_, Self::Type<'_>> {
306        let err = anyhow!("Type annotations are not supported by this parser");
307        let err = Error::new(input, ErrorKind::Type(err));
308        Err(NomErr::Failure(err))
309    }
310}
311
312impl<T: ParseLiteral> Parse for Untyped<T> {
313    type Base = Self;
314
315    const FEATURES: Features = Features::all().without(Features::TYPE_ANNOTATIONS);
316}
317
318/// Wrapper for [`Grammar`] types that allows to convert the type to a [`Parse`]r. The resulting
319/// parser supports all [`Features`].
320///
321/// # Examples
322///
323/// See [`Grammar`] docs for an example of usage.
324#[derive(Debug)]
325// TODO: consider name change (`Parser`?)
326pub struct Typed<T>(PhantomData<T>);
327
328impl<T: Grammar> Parse for Typed<T> {
329    type Base = T;
330
331    const FEATURES: Features = Features::all();
332}
333
334/// Trait allowing to mock out type annotation support together with [`WithMockedTypes`].
335/// It specifies recognized type annotations; if any other annotation is used, an error
336/// will be raised.
337///
338/// When used as a [`Parse`]r, all [`Features`] are on.
339///
340/// # Examples
341///
342/// ```
343/// # use arithmetic_parser::grammars::{F64Grammar, MockTypes, WithMockedTypes};
344/// struct MockedTypesList;
345///
346/// impl MockTypes for MockedTypesList {
347///     const MOCKED_TYPES: &'static [&'static str] = &["Num"];
348/// }
349///
350/// // Grammar that recognizes `Num` type annotation.
351/// type Grammar = WithMockedTypes<F64Grammar, MockedTypesList>;
352/// ```
353pub trait MockTypes: 'static {
354    /// List of mocked type annotations.
355    const MOCKED_TYPES: &'static [&'static str];
356}
357
358/// Decorator for a grammar that mocks type parsing.
359///
360/// # Examples
361///
362/// See [`MockTypes`] for examples of usage.
363#[derive(Debug)]
364pub struct WithMockedTypes<T, Ty>(PhantomData<(T, Ty)>);
365
366impl<T: ParseLiteral, Ty: MockTypes> ParseLiteral for WithMockedTypes<T, Ty> {
367    type Lit = T::Lit;
368
369    fn parse_literal(input: InputSpan<'_>) -> NomResult<'_, Self::Lit> {
370        T::parse_literal(input)
371    }
372}
373
374impl<T: ParseLiteral, Ty: MockTypes> Grammar for WithMockedTypes<T, Ty> {
375    type Type<'a> = ();
376
377    fn parse_type(input: InputSpan<'_>) -> NomResult<'_, Self::Type<'_>> {
378        fn type_parser<M: MockTypes>(input: InputSpan<'_>) -> NomResult<'_, ()> {
379            for &annotation in M::MOCKED_TYPES {
380                if input.fragment().starts_with(annotation) {
381                    let rest = input.take_from(annotation.len());
382                    return Ok((rest, ()));
383                }
384            }
385            let err = anyhow!("Unrecognized type annotation");
386            let err = Error::new(input, ErrorKind::Type(err));
387            Err(NomErr::Failure(err))
388        }
389
390        type_parser::<Ty>(input)
391    }
392}
393
394impl<T: ParseLiteral, Ty: MockTypes> Parse for WithMockedTypes<T, Ty> {
395    type Base = Self;
396
397    const FEATURES: Features = Features::all();
398}