2024-02-19 23:38:15 +00:00
|
|
|
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<PathBuf>,
|
|
|
|
|
|
|
|
/// 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<String>) {
|
|
|
|
|
|
|
|
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)
|
2024-02-23 00:40:09 +00:00
|
|
|
.names(vm.names())
|
2024-02-19 23:38:15 +00:00
|
|
|
.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();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|