use super::errors::{ErrorKind, ParserError}; use super::{core::Parser, core::Result, slice::Slice}; use crate::ast; impl<'s, S> Parser where S: Slice<'s>, { pub(super) fn get_expression(&mut self) -> Result> { let exp = self.get_inline_expression(false)?; self.skip_blank(); if !self.is_current_byte(b'-') || !self.is_byte_at(b'>', self.ptr + 1) { if let ast::InlineExpression::TermReference { ref attribute, .. } = exp { if attribute.is_some() { return error!(ErrorKind::TermAttributeAsPlaceable, self.ptr); } } return Ok(ast::Expression::Inline(exp)); } match exp { ast::InlineExpression::MessageReference { ref attribute, .. } => { if attribute.is_none() { return error!(ErrorKind::MessageReferenceAsSelector, self.ptr); } else { return error!(ErrorKind::MessageAttributeAsSelector, self.ptr); } } ast::InlineExpression::TermReference { ref attribute, .. } => { if attribute.is_none() { return error!(ErrorKind::TermReferenceAsSelector, self.ptr); } } ast::InlineExpression::StringLiteral { .. } | ast::InlineExpression::NumberLiteral { .. } | ast::InlineExpression::VariableReference { .. } | ast::InlineExpression::FunctionReference { .. } => {} _ => { return error!(ErrorKind::ExpectedSimpleExpressionAsSelector, self.ptr); } }; self.ptr += 2; // -> self.skip_blank_inline(); if !self.skip_eol() { return error!( ErrorKind::ExpectedCharRange { range: "\n | \r\n".to_string() }, self.ptr ); } self.skip_blank(); let variants = self.get_variants()?; Ok(ast::Expression::Select { selector: exp, variants, }) } pub(super) fn get_inline_expression( &mut self, only_literal: bool, ) -> Result> { match get_current_byte!(self) { Some(b'"') => { self.ptr += 1; // " let start = self.ptr; while let Some(b) = get_current_byte!(self) { match b { b'\\' => match get_byte!(self, self.ptr + 1) { Some(b'\\') | Some(b'{') | Some(b'"') => self.ptr += 2, Some(b'u') => { self.ptr += 2; self.skip_unicode_escape_sequence(4)?; } Some(b'U') => { self.ptr += 2; self.skip_unicode_escape_sequence(6)?; } b => { let seq = b.unwrap_or(&b' ').to_string(); return error!(ErrorKind::UnknownEscapeSequence(seq), self.ptr); } }, b'"' => { break; } b'\n' => { return error!(ErrorKind::UnterminatedStringLiteral, self.ptr); } _ => self.ptr += 1, } } self.expect_byte(b'"')?; let slice = self.source.slice(start..self.ptr - 1); Ok(ast::InlineExpression::StringLiteral { value: slice }) } Some(b) if b.is_ascii_digit() => { let num = self.get_number_literal()?; Ok(ast::InlineExpression::NumberLiteral { value: num }) } Some(b'-') if !only_literal => { self.ptr += 1; // - if self.is_identifier_start() { self.ptr += 1; let id = self.get_identifier_unchecked(); let attribute = self.get_attribute_accessor()?; let arguments = self.get_call_arguments()?; Ok(ast::InlineExpression::TermReference { id, attribute, arguments, }) } else { self.ptr -= 1; let num = self.get_number_literal()?; Ok(ast::InlineExpression::NumberLiteral { value: num }) } } Some(b'$') if !only_literal => { self.ptr += 1; // $ let id = self.get_identifier()?; Ok(ast::InlineExpression::VariableReference { id }) } Some(b) if b.is_ascii_alphabetic() => { self.ptr += 1; let id = self.get_identifier_unchecked(); let arguments = self.get_call_arguments()?; if let Some(arguments) = arguments { if !Self::is_callee(&id.name) { return error!(ErrorKind::ForbiddenCallee, self.ptr); } Ok(ast::InlineExpression::FunctionReference { id, arguments }) } else { let attribute = self.get_attribute_accessor()?; Ok(ast::InlineExpression::MessageReference { id, attribute }) } } Some(b'{') if !only_literal => { self.ptr += 1; // { let exp = self.get_placeable()?; Ok(ast::InlineExpression::Placeable { expression: Box::new(exp), }) } _ if only_literal => error!(ErrorKind::ExpectedLiteral, self.ptr), _ => error!(ErrorKind::ExpectedInlineExpression, self.ptr), } } pub fn get_call_arguments(&mut self) -> Result>> { self.skip_blank(); if !self.take_byte_if(b'(') { return Ok(None); } let mut positional = vec![]; let mut named = vec![]; let mut argument_names = vec![]; self.skip_blank(); while self.ptr < self.length { if self.is_current_byte(b')') { break; } let expr = self.get_inline_expression(false)?; if let ast::InlineExpression::MessageReference { ref id, attribute: None, } = expr { self.skip_blank(); if self.is_current_byte(b':') { if argument_names.contains(&id.name) { return error!( ErrorKind::DuplicatedNamedArgument(id.name.as_ref().to_owned()), self.ptr ); } self.ptr += 1; self.skip_blank(); let val = self.get_inline_expression(true)?; argument_names.push(id.name.clone()); named.push(ast::NamedArgument { name: ast::Identifier { name: id.name.clone(), }, value: val, }); } else { if !argument_names.is_empty() { return error!(ErrorKind::PositionalArgumentFollowsNamed, self.ptr); } positional.push(expr); } } else { if !argument_names.is_empty() { return error!(ErrorKind::PositionalArgumentFollowsNamed, self.ptr); } positional.push(expr); } self.skip_blank(); self.take_byte_if(b','); self.skip_blank(); } self.expect_byte(b')')?; Ok(Some(ast::CallArguments { positional, named })) } }