1use crate::core::cache::Allocs;
2use crate::grammar::action_result::ActionResult;
3use crate::grammar::action_result::ActionResult::*;
4use crate::grammar::escaped_string::EscapedString;
5use crate::grammar::rule_action::RuleAction;
6use crate::grammar::{AnnotatedRuleExpr, Block, GrammarFile, Rule, RuleExpr};
7use crate::grammar::{CharClass, RuleAnnotation};
8use std::borrow::Cow;
9
10#[macro_export]
11macro_rules! result_match {
12 {match $e1:expr => $p1:pat_param, $(match $es:expr => $ps:pat_param,)* create $body:expr} => {
13 match $e1 {
14 $p1 => {
15 result_match! { $(match $es => $ps,)* create $body }
16 },
17 _ => None,
18 }
19 };
20 {create $body:expr} => {
21 Some($body)
22 };
23}
24
25pub fn parse_grammarfile<'arn_in, 'arn_out, 'grm>(
26 r: &'arn_in ActionResult<'arn_in, 'grm>,
27 src: &'grm str,
28 allocs: Allocs<'arn_out>,
29 parse_a: impl Fn(
30 &'arn_in ActionResult<'arn_in, 'grm>,
31 &'grm str,
32 ) -> Option<RuleAction<'arn_out, 'grm>>,
33) -> Option<GrammarFile<'arn_out, 'grm>> {
34 result_match! {
35 match r => Construct(_, "GrammarFile", rules),
36 match &rules[..] => [rules],
37 create GrammarFile {
38 rules: allocs.try_alloc_extend(rules.iter_list().map(|rule| parse_rule(rule, src, allocs, &parse_a)))?,
39 }
40 }
41}
42
43fn parse_rule<'arn_in, 'arn_out, 'grm>(
44 r: &'arn_in ActionResult<'arn_in, 'grm>,
45 src: &'grm str,
46 allocs: Allocs<'arn_out>,
47 parse_a: &impl Fn(
48 &'arn_in ActionResult<'arn_in, 'grm>,
49 &'grm str,
50 ) -> Option<RuleAction<'arn_out, 'grm>>,
51) -> Option<Rule<'arn_out, 'grm>> {
52 result_match! {
53 match r => Construct(_, "Rule", rule_body),
54 match &rule_body[..] => [name, extend, args, blocks],
55 create Rule {
56 name: parse_identifier(name, src)?,
57 adapt: extend.iter_list().next().is_some(),
58 args: allocs.try_alloc_extend(args.iter_list().map(|n| parse_identifier(n, src)))?,
59 blocks: allocs.try_alloc_extend(blocks.iter_list().map(|block| parse_block(block, src, allocs, parse_a)))?,
60 }
61 }
62}
63
64fn parse_block<'arn_in, 'arn_out, 'grm>(
65 r: &'arn_in ActionResult<'arn_in, 'grm>,
66 src: &'grm str,
67 allocs: Allocs<'arn_out>,
68 parse_a: &impl Fn(
69 &'arn_in ActionResult<'arn_in, 'grm>,
70 &'grm str,
71 ) -> Option<RuleAction<'arn_out, 'grm>>,
72) -> Option<Block<'arn_out, 'grm>> {
73 result_match! {
74 match r => Construct(_, "Block", b),
75 match &b[..] => [name, extend, cs],
76 create Block { name: parse_identifier(name, src)?,
77 adapt: extend.iter_list().next().is_some(),
78 constructors: parse_constructors(cs, src, allocs, parse_a)? }
79 }
80}
81
82fn parse_constructors<'arn_in, 'arn_out, 'grm>(
83 r: &'arn_in ActionResult<'arn_in, 'grm>,
84 src: &'grm str,
85 allocs: Allocs<'arn_out>,
86 parse_a: &impl Fn(
87 &'arn_in ActionResult<'arn_in, 'grm>,
88 &'grm str,
89 ) -> Option<RuleAction<'arn_out, 'grm>>,
90) -> Option<&'arn_out [AnnotatedRuleExpr<'arn_out, 'grm>]> {
91 result_match! {
92 create allocs.try_alloc_extend(r.iter_list().map(|c| parse_annotated_rule_expr(c, src, allocs, parse_a)))?
93 }
94}
95
96fn parse_annotated_rule_expr<'arn_in, 'arn_out, 'grm>(
97 r: &'arn_in ActionResult<'arn_in, 'grm>,
98 src: &'grm str,
99 allocs: Allocs<'arn_out>,
100 parse_a: &impl Fn(
101 &'arn_in ActionResult<'arn_in, 'grm>,
102 &'grm str,
103 ) -> Option<RuleAction<'arn_out, 'grm>>,
104) -> Option<AnnotatedRuleExpr<'arn_out, 'grm>> {
105 result_match! {
106 match r => Construct(_, "AnnotatedExpr", body),
107 match &body[..] => [annots, e],
108 create AnnotatedRuleExpr(allocs.try_alloc_extend(annots.iter_list().map(|annot| parse_rule_annotation(annot, src)))?, parse_rule_expr(e, src, allocs, parse_a)?)
109 }
110}
111
112fn parse_rule_annotation<'arn_in, 'grm>(
113 r: &'arn_in ActionResult<'arn_in, 'grm>,
114 src: &'grm str,
115) -> Option<RuleAnnotation<'grm>> {
116 Some(match r {
117 Construct(_, "Error", b) => RuleAnnotation::Error(parse_string(&b[0], src)?),
118 Construct(_, "DisableLayout", _) => RuleAnnotation::DisableLayout,
119 Construct(_, "EnableLayout", _) => RuleAnnotation::EnableLayout,
120 Construct(_, "DisableRecovery", _) => RuleAnnotation::DisableRecovery,
121 Construct(_, "EnableRecovery", _) => RuleAnnotation::EnableRecovery,
122 _ => return None,
123 })
124}
125
126fn parse_rule_expr<'arn_in, 'arn_out, 'grm>(
127 r: &'arn_in ActionResult<'arn_in, 'grm>,
128 src: &'grm str,
129 allocs: Allocs<'arn_out>,
130 parse_a: &impl Fn(
131 &'arn_in ActionResult<'arn_in, 'grm>,
132 &'grm str,
133 ) -> Option<RuleAction<'arn_out, 'grm>>,
134) -> Option<RuleExpr<'arn_out, 'grm>> {
135 Some(match r {
136 Construct(_, "Action", b) => RuleExpr::Action(
137 allocs.alloc(parse_rule_expr(&b[0], src, allocs, parse_a)?),
138 parse_a(&b[1], src)?,
139 ),
140 Construct(_, "Choice", b) => RuleExpr::Choice(result_match! {
141 create allocs.try_alloc_extend(b[0].iter_list().map(|sub| parse_rule_expr(sub, src, allocs, parse_a)))?
142 }?),
143 Construct(_, "Sequence", b) => RuleExpr::Sequence(result_match! {
144 create allocs.try_alloc_extend(b[0].iter_list().map(|sub| parse_rule_expr(sub, src, allocs, parse_a)))?
145 }?),
146 Construct(_, "NameBind", b) => RuleExpr::NameBind(
147 parse_identifier(&b[0], src)?,
148 allocs.alloc(parse_rule_expr(&b[1], src, allocs, parse_a)?),
149 ),
150 Construct(_, "Repeat", b) => RuleExpr::Repeat {
151 expr: allocs.alloc(parse_rule_expr(&b[0], src, allocs, parse_a)?),
152 min: parse_u64(&b[1], src)?,
153 max: parse_option(&b[2], src, parse_u64)?,
154 delim: allocs.alloc(parse_rule_expr(&b[3], src, allocs, parse_a)?),
155 },
156 Construct(_, "Literal", b) => RuleExpr::Literal(parse_string(&b[0], src)?),
157 Construct(_, "CharClass", b) => RuleExpr::CharClass(parse_charclass(&b[0], src, allocs)?),
158 Construct(_, "SliceInput", b) => {
159 RuleExpr::SliceInput(allocs.alloc(parse_rule_expr(&b[0], src, allocs, parse_a)?))
160 }
161 Construct(_, "PosLookahead", b) => {
162 RuleExpr::PosLookahead(allocs.alloc(parse_rule_expr(&b[0], src, allocs, parse_a)?))
163 }
164 Construct(_, "NegLookahead", b) => {
165 RuleExpr::NegLookahead(allocs.alloc(parse_rule_expr(&b[0], src, allocs, parse_a)?))
166 }
167 Construct(_, "This", _) => RuleExpr::This,
168 Construct(_, "Next", _) => RuleExpr::Next,
169 Construct(_, "Guid", _) => RuleExpr::Guid,
170 Construct(_, "RunVar", b) => RuleExpr::RunVar(
171 parse_identifier(&b[0], src)?,
172 result_match! {
173 create allocs.try_alloc_extend(b[1].iter_list().map(|sub| {
174 parse_rule_expr(sub, src, allocs, parse_a)
175 }))?
176 }?,
177 ),
178 Construct(_, "AtAdapt", b) => {
179 RuleExpr::AtAdapt(parse_identifier(&b[0], src)?, parse_identifier(&b[1], src)?)
180 }
181 _ => return None,
182 })
183}
184
185pub(crate) fn parse_identifier<'grm>(
186 r: &ActionResult<'_, 'grm>,
187 src: &'grm str,
188) -> Option<&'grm str> {
189 match r {
190 Value(span) => Some(&src[*span]),
191 Literal(s) => match s.to_cow() {
193 Cow::Borrowed(s) => Some(s),
194 Cow::Owned(_) => None,
195 },
196 _ => None,
197 }
198}
199
200pub(crate) fn parse_string<'arn, 'grm>(
201 r: &'arn ActionResult<'arn, 'grm>,
202 src: &'grm str,
203) -> Option<EscapedString<'grm>> {
204 result_match! {
205 match r => Value(span),
206 create EscapedString::from_escaped(&src[*span])
207 }
208}
209
210fn parse_string_char(r: &ActionResult<'_, '_>, src: &str) -> Option<char> {
211 Some(match r {
212 Value(span) => src[*span].chars().next().unwrap(),
213 Literal(c) => c.chars().next().unwrap(),
214 _ => return None,
215 })
216}
217
218fn parse_charclass<'arn_out>(
219 r: &ActionResult<'_, '_>,
220 src: &str,
221 allocs: Allocs<'arn_out>,
222) -> Option<CharClass<'arn_out>> {
223 result_match! {
224 match r => Construct(_, "CharClass", b),
225 create CharClass {
226 neg: b[0].iter_list().next().is_some(),
227 ranges: allocs.try_alloc_extend(b[1].iter_list().map(|p| result_match! {
228 match p => Construct(_, "Range", pb),
229 create (parse_string_char(&pb[0], src)?, parse_string_char(&pb[1], src)?)
230 }))?
231 }
232 }
233}
234
235fn parse_option<'arn, 'grm, T>(
236 r: &ActionResult<'arn, 'grm>,
237 src: &str,
238 sub: impl Fn(&ActionResult<'arn, 'grm>, &str) -> Option<T>,
239) -> Option<Option<T>> {
240 match r {
241 Construct(_, "None", []) => Some(None),
242 Construct(_, "Some", b) => Some(Some(sub(&b[0], src)?)),
243 _ => None,
244 }
245}
246
247fn parse_u64(r: &ActionResult<'_, '_>, src: &str) -> Option<u64> {
248 match r {
249 Literal(v) => v.parse().ok(),
250 Value(span) => src[*span].parse().ok(),
251 _ => None,
252 }
253}
254
255pub fn parse_rule_action<'arn, 'grm>(
256 r: &ActionResult<'_, 'grm>,
257 src: &'grm str,
258 allocs: Allocs<'arn>,
259) -> Option<RuleAction<'arn, 'grm>> {
260 Some(match r {
261 Construct(_, "Construct", b) => RuleAction::Construct(
262 parse_identifier(&b[0], src).unwrap(),
263 result_match! {
264 create allocs.try_alloc_extend(b[1].iter_list().map(|sub| parse_rule_action(sub, src, allocs)))?
265 }?,
266 ),
267 Construct(_, "InputLiteral", b) => RuleAction::InputLiteral(parse_string(&b[0], src)?),
268 Construct(_, "Name", b) => RuleAction::Name(parse_identifier(&b[0], src)?),
269 _ => return None,
270 })
271}