luaur_ast/methods/
parser_parse_attribute.rs1use crate::functions::is_constant_literal::is_constant_literal;
8use crate::functions::is_literal_table::is_literal_table;
9use crate::records::allocator::Allocator;
10use crate::records::ast_array::AstArray;
11use crate::records::ast_attr::{AstAttr, AstAttrType};
12use crate::records::ast_expr::AstExpr;
13use crate::records::ast_name::AstName;
14use crate::records::lexeme::Type;
15use crate::records::location::Location;
16use crate::records::match_lexeme::MatchLexeme;
17use crate::records::parser::Parser;
18use crate::records::temp_vector::TempVector;
19use luaur_common::LUAU_ASSERT;
20
21impl Parser {
22 pub fn parse_attribute(&mut self, attributes: &mut TempVector<'_, *mut AstAttr>) {
23 let empty: AstArray<*mut AstExpr> = AstArray {
24 data: core::ptr::null_mut(),
25 size: 0,
26 };
27
28 LUAU_ASSERT!(
29 self.lexer.current().r#type == Type::Attribute
30 || self.lexer.current().r#type == Type::AttributeOpen
31 );
32
33 if self.lexer.current().r#type == Type::Attribute {
34 let loc = self.lexer.current().location;
35 let name = unsafe { self.lexer.current().data.name };
36 let name_str = unsafe { core::ffi::CStr::from_ptr(name).to_string_lossy() };
37 let ty = self.validate_attribute(loc, &name_str, attributes, &empty);
38
39 self.next_lexeme();
40
41 let node = unsafe {
42 (*self.allocator).alloc(
43 AstAttr::ast_attr_location_type_item_ast_array_ast_expr_ast_name(
44 loc,
45 ty.unwrap_or(AstAttrType::Unknown),
46 empty,
47 AstName { value: name },
48 ),
49 )
50 };
51 attributes.push_back(node);
52 } else {
53 let open = *self.lexer.current();
54 self.next_lexeme();
55
56 if self.lexer.current().r#type != Type(b']' as i32) {
57 loop {
58 let name = self.parse_name("attribute name");
59 let name_loc = name.location;
60 let attr_name = name.name.value;
61
62 let ct = self.lexer.current().r#type;
63 if ct == Type::RawString
64 || ct == Type::QuotedString
65 || ct == Type(b'{' as i32)
66 || ct == Type(b'(' as i32)
67 {
68 let (args, args_location, _expr_location) =
69 self.parse_call_list(core::ptr::null_mut());
70
71 for i in 0..args.size {
72 let arg = unsafe { *args.data.add(i) };
73 if !is_constant_literal(arg) && !is_literal_table(arg) {
74 self.report(
75 args_location,
76 format_args!(
77 "Only literals can be passed as arguments for attributes"
78 ),
79 );
80 }
81 }
82
83 let attr_name_str =
84 unsafe { core::ffi::CStr::from_ptr(attr_name).to_string_lossy() };
85 let ty =
86 self.validate_attribute(name_loc, &attr_name_str, attributes, &args);
87
88 let node = unsafe {
89 (*self.allocator).alloc(
90 AstAttr::ast_attr_location_type_item_ast_array_ast_expr_ast_name(
91 Location::new(name_loc.begin, args_location.end),
92 ty.unwrap_or(AstAttrType::Unknown),
93 args,
94 AstName { value: attr_name },
95 ),
96 )
97 };
98 attributes.push_back(node);
99 } else {
100 let attr_name_str =
101 unsafe { core::ffi::CStr::from_ptr(attr_name).to_string_lossy() };
102 let ty =
103 self.validate_attribute(name_loc, &attr_name_str, attributes, &empty);
104 let node = unsafe {
105 (*self.allocator).alloc(
106 AstAttr::ast_attr_location_type_item_ast_array_ast_expr_ast_name(
107 name_loc,
108 ty.unwrap_or(AstAttrType::Unknown),
109 empty,
110 AstName { value: attr_name },
111 ),
112 )
113 };
114 attributes.push_back(node);
115 }
116
117 if self.lexer.current().r#type == Type(b',' as i32) {
118 self.next_lexeme();
119 } else {
120 break;
121 }
122 }
123 } else {
124 let end_loc = self.lexer.current().location;
125 self.report(
126 Location::new(open.location.begin, end_loc.end),
127 format_args!("Attribute list cannot be empty"),
128 );
129
130 let node = unsafe {
132 (*self.allocator).alloc(
133 AstAttr::ast_attr_location_type_item_ast_array_ast_expr_ast_name(
134 Location::new(open.location.begin, end_loc.end),
135 AstAttrType::Unknown,
136 empty,
137 self.name_error,
138 ),
139 )
140 };
141 attributes.push_back(node);
142 }
143
144 self.expect_match_and_consume(']', &MatchLexeme::new(&open), false);
145 }
146 }
147}