96 lines
3.5 KiB
Rust
96 lines
3.5 KiB
Rust
use tb_frontend::{editing::*, lexer, parser};
|
|
#[test]
|
|
fn normalization_protects_tokens_and_is_idempotent() {
|
|
for source in [
|
|
" print Wort$; \"MiX Case\" ' Kommentar bleibt",
|
|
"data MiXeD, frei Text,\"Z\": print 1",
|
|
"if x% then print x%",
|
|
"rem frei text",
|
|
"print &Hff; 2.0#",
|
|
] {
|
|
let normalized = normalize_line(source);
|
|
assert_eq!(normalize_line(&normalized), normalized);
|
|
let tokens = |s| {
|
|
lexer::lex(s)
|
|
.tokens
|
|
.into_iter()
|
|
.map(|t| t.kind)
|
|
.collect::<Vec<_>>()
|
|
};
|
|
assert_eq!(tokens(source), tokens(&normalized));
|
|
}
|
|
assert_eq!(
|
|
normalize_line("print Wert$; \"MiX\" ' Kommentar"),
|
|
"PRINT Wert$; \"MiX\" ' Kommentar"
|
|
);
|
|
assert!(normalize_line("data MiXeD, frei Text").ends_with(" MiXeD, frei Text"));
|
|
}
|
|
#[test]
|
|
fn incomplete_is_parser_context_not_a_second_grammar() {
|
|
for source in ["SUB X\n", "IF 1 THEN\n", "FUNCTION X\n"] {
|
|
assert!(
|
|
parser::parse("M", &lexer::lex(source).tokens).incomplete,
|
|
"{source}"
|
|
);
|
|
}
|
|
for source in ["PRINT )\n", "SUB X\nPRINT )\n", "PRINT 1\n"] {
|
|
assert!(
|
|
!parser::parse("M", &lexer::lex(source).tokens).incomplete,
|
|
"{source}"
|
|
);
|
|
}
|
|
}
|
|
#[test]
|
|
fn declaration_maintenance_and_procedure_generation_are_transactional() {
|
|
let source = "DEFINT A-Z\nSUB Work (x)\nx=x+1\nEND SUB\n";
|
|
let (updated, notice) = maintain_declarations(source);
|
|
assert!(notice.is_none(), "{notice:?}");
|
|
assert!(updated.contains("DECLARE SUB WORK (X%)"), "{updated}");
|
|
assert_eq!(maintain_declarations(&updated), (updated.clone(), None));
|
|
for source in [
|
|
"SUB Draft\n",
|
|
"PRINT )\n",
|
|
"DECLARE SUB Work(x$)\nSUB Work(x%)\nEND SUB\n",
|
|
] {
|
|
let (same, notice) = maintain_declarations(source);
|
|
assert_eq!(same, source);
|
|
assert!(notice.is_some(), "{source}");
|
|
}
|
|
let manual = "DECLARE SUB Work(x AS INTEGER)\nSUB Work(x%)\nEND SUB\n";
|
|
assert_eq!(maintain_declarations(manual), (manual.into(), None));
|
|
let addition = new_procedure(source, source.len(), "Fresh", true).unwrap();
|
|
assert!(addition.contains("FUNCTION Fresh%"));
|
|
assert!(
|
|
tb_frontend::analyze_source("M", &format!("{source}{addition}"))
|
|
.diagnostics
|
|
.is_empty()
|
|
);
|
|
for name in ["Work", "PRINT", "bad name", ""] {
|
|
assert!(new_procedure(source, 0, name, false).is_err());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn deftype_context_does_not_leak_from_another_procedure() {
|
|
let source =
|
|
"' Modulrumpf\nDEFINT A-Z\nSUB First\nDEFSTR A-Z\nx$=\"x\"\nEND SUB\nSUB Second\nEND SUB\n";
|
|
let inside = source.find("x$=").unwrap();
|
|
assert!(new_procedure(source, inside, "Fresh", true)
|
|
.unwrap()
|
|
.contains("FUNCTION Fresh$"));
|
|
let second = source.find("SUB Second").unwrap();
|
|
assert!(new_procedure(source, second, "Fresh", true)
|
|
.unwrap()
|
|
.contains("FUNCTION Fresh%"));
|
|
let before = new_procedure(source, 0, "Fresh", true).unwrap();
|
|
assert!(before.contains("FUNCTION Fresh!"));
|
|
assert!(before.contains("DEFSNG A-Z"));
|
|
for at in [inside, second, 0] {
|
|
let addition = new_procedure(source, at, "Fresh", true).unwrap();
|
|
let diagnostics =
|
|
tb_frontend::analyze_source("M", &format!("{source}{addition}")).diagnostics;
|
|
assert!(diagnostics.is_empty(), "{diagnostics:?}");
|
|
}
|
|
assert!(new_procedure(source, 0, "Foo ' comment", false).is_err());
|
|
}
|