use crate::ast::{Element, Location};
use miette::{NamedSource, SourceSpan};
pub use crate::rinha::*;
#[derive(Debug, Clone, serde::Serialize)]
pub struct Var {
pub text: String,
pub location: Location,
}
impl Element for Var {
fn location(&self) -> &Location {
&self.location
}
}
#[derive(miette::Diagnostic, thiserror::Error, Debug)]
#[error("could not parse due the following errors")]
#[diagnostic()]
pub struct ParseError {
#[source_code]
source_code: NamedSource,
#[related]
related: Vec<InnerError>,
}
#[derive(miette::Diagnostic, thiserror::Error, Debug)]
#[error("can't parse the file")]
#[diagnostic()]
pub enum InnerError {
#[error("expected function body")]
#[diagnostic(
code(zu::expected_function_body),
url(docsrs),
help("maybe add a body")
)]
FunctionBodyMissing {
#[label = "here"]
err_span: SourceSpan,
},
#[error("expected or binding or signature statement")]
#[diagnostic(
code(zu::expected_statement),
url(docsrs),
help("maybe assign a type representation")
)]
ExpectedStatement {
#[label = "here"]
err_span: SourceSpan,
},
#[error("record index isn't supported yet")]
#[diagnostic(
code(zu::unsupported_record_index),
url(docsrs),
help("maybe open a PR")
)]
UnsupportedRecordIndex {
#[label = "here"]
err_span: SourceSpan,
},
#[error("invalid token")]
#[diagnostic(code(zu::invalid_token), url(docsrs))]
InvalidToken {
#[label = "here"]
err_span: SourceSpan,
},
#[error("unrecognized token")]
#[diagnostic(code(zu::unrecognized_token), url(docsrs))]
UnrecoginzedToken {
#[label = "here"]
err_span: SourceSpan,
#[help]
help: String,
},
#[error("expected token, but got eof")]
#[diagnostic(code(zu::expected_token), url(docsrs))]
ExpectedToken {
#[label = "here"]
err_span: SourceSpan,
#[help]
help: String,
},
#[error("extra token, {}", token)]
#[diagnostic(code(zu::extra_token), url(docsrs))]
ExtraToken {
#[label = "here"]
err_span: SourceSpan,
token: String,
},
}
fn fmt_expected(expected: &[String]) -> String {
let mut f = String::new();
if !expected.is_empty() {
for (i, e) in expected.iter().enumerate() {
let sep = match i {
0 => "expected one of",
_ if i < expected.len() - 1 => ",",
_ => " or",
};
f.push_str(&format!("{sep} {e}"));
}
}
f
}
pub fn parse_or_report(filename: &str, text: &str) -> Result<crate::ast::File, ParseError> {
let mut errors = vec![];
let ast = match crate::rinha::FileParser::new().parse(&mut errors, filename, text) {
Ok(ast) => ast,
Err(error) => {
errors.push(lalrpop_util::ErrorRecovery {
dropped_tokens: vec![],
error,
});
return Err(ParseError {
related: vec![],
source_code: NamedSource::new(filename, text.to_string()),
});
}
};
if errors.is_empty() {
return Ok(ast);
}
Err(ParseError {
related: errors
.into_iter()
.map(|recovery| {
use lalrpop_util::ParseError::*;
match recovery.error {
InvalidToken { location } => InnerError::InvalidToken {
err_span: SourceSpan::from(location..location),
},
UnrecognizedEof { location, expected } => InnerError::ExpectedToken {
err_span: SourceSpan::from(location..location),
help: fmt_expected(&expected),
},
UnrecognizedToken { token, expected } => InnerError::UnrecoginzedToken {
err_span: SourceSpan::from(token.0..token.2),
help: fmt_expected(&expected),
},
ExtraToken { ref token } => InnerError::ExtraToken {
err_span: SourceSpan::from(token.0..token.2),
token: token.1.to_string(),
},
User { error } => error,
}
})
.collect(),
source_code: NamedSource::new(filename, text.to_string()),
})
}