Spezifikationsabgleich und Regressionsnachweise abschließen
This commit is contained in:
@@ -194,7 +194,6 @@ impl<'a> P<'a> {
|
||||
|
||||
fn parse_statement(&mut self) -> Option<Stmt> {
|
||||
let pos = self.pos();
|
||||
|
||||
// Zeilennummern und Labels nur am Zeilenanfang
|
||||
if self.at_line_start {
|
||||
if let TokenKind::Num(NumValue::Int(n)) = self.k() {
|
||||
@@ -611,7 +610,10 @@ impl<'a> P<'a> {
|
||||
self.advance();
|
||||
if self.k() == TokenKind::LParen {
|
||||
// Grafikform GET (x1,y1)-(x2,y2): deklariertes Non-Feature
|
||||
self.err("Feature unavailable");
|
||||
self.err(format!(
|
||||
"Feature unavailable: {}",
|
||||
if k == Kw::Get { "GET" } else { "PUT" }
|
||||
));
|
||||
self.sync();
|
||||
return None;
|
||||
}
|
||||
@@ -1804,7 +1806,7 @@ impl<'a> P<'a> {
|
||||
// Non-Feature — an der Syntax erkennbar, weil `DEF` sonst nur
|
||||
// `DEF FNname` einleitet.
|
||||
if matches!(self.k(), TokenKind::Ident { ref name, suffix: None } if name == "SEG") {
|
||||
self.err("Feature unavailable");
|
||||
self.err("Feature unavailable: DEF SEG");
|
||||
self.sync();
|
||||
return None;
|
||||
}
|
||||
@@ -1885,6 +1887,21 @@ impl<'a> P<'a> {
|
||||
self.sync();
|
||||
return None;
|
||||
}
|
||||
// Ausgeschlossene Anweisungen besitzen eigene Koordinaten-/Dateisyntax.
|
||||
// Erst Zuweisungen erkennen, damit gleichnamige Variablen/Arrays gültig bleiben.
|
||||
if let Expr::Name {
|
||||
name, suffix: None, ..
|
||||
} = &target
|
||||
{
|
||||
if matches!(
|
||||
name.as_str(),
|
||||
"CIRCLE" | "WINDOW" | "IOCTL" | "PSET" | "PRESET" | "PALETTE"
|
||||
) {
|
||||
self.err(format!("Feature unavailable: {name}"));
|
||||
self.sync();
|
||||
return None;
|
||||
}
|
||||
}
|
||||
// Ereignissteuerung: `TIMER ON`, `KEY(5) OFF`, `UEVENT STOP` …
|
||||
if let Expr::Name {
|
||||
ref name,
|
||||
|
||||
@@ -265,7 +265,8 @@ fn builtin_fn(name: &str) -> Option<(u8, u8, &'static [ArgK], RetK)> {
|
||||
(5, 5, &[N, N, N, N, N], Db)
|
||||
}
|
||||
"IPMT" | "IPMT#" | "PPMT" | "PPMT#" | "RATE" | "RATE#" => (6, 6, &[N, N, N, N, N, N], Db),
|
||||
"NPV" | "NPV#" | "IRR" | "IRR#" => (2, 2, &[N, A], Db),
|
||||
"NPV" | "NPV#" => (2, 2, &[N, A], Db),
|
||||
"IRR" | "IRR#" => (2, 2, &[A, N], Db),
|
||||
"MIRR" | "MIRR#" => (3, 3, &[A, N, N], Db),
|
||||
"SLN" | "SLN#" => (3, 3, &[N, N, N], Db),
|
||||
"SYD" | "SYD#" | "DDB" | "DDB#" => (4, 4, &[N, N, N, N], Db),
|
||||
@@ -278,6 +279,7 @@ fn builtin_fn(name: &str) -> Option<(u8, u8, &'static [ArgK], RetK)> {
|
||||
"CVD" | "CVDMBF" => (1, 1, &[S], Db),
|
||||
"CVC" => (1, 1, &[S], Cu),
|
||||
// Dateisystem und System
|
||||
"SHELL" => (1, 1, &[S], L),
|
||||
"CURDIR$" => (0, 1, &[S], St),
|
||||
"DIR$" => (0, 1, &[S], St),
|
||||
"LPOS" => (1, 1, &[N], I),
|
||||
@@ -2861,7 +2863,7 @@ impl Sema {
|
||||
} => {
|
||||
let Some(art) = trap_art(device) else {
|
||||
// COM/PEN/PLAY/STRIG sind Non-Feature — namentlich.
|
||||
self.err(*pos, "Feature unavailable");
|
||||
self.err(*pos, format!("Feature unavailable: {device}"));
|
||||
return;
|
||||
};
|
||||
// Wertebereich prüfen, soweit die Kennung konstant ist.
|
||||
@@ -2908,7 +2910,7 @@ impl Sema {
|
||||
} => {
|
||||
match device.as_str() {
|
||||
"TIMER" | "KEY" | "UEVENT" | "SIGNAL" | "EVENT" => {}
|
||||
_ => self.err(*pos, "Feature unavailable"),
|
||||
_ => self.err(*pos, format!("Feature unavailable: {device}")),
|
||||
}
|
||||
if let Some(i) = index {
|
||||
self.want_num(i, scope);
|
||||
@@ -2939,7 +2941,7 @@ impl Sema {
|
||||
return;
|
||||
}
|
||||
let Some(art) = trap_art(device) else {
|
||||
self.err(*pos, "Feature unavailable");
|
||||
self.err(*pos, format!("Feature unavailable: {device}"));
|
||||
return;
|
||||
};
|
||||
if let Some(i) = index {
|
||||
@@ -3566,7 +3568,7 @@ impl Sema {
|
||||
for a in args {
|
||||
self.lower_expr(a, scope);
|
||||
}
|
||||
self.err(pos, "Feature unavailable");
|
||||
self.err(pos, format!("Feature unavailable: {name}"));
|
||||
return;
|
||||
}
|
||||
self.err(pos, "Subprogram not defined");
|
||||
@@ -4147,7 +4149,7 @@ impl Sema {
|
||||
fn reject_com_device(&mut self, file: &Expr, pos: SourcePos) {
|
||||
if let Expr::StrLit(s, _) = file {
|
||||
if ist_com_geraet(s) {
|
||||
self.err(pos, "Feature unavailable");
|
||||
self.err(pos, format!("Feature unavailable: {s}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4922,7 +4924,7 @@ impl Sema {
|
||||
for a in idx {
|
||||
self.lower_expr(a, scope);
|
||||
}
|
||||
self.err(pos, "Feature unavailable");
|
||||
self.err(pos, format!("Feature unavailable: {full_name}"));
|
||||
return (HExpr::Int(0), Ty::Unknown);
|
||||
}
|
||||
// 4. Builtin-Funktion
|
||||
@@ -4982,7 +4984,7 @@ impl Sema {
|
||||
.is_some_and(|v| suffix.is_none_or(|s| v.ty == suffix_ty(s)));
|
||||
if !declared {
|
||||
if banned_feature(&full_name) {
|
||||
self.err(pos, "Feature unavailable");
|
||||
self.err(pos, format!("Feature unavailable: {full_name}"));
|
||||
return (HExpr::Int(0), Ty::Unknown);
|
||||
}
|
||||
if let Some((0, _, _, _)) = builtin_fn(&full_name) {
|
||||
@@ -5628,13 +5630,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hardware_features_zur_compilezeit_abgelehnt() {
|
||||
assert!(diags("POKE 100, 1").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("x = PEEK(100)").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("p = VARPTR(a%)").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("SOUND 440, 10").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("CHAIN \"prog\"").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("PLAY \"cde\"").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("CIRCLE 1, 2").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("POKE 100, 1").contains(&"Feature unavailable: POKE".to_string()));
|
||||
assert!(diags("x = PEEK(100)").contains(&"Feature unavailable: PEEK".to_string()));
|
||||
assert!(diags("p = VARPTR(a%)").contains(&"Feature unavailable: VARPTR".to_string()));
|
||||
assert!(diags("SOUND 440, 10").contains(&"Feature unavailable: SOUND".to_string()));
|
||||
assert!(diags("CHAIN \"prog\"").contains(&"Feature unavailable: CHAIN".to_string()));
|
||||
assert!(diags("PLAY \"cde\"").contains(&"Feature unavailable: PLAY".to_string()));
|
||||
assert!(diags("CIRCLE 1, 2").contains(&"Feature unavailable: CIRCLE".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5726,7 +5728,7 @@ mod tests {
|
||||
fn datei_ea_und_events() {
|
||||
let src = "OPEN \"test.dat\" FOR RANDOM AS #1 LEN = 64\nCLOSE #1\nOPEN \"o\", #2, \"f.txt\"\nCLOSE\nTIMER ON\nKEY(5) OFF";
|
||||
assert!(diags(src).is_empty(), "{:?}", diags(src));
|
||||
assert!(diags("PEN ON").contains(&"Feature unavailable".to_string()));
|
||||
assert!(diags("PEN ON").contains(&"Feature unavailable: PEN".to_string()));
|
||||
}
|
||||
|
||||
fn form_catalog() -> FormCatalog {
|
||||
|
||||
Reference in New Issue
Block a user