use std::{
backtrace::Backtrace,
error::Error as StdError,
fmt::{Display, Formatter},
};
use tree_sitter::{Language, Parser, Tree};
pub fn parse_tree(source: &str) -> Result<Tree, ParseError> {
let mut parser = Parser::new();
let language: Language = tree_sitter_achitekfile::LANGUAGE.into();
parser.set_language(&language)?;
let ast: Tree = parser
.parse(source, None)
.ok_or_else(ParseError::parse_cancelled)?;
Ok(ast)
}
#[derive(Debug)]
pub struct ParseError {
kind: ParseErrorKind,
backtrace: Backtrace,
}
impl ParseError {
fn language(source: tree_sitter::LanguageError) -> Self {
Self {
kind: ParseErrorKind::Language(source),
backtrace: Backtrace::capture(),
}
}
fn parse_cancelled() -> Self {
Self {
kind: ParseErrorKind::ParseCancelled,
backtrace: Backtrace::capture(),
}
}
pub fn is_parse_cancelled(&self) -> bool {
matches!(self.kind, ParseErrorKind::ParseCancelled)
}
pub fn is_language(&self) -> bool {
matches!(self.kind, ParseErrorKind::Language(_))
}
pub fn language_error(&self) -> Option<&tree_sitter::LanguageError> {
match &self.kind {
ParseErrorKind::Language(source) => Some(source),
ParseErrorKind::ParseCancelled => None,
}
}
pub fn backtrace(&self) -> &Backtrace {
&self.backtrace
}
}
impl From<tree_sitter::LanguageError> for ParseError {
fn from(source: tree_sitter::LanguageError) -> Self {
Self::language(source)
}
}
impl Display for ParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.kind {
ParseErrorKind::Language(source) => {
writeln!(f, "failed to configure the Achitek parser: {source}")?;
}
ParseErrorKind::ParseCancelled => {
writeln!(f, "tree-sitter did not produce a parse tree")?;
}
}
write!(f, "backtrace:\n{}", self.backtrace)
}
}
impl StdError for ParseError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match &self.kind {
ParseErrorKind::Language(source) => Some(source),
ParseErrorKind::ParseCancelled => None,
}
}
}
#[derive(Debug)]
enum ParseErrorKind {
Language(tree_sitter::LanguageError),
ParseCancelled,
}