From 21a1a5844761838a8ce2e0c8027a748275aded50 Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Mon, 22 Jun 2020 23:15:28 +0700 Subject: [PATCH] Sample repl setup --- src/repl/eval.rs | 581 +++++++++++++++++++++++++++++++++++++++++++++ src/repl/lpus.pest | 19 ++ src/repl/lval.rs | 182 ++++++++++++++ src/repl/parser.rs | 56 +++++ src/repl/repl.rs | 44 ++++ 5 files changed, 882 insertions(+) create mode 100644 src/repl/eval.rs create mode 100644 src/repl/lpus.pest create mode 100644 src/repl/lval.rs create mode 100644 src/repl/parser.rs create mode 100644 src/repl/repl.rs diff --git a/src/repl/eval.rs b/src/repl/eval.rs new file mode 100644 index 0000000..8b741f5 --- /dev/null +++ b/src/repl/eval.rs @@ -0,0 +1,581 @@ +use crate::{ + error::{BlisprError, BlisprResult}, + lenv::Lenv, + lval::{ + lval_add, lval_join, lval_lambda, lval_num, lval_pop, lval_qexpr, lval_sexpr, Lval, LvalFun, + }, +}; +use log::debug; +use std::{collections::HashMap, ops::{Add, Div, Mul, Rem, Sub}}; + +// macro to shorten code for applying a binary operation to two Lvals +macro_rules! apply_binop { + ( $op:ident, $x:ident, $y:ident ) => { + match (*$x, *$y) { + (Lval::Num(x_num), Lval::Num(y_num)) => { + $x = lval_num(x_num.$op(y_num)); + continue; + } + _ => return Err(BlisprError::NotANumber), + } + }; +} + +// apply a binary operation {+ - * / ^ % min max} to a list of arguments in succession +fn builtin_op(mut v: &mut Lval, func: &str) -> BlisprResult { + let mut child_count; + match *v { + Lval::Sexpr(ref children) => { + child_count = children.len(); + } + _ => return Ok(Box::new(v.clone())), + } + + let mut x = lval_pop(&mut v, 0)?; + + // If no args given and we're doing subtraction, perform unary negation + if (func == "-" || func == "sub") && child_count == 1 { + debug!("builtin_op: Unary negation on {}", x); + let x_num = x.as_num()?; + return Ok(lval_num(-x_num)); + } + + // consume the children until empty + // and operate on x + while child_count > 1 { + let y = lval_pop(&mut v, 0)?; + child_count -= 1; + match func { + "+" | "add" => { + debug!("builtin_op: Add {} and {}", x, y); + apply_binop!(add, x, y) + } + "-" | "sub" => { + debug!("builtin_op: Subtract {} and {}", x, y); + apply_binop!(sub, x, y) + } + "*" | "mul" => { + debug!("builtin_op: Multiply {} and {}", x, y); + apply_binop!(mul, x, y) + } + "/" | "div" => { + if y.as_num()? == 0 { + debug!("builtin_op: Failed divide {} by {}", x, y); + return Err(BlisprError::DivideByZero); + } else { + debug!("builtin_op: Divide {} by {}", x, y); + apply_binop!(div, x, y) + } + } + "%" | "rem" => { + debug!("builtin_op: {} % {}", x, y); + apply_binop!(rem, x, y) + } + "^" | "pow" => { + debug!("builtin_op: Raise {} to the {} power", x, y); + let y_num = y.as_num()?; + let x_num = x.as_num()?; + let mut coll = 1; + for _ in 0..y_num { + coll *= x_num; + } + x = lval_num(coll); + } + "min" => { + debug!("builtin_op: Min {} and {}", x, y); + let x_num = x.as_num()?; + let y_num = y.as_num()?; + if x_num < y_num { + x = lval_num(x_num); + } else { + x = lval_num(y_num); + }; + } + "max" => { + debug!("builtin_op: Max {} and {}", x, y); + let x_num = x.as_num()?; + let y_num = y.as_num()?; + if x_num > y_num { + x = lval_num(x_num); + } else { + x = lval_num(y_num); + }; + } + _ => unreachable!(), + } + } + Ok(x) +} + +// Operator aliases, function pointers will be stored in env +// TODO macro?? create_builtin!(a, &str) +pub fn builtin_add(a: &mut Lval) -> BlisprResult { + builtin_op(a, "+") +} + +pub fn builtin_sub(a: &mut Lval) -> BlisprResult { + builtin_op(a, "-") +} + +pub fn builtin_mul(a: &mut Lval) -> BlisprResult { + builtin_op(a, "*") +} + +pub fn builtin_div(a: &mut Lval) -> BlisprResult { + builtin_op(a, "/") +} + +pub fn builtin_pow(a: &mut Lval) -> BlisprResult { + builtin_op(a, "^") +} + +pub fn builtin_rem(a: &mut Lval) -> BlisprResult { + builtin_op(a, "%") +} + +pub fn builtin_max(a: &mut Lval) -> BlisprResult { + builtin_op(a, "max") +} + +pub fn builtin_min(a: &mut Lval) -> BlisprResult { + builtin_op(a, "min") +} + +// define a list of values +// if "def" define in global env +// if "=" define in local env +fn builtin_var(e: &mut Lenv, a: &mut Lval, func: &str) -> BlisprResult { + let args = lval_pop(a, 0)?; + match *args { + Lval::Qexpr(names) => { + // grab the rest of the vals + let mut vals = Vec::new(); + for _ in 0..a.len()? { + vals.push(lval_pop(a, 0)?); + } + let names_len = names.len(); + let vals_len = vals.len(); + // TODO assert all symbols? + if vals_len != names_len { + Err(BlisprError::NumArguments(names_len, vals_len)) + } else { + for (k, v) in names.iter().zip(vals.iter()) { + let scope = if func == "def" { "global" } else { "local" }; + debug!("adding key, value pair {}, {} to {} env {}", k, v, scope, e); + let name = k.clone().as_string()?; + if scope == "local" { + e.put(name, v.clone()); + } else { + //e.def(name, v.clone())?; + debug!("warning: global scope definition unimplemented!"); + e.put(name, v.clone()); + } + } + Ok(lval_sexpr()) + } + } + _ => Err(BlisprError::WrongType( + "qexpr".to_string(), + format!("{:?}", args), + )), + } +} + +// BROKEN +//pub fn builtin_def_stub(_v: &Lval) -> BlisprResult { +// Ok(lval_sexpr()) +//} + +// FOR NOW def IS LOCAL ENV ASSIGN +fn builtin_def(e: &mut Lenv, v: &mut Lval) -> BlisprResult { + builtin_var(e, v, "def") +} + +pub fn builtin_put_stub(_v: &mut Lval) -> BlisprResult { + Ok(lval_sexpr()) +} + +//BROKEN +//fn builtin_put(e: &mut Lenv, v: &Lval) -> BlisprResult { +// builtin_var(e, v, "=") +//} + +// Attach a value to the front of a qexpr +pub fn builtin_cons(v: &mut Lval) -> BlisprResult { + let child_count = v.len()?; + if child_count != 2 { + return Err(BlisprError::NumArguments(2, child_count)); + } + let new_elem = lval_pop(v, 0)?; + let qexpr = lval_pop(v, 0)?; + match *qexpr { + Lval::Qexpr(ref children) => { + let mut ret = lval_qexpr(); + lval_add(&mut ret, &new_elem)?; + for c in children { + lval_add(&mut ret, &c.clone())?; + } + Ok(ret) + } + _ => Err(BlisprError::WrongType( + "qexpr".to_string(), + format!("{:?}", v), + )), + } +} + +// correct call dispatched in lval_call +pub fn builtin_eval_stub(_v: &mut Lval) -> BlisprResult { + Ok(lval_sexpr()) +} + +// Evaluate qexpr as a sexpr +pub fn builtin_eval(e: &mut Lenv, v: &mut Lval) -> BlisprResult { + let qexpr = lval_pop(v, 0)?; + match *qexpr { + Lval::Qexpr(ref children) => { + let mut new_sexpr = lval_sexpr(); + for c in children { + let cloned = Box::new(*c.clone()); + lval_add(&mut new_sexpr, &cloned)?; + } + debug!("builtin_eval: {:?}", new_sexpr); + lval_eval(e, &mut new_sexpr) + } + _ => { + // add it back + lval_add(v, &qexpr)?; + lval_eval(e, v) + } + } +} + +// terminate the program (or exit the prompt) +pub fn builtin_exit(_v: &mut Lval) -> BlisprResult { + // always succeeds + println!("Goodbye!"); + ::std::process::exit(0); +} + +// Return the first element of a qexpr +pub fn builtin_head(v: &mut Lval) -> BlisprResult { + let mut qexpr = lval_pop(v, 0)?; + match *qexpr { + Lval::Qexpr(ref mut children) => { + if children.is_empty() { + return Err(BlisprError::EmptyList); + } + debug!("builtin_head: Returning the first element"); + Ok(children[0].clone()) + } + _ => Err(BlisprError::WrongType( + "qexpr".to_string(), + format!("{:?}", qexpr), + )), + } +} + +// Return everything but the last element of a qexpr +pub fn builtin_init(v: &mut Lval) -> BlisprResult { + let qexpr = lval_pop(v, 0)?; + match *qexpr { + Lval::Qexpr(ref children) => { + let mut ret = lval_qexpr(); + for item in children.iter().take(children.len() - 1) { + lval_add(&mut ret, &item.clone())?; + } + Ok(ret) + } + _ => Err(BlisprError::WrongType( + "qexpr".to_string(), + format!("{:?}", qexpr), + )), + } +} + +// Join the children into one qexpr +pub fn builtin_join(v: &mut Lval) -> BlisprResult { + let mut ret = lval_qexpr(); + for _ in 0..v.len()? { + let next = lval_pop(v, 0)?; + match *next { + Lval::Qexpr(_) => { + lval_join(&mut ret, next)?; + } + _ => { + return Err(BlisprError::WrongType( + "qexpr".to_string(), + format!("{:?}", next), + )) + } + } + } + Ok(ret) +} + +//builtin_lambda returns a lambda lval from two lists of symbols +pub fn builtin_lambda(v: &mut Lval) -> BlisprResult { + // ensure there's only two arguments + let child_count = v.len()?; + if child_count != 2 { + return Err(BlisprError::NumArguments(2, child_count)); + } + + // first qexpr should contain only symbols - lval.as_string().is_ok() + let formals = lval_pop(v, 0)?; + let formals_ret = formals.clone(); // ewwww but it gets moved on me?! this might be why Rc<> - it doesn't need to mutate + let body = lval_pop(v, 0)?; + match *formals { + Lval::Qexpr(contents) => { + for cell in contents { + if cell.as_string().is_err() { + return Err(BlisprError::WrongType( + "Symbol".to_string(), + format!("{:?}", cell), + )); + } + } + match *body { + Lval::Qexpr(_) => Ok(lval_lambda(HashMap::new(), formals_ret, body)), + _ => Err(BlisprError::WrongType( + "Q-Expression".to_string(), + format!("{:?}", body), + )), + } + } + _ => Err(BlisprError::WrongType( + "Q-Expression".to_string(), + format!("{:?}", formals), + )), + } +} + +// make sexpr into a qexpr +pub fn builtin_list(v: &mut Lval) -> BlisprResult { + match *v { + Lval::Sexpr(ref children) => { + debug!("builtin_list: Building qexpr from {:?}", children); + let mut new_qexpr = lval_qexpr(); + for c in children { + let cloned = Box::new(*c.clone()); + lval_add(&mut new_qexpr, &cloned)?; + } + Ok(new_qexpr) + } + _ => Ok(Box::new(v.clone())), + } +} + +pub fn builtin_len(v: &mut Lval) -> BlisprResult { + let child_count = v.len()?; + match child_count { + 1 => { + let qexpr = lval_pop(v, 0)?; + match *qexpr { + Lval::Qexpr(_) => { + debug!("Returning length of {:?}", qexpr); + Ok(lval_num(qexpr.len()? as i64)) + } + _ => Err(BlisprError::WrongType( + "qexpr".to_string(), + format!("{:?}", qexpr), + )), + } + } + _ => Err(BlisprError::NumArguments(1, child_count)), + } +} + +pub fn builtin_printenv_stub(_v: &mut Lval) -> BlisprResult { + Ok(lval_sexpr()) +} + +// Print all the named variables in the environment +pub fn builtin_printenv(e: &mut Lenv) -> BlisprResult { + // we don't use the input + lval_eval(e, &mut *e.list_all()?) +} + +pub fn builtin_tail(v: &mut Lval) -> BlisprResult { + let mut qexpr = lval_pop(v, 0)?; + debug!("Returning tail of {:?}", qexpr); + match *qexpr { + Lval::Qexpr(ref mut children) => { + if children.is_empty() { + return Err(BlisprError::EmptyList); + } + let mut ret = lval_qexpr(); + for c in &children[1..] { + lval_add(&mut ret, &c.clone())?; + } + Ok(ret) + } + _ => Err(BlisprError::WrongType( + "qexpr".to_string(), + format!("{:?}", qexpr), + )), + } +} + +// Call a Lval::Fun(f) on an argument list +// This will handle both builtins and lambdas +pub fn lval_call(e: &mut Lenv, f: Lval, args: &mut Lval) -> BlisprResult { + match f { + Lval::Fun(func) => { + match func { + // if its one of the ones that need an environment, intercept and route to the properly typed fn + LvalFun::Builtin(name, fp) => match name.as_str() { + "eval" => builtin_eval(e, args), + "def" => builtin_def(e, args), + //"=" => builtin_put(e, args), + "printenv" => builtin_printenv(e), + // Otherwise, just apply the actual stored function pointer + _ => fp(args), + }, + LvalFun::Lambda(env, mut formals, body) => { + debug!( + "Executing lambda. Environment: {:?}, Formals: {:?}, body: {:?}", + env, formals, body + ); + // If it's a Lambda, bind arguments to a new local environment + + // First, build the lookup hashmap + let mut new_env: HashMap> = HashMap::new(); + // grab the argument and body + let given = args.len()?; + let total = formals.len()?; + + while args.len()? > 0 { + // if we've run out of args to bind, error + if formals.len()? == 0 { + return Err(BlisprError::NumArguments(total, given)); + } + + // grab first symbol from formals + let sym = lval_pop(&mut formals, 0)?; + + // special case to handle '&' + if &sym.as_string()? == "&" { + // make sure there's one symbol left + if formals.len()? != 1 { + return Err(BlisprError::FunctionFormat); + } + + // next formal should be found to remaining args + let next_sym = lval_pop(&mut formals, 0)?; + let arglist = builtin_list(args)?; + let curr = new_env + .entry(next_sym.as_string()?) + .or_insert(arglist.clone()); + if *curr != arglist { + *curr = arglist.clone(); + } + break; + } + + // grab next argument from list + let val = lval_pop(args, 0)?; + + // bind a copy to the function's environment + debug!("lval_call: adding {},{} to local fn environment", sym, val); + let curr = new_env.entry(sym.as_string()?).or_insert(val.clone()); + // if we're overwriting, overwrite! + if *curr != val { + *curr = val.clone(); + } + } + // Use the lookup map to initialize the new child env for evaluation + let mut local_env = Lenv::new(Some(new_env.clone()), Some(e)); + // if all formals have been bound + if formals.len()? == 0 { + // Evaluate and return + // first, apply any held by the lambda. + for (k, v) in env { + local_env.put(k, v); + } + let mut ret = lval_sexpr(); + lval_add(&mut ret, &body)?; + debug!("lval_call: evaluating fully applied lambda {}", ret); + // evaluate with the environment of the function, which now has the env this was called with as a parent. + builtin_eval(&mut local_env, &mut ret) + } else { + // Otherwise return partially evaluated function + // build a new lval for it + debug!("Returning partially applied lambda"); + Ok(lval_lambda(new_env, formals.clone(), body.clone())) + } + } + } + } + _ => Err(BlisprError::WrongType( + "Function".to_string(), + format!("{:?}", f), + )), + } +} + +// Given a slice of boxed Lvals, return a single evaluated sexpr +fn eval_cells(e: &mut Lenv, cells: &[Box]) -> BlisprResult { + cells.iter().fold(Ok(lval_sexpr()), |acc, c| { + match acc { + Ok(mut lval) => { + lval_add(&mut lval, &*lval_eval(e, &mut c.clone())?)?; + Ok(lval) + } + // it's just a Result so we can bubble errors out of the fold + Err(_) => unreachable!(), + } + }) +} + +// Fully evaluate an `Lval` +pub fn lval_eval(e: &mut Lenv, v: &mut Lval) -> BlisprResult { + let child_count; + let mut args_eval; + match v { + Lval::Blispr(forms) => { + // If it's multiple, evaluate each and return the result of the last + args_eval = eval_cells(e, forms)?; + let forms_len = args_eval.len()?; + return Ok(lval_pop(&mut args_eval, forms_len - 1)?); + } + Lval::Sym(s) => { + // If it's a symbol, perform an environment lookup + let result = e.get(&s)?; + debug!( + "lval_eval: Symbol lookup - retrieved {:?} from key {:?}", + result, s + ); + // The environment stores Lvals ready to go, we're done + return Ok(result); + } + Lval::Sexpr(ref mut cells) => { + // If it's a Sexpr, we're going to continue past this match + // First, though, recursively evaluate each child with lval_eval() + debug!("lval_eval: Sexpr, evaluating children"); + // grab the length and evaluate the children + child_count = cells.len(); + args_eval = eval_cells(e, cells)?; + } + // if it's not a sexpr, we're done, return as is + _ => { + debug!("lval_eval: Non-sexpr: {:?}", v); + return Ok(Box::new(v.clone())); + } + } + if child_count == 0 { + // It was a Sexpr, but it was empty. We're done, return it + Ok(Box::new(v.clone())) + } else if child_count == 1 { + // Single expression + debug!("Single-expression"); + lval_eval(e, &mut *lval_pop(v, 0)?) + } else { + // Function call + // We'll pop the first element off and attempt to call it on the rest of the elements + // lval_call will handle typechecking fp + let fp = lval_pop(&mut args_eval, 0)?; + debug!("Calling function {:?} on {:?}", fp, v); + lval_call(e, *fp, &mut *args_eval) + } +} diff --git a/src/repl/lpus.pest b/src/repl/lpus.pest new file mode 100644 index 0000000..374563a --- /dev/null +++ b/src/repl/lpus.pest @@ -0,0 +1,19 @@ +COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" } +WHITESPACE = _{ (" " | NEWLINE ) } + +num = @{ int } + int = { ("+" | "-")? ~ digit+ } + digit = { '0'..'9' } + +symbol = @{ (letter | digit | "_" | arithmetic_ops | "\\" | comparison_ops | "&")+ } + letter = { 'a' .. 'z' | 'A' .. 'Z' } + arithmetic_ops = { "+" | "-" | "*" | "/" | "%" | "^" } + comparison_ops = { "=" | "<" | ">" | "!" } + +sexpr = { "(" ~ expr* ~ ")" } + +qexpr = { "{" ~ expr* ~ "}" } + +expr = { num | symbol | sexpr | qexpr } + +program = { SOI ~ expr* ~ EOI } diff --git a/src/repl/lval.rs b/src/repl/lval.rs new file mode 100644 index 0000000..89096b7 --- /dev/null +++ b/src/repl/lval.rs @@ -0,0 +1,182 @@ +use std::{collections::HashMap, fmt}; + +// The recursive types hold their children in one of these bad boys +// TODO Should this be a VecDeque or a LinkedList instead? +type LvalChildren = Vec>; +pub type LBuiltin = fn(&mut Lval) -> ReplResult; + +// There are two types of function - builtin and lambda +#[derive(Clone)] +pub enum LvalFun { + Builtin(String, LBuiltin), // (name, function pointer) + Lambda(HashMap>, Box, Box), // (environment(?), formals, body), both should be Qexpr // TODO these should both be Rc +} + +// The book has a pointer to an Lenv in the Lambda +// I instead just store a plain old hashmap of any extras +// it's then applied in lval_call + +// The main type - all possible Blispr values +#[derive(Debug, Clone, PartialEq)] +pub enum Lval { + Lpus(LvalChildren), + Fun(LvalFun), + Num(i64), + Sym(String), + Sexpr(LvalChildren), + Qexpr(LvalChildren), +} + +impl Lval { + pub fn as_num(&self) -> Result { + match *self { + Lval::Num(n_num) => Ok(n_num), + _ => Err("".into()), + } + } + pub fn as_string(&self) -> Result { + match self { + Lval::Sym(s) => Ok(s.to_string()), + _ => Err(BlisprError::WrongType( + "symbol".to_string(), + format!("{}", self), + )), + } + } + pub fn len(&self) -> Result { + match *self { + Lval::Sexpr(ref children) | Lval::Qexpr(ref children) | Lval::Blispr(ref children) => { + Ok(children.len()) + } + _ => Err(BlisprError::NoChildren), + } + } +} + +impl fmt::Debug for LvalFun { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + LvalFun::Builtin(name, _) => write!(f, "Builtin({})", name), + LvalFun::Lambda(env, formals, body) => { + write!(f, "Lambda({{{:?}}},{{{}}},{{{}}})", env, formals, body) + } + } + } +} + +impl PartialEq for LvalFun { + fn eq(&self, other: &LvalFun) -> bool { + match self { + LvalFun::Builtin(name, _) => match other { + LvalFun::Builtin(other_name, _) => name == other_name, + _ => false, + }, + LvalFun::Lambda(env, formals, body) => match other { + LvalFun::Lambda(other_env, other_f, other_b) => { + formals == other_f && body == other_b && env == other_env + } + _ => false, + }, + } + } +} + +impl fmt::Display for Lval { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Lval::Blispr(_cells) => write!(f, ""), + Lval::Fun(lf) => match lf { + LvalFun::Builtin(name, _) => write!(f, "", name), + LvalFun::Lambda(_, formals, body) => write!(f, "(\\ {} {})", formals, body), + }, + Lval::Num(n) => write!(f, "{}", n), + Lval::Sym(s) => write!(f, "{}", s), + Lval::Sexpr(cell) => write!(f, "({})", lval_expr_print(cell)), + Lval::Qexpr(cell) => write!(f, "{{{}}}", lval_expr_print(cell)), + } + } +} + +fn lval_expr_print(cell: &[Box]) -> String { + let mut ret = String::new(); + for i in 0..cell.len() { + ret.push_str(&format!("{}", cell[i])); + if i < cell.len() - 1 { + ret.push_str(" "); + } + } + ret +} + +// Constructors +// Each allocates a brand new boxed Lval +// The recursive types start empty + +pub fn lval_blispr() -> Box { + Box::new(Lval::Blispr(Vec::new())) +} + +pub fn lval_builtin(f: LBuiltin, name: &str) -> Box { + Box::new(Lval::Fun(LvalFun::Builtin(name.to_string(), f))) +} + +pub fn lval_lambda( + env: HashMap>, + formals: Box, + body: Box, +) -> Box { + Box::new(Lval::Fun(LvalFun::Lambda(env, formals, body))) +} + +pub fn lval_num(n: i64) -> Box { + Box::new(Lval::Num(n)) +} + +pub fn lval_sym(s: &str) -> Box { + Box::new(Lval::Sym(s.into())) +} + +pub fn lval_sexpr() -> Box { + Box::new(Lval::Sexpr(Vec::new())) +} + +pub fn lval_qexpr() -> Box { + Box::new(Lval::Qexpr(Vec::new())) +} + +// Manipulating children + +// Add lval x to lval::sexpr or lval::qexpr v +pub fn lval_add(v: &mut Lval, x: &Lval) -> Result<()> { + match *v { + Lval::Sexpr(ref mut children) + | Lval::Qexpr(ref mut children) + | Lval::Blispr(ref mut children) => { + children.push(Box::new(x.clone())); + } + _ => return Err(BlisprError::NoChildren), + } + Ok(()) +} + +// Extract single element of sexpr at index i +pub fn lval_pop(v: &mut Lval, i: usize) -> BlisprResult { + match *v { + Lval::Sexpr(ref mut children) + | Lval::Qexpr(ref mut children) + | Lval::Blispr(ref mut children) => { + let ret = (&children[i]).clone(); + children.remove(i); + Ok(ret) + } + _ => Err(BlisprError::NoChildren), + } +} + +// Add each cell in y to x +pub fn lval_join(x: &mut Lval, mut y: Box) -> Result<()> { + while y.len()? > 0 { + lval_add(x, &*lval_pop(&mut y, 0)?)?; + } + Ok(()) +} diff --git a/src/repl/parser.rs b/src/repl/parser.rs new file mode 100644 index 0000000..9f25cb0 --- /dev/null +++ b/src/repl/parser.rs @@ -0,0 +1,56 @@ +use pest::{iterators::Pair, Parser}; + +#[derive(Parser)] +#[grammar = "lpus.pest"] +pub struct LpusParser; + +fn is_bracket_or_eoi(parsed: &Pair) -> bool { + if parsed.as_rule() == Rule::EOI { + return true; + } + let c = parsed.as_str(); + c == "(" || c == ")" || c == "{" || c == "}" +} + +// Read a rule with children into the given containing Lval +fn read_to_lval(mut v: &mut Lval, parsed: Pair) -> Result<()> { + for child in parsed.into_inner() { + if is_bracket_or_eoi(&child) { + continue; + } + lval_add(&mut v, &*lval_read(child)?)?; + } + Ok(()) +} + +fn lval_read(parsed: Pair) -> ReplResult { + match parsed.as_rule() { + // Rule::program => { + // let mut ret = lval_lpus(); + // read_to_lval(&mut ret, parsed)?; + // Ok(ret) + // } + // Rule::expr => lval_read(parsed.into_inner().next().unwrap()), + Rule::sexpr => { + let mut ret = lval_sexpr(); + read_to_lval(&mut ret, parsed)?; + Ok(ret) + } + // Rule::qexpr => { + // let mut ret = lval_qexpr(); + // read_to_lval(&mut ret, parsed)?; + // Ok(ret) + // } + Rule::num => Ok(lval_num(parsed.as_str().parse::()?)), + Rule::symbol => Ok(lval_sym(parsed.as_str())), + _ => unreachable!(), // COMMENT/WHITESPACE etc + } +} + +pub fn eval_str(e: &mut Lenv, s: &str) -> ReplResult { + let parsed = LpusParser::parse(Rule::sexpr, s)?.next().unwrap(); + // debug!("{}", parsed); + let mut lval_ptr = lval_read(parsed)?; + // debug!("Parsed: {:?}", *lval_ptr); + lval_eval(e, &mut *lval_ptr) +} diff --git a/src/repl/repl.rs b/src/repl/repl.rs new file mode 100644 index 0000000..3d22340 --- /dev/null +++ b/src/repl/repl.rs @@ -0,0 +1,44 @@ +use rustyline::error::ReadlineError; +use rustyline::Editor; + +fn repl(e: &mut Lenv) -> Result<()> { + println!("LPUS v0.0.1"); + println!("Use exit(), Ctrl-C, or Ctrl-D to exit prompt"); + + let mut rl = Editor::<()>::new(); + if rl.load_history("./.lpus-history.txt").is_err() { + println!("No history found."); + } + + loop { + let input = rl.readline("lpus> "); + + match input { + Ok(line) => { + rl.add_history_entry(line.as_ref()); + print_eval_result(eval_str(e, &line)); + } + Err(ReadlineError::Interrupted) => { + info!("CTRL-C"); + break; + } + Err(ReadlineError::Eof) => { + info!("CTRL-D"); + break; + } + Err(err) => { + warn!("Error: {:?}", err); + break; + } + } + } + rl.save_history("./.blispr-history.txt")?; + Ok(()) +} + +fn print_eval_result(v: ReplResult) { + match v { + Ok(res) => println!("{}", res), + Err(e) => eprintln!("Error: {}", e), + } +}