use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc}; use clap::{Parser as ArgParser, ColorChoice}; use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}, value::Value}; 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, /// Runs a repl, loading the provided program first #[arg(short, long)] repl: bool, /// Print out debug information #[arg(short, long)] debug: bool, /// Choses color #[arg(short, long)] color: Option, /// Disables optimizations #[arg(long)] disable_optimizations: bool, } pub struct State<'a> { parser: Parser, compiler: Compiler<'a>, vm: Rc>, repl: bool, color: bool, #[allow(unused)] debug: bool, } impl<'a> State<'a> { pub fn new (args: Args) -> (Self, Option) { let stdin = read_stdin(); let file; if let Some(path) = &args.file { file = Some(fs::read_to_string(path).unwrap()); } else if stdin.len() > 0 { file = Some(stdin); } else { file = None; } let repl = args.repl || file.is_none(); let parser = ParserBuilder::new() .optimize(!args.disable_optimizations) .build(); let mut vm = Vm::new(); let compiler = CompilerBuilder::new() .repl(repl) .debug(args.debug) .names(vm.names()) .globals(vm.global_names()) .build(); matrix_stdlib::load(&mut vm); let color = match args.color { Some(ColorChoice::Auto) | None => { io::stdout().is_terminal() }, Some(ColorChoice::Always) => true, Some(ColorChoice::Never) => false, }; (Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, repl, debug: args.debug, color }, file) } pub fn execute(&mut self, code: String) -> matrix::Result { let ast = self.parser.parse(code)?; let fun = self.compiler.compile(&ast)?; let val = self.vm.try_borrow_mut().unwrap().run(fun)?; Ok(val) } } pub fn error(err: matrix::Error, state: &State) { if state.color { println!("\x1b[31m\x1b[1mError:\x1b[0m {err}"); } else { println!("Error: {err}"); } } fn read_stdin() -> String { let mut buffer = String::new(); let mut stdin = io::stdin(); if stdin.is_terminal() { return String::new(); } stdin.read_to_string(&mut buffer).unwrap(); buffer } fn main() { let args = Args::parse(); let (mut state, file) = State::new(args); if let Some(file) = file { if let Err(err) = state.execute(file) { error(err, &state); } } if state.repl { Repl::new(state).run(); } }