Phase 4: Ereignisschleife und klassische Traps

This commit is contained in:
2026-09-04 15:56:47 +02:00
parent 060674b466
commit 5cf5a6582c
40 changed files with 4024 additions and 176 deletions

View File

@@ -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(),