summaryrefslogtreecommitdiff
path: root/matrix-bin/src/main.rs
blob: 225b353644374841941802776edb91763d119546 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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<PathBuf>,

    /// 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<ColorChoice>,

    /// Disables optimizations
    #[arg(long)]
    disable_optimizations: bool,
}

pub struct State<'a> {
    parser: Parser,
    compiler: Compiler<'a>,
    vm: Rc<RefCell<Vm>>,
    repl: bool,
    color: bool,
    #[allow(unused)]
    debug: 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 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<Value> {
        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();
    }

}