arithmetic_parser/parser/
mod.rs1use nom::{
4 branch::alt,
5 bytes::complete::tag,
6 character::complete::char as tag_char,
7 combinator::{cut, map, not, opt, peek},
8 multi::many0,
9 sequence::{delimited, preceded, terminated},
10 Err as NomErr, Parser as _,
11};
12
13pub use self::helpers::is_valid_variable_name;
14use self::{
15 expr::expr,
16 helpers::{ws, Complete, GrammarType, Streaming},
17 lvalue::{destructure, lvalue},
18};
19use crate::{
20 alloc::{vec, Box},
21 grammars::Parse,
22 spans::with_span,
23 Block, Error, ErrorKind, FnDefinition, InputSpan, NomResult, SpannedStatement, Statement,
24};
25
26mod expr;
27mod helpers;
28mod lvalue;
29#[cfg(test)]
30mod tests;
31
32#[allow(clippy::option_if_let_else)]
33fn statement<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedStatement<'_, T::Base>>
34where
35 T: Parse,
36 Ty: GrammarType,
37{
38 let assignment = (tag("="), peek(not(tag_char('='))));
39 let assignment_parser = (
40 opt(terminated(
41 lvalue::<T, Ty>,
42 delimited(ws::<Ty>, assignment, ws::<Ty>),
43 )),
44 expr::<T, Ty>,
45 );
46
47 with_span(map(assignment_parser, |(lvalue, rvalue)| {
48 if let Some(lvalue) = lvalue {
50 Statement::Assignment {
51 lhs: lvalue,
52 rhs: Box::new(rvalue),
53 }
54 } else {
55 Statement::Expr(rvalue)
56 }
57 }))
58 .parse(input)
59}
60
61pub(crate) fn statements<T>(input_span: InputSpan<'_>) -> Result<Block<'_, T::Base>, Error>
63where
64 T: Parse,
65{
66 if !input_span.fragment().is_ascii() {
67 return Err(Error::new(input_span, ErrorKind::NonAsciiInput));
68 }
69 statements_inner::<T, Complete>(input_span)
70}
71
72pub(crate) fn streaming_statements<T>(
74 input_span: InputSpan<'_>,
75) -> Result<Block<'_, T::Base>, Error>
76where
77 T: Parse,
78{
79 if !input_span.fragment().is_ascii() {
80 return Err(Error::new(input_span, ErrorKind::NonAsciiInput));
81 }
82
83 statements_inner::<T, Complete>(input_span)
84 .or_else(|_| statements_inner::<T, Streaming>(input_span))
85}
86
87fn statements_inner<T, Ty>(input_span: InputSpan<'_>) -> Result<Block<'_, T::Base>, Error>
88where
89 T: Parse,
90 Ty: GrammarType,
91{
92 delimited(ws::<Ty>, separated_statements::<T, Ty>, ws::<Ty>)
93 .parse(input_span)
94 .map_err(|e| match e {
95 NomErr::Failure(e) | NomErr::Error(e) => e,
96 NomErr::Incomplete(_) => ErrorKind::Incomplete.with_span(&input_span.into()),
97 })
98 .and_then(|(remaining, statements)| {
99 if remaining.fragment().is_empty() {
100 Ok(statements)
101 } else {
102 Err(ErrorKind::Leftovers.with_span(&remaining.into()))
103 }
104 })
105}
106
107fn separated_statement<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedStatement<'_, T::Base>>
108where
109 T: Parse,
110 Ty: GrammarType,
111{
112 terminated(statement::<T, Ty>, preceded(ws::<Ty>, tag_char(';'))).parse(input)
113}
114
115fn separated_statements<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, Block<'_, T::Base>>
117where
118 T: Parse,
119 Ty: GrammarType,
120{
121 map(
122 (
123 many0(terminated(separated_statement::<T, Ty>, ws::<Ty>)),
124 opt(expr::<T, Ty>),
125 ),
126 |(statements, return_value)| Block {
127 statements,
128 return_value: return_value.map(Box::new),
129 },
130 )
131 .parse(input)
132}
133
134fn block<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, Block<'_, T::Base>>
136where
137 T: Parse,
138 Ty: GrammarType,
139{
140 preceded(
141 terminated(tag_char('{'), ws::<Ty>),
142 cut(terminated(
143 separated_statements::<T, Ty>,
144 preceded(ws::<Ty>, tag_char('}')),
145 )),
146 )
147 .parse(input)
148}
149
150fn fn_def<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, FnDefinition<'_, T::Base>>
152where
153 T: Parse,
154 Ty: GrammarType,
155{
156 let body_parser = alt((
157 block::<T, Ty>,
158 map(expr::<T, Ty>, |spanned| Block {
159 statements: vec![],
160 return_value: Some(Box::new(spanned)),
161 }),
162 ));
163
164 let args_parser = preceded(
165 terminated(tag_char('|'), ws::<Ty>),
166 cut(terminated(
167 destructure::<T, Ty>,
168 preceded(ws::<Ty>, tag_char('|')),
169 )),
170 );
171
172 let parser = (with_span(args_parser), cut(preceded(ws::<Ty>, body_parser)));
173 map(parser, |(args, body)| FnDefinition { args, body }).parse(input)
174}