use std::{path::PathBuf, io::{self, Read, IsTerminal}, cell::RefCell, rc::Rc}; use clap::{Parser as ArgParser, ColorChoice}; use matrix_lang::prelude::*; use repl::Repl; mod repl; mod helper; #[derive(Debug, ArgParser)] #[command(version, long_about = None)] pub struct Args { /// A path to a input program. Uses stdin if not specified. file: Option, /// Compiles the given program #[arg(short, long)] compile: bool, /// Optional output for compiled output #[arg(short, long)] output: Option, /// Print out debug information #[arg(short, long)] debug: bool, /// Choses color #[arg(long)] color: Option, /// Disables optimizations #[arg(long)] disable_optimizations: bool, } pub enum Mode { Repl, Execute(Vec), Compile(Vec, PathBuf), } pub struct State<'a> { parser: Parser, compiler: Compiler<'a>, vm: Rc>, color: bool, } pub fn error(err: Exception, state: &State) { if state.color { println!("\x1b[31mError:\x1b[0m {err}"); } else { println!("Error: {err}"); } } impl<'a> State<'a> { pub fn new (args: Args) -> Result<(Self, Mode)> { let mut buffer = Vec::new(); if let Some(path) = &args.file { let mut f = File::open(path)?; f.read_to_end(&mut buffer)?; } else { let mut stdin = io::stdin(); if !stdin.is_terminal() { stdin.read_to_end(&mut buffer)?; } } let mode; let repl; if args.compile { let path = match (args.output, args.file) { (Some(path), _) => path, (None, Some(path)) => { let mut path = path.clone(); path.set_extension("matc"); path }, (None, None) => { PathBuf::from("matc.out") } }; mode = Mode::Compile(buffer, path); repl = false; } else if buffer.len() > 0 { mode = Mode::Execute(buffer); repl = false; } else { mode = Mode::Repl; repl = true; } let mut vm = Vm::new(); let parser = ParserBuilder::new() .optimize(!args.disable_optimizations) .build(); let compiler = CompilerBuilder::new() .repl(repl) .debug(args.debug) .names(vm.names()) .globals(vm.globals()) .build(); matrix_std::load(&mut vm); let color = match args.color { Some(ColorChoice::Auto) | None => { io::stdout().is_terminal() }, Some(ColorChoice::Always) => true, Some(ColorChoice::Never) => false, }; Ok((Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, color, }, mode)) } pub fn execute(&mut self, fun: Rc) -> Result { let val = self.vm.borrow_mut().run(fun)?; Ok(val) } pub fn compile(&mut self, code: String) -> Result> { let ast = self.parser.parse(code)?; let fun = self.compiler.compile(&ast)?; Ok(fun) } pub fn load_program(&mut self, mut buffer: Vec) -> Result> { let res = Program::load(buffer.as_mut_slice())?; match res { Some(fun) => { Ok(fun) }, None => { let body = buffer_to_string(buffer)?; self.compile(body) }, } } } fn buffer_to_string(buffer: Vec) -> Result { String::from_utf8(buffer) .map_err(|e| exception!(IO_EXCEPTION, "{e}")) } fn handle_mode(state: &mut State, mode: Mode) -> Result<()> { match mode { Mode::Repl => { let mut repl = Repl::new(state); repl.run()?; }, Mode::Execute(buffer) => { let fun = state.load_program(buffer)?; state.execute(fun)?; }, Mode::Compile(buffer, path) => { let body = buffer_to_string(buffer)?; let fun = state.compile(body)?; let mut file = File::create(path).map_err(|e| exception!(IO_EXCEPTION, "{e}") )?; Program::save(fun, &mut file)?; } }; Ok(()) } fn load() -> Result<()> { let args = Args::parse(); let (mut state, mode) = State::new(args)?; if let Err(e) = handle_mode(&mut state, mode) { error(e, &state); } Ok(()) } fn main() { if let Err(e) = load() { println!("\x1b[31mFatal:\x1b[0m {e}"); } }