use super::{ comment, errors::{ErrorKind, ParserError}, slice::Slice, }; use crate::ast; pub type Result = std::result::Result; pub struct Parser { pub(super) source: S, pub(super) ptr: usize, pub(super) length: usize, } impl<'s, S> Parser where S: Slice<'s>, { pub fn new(source: S) -> Self { let length = source.as_ref().len(); Self { source, ptr: 0, length, } } pub fn parse( mut self, ) -> std::result::Result, (ast::Resource, Vec)> { let mut errors = vec![]; let mut body = vec![]; self.skip_blank_block(); let mut last_comment = None; let mut last_blank_count = 0; while self.ptr < self.length { let entry_start = self.ptr; let mut entry = self.get_entry(entry_start); if let Some(comment) = last_comment.take() { match entry { Ok(ast::Entry::Message(ref mut msg)) if last_blank_count < 2 => { msg.comment = Some(comment); } Ok(ast::Entry::Term(ref mut term)) if last_blank_count < 2 => { term.comment = Some(comment); } _ => { body.push(ast::Entry::Comment(comment)); } } } match entry { Ok(ast::Entry::Comment(comment)) => { last_comment = Some(comment); } Ok(entry) => { body.push(entry); } Err(mut err) => { self.skip_to_next_entry_start(); err.slice = Some(entry_start..self.ptr); errors.push(err); let content = self.source.slice(entry_start..self.ptr); body.push(ast::Entry::Junk { content }); } } last_blank_count = self.skip_blank_block(); } if let Some(last_comment) = last_comment.take() { body.push(ast::Entry::Comment(last_comment)); } if errors.is_empty() { Ok(ast::Resource { body }) } else { Err((ast::Resource { body }, errors)) } } fn get_entry(&mut self, entry_start: usize) -> Result> { let entry = match get_current_byte!(self) { Some(b'#') => { let (comment, level) = self.get_comment()?; match level { comment::Level::Regular => ast::Entry::Comment(comment), comment::Level::Group => ast::Entry::GroupComment(comment), comment::Level::Resource => ast::Entry::ResourceComment(comment), comment::Level::None => unreachable!(), } } Some(b'-') => ast::Entry::Term(self.get_term(entry_start)?), _ => ast::Entry::Message(self.get_message(entry_start)?), }; Ok(entry) } pub fn get_message(&mut self, entry_start: usize) -> Result> { let id = self.get_identifier()?; self.skip_blank_inline(); self.expect_byte(b'=')?; let pattern = self.get_pattern()?; self.skip_blank_block(); let attributes = self.get_attributes(); if pattern.is_none() && attributes.is_empty() { let entry_id = id.name.as_ref().to_owned(); return error!( ErrorKind::ExpectedMessageField { entry_id }, entry_start, self.ptr ); } Ok(ast::Message { id, value: pattern, attributes, comment: None, }) } pub fn get_term(&mut self, entry_start: usize) -> Result> { self.expect_byte(b'-')?; let id = self.get_identifier()?; self.skip_blank_inline(); self.expect_byte(b'=')?; self.skip_blank_inline(); let value = self.get_pattern()?; self.skip_blank_block(); let attributes = self.get_attributes(); if let Some(value) = value { Ok(ast::Term { id, value, attributes, comment: None, }) } else { error!( ErrorKind::ExpectedTermField { entry_id: id.name.as_ref().to_owned() }, entry_start, self.ptr ) } } fn get_attributes(&mut self) -> Vec> { let mut attributes = vec![]; loop { let line_start = self.ptr; self.skip_blank_inline(); if !self.take_byte_if(b'.') { self.ptr = line_start; break; } if let Ok(attr) = self.get_attribute() { attributes.push(attr); } else { self.ptr = line_start; break; } } attributes } fn get_attribute(&mut self) -> Result> { let id = self.get_identifier()?; self.skip_blank_inline(); self.expect_byte(b'=')?; let pattern = self.get_pattern()?; match pattern { Some(pattern) => Ok(ast::Attribute { id, value: pattern }), None => error!(ErrorKind::MissingValue, self.ptr), } } pub(super) fn get_identifier_unchecked(&mut self) -> ast::Identifier { let mut ptr = self.ptr; while matches!(get_byte!(self, ptr), Some(b) if b.is_ascii_alphanumeric() || *b == b'-' || *b == b'_') { ptr += 1; } let name = self.source.slice(self.ptr - 1..ptr); self.ptr = ptr; ast::Identifier { name } } pub(super) fn get_identifier(&mut self) -> Result> { if !self.is_identifier_start() { return error!( ErrorKind::ExpectedCharRange { range: "a-zA-Z".to_string() }, self.ptr ); } self.ptr += 1; Ok(self.get_identifier_unchecked()) } pub(super) fn get_attribute_accessor(&mut self) -> Result>> { if self.take_byte_if(b'.') { let ident = self.get_identifier()?; Ok(Some(ident)) } else { Ok(None) } } fn get_variant_key(&mut self) -> Result> { self.skip_blank(); let key = if self.is_number_start() { ast::VariantKey::NumberLiteral { value: self.get_number_literal()?, } } else { ast::VariantKey::Identifier { name: self.get_identifier()?.name, } }; self.skip_blank(); self.expect_byte(b']')?; Ok(key) } pub(super) fn get_variants(&mut self) -> Result>> { let mut variants = Vec::with_capacity(2); let mut has_default = false; loop { let default = self.take_byte_if(b'*'); if default { if has_default { return error!(ErrorKind::MultipleDefaultVariants, self.ptr); } else { has_default = true; } } if !self.take_byte_if(b'[') { break; } let key = self.get_variant_key()?; let value = self.get_pattern()?; if let Some(value) = value { variants.push(ast::Variant { key, value, default, }); self.skip_blank(); } else { return error!(ErrorKind::MissingValue, self.ptr); } } if has_default { Ok(variants) } else { error!(ErrorKind::MissingDefaultVariant, self.ptr) } } pub(super) fn get_placeable(&mut self) -> Result> { self.skip_blank(); let exp = self.get_expression()?; self.skip_blank_inline(); self.expect_byte(b'}')?; let invalid_expression_found = match &exp { ast::Expression::Inline(ast::InlineExpression::TermReference { ref attribute, .. }) => attribute.is_some(), _ => false, }; if invalid_expression_found { return error!(ErrorKind::TermAttributeAsPlaceable, self.ptr); } Ok(exp) } }