pr47/parse/parser/
attr.rs

1use super::Parser;
2
3use smallvec::SmallVec;
4use xjbutil::defer;
5use crate::awa;
6
7use crate::diag::diag_data;
8use crate::diag::location::SourceRange;
9use crate::parse::lexer::LexerMode;
10use crate::syntax::attr::{AttrAssignLikeItem, AttrCallLikeItem, AttrItem, AttrValue, Attribute};
11use crate::syntax::id::Identifier;
12use crate::syntax::token::{Token, TokenInner};
13
14impl<'s, 'd> Parser<'s, 'd> {
15    pub fn parse_attribute(
16        &mut self,
17        hash_token: Token<'s>,
18        is_global: bool,
19        skip_set: &[&[TokenInner<'_>]]
20    ) -> Option<Attribute<'s>> {
21        debug_assert_eq!(hash_token.token_inner, TokenInner::SymHash);
22
23        let this: &mut Parser<'s, 'd> = self;
24
25        defer!(|this: &mut Parser<'s, 'd>| {
26            this.lexer.pop_lexer_mode()
27        }, this);
28
29        this.lexer.push_lexer_mode(LexerMode::LexAttr);
30        this.parse_attribute_impl(hash_token, is_global, skip_set)
31    }
32
33    fn parse_attribute_impl(
34        &mut self,
35        hash_token: Token<'s>,
36        is_global: bool,
37        skip_set: &[&[TokenInner<'_>]]
38    ) -> Option<Attribute<'s>> {
39        let hash_range: SourceRange = hash_token.range;
40
41        let exclaim_range = if is_global {
42            self.expect_n_consume(TokenInner::SymExclaim, skip_set)
43                .expect("only appoint `is_global` when here's surely a global attribute")
44                .range
45        } else {
46            SourceRange::unknown()
47        };
48
49        let left_bracket_range: SourceRange =
50            self.expect_n_consume(TokenInner::SymLBracket, skip_set)?.range;
51        let (items, right_bracket_range): (SmallVec<[AttrItem<'s>; 4]>, SourceRange) =
52            self.parse_attribute_list(TokenInner::SymRBracket, skip_set)?;
53
54        Some(Attribute {
55            items,
56
57            hash_loc: hash_range.left(),
58            exclaim_loc: exclaim_range.left(),
59            left_bracket_loc: left_bracket_range.left(),
60            right_bracket_loc: right_bracket_range.left()
61        })
62    }
63
64    pub fn parse_attribute_list(
65        &mut self,
66        termination: TokenInner<'_>,
67        skip_set: &[&[TokenInner<'_>]]
68    ) -> Option<(SmallVec<[AttrItem<'s>; 4]>, SourceRange)> {
69        self.parse_list_alike_nonnull(
70            Self::parse_attribute_item,
71            skip_set,
72            TokenInner::SymComma,
73            termination,
74            skip_set
75        )
76    }
77
78    pub fn parse_attribute_item(&mut self, skip_set: &[&[TokenInner<'_>]]) -> Option<AttrItem<'s>> {
79        let ident: Identifier<'s> = self.parse_ident().or_else(|| {
80            self.skip_to_any_of(skip_set);
81            None
82        })?;
83
84        match self.current_token().token_inner {
85            TokenInner::SymEq => {
86                let eq_token: Token<'s> = self.consume_token();
87                self.parse_attribute_assign_alike_item(ident, eq_token, skip_set)
88            }
89            TokenInner::SymLParen => {
90                let lparen_token: Token<'s> = self.consume_token();
91                self.parse_attribute_call_alike_item(ident, lparen_token, skip_set)
92            }
93            _ => Some(AttrItem::IdentifierItem(ident))
94        }
95    }
96
97    pub fn parse_attribute_assign_alike_item(
98        &mut self,
99        ident: Identifier<'s>,
100        eq_token: Token<'s>,
101        skip_set: &[&[TokenInner<'_>]]
102    ) -> Option<AttrItem<'s>> {
103        debug_assert_eq!(eq_token.token_inner, TokenInner::SymEq);
104
105        let attr_value: AttrValue<'s> = self.parse_attr_value(skip_set)?;
106        Some(AttrItem::AssignLikeItem(AttrAssignLikeItem {
107            ident,
108            value: attr_value,
109            assign_loc: eq_token.range.left()
110        }))
111    }
112
113    pub fn parse_attribute_call_alike_item(
114        &mut self,
115        ident: Identifier<'s>,
116        lparen_token: Token<'s>,
117        skip_set: &[&[TokenInner<'_>]]
118    ) -> Option<AttrItem<'s>> {
119        debug_assert_eq!(lparen_token.token_inner, TokenInner::SymLParen);
120        let (items, right_paren_range): (SmallVec<[AttrItem; 4]>, SourceRange) =
121            self.parse_attribute_list(TokenInner::SymRParen, skip_set)?;
122        Some(AttrItem::CallLikeItem(AttrCallLikeItem {
123            ident,
124            args: items.into_iter().collect::<Vec<_>>(),
125            left_paren_loc: lparen_token.range.left(),
126            right_paren_loc: right_paren_range.left()
127        }))
128    }
129
130    pub fn parse_attr_value(&mut self, skip_set: &[&[TokenInner<'_>]]) -> Option<AttrValue<'s>> {
131        let range: SourceRange = self.current_token().range;
132        match self.current_token().token_inner {
133            TokenInner::Ident(_) => {
134                let ident: Identifier<'s> = self.parse_ident().or_else(|| {
135                    self.skip_to_any_of(skip_set);
136                    None
137                })?;
138                Some(AttrValue::ident_value(ident))
139            },
140            TokenInner::LitInt(int_value) => Some(AttrValue::int_value(int_value, range)),
141            TokenInner::LitFloat(float_value) => Some(AttrValue::float_value(float_value, range)),
142            TokenInner::LitChar(char_value) => Some(AttrValue::char_value(char_value, range)),
143            TokenInner::LitStr(str_value) => Some(AttrValue::string_value(str_value, range)),
144            TokenInner::KwdTrue => Some(AttrValue::bool_value(true, range)),
145            TokenInner::KwdFalse => Some(AttrValue::bool_value(false, range)),
146            _ => {
147                self.diag.borrow_mut()
148                    .diag(range.left(), diag_data::err_expected_any_of_0_got_1)
149                    .add_arg2(awa![
150                        TokenInner::Ident(""),
151                        TokenInner::LitInt(0),
152                        TokenInner::LitFloat(0.0),
153                        TokenInner::LitChar(' '),
154                        TokenInner::LitStr(""),
155                        TokenInner::KwdTrue,
156                        TokenInner::KwdFalse
157                    ])
158                    .add_arg2(self.current_token().token_inner)
159                    .add_mark(range.into())
160                    .emit();
161                self.skip_to_any_of(skip_set);
162                None
163            }
164        }
165    }
166}
167
168#[cfg(test)]
169mod test {
170    use std::cell::RefCell;
171
172    use crate::diag::DiagContext;
173    use crate::parse::parser::Parser;
174    use crate::syntax::attr::{
175        AttrAssignLikeItem,
176        AttrCallLikeItem,
177        AttrItem,
178        AttrValue,
179        AttrValueInner,
180        Attribute
181    };
182    use crate::syntax::id::{assert_ident_unqual, assert_ident_qual};
183    use crate::syntax::token::Token;
184
185    #[test]
186    fn test_parse_global_attribute() {
187        let source: &str = "#![some::attribute, another = config, call(arg1, arg2, par3 = arg3)]";
188
189        let diag: RefCell<DiagContext> = RefCell::new(DiagContext::new());
190        let mut parser: Parser = Parser::new(
191            0, source, &diag
192        );
193
194        let hash_token: Token = parser.consume_token();
195        let attr: Attribute = parser.parse_attribute(hash_token, true, &[]).unwrap();
196        assert_eq!(attr.items.len(), 3);
197
198        if let AttrItem::IdentifierItem(ident) = &attr.items[0] {
199            assert_ident_qual(ident, &["some", "attribute"]);
200        } else {
201            panic!("should be a identifier item");
202        }
203
204        if let AttrItem::AssignLikeItem(AttrAssignLikeItem { ident, value, .. }) = &attr.items[1] {
205            assert_ident_unqual(ident, "another");
206            if let AttrValue { inner: AttrValueInner::Identifier(ident), .. } = &value {
207                assert_ident_unqual(ident, "config");
208            } else {
209                panic!("should be a identifier value");
210            }
211        } else {
212            panic!("should be a assign like item");
213        }
214
215        if let AttrItem::CallLikeItem(AttrCallLikeItem { ident, args, .. }) = &attr.items[2] {
216            assert_ident_unqual(ident, "call");
217            assert_eq!(args.len(), 3);
218
219            if let AttrItem::IdentifierItem(ident) = &args[0] {
220                assert_ident_unqual(ident, "arg1");
221            } else {
222                panic!("should be a identifier item");
223            }
224
225            if let AttrItem::IdentifierItem(ident) = &args[1] {
226                assert_ident_unqual(ident, "arg2");
227            } else {
228                panic!("should be a identifier item");
229            }
230
231            if let AttrItem::AssignLikeItem(AttrAssignLikeItem { ident, value, .. }) = &args[2] {
232                assert_ident_unqual(ident, "par3");
233                if let AttrValue { inner: AttrValueInner::Identifier(ident), .. } = &value {
234                    assert_ident_unqual(ident, "arg3");
235                } else {
236                    panic!("should be a identifier value");
237                }
238            } else {
239                panic!("should be a assign like item");
240            }
241        }
242    }
243}