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