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}