Phase 5: Ausführung und Output implementieren und archivieren

This commit is contained in:
2026-09-06 18:56:47 +02:00
parent 9c85349a4d
commit e3dc9028bf
37 changed files with 2404 additions and 450 deletions

View File

@@ -15,7 +15,7 @@ use std::process::ExitCode;
use tb_runtime::host::{Ereignis, Host};
use tb_ui::host::TerminalHost;
use tb_vm::interp::{RunEvent, Vm};
use tb_vm::project_io::{module_name, relative_case_insensitive, SourceLoader};
use tb_vm::project_io::{module_name, new_execution, run_target, SourceLoader};
fn main() -> ExitCode {
let args: Vec<String> = std::env::args().skip(1).collect();
@@ -105,12 +105,7 @@ fn compile(
return Err(ExitCode::from(1));
}
};
let mut catalog = tb_frontend::forms::FormCatalog::default();
for form in &input.forms {
catalog.append(&form.catalog());
}
let compiled =
tb_vm::compile_project(&module_name(&path), &input.units, &catalog, &input.forms);
let compiled = input.compile(&mut Default::default(), &module_name(&path));
match compiled {
Ok(m) => Ok((path, m)),
Err(diags) => {
@@ -204,20 +199,6 @@ fn cmd_run(args: &[String]) -> ExitCode {
}
}
fn run_target(current: &Path, program: &str) -> Result<PathBuf, String> {
let base = current.parent().unwrap_or(Path::new("."));
if Path::new(program).extension().is_some() {
return relative_case_insensitive(base, program).map_err(|error| error.to_string());
}
for extension in ["bas", "frm", "mak", "tbc"] {
let candidate = format!("{program}.{extension}");
if let Ok(path) = relative_case_insensitive(base, &candidate) {
return Ok(path);
}
}
Err(format!("{program}: Programm nicht gefunden"))
}
fn run_chain(
args: &[String],
host: &mut dyn Host,
@@ -233,27 +214,20 @@ fn run_chain(
loop {
let current_arg = current.display().to_string();
let (path, module) = compile(Some(&current_arg))?;
let mut vm = Vm::new(module);
if let Some(line) = start_line.take() {
if let Err(error) = vm.start_at_line(line) {
eprintln!("Runtime error {}: {}", error.0, error);
return Err(ExitCode::from(2));
}
}
if let Some((cols, rows)) = size {
vm.rt.screen.resize(cols, rows);
}
vm.rt.command.clone_from(&command);
vm.set_poll_interrupt(true);
let mut vm = new_execution(module, &command, size, start_line.take()).map_err(|error| {
eprintln!("Runtime error {}: {}", error.0, error);
ExitCode::from(2)
})?;
if !vm.rt.zeitpunkt().1 {
eprintln!("Zeitzone nicht ermittelbar — Zeitfunktionen rechnen in UTC.");
}
let event = vm.run(host);
let event = if event == RunEvent::Ended && vm.forms.has_visible_forms() {
vm.run_visible_forms(host)
} else {
event
};
let event =
if event == RunEvent::Ended && vm.forms.has_visible_forms() && !vm.is_terminated() {
vm.run_visible_forms(host)
} else {
event
};
match event {
RunEvent::Restart { program, line } => {
if let Some(program) = program {

View File

@@ -183,3 +183,54 @@ fn erlaubte_grafik_und_benannte_nonfeatures_werden_per_cli_geprueft() {
assert!(out.status.success(), "{out:?}");
std::fs::remove_dir_all(dir).unwrap();
}
#[test]
fn explicit_startup_order_and_form_selection_survive_tbc() {
let dir = std::env::temp_dir().join(format!("tb-startup-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("a.bas"), "PRINT \"A\"\n").unwrap();
std::fs::write(dir.join("b.bas"), "PRINT \"B\"\n").unwrap();
for (file, name) in [("one.frm", "One"), ("two.frm", "Two")] {
std::fs::write(
dir.join(file),
format!("VERSION 1.00\nBEGIN Form {name}\nEND\n"),
)
.unwrap();
}
for startup in [None, Some("b.bas"), Some("two.frm")] {
std::fs::write(
dir.join("p.mak"),
format!(
"a.bas\nb.bas\none.frm\ntwo.frm\n{}",
startup
.map(|s| format!("' $STARTUP: \"{s}\"\n"))
.unwrap_or_default()
),
)
.unwrap();
let out = tbc(&dir, "build", "p.mak");
assert!(out.status.success(), "{out:?}");
let m =
tb_vm::bytecode::CompiledModule::from_tbc(&std::fs::read(dir.join("p.tbc")).unwrap())
.unwrap();
let chosen = m
.startup_form
.map(|i| m.objects[i as usize].name.to_ascii_uppercase());
assert_eq!(
chosen.as_deref(),
match startup {
None => Some("ONE"),
Some("two.frm") => Some("TWO"),
_ => None,
}
);
if startup == Some("b.bas") {
let direct = tbc(&dir, "run", "p.mak");
let serialized = tbc(&dir, "run", "p.tbc");
assert!(direct.status.success() && serialized.status.success());
assert_eq!(direct.stdout, b"B\nA\n");
assert_eq!(direct.stdout, serialized.stdout);
}
}
std::fs::remove_dir_all(dir).unwrap();
}