use crate::{
error::{Error, ErrorInfo, SyntaxError},
lexer::{self, lex, Lexer, Token},
parser::{
ast::{Block, CallTarget, Document, Element, Lines, Node, Text},
call::CallParseContext,
},
SyntaxResult,
};
use std::ops::Range;
static UNKNOWN: &str = "unknown";
pub mod ast;
mod block;
mod call;
pub mod iter;
mod link;
pub(crate) mod path;
mod string;
#[derive(Debug)]
pub struct ParserOptions {
pub file_name: String,
pub line_offset: usize,
pub byte_offset: usize,
}
impl ParserOptions {
pub fn new(
file_name: String,
line_offset: usize,
byte_offset: usize,
) -> Self {
Self {
file_name,
line_offset,
byte_offset,
}
}
}
impl Default for ParserOptions {
fn default() -> Self {
Self {
file_name: UNKNOWN.to_string(),
line_offset: 0,
byte_offset: 0,
}
}
}
#[derive(Debug)]
pub(crate) struct ParseState {
file_name: String,
line: usize,
byte: usize,
}
impl ParseState {
pub fn new() -> Self {
Self {
file_name: UNKNOWN.to_string(),
line: 0,
byte: 0,
}
}
pub fn file_name(&self) -> &str {
&self.file_name
}
pub fn line(&self) -> &usize {
&self.line
}
pub fn line_mut(&mut self) -> &mut usize {
&mut self.line
}
pub fn byte(&self) -> &usize {
&self.byte
}
pub fn byte_mut(&mut self) -> &mut usize {
&mut self.byte
}
pub fn line_range(&self) -> Range<usize> {
self.line.clone()..self.line.clone() + 1
}
}
impl From<&ParserOptions> for ParseState {
fn from(opts: &ParserOptions) -> Self {
Self {
file_name: opts.file_name.clone(),
line: opts.line_offset.clone(),
byte: opts.byte_offset.clone(),
}
}
}
pub struct Parser<'source> {
source: &'source str,
lexer: Lexer<'source>,
state: ParseState,
stack: Vec<(&'source str, Block<'source>)>,
next_token: Option<Token>,
errors: Option<&'source mut Vec<Error>>,
}
impl<'source> Parser<'source> {
pub fn new(source: &'source str, options: ParserOptions) -> Self {
let lexer = lex(source);
let state = ParseState::from(&options);
Self {
source,
lexer,
state,
stack: vec![],
next_token: None,
errors: None,
}
}
pub fn set_errors(&mut self, errors: &'source mut Vec<Error>) {
self.errors = Some(errors);
}
pub fn parse(&mut self) -> SyntaxResult<Node<'source>> {
let mut doc = Document(&self.source, vec![]);
for node in self {
let node = node?;
doc.nodes_mut().push(node);
}
Ok(Node::Document(doc))
}
fn token(&mut self) -> Option<Token> {
if let Some(t) = self.next_token.take() {
self.next_token = None;
Some(t)
} else {
self.lexer.next()
}
}
fn advance(&mut self, next: Token) -> SyntaxResult<Option<Node<'source>>> {
if next.is_newline() {
*self.state.line_mut() += 1;
}
if next.is_text() {
let mut line_range = self.state.line_range();
let (span, next) = block::until(
&mut self.lexer,
&mut self.state,
next.span().clone(),
&|t: &Token| !t.is_text(),
);
self.next_token = next;
line_range.end = self.state.line() + 1;
return Ok(Some(Node::Text(Text::new(
self.source,
span,
line_range,
))));
}
match next {
Token::Block(lex, mut span) => match lex {
lexer::Block::StartRawBlock => {
return block::raw(
self.source,
&mut self.lexer,
&mut self.state,
span,
)
.map(Some);
}
lexer::Block::StartRawComment => {
return block::raw_comment(
self.source,
&mut self.lexer,
&mut self.state,
span,
)
.map(Some);
}
lexer::Block::StartRawStatement => {
return block::raw_statement(
self.source,
&mut self.lexer,
&mut self.state,
span,
)
.map(Some);
}
lexer::Block::StartComment => {
return block::comment(
self.source,
&mut self.lexer,
&mut self.state,
span,
)
.map(Some);
}
lexer::Block::StartBlockScope => {
let block = block::scope(
self.source,
&mut self.lexer,
&mut self.state,
span,
)?;
let name = block.name().ok_or_else(|| {
*self.state.byte_mut() =
block.call().target().span().start;
SyntaxError::BlockName(
ErrorInfo::from((self.source, &mut self.state))
.into(),
)
})?;
match block.call().target() {
CallTarget::Path(ref _path) => {}
CallTarget::SubExpr(_) => {
if !block.call().is_partial() {
return Err(SyntaxError::BlockTargetSubExpr(
ErrorInfo::from((
self.source,
&mut self.state,
))
.into(),
));
}
}
}
self.stack.push((name, block));
while let Some(t) = self.token() {
match self.advance(t) {
Ok(mut node) => {
if node.is_none() || self.stack.is_empty() {
return Ok(node);
} else {
let (_, current) =
self.stack.last_mut().unwrap();
if let Some(node) = node.take() {
match node {
Node::Statement(call) => {
if call.is_conditional() {
let mut condition =
Block::new(
self.source,
call.open_span()
.clone(),
false,
self.state
.line_range(),
);
condition.set_call(call);
current.add_condition(
condition,
);
} else {
current.push(
Node::Statement(call),
);
}
}
_ => {
current.push(node);
}
}
}
}
}
Err(e) => return Err(e),
}
}
}
lexer::Block::EndBlockScope => {
let temp = block::scope(
self.source,
&mut self.lexer,
&mut self.state,
span.clone(),
)?;
if self.stack.is_empty() {
let notes = if let Some(close) = temp.name() {
vec![format!("perhaps open the block '{}'", close)]
} else {
vec![]
};
*self.state.byte_mut() = span.start;
return Err(SyntaxError::BlockNotOpen(
ErrorInfo::from((
self.source,
&mut self.state,
notes,
))
.into(),
));
}
let (open_name, mut block) = self.stack.pop().unwrap();
if let Some(close_name) = temp.name() {
if open_name != close_name {
let notes = vec![format!(
"opening name is '{}'",
open_name
)];
return Err(SyntaxError::TagNameMismatch(
ErrorInfo::from((
self.source,
&mut self.state,
notes,
))
.into(),
));
}
if temp.call().is_closed() {
let end_tag_close = temp.call().span();
span.end = end_tag_close.end;
}
block.exit(span);
block.lines_end(self.state.line());
return Ok(Some(Node::Block(block)));
} else {
return Err(SyntaxError::ExpectedIdentifier(
ErrorInfo::from((self.source, &mut self.state))
.into(),
));
}
}
lexer::Block::StartStatement => {
let context = if self.stack.is_empty() {
CallParseContext::Statement
} else {
CallParseContext::ScopeStatement
};
let call = call::parse(
self.source,
&mut self.lexer,
&mut self.state,
span,
context,
)?;
return Ok(Some(Node::Statement(call)));
}
lexer::Block::StartLink => {
let link = link::parse(
self.source,
&mut self.lexer,
&mut self.state,
span,
)?;
return Ok(Some(Node::Link(link)));
}
_ => {}
},
Token::Link(_, _) => {}
Token::RawComment(_, _) => {}
Token::RawStatement(_, _) => {}
Token::Comment(_, _) => {}
Token::Parameters(_, _) => {}
Token::Array(_, _) => {}
Token::DoubleQuoteString(_, _) => {}
Token::SingleQuoteString(_, _) => {}
}
Ok(None)
}
}
impl<'source> Iterator for Parser<'source> {
type Item = SyntaxResult<Node<'source>>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(t) = self.token() {
match self.advance(t) {
Ok(node) => return node.map(Ok),
Err(e) => {
if let Some(ref mut errors) = self.errors.as_mut() {
errors.push(Error::from(e));
self.next_token = self.lexer.until_mode();
return self.next();
} else {
return Some(Err(e));
}
}
}
}
None
}
}