libhanzzok/syntax/parse/
parse_decorator_chain.rs

1use nom::{
2    branch::alt,
3    combinator::{map, not, opt, recognize},
4    multi::{many0, many1},
5    sequence::{preceded, terminated, tuple},
6};
7
8use crate::{
9    core::ast::{DecoratorChainNode, DecoratorNode, InlineObjectNode, TextNode},
10    syntax::{Token, TokenKind},
11};
12
13use super::{
14    nom_ext::{satisfy, satisfy_transform, skip_any_spaces, tag, HanzzokParser},
15    parse_hzdata::parse_hzdata_paired,
16    parse_inline_constructor::parse_inline_constructor,
17    parse_text::{parse_escaped_text, parse_fallback_text},
18    ParseResult,
19};
20
21fn parse_decorator_params(p: HanzzokParser) -> ParseResult<Vec<Token>> {
22    parse_hzdata_paired(
23        TokenKind::PunctuationLeftParenthesis,
24        TokenKind::PunctuationRightParenthesis,
25        true,
26    )(p)
27}
28
29pub fn parse_decorator_chain(p: HanzzokParser) -> ParseResult<DecoratorChainNode> {
30    let tt = p.create_tracker();
31
32    let (p, _) = tag(TokenKind::PunctuationLeftSquareBracket)(p)?;
33
34    let (p, _) = skip_any_spaces(p)?;
35
36    let (p, main_text) = alt((
37        map(
38            parse_inline_constructor,
39            InlineObjectNode::InlineConstructor,
40        ),
41        map(
42            many1(alt((
43                parse_escaped_text,
44                preceded(
45                    not(alt((
46                        recognize(tuple((
47                            tag(TokenKind::PunctuationFullStop),
48                            satisfy(|t| matches!(t.kind, TokenKind::Word(_))),
49                        ))),
50                        recognize(tag(TokenKind::PunctuationRightSquareBracket)),
51                    ))),
52                    parse_fallback_text,
53                ),
54            ))),
55            |nodes| {
56                InlineObjectNode::Text(TextNode {
57                    tokens: nodes
58                        .into_iter()
59                        .flat_map(|node| node.tokens.into_iter())
60                        .collect(),
61                })
62            },
63        ),
64    ))(p)?;
65
66    let (p, _) = skip_any_spaces(p)?;
67
68    let (p, decorators) = many0(terminated(
69        |p: HanzzokParser| {
70            let tt = p.create_tracker();
71            let (p, _) = tag(TokenKind::PunctuationFullStop)(p)?;
72            let (p, name) = many1(satisfy_transform(|t| match &t.kind {
73                TokenKind::Word(w) => Some(w.clone()),
74                TokenKind::PunctuationHyphenMinus => Some("-".to_owned()),
75                _ => None,
76            }))(p)?;
77            let (p, params) = opt(parse_decorator_params)(p)?;
78            let tokens = tt.end(&p);
79            Ok((
80                p,
81                DecoratorNode {
82                    tokens,
83                    name: name.into_iter().map(|(_, name)| name).collect(),
84                    params: params
85                        .map(|params| params.into_iter().map(|t| t.text).collect::<String>()),
86                },
87            ))
88        },
89        skip_any_spaces,
90    ))(p)?;
91
92    let (p, _) = tag(TokenKind::PunctuationRightSquareBracket)(p)?;
93
94    let tokens = tt.end(&p);
95
96    Ok((
97        p,
98        DecoratorChainNode {
99            main_text: Box::new(main_text),
100            decorators,
101            tokens,
102        },
103    ))
104}