feat: CLI 支持 REPL 编译到字节码
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user