py_ast/parse/
mod.rs

1use py_lex::*;
2use terl::*;
3type Result<T> = terl::Result<T, terl::ParseError>;
4
5mod expr;
6mod flow;
7mod item;
8mod stmt;
9mod types;
10
11pub use expr::*;
12pub use flow::*;
13pub use item::*;
14pub use stmt::*;
15pub use types::*;
16
17#[derive(Debug, Clone)]
18pub struct Ident(String);
19
20impl std::ops::Deref for Ident {
21    type Target = str;
22
23    fn deref(&self) -> &Self::Target {
24        &self.0
25    }
26}
27
28impl std::fmt::Display for Ident {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        self.0.fmt(f)
31    }
32}
33
34impl ParseUnit<Token> for Ident {
35    type Target = Ident;
36
37    fn parse(p: &mut Parser<Token>) -> ParseResult<Self, Token> {
38        let Some(token) = p.next() else {
39            return p.unmatch("expect a `Ident`, but no token left");
40        };
41
42        if token.chars().next().is_some_and(|c| c.is_ascii_digit()) {
43            return p.unmatch("bad ident! ident should not start with a digit");
44        }
45
46        use py_lex::*;
47
48        let keeps = &[
49            ops::KEPPING_KEYWORDS,
50            ops::sub_classes::KEPPING_KEYWORDS,
51            preprocess::KEPPING_KEYWORDS,
52            syntax::KEPPING_KEYWORDS,
53            types::KEPPING_KEYWORDS,
54        ];
55
56        for keeps in keeps {
57            if keeps.with(|keeps| keeps.contains(&**token)) {
58                return p.unmatch("keeping keywords could not be ident");
59            }
60        }
61
62        // keeping keywords cant be used as identifiers
63        Ok(Ident(token.string.clone()))
64    }
65}
66
67/// use to define a complex parse unit which could be one of its variants
68#[macro_export]
69macro_rules! complex_pu {
70    (
71        $(#[$metas:meta])*
72        cpu $enum_name:ident {
73        $(
74            $(#[$v_metas:meta])*
75            $variant:ident
76        ),*
77    }) => {
78        #[derive(Debug, Clone)]
79        $(#[$metas])*
80        pub enum $enum_name {
81            $(
82                $(#[$v_metas])*
83                $variant($variant),
84            )*
85        }
86
87        $(
88        impl From<$variant> for $enum_name {
89             fn from(v: $variant) -> $enum_name {
90                <$enum_name>::$variant(v)
91            }
92        }
93        )*
94
95
96        impl terl::ParseUnit<py_lex::Token> for $enum_name {
97            type Target = $enum_name;
98
99            fn parse(p: &mut terl::Parser<py_lex::Token>) -> terl::ParseResult<Self, py_lex::Token>
100            {
101                terl::Try::<$enum_name, _>::new(p)
102                $(
103                .or_try::<Self, _>(|p| {
104                    p.once_no_try::<$variant ,_>($variant::parse)
105                        .map(<$enum_name>::$variant)
106                })
107                )*
108                .finish()
109            }
110        }
111    };
112}
113
114#[cfg(test)]
115mod tests {
116    use crate::parse_test;
117
118    use super::*;
119
120    #[test]
121    fn good_ident() {
122        parse_test("*)(&%^&*a(*&^%", |p| {
123            assert!(&*p.parse::<Ident>()? == "a");
124            Ok(())
125        })
126    }
127
128    #[test]
129    #[should_panic]
130    fn bad_ident() {
131        parse_test("1*)(&%^&*a(*&^%", |p| {
132            p.parse::<Ident>()?;
133            Ok(())
134        })
135    }
136}