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}