Phase 4: Formulardateien und Konvertierung
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
//! - `tbc run <datei.bas>` Kompilieren und sofort ausführen
|
||||
//! - `tbc build <datei.bas>` Kompilieren zu `datei.tbc`
|
||||
//! - `tbc check <datei.bas>` Nur Syntax-/Semantikprüfung
|
||||
//! - `tbc convert-frm <quelle.frm> <ziel.frm>` Binärformular in Text wandeln
|
||||
//!
|
||||
//! Exit-Codes von `run` (Entscheidung D6, docs/tbvm-design.md):
|
||||
//! 0 = END/SYSTEM/Programmende · 3 = STOP · 2 = Laufzeitfehler ·
|
||||
@@ -21,20 +22,68 @@ fn main() -> ExitCode {
|
||||
Some("run") => cmd_run(&args[1..]),
|
||||
Some("build") => cmd_build(&args[1..]),
|
||||
Some("check") => cmd_check(&args[1..]),
|
||||
Some("convert-frm") => cmd_convert_frm(&args[1..]),
|
||||
_ => {
|
||||
eprintln!("Aufruf: tbc run|build|check <datei.bas>");
|
||||
eprintln!(
|
||||
"Aufruf: tbc run|build|check <datei.bas> | tbc convert-frm <quelle.frm> <ziel.frm>"
|
||||
);
|
||||
ExitCode::from(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_convert_frm(args: &[String]) -> ExitCode {
|
||||
if args.len() != 2 {
|
||||
eprintln!("Aufruf: tbc convert-frm <quelle.frm> <ziel.frm>");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
let input = Path::new(&args[0]);
|
||||
let output = Path::new(&args[1]);
|
||||
let bytes = match std::fs::read(input) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(error) => {
|
||||
eprintln!("{}: {error}", input.display());
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
};
|
||||
let converted = match tb_ui::frm::read_binary(&input.display().to_string(), &bytes) {
|
||||
Ok(converted) => converted,
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
};
|
||||
let text = tb_ui::frm::write_text(&converted.form);
|
||||
if let Err(error) = std::fs::write(output, text) {
|
||||
eprintln!("{}: {error}", output.display());
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
for warning in &converted.skipped {
|
||||
eprintln!(
|
||||
"{}: Byte 0x{:04x}: {} nicht übernommen",
|
||||
input.display(),
|
||||
warning.offset,
|
||||
warning.name
|
||||
);
|
||||
}
|
||||
eprintln!(
|
||||
"{}: {} nicht übernommene Binärangaben",
|
||||
input.display(),
|
||||
converted.skipped.len()
|
||||
);
|
||||
println!("{}", output.display());
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
fn module_name(path: &Path) -> String {
|
||||
path.file_stem()
|
||||
.map(|s| s.to_string_lossy().to_uppercase())
|
||||
.unwrap_or_else(|| "MODUL".into())
|
||||
}
|
||||
|
||||
fn compile(path_arg: Option<&String>) -> Result<(PathBuf, tb_vm::bytecode::CompiledModule), ExitCode> {
|
||||
fn compile(
|
||||
path_arg: Option<&String>,
|
||||
) -> Result<(PathBuf, tb_vm::bytecode::CompiledModule), ExitCode> {
|
||||
let Some(path) = path_arg else {
|
||||
eprintln!("Aufruf: tbc run|build|check <datei.bas>");
|
||||
return Err(ExitCode::from(1));
|
||||
@@ -133,7 +182,11 @@ fn cmd_run(args: &[String]) -> ExitCode {
|
||||
eprintln!("STOP in line {line}");
|
||||
ExitCode::from(3)
|
||||
}
|
||||
RunEvent::Error { code, line, message } => {
|
||||
RunEvent::Error {
|
||||
code,
|
||||
line,
|
||||
message,
|
||||
} => {
|
||||
eprintln!("Runtime error {code}: {message} in line {line}");
|
||||
ExitCode::from(2)
|
||||
}
|
||||
|
||||
24
crates/tb-cli/tests/convert_frm.rs
Normal file
24
crates/tb-cli/tests/convert_frm.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use std::process::Command;
|
||||
|
||||
#[test]
|
||||
fn conversion_failure_leaves_no_output() {
|
||||
let dir = std::env::temp_dir().join(format!("tbc-convert-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let input = dir.join("broken.frm");
|
||||
let output = dir.join("converted.frm");
|
||||
std::fs::write(&input, b"not a form").unwrap();
|
||||
|
||||
let result = Command::new(env!("CARGO_BIN_EXE_tbc"))
|
||||
.args([
|
||||
"convert-frm",
|
||||
input.to_str().unwrap(),
|
||||
output.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(!result.status.success());
|
||||
assert!(!output.exists());
|
||||
assert!(String::from_utf8_lossy(&result.stderr).contains("0x0000"));
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
Reference in New Issue
Block a user