Sample repl setup

This commit is contained in:
nganhkhoa 2020-06-22 23:15:28 +07:00
parent 1707b301ff
commit 21a1a58447
5 changed files with 882 additions and 0 deletions

581
src/repl/eval.rs Normal file
View File

@ -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<String, Box<Lval>> = 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<Lval>]) -> 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)
}
}

19
src/repl/lpus.pest Normal file
View File

@ -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 }

182
src/repl/lval.rs Normal file
View File

@ -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<Box<Lval>>;
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<String, Box<Lval>>, Box<Lval>, Box<Lval>), // (environment(?), formals, body), both should be Qexpr // TODO these should both be Rc<T>
}
// 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<i64> {
match *self {
Lval::Num(n_num) => Ok(n_num),
_ => Err("".into()),
}
}
pub fn as_string(&self) -> Result<String> {
match self {
Lval::Sym(s) => Ok(s.to_string()),
_ => Err(BlisprError::WrongType(
"symbol".to_string(),
format!("{}", self),
)),
}
}
pub fn len(&self) -> Result<usize> {
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, "<toplevel>"),
Lval::Fun(lf) => match lf {
LvalFun::Builtin(name, _) => write!(f, "<builtin: {}>", 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<Lval>]) -> 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<Lval> {
Box::new(Lval::Blispr(Vec::new()))
}
pub fn lval_builtin(f: LBuiltin, name: &str) -> Box<Lval> {
Box::new(Lval::Fun(LvalFun::Builtin(name.to_string(), f)))
}
pub fn lval_lambda(
env: HashMap<String, Box<Lval>>,
formals: Box<Lval>,
body: Box<Lval>,
) -> Box<Lval> {
Box::new(Lval::Fun(LvalFun::Lambda(env, formals, body)))
}
pub fn lval_num(n: i64) -> Box<Lval> {
Box::new(Lval::Num(n))
}
pub fn lval_sym(s: &str) -> Box<Lval> {
Box::new(Lval::Sym(s.into()))
}
pub fn lval_sexpr() -> Box<Lval> {
Box::new(Lval::Sexpr(Vec::new()))
}
pub fn lval_qexpr() -> Box<Lval> {
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<Lval>) -> Result<()> {
while y.len()? > 0 {
lval_add(x, &*lval_pop(&mut y, 0)?)?;
}
Ok(())
}

56
src/repl/parser.rs Normal file
View File

@ -0,0 +1,56 @@
use pest::{iterators::Pair, Parser};
#[derive(Parser)]
#[grammar = "lpus.pest"]
pub struct LpusParser;
fn is_bracket_or_eoi(parsed: &Pair<Rule>) -> 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<Rule>) -> 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<Rule>) -> 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::<i64>()?)),
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)
}

44
src/repl/repl.rs Normal file
View File

@ -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),
}
}