187 lines
4.5 KiB
Rust
187 lines
4.5 KiB
Rust
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, 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<PathBuf>,
|
|
|
|
/// Compiles the given program
|
|
#[arg(short, long)]
|
|
compile: bool,
|
|
|
|
/// Optional output for compiled output
|
|
#[arg(short, long)]
|
|
output: Option<PathBuf>,
|
|
|
|
/// Print out debug information
|
|
#[arg(short, long)]
|
|
debug: bool,
|
|
|
|
/// Choses color
|
|
#[arg(long)]
|
|
color: Option<ColorChoice>,
|
|
|
|
/// Disables optimizations
|
|
#[arg(long)]
|
|
disable_optimizations: bool,
|
|
}
|
|
|
|
pub enum Mode {
|
|
Repl,
|
|
Execute(String),
|
|
Compile(String, PathBuf),
|
|
}
|
|
|
|
pub struct State<'a> {
|
|
parser: Parser,
|
|
compiler: Compiler<'a>,
|
|
vm: Rc<RefCell<Vm>>,
|
|
color: bool,
|
|
}
|
|
|
|
impl<'a> State<'a> {
|
|
pub fn new (args: Args) -> (Self, Mode) {
|
|
|
|
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 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")
|
|
}
|
|
};
|
|
let file = file.unwrap_or(String::new());
|
|
mode = Mode::Compile(file, path);
|
|
repl = false;
|
|
} else if let Some(file) = file {
|
|
mode = Mode::Execute(file);
|
|
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,
|
|
};
|
|
|
|
(Self {
|
|
parser,
|
|
vm: Rc::new(RefCell::new(vm)),
|
|
compiler,
|
|
color,
|
|
}, mode)
|
|
}
|
|
|
|
pub fn execute(&mut self, fun: Rc<Function>) -> Result<Value> {
|
|
let val = self.vm.try_borrow_mut().unwrap().run(fun)?;
|
|
Ok(val)
|
|
}
|
|
|
|
pub fn compile(&mut self, code: String) -> Result<Rc<Function>> {
|
|
let ast = self.parser.parse(code)?;
|
|
let fun = self.compiler.compile(&ast)?;
|
|
Ok(fun)
|
|
}
|
|
|
|
pub fn load_program(&mut self, body: String) -> Result<Rc<Function>> {
|
|
match Program::load(&body)? {
|
|
Some(fun) => {
|
|
Ok(fun)
|
|
},
|
|
None => {
|
|
self.compile(body)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn error(err: Exception, state: &State) {
|
|
if state.color {
|
|
println!("\x1b[31mError:\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 handle_mode(state: &mut State, mode: Mode) -> Result<()> {
|
|
match mode {
|
|
Mode::Repl => {
|
|
let mut repl = Repl::new(state);
|
|
repl.run();
|
|
},
|
|
Mode::Execute(body) => {
|
|
let fun = state.load_program(body)?;
|
|
state.execute(fun)?;
|
|
},
|
|
Mode::Compile(body, path) => {
|
|
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 main() {
|
|
|
|
let args = Args::parse();
|
|
let (mut state, mode) = State::new(args);
|
|
|
|
if let Err(e) = handle_mode(&mut state, mode) {
|
|
error(e, &state);
|
|
}
|
|
}
|