Files
TerminalBasic/crates/tb-frontend/tests/editing.rs

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());
}