Work on error messages
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
7f7bf5c98d
commit
42698bb219
13 changed files with 333 additions and 160 deletions
|
|
@ -22,7 +22,7 @@ fn asting_benchmark(c: &mut Criterion) {
|
||||||
parsing.bench_with_input(BenchmarkId::from_parameter(size), &input, |b, input| {
|
parsing.bench_with_input(BenchmarkId::from_parameter(size), &input, |b, input| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let tokens = nomo::lexer::parse(input.clone()).unwrap();
|
let tokens = nomo::lexer::parse(input.clone()).unwrap();
|
||||||
let _ast = nomo::parser::parse(tokens.tokens()).unwrap();
|
let _ast = nomo::parser::parse(input.clone(), tokens.tokens()).unwrap();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ fn asting_nested(c: &mut Criterion) {
|
||||||
parsing.bench_with_input(BenchmarkId::from_parameter(size), &input, |b, input| {
|
parsing.bench_with_input(BenchmarkId::from_parameter(size), &input, |b, input| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let tokens = nomo::lexer::parse(input.clone()).unwrap();
|
let tokens = nomo::lexer::parse(input.clone()).unwrap();
|
||||||
let _ast = nomo::parser::parse(tokens.tokens()).unwrap();
|
let _ast = nomo::parser::parse(input.clone(), tokens.tokens()).unwrap();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -579,14 +579,15 @@ fn emit_expr_load(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::compiler::emit_machine;
|
use crate::compiler::emit_machine;
|
||||||
|
use crate::input::NomoInput;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_simple_variable_interpolation() {
|
fn check_simple_variable_interpolation() {
|
||||||
let input = "Hello {{= world }}";
|
let input = NomoInput::from("Hello {{= world }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = crate::parser::parse(parsed.tokens()).unwrap();
|
let ast = crate::parser::parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
let emit = emit_machine(ast);
|
let emit = emit_machine(ast);
|
||||||
|
|
||||||
|
|
@ -619,11 +620,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_if_else_if() {
|
fn check_if_else_if() {
|
||||||
let input = "{{ if foo }} foo {{ else if bar }} bar {{ else }} foobar {{ end }}";
|
let input =
|
||||||
|
NomoInput::from("{{ if foo }} foo {{ else if bar }} bar {{ else }} foobar {{ end }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = crate::parser::parse(parsed.tokens()).unwrap();
|
let ast = crate::parser::parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
let emit = emit_machine(ast);
|
let emit = emit_machine(ast);
|
||||||
|
|
||||||
|
|
@ -632,11 +634,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_function_call() {
|
fn check_function_call() {
|
||||||
let input = "{{ if foo(23) }} bar {{ else }} foobar {{ end }}";
|
let input = NomoInput::from("{{ if foo(23) }} bar {{ else }} foobar {{ end }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = crate::parser::parse(parsed.tokens()).unwrap();
|
let ast = crate::parser::parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
let emit = emit_machine(ast);
|
let emit = emit_machine(ast);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use annotate_snippets::AnnotationKind;
|
use annotate_snippets::AnnotationKind;
|
||||||
use annotate_snippets::Level;
|
use annotate_snippets::Level;
|
||||||
|
use annotate_snippets::Patch;
|
||||||
use annotate_snippets::Renderer;
|
use annotate_snippets::Renderer;
|
||||||
use annotate_snippets::Snippet;
|
use annotate_snippets::Snippet;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -15,25 +16,38 @@ use crate::parser::AstError;
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub struct AstFailure {
|
pub struct AstFailure {
|
||||||
errors: Vec<AstError>,
|
errors: Vec<AstError>,
|
||||||
|
input: NomoInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for AstFailure {
|
impl std::fmt::Display for AstFailure {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_str("TODO")
|
self.render(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstFailure {
|
impl AstFailure {
|
||||||
pub(crate) fn from_errors(errors: Vec<AstError>) -> AstFailure {
|
pub(crate) fn from_errors(errors: Vec<AstError>, source: NomoInput) -> AstFailure {
|
||||||
AstFailure { errors }
|
AstFailure {
|
||||||
|
errors,
|
||||||
|
input: source,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a CLI printable report
|
/// Create a CLI printable report
|
||||||
pub fn to_report(&self, source: &str) -> String {
|
pub fn to_report(&self) -> String {
|
||||||
let reports = self
|
self.to_string()
|
||||||
.errors
|
}
|
||||||
.iter()
|
|
||||||
.map(|error| {
|
/// Render this failure to the given formatter
|
||||||
|
///
|
||||||
|
/// Note, you can also use [`to_string`](ToString::to_string), as this type also implements
|
||||||
|
/// [`Display`](std::fmt::Display).
|
||||||
|
pub fn render(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
let renderer = annotate_snippets::Renderer::styled()
|
||||||
|
.decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
|
||||||
|
|
||||||
|
for error in &self.errors {
|
||||||
|
let mut snippets = vec![
|
||||||
annotate_snippets::Level::ERROR
|
annotate_snippets::Level::ERROR
|
||||||
.primary_title(
|
.primary_title(
|
||||||
error
|
error
|
||||||
|
|
@ -41,26 +55,39 @@ impl AstFailure {
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or("An error occurred while producing an Ast"),
|
.unwrap_or("An error occurred while producing an Ast"),
|
||||||
)
|
)
|
||||||
.element(annotate_snippets::Snippet::source(source).annotation(
|
.element(
|
||||||
annotate_snippets::AnnotationKind::Primary.span(
|
annotate_snippets::Snippet::source(self.input.as_str()).annotation(
|
||||||
constrain_without_whitespace(
|
annotate_snippets::AnnotationKind::Primary.span(
|
||||||
source,
|
constrain_without_whitespace(
|
||||||
error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0),
|
self.input.as_ref(),
|
||||||
|
error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
))
|
)
|
||||||
.elements(
|
.elements(
|
||||||
error
|
error
|
||||||
.help
|
.help
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|help| annotate_snippets::Level::HELP.message(help)),
|
.map(|help| annotate_snippets::Level::HELP.message(help)),
|
||||||
)
|
),
|
||||||
})
|
];
|
||||||
.collect::<Vec<_>>();
|
if let Some((range, help)) = &error.replacement {
|
||||||
|
snippets.push(
|
||||||
|
annotate_snippets::Level::NOTE
|
||||||
|
.secondary_title("Try adding it")
|
||||||
|
.element(
|
||||||
|
Snippet::source(self.input.as_str())
|
||||||
|
.patch(Patch::new(range.range.clone(), help.clone())),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let renderer = annotate_snippets::Renderer::styled()
|
writeln!(f, "{}", renderer.render(&snippets))?;
|
||||||
.decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
|
writeln!(f)?;
|
||||||
renderer.render(&reports)
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -297,14 +297,15 @@ mod tests {
|
||||||
use crate::eval::execute;
|
use crate::eval::execute;
|
||||||
use crate::functions::FunctionMap;
|
use crate::functions::FunctionMap;
|
||||||
use crate::functions::NomoFunctionError;
|
use crate::functions::NomoFunctionError;
|
||||||
|
use crate::input::NomoInput;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_simple_variable_interpolation() {
|
fn check_simple_variable_interpolation() {
|
||||||
let input = "Hello {{= world }}";
|
let input = NomoInput::from("Hello {{= world }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = crate::parser::parse(parsed.tokens()).unwrap();
|
let ast = crate::parser::parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
let emit = crate::compiler::emit_machine(ast);
|
let emit = crate::compiler::emit_machine(ast);
|
||||||
|
|
||||||
|
|
@ -321,11 +322,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_method_call() {
|
fn check_method_call() {
|
||||||
let input = "Hello {{= foo(world) }}";
|
let input = NomoInput::from("Hello {{= foo(world) }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = crate::parser::parse(parsed.tokens()).unwrap();
|
let ast = crate::parser::parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
let emit = crate::compiler::emit_machine(ast);
|
let emit = crate::compiler::emit_machine(ast);
|
||||||
|
|
||||||
|
|
@ -348,11 +349,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_conditional_access() {
|
fn check_conditional_access() {
|
||||||
let input = "Hello {{= unknown? }}";
|
let input = NomoInput::from("Hello {{= unknown? }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = crate::parser::parse(parsed.tokens()).unwrap();
|
let ast = crate::parser::parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
let emit = crate::compiler::emit_machine(ast);
|
let emit = crate::compiler::emit_machine(ast);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,8 @@ unstable_pub!(
|
||||||
mod parser
|
mod parser
|
||||||
);
|
);
|
||||||
|
|
||||||
|
mod winnow_ext;
|
||||||
|
|
||||||
/// Errors in this library
|
/// Errors in this library
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
||||||
|
|
@ -203,7 +205,7 @@ impl Nomo {
|
||||||
) -> Result<(), NomoError> {
|
) -> Result<(), NomoError> {
|
||||||
let source = value.into();
|
let source = value.into();
|
||||||
let parse = lexer::parse(source.clone())?;
|
let parse = lexer::parse(source.clone())?;
|
||||||
let ast = parser::parse(parse.tokens())?;
|
let ast = parser::parse(source.clone(), parse.tokens())?;
|
||||||
|
|
||||||
let instructions = compiler::emit_machine(ast);
|
let instructions = compiler::emit_machine(ast);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use winnow::error::AddContext;
|
||||||
use winnow::error::FromRecoverableError;
|
use winnow::error::FromRecoverableError;
|
||||||
use winnow::error::ModalError;
|
use winnow::error::ModalError;
|
||||||
use winnow::error::ParserError;
|
use winnow::error::ParserError;
|
||||||
|
use winnow::stream::Location;
|
||||||
use winnow::stream::Offset;
|
use winnow::stream::Offset;
|
||||||
use winnow::stream::Recoverable;
|
use winnow::stream::Recoverable;
|
||||||
use winnow::stream::Stream;
|
use winnow::stream::Stream;
|
||||||
|
|
@ -28,6 +29,7 @@ use winnow::token::any;
|
||||||
|
|
||||||
use crate::SourceSpan;
|
use crate::SourceSpan;
|
||||||
use crate::errors::AstFailure;
|
use crate::errors::AstFailure;
|
||||||
|
use crate::input::NomoInput;
|
||||||
use crate::lexer::TemplateToken;
|
use crate::lexer::TemplateToken;
|
||||||
use crate::lexer::TokenKind;
|
use crate::lexer::TokenKind;
|
||||||
use crate::lexer::TokenOperator;
|
use crate::lexer::TokenOperator;
|
||||||
|
|
@ -50,6 +52,7 @@ pub struct AstError {
|
||||||
pub(crate) message: Option<String>,
|
pub(crate) message: Option<String>,
|
||||||
pub(crate) help: Option<String>,
|
pub(crate) help: Option<String>,
|
||||||
pub(crate) span: Option<crate::SourceSpan>,
|
pub(crate) span: Option<crate::SourceSpan>,
|
||||||
|
pub(crate) replacement: Option<(SourceSpan, String)>,
|
||||||
|
|
||||||
is_fatal: bool,
|
is_fatal: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -60,6 +63,7 @@ impl AstError {
|
||||||
message: None,
|
message: None,
|
||||||
help: None,
|
help: None,
|
||||||
span: None,
|
span: None,
|
||||||
|
replacement: None,
|
||||||
|
|
||||||
is_fatal: false,
|
is_fatal: false,
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +78,11 @@ impl AstError {
|
||||||
self.help = Some(help.to_string());
|
self.help = Some(help.to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn replacement(mut self, span: SourceSpan, replacement: &str) -> Self {
|
||||||
|
self.replacement = Some((span, replacement.to_string()));
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModalError for AstError {
|
impl ModalError for AstError {
|
||||||
|
|
@ -132,6 +141,7 @@ impl AddContext<Input<'_>, AstError> for AstError {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.message = context.message.or(self.message);
|
self.message = context.message.or(self.message);
|
||||||
self.help = context.help.or(self.help);
|
self.help = context.help.or(self.help);
|
||||||
|
self.replacement = context.replacement.or(self.replacement);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +172,7 @@ impl<'input> Parser<Input<'input>, TemplateToken, AstError> for TokenKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(input: &[TemplateToken]) -> Result<TemplateAst<'_>, AstFailure> {
|
pub fn parse(source: NomoInput, input: &[TemplateToken]) -> Result<TemplateAst<'_>, AstFailure> {
|
||||||
let (_remaining, val, errors) = parse_asts.recoverable_parse(TokenSlice::new(input));
|
let (_remaining, val, errors) = parse_asts.recoverable_parse(TokenSlice::new(input));
|
||||||
|
|
||||||
if errors.is_empty()
|
if errors.is_empty()
|
||||||
|
|
@ -170,7 +180,7 @@ pub fn parse(input: &[TemplateToken]) -> Result<TemplateAst<'_>, AstFailure> {
|
||||||
{
|
{
|
||||||
Ok(TemplateAst { root: val })
|
Ok(TemplateAst { root: val })
|
||||||
} else {
|
} else {
|
||||||
Err(AstFailure::from_errors(errors))
|
Err(AstFailure::from_errors(errors, source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,26 +319,42 @@ fn parse_action<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'in
|
||||||
alt((
|
alt((
|
||||||
parse_conditional_chain,
|
parse_conditional_chain,
|
||||||
parse_for_chain,
|
parse_for_chain,
|
||||||
(parse_block(
|
parse_block(parse_unknown_action)
|
||||||
cut_err(not(repeat_till(
|
|
||||||
0..,
|
|
||||||
parse_expression,
|
|
||||||
peek((ws, TokenKind::RightDelim)),
|
|
||||||
)
|
|
||||||
.map(|((), _)| ())))
|
|
||||||
.context(
|
|
||||||
AstError::ctx()
|
|
||||||
.msg("Standlone action block")
|
|
||||||
.help("If you want to output this expression, add a '=' to the block"),
|
|
||||||
)
|
|
||||||
.take()
|
.take()
|
||||||
.map(TemplateAstExpr::Invalid),
|
.map(TemplateAstExpr::Invalid),
|
||||||
)),
|
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_unknown_action<'input>(
|
||||||
|
input: &mut Input<'input>,
|
||||||
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
|
let expression = peek(parse_expression).parse_next(input);
|
||||||
|
|
||||||
|
let is_expr = match expression {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(err) if err.is_backtrack() => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_expr {
|
||||||
|
return Err(AstError::ctx()
|
||||||
|
.msg("Standlone action block")
|
||||||
|
.help("If you want to output this expression, add a '=' to the block")
|
||||||
|
.cut());
|
||||||
|
}
|
||||||
|
|
||||||
|
let keywords = peek(parse_keyword).parse_next(input);
|
||||||
|
|
||||||
|
let is_keyword = keywords.is_ok();
|
||||||
|
|
||||||
|
if is_keyword {
|
||||||
|
return Err(AstError::ctx().msg("Unexpected keyword").cut());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(AstError::ctx().cut())
|
||||||
|
}
|
||||||
fn parse_for_chain<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
fn parse_for_chain<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
trace("for_loop", |input: &mut Input<'input>| {
|
trace("for_loop", |input: &mut Input<'input>| {
|
||||||
let for_block = parse_for_loop.map(Box::new).parse_next(input)?;
|
let for_block = parse_for_loop.map(Box::new).parse_next(input)?;
|
||||||
|
|
@ -368,26 +394,34 @@ fn parse_for_chain<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<
|
||||||
fn parse_for_loop<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
fn parse_for_loop<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
trace(
|
trace(
|
||||||
"for_loop_inner",
|
"for_loop_inner",
|
||||||
parse_block(
|
parse_block(|input: &mut Input<'input>| {
|
||||||
(
|
let _ignored = surrounded(ws, TokenKind::For).parse_next(input)?;
|
||||||
ws,
|
|
||||||
TokenKind::For,
|
let value_ident =
|
||||||
ws,
|
cut_err(TokenKind::Ident.context(AstError::ctx().msg("Missing ident here")))
|
||||||
cut_err(TokenKind::Ident.context(AstError::ctx().msg("Expected identifier here"))),
|
.parse_next(input)?;
|
||||||
ws,
|
|
||||||
cut_err(
|
let _ignored = cut_err(
|
||||||
TokenKind::In.context(AstError::ctx().msg("Missing `in` in `for _ in <expr>`")),
|
preceded(ws, TokenKind::In).context(
|
||||||
|
AstError::ctx()
|
||||||
|
.msg("Missing `in` in `for .. in ` loop")
|
||||||
|
.replacement(
|
||||||
|
SourceSpan {
|
||||||
|
range: input.current_token_start()..(input.current_token_start()),
|
||||||
|
},
|
||||||
|
" in",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
ws,
|
|
||||||
parse_expression.map(Box::new),
|
|
||||||
)
|
)
|
||||||
.map(|(_, _for, _, value_ident, _, _in, _, value_expression)| {
|
.parse_next(input)?;
|
||||||
TemplateAstExpr::For {
|
|
||||||
value_ident,
|
let value_expression = parse_expression.map(Box::new).parse_next(input)?;
|
||||||
value_expression,
|
|
||||||
}
|
Ok(TemplateAstExpr::For {
|
||||||
}),
|
value_ident,
|
||||||
),
|
value_expression,
|
||||||
|
})
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
@ -614,22 +648,25 @@ fn parse_operand<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'i
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_keywords_fail<'input>(
|
fn parse_keyword<'input>(input: &mut Input<'input>) -> Result<TemplateToken, AstError> {
|
||||||
input: &mut Input<'input>,
|
alt((
|
||||||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
|
||||||
let value = alt((
|
|
||||||
TokenKind::ConditionalIf,
|
TokenKind::ConditionalIf,
|
||||||
|
TokenKind::ConditionalElse,
|
||||||
TokenKind::For,
|
TokenKind::For,
|
||||||
TokenKind::End,
|
TokenKind::End,
|
||||||
TokenKind::In,
|
TokenKind::In,
|
||||||
))
|
))
|
||||||
.take()
|
.parse_next(input)
|
||||||
.map(TemplateAstExpr::Invalid)
|
}
|
||||||
.parse_next(input)?;
|
|
||||||
|
|
||||||
cut_err(fail::<_, (), _>.context(AstError::ctx().msg("Found literal, expected expression")))
|
fn parse_keywords_fail<'input>(
|
||||||
.value(value)
|
input: &mut Input<'input>,
|
||||||
.parse_next(input)
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
|
let _value = parse_keyword.parse_next(input)?;
|
||||||
|
|
||||||
|
Err(AstError::ctx()
|
||||||
|
.msg("Found literal, expected expression")
|
||||||
|
.cut())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_literal<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
fn parse_literal<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
|
|
@ -735,6 +772,7 @@ mod tests {
|
||||||
use winnow::combinator::fail;
|
use winnow::combinator::fail;
|
||||||
use winnow::stream::TokenSlice;
|
use winnow::stream::TokenSlice;
|
||||||
|
|
||||||
|
use crate::input::NomoInput;
|
||||||
use crate::lexer::TokenKind;
|
use crate::lexer::TokenKind;
|
||||||
use crate::parser::AstError;
|
use crate::parser::AstError;
|
||||||
use crate::parser::AstFailure;
|
use crate::parser::AstFailure;
|
||||||
|
|
@ -744,25 +782,22 @@ mod tests {
|
||||||
use crate::parser::parse_block;
|
use crate::parser::parse_block;
|
||||||
use crate::parser::parse_end;
|
use crate::parser::parse_end;
|
||||||
|
|
||||||
fn panic_pretty<'a>(
|
fn panic_pretty<'a>(tokens: Result<TemplateAst<'a>, AstFailure>) -> TemplateAst<'a> {
|
||||||
input: &'_ str,
|
|
||||||
tokens: Result<TemplateAst<'a>, AstFailure>,
|
|
||||||
) -> TemplateAst<'a> {
|
|
||||||
match tokens {
|
match tokens {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(failure) => {
|
Err(failure) => {
|
||||||
panic!("{}", failure.to_report(input));
|
panic!("{}", failure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_only_content() {
|
fn check_only_content() {
|
||||||
let input = "Hello World";
|
let input = NomoInput::from("Hello World");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = parse(parsed.tokens()).unwrap();
|
let ast = parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast, @r#"
|
insta::assert_debug_snapshot!(ast, @r#"
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
|
|
@ -777,11 +812,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_simple_variable_interpolation() {
|
fn check_simple_variable_interpolation() {
|
||||||
let input = "Hello {{= world }}";
|
let input = NomoInput::from("Hello {{= world }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = parse(parsed.tokens()).unwrap();
|
let ast = parse(input, parsed.tokens()).unwrap();
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast, @r#"
|
insta::assert_debug_snapshot!(ast, @r#"
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
|
|
@ -805,11 +840,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_simple_if() {
|
fn check_simple_if() {
|
||||||
let input = "{{ if foo }} Hiii {{ end }}";
|
let input = NomoInput::from("{{ if foo }} Hiii {{ end }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast, @r#"
|
insta::assert_debug_snapshot!(ast, @r#"
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
|
|
@ -850,35 +885,39 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_invalid_action() {
|
fn check_invalid_action() {
|
||||||
let input = r#"{{ value }}
|
let input = NomoInput::from(
|
||||||
|
r#"{{ value }}
|
||||||
{{ value }}
|
{{ value }}
|
||||||
{{ value }}
|
{{ value }}
|
||||||
{{ value }}
|
{{ value }}
|
||||||
{{ value }}"#;
|
{{ value }}"#,
|
||||||
|
);
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = parse(parsed.tokens()).unwrap_err();
|
let ast = parse(input, parsed.tokens()).unwrap_err();
|
||||||
|
|
||||||
insta::assert_snapshot!(ast.to_report(input));
|
insta::assert_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_nested_simple_if() {
|
fn check_nested_simple_if() {
|
||||||
let input = r#"{{ if foo }}
|
let input = NomoInput::from(
|
||||||
|
r#"{{ if foo }}
|
||||||
{{ if bar }}
|
{{ if bar }}
|
||||||
Hiii
|
Hiii
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{= value }}
|
{{= value }}
|
||||||
"#;
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
insta::assert_debug_snapshot!("simple_if_tokens", parsed);
|
insta::assert_debug_snapshot!("simple_if_tokens", parsed);
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!("simple_if_ast", ast);
|
insta::assert_debug_snapshot!("simple_if_ast", ast);
|
||||||
}
|
}
|
||||||
|
|
@ -887,9 +926,9 @@ mod tests {
|
||||||
fn check_parsing_block() {
|
fn check_parsing_block() {
|
||||||
use winnow::RecoverableParser;
|
use winnow::RecoverableParser;
|
||||||
|
|
||||||
let input = "{{ foo }}";
|
let input = NomoInput::from("{{ foo }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input).unwrap();
|
||||||
|
|
||||||
let result = alt((
|
let result = alt((
|
||||||
parse_end,
|
parse_end,
|
||||||
|
|
@ -924,6 +963,7 @@ mod tests {
|
||||||
range: 2..6,
|
range: 2..6,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
replacement: None,
|
||||||
is_fatal: false,
|
is_fatal: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -933,11 +973,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_empty_if_output() {
|
fn check_empty_if_output() {
|
||||||
let input = "{{ if foo }}{{ end }}";
|
let input = NomoInput::from("{{ if foo }}{{ end }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast, @r#"
|
insta::assert_debug_snapshot!(ast, @r#"
|
||||||
TemplateAst {
|
TemplateAst {
|
||||||
|
|
@ -970,99 +1010,101 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_if_else() {
|
fn check_if_else() {
|
||||||
let input = "{{ if foo }} foo {{ else }} bar {{ end }}";
|
let input = NomoInput::from("{{ if foo }} foo {{ else }} bar {{ end }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_if_else_if() {
|
fn check_if_else_if() {
|
||||||
let input = "{{ if foo }} foo {{ else if bar }} bar {{ end }}";
|
let input = NomoInput::from("{{ if foo }} foo {{ else if bar }} bar {{ end }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_trim_whitespace() {
|
fn check_trim_whitespace() {
|
||||||
let input = "{{ if foo -}} foo {{- else if bar -}} bar {{- end }}";
|
let input = NomoInput::from("{{ if foo -}} foo {{- else if bar -}} bar {{- end }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_for_loop() {
|
fn check_for_loop() {
|
||||||
let input = "{{ for value in array }} Hi: {{= value }} {{ else }} No Content :C {{ end }}";
|
let input = NomoInput::from(
|
||||||
|
"{{ for value in array }} Hi: {{= value }} {{ else }} No Content :C {{ end }}",
|
||||||
|
);
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_math_expression() {
|
fn check_math_expression() {
|
||||||
let input = "{{= 5 * 3 + 2 / 3 }}";
|
let input = NomoInput::from("{{= 5 * 3 + 2 / 3 }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_logical_expression() {
|
fn check_logical_expression() {
|
||||||
let input = "{{= true && false || 3 >= 2 && 5 == 2 }}";
|
let input = NomoInput::from("{{= true && false || 3 >= 2 && 5 == 2 }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_function_call() {
|
fn check_function_call() {
|
||||||
let input = "{{= foo(2 * 3, bar(2 + baz)) }}";
|
let input = NomoInput::from("{{= foo(2 * 3, bar(2 + baz)) }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_conditional_access() {
|
fn check_conditional_access() {
|
||||||
let input = "{{= foo? }}";
|
let input = NomoInput::from("{{= foo? }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_access_operator() {
|
fn check_access_operator() {
|
||||||
let input = "{{= foo?.bar }}";
|
let input = NomoInput::from("{{= foo?.bar }}");
|
||||||
|
|
||||||
let parsed = crate::lexer::parse(input.into()).unwrap();
|
let parsed = crate::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let ast = panic_pretty(input, parse(parsed.tokens()));
|
let ast = panic_pretty(parse(input, parsed.tokens()));
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: src/parser/mod.rs
|
source: src/parser/mod.rs
|
||||||
expression: ast.to_report(input)
|
expression: ast
|
||||||
---
|
---
|
||||||
[1m[91merror[0m[1m: Standlone action block[0m
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
|
|
@ -8,23 +8,31 @@ expression: ast.to_report(input)
|
||||||
[1m[94m│[0m [1m[91m━━━━━[0m
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
[1m[94m│[0m
|
[1m[94m│[0m
|
||||||
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
|
||||||
[1m[91merror[0m[1m: Standlone action block[0m
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m2[0m [1m[94m│[0m {{ value }}
|
[1m[94m2[0m [1m[94m│[0m {{ value }}
|
||||||
[1m[94m│[0m [1m[91m━━━━━[0m
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m│[0m
|
||||||
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
|
||||||
[1m[91merror[0m[1m: Standlone action block[0m
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m3[0m [1m[94m│[0m {{ value }}
|
[1m[94m3[0m [1m[94m│[0m {{ value }}
|
||||||
[1m[94m│[0m [1m[91m━━━━━[0m
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m│[0m
|
||||||
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
|
||||||
[1m[91merror[0m[1m: Standlone action block[0m
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m4[0m [1m[94m│[0m {{ value }}
|
[1m[94m4[0m [1m[94m│[0m {{ value }}
|
||||||
[1m[94m│[0m [1m[91m━━━━━[0m
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m│[0m
|
||||||
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
|
||||||
[1m[91merror[0m[1m: Standlone action block[0m
|
[1m[91merror[0m[1m: Standlone action block[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m5[0m [1m[94m│[0m {{ value }}
|
[1m[94m5[0m [1m[94m│[0m {{ value }}
|
||||||
[1m[94m│[0m [1m[91m━━━━━[0m
|
[1m[94m│[0m [1m[91m━━━━━[0m
|
||||||
|
[1m[94m│[0m
|
||||||
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
[1m[94m╰ [0m[1mhelp[0m: If you want to output this expression, add a '=' to the block
|
||||||
|
|
|
||||||
51
src/winnow_ext.rs
Normal file
51
src/winnow_ext.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use winnow::Parser;
|
||||||
|
use winnow::error::AddContext;
|
||||||
|
use winnow::error::ParserError;
|
||||||
|
use winnow::stream::Stream;
|
||||||
|
|
||||||
|
pub trait ParserExt<I, O, E>: Parser<I, O, E> {
|
||||||
|
fn with_context<F, C>(self, context: F) -> WithContext<Self, I, O, E, F, C>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
I: Stream,
|
||||||
|
E: AddContext<I, C> + ParserError<I>,
|
||||||
|
F: Fn(&<I as Stream>::Slice, &E) -> C,
|
||||||
|
{
|
||||||
|
WithContext {
|
||||||
|
parser: self,
|
||||||
|
context,
|
||||||
|
_pd: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WithContext<P, I, O, E, F, C> {
|
||||||
|
parser: P,
|
||||||
|
context: F,
|
||||||
|
_pd: PhantomData<(I, O, E, C)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, I, O, E, F, C> Parser<I, O, E> for WithContext<P, I, O, E, F, C>
|
||||||
|
where
|
||||||
|
P: Parser<I, O, E>,
|
||||||
|
I: Stream,
|
||||||
|
E: AddContext<I, C> + ParserError<I>,
|
||||||
|
F: Fn(&<I as Stream>::Slice, &E) -> C,
|
||||||
|
{
|
||||||
|
fn parse_next(&mut self, input: &mut I) -> winnow::Result<O, E> {
|
||||||
|
let start = input.checkpoint();
|
||||||
|
|
||||||
|
let res = self.parser.parse_next(input);
|
||||||
|
|
||||||
|
res.map_err(|err| {
|
||||||
|
let offset = input.offset_from(&start);
|
||||||
|
input.reset(&start);
|
||||||
|
let taken = input.next_slice(offset);
|
||||||
|
|
||||||
|
let context = (self.context)(&taken, &err);
|
||||||
|
err.add_context(input, &start, context)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,11 +7,13 @@ fn check_files() {
|
||||||
for file in files {
|
for file in files {
|
||||||
let input = std::fs::read_to_string(file.unwrap().path()).unwrap();
|
let input = std::fs::read_to_string(file.unwrap().path()).unwrap();
|
||||||
|
|
||||||
let Ok(parsed) = nomo::lexer::parse(input.into()) else {
|
let input = nomo::input::NomoInput::from(input);
|
||||||
|
|
||||||
|
let Ok(parsed) = nomo::lexer::parse(input.clone()) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(ast) = nomo::parser::parse(parsed.tokens()) else {
|
let Ok(ast) = nomo::parser::parse(input, parsed.tokens()) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
source: tests/file_tests.rs
|
source: tests/file_tests.rs
|
||||||
expression: parsed
|
expression: parsed
|
||||||
info:
|
info:
|
||||||
input: "{{ if }} {{ end }}\n{{ if if }} {{ end }}\n{{ if if }} {{ for foo in bar }} {{ end }} {{ end }}\n{{ for in bar }} {{ end }}\n{{ for blah bar }} {{ end }}\n{{ else }}"
|
input: "{{ if }} {{ end }}\n{{ if if }} {{ end }}\n{{ if if }} {{ for foo in bar }} {{ end }} {{ end }}\n{{ for in bar }} {{ end }}\n{{ for blah bar }} {{ end }}\n{{ for blah}} {{ end }}\n{{ else }}"
|
||||||
context: {}
|
context: {}
|
||||||
---
|
---
|
||||||
ParsedTemplate {
|
ParsedTemplate {
|
||||||
|
|
@ -99,8 +99,21 @@ ParsedTemplate {
|
||||||
[Whitespace]"\n" (149..150),
|
[Whitespace]"\n" (149..150),
|
||||||
[LeftDelim]"{{" (150..152),
|
[LeftDelim]"{{" (150..152),
|
||||||
[Whitespace]" " (152..153),
|
[Whitespace]" " (152..153),
|
||||||
[ConditionalElse]"else" (153..157),
|
[For]"for" (153..156),
|
||||||
[Whitespace]" " (157..158),
|
[Whitespace]" " (156..157),
|
||||||
[RightDelim]"}}" (158..160),
|
[Ident]"blah" (157..161),
|
||||||
|
[RightDelim]"}}" (161..163),
|
||||||
|
[Whitespace]" " (163..164),
|
||||||
|
[LeftDelim]"{{" (164..166),
|
||||||
|
[Whitespace]" " (166..167),
|
||||||
|
[End]"end" (167..170),
|
||||||
|
[Whitespace]" " (170..171),
|
||||||
|
[RightDelim]"}}" (171..173),
|
||||||
|
[Whitespace]"\n" (173..174),
|
||||||
|
[LeftDelim]"{{" (174..176),
|
||||||
|
[Whitespace]" " (176..177),
|
||||||
|
[ConditionalElse]"else" (177..181),
|
||||||
|
[Whitespace]" " (181..182),
|
||||||
|
[RightDelim]"}}" (182..184),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,31 +2,50 @@
|
||||||
source: tests/file_tests.rs
|
source: tests/file_tests.rs
|
||||||
expression: ast
|
expression: ast
|
||||||
info:
|
info:
|
||||||
input: "{{ if }} {{ end }}\n{{ if if }} {{ end }}\n{{ if if }} {{ for foo in bar }} {{ end }} {{ end }}\n{{ for in bar }} {{ end }}\n{{ for blah bar }} {{ end }}\n{{ else }}"
|
input: "{{ if }} {{ end }}\n{{ if if }} {{ end }}\n{{ if if }} {{ for foo in bar }} {{ end }} {{ end }}\n{{ for in bar }} {{ end }}\n{{ for blah bar }} {{ end }}\n{{ for blah}} {{ end }}\n{{ else }}"
|
||||||
context: {}
|
context: {}
|
||||||
---
|
---
|
||||||
[1m[91merror[0m[1m: Expected an expression after 'if'[0m
|
[1m[91merror[0m[1m: Expected an expression after 'if'[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m1[0m [1m[94m│[0m {{ if }} {{ end }}
|
[1m[94m1[0m [1m[94m│[0m {{ if }} {{ end }}
|
||||||
[1m[94m│[0m [1m[91m━━[0m
|
[1m[94m╰╴[0m [1m[91m━━[0m
|
||||||
[1m[94m╰╴[0m
|
|
||||||
[1m[91merror[0m[1m: Expected an expression after 'if'[0m
|
[1m[91merror[0m[1m: Expected an expression after 'if'[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m2[0m [1m[94m│[0m {{ if if }} {{ end }}
|
[1m[94m2[0m [1m[94m│[0m {{ if if }} {{ end }}
|
||||||
[1m[94m╰╴[0m [1m[91m━━[0m
|
[1m[94m╰╴[0m [1m[91m━━[0m
|
||||||
|
|
||||||
[1m[91merror[0m[1m: Expected an expression after 'if'[0m
|
[1m[91merror[0m[1m: Expected an expression after 'if'[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m3[0m [1m[94m│[0m {{ if if }} {{ for foo in bar }} {{ end }} {{ end }}
|
[1m[94m3[0m [1m[94m│[0m {{ if if }} {{ for foo in bar }} {{ end }} {{ end }}
|
||||||
[1m[94m╰╴[0m [1m[91m━━[0m
|
[1m[94m╰╴[0m [1m[91m━━[0m
|
||||||
[1m[91merror[0m[1m: Expected identifier here[0m
|
|
||||||
|
[1m[91merror[0m[1m: Missing ident here[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m4[0m [1m[94m│[0m {{ for in bar }} {{ end }}
|
[1m[94m4[0m [1m[94m│[0m {{ for in bar }} {{ end }}
|
||||||
[1m[94m╰╴[0m [1m[91m━━━━━━[0m
|
[1m[94m╰╴[0m [1m[91m━━━━━━[0m
|
||||||
[1m[91merror[0m[1m: Missing `in` in `for _ in <expr>`[0m
|
|
||||||
|
[1m[91merror[0m[1m: Missing `in` in `for .. in ` loop[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m5[0m [1m[94m│[0m {{ for blah bar }} {{ end }}
|
[1m[94m5[0m [1m[94m│[0m {{ for blah bar }} {{ end }}
|
||||||
[1m[94m╰╴[0m [1m[91m━━━━━━━━[0m
|
[1m[94m│[0m [1m[91m━━━━━━━━[0m
|
||||||
[1m[91merror[0m[1m: An error occurred while producing an Ast[0m
|
[1m[94m╰╴[0m
|
||||||
|
[1m[92mnote[0m: Try adding it
|
||||||
|
[1m[94m╭╴[0m
|
||||||
|
[1m[94m5[0m [1m[94m│ [0m{{ for blah[92m in[0m bar }} {{ end }}
|
||||||
|
[1m[94m╰╴[0m [92m++[0m
|
||||||
|
|
||||||
|
[1m[91merror[0m[1m: Missing `in` in `for .. in ` loop[0m
|
||||||
[1m[94m ╭▸ [0m
|
[1m[94m ╭▸ [0m
|
||||||
[1m[94m6[0m [1m[94m│[0m {{ else }}
|
[1m[94m6[0m [1m[94m│[0m {{ for blah}} {{ end }}
|
||||||
[1m[94m╰╴[0m [1m[91m━━━━━━━[0m
|
[1m[94m│[0m [1m[91m━━━━[0m
|
||||||
|
[1m[94m╰╴[0m
|
||||||
|
[1m[92mnote[0m: Try adding it
|
||||||
|
[1m[94m╭╴[0m
|
||||||
|
[1m[94m6[0m [1m[94m│ [0m{{ for blah[92m in[0m}} {{ end }}
|
||||||
|
[1m[94m╰╴[0m [92m++[0m
|
||||||
|
|
||||||
|
[1m[91merror[0m[1m: Unexpected keyword[0m
|
||||||
|
[1m[94m ╭▸ [0m
|
||||||
|
[1m[94m7[0m [1m[94m│[0m {{ else }}
|
||||||
|
[1m[94m╰╴[0m [1m[91m━━━━[0m
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,5 @@
|
||||||
{{ if if }} {{ for foo in bar }} {{ end }} {{ end }}
|
{{ if if }} {{ for foo in bar }} {{ end }} {{ end }}
|
||||||
{{ for in bar }} {{ end }}
|
{{ for in bar }} {{ end }}
|
||||||
{{ for blah bar }} {{ end }}
|
{{ for blah bar }} {{ end }}
|
||||||
|
{{ for blah}} {{ end }}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
|
@ -5,6 +5,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use nomo::Context;
|
use nomo::Context;
|
||||||
use nomo::functions::FunctionMap;
|
use nomo::functions::FunctionMap;
|
||||||
|
use nomo::input::NomoInput;
|
||||||
|
|
||||||
test_each_file::test_each_path! { for ["nomo"] in "./tests/cases/" as cases => check_for_input }
|
test_each_file::test_each_path! { for ["nomo"] in "./tests/cases/" as cases => check_for_input }
|
||||||
|
|
||||||
|
|
@ -43,16 +44,18 @@ fn check_for_input([path]: [&Path; 1]) {
|
||||||
context.try_insert(k, v).unwrap();
|
context.try_insert(k, v).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed = nomo::lexer::parse(input.into()).unwrap();
|
let input = NomoInput::from(input);
|
||||||
|
|
||||||
|
let parsed = nomo::lexer::parse(input.clone()).unwrap();
|
||||||
|
|
||||||
let _guard = settings.bind_to_scope();
|
let _guard = settings.bind_to_scope();
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(format!("{basename}.1-parsed"), parsed);
|
insta::assert_debug_snapshot!(format!("{basename}.1-parsed"), parsed);
|
||||||
|
|
||||||
let ast = match nomo::parser::parse(parsed.tokens()) {
|
let ast = match nomo::parser::parse(input, parsed.tokens()) {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{}", err.to_report(input));
|
eprintln!("{}", err);
|
||||||
panic!("Could not evaluate ast");
|
panic!("Could not evaluate ast");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -97,7 +100,9 @@ fn check_errors([path]: [&Path; 1]) {
|
||||||
|
|
||||||
let _guard = settings.bind_to_scope();
|
let _guard = settings.bind_to_scope();
|
||||||
|
|
||||||
let parsed = nomo::lexer::parse(input.into()).map_err(|err| err.to_report());
|
let input = NomoInput::from(input);
|
||||||
|
|
||||||
|
let parsed = nomo::lexer::parse(input.clone()).map_err(|err| err.to_report());
|
||||||
|
|
||||||
match &parsed {
|
match &parsed {
|
||||||
Ok(parsed) => insta::assert_debug_snapshot!(format!("{basename}.1-parsed"), parsed),
|
Ok(parsed) => insta::assert_debug_snapshot!(format!("{basename}.1-parsed"), parsed),
|
||||||
|
|
@ -108,7 +113,7 @@ fn check_errors([path]: [&Path; 1]) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ast = nomo::parser::parse(parsed.tokens()).map_err(|err| err.to_report(input));
|
let ast = nomo::parser::parse(input, parsed.tokens()).map_err(|err| err.to_string());
|
||||||
|
|
||||||
match &ast {
|
match &ast {
|
||||||
Ok(ast) => insta::assert_debug_snapshot!(format!("{basename}.2-ast"), ast),
|
Ok(ast) => insta::assert_debug_snapshot!(format!("{basename}.2-ast"), ast),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue