feat: CLI 支持 REPL 编译到字节码

This commit is contained in:
爱特每羊队
2026-06-05 03:02:52 +08:00
parent 160e972f77
commit b65a0678f2
2 changed files with 52 additions and 21 deletions
+48 -17
View File
@@ -21,11 +21,23 @@ struct Cli {
#[derive(Subcommand)] #[derive(Subcommand)]
enum Commands { enum Commands {
Eval { code: String }, Eval {
Run { file: String }, code: String,
Compile { file: String }, },
Bench { suite: Option<String> }, Run {
Test { suite: Option<String> }, file: String,
},
Compile {
#[arg(short = 'e')]
expr: Option<String>,
file: Option<String>,
},
Bench {
suite: Option<String>,
},
Test {
suite: Option<String>,
},
} }
fn main() -> ExitCode { fn main() -> ExitCode {
@@ -34,7 +46,7 @@ fn main() -> ExitCode {
match cli.command { match cli.command {
Some(Commands::Eval { code }) => eval(&code), Some(Commands::Eval { code }) => eval(&code),
Some(Commands::Run { file }) => run(&file), Some(Commands::Run { file }) => run(&file),
Some(Commands::Compile { file }) => compile(&file), Some(Commands::Compile { expr, file }) => compile(expr, file),
Some(Commands::Bench { .. }) => not_implemented("bench"), Some(Commands::Bench { .. }) => not_implemented("bench"),
Some(Commands::Test { .. }) => not_implemented("test"), Some(Commands::Test { .. }) => not_implemented("test"),
None => repl(), None => repl(),
@@ -43,15 +55,24 @@ fn main() -> ExitCode {
fn eval(code: &str) -> ExitCode { fn eval(code: &str) -> ExitCode {
let allocator = Allocator::default(); let allocator = Allocator::default();
match oxide_parser::parse(&allocator, code) { let program = match oxide_parser::parse(&allocator, code) {
Ok(program) => { Ok(p) => p,
println!("{program:#?}");
ExitCode::SUCCESS
}
Err(errors) => { Err(errors) => {
for err in &errors { for err in &errors {
eprintln!("{}", Red.paint(err.to_string())); eprintln!("{}", Red.paint(err.to_string()));
} }
return ExitCode::FAILURE;
}
};
let compiler = Compiler::new();
match compiler.compile(&program) {
Ok(module) => {
print!("{module}");
ExitCode::SUCCESS
}
Err(err) => {
eprintln!("{}", Red.paint(err));
ExitCode::FAILURE ExitCode::FAILURE
} }
} }
@@ -67,13 +88,23 @@ fn run(file: &str) -> ExitCode {
} }
} }
fn compile(file: &str) -> ExitCode { fn compile(expr: Option<String>, file: Option<String>) -> ExitCode {
let source = match fs::read_to_string(file) { let source = if let Some(code) = expr {
Ok(s) => s, code
Err(err) => { } else if let Some(path) = file {
eprintln!("{}", Red.paint(format!("Cannot read {file}: {err}"))); match fs::read_to_string(&path) {
return ExitCode::FAILURE; Ok(s) => s,
Err(err) => {
eprintln!("{}", Red.paint(format!("Cannot read {path}: {err}")));
return ExitCode::FAILURE;
}
} }
} else {
eprintln!(
"{}",
Red.paint("compile requires -e '<code>' or a file path")
);
return ExitCode::FAILURE;
}; };
let allocator = Allocator::default(); let allocator = Allocator::default();
+4 -4
View File
@@ -10,8 +10,8 @@ fn eval_simple_expression() {
assert!(output.status.success(), "eval 1+2 should exit 0"); assert!(output.status.success(), "eval 1+2 should exit 0");
let stdout = String::from_utf8_lossy(&output.stdout); let stdout = String::from_utf8_lossy(&output.stdout);
assert!( assert!(
stdout.contains("Program"), stdout.contains("HALT"),
"output should contain AST node: {stdout}" "output should contain HALT bytecode: {stdout}"
); );
} }
@@ -44,8 +44,8 @@ fn run_file() {
assert!(output.status.success(), "run hello.js should exit 0"); assert!(output.status.success(), "run hello.js should exit 0");
let stdout = String::from_utf8_lossy(&output.stdout); let stdout = String::from_utf8_lossy(&output.stdout);
assert!( assert!(
stdout.contains("Program"), stdout.contains("HALT"),
"run output should contain AST node" "run output should contain bytecode: {stdout}"
); );
} }