Start fixing error outputs

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-16 11:22:29 +01:00
parent d6ac7af36b
commit 7f7bf5c98d
16 changed files with 319 additions and 21 deletions

View file

@ -1,3 +1,5 @@
#![allow(missing_docs)]
#[test]
fn check_files() {
let files = std::fs::read_dir("tests/checks/").unwrap();

View file

@ -0,0 +1,106 @@
---
source: tests/file_tests.rs
expression: parsed
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 }}"
context: {}
---
ParsedTemplate {
tokens: [
[LeftDelim]"{{" (0..2),
[Whitespace]" " (2..3),
[ConditionalIf]"if" (3..5),
[Whitespace]" " (5..6),
[RightDelim]"}}" (6..8),
[Whitespace]" " (8..9),
[LeftDelim]"{{" (9..11),
[Whitespace]" " (11..12),
[End]"end" (12..15),
[Whitespace]" " (15..16),
[RightDelim]"}}" (16..18),
[Whitespace]"\n" (18..19),
[LeftDelim]"{{" (19..21),
[Whitespace]" " (21..22),
[ConditionalIf]"if" (22..24),
[Whitespace]" " (24..25),
[ConditionalIf]"if" (25..27),
[Whitespace]" " (27..28),
[RightDelim]"}}" (28..30),
[Whitespace]" " (30..31),
[LeftDelim]"{{" (31..33),
[Whitespace]" " (33..34),
[End]"end" (34..37),
[Whitespace]" " (37..38),
[RightDelim]"}}" (38..40),
[Whitespace]"\n" (40..41),
[LeftDelim]"{{" (41..43),
[Whitespace]" " (43..44),
[ConditionalIf]"if" (44..46),
[Whitespace]" " (46..47),
[ConditionalIf]"if" (47..49),
[Whitespace]" " (49..50),
[RightDelim]"}}" (50..52),
[Whitespace]" " (52..53),
[LeftDelim]"{{" (53..55),
[Whitespace]" " (55..56),
[For]"for" (56..59),
[Whitespace]" " (59..60),
[Ident]"foo" (60..63),
[Whitespace]" " (63..64),
[In]"in" (64..66),
[Whitespace]" " (66..67),
[Ident]"bar" (67..70),
[Whitespace]" " (70..71),
[RightDelim]"}}" (71..73),
[Whitespace]" " (73..74),
[LeftDelim]"{{" (74..76),
[Whitespace]" " (76..77),
[End]"end" (77..80),
[Whitespace]" " (80..81),
[RightDelim]"}}" (81..83),
[Whitespace]" " (83..84),
[LeftDelim]"{{" (84..86),
[Whitespace]" " (86..87),
[End]"end" (87..90),
[Whitespace]" " (90..91),
[RightDelim]"}}" (91..93),
[Whitespace]"\n" (93..94),
[LeftDelim]"{{" (94..96),
[Whitespace]" " (96..97),
[For]"for" (97..100),
[Whitespace]" " (100..101),
[In]"in" (101..103),
[Whitespace]" " (103..104),
[Ident]"bar" (104..107),
[Whitespace]" " (107..108),
[RightDelim]"}}" (108..110),
[Whitespace]" " (110..111),
[LeftDelim]"{{" (111..113),
[Whitespace]" " (113..114),
[End]"end" (114..117),
[Whitespace]" " (117..118),
[RightDelim]"}}" (118..120),
[Whitespace]"\n" (120..121),
[LeftDelim]"{{" (121..123),
[Whitespace]" " (123..124),
[For]"for" (124..127),
[Whitespace]" " (127..128),
[Ident]"blah" (128..132),
[Whitespace]" " (132..133),
[Ident]"bar" (133..136),
[Whitespace]" " (136..137),
[RightDelim]"}}" (137..139),
[Whitespace]" " (139..140),
[LeftDelim]"{{" (140..142),
[Whitespace]" " (142..143),
[End]"end" (143..146),
[Whitespace]" " (146..147),
[RightDelim]"}}" (147..149),
[Whitespace]"\n" (149..150),
[LeftDelim]"{{" (150..152),
[Whitespace]" " (152..153),
[ConditionalElse]"else" (153..157),
[Whitespace]" " (157..158),
[RightDelim]"}}" (158..160),
],
}

View file

@ -0,0 +1,32 @@
---
source: tests/file_tests.rs
expression: ast
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 }}"
context: {}
---
error: Expected an expression after 'if'
 ╭▸ 
1 │ {{ if }} {{ end }}
│ ━━
╰╴
error: Expected an expression after 'if'
 ╭▸ 
2 │ {{ if if }} {{ end }}
╰╴ ━━
error: Expected an expression after 'if'
 ╭▸ 
3 │ {{ if if }} {{ for foo in bar }} {{ end }} {{ end }}
╰╴ ━━
error: Expected identifier here
 ╭▸ 
4 │ {{ for in bar }} {{ end }}
╰╴ ━━━━━━
error: Missing `in` in `for _ in <expr>`
 ╭▸ 
5 │ {{ for blah bar }} {{ end }}
╰╴ ━━━━━━━━
error: An error occurred while producing an Ast
 ╭▸ 
6 │ {{ else }}
╰╴ ━━━━━━━

View file

@ -0,0 +1,8 @@
{}
---
{{ if }} {{ end }}
{{ if if }} {{ end }}
{{ if if }} {{ for foo in bar }} {{ end }} {{ end }}
{{ for in bar }} {{ end }}
{{ for blah bar }} {{ end }}
{{ else }}

View file

@ -0,0 +1,24 @@
---
source: tests/file_tests.rs
expression: parsed
info:
input: "{{ if if }} {{ end }}"
context: {}
---
ParsedTemplate {
tokens: [
[LeftDelim]"{{" (0..2),
[Whitespace]" " (2..3),
[ConditionalIf]"if" (3..5),
[Whitespace]" " (5..6),
[ConditionalIf]"if" (6..8),
[Whitespace]" " (8..9),
[RightDelim]"}}" (9..11),
[Whitespace]" " (11..12),
[LeftDelim]"{{" (12..14),
[Whitespace]" " (14..15),
[End]"end" (15..18),
[Whitespace]" " (18..19),
[RightDelim]"}}" (19..21),
],
}

View file

@ -0,0 +1,11 @@
---
source: tests/file_tests.rs
expression: ast
info:
input: "{{ if if }} {{ end }}"
context: {}
---
error: Expected an expression after 'if'
 ╭▸ 
1 │ {{ if if }} {{ end }}
╰╴ ━━

View file

@ -0,0 +1,3 @@
{}
---
{{ if if }} {{ end }}

View file

@ -1,3 +1,5 @@
#![allow(missing_docs)]
use std::collections::HashMap;
use std::path::Path;
@ -6,6 +8,8 @@ use nomo::functions::FunctionMap;
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/errors/" as error_cases => check_errors }
#[derive(serde::Serialize)]
struct Info {
input: String,
@ -63,3 +67,67 @@ fn check_for_input([path]: [&Path; 1]) {
insta::assert_debug_snapshot!(format!("{basename}.4-output"), output);
}
fn check_errors([path]: [&Path; 1]) {
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("errors");
settings.set_prepend_module_to_snapshot(false);
let basename = path.file_stem().unwrap().to_string_lossy();
let input = std::fs::read_to_string(path).unwrap();
let (context, input) = input.split_once("\n---\n").unwrap_or_else(|| ("", &input));
let map = if !context.is_empty() {
serde_json::from_str::<HashMap<String, serde_json::Value>>(context).unwrap()
} else {
HashMap::new()
};
settings.set_info(&Info {
input: input.to_string(),
context: map.clone(),
});
let mut context = Context::new();
for (k, v) in map {
context.try_insert(k, v).unwrap();
}
let _guard = settings.bind_to_scope();
let parsed = nomo::lexer::parse(input.into()).map_err(|err| err.to_report());
match &parsed {
Ok(parsed) => insta::assert_debug_snapshot!(format!("{basename}.1-parsed"), parsed),
Err(parsed) => insta::assert_snapshot!(format!("{basename}.1-parsed"), parsed),
}
let Ok(parsed) = parsed else {
return;
};
let ast = nomo::parser::parse(parsed.tokens()).map_err(|err| err.to_report(input));
match &ast {
Ok(ast) => insta::assert_debug_snapshot!(format!("{basename}.2-ast"), ast),
Err(ast) => insta::assert_snapshot!(format!("{basename}.2-ast"), ast),
}
let Ok(ast) = ast else {
return;
};
let emit = nomo::compiler::emit_machine(ast);
insta::assert_debug_snapshot!(format!("{basename}.3-instructions"), emit);
let output = nomo::eval::execute(&FunctionMap::default(), &emit, &context)
.map_err(|err| err.to_string());
match &output {
Ok(output) => insta::assert_debug_snapshot!(format!("{basename}.4-output"), output),
Err(output) => insta::assert_snapshot!(format!("{basename}.4-output"), output),
}
}