1 | use std::borrow::Cow; |
2 | use std::str; |
3 | |
4 | use nom::branch::alt; |
5 | use nom::bytes::complete::{tag, take_until}; |
6 | use nom::character::complete::char; |
7 | use nom::combinator::{ |
8 | complete, consumed, cut, eof, map, map_res, not, opt, peek, recognize, value, |
9 | }; |
10 | use nom::error::{Error, ErrorKind}; |
11 | use nom::error_position; |
12 | use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1}; |
13 | use nom::sequence::{delimited, pair, preceded, terminated, tuple}; |
14 | |
15 | use crate::{ErrorContext, ParseResult}; |
16 | |
17 | use super::{ |
18 | bool_lit, char_lit, identifier, is_ws, keyword, num_lit, path_or_identifier, skip_till, |
19 | str_lit, ws, Expr, PathOrIdentifier, State, |
20 | }; |
21 | |
22 | #[derive (Debug, PartialEq)] |
23 | pub enum Node<'a> { |
24 | Lit(Lit<'a>), |
25 | Comment(Comment<'a>), |
26 | Expr(Ws, Expr<'a>), |
27 | Call(Call<'a>), |
28 | Let(Let<'a>), |
29 | If(If<'a>), |
30 | Match(Match<'a>), |
31 | Loop(Box<Loop<'a>>), |
32 | Extends(Extends<'a>), |
33 | BlockDef(BlockDef<'a>), |
34 | Include(Include<'a>), |
35 | Import(Import<'a>), |
36 | Macro(Macro<'a>), |
37 | Raw(Raw<'a>), |
38 | Break(Ws), |
39 | Continue(Ws), |
40 | } |
41 | |
42 | impl<'a> Node<'a> { |
43 | pub(super) fn many(i: &'a str, s: &State<'_>) -> ParseResult<'a, Vec<Self>> { |
44 | complete(many0(alt(( |
45 | map(|i| Lit::parse(i, s), Self::Lit), |
46 | map(|i| Comment::parse(i, s), Self::Comment), |
47 | |i| Self::expr(i, s), |
48 | |i| Self::parse(i, s), |
49 | ))))(i) |
50 | } |
51 | |
52 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
53 | let mut p = delimited( |
54 | |i| s.tag_block_start(i), |
55 | alt(( |
56 | map(|i| Call::parse(i, s), Self::Call), |
57 | map(|i| Let::parse(i, s), Self::Let), |
58 | map(|i| If::parse(i, s), Self::If), |
59 | map(|i| Loop::parse(i, s), |l| Self::Loop(Box::new(l))), |
60 | map(|i| Match::parse(i, s), Self::Match), |
61 | map(Extends::parse, Self::Extends), |
62 | map(Include::parse, Self::Include), |
63 | map(Import::parse, Self::Import), |
64 | map(|i| BlockDef::parse(i, s), Self::BlockDef), |
65 | map(|i| Macro::parse(i, s), Self::Macro), |
66 | map(|i| Raw::parse(i, s), Self::Raw), |
67 | |i| Self::r#break(i, s), |
68 | |i| Self::r#continue(i, s), |
69 | )), |
70 | cut(|i| s.tag_block_end(i)), |
71 | ); |
72 | |
73 | s.nest(i)?; |
74 | let result = p(i); |
75 | s.leave(); |
76 | |
77 | result |
78 | } |
79 | |
80 | fn r#break(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
81 | let mut p = tuple(( |
82 | opt(Whitespace::parse), |
83 | ws(keyword("break" )), |
84 | opt(Whitespace::parse), |
85 | )); |
86 | let (j, (pws, _, nws)) = p(i)?; |
87 | if !s.is_in_loop() { |
88 | return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))); |
89 | } |
90 | Ok((j, Self::Break(Ws(pws, nws)))) |
91 | } |
92 | |
93 | fn r#continue(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
94 | let mut p = tuple(( |
95 | opt(Whitespace::parse), |
96 | ws(keyword("continue" )), |
97 | opt(Whitespace::parse), |
98 | )); |
99 | let (j, (pws, _, nws)) = p(i)?; |
100 | if !s.is_in_loop() { |
101 | return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))); |
102 | } |
103 | Ok((j, Self::Continue(Ws(pws, nws)))) |
104 | } |
105 | |
106 | fn expr(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
107 | let mut p = tuple(( |
108 | |i| s.tag_expr_start(i), |
109 | cut(tuple(( |
110 | opt(Whitespace::parse), |
111 | ws(|i| Expr::parse(i, s.level.get())), |
112 | opt(Whitespace::parse), |
113 | |i| s.tag_expr_end(i), |
114 | ))), |
115 | )); |
116 | let (i, (_, (pws, expr, nws, _))) = p(i)?; |
117 | Ok((i, Self::Expr(Ws(pws, nws), expr))) |
118 | } |
119 | } |
120 | |
121 | #[derive (Clone, Debug, PartialEq)] |
122 | pub enum Target<'a> { |
123 | Name(&'a str), |
124 | Tuple(Vec<&'a str>, Vec<Target<'a>>), |
125 | Struct(Vec<&'a str>, Vec<(&'a str, Target<'a>)>), |
126 | NumLit(&'a str), |
127 | StrLit(&'a str), |
128 | CharLit(&'a str), |
129 | BoolLit(&'a str), |
130 | Path(Vec<&'a str>), |
131 | OrChain(Vec<Target<'a>>), |
132 | } |
133 | |
134 | impl<'a> Target<'a> { |
135 | /// Parses multiple targets with `or` separating them |
136 | pub(super) fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
137 | map( |
138 | separated_list1(ws(tag("or" )), |i| { |
139 | s.nest(i)?; |
140 | let ret = Self::parse_one(i, s)?; |
141 | s.leave(); |
142 | Ok(ret) |
143 | }), |
144 | |mut opts| match opts.len() { |
145 | 1 => opts.pop().unwrap(), |
146 | _ => Self::OrChain(opts), |
147 | }, |
148 | )(i) |
149 | } |
150 | |
151 | /// Parses a single target without an `or`, unless it is wrapped in parentheses. |
152 | fn parse_one(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
153 | let mut opt_opening_paren = map(opt(ws(char('(' ))), |o| o.is_some()); |
154 | let mut opt_closing_paren = map(opt(ws(char(')' ))), |o| o.is_some()); |
155 | let mut opt_opening_brace = map(opt(ws(char('{' ))), |o| o.is_some()); |
156 | |
157 | let (i, lit) = opt(Self::lit)(i)?; |
158 | if let Some(lit) = lit { |
159 | return Ok((i, lit)); |
160 | } |
161 | |
162 | // match tuples and unused parentheses |
163 | let (i, target_is_tuple) = opt_opening_paren(i)?; |
164 | if target_is_tuple { |
165 | let (i, is_empty_tuple) = opt_closing_paren(i)?; |
166 | if is_empty_tuple { |
167 | return Ok((i, Self::Tuple(Vec::new(), Vec::new()))); |
168 | } |
169 | |
170 | let (i, first_target) = Self::parse(i, s)?; |
171 | let (i, is_unused_paren) = opt_closing_paren(i)?; |
172 | if is_unused_paren { |
173 | return Ok((i, first_target)); |
174 | } |
175 | |
176 | let mut targets = vec![first_target]; |
177 | let (i, _) = cut(tuple(( |
178 | fold_many0( |
179 | preceded(ws(char(',' )), |i| Self::parse(i, s)), |
180 | || (), |
181 | |_, target| { |
182 | targets.push(target); |
183 | }, |
184 | ), |
185 | opt(ws(char(',' ))), |
186 | ws(cut(char(')' ))), |
187 | )))(i)?; |
188 | return Ok((i, Self::Tuple(Vec::new(), targets))); |
189 | } |
190 | |
191 | let path = |i| { |
192 | map_res(path_or_identifier, |v| match v { |
193 | PathOrIdentifier::Path(v) => Ok(v), |
194 | PathOrIdentifier::Identifier(v) => Err(v), |
195 | })(i) |
196 | }; |
197 | |
198 | // match structs |
199 | let (i, path) = opt(path)(i)?; |
200 | if let Some(path) = path { |
201 | let i_before_matching_with = i; |
202 | let (i, _) = opt(ws(keyword("with" )))(i)?; |
203 | |
204 | let (i, is_unnamed_struct) = opt_opening_paren(i)?; |
205 | if is_unnamed_struct { |
206 | let (i, targets) = alt(( |
207 | map(char(')' ), |_| Vec::new()), |
208 | terminated( |
209 | cut(separated_list1(ws(char(',' )), |i| Self::parse(i, s))), |
210 | pair(opt(ws(char(',' ))), ws(cut(char(')' )))), |
211 | ), |
212 | ))(i)?; |
213 | return Ok((i, Self::Tuple(path, targets))); |
214 | } |
215 | |
216 | let (i, is_named_struct) = opt_opening_brace(i)?; |
217 | if is_named_struct { |
218 | let (i, targets) = alt(( |
219 | map(char('}' ), |_| Vec::new()), |
220 | terminated( |
221 | cut(separated_list1(ws(char(',' )), |i| Self::named(i, s))), |
222 | pair(opt(ws(char(',' ))), ws(cut(char('}' )))), |
223 | ), |
224 | ))(i)?; |
225 | return Ok((i, Self::Struct(path, targets))); |
226 | } |
227 | |
228 | return Ok((i_before_matching_with, Self::Path(path))); |
229 | } |
230 | |
231 | // neither literal nor struct nor path |
232 | let (new_i, name) = identifier(i)?; |
233 | Ok((new_i, Self::verify_name(i, name)?)) |
234 | } |
235 | |
236 | fn lit(i: &'a str) -> ParseResult<'a, Self> { |
237 | alt(( |
238 | map(str_lit, Self::StrLit), |
239 | map(char_lit, Self::CharLit), |
240 | map(num_lit, Self::NumLit), |
241 | map(bool_lit, Self::BoolLit), |
242 | ))(i) |
243 | } |
244 | |
245 | fn named(init_i: &'a str, s: &State<'_>) -> ParseResult<'a, (&'a str, Self)> { |
246 | let (i, (src, target)) = pair( |
247 | identifier, |
248 | opt(preceded(ws(char(':' )), |i| Self::parse(i, s))), |
249 | )(init_i)?; |
250 | |
251 | let target = match target { |
252 | Some(target) => target, |
253 | None => Self::verify_name(init_i, src)?, |
254 | }; |
255 | |
256 | Ok((i, (src, target))) |
257 | } |
258 | |
259 | fn verify_name(input: &'a str, name: &'a str) -> Result<Self, nom::Err<ErrorContext<'a>>> { |
260 | match name { |
261 | "self" | "writer" => Err(nom::Err::Failure(ErrorContext { |
262 | input, |
263 | message: Some(Cow::Owned(format!("Cannot use ` {name}` as a name" ))), |
264 | })), |
265 | _ => Ok(Self::Name(name)), |
266 | } |
267 | } |
268 | } |
269 | |
270 | #[derive (Debug, PartialEq)] |
271 | pub struct When<'a> { |
272 | pub ws: Ws, |
273 | pub target: Target<'a>, |
274 | pub nodes: Vec<Node<'a>>, |
275 | } |
276 | |
277 | impl<'a> When<'a> { |
278 | fn r#match(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
279 | let mut p = tuple(( |
280 | |i| s.tag_block_start(i), |
281 | opt(Whitespace::parse), |
282 | ws(keyword("else" )), |
283 | cut(tuple(( |
284 | opt(Whitespace::parse), |
285 | |i| s.tag_block_end(i), |
286 | cut(|i| Node::many(i, s)), |
287 | ))), |
288 | )); |
289 | let (i, (_, pws, _, (nws, _, nodes))) = p(i)?; |
290 | Ok(( |
291 | i, |
292 | Self { |
293 | ws: Ws(pws, nws), |
294 | target: Target::Name("_" ), |
295 | nodes, |
296 | }, |
297 | )) |
298 | } |
299 | |
300 | #[allow (clippy::self_named_constructors)] |
301 | fn when(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
302 | let mut p = tuple(( |
303 | |i| s.tag_block_start(i), |
304 | opt(Whitespace::parse), |
305 | ws(keyword("when" )), |
306 | cut(tuple(( |
307 | ws(|i| Target::parse(i, s)), |
308 | opt(Whitespace::parse), |
309 | |i| s.tag_block_end(i), |
310 | cut(|i| Node::many(i, s)), |
311 | ))), |
312 | )); |
313 | let (i, (_, pws, _, (target, nws, _, nodes))) = p(i)?; |
314 | Ok(( |
315 | i, |
316 | Self { |
317 | ws: Ws(pws, nws), |
318 | target, |
319 | nodes, |
320 | }, |
321 | )) |
322 | } |
323 | } |
324 | |
325 | #[derive (Debug, PartialEq)] |
326 | pub struct Cond<'a> { |
327 | pub ws: Ws, |
328 | pub cond: Option<CondTest<'a>>, |
329 | pub nodes: Vec<Node<'a>>, |
330 | } |
331 | |
332 | impl<'a> Cond<'a> { |
333 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
334 | let mut p = tuple(( |
335 | |i| s.tag_block_start(i), |
336 | opt(Whitespace::parse), |
337 | ws(alt((keyword("else" ), |i| { |
338 | let _ = keyword("elif" )(i)?; |
339 | Err(nom::Err::Failure(ErrorContext { |
340 | input: i, |
341 | message: Some(Cow::Borrowed( |
342 | "unknown `elif` keyword; did you mean `else if`?" , |
343 | )), |
344 | })) |
345 | }))), |
346 | cut(tuple(( |
347 | opt(|i| CondTest::parse(i, s)), |
348 | opt(Whitespace::parse), |
349 | |i| s.tag_block_end(i), |
350 | cut(|i| Node::many(i, s)), |
351 | ))), |
352 | )); |
353 | let (i, (_, pws, _, (cond, nws, _, nodes))) = p(i)?; |
354 | Ok(( |
355 | i, |
356 | Self { |
357 | ws: Ws(pws, nws), |
358 | cond, |
359 | nodes, |
360 | }, |
361 | )) |
362 | } |
363 | } |
364 | |
365 | #[derive (Debug, PartialEq)] |
366 | pub struct CondTest<'a> { |
367 | pub target: Option<Target<'a>>, |
368 | pub expr: Expr<'a>, |
369 | } |
370 | |
371 | impl<'a> CondTest<'a> { |
372 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
373 | let mut p: impl FnMut(&str) -> Result<…, …> = preceded( |
374 | first:ws(keyword("if" )), |
375 | second:cut(parser:tuple(( |
376 | opt(delimited( |
377 | first:ws(alt((keyword("let" ), keyword("set" )))), |
378 | second:ws(|i| Target::parse(i, s)), |
379 | third:ws(inner:char('=' )), |
380 | )), |
381 | ws(|i: &str| Expr::parse(i, level:s.level.get())), |
382 | ))), |
383 | ); |
384 | let (i: &str, (target: Option>, expr: Expr<'_>)) = p(i)?; |
385 | Ok((i, Self { target, expr })) |
386 | } |
387 | } |
388 | |
389 | #[derive (Clone, Copy, Debug, PartialEq)] |
390 | pub enum Whitespace { |
391 | Preserve, |
392 | Suppress, |
393 | Minimize, |
394 | } |
395 | |
396 | impl Whitespace { |
397 | fn parse(i: &str) -> ParseResult<'_, Self> { |
398 | alt(( |
399 | value(Self::Preserve, parser:char('+' )), |
400 | value(Self::Suppress, parser:char('-' )), |
401 | value(Self::Minimize, parser:char('~' )), |
402 | ))(i) |
403 | } |
404 | } |
405 | |
406 | #[derive (Debug, PartialEq)] |
407 | pub struct Loop<'a> { |
408 | pub ws1: Ws, |
409 | pub var: Target<'a>, |
410 | pub iter: Expr<'a>, |
411 | pub cond: Option<Expr<'a>>, |
412 | pub body: Vec<Node<'a>>, |
413 | pub ws2: Ws, |
414 | pub else_nodes: Vec<Node<'a>>, |
415 | pub ws3: Ws, |
416 | } |
417 | |
418 | impl<'a> Loop<'a> { |
419 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
420 | fn content<'a>(i: &'a str, s: &State<'_>) -> ParseResult<'a, Vec<Node<'a>>> { |
421 | s.enter_loop(); |
422 | let result = Node::many(i, s); |
423 | s.leave_loop(); |
424 | result |
425 | } |
426 | |
427 | let if_cond = preceded( |
428 | ws(keyword("if" )), |
429 | cut(ws(|i| Expr::parse(i, s.level.get()))), |
430 | ); |
431 | |
432 | let else_block = |i| { |
433 | let mut p = preceded( |
434 | ws(keyword("else" )), |
435 | cut(tuple(( |
436 | opt(Whitespace::parse), |
437 | delimited( |
438 | |i| s.tag_block_end(i), |
439 | |i| Node::many(i, s), |
440 | |i| s.tag_block_start(i), |
441 | ), |
442 | opt(Whitespace::parse), |
443 | ))), |
444 | ); |
445 | let (i, (pws, nodes, nws)) = p(i)?; |
446 | Ok((i, (pws, nodes, nws))) |
447 | }; |
448 | |
449 | let mut p = tuple(( |
450 | opt(Whitespace::parse), |
451 | ws(keyword("for" )), |
452 | cut(tuple(( |
453 | ws(|i| Target::parse(i, s)), |
454 | ws(keyword("in" )), |
455 | cut(tuple(( |
456 | ws(|i| Expr::parse(i, s.level.get())), |
457 | opt(if_cond), |
458 | opt(Whitespace::parse), |
459 | |i| s.tag_block_end(i), |
460 | cut(tuple(( |
461 | |i| content(i, s), |
462 | cut(tuple(( |
463 | |i| s.tag_block_start(i), |
464 | opt(Whitespace::parse), |
465 | opt(else_block), |
466 | ws(keyword("endfor" )), |
467 | opt(Whitespace::parse), |
468 | ))), |
469 | ))), |
470 | ))), |
471 | ))), |
472 | )); |
473 | let (i, (pws1, _, (var, _, (iter, cond, nws1, _, (body, (_, pws2, else_block, _, nws2)))))) = |
474 | p(i)?; |
475 | let (nws3, else_block, pws3) = else_block.unwrap_or_default(); |
476 | Ok(( |
477 | i, |
478 | Self { |
479 | ws1: Ws(pws1, nws1), |
480 | var, |
481 | iter, |
482 | cond, |
483 | body, |
484 | ws2: Ws(pws2, nws3), |
485 | else_nodes: else_block, |
486 | ws3: Ws(pws3, nws2), |
487 | }, |
488 | )) |
489 | } |
490 | } |
491 | |
492 | #[derive (Debug, PartialEq)] |
493 | pub struct Macro<'a> { |
494 | pub ws1: Ws, |
495 | pub name: &'a str, |
496 | pub args: Vec<&'a str>, |
497 | pub nodes: Vec<Node<'a>>, |
498 | pub ws2: Ws, |
499 | } |
500 | |
501 | impl<'a> Macro<'a> { |
502 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
503 | fn parameters(i: &str) -> ParseResult<'_, Vec<&str>> { |
504 | delimited( |
505 | ws(char('(' )), |
506 | separated_list0(char(',' ), ws(identifier)), |
507 | tuple((opt(ws(char(',' ))), char(')' ))), |
508 | )(i) |
509 | } |
510 | |
511 | let mut start = tuple(( |
512 | opt(Whitespace::parse), |
513 | ws(keyword("macro" )), |
514 | cut(tuple(( |
515 | ws(identifier), |
516 | opt(ws(parameters)), |
517 | opt(Whitespace::parse), |
518 | |i| s.tag_block_end(i), |
519 | ))), |
520 | )); |
521 | let (i, (pws1, _, (name, params, nws1, _))) = start(i)?; |
522 | |
523 | let mut end = cut(tuple(( |
524 | |i| Node::many(i, s), |
525 | cut(tuple(( |
526 | |i| s.tag_block_start(i), |
527 | opt(Whitespace::parse), |
528 | ws(keyword("endmacro" )), |
529 | cut(preceded( |
530 | opt(|before| { |
531 | let (after, end_name) = ws(identifier)(before)?; |
532 | check_end_name(before, after, name, end_name, "macro" ) |
533 | }), |
534 | opt(Whitespace::parse), |
535 | )), |
536 | ))), |
537 | ))); |
538 | let (i, (contents, (_, pws2, _, nws2))) = end(i)?; |
539 | |
540 | if name == "super" { |
541 | // TODO: yield a a better error message here |
542 | return Err(ErrorContext::from_err(nom::Err::Failure(Error::new( |
543 | i, |
544 | ErrorKind::Fail, |
545 | )))); |
546 | } |
547 | |
548 | Ok(( |
549 | i, |
550 | Self { |
551 | ws1: Ws(pws1, nws1), |
552 | name, |
553 | args: params.unwrap_or_default(), |
554 | nodes: contents, |
555 | ws2: Ws(pws2, nws2), |
556 | }, |
557 | )) |
558 | } |
559 | } |
560 | |
561 | #[derive (Debug, PartialEq)] |
562 | pub struct Import<'a> { |
563 | pub ws: Ws, |
564 | pub path: &'a str, |
565 | pub scope: &'a str, |
566 | } |
567 | |
568 | impl<'a> Import<'a> { |
569 | fn parse(i: &'a str) -> ParseResult<'a, Self> { |
570 | let mut p: impl FnMut(&str) -> Result<…, …> = tuple(( |
571 | opt(Whitespace::parse), |
572 | ws(inner:keyword("import" )), |
573 | cut(parser:tuple(( |
574 | ws(inner:str_lit), |
575 | ws(inner:keyword("as" )), |
576 | cut(parser:pair(first:ws(identifier), second:opt(Whitespace::parse))), |
577 | ))), |
578 | )); |
579 | let (i: &str, (pws: Option, _, (path: &str, _, (scope: &str, nws: Option)))) = p(i)?; |
580 | Ok(( |
581 | i, |
582 | Self { |
583 | ws: Ws(pws, nws), |
584 | path, |
585 | scope, |
586 | }, |
587 | )) |
588 | } |
589 | } |
590 | |
591 | #[derive (Debug, PartialEq)] |
592 | pub struct Call<'a> { |
593 | pub ws: Ws, |
594 | pub scope: Option<&'a str>, |
595 | pub name: &'a str, |
596 | pub args: Vec<Expr<'a>>, |
597 | } |
598 | |
599 | impl<'a> Call<'a> { |
600 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
601 | let mut p = tuple(( |
602 | opt(Whitespace::parse), |
603 | ws(keyword("call" )), |
604 | cut(tuple(( |
605 | opt(tuple((ws(identifier), ws(tag("::" ))))), |
606 | ws(identifier), |
607 | opt(ws(|nested| Expr::arguments(nested, s.level.get(), true))), |
608 | opt(Whitespace::parse), |
609 | ))), |
610 | )); |
611 | let (i, (pws, _, (scope, name, args, nws))) = p(i)?; |
612 | let scope = scope.map(|(scope, _)| scope); |
613 | let args = args.unwrap_or_default(); |
614 | Ok(( |
615 | i, |
616 | Self { |
617 | ws: Ws(pws, nws), |
618 | scope, |
619 | name, |
620 | args, |
621 | }, |
622 | )) |
623 | } |
624 | } |
625 | |
626 | #[derive (Debug, PartialEq)] |
627 | pub struct Match<'a> { |
628 | pub ws1: Ws, |
629 | pub expr: Expr<'a>, |
630 | pub arms: Vec<When<'a>>, |
631 | pub ws2: Ws, |
632 | } |
633 | |
634 | impl<'a> Match<'a> { |
635 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
636 | let mut p = tuple(( |
637 | opt(Whitespace::parse), |
638 | ws(keyword("match" )), |
639 | cut(tuple(( |
640 | ws(|i| Expr::parse(i, s.level.get())), |
641 | opt(Whitespace::parse), |
642 | |i| s.tag_block_end(i), |
643 | cut(tuple(( |
644 | ws(many0(ws(value((), |i| Comment::parse(i, s))))), |
645 | many1(|i| When::when(i, s)), |
646 | cut(tuple(( |
647 | opt(|i| When::r#match(i, s)), |
648 | cut(tuple(( |
649 | ws(|i| s.tag_block_start(i)), |
650 | opt(Whitespace::parse), |
651 | ws(keyword("endmatch" )), |
652 | opt(Whitespace::parse), |
653 | ))), |
654 | ))), |
655 | ))), |
656 | ))), |
657 | )); |
658 | let (i, (pws1, _, (expr, nws1, _, (_, arms, (else_arm, (_, pws2, _, nws2)))))) = p(i)?; |
659 | |
660 | let mut arms = arms; |
661 | if let Some(arm) = else_arm { |
662 | arms.push(arm); |
663 | } |
664 | |
665 | Ok(( |
666 | i, |
667 | Self { |
668 | ws1: Ws(pws1, nws1), |
669 | expr, |
670 | arms, |
671 | ws2: Ws(pws2, nws2), |
672 | }, |
673 | )) |
674 | } |
675 | } |
676 | |
677 | #[derive (Debug, PartialEq)] |
678 | pub struct BlockDef<'a> { |
679 | pub ws1: Ws, |
680 | pub name: &'a str, |
681 | pub nodes: Vec<Node<'a>>, |
682 | pub ws2: Ws, |
683 | } |
684 | |
685 | impl<'a> BlockDef<'a> { |
686 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
687 | let mut start = tuple(( |
688 | opt(Whitespace::parse), |
689 | ws(keyword("block" )), |
690 | cut(tuple((ws(identifier), opt(Whitespace::parse), |i| { |
691 | s.tag_block_end(i) |
692 | }))), |
693 | )); |
694 | let (i, (pws1, _, (name, nws1, _))) = start(i)?; |
695 | |
696 | let mut end = cut(tuple(( |
697 | |i| Node::many(i, s), |
698 | cut(tuple(( |
699 | |i| s.tag_block_start(i), |
700 | opt(Whitespace::parse), |
701 | ws(keyword("endblock" )), |
702 | cut(tuple(( |
703 | opt(|before| { |
704 | let (after, end_name) = ws(identifier)(before)?; |
705 | check_end_name(before, after, name, end_name, "block" ) |
706 | }), |
707 | opt(Whitespace::parse), |
708 | ))), |
709 | ))), |
710 | ))); |
711 | let (i, (nodes, (_, pws2, _, (_, nws2)))) = end(i)?; |
712 | |
713 | Ok(( |
714 | i, |
715 | BlockDef { |
716 | ws1: Ws(pws1, nws1), |
717 | name, |
718 | nodes, |
719 | ws2: Ws(pws2, nws2), |
720 | }, |
721 | )) |
722 | } |
723 | } |
724 | |
725 | fn check_end_name<'a>( |
726 | before: &'a str, |
727 | after: &'a str, |
728 | name: &'a str, |
729 | end_name: &'a str, |
730 | kind: &str, |
731 | ) -> ParseResult<'a> { |
732 | if name == end_name { |
733 | return Ok((after, end_name)); |
734 | } |
735 | let message: String = if name.is_empty() && !end_name.is_empty() { |
736 | format!("unexpected name ` {end_name}` in `end {kind}` tag for unnamed ` {kind}`" ) |
737 | } else { |
738 | format!("expected name ` {name}` in `end {kind}` tag, found ` {end_name}`" ) |
739 | }; |
740 | Err(nom::Err::Failure(ErrorContext { |
741 | input: before, |
742 | message: Some(Cow::Owned(message)), |
743 | })) |
744 | } |
745 | |
746 | #[derive (Debug, PartialEq)] |
747 | pub struct Lit<'a> { |
748 | pub lws: &'a str, |
749 | pub val: &'a str, |
750 | pub rws: &'a str, |
751 | } |
752 | |
753 | impl<'a> Lit<'a> { |
754 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
755 | let p_start = alt(( |
756 | tag(s.syntax.block_start), |
757 | tag(s.syntax.comment_start), |
758 | tag(s.syntax.expr_start), |
759 | )); |
760 | |
761 | let (i, _) = not(eof)(i)?; |
762 | let (i, content) = opt(recognize(skip_till(p_start)))(i)?; |
763 | let (i, content) = match content { |
764 | Some("" ) => { |
765 | // {block,comment,expr}_start follows immediately. |
766 | return Err(nom::Err::Error(error_position!(i, ErrorKind::TakeUntil))); |
767 | } |
768 | Some(content) => (i, content), |
769 | None => ("" , i), // there is no {block,comment,expr}_start: take everything |
770 | }; |
771 | Ok((i, Self::split_ws_parts(content))) |
772 | } |
773 | |
774 | pub(crate) fn split_ws_parts(s: &'a str) -> Self { |
775 | let trimmed_start = s.trim_start_matches(is_ws); |
776 | let len_start = s.len() - trimmed_start.len(); |
777 | let trimmed = trimmed_start.trim_end_matches(is_ws); |
778 | Self { |
779 | lws: &s[..len_start], |
780 | val: trimmed, |
781 | rws: &trimmed_start[trimmed.len()..], |
782 | } |
783 | } |
784 | } |
785 | |
786 | #[derive (Debug, PartialEq)] |
787 | pub struct Raw<'a> { |
788 | pub ws1: Ws, |
789 | pub lit: Lit<'a>, |
790 | pub ws2: Ws, |
791 | } |
792 | |
793 | impl<'a> Raw<'a> { |
794 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
795 | let endraw = tuple(( |
796 | |i| s.tag_block_start(i), |
797 | opt(Whitespace::parse), |
798 | ws(keyword("endraw" )), |
799 | opt(Whitespace::parse), |
800 | peek(|i| s.tag_block_end(i)), |
801 | )); |
802 | |
803 | let mut p = tuple(( |
804 | opt(Whitespace::parse), |
805 | ws(keyword("raw" )), |
806 | cut(tuple(( |
807 | opt(Whitespace::parse), |
808 | |i| s.tag_block_end(i), |
809 | consumed(skip_till(endraw)), |
810 | ))), |
811 | )); |
812 | |
813 | let (_, (pws1, _, (nws1, _, (contents, (i, (_, pws2, _, nws2, _)))))) = p(i)?; |
814 | let lit = Lit::split_ws_parts(contents); |
815 | let ws1 = Ws(pws1, nws1); |
816 | let ws2 = Ws(pws2, nws2); |
817 | Ok((i, Self { ws1, lit, ws2 })) |
818 | } |
819 | } |
820 | |
821 | #[derive (Debug, PartialEq)] |
822 | pub struct Let<'a> { |
823 | pub ws: Ws, |
824 | pub var: Target<'a>, |
825 | pub val: Option<Expr<'a>>, |
826 | } |
827 | |
828 | impl<'a> Let<'a> { |
829 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
830 | let mut p = tuple(( |
831 | opt(Whitespace::parse), |
832 | ws(alt((keyword("let" ), keyword("set" )))), |
833 | cut(tuple(( |
834 | ws(|i| Target::parse(i, s)), |
835 | opt(preceded( |
836 | ws(char('=' )), |
837 | ws(|i| Expr::parse(i, s.level.get())), |
838 | )), |
839 | opt(Whitespace::parse), |
840 | ))), |
841 | )); |
842 | let (i, (pws, _, (var, val, nws))) = p(i)?; |
843 | |
844 | Ok(( |
845 | i, |
846 | Let { |
847 | ws: Ws(pws, nws), |
848 | var, |
849 | val, |
850 | }, |
851 | )) |
852 | } |
853 | } |
854 | |
855 | #[derive (Debug, PartialEq)] |
856 | pub struct If<'a> { |
857 | pub ws: Ws, |
858 | pub branches: Vec<Cond<'a>>, |
859 | } |
860 | |
861 | impl<'a> If<'a> { |
862 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
863 | let mut p = tuple(( |
864 | opt(Whitespace::parse), |
865 | |i| CondTest::parse(i, s), |
866 | cut(tuple(( |
867 | opt(Whitespace::parse), |
868 | |i| s.tag_block_end(i), |
869 | cut(tuple(( |
870 | |i| Node::many(i, s), |
871 | many0(|i| Cond::parse(i, s)), |
872 | cut(tuple(( |
873 | |i| s.tag_block_start(i), |
874 | opt(Whitespace::parse), |
875 | ws(keyword("endif" )), |
876 | opt(Whitespace::parse), |
877 | ))), |
878 | ))), |
879 | ))), |
880 | )); |
881 | |
882 | let (i, (pws1, cond, (nws1, _, (nodes, elifs, (_, pws2, _, nws2))))) = p(i)?; |
883 | let mut branches = vec![Cond { |
884 | ws: Ws(pws1, nws1), |
885 | cond: Some(cond), |
886 | nodes, |
887 | }]; |
888 | branches.extend(elifs); |
889 | |
890 | Ok(( |
891 | i, |
892 | Self { |
893 | ws: Ws(pws2, nws2), |
894 | branches, |
895 | }, |
896 | )) |
897 | } |
898 | } |
899 | |
900 | #[derive (Debug, PartialEq)] |
901 | pub struct Include<'a> { |
902 | pub ws: Ws, |
903 | pub path: &'a str, |
904 | } |
905 | |
906 | impl<'a> Include<'a> { |
907 | fn parse(i: &'a str) -> ParseResult<'a, Self> { |
908 | let mut p: impl FnMut(&str) -> Result<…, …> = tuple(( |
909 | opt(Whitespace::parse), |
910 | ws(inner:keyword("include" )), |
911 | cut(parser:pair(first:ws(str_lit), second:opt(Whitespace::parse))), |
912 | )); |
913 | let (i: &str, (pws: Option, _, (path: &str, nws: Option))) = p(i)?; |
914 | Ok(( |
915 | i, |
916 | Self { |
917 | ws: Ws(pws, nws), |
918 | path, |
919 | }, |
920 | )) |
921 | } |
922 | } |
923 | |
924 | #[derive (Debug, PartialEq)] |
925 | pub struct Extends<'a> { |
926 | pub path: &'a str, |
927 | } |
928 | |
929 | impl<'a> Extends<'a> { |
930 | fn parse(i: &'a str) -> ParseResult<'a, Self> { |
931 | let (i: &str, path: &str) = preceded(first:ws(keyword("extends" )), second:cut(parser:ws(inner:str_lit)))(i)?; |
932 | Ok((i, Self { path })) |
933 | } |
934 | } |
935 | |
936 | #[derive (Debug, PartialEq)] |
937 | pub struct Comment<'a> { |
938 | pub ws: Ws, |
939 | pub content: &'a str, |
940 | } |
941 | |
942 | impl<'a> Comment<'a> { |
943 | fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> { |
944 | fn body<'a>(mut i: &'a str, s: &State<'_>) -> ParseResult<'a> { |
945 | let mut level = 0; |
946 | loop { |
947 | let (end, tail) = take_until(s.syntax.comment_end)(i)?; |
948 | match take_until::<_, _, ErrorContext<'_>>(s.syntax.comment_start)(i) { |
949 | Ok((start, _)) if start.as_ptr() < end.as_ptr() => { |
950 | level += 1; |
951 | i = &start[2..]; |
952 | } |
953 | _ if level > 0 => { |
954 | level -= 1; |
955 | i = &end[2..]; |
956 | } |
957 | _ => return Ok((end, tail)), |
958 | } |
959 | } |
960 | } |
961 | |
962 | let mut p = tuple(( |
963 | |i| s.tag_comment_start(i), |
964 | cut(tuple(( |
965 | opt(Whitespace::parse), |
966 | |i| body(i, s), |
967 | |i| s.tag_comment_end(i), |
968 | ))), |
969 | )); |
970 | let (i, (content, (pws, tail, _))) = p(i)?; |
971 | let nws = if tail.ends_with('-' ) { |
972 | Some(Whitespace::Suppress) |
973 | } else if tail.ends_with('+' ) { |
974 | Some(Whitespace::Preserve) |
975 | } else if tail.ends_with('~' ) { |
976 | Some(Whitespace::Minimize) |
977 | } else { |
978 | None |
979 | }; |
980 | Ok(( |
981 | i, |
982 | Self { |
983 | ws: Ws(pws, nws), |
984 | content, |
985 | }, |
986 | )) |
987 | } |
988 | } |
989 | |
990 | /// First field is "minus/plus sign was used on the left part of the item". |
991 | /// |
992 | /// Second field is "minus/plus sign was used on the right part of the item". |
993 | #[derive (Clone, Copy, Debug, PartialEq)] |
994 | pub struct Ws(pub Option<Whitespace>, pub Option<Whitespace>); |
995 | |