Projektmodule und vollständiges TBC-Kompilat umsetzen und Change archivieren

This commit is contained in:
2026-09-05 23:06:56 +02:00
parent 58b1f620ea
commit 815825dde7
40 changed files with 4177 additions and 555 deletions

View File

@@ -16,6 +16,12 @@ use tb_runtime::value::TypeInit;
pub fn compile(hir: &HirModule) -> CompiledModule {
let mut cg = Codegen {
common_arrays: hir
.commons
.iter()
.filter(|c| c.dims.is_some())
.map(|c| c.slot)
.collect(),
strings: Vec::new(),
string_ids: HashMap::new(),
jump_tables: Vec::new(),
@@ -30,6 +36,13 @@ pub fn compile(hir: &HirModule) -> CompiledModule {
procs.push(cg.compile_proc(proc));
}
CompiledModule {
modules: vec![(hir.name.clone(), hir.option_base)],
sources: vec![tb_frontend::source::SourceFile {
module: 0,
path: hir.name.clone(),
}],
form_initial: vec![],
startup_form: None,
name: hir.name.clone(),
option_base: hir.option_base,
strings: cg.strings,
@@ -67,7 +80,7 @@ fn slot_init(v: &hir::HVar) -> TypeInit {
}
}
fn type_init(t: &HTy) -> TypeInit {
pub(crate) fn type_init(t: &HTy) -> TypeInit {
match t {
HTy::Num(NumTy::Int) => TypeInit::Int,
HTy::Num(NumTy::Lng) => TypeInit::Lng,
@@ -82,6 +95,7 @@ fn type_init(t: &HTy) -> TypeInit {
}
struct Codegen {
common_arrays: std::collections::HashSet<u16>,
strings: Vec<Rc<str>>,
string_ids: HashMap<String, u16>,
jump_tables: Vec<Vec<u32>>,
@@ -95,6 +109,7 @@ struct Codegen {
}
struct ProcCtx {
pos: tb_frontend::SourcePos,
code: Vec<Instr>,
/// LabelId → Instruktionsindex.
label_pc: Vec<Option<u32>>,
@@ -134,6 +149,10 @@ impl ProcCtx {
self.label_pc[label as usize] = Some(pc);
}
fn emit(&mut self, i: Instr) {
if matches!(i, Instr::Stmt(_) | Instr::InitStmt(_)) {
self.code
.push(Instr::Source(self.pos.source, self.pos.column));
}
self.code.push(i);
}
/// Sprunginstruktion mit noch unbekanntem Ziel emittieren.
@@ -162,6 +181,7 @@ impl Codegen {
fn compile_proc(&mut self, proc: &hir::HProc) -> ProcCode {
let mut ctx = ProcCtx {
pos: tb_frontend::SourcePos::default(),
code: Vec::new(),
label_pc: vec![None; proc.label_count as usize],
fixups: Vec::new(),
@@ -238,6 +258,10 @@ impl Codegen {
}
ProcCode {
module: 0,
kind: proc.kind,
params: proc.params.clone(),
ret_ty: proc.ret_ty.clone(),
name: proc.name.clone(),
n_params: proc.params.len() as u16,
locals_init: proc.locals.iter().map(slot_init).collect(),
@@ -268,13 +292,14 @@ impl Codegen {
}
fn stmt_inner(&mut self, ctx: &mut ProcCtx, proc: &hir::HProc, stmt: &HStmt, boundary: bool) {
ctx.pos = stmt.pos;
match &stmt.kind {
HStmtKind::Label(l) => {
ctx.bind(*l);
return;
}
_ if boundary => ctx.emit(Instr::Stmt(stmt.line)),
_ => {}
_ => ctx.emit(Instr::InitStmt(stmt.line)),
}
match &stmt.kind {
HStmtKind::Label(_) => unreachable!(),
@@ -478,6 +503,7 @@ impl Codegen {
}
}
HStmtKind::Loop {
end_pos,
pre,
post,
body,
@@ -496,6 +522,8 @@ impl Codegen {
for s in body {
self.stmt(ctx, proc, s);
}
ctx.pos = *end_pos;
ctx.emit(Instr::Stmt(end_pos.line));
match post {
Some((is_until, cond)) => {
self.expr(ctx, cond);
@@ -511,6 +539,7 @@ impl Codegen {
ctx.bind(*exit_label);
}
HStmtKind::For {
end_pos,
var,
ty,
from,
@@ -533,7 +562,7 @@ impl Codegen {
*step_slot,
body,
*exit_label,
stmt.line,
*end_pos,
);
}
HStmtKind::Goto(l) => ctx.emit_jump(Instr::Jump(0), *l),
@@ -663,6 +692,8 @@ impl Codegen {
let init = type_init(elem);
if *redim {
ctx.emit(Instr::RedimArr(global, s, dims.len() as u8, init));
} else if global && self.common_arrays.contains(&s) {
ctx.emit(Instr::CommonArr(global, s, dims.len() as u8, init));
} else {
ctx.emit(Instr::DimArr(global, s, dims.len() as u8, init));
}
@@ -697,7 +728,7 @@ impl Codegen {
step_slot: Option<VarSlot>,
body: &[HStmt],
exit_label: u16,
line: u32,
end_pos: tb_frontend::SourcePos,
) {
// Startwert, Grenze, ggf. Schritt einmal auswerten.
self.store_place(ctx, var, |cg, ctx| cg.expr(ctx, from));
@@ -715,15 +746,6 @@ impl Codegen {
let l_test = ctx.new_label();
let l_body = ctx.new_label();
ctx.bind(l_test);
// Anweisungsgrenze für den Rücksprung von `NEXT`. Sie kann nicht
// auf die Grenze des `FOR` zeigen — dort stünde die Initialisierung
// noch einmal.
//
// ponytail: gemeldet wird die Zeile des `FOR`, nicht die des
// `NEXT` — die trägt das HIR nicht. Ceiling: braucht der Debugger
// in Phase 5 die genaue Zeile, bekommt `HStmtKind::For` ein Feld
// `next_line`.
ctx.emit(Instr::Stmt(line));
match const_step {
Some(s) => {
// Vergleichsrichtung zur Compilezeit.
@@ -757,7 +779,9 @@ impl Codegen {
for s in body {
self.stmt(ctx, proc, s);
}
// NEXT: inkrementieren, zurück zum Test.
// NEXT: eigene Quellgrenze für Fehler, RESUME und Debugger.
ctx.pos = end_pos;
ctx.emit(Instr::Stmt(end_pos.line));
self.store_place(ctx, var, |cg, ctx| {
cg.load_place(ctx, var);
match (step, step_slot) {
@@ -777,6 +801,7 @@ impl Codegen {
match e {
HExpr::Int(v) => ctx.emit(Instr::PushInt(*v)),
HExpr::Lng(v) => ctx.emit(Instr::PushLng(*v)),
HExpr::UdtId(id) => ctx.emit(Instr::PushUdtId(*id)),
HExpr::Sng(v) => ctx.emit(Instr::PushSng(*v)),
HExpr::Dbl(v) => ctx.emit(Instr::PushDbl(*v)),
HExpr::Cur(v) => ctx.emit(Instr::PushCur(*v)),