Phase 4: Formularmodell und Objektsprache

This commit is contained in:
2026-09-04 17:30:32 +02:00
parent 5cf5a6582c
commit 54488b5b67
35 changed files with 7195 additions and 627 deletions

View File

@@ -43,9 +43,14 @@ pub fn compile(hir: &HirModule) -> CompiledModule {
data: hir
.data
.iter()
.map(|d| DataItem { text: d.text.clone(), line: d.line })
.map(|d| DataItem {
text: d.text.clone(),
line: d.line,
})
.collect(),
jump_tables: cg.jump_tables,
objects: hir.objects.clone(),
event_procs: hir.event_procs.clone(),
}
}
@@ -68,6 +73,7 @@ fn type_init(t: &HTy) -> TypeInit {
HTy::Str => TypeInit::Str,
HTy::FixedStr(n) => TypeInit::FixedStr(*n),
HTy::Udt(id) => TypeInit::Udt(*id),
HTy::Form | HTy::Control => TypeInit::Empty,
}
}
@@ -169,8 +175,8 @@ impl Codegen {
for (idx, label) in std::mem::take(&mut ctx.fixups) {
// Modulweites `ON ERROR GOTO` in einer Prozedur: das Label lebt
// im Modulrumpf, nicht im eigenen.
let modulweit = proc.kind != hir::HProcKind::Main
&& matches!(ctx.code[idx], Instr::OnErrorGoto(_));
let modulweit =
proc.kind != hir::HProcKind::Main && matches!(ctx.code[idx], Instr::OnErrorGoto(_));
let pc = if modulweit {
self.modul_label_pc
.get(label as usize)
@@ -241,6 +247,52 @@ impl Codegen {
HStmtKind::Assign { place, value } => {
self.store_place(ctx, place, |cg, ctx| cg.expr(ctx, value));
}
HStmtKind::SetObjectProperty {
object,
index,
property,
value,
} => {
if let Some(index) = index {
self.expr(ctx, index);
}
self.expr(ctx, value);
ctx.emit(Instr::StoreObjectProperty(
*object,
*property,
index.is_some(),
));
}
HStmtKind::SetDynamicObjectProperty {
object,
property,
value,
} => {
self.expr(ctx, object);
self.expr(ctx, value);
let property = self.pool(property);
ctx.emit(Instr::StoreDynamicObjectProperty(property));
}
HStmtKind::ObjectMethod {
object,
method,
args,
} => {
for arg in args {
self.expr(ctx, arg);
}
ctx.emit(Instr::ObjectMethod(*object, *method, args.len() as u8));
}
HStmtKind::ObjectLoad {
object,
index,
unload,
} => {
if let Some(index) = index {
self.expr(ctx, index);
}
ctx.emit(Instr::ObjectLoad(*object, *unload, index.is_some()));
}
HStmtKind::Print { items, trailing } => {
for item in items {
match item {
@@ -277,12 +329,21 @@ impl Codegen {
}
ctx.emit(Instr::Field(fields.len() as u8));
}
HStmtKind::LsetRset { rset, target, value } => {
HStmtKind::LsetRset {
rset,
target,
value,
} => {
self.make_ref(ctx, target);
self.expr(ctx, value);
ctx.emit(Instr::LsetRset(*rset));
}
HStmtKind::GetPut { put, file, recnum, var } => {
HStmtKind::GetPut {
put,
file,
recnum,
var,
} => {
self.expr(ctx, file);
if let Some(r) = recnum {
self.expr(ctx, r);
@@ -301,12 +362,19 @@ impl Codegen {
HTy::Udt(i) => (7, *i),
// Variable Strings: Länge erst zur Laufzeit.
HTy::Str => (8, 0),
HTy::Form | HTy::Control => (8, 0),
}
}
};
ctx.emit(Instr::GetPut(*put, recnum.is_some(), art, zusatz));
}
HStmtKind::Input { file, line_mode, prompt, question, targets } => {
HStmtKind::Input {
file,
line_mode,
prompt,
question,
targets,
} => {
if let Some(f) = file {
// Dateinummer zuerst, dann die Referenzen darüber.
self.expr(ctx, f);
@@ -506,7 +574,12 @@ impl Codegen {
}
}
HStmtKind::Restore(idx) => ctx.emit(Instr::Restore(*idx)),
HStmtKind::Dim { slot, elem, dims, redim } => {
HStmtKind::Dim {
slot,
elem,
dims,
redim,
} => {
for (lo, hi) in dims {
self.expr(ctx, lo);
self.expr(ctx, hi);
@@ -637,6 +710,36 @@ impl Codegen {
ctx.emit(Instr::PushStr(idx));
}
HExpr::Load(p) => self.load_place(ctx, p),
HExpr::ObjectProperty {
object,
index,
property,
..
} => {
if let Some(index) = index {
self.expr(ctx, index);
}
ctx.emit(Instr::LoadObjectProperty(
*object,
*property,
index.is_some(),
));
}
HExpr::DynamicObjectProperty { object, property } => {
self.expr(ctx, object);
let property = self.pool(property);
ctx.emit(Instr::LoadDynamicObjectProperty(property));
}
HExpr::ObjectRef { object, index, .. } => {
if let Some(index) = index {
self.expr(ctx, index);
}
ctx.emit(Instr::PushObject(*object, index.is_some()));
}
HExpr::TypeOf { value, class } => {
self.expr(ctx, value);
ctx.emit(Instr::TypeOf(class.id()));
}
HExpr::Conv { from, to, arg } => {
self.expr(ctx, arg);
emit_conv(ctx, *from, *to);
@@ -1276,7 +1379,12 @@ mod tests {
let m = compile_src("SUB Inc (x%)\nx% = x% + 1\nEND SUB\nn% = 1\nInc n%\nInc (n%)");
let code = main_code(&m);
assert!(code.contains(&Instr::MakeRefGlobal(0)));
assert!(code.iter().filter(|i| matches!(i, Instr::Call(1, 1))).count() == 2);
assert!(
code.iter()
.filter(|i| matches!(i, Instr::Call(1, 1)))
.count()
== 2
);
// Prozedurrumpf liest/schreibt über Referenz
let sub = &m.procs[1].code;
assert!(sub.contains(&Instr::LoadRef(0)));
@@ -1309,7 +1417,12 @@ mod tests {
let m = compile_src("DATA 1, 2\nREAD a%, b%\nRESTORE\nREAD c%");
let code = main_code(&m);
assert_eq!(m.data.len(), 2);
assert!(code.iter().filter(|i| matches!(i, Instr::ReadData(1))).count() == 3);
assert!(
code.iter()
.filter(|i| matches!(i, Instr::ReadData(1)))
.count()
== 3
);
assert!(code.contains(&Instr::Restore(0)));
assert!(code.contains(&Instr::ConvR8I2));
}
@@ -1318,7 +1431,9 @@ mod tests {
fn fehlerbehandlung_emit() {
let m = compile_src("ON ERROR GOTO H\nERROR 5\nEND\nH:\nRESUME NEXT");
let code = main_code(&m);
assert!(code.iter().any(|i| matches!(i, Instr::OnErrorGoto(t) if *t > 0)));
assert!(code
.iter()
.any(|i| matches!(i, Instr::OnErrorGoto(t) if *t > 0)));
assert!(code.contains(&Instr::RaiseError));
assert!(code.contains(&Instr::ResumeNext));
}