feat: 逐指令 trace 补 pc 并加 eval/run --trace 旗标

This commit is contained in:
2026-09-26 09:40:37 +00:00
parent 34b0f93aa7
commit 65ef05bcc4
5 changed files with 66 additions and 9 deletions
+14 -7
View File
@@ -46,11 +46,17 @@ struct Cli {
enum Commands {
Eval {
code: String,
/// 逐指令 trace:每条指令向 stderr 写一行 pc + opcode + 操作数。
#[arg(long)]
trace: bool,
},
Run {
file: String,
#[arg(long, default_value = "1")]
repeat: u64,
/// 逐指令 trace:每条指令向 stderr 写一行 pc + opcode + 操作数。
#[arg(long)]
trace: bool,
},
Compile {
#[arg(short = 'e')]
@@ -87,16 +93,16 @@ fn main() -> ExitCode {
let cli = Cli::parse();
match cli.command {
Some(Commands::Eval { code }) => {
Some(Commands::Eval { code, trace }) => {
let kernel = make_kernel(cli.verbose, cli.quiet);
let pool = make_pool(&kernel);
eval(&code, &kernel, &pool)
eval(&code, &kernel, &pool, trace)
}
Some(Commands::Run { file, repeat }) => {
Some(Commands::Run { file, repeat, trace }) => {
let kernel = make_kernel(cli.verbose, cli.quiet);
let pool = make_pool(&kernel);
for n in 0..repeat {
let code = run(&file, &kernel, &pool);
let code = run(&file, &kernel, &pool, trace);
if code != ExitCode::SUCCESS && code != ExitCode::FAILURE {
return code;
}
@@ -155,7 +161,7 @@ fn make_pool(kernel: &Arc<KernelCore>) -> Arc<VmPool> {
VmPool::new(Arc::clone(kernel), kernel.config.min_pool_size, kernel.config.max_pool_size)
}
fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool) -> ExitCode {
let allocator = Allocator::default();
let program = match oxide_parser::parse(&allocator, code) {
Ok(p) => p,
@@ -180,6 +186,7 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
};
let mut guard = pool.spawn();
guard.vm_mut().set_instruction_trace(trace);
match guard.vm_mut().run(&module) {
Ok(result) => {
format_result(guard.vm(), kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
@@ -284,9 +291,9 @@ fn format_array(
format!("[{}]", items.join(", "))
}
fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool) -> ExitCode {
match fs::read_to_string(file) {
Ok(source) => eval(&source, kernel, pool),
Ok(source) => eval(&source, kernel, pool, trace),
Err(err) => {
kernel_error!("cannot read {}: {}", file, err);
eprintln!("{}", Red.paint(format!("Cannot read {file}: {err}")));
+31
View File
@@ -47,3 +47,34 @@ fn bench_help() {
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("--iterations"), "bench --help should list --iterations flag");
}
#[test]
fn eval_trace_flag_pc_lines() {
let output = Command::new(env!("CARGO_BIN_EXE_oxide"))
.args(["eval", "--trace", "1 + 2"])
.output()
.expect("failed to run oxide eval --trace");
assert!(output.status.success(), "eval --trace should exit 0");
// trace 行只走 stderr,stdout 保持纯结果。
let stdout = String::from_utf8_lossy(&output.stdout);
assert_eq!(stdout.trim(), "3", "--trace 不应污染 stdout: {stdout}");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.lines().all(|l| l.starts_with("pc=")), "trace 行应全部为 pc 行: {stderr}");
assert!(stderr.contains("LOAD_CONST"), "trace 应含 LOAD_CONST: {stderr}");
assert!(stderr.contains("ADD"), "trace 应含 ADD: {stderr}");
assert!(stderr.contains("HALT"), "trace 应含 HALT: {stderr}");
}
#[test]
fn eval_no_trace_flag_no_pc_lines() {
let output = Command::new(env!("CARGO_BIN_EXE_oxide"))
.args(["eval", "1 + 2"])
.output()
.expect("failed to run oxide eval");
assert!(output.status.success(), "eval should exit 0");
// 默认路径零 trace 输出。
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!stderr.contains("pc="), "默认路径不应有 trace 行: {stderr}");
}