refactor: extract CLI to binary
Also start getting rid of anyhow
This commit is contained in:
parent
e9c350e925
commit
7e4166386f
6 changed files with 96 additions and 45 deletions
|
@ -1,10 +1,12 @@
|
|||
[package]
|
||||
name = "mute"
|
||||
version = "0.1.0"
|
||||
author = "Raine Godmaire <its@raine.ing>"
|
||||
authors = ["Raine Godmaire <its@raine.ing>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[[bin]]
|
||||
name = "mute"
|
||||
path = "src/bin/cli.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
use node::Node;
|
||||
use rustyline::{error::ReadlineError, DefaultEditor};
|
||||
|
||||
mod env;
|
||||
mod error;
|
||||
mod evaluator;
|
||||
mod macros;
|
||||
mod node;
|
||||
mod parser;
|
||||
use mute::{eval, parse, Environment, Node};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(version, author, about)]
|
||||
|
@ -35,14 +29,16 @@ fn main() {
|
|||
match args.command {
|
||||
Command::Run { file } => {
|
||||
let input = std::fs::read_to_string(file).unwrap();
|
||||
let res = run(&input);
|
||||
let env = Environment::new();
|
||||
let res = run(&env, &input);
|
||||
match res {
|
||||
Ok(expressions) => expressions.into_iter().for_each(|expr| println!("{expr}")),
|
||||
Err(err) => println!("{}", err),
|
||||
}
|
||||
}
|
||||
Command::RunCommand { command } => {
|
||||
let res = run(&command);
|
||||
let env = Environment::new();
|
||||
let res = run(&env, &command);
|
||||
match res {
|
||||
Ok(expressions) => expressions.into_iter().for_each(|expr| println!("{expr}")),
|
||||
Err(err) => println!("{}", err),
|
||||
|
@ -52,14 +48,13 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
fn run(command: &str) -> Result<Vec<Node>> {
|
||||
let env = env::Environment::new();
|
||||
let ast = parser::parse_str(command)?;
|
||||
evaluator::eval(&env, ast)
|
||||
fn run(env: &Environment, command: &str) -> Result<Vec<Node>> {
|
||||
let ast = parse(command)?;
|
||||
eval(&env, ast)
|
||||
}
|
||||
|
||||
fn repl() {
|
||||
let env = env::Environment::new();
|
||||
let env = Environment::new();
|
||||
let mut rl = DefaultEditor::new().unwrap();
|
||||
|
||||
println!("Mute -- REPL");
|
||||
|
@ -71,12 +66,9 @@ fn repl() {
|
|||
Ok(line) => {
|
||||
rl.add_history_entry(line.as_str()).unwrap();
|
||||
|
||||
let ast = parser::parse_str(&line).unwrap();
|
||||
let res = evaluator::eval(&env, ast);
|
||||
|
||||
match res {
|
||||
match run(&env, &line) {
|
||||
Ok(expressions) => expressions.into_iter().for_each(|expr| println!("{expr}")),
|
||||
Err(err) => println!("{}", err),
|
||||
Err(err) => println!("error: {}", err),
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => {
|
34
src/error.rs
34
src/error.rs
|
@ -1,7 +1,12 @@
|
|||
use thiserror::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("ParserError: {0}")]
|
||||
ParserError(ParserError),
|
||||
|
||||
#[error("could not find symbol '{0}' in environment")]
|
||||
NotInEnv(String),
|
||||
#[error("expression does not have a valid operator")]
|
||||
|
@ -18,4 +23,33 @@ pub enum Error {
|
|||
ExpectedString,
|
||||
#[error("expected {0} arguments, got {1}")]
|
||||
MismatchedArgCount(usize, usize),
|
||||
|
||||
#[error("please file a bug report")]
|
||||
Unreachable,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ParserError {
|
||||
#[error("closing parenthesis does not have matching open parenthesis")]
|
||||
UnopenedParenthesis,
|
||||
#[error("unclosed list")]
|
||||
UnclosedParenthesis,
|
||||
#[error("closing bracket does not have matching open parenthesis")]
|
||||
UnopenedBracket,
|
||||
#[error("unclosed vector")]
|
||||
UnclosedBracket,
|
||||
#[error("closing brace does not have matching open parenthesis")]
|
||||
UnopenedBrace,
|
||||
#[error("unclosed hashmap")]
|
||||
UnclosedBrace,
|
||||
|
||||
#[error("unexpected character '{0}'")]
|
||||
UnexpectedChar(char),
|
||||
#[error("string is never closed")]
|
||||
UnterminatedString,
|
||||
#[error("the quote does not have a valid follower node")]
|
||||
NoFollowerNode,
|
||||
|
||||
#[error("please file a bug report")]
|
||||
Unreachable,
|
||||
}
|
||||
|
|
18
src/lib.rs
Normal file
18
src/lib.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
mod env;
|
||||
mod error;
|
||||
mod evaluator;
|
||||
mod macros;
|
||||
mod node;
|
||||
mod parser;
|
||||
|
||||
pub use env::Environment;
|
||||
pub use error::{Error, Result};
|
||||
pub use node::Node;
|
||||
|
||||
pub fn parse(input: &str) -> Result<Vec<Node>> {
|
||||
parser::parse_str(input)
|
||||
}
|
||||
|
||||
pub fn eval(env: &Environment, input: Vec<Node>) -> anyhow::Result<Vec<Node>> {
|
||||
evaluator::eval(env, input)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use std::{iter::Peekable, str::Chars};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use crate::error::ParserError;
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd)]
|
||||
pub enum Token {
|
||||
|
@ -41,7 +41,7 @@ pub enum Token {
|
|||
Nil,
|
||||
}
|
||||
|
||||
pub fn read(input: &str) -> Result<Vec<Token>> {
|
||||
pub fn read(input: &str) -> Result<Vec<Token>, ParserError> {
|
||||
let mut input = input.chars().peekable();
|
||||
let mut tokens = Vec::new();
|
||||
|
||||
|
@ -52,7 +52,7 @@ pub fn read(input: &str) -> Result<Vec<Token>> {
|
|||
Ok(tokens)
|
||||
}
|
||||
|
||||
fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>> {
|
||||
fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>, ParserError> {
|
||||
let tok = match input.next() {
|
||||
Some(tok) => tok,
|
||||
None => return Ok(None),
|
||||
|
@ -128,13 +128,13 @@ fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>> {
|
|||
Some(tok) => tok,
|
||||
None => return Ok(None),
|
||||
},
|
||||
_ => bail!("illegal token"),
|
||||
c => Err(ParserError::UnexpectedChar(c))?,
|
||||
};
|
||||
|
||||
Ok(Some(tok))
|
||||
}
|
||||
|
||||
fn read_string(input: &mut Peekable<Chars>) -> Result<Token> {
|
||||
fn read_string(input: &mut Peekable<Chars>) -> Result<Token, ParserError> {
|
||||
let mut raw_str = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -146,7 +146,7 @@ fn read_string(input: &mut Peekable<Chars>) -> Result<Token> {
|
|||
}
|
||||
|
||||
Some(_) => (),
|
||||
None => bail!("unbalanced string"),
|
||||
None => Err(ParserError::UnterminatedString)?,
|
||||
}
|
||||
|
||||
raw_str.push(input.next().unwrap())
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
use std::{iter::Peekable, vec::IntoIter};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
mod lexer;
|
||||
use lexer::Token;
|
||||
|
||||
use crate::node::Node;
|
||||
use crate::error::ParserError;
|
||||
use crate::{Error, Node};
|
||||
|
||||
pub fn parse_str(input: &str) -> Result<Vec<Node>> {
|
||||
let mut tokens = lexer::read(input)?.into_iter().peekable();
|
||||
pub fn parse_str(input: &str) -> crate::Result<Vec<Node>> {
|
||||
let mut tokens = lexer::read(input)
|
||||
.map_err(|err| Error::ParserError(err))?
|
||||
.into_iter()
|
||||
.peekable();
|
||||
let mut ast = Vec::new();
|
||||
|
||||
while let Some(node) = next_statement(&mut tokens)? {
|
||||
while let Some(node) = next_statement(&mut tokens).map_err(|err| Error::ParserError(err))? {
|
||||
ast.push(node)
|
||||
}
|
||||
|
||||
Ok(ast)
|
||||
}
|
||||
|
||||
fn next_statement(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Option<Node>> {
|
||||
fn next_statement(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Option<Node>, ParserError> {
|
||||
let tok = match tokens.next() {
|
||||
Some(tok) => tok,
|
||||
None => return Ok(None),
|
||||
|
@ -26,13 +28,13 @@ fn next_statement(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Option<Node>
|
|||
|
||||
let node = match tok {
|
||||
Token::LeftParen => read_list(tokens, Token::RightParen)?,
|
||||
Token::RightParen => bail!("closing parenthsis does not have matching open parenthesis"),
|
||||
Token::RightParen => Err(ParserError::UnopenedParenthesis)?,
|
||||
|
||||
Token::LeftBracket => read_list(tokens, Token::RightBracket)?,
|
||||
Token::RightBracket => bail!("closing bracket does not have matching open bracket"),
|
||||
Token::RightBracket => Err(ParserError::UnopenedBracket)?,
|
||||
|
||||
Token::LeftBrace => read_list(tokens, Token::RightBrace)?,
|
||||
Token::RightBrace => bail!("closing brace does not have matching open brace"),
|
||||
Token::RightBrace => Err(ParserError::UnopenedBrace)?,
|
||||
|
||||
Token::WeirdSign => read_quote(tokens, "splice-unquote")?,
|
||||
Token::Apostrophe => read_quote(tokens, "quote")?,
|
||||
|
@ -66,12 +68,12 @@ fn next_statement(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Option<Node>
|
|||
Ok(Some(node))
|
||||
}
|
||||
|
||||
fn read_list(tokens: &mut Peekable<IntoIter<Token>>, closer: Token) -> Result<Node> {
|
||||
fn read_list(tokens: &mut Peekable<IntoIter<Token>>, closer: Token) -> Result<Node, ParserError> {
|
||||
let mut list = match closer {
|
||||
Token::RightParen => Vec::new(),
|
||||
Token::RightBracket => vec![Node::Symbol("vector".into())],
|
||||
Token::RightBrace => vec![Node::Symbol("hashmap".into())],
|
||||
_ => bail!("unreachable"),
|
||||
_ => Err(ParserError::Unreachable)?,
|
||||
};
|
||||
|
||||
loop {
|
||||
|
@ -88,10 +90,10 @@ fn read_list(tokens: &mut Peekable<IntoIter<Token>>, closer: Token) -> Result<No
|
|||
match next_statement(tokens)? {
|
||||
Some(node) => list.push(node),
|
||||
None => match closer {
|
||||
Token::RightParen => bail!("unclosed list"),
|
||||
Token::RightBracket => bail!("unclosed vector"),
|
||||
Token::RightBrace => bail!("unclosed hashmap"),
|
||||
_ => bail!("unreachable"),
|
||||
Token::RightParen => Err(ParserError::UnclosedParenthesis)?,
|
||||
Token::RightBracket => Err(ParserError::UnclosedBracket)?,
|
||||
Token::RightBrace => Err(ParserError::UnclosedBrace)?,
|
||||
_ => Err(ParserError::Unreachable)?,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -99,10 +101,13 @@ fn read_list(tokens: &mut Peekable<IntoIter<Token>>, closer: Token) -> Result<No
|
|||
Ok(Node::List(list))
|
||||
}
|
||||
|
||||
fn read_quote(tokens: &mut Peekable<IntoIter<Token>>, quote_type: &str) -> Result<Node> {
|
||||
fn read_quote(
|
||||
tokens: &mut Peekable<IntoIter<Token>>,
|
||||
quote_type: &str,
|
||||
) -> Result<Node, ParserError> {
|
||||
let follower_node = match next_statement(tokens)? {
|
||||
Some(node) => node,
|
||||
None => bail!("quote does not have a valid follower node"),
|
||||
None => Err(ParserError::NoFollowerNode)?,
|
||||
};
|
||||
|
||||
Ok(Node::List(vec![
|
||||
|
|
Loading…
Reference in a new issue