Rework error handling and main
parent
2ccc3e8ef1
commit
8af9855a27
|
@ -13,9 +13,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.71"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
|
@ -207,9 +207,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.19.9"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87"
|
||||
checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8"
|
||||
dependencies = [
|
||||
"ascii-canvas",
|
||||
"bit-set",
|
||||
|
@ -219,9 +219,9 @@ dependencies = [
|
|||
"itertools",
|
||||
"lalrpop-util",
|
||||
"petgraph",
|
||||
"pico-args 0.4.2",
|
||||
"pico-args",
|
||||
"regex",
|
||||
"regex-syntax 0.6.29",
|
||||
"regex-syntax",
|
||||
"string_cache",
|
||||
"term",
|
||||
"tiny-keccak",
|
||||
|
@ -230,9 +230,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.19.9"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5c1f7869c94d214466c5fd432dfed12c379fd87786768d36455892d46b18edd"
|
||||
checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
@ -281,8 +281,9 @@ dependencies = [
|
|||
"anyhow",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"pico-args 0.5.0",
|
||||
"pico-args",
|
||||
"regex",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -339,12 +340,6 @@ dependencies = [
|
|||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
|
@ -359,18 +354,18 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -403,15 +398,9 @@ checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.1",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.1"
|
||||
|
@ -471,9 +460,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.15"
|
||||
version = "2.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -493,18 +482,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -4,10 +4,11 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop = "0.19.9"
|
||||
lalrpop = "0.20.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
lalrpop-util = { version = "0.19.9", features = ["lexer"] }
|
||||
anyhow = "1.0.72"
|
||||
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
||||
pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] }
|
||||
regex = "1"
|
||||
thiserror = "1.0.43"
|
||||
|
|
59
src/cli.rs
59
src/cli.rs
|
@ -1,45 +1,60 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{ffi::OsString, path::PathBuf};
|
||||
|
||||
use pico_args::Arguments;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub const USAGE: &str = "
|
||||
USAGE:
|
||||
mul [-h|--help] (parse <FILE> | evaluate <FILE> | repl)
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information (this text)
|
||||
|
||||
COMMANDS:
|
||||
parse <FILE> Parse the given file and print a serialized AST to stdout
|
||||
evaluate, eval <FILE> Evaluate the given file
|
||||
repl Start the Read-Evaluate-Print-Loop
|
||||
";
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
ArgsError(pico_args::Error),
|
||||
#[error("Problem while parsing arguments, {0}")]
|
||||
ArgsError(#[from] pico_args::Error),
|
||||
#[error("Invalid command '{0}'\n{USAGE}")]
|
||||
InvalidCommand(String),
|
||||
PrintUsage,
|
||||
}
|
||||
|
||||
impl From<pico_args::Error> for Error {
|
||||
fn from(value: pico_args::Error) -> Self {
|
||||
Error::ArgsError(value)
|
||||
}
|
||||
#[error("Unrecognized arguments {0:?}\n{USAGE}")]
|
||||
UnrecognizedArguments(Vec<OsString>),
|
||||
}
|
||||
|
||||
pub enum Command {
|
||||
Parse { source: PathBuf },
|
||||
Run { source: PathBuf },
|
||||
Parse { path: PathBuf },
|
||||
Evaluate { path: PathBuf },
|
||||
Repl,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn from_environment() -> Result<Self, Error> {
|
||||
pub fn from_environment() -> Result<Option<Self>, Error> {
|
||||
let mut args = Arguments::from_env();
|
||||
if args.contains(["-h", "--help"]) {
|
||||
return Err(Error::PrintUsage);
|
||||
return Ok(None);
|
||||
}
|
||||
let cmd = match args.subcommand()?.as_deref() {
|
||||
Some("parse") => {
|
||||
let source = args.free_from_str()?;
|
||||
Command::Parse { source }
|
||||
let path = args.free_from_str()?;
|
||||
Command::Parse { path }
|
||||
}
|
||||
Some("run") => {
|
||||
let source = args.free_from_str()?;
|
||||
Command::Run { source }
|
||||
Some("evaluate") | Some("eval") => {
|
||||
let path = args.free_from_str()?;
|
||||
Command::Evaluate { path }
|
||||
}
|
||||
Some("repl") => Command::Repl,
|
||||
Some("repl") | None => Command::Repl,
|
||||
Some(cmd) => return Err(Error::InvalidCommand(cmd.to_string())),
|
||||
None => return Err(Error::PrintUsage),
|
||||
};
|
||||
Ok(cmd)
|
||||
let remaining = args.finish();
|
||||
if remaining.is_empty() {
|
||||
Ok(Some(cmd))
|
||||
} else {
|
||||
Err(Error::UnrecognizedArguments(remaining))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
74
src/main.rs
74
src/main.rs
|
@ -1,75 +1,57 @@
|
|||
use std::fs::read_to_string;
|
||||
use std::io::{stdin, stdout, BufRead, Write};
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
use mul::cli::{Command, Error};
|
||||
use mul::phase::parse::{parse_program, parse_repl_line};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let command = Command::from_environment()?;
|
||||
use mul::cli::{Command, USAGE};
|
||||
use mul::phase::parse::{parse_interactive_entry, parse_program};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let Some(command) = Command::from_environment()? else {
|
||||
eprintln!("mul\n{USAGE}");
|
||||
return Ok(())
|
||||
};
|
||||
match command {
|
||||
Command::Parse { source } => parse(source.as_path()),
|
||||
Command::Run { source } => run(source.as_path()),
|
||||
Command::Repl => repl(),
|
||||
}
|
||||
Command::Parse { path } => {
|
||||
let source = read_to_string(&path)
|
||||
.with_context(|| format!("failed to open {}", path.display()))?;
|
||||
let program = parse_program(source.as_str())
|
||||
.map_err(|e| anyhow!("{e}"))
|
||||
.with_context(|| format!("failed to parse {}", path.display()))?;
|
||||
println!("{program:#?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse(file: &Path) {
|
||||
let content = read_to_string(file).expect("Failed to read file");
|
||||
let program = parse_program(content.as_str()).unwrap_or_else(|e| {
|
||||
eprintln!("{e}");
|
||||
exit(1);
|
||||
});
|
||||
println!("{program:#?}");
|
||||
Command::Evaluate { path } => {
|
||||
todo!()
|
||||
}
|
||||
Command::Repl => repl(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(file: &Path) {
|
||||
let content = read_to_string(file).expect("Failed to read file");
|
||||
let program = parse_program(content.as_str()).unwrap_or_else(|e| {
|
||||
eprintln!("{e}");
|
||||
exit(1);
|
||||
});
|
||||
// let v = interpreter.interpret(&program).unwrap_or_else(|e| {
|
||||
// eprintln!("{e}");
|
||||
// exit(1);
|
||||
// });
|
||||
// println!("{v:?}")
|
||||
}
|
||||
|
||||
fn repl() {
|
||||
fn repl() -> Result<()> {
|
||||
let mut stdin = stdin().lock();
|
||||
let mut stdout = stdout().lock();
|
||||
println!("welcome to M U L");
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
print!("> ");
|
||||
stdout.flush().unwrap();
|
||||
stdout.flush()?;
|
||||
stdin
|
||||
.read_line(&mut line)
|
||||
.expect("Failed to read from stdin");
|
||||
.context("Failed to read from stdin")?;
|
||||
if line.trim().is_empty() {
|
||||
println!();
|
||||
continue;
|
||||
}
|
||||
if !line.trim_end().ends_with(';') {
|
||||
line.push(';');
|
||||
}
|
||||
let parsed = match parse_repl_line(&line) {
|
||||
let parsed = match parse_interactive_entry(&line) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(lalrpop_util::ParseError::UnrecognizedEOF { .. }) => break,
|
||||
Err(lalrpop_util::ParseError::UnrecognizedEof { .. }) => break,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
println!("{parsed:?}");
|
||||
// match intepreter.interpret_statement(&parsed) {
|
||||
// Ok(value) => println!("{value:?}"),
|
||||
// Err(e) => {
|
||||
// eprintln!("{e}");
|
||||
// continue;
|
||||
// }
|
||||
// };
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1 +1,7 @@
|
|||
pub mod parse;
|
||||
// |
|
||||
// |
|
||||
// syntax
|
||||
// |
|
||||
// v
|
||||
pub mod evaluate;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError};
|
||||
|
||||
use crate::syntax::{self, Line};
|
||||
use crate::syntax::{InteractiveEntry, Program};
|
||||
|
||||
lalrpop_mod!(parser, "/phase/parse/parser.rs");
|
||||
|
||||
pub type Result<'input, T> = std::result::Result<T, ParseError<usize, Token<'input>, &'static str>>;
|
||||
|
||||
pub fn parse_program(input: &str) -> Result<syntax::Program> {
|
||||
pub fn parse_program(input: &'_ str) -> Result<'_, Program> {
|
||||
parser::ProgramParser::new().parse(input)
|
||||
}
|
||||
|
||||
pub fn parse_repl_line(input: &str) -> Result<Line> {
|
||||
parser::LineParser::new().parse(input)
|
||||
pub fn parse_interactive_entry(input: &'_ str) -> Result<'_, InteractiveEntry> {
|
||||
parser::InteractiveEntryParser::new().parse(input)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::syntax::{
|
|||
Block,
|
||||
Expression,
|
||||
Item,
|
||||
Line,
|
||||
InteractiveEntry,
|
||||
Name,
|
||||
Program,
|
||||
};
|
||||
|
@ -12,10 +12,10 @@ grammar;
|
|||
|
||||
pub Program: Program = Item*;
|
||||
|
||||
pub Line: Line = {
|
||||
Item => Line::Item(<>),
|
||||
Binding => Line::Binding(<>),
|
||||
<Expression> ";"? => Line::Expression(<>),
|
||||
pub InteractiveEntry: InteractiveEntry = {
|
||||
Item => InteractiveEntry::Item(<>),
|
||||
Binding => InteractiveEntry::Binding(<>),
|
||||
<Expression> ";"? => InteractiveEntry::Expression(<>),
|
||||
}
|
||||
|
||||
Item: Item = {
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
//! Syntax elements.
|
||||
|
||||
/// Identifiers.
|
||||
pub type Name = String;
|
||||
|
||||
/// The rough equivalent of a module.
|
||||
pub type Program = Vec<Item>;
|
||||
|
||||
/// A fragment of code entered in, e.g., the REPL.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Line {
|
||||
pub enum InteractiveEntry {
|
||||
Item(Item),
|
||||
Binding(Binding),
|
||||
Expression(Expression),
|
||||
}
|
||||
|
||||
/// A top level definition.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Item {
|
||||
Fn {
|
||||
|
@ -18,9 +24,11 @@ pub enum Item {
|
|||
},
|
||||
}
|
||||
|
||||
/// Syntactic mapping from an identifier to an expression.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Binding(pub Name, pub Expression);
|
||||
|
||||
/// Some individually addressable syntax node, either atomic or compound.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expression {
|
||||
Variable(Name),
|
||||
|
@ -37,6 +45,7 @@ pub enum Expression {
|
|||
Integer(i64),
|
||||
}
|
||||
|
||||
/// A sequence of bindings with a final result.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block {
|
||||
pub bindings: Vec<Binding>,
|
||||
|
|
Loading…
Reference in New Issue