diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..66f6afe --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,101 @@ +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +use crate::{FileTree, Runtime}; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +#[command(propagate_version = true)] +struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Subcommand)] +enum Command { + /// Generate documentation for the runtime + Doc, + /// Type check a script + Check { + #[arg()] + file: PathBuf, + }, + /// Test a script + Test { + #[arg()] + file: PathBuf, + }, + /// Run a script + Run { + #[arg()] + file: PathBuf, + }, +} + +/// Run a basic CLI for a given runtime +/// +/// This is useful for providing users to check their scripts or run their tests +/// with the runtime the host application provides. +/// +/// This CLI provides the following subcommands: +/// - `doc`: generate documentation +/// - `check`: type check a script +/// - `test`: run tests for a script +/// - `run`: run the main function of a script +pub fn cli(rt: Runtime) { + match cli_inner(rt) { + Ok(()) => std::process::exit(0), + Err(err) => { + eprintln!("{err}"); + std::process::exit(1); + } + } +} + +fn cli_inner(rt: Runtime) -> Result<(), String> { + let cli = Cli::parse(); + + match &cli.command { + Command::Doc => { + rt.print_documentation(); + } + Command::Check { file } => { + FileTree::read(file) + .parse() + .map_err(|r| r.to_string())? + .typecheck(rt, usize::BITS / 8) + .map_err(|r| r.to_string())?; + println!("All ok!") + } + Command::Test { file } => { + let mut p = FileTree::read(file) + .parse() + .map_err(|r| r.to_string())? + .typecheck(rt, usize::BITS / 8) + .map_err(|r| r.to_string())? + .lower() + .codegen(); + + if let Err(()) = p.run_tests(()) { + return Err("tests failed".into()); + } + } + Command::Run { file } => { + let mut p = FileTree::read(file) + .parse() + .map_err(|r| r.to_string())? + .typecheck(rt, usize::BITS / 8) + .map_err(|r| r.to_string())? + .lower() + .codegen(); + + let f = p + .get_function::<(), (), ()>("main") + .map_err(|e| e.to_string())?; + + f.call(&mut ()) + } + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index b1c99e4..ae06fb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate self as roto; mod ast; +mod cli; mod codegen; mod file_tree; mod lower; @@ -12,6 +13,7 @@ mod pipeline; mod runtime; mod typechecker; +pub use cli::cli; pub use codegen::TypedFunc; pub use file_tree::{FileTree, SourceFile}; pub use lower::eval::Memory; diff --git a/src/main.rs b/src/main.rs index 5d99013..1fd2858 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,5 @@ -use std::path::Path; - use clap::Parser; -use roto::{IrValue, Memory, Runtime}; +use roto::{cli, Runtime}; #[derive(Parser)] struct Cli { @@ -16,27 +14,5 @@ fn main() { .format_target(false) .init(); - let settings = Cli::parse(); - - let args = match settings.rx.as_ref().map(AsRef::as_ref) { - Some("true") => vec![IrValue::Bool(true)], - Some("false") => vec![IrValue::Bool(false)], - Some(x) => vec![IrValue::U32(x.parse().unwrap())], - _ => vec![], - }; - - let mut mem = Memory::new(); - - let ptr = mem.allocate(0); - let result = roto::interpret( - Runtime::new(), - Path::new(&settings.file), - &mut mem, - IrValue::Pointer(ptr), - args, - ); - match result { - Ok(_) => println!("Ok!"), - Err(e) => eprintln!("{e}"), - } + cli(Runtime::new()); }