use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs}; use clap::Parser as ArgParser; use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}}; use repl::Repl; mod repl; #[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, /// Disables optimizations #[arg(long)] disable_optimizations: bool, } pub struct State<'a> { parser: Parser, compiler: Compiler<'a>, vm: Vm, repl: 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 vm = Vm::new(); let compiler = CompilerBuilder::new() .repl(repl) .debug(args.debug) .names(vm.names()) .build(); (Self { parser, vm, compiler, repl }, 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.run(fun)?; if self.repl { println!("{val}"); } Ok(()) } } pub fn error(err: matrix::Error) { println!("\x1b[31m\x1b[1mError:\x1b[0m {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); } } if state.repl { Repl::new(state).run(); } }