70 lines
2.0 KiB
Rust
70 lines
2.0 KiB
Rust
use std::fs::read_to_string;
|
|
use std::io::{stdin, stdout, BufRead, Write};
|
|
use std::path::Path;
|
|
|
|
use anyhow::{anyhow, Context, Result};
|
|
|
|
use mul::cli::{Command, USAGE};
|
|
use mul::evaluate::Interpreter;
|
|
use mul::parse::{parse_interactive_entry, parse_program};
|
|
use mul::syntax::Program;
|
|
|
|
fn main() -> Result<()> {
|
|
let Some(command) = Command::from_environment()? else {
|
|
eprintln!("mul\n{USAGE}");
|
|
return Ok(())
|
|
};
|
|
match command {
|
|
Command::Parse { path } => {
|
|
let program = parse(path.as_path())?;
|
|
println!("{program:#?}");
|
|
Ok(())
|
|
}
|
|
Command::Run { path } => {
|
|
let program = parse(path.as_path())?;
|
|
let mut interpreter = Interpreter::new();
|
|
let _result = interpreter.run(&program)?;
|
|
Ok(())
|
|
}
|
|
Command::Repl => repl(),
|
|
}
|
|
}
|
|
|
|
fn parse(path: &Path) -> Result<Program> {
|
|
let source =
|
|
read_to_string(path).with_context(|| format!("failed to open {}", path.display()))?;
|
|
parse_program(source.as_str())
|
|
.map_err(|e| anyhow!("{e}"))
|
|
.with_context(|| format!("failed to parse {}", path.display()))
|
|
}
|
|
|
|
fn repl() -> Result<()> {
|
|
let mut stdin = stdin().lock();
|
|
let mut stdout = stdout().lock();
|
|
let mut interpreter = Interpreter::new();
|
|
println!("welcome to M U L");
|
|
loop {
|
|
let mut line = String::new();
|
|
print!("> ");
|
|
stdout.flush()?;
|
|
stdin
|
|
.read_line(&mut line)
|
|
.context("Failed to read from stdin")?;
|
|
if line.trim().is_empty() {
|
|
println!();
|
|
continue;
|
|
}
|
|
let parsed = match parse_interactive_entry(&line) {
|
|
Ok(parsed) => parsed,
|
|
Err(lalrpop_util::ParseError::UnrecognizedEof { .. }) => break,
|
|
Err(e) => {
|
|
eprintln!("{e}");
|
|
continue;
|
|
}
|
|
};
|
|
let value = interpreter.evaluate_interactive_entry(&parsed)?;
|
|
println!("{value}");
|
|
}
|
|
Ok(())
|
|
}
|