| 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 | |