talc/talc-bin/src/main.rs
2024-12-21 00:55:45 -05:00

142 lines
2.7 KiB
Rust

use clap::{ColorChoice, Parser};
use std::{path::PathBuf, process::ExitCode, rc::Rc};
use talc_lang::{
compiler::compile,
lstring::LString,
optimize::optimize,
parser,
prelude::*,
serial,
symbol::Symbol,
value::{function::disasm_recursive, Value},
vm::Vm,
};
mod helper;
mod repl;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Start the repl
#[arg(short, long)]
repl: bool,
/// Show disassembled bytecode
#[arg(short, long)]
disasm: bool,
/// Show generated AST
#[arg(short = 'a', long)]
show_ast: bool,
/// Compile to a bytecode file
#[arg(short, long)]
compile: Option<PathBuf>,
/// Don't apply optimizations
#[arg(long)]
no_opt: bool,
/// Set history file
#[arg(short = 'H', long)]
histfile: Option<PathBuf>,
/// enable or disable color
#[arg(short, long, default_value = "auto")]
color: ColorChoice,
// file to run and arguments to pass
#[arg()]
args: Vec<LString>,
}
fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
let mut vm = Vm::new(256, args.args.clone());
talc_std::load_all(&mut vm);
let mut ex = match parser::parse(src) {
Ok(ex) => ex,
Err(e) => {
eprintln!("Error: {e}");
return ExitCode::FAILURE
}
};
if !args.no_opt {
optimize(&mut ex);
}
if args.show_ast {
eprintln!("{}", ex);
}
let func = match compile(&ex, Some(name)) {
Ok(f) => Rc::new(f),
Err(e) => {
eprintln!("Error: {e}");
return ExitCode::FAILURE
}
};
if args.disasm {
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
eprintln!("Error: {e}");
return ExitCode::FAILURE
}
}
if let Some(path) = &args.compile {
let f = std::fs::File::options()
.write(true)
.truncate(true)
.create(true)
.open(path);
let mut w = match f {
Ok(f) => f,
Err(e) => {
eprintln!("Error opening output file: {e}");
return ExitCode::FAILURE
}
};
if let Err(e) = serial::write_program(&mut w, &func) {
eprintln!("Error writing bytecode: {e}");
return ExitCode::FAILURE
}
return ExitCode::SUCCESS
}
let res = vm.run_function(func.clone(), vec![func.into()]);
match res {
Err(e) => {
eprintln!("{e}");
ExitCode::FAILURE
}
Ok(Value::Bool(false)) => ExitCode::FAILURE,
Ok(Value::Int(n)) => ExitCode::from(n.to_u64().unwrap_or(1) as u8),
_ => ExitCode::SUCCESS,
}
}
fn main() -> ExitCode {
let args = Args::parse();
if args.repl || args.args.is_empty() {
if args.compile.is_some() {
eprintln!("Error: cannot compile in REPL mode");
return ExitCode::FAILURE
}
return repl::repl(&args)
}
let file = args.args[0].as_ref();
match std::fs::read_to_string(file.to_os_str()) {
Ok(s) => exec(Symbol::get(file), &s, &args),
Err(e) => {
eprintln!("Error opening source file: {e}");
ExitCode::FAILURE
}
}
}