1 | use super::errors::{ErrorKind, ParserError}; |
2 | use super::{core::Parser, core::Result, slice::Slice}; |
3 | use crate::ast; |
4 | |
5 | impl<'s, S> Parser<S> |
6 | where |
7 | S: Slice<'s>, |
8 | { |
9 | pub(super) fn get_expression(&mut self) -> Result<ast::Expression<S>> { |
10 | let exp = self.get_inline_expression(false)?; |
11 | |
12 | self.skip_blank(); |
13 | |
14 | if !self.is_current_byte(b'-' ) || !self.is_byte_at(b'>' , self.ptr + 1) { |
15 | if let ast::InlineExpression::TermReference { ref attribute, .. } = exp { |
16 | if attribute.is_some() { |
17 | return error!(ErrorKind::TermAttributeAsPlaceable, self.ptr); |
18 | } |
19 | } |
20 | return Ok(ast::Expression::Inline(exp)); |
21 | } |
22 | |
23 | match exp { |
24 | ast::InlineExpression::MessageReference { ref attribute, .. } => { |
25 | if attribute.is_none() { |
26 | return error!(ErrorKind::MessageReferenceAsSelector, self.ptr); |
27 | } else { |
28 | return error!(ErrorKind::MessageAttributeAsSelector, self.ptr); |
29 | } |
30 | } |
31 | ast::InlineExpression::TermReference { ref attribute, .. } => { |
32 | if attribute.is_none() { |
33 | return error!(ErrorKind::TermReferenceAsSelector, self.ptr); |
34 | } |
35 | } |
36 | ast::InlineExpression::StringLiteral { .. } |
37 | | ast::InlineExpression::NumberLiteral { .. } |
38 | | ast::InlineExpression::VariableReference { .. } |
39 | | ast::InlineExpression::FunctionReference { .. } => {} |
40 | _ => { |
41 | return error!(ErrorKind::ExpectedSimpleExpressionAsSelector, self.ptr); |
42 | } |
43 | }; |
44 | |
45 | self.ptr += 2; // -> |
46 | |
47 | self.skip_blank_inline(); |
48 | if !self.skip_eol() { |
49 | return error!( |
50 | ErrorKind::ExpectedCharRange { |
51 | range: " \n | \r\n" .to_string() |
52 | }, |
53 | self.ptr |
54 | ); |
55 | } |
56 | self.skip_blank(); |
57 | |
58 | let variants = self.get_variants()?; |
59 | |
60 | Ok(ast::Expression::Select { |
61 | selector: exp, |
62 | variants, |
63 | }) |
64 | } |
65 | |
66 | pub(super) fn get_inline_expression( |
67 | &mut self, |
68 | only_literal: bool, |
69 | ) -> Result<ast::InlineExpression<S>> { |
70 | match get_current_byte!(self) { |
71 | Some(b'"' ) => { |
72 | self.ptr += 1; // " |
73 | let start = self.ptr; |
74 | while let Some(b) = get_current_byte!(self) { |
75 | match b { |
76 | b' \\' => match get_byte!(self, self.ptr + 1) { |
77 | Some(b' \\' ) | Some(b'{' ) | Some(b'"' ) => self.ptr += 2, |
78 | Some(b'u' ) => { |
79 | self.ptr += 2; |
80 | self.skip_unicode_escape_sequence(4)?; |
81 | } |
82 | Some(b'U' ) => { |
83 | self.ptr += 2; |
84 | self.skip_unicode_escape_sequence(6)?; |
85 | } |
86 | b => { |
87 | let seq = b.unwrap_or(&b' ' ).to_string(); |
88 | return error!(ErrorKind::UnknownEscapeSequence(seq), self.ptr); |
89 | } |
90 | }, |
91 | b'"' => { |
92 | break; |
93 | } |
94 | b' \n' => { |
95 | return error!(ErrorKind::UnterminatedStringLiteral, self.ptr); |
96 | } |
97 | _ => self.ptr += 1, |
98 | } |
99 | } |
100 | |
101 | self.expect_byte(b'"' )?; |
102 | let slice = self.source.slice(start..self.ptr - 1); |
103 | Ok(ast::InlineExpression::StringLiteral { value: slice }) |
104 | } |
105 | Some(b) if b.is_ascii_digit() => { |
106 | let num = self.get_number_literal()?; |
107 | Ok(ast::InlineExpression::NumberLiteral { value: num }) |
108 | } |
109 | Some(b'-' ) if !only_literal => { |
110 | self.ptr += 1; // - |
111 | if self.is_identifier_start() { |
112 | self.ptr += 1; |
113 | let id = self.get_identifier_unchecked(); |
114 | let attribute = self.get_attribute_accessor()?; |
115 | let arguments = self.get_call_arguments()?; |
116 | Ok(ast::InlineExpression::TermReference { |
117 | id, |
118 | attribute, |
119 | arguments, |
120 | }) |
121 | } else { |
122 | self.ptr -= 1; |
123 | let num = self.get_number_literal()?; |
124 | Ok(ast::InlineExpression::NumberLiteral { value: num }) |
125 | } |
126 | } |
127 | Some(b'$' ) if !only_literal => { |
128 | self.ptr += 1; // $ |
129 | let id = self.get_identifier()?; |
130 | Ok(ast::InlineExpression::VariableReference { id }) |
131 | } |
132 | Some(b) if b.is_ascii_alphabetic() => { |
133 | self.ptr += 1; |
134 | let id = self.get_identifier_unchecked(); |
135 | let arguments = self.get_call_arguments()?; |
136 | if let Some(arguments) = arguments { |
137 | if !Self::is_callee(&id.name) { |
138 | return error!(ErrorKind::ForbiddenCallee, self.ptr); |
139 | } |
140 | |
141 | Ok(ast::InlineExpression::FunctionReference { id, arguments }) |
142 | } else { |
143 | let attribute = self.get_attribute_accessor()?; |
144 | Ok(ast::InlineExpression::MessageReference { id, attribute }) |
145 | } |
146 | } |
147 | Some(b'{' ) if !only_literal => { |
148 | self.ptr += 1; // { |
149 | let exp = self.get_placeable()?; |
150 | Ok(ast::InlineExpression::Placeable { |
151 | expression: Box::new(exp), |
152 | }) |
153 | } |
154 | _ if only_literal => error!(ErrorKind::ExpectedLiteral, self.ptr), |
155 | _ => error!(ErrorKind::ExpectedInlineExpression, self.ptr), |
156 | } |
157 | } |
158 | |
159 | pub fn get_call_arguments(&mut self) -> Result<Option<ast::CallArguments<S>>> { |
160 | self.skip_blank(); |
161 | if !self.take_byte_if(b'(' ) { |
162 | return Ok(None); |
163 | } |
164 | |
165 | let mut positional = vec![]; |
166 | let mut named = vec![]; |
167 | let mut argument_names = vec![]; |
168 | |
169 | self.skip_blank(); |
170 | |
171 | while self.ptr < self.length { |
172 | if self.is_current_byte(b')' ) { |
173 | break; |
174 | } |
175 | |
176 | let expr = self.get_inline_expression(false)?; |
177 | |
178 | if let ast::InlineExpression::MessageReference { |
179 | ref id, |
180 | attribute: None, |
181 | } = expr |
182 | { |
183 | self.skip_blank(); |
184 | if self.is_current_byte(b':' ) { |
185 | if argument_names.contains(&id.name) { |
186 | return error!( |
187 | ErrorKind::DuplicatedNamedArgument(id.name.as_ref().to_owned()), |
188 | self.ptr |
189 | ); |
190 | } |
191 | self.ptr += 1; |
192 | self.skip_blank(); |
193 | let val = self.get_inline_expression(true)?; |
194 | |
195 | argument_names.push(id.name.clone()); |
196 | named.push(ast::NamedArgument { |
197 | name: ast::Identifier { |
198 | name: id.name.clone(), |
199 | }, |
200 | value: val, |
201 | }); |
202 | } else { |
203 | if !argument_names.is_empty() { |
204 | return error!(ErrorKind::PositionalArgumentFollowsNamed, self.ptr); |
205 | } |
206 | positional.push(expr); |
207 | } |
208 | } else { |
209 | if !argument_names.is_empty() { |
210 | return error!(ErrorKind::PositionalArgumentFollowsNamed, self.ptr); |
211 | } |
212 | positional.push(expr); |
213 | } |
214 | |
215 | self.skip_blank(); |
216 | self.take_byte_if(b',' ); |
217 | self.skip_blank(); |
218 | } |
219 | |
220 | self.expect_byte(b')' )?; |
221 | |
222 | Ok(Some(ast::CallArguments { positional, named })) |
223 | } |
224 | } |
225 | |