Phase 4: Ereignisschleife und klassische Traps
This commit is contained in:
@@ -331,10 +331,28 @@ pub enum Stmt {
|
||||
to: Option<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
NameStmt { old: Expr, new: Expr, pos: SourcePos },
|
||||
NameStmt {
|
||||
old: Expr,
|
||||
new: Expr,
|
||||
pos: SourcePos,
|
||||
},
|
||||
// ---- Bildschirm/Ereignisse ----
|
||||
/// `VIEW PRINT [oben TO unten]`.
|
||||
ViewPrint { top: Option<Expr>, bottom: Option<Expr>, pos: SourcePos },
|
||||
ViewPrint {
|
||||
top: Option<Expr>,
|
||||
bottom: Option<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// `ON TIMER(n&) GOSUB ziel`, `ON KEY(n%) GOSUB ziel`,
|
||||
/// `ON UEVENT GOSUB ziel`, `ON SIGNAL(n%) GOSUB ziel` — die
|
||||
/// klassischen Ereignis-Traps (Sprachreferenz §8). `GOSUB 0` schaltet
|
||||
/// den Trap ab.
|
||||
TrapDef {
|
||||
device: String,
|
||||
index: Option<Expr>,
|
||||
target: LabelRef,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// `TIMER ON`, `KEY(5) OFF`, `UEVENT STOP` …
|
||||
EventControl {
|
||||
device: String,
|
||||
|
||||
@@ -329,6 +329,8 @@ pub enum Builtin {
|
||||
CommandS,
|
||||
Doevents,
|
||||
Sleep,
|
||||
/// `SetUEvent` — löst das benutzerdefinierte Ereignis aus (§8).
|
||||
SetUEvent,
|
||||
Beep,
|
||||
// ISAM (Change `phase-3-isam`)
|
||||
IsamOpen,
|
||||
@@ -537,6 +539,22 @@ pub enum HStmtKind {
|
||||
targets: Vec<LabelId>,
|
||||
},
|
||||
ReturnGosub(Option<LabelId>),
|
||||
/// Ereignis-Trap erklären (Sprachreferenz §8). `art` ist die Quelle
|
||||
/// (0 KEY, 1 TIMER, 2 UEVENT, 3 SIGNAL), `index` ihre Kennung bzw. bei
|
||||
/// `TIMER` das Intervall in Sekunden. `ziel = None` = `GOSUB 0`.
|
||||
TrapDef {
|
||||
art: u8,
|
||||
index: HExpr,
|
||||
ziel: Option<LabelId>,
|
||||
},
|
||||
/// `<quelle>(n) ON|OFF|STOP` — `zustand` 0 An, 1 Aus, 2 Gestoppt.
|
||||
TrapSet {
|
||||
art: u8,
|
||||
index: HExpr,
|
||||
zustand: u8,
|
||||
},
|
||||
/// `EVENT ON` / `EVENT OFF`.
|
||||
EventSwitch(bool),
|
||||
/// EXIT SUB/FUNCTION/DEF bzw. Rumpfende.
|
||||
ExitProc,
|
||||
CallSub {
|
||||
|
||||
@@ -666,12 +666,11 @@ impl<'a> P<'a> {
|
||||
let decls = self.parse_var_decls();
|
||||
Some(Stmt::StaticDecl { decls, pos })
|
||||
}
|
||||
TokenKind::Ident { ref name, .. } if name == "LPRINT" => {
|
||||
self.parse_print(pos, true)
|
||||
}
|
||||
TokenKind::Ident { ref name, suffix: None }
|
||||
if name == "VIEW" && self.k_at(1) == TokenKind::Kw(Kw::Print) =>
|
||||
{
|
||||
TokenKind::Ident { ref name, .. } if name == "LPRINT" => self.parse_print(pos, true),
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} if name == "VIEW" && self.k_at(1) == TokenKind::Kw(Kw::Print) => {
|
||||
self.advance(); // VIEW
|
||||
self.advance(); // PRINT
|
||||
let (mut top, mut bottom) = (None, None);
|
||||
@@ -972,9 +971,7 @@ impl<'a> P<'a> {
|
||||
}
|
||||
if matches!(self.k(), TokenKind::Eol) {
|
||||
// Blockform
|
||||
let stop = |p: &P| {
|
||||
p.is_kw(Kw::ElseIf) || p.is_kw(Kw::Else) || p.at_end_pair(Kw::If)
|
||||
};
|
||||
let stop = |p: &P| p.is_kw(Kw::ElseIf) || p.is_kw(Kw::Else) || p.at_end_pair(Kw::If);
|
||||
let then_body = self.parse_stmt_list(stop);
|
||||
let mut elseifs = Vec::new();
|
||||
while self.eat_kw(Kw::ElseIf) {
|
||||
@@ -1071,10 +1068,12 @@ impl<'a> P<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let body = self.parse_stmt_list(|p: &P| {
|
||||
p.is_kw(Kw::Case) || p.at_end_pair(Kw::Select)
|
||||
let body = self.parse_stmt_list(|p: &P| p.is_kw(Kw::Case) || p.at_end_pair(Kw::Select));
|
||||
arms.push(CaseArm {
|
||||
specs,
|
||||
body,
|
||||
pos: arm_pos,
|
||||
});
|
||||
arms.push(CaseArm { specs, body, pos: arm_pos });
|
||||
}
|
||||
if self.at_end_pair(Kw::Select) {
|
||||
self.advance();
|
||||
@@ -1149,6 +1148,46 @@ impl<'a> P<'a> {
|
||||
Some(Stmt::While { cond, body, pos })
|
||||
}
|
||||
|
||||
/// `ON <quelle>[(n)] GOSUB ziel`. Liefert `None` und lässt den Cursor
|
||||
/// unberührt, wenn hier kein Trap steht.
|
||||
fn parse_trap_def(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
let start = self.i;
|
||||
let TokenKind::Ident { name, suffix: None } = self.k() else {
|
||||
return None;
|
||||
};
|
||||
if !matches!(
|
||||
name.as_str(),
|
||||
"TIMER" | "KEY" | "UEVENT" | "SIGNAL" | "COM" | "PEN" | "PLAY" | "STRIG"
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
let device = name.clone();
|
||||
self.advance();
|
||||
let index = if self.eat(&TokenKind::LParen) {
|
||||
let e = self.parse_expr();
|
||||
if e.is_none() || !self.eat(&TokenKind::RParen) {
|
||||
self.i = start;
|
||||
return None;
|
||||
}
|
||||
e
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !self.eat_kw(Kw::Gosub) {
|
||||
// Doch kein Trap — z. B. `ON TIMER GOTO …` gibt es nicht, aber
|
||||
// die Diagnose dafür gehört an die allgemeine Stelle.
|
||||
self.i = start;
|
||||
return None;
|
||||
}
|
||||
let target = self.parse_label_ref()?;
|
||||
Some(Stmt::TrapDef {
|
||||
device,
|
||||
index,
|
||||
target,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_on(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
self.advance(); // ON
|
||||
let local = self.eat_kw(Kw::Local);
|
||||
@@ -1186,7 +1225,13 @@ impl<'a> P<'a> {
|
||||
self.sync();
|
||||
return None;
|
||||
}
|
||||
// ON ausdruck GOTO/GOSUB liste (Event-Traps: Phase 3/4)
|
||||
// ON <quelle>[(n)] GOSUB ziel — klassische Ereignis-Traps (§8).
|
||||
// Muss vor dem berechneten `ON ausdruck GOSUB` stehen, sonst
|
||||
// verschwindet `ON TIMER(5) GOSUB Marke` still darin.
|
||||
if let Some(st) = self.parse_trap_def(pos) {
|
||||
return Some(st);
|
||||
}
|
||||
// ON ausdruck GOTO/GOSUB liste
|
||||
let expr = self.parse_expr()?;
|
||||
let gosub = if self.eat_kw(Kw::Goto) {
|
||||
false
|
||||
@@ -1528,26 +1573,34 @@ impl<'a> P<'a> {
|
||||
return None;
|
||||
}
|
||||
// Ereignissteuerung: `TIMER ON`, `KEY(5) OFF`, `UEVENT STOP` …
|
||||
if let Expr::Name { ref name, suffix: None, ref args, .. } = target {
|
||||
if let Expr::Name {
|
||||
ref name,
|
||||
suffix: None,
|
||||
ref args,
|
||||
..
|
||||
} = target
|
||||
{
|
||||
if matches!(
|
||||
name.as_str(),
|
||||
"TIMER" | "KEY" | "COM" | "PEN" | "PLAY" | "STRIG" | "UEVENT"
|
||||
| "EVENT"
|
||||
"TIMER" | "KEY" | "COM" | "PEN" | "PLAY" | "STRIG" | "UEVENT" | "EVENT"
|
||||
) {
|
||||
let action = match self.k() {
|
||||
TokenKind::Kw(Kw::On) => Some(EventAction::On),
|
||||
TokenKind::Kw(Kw::Stop) => Some(EventAction::Stop),
|
||||
TokenKind::Ident { ref name, suffix: None }
|
||||
if name == "OFF" =>
|
||||
{
|
||||
Some(EventAction::Off)
|
||||
}
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} if name == "OFF" => Some(EventAction::Off),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(action) = action {
|
||||
self.advance();
|
||||
let index = args.clone().and_then(|mut a| {
|
||||
if a.is_empty() { None } else { Some(a.remove(0)) }
|
||||
if a.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(a.remove(0))
|
||||
}
|
||||
});
|
||||
return Some(Stmt::EventControl {
|
||||
device: name.clone(),
|
||||
|
||||
@@ -233,9 +233,7 @@ fn builtin_fn(name: &str) -> Option<(u8, u8, &'static [ArgK], RetK)> {
|
||||
"NOW" => (0, 0, &[], Db),
|
||||
"DATESERIAL" | "TIMESERIAL" => (3, 3, &[N, N, N], Db),
|
||||
"DATEVALUE" | "TIMEVALUE" => (1, 1, &[S], Db),
|
||||
"DAY" | "MONTH" | "YEAR" | "WEEKDAY" | "HOUR" | "MINUTE" | "SECOND" => {
|
||||
(1, 1, &[N], I)
|
||||
}
|
||||
"DAY" | "MONTH" | "YEAR" | "WEEKDAY" | "HOUR" | "MINUTE" | "SECOND" => (1, 1, &[N], I),
|
||||
"FORMAT$" => (1, 2, &[A, S], St),
|
||||
"ERR" | "ERL" => (0, 0, &[], L),
|
||||
"FRE" => (1, 1, &[A], L),
|
||||
@@ -253,9 +251,7 @@ fn builtin_fn(name: &str) -> Option<(u8, u8, &'static [ArgK], RetK)> {
|
||||
"FV" | "FV#" | "PV" | "PV#" | "PMT" | "PMT#" | "NPER" | "NPER#" => {
|
||||
(5, 5, &[N, N, N, N, N], Db)
|
||||
}
|
||||
"IPMT" | "IPMT#" | "PPMT" | "PPMT#" | "RATE" | "RATE#" => {
|
||||
(6, 6, &[N, 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),
|
||||
"MIRR" | "MIRR#" => (3, 3, &[A, N, N], Db),
|
||||
"SLN" | "SLN#" => (3, 3, &[N, N, N], Db),
|
||||
@@ -636,9 +632,9 @@ impl Sema {
|
||||
self.prescan(&a.body, scope, module_data);
|
||||
}
|
||||
}
|
||||
Stmt::For { body, .. }
|
||||
| Stmt::DoLoop { body, .. }
|
||||
| Stmt::While { body, .. } => self.prescan(body, scope, module_data),
|
||||
Stmt::For { body, .. } | Stmt::DoLoop { body, .. } | Stmt::While { body, .. } => {
|
||||
self.prescan(body, scope, module_data)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -880,21 +876,25 @@ impl Sema {
|
||||
Expr::CurrencyLit(v) => Some(ConstVal::Num(*v as f64 / 10_000.0)),
|
||||
Expr::StrLit(s) => Some(ConstVal::Str(s.clone())),
|
||||
Expr::Paren(e) => self.fold_const(e),
|
||||
Expr::Name { name, args: None, .. } => {
|
||||
self.consts.get(name).and_then(|(_, v)| v.clone())
|
||||
}
|
||||
Expr::Unary { op: UnOp::Neg, operand, .. } => {
|
||||
match self.fold_const(operand)? {
|
||||
ConstVal::Num(n) => Some(ConstVal::Num(-n)),
|
||||
ConstVal::Str(_) => None,
|
||||
}
|
||||
}
|
||||
Expr::Unary { op: UnOp::Not, operand, .. } => {
|
||||
match self.fold_const(operand)? {
|
||||
ConstVal::Num(n) => Some(ConstVal::Num(!(n as i64) as f64)),
|
||||
ConstVal::Str(_) => None,
|
||||
}
|
||||
}
|
||||
Expr::Name {
|
||||
name, args: None, ..
|
||||
} => self.consts.get(name).and_then(|(_, v)| v.clone()),
|
||||
Expr::Unary {
|
||||
op: UnOp::Neg,
|
||||
operand,
|
||||
..
|
||||
} => match self.fold_const(operand)? {
|
||||
ConstVal::Num(n) => Some(ConstVal::Num(-n)),
|
||||
ConstVal::Str(_) => None,
|
||||
},
|
||||
Expr::Unary {
|
||||
op: UnOp::Not,
|
||||
operand,
|
||||
..
|
||||
} => match self.fold_const(operand)? {
|
||||
ConstVal::Num(n) => Some(ConstVal::Num(!(n as i64) as f64)),
|
||||
ConstVal::Str(_) => None,
|
||||
},
|
||||
Expr::Binary { op, lhs, rhs, .. } => {
|
||||
let l = self.fold_const(lhs)?;
|
||||
let r = self.fold_const(rhs)?;
|
||||
@@ -1513,25 +1513,41 @@ impl Sema {
|
||||
Some(t) => {
|
||||
self.check_label_exists(t, scope, *pos);
|
||||
match t {
|
||||
LabelRef::Name(n) => {
|
||||
self.data_marks_name.get(n).copied().unwrap_or(0)
|
||||
}
|
||||
LabelRef::Line(n) => {
|
||||
self.data_marks_line.get(n).copied().unwrap_or(0)
|
||||
}
|
||||
LabelRef::Name(n) => self.data_marks_name.get(n).copied().unwrap_or(0),
|
||||
LabelRef::Line(n) => self.data_marks_line.get(n).copied().unwrap_or(0),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.push(out, scope, HStmtKind::Restore(idx));
|
||||
}
|
||||
Stmt::DefFn { name, suffix, params, body, pos } => {
|
||||
Stmt::DefFn {
|
||||
name,
|
||||
suffix,
|
||||
params,
|
||||
body,
|
||||
pos,
|
||||
} => {
|
||||
self.lower_def_fn(name, suffix, params, None, Some(body), scope, *pos);
|
||||
}
|
||||
Stmt::DefFnBlock { name, suffix, params, body, pos } => {
|
||||
Stmt::DefFnBlock {
|
||||
name,
|
||||
suffix,
|
||||
params,
|
||||
body,
|
||||
pos,
|
||||
} => {
|
||||
self.lower_def_fn(name, suffix, params, Some(body), None, scope, *pos);
|
||||
}
|
||||
// ---- Datei-E/A (Grammatik Phase 1, Laufzeit Phase 3) ----
|
||||
Stmt::Open { file, mode, isam, number, len, pos, .. } => {
|
||||
Stmt::Open {
|
||||
file,
|
||||
mode,
|
||||
isam,
|
||||
number,
|
||||
len,
|
||||
pos,
|
||||
..
|
||||
} => {
|
||||
let (fe, _) = self.want_str(file, scope);
|
||||
self.reject_com_device(file, *pos);
|
||||
let (ne, nt) = self.want_num(number, scope);
|
||||
@@ -1779,12 +1795,67 @@ impl Sema {
|
||||
self.push(
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::BuiltinStmt { b: Builtin::ViewPrint, args: a },
|
||||
HStmtKind::BuiltinStmt {
|
||||
b: Builtin::ViewPrint,
|
||||
args: a,
|
||||
},
|
||||
);
|
||||
}
|
||||
Stmt::EventControl { device, index, action, pos } => {
|
||||
Stmt::TrapDef {
|
||||
device,
|
||||
index,
|
||||
target,
|
||||
pos,
|
||||
} => {
|
||||
let Some(art) = trap_art(device) else {
|
||||
// COM/PEN/PLAY/STRIG sind Non-Feature — namentlich.
|
||||
self.err(*pos, "Feature unavailable");
|
||||
return;
|
||||
};
|
||||
// Wertebereich prüfen, soweit die Kennung konstant ist.
|
||||
if let Some(i) = index {
|
||||
if let Some(n) = const_zahl(i) {
|
||||
if !trap_bereich_ok(art, n) {
|
||||
self.err(*pos, trap_bereich_text(art));
|
||||
}
|
||||
}
|
||||
} else if art != TRAP_UEVENT {
|
||||
self.err(*pos, trap_bereich_text(art));
|
||||
}
|
||||
if index.is_some() && art == TRAP_UEVENT {
|
||||
self.err(*pos, "ON UEVENT nimmt keine Kennung");
|
||||
}
|
||||
let hindex = match index {
|
||||
Some(i) => self.lower_num_as(i, scope, NumTy::Lng),
|
||||
None => HExpr::Lng(0),
|
||||
};
|
||||
// `GOSUB 0` schaltet den Trap ab und meint nicht Zeile 0.
|
||||
let ziel = if matches!(target, LabelRef::Line(0)) {
|
||||
None
|
||||
} else {
|
||||
match self.label_id(target, scope, *pos) {
|
||||
Some(id) => Some(id),
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
self.push(
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::TrapDef {
|
||||
art,
|
||||
index: hindex,
|
||||
ziel,
|
||||
},
|
||||
);
|
||||
}
|
||||
Stmt::EventControl {
|
||||
device,
|
||||
index,
|
||||
action,
|
||||
pos,
|
||||
} => {
|
||||
match device.as_str() {
|
||||
"TIMER" | "KEY" | "UEVENT" | "EVENT" => {}
|
||||
"TIMER" | "KEY" | "UEVENT" | "SIGNAL" | "EVENT" => {}
|
||||
_ => self.err(*pos, "Feature unavailable"),
|
||||
}
|
||||
if let Some(i) = index {
|
||||
@@ -1805,7 +1876,45 @@ impl Sema {
|
||||
);
|
||||
return;
|
||||
}
|
||||
self.push(out, scope, HStmtKind::Unsupported("Ereignissteuerung"));
|
||||
if device == "EVENT" {
|
||||
// Das Vorbild kennt nur ON und OFF — kein STOP.
|
||||
if *action == EventAction::Stop {
|
||||
self.err(*pos, "EVENT kennt nur ON und OFF");
|
||||
return;
|
||||
}
|
||||
let an = *action == EventAction::On;
|
||||
self.push(out, scope, HStmtKind::EventSwitch(an));
|
||||
return;
|
||||
}
|
||||
let Some(art) = trap_art(device) else {
|
||||
self.err(*pos, "Feature unavailable");
|
||||
return;
|
||||
};
|
||||
if let Some(i) = index {
|
||||
if let Some(n) = const_zahl(i) {
|
||||
if !trap_bereich_ok(art, n) {
|
||||
self.err(*pos, trap_bereich_text(art));
|
||||
}
|
||||
}
|
||||
}
|
||||
let hindex = match index {
|
||||
Some(i) => self.lower_num_as(i, scope, NumTy::Lng),
|
||||
None => HExpr::Lng(0),
|
||||
};
|
||||
let zustand = match action {
|
||||
EventAction::On => 0,
|
||||
EventAction::Off => 1,
|
||||
EventAction::Stop => 2,
|
||||
};
|
||||
self.push(
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::TrapSet {
|
||||
art,
|
||||
index: hindex,
|
||||
zustand,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2343,10 +2452,22 @@ impl Sema {
|
||||
let lowered = self.check_and_lower_builtin_args(name, args, min, max, spec, scope, pos);
|
||||
|
||||
match name {
|
||||
"BEEP" => self.push(out, scope, HStmtKind::BuiltinStmt { b: Builtin::Beep, args: vec![] }),
|
||||
"DOEVENTS" => {
|
||||
self.push(out, scope, HStmtKind::BuiltinStmt { b: Builtin::Doevents, args: vec![] })
|
||||
}
|
||||
"BEEP" => self.push(
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::BuiltinStmt {
|
||||
b: Builtin::Beep,
|
||||
args: vec![],
|
||||
},
|
||||
),
|
||||
"DOEVENTS" => self.push(
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::BuiltinStmt {
|
||||
b: Builtin::Doevents,
|
||||
args: vec![],
|
||||
},
|
||||
),
|
||||
"RANDOMIZE" => {
|
||||
let a = lowered
|
||||
.into_iter()
|
||||
@@ -2494,19 +2615,29 @@ impl Sema {
|
||||
self.push(out, scope, HStmtKind::BuiltinStmt { b, args: a });
|
||||
}
|
||||
// ---- ISAM ------------------------------------------------------
|
||||
"CREATEINDEX" | "DELETEINDEX" | "SETINDEX" | "INSERT" | "RETRIEVE"
|
||||
| "UPDATE" | "DELETE" | "DELETETABLE" | "MOVEFIRST" | "MOVELAST"
|
||||
| "MOVENEXT" | "MOVEPREVIOUS" | "SEEKEQ" | "SEEKGT" | "SEEKGE"
|
||||
| "BEGINTRANS" | "COMMITTRANS" | "ROLLBACK" => {
|
||||
"CREATEINDEX" | "DELETEINDEX" | "SETINDEX" | "INSERT" | "RETRIEVE" | "UPDATE"
|
||||
| "DELETE" | "DELETETABLE" | "MOVEFIRST" | "MOVELAST" | "MOVENEXT" | "MOVEPREVIOUS"
|
||||
| "SEEKEQ" | "SEEKGT" | "SEEKGE" | "BEGINTRANS" | "COMMITTRANS" | "ROLLBACK" => {
|
||||
self.lower_isam_stmt(name, args, lowered, scope, out)
|
||||
}
|
||||
"SETUEVENT" => self.push(
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::BuiltinStmt {
|
||||
b: Builtin::SetUEvent,
|
||||
args: vec![],
|
||||
},
|
||||
),
|
||||
// Bildschirm-/Datei-/System-Anweisungen späterer Phasen.
|
||||
_ => self.push(out, scope, HStmtKind::Unsupported(match name {
|
||||
"RUN" => "RUN",
|
||||
"MSGBOX" => "MSGBOX",
|
||||
"SETUEVENT" => "SETUEVENT",
|
||||
_ => "Anweisung",
|
||||
})),
|
||||
_ => self.push(
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::Unsupported(match name {
|
||||
"RUN" => "RUN",
|
||||
"MSGBOX" => "MSGBOX",
|
||||
_ => "Anweisung",
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2897,9 +3028,12 @@ impl Sema {
|
||||
let (re, rt) = self.lower_expr(rhs, scope);
|
||||
self.lower_binary(*op, le, lt, re, rt, *pos)
|
||||
}
|
||||
Expr::Name { name, suffix, args, pos } => {
|
||||
self.lower_name_expr(name, suffix, args, *pos, scope)
|
||||
}
|
||||
Expr::Name {
|
||||
name,
|
||||
suffix,
|
||||
args,
|
||||
pos,
|
||||
} => self.lower_name_expr(name, suffix, args, *pos, scope),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3742,6 +3876,7 @@ fn stmt_pos(stmt: &Stmt) -> SourcePos {
|
||||
| Stmt::LockStmt { pos, .. }
|
||||
| Stmt::NameStmt { pos, .. }
|
||||
| Stmt::ViewPrint { pos, .. }
|
||||
| Stmt::TrapDef { pos, .. }
|
||||
| Stmt::EventControl { pos, .. }
|
||||
| Stmt::Include { pos, .. }
|
||||
| Stmt::MetaArrays { pos, .. }
|
||||
@@ -4082,3 +4217,52 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Ereignis-Traps (Sprachreferenz §8) -------------------------------------
|
||||
|
||||
pub const TRAP_KEY: u8 = 0;
|
||||
pub const TRAP_TIMER: u8 = 1;
|
||||
pub const TRAP_UEVENT: u8 = 2;
|
||||
pub const TRAP_SIGNAL: u8 = 3;
|
||||
|
||||
/// Quellenname → Art. `None` für die Non-Features.
|
||||
fn trap_art(device: &str) -> Option<u8> {
|
||||
Some(match device {
|
||||
"KEY" => TRAP_KEY,
|
||||
"TIMER" => TRAP_TIMER,
|
||||
"UEVENT" => TRAP_UEVENT,
|
||||
"SIGNAL" => TRAP_SIGNAL,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Konstanter Zahlenwert eines Ausdrucks, soweit direkt ablesbar.
|
||||
fn const_zahl(e: &Expr) -> Option<i64> {
|
||||
match e {
|
||||
Expr::IntLit(n) => Some(*n as i64),
|
||||
Expr::LongLit(n) => Some(*n as i64),
|
||||
Expr::SingleLit(n) => Some(*n as i64),
|
||||
Expr::DoubleLit(n) => Some(*n as i64),
|
||||
Expr::Paren(inner) => const_zahl(inner),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wertebereiche nach der Original-Hilfe (Belege in `umfang-und-form.md`).
|
||||
fn trap_bereich_ok(art: u8, n: i64) -> bool {
|
||||
match art {
|
||||
TRAP_KEY => matches!(n, 0..=25 | 30 | 31),
|
||||
TRAP_TIMER => (1..=86_400).contains(&n),
|
||||
TRAP_SIGNAL => (1..=2).contains(&n),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn trap_bereich_text(art: u8) -> &'static str {
|
||||
match art {
|
||||
TRAP_KEY => "ON KEY: Kennung 0, 1-25 oder 30-31 erwartet",
|
||||
TRAP_TIMER => "ON TIMER: Intervall 1 bis 86400 Sekunden erwartet",
|
||||
TRAP_SIGNAL => "ON SIGNAL: Kennung 1 (SIGINT) oder 2 (SIGTERM) erwartet",
|
||||
_ => "Kennung erwartet",
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user