use crate::lexer::Token; use miette::{Diagnostic, NamedSource, SourceSpan}; use std::fmt; use std::sync::Arc; #[derive(Debug, Diagnostic)] pub enum SyntaxError { #[diagnostic(code(tachyon::syntax::missing_close))] #[diagnostic(help("add a ']' to match this opening bracket"))] MissingClosingBracket { #[source_code] src: NamedSource>, #[label("this bracket is never closed")] span: SourceSpan, }, #[diagnostic(code(tachyon::syntax::missing_open))] #[diagnostic(help("add a '[' to match this closing bracket"))] MissingOpenBracket { #[source_code] src: NamedSource>, #[label("this bracket is never opened")] span: SourceSpan, }, } impl fmt::Display for SyntaxError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { SyntaxError::MissingClosingBracket { .. } => write!(f, "unmatched opening bracket"), SyntaxError::MissingOpenBracket { .. } => write!(f, "unmatched closing bracket"), } } } impl std::error::Error for SyntaxError {} pub fn parse_brackets( tokens: &[Token], filename: &str, source: &str, ) -> Result, SyntaxError> { let mut jump_map = vec![0; tokens.len()]; let mut balance = Vec::new(); for (pos, &e) in tokens.iter().enumerate() { if e.byte == b'[' { balance.push(pos); } else if e.byte == b']' { match balance.pop() { Some(pop) => { jump_map[pos] = pop; jump_map[pop] = pos; } None => { return Err(SyntaxError::MissingOpenBracket { src: NamedSource::new(Arc::from(filename), Arc::from(source)), span: (e.offset, 1).into(), }); } } } } match balance.last() { Some(pos) => { let tok = &tokens[*pos]; Err(SyntaxError::MissingClosingBracket { src: NamedSource::new(Arc::from(filename), Arc::from(source)), span: (tok.offset, 1).into(), }) } None => Ok(jump_map), } } #[cfg(test)] mod tests;