Phase 4: Steuerelemente implementieren

This commit is contained in:
2026-09-05 12:49:46 +02:00
parent 1cb6ae8fd9
commit 19804e0e2d
36 changed files with 6472 additions and 689 deletions

View File

@@ -20,6 +20,10 @@ pub fn compile(hir: &HirModule) -> CompiledModule {
string_ids: HashMap::new(),
jump_tables: Vec::new(),
modul_label_pc: Vec::new(),
initialize_main: hir
.objects
.iter()
.any(|object| object.class == tb_frontend::forms::ObjectClass::Form),
};
let mut procs = Vec::new();
for proc in &hir.procs {
@@ -29,7 +33,7 @@ pub fn compile(hir: &HirModule) -> CompiledModule {
name: hir.name.clone(),
option_base: hir.option_base,
strings: cg.strings,
globals_init: hir.globals.iter().map(|g| slot_init(g)).collect(),
globals_init: hir.globals.iter().map(slot_init).collect(),
global_names: hir.globals.iter().map(|g| g.name.clone()).collect(),
udts: hir
.udts
@@ -85,6 +89,9 @@ struct Codegen {
/// `ON ERROR GOTO` aus einer Prozedur zeigt dorthin; da Prozedur 0
/// zuerst übersetzt wird, stehen die Positionen rechtzeitig fest.
modul_label_pc: Vec<Option<u32>>,
/// Formulare müssen erst nach den globalen DIM-Anweisungen Ereignisse
/// zustellen; reine Textprogramme behalten ihre bisherigen Grenzen.
initialize_main: bool,
}
struct ProcCtx {
@@ -160,7 +167,30 @@ impl Codegen {
fixups: Vec::new(),
table_fixups: Vec::new(),
};
if proc.kind == hir::HProcKind::Main {
let declarations = proc
.body
.iter()
.filter(|stmt| matches!(stmt.kind, HStmtKind::Dim { redim: false, .. }))
.collect::<Vec<_>>();
for stmt in declarations {
self.stmt_inner(&mut ctx, proc, stmt, false);
}
if self.initialize_main
|| proc
.body
.iter()
.any(|stmt| matches!(stmt.kind, HStmtKind::Dim { redim: false, .. }))
{
ctx.emit(Instr::Stmt(0));
}
}
for stmt in &proc.body {
if proc.kind == hir::HProcKind::Main
&& matches!(stmt.kind, HStmtKind::Dim { redim: false, .. })
{
continue;
}
self.stmt(&mut ctx, proc, stmt);
}
// Rumpfende
@@ -234,12 +264,17 @@ impl Codegen {
// ---- Anweisungen -------------------------------------------------------
fn stmt(&mut self, ctx: &mut ProcCtx, proc: &hir::HProc, stmt: &HStmt) {
self.stmt_inner(ctx, proc, stmt, true);
}
fn stmt_inner(&mut self, ctx: &mut ProcCtx, proc: &hir::HProc, stmt: &HStmt, boundary: bool) {
match &stmt.kind {
HStmtKind::Label(l) => {
ctx.bind(*l);
return;
}
_ => ctx.emit(Instr::Stmt(stmt.line)),
_ if boundary => ctx.emit(Instr::Stmt(stmt.line)),
_ => {}
}
match &stmt.kind {
HStmtKind::Label(_) => unreachable!(),
@@ -263,6 +298,23 @@ impl Codegen {
index.is_some(),
));
}
HStmtKind::SetObjectIndexedProperty {
object,
object_index,
property,
index,
value,
} => {
if let Some(object_index) = object_index {
self.expr(ctx, object_index);
}
self.expr(ctx, index);
self.expr(ctx, value);
ctx.emit(Instr::StoreObjectIndexedProperty(
*object,
*property | if object_index.is_some() { 0x8000 } else { 0 },
));
}
HStmtKind::SetDynamicObjectProperty {
object,
property,
@@ -275,13 +327,21 @@ impl Codegen {
}
HStmtKind::ObjectMethod {
object,
index,
method,
args,
} => {
if let Some(index) = index {
self.expr(ctx, index);
}
for arg in args {
self.expr(ctx, arg);
}
ctx.emit(Instr::ObjectMethod(*object, *method, args.len() as u8));
ctx.emit(Instr::ObjectMethod(
*object,
*method,
args.len() as u8 | if index.is_some() { 0x80 } else { 0 },
));
}
HStmtKind::ObjectLoad {
object,
@@ -509,6 +569,18 @@ impl Codegen {
None => ctx.emit(Instr::RetGosub),
Some(l) => ctx.emit_jump(Instr::RetGosubTo(0), *l),
},
HStmtKind::Run { target, string } => {
if let Some(target) = target {
self.expr(ctx, target);
}
ctx.emit(Instr::Run(if target.is_none() {
0
} else if *string {
2
} else {
1
}));
}
HStmtKind::ExitProc => self.emit_proc_exit(ctx, proc),
HStmtKind::CallSub { proc: id, args } => {
for a in args {
@@ -533,6 +605,9 @@ impl Codegen {
}
let id = builtin_id(*b);
ctx.emit(Instr::CallBuiltin(id, args.len() as u8));
if matches!(b, Builtin::MsgBox) {
ctx.emit(Instr::Pop);
}
if builtin_returns_value(*b) {
ctx.emit(Instr::Pop);
}
@@ -725,6 +800,41 @@ impl Codegen {
index.is_some(),
));
}
HExpr::ObjectIndexedProperty {
object,
object_index,
property,
index,
..
} => {
if let Some(object_index) = object_index {
self.expr(ctx, object_index);
}
self.expr(ctx, index);
ctx.emit(Instr::LoadObjectIndexedProperty(
*object,
*property | if object_index.is_some() { 0x8000 } else { 0 },
));
}
HExpr::ObjectMethodCall {
object,
index,
method,
args,
..
} => {
if let Some(index) = index {
self.expr(ctx, index);
}
for arg in args {
self.expr(ctx, arg);
}
ctx.emit(Instr::ObjectMethodFn(
*object,
*method,
args.len() as u8 | if index.is_some() { 0x80 } else { 0 },
));
}
HExpr::DynamicObjectProperty { object, property } => {
self.expr(ctx, object);
let property = self.pool(property);
@@ -1173,6 +1283,9 @@ fn builtin_id(b: Builtin) -> u16 {
Builtin::Width => ids::WIDTH,
Builtin::ViewPrint => ids::VIEW_PRINT,
Builtin::ScreenStmt => ids::SCREEN_STMT,
Builtin::GraphicsLine => ids::GRAPHICS_LINE,
Builtin::GraphicsPaint => ids::GRAPHICS_PAINT,
Builtin::GraphicsView => ids::GRAPHICS_VIEW,
Builtin::KeyAssign => ids::KEY_ASSIGN,
Builtin::KeyList => ids::KEY_LIST,
Builtin::KeyDisplay => ids::KEY_DISPLAY,
@@ -1277,6 +1390,10 @@ fn builtin_id(b: Builtin) -> u16 {
Builtin::IsamSavepoint => ids::ISAM_SAVEPOINT,
Builtin::IsamSetmem => ids::ISAM_SETMEM,
Builtin::IsamBof => ids::ISAM_BOF,
Builtin::MsgBox => ids::MSGBOX,
Builtin::InputBoxS => ids::INPUTBOX_S,
Builtin::ClipboardAdd => ids::CLIPBOARD_ADD,
Builtin::ClipboardGet => ids::CLIPBOARD_GET,
}
}