refactoring, stdlib

This commit is contained in:
TriMill 2022-09-17 19:23:54 -04:00
parent 285c13f03e
commit 48cc98a6ed
8 changed files with 141 additions and 16 deletions

View File

@ -6,6 +6,7 @@ use rustyline::{self, error::ReadlineError};
const C_RESET: &str = "\x1b[0m";
const C_BLUE: &str = "\x1b[94m";
const C_RED: &str = "\x1b[91m";
const PROMPT: &str = "\x1b[94m>> \x1b[0m";
fn panic_hook(info: &PanicInfo) {
@ -32,7 +33,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let src = fs::read_to_string(fname)?;
let res = interpret(&src, Some(fname.into()), None, false);
if let Err(e) = res {
println!("{}", e);
eprintln!("Error: {}", e);
}
} else {
repl()?;
@ -53,7 +54,7 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
match result {
Ok(Value::Nil) => (),
Ok(value) => println!("{}", value.repr()),
Err(e) => print!("{}", e)
Err(e) => eprintln!("{}Error: {}{}", C_RED, C_RESET, e)
}
}
Err(ReadlineError::Eof) => break,

View File

@ -37,6 +37,7 @@ impl Environment {
self.map.insert(name, value);
}
#[allow(clippy::result_unit_err)]
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
match self.map.contains_key(&name) {
true => { self.map.insert(name, value); Ok(()) },

View File

@ -138,7 +138,7 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
=> eval_pipeline(lhs, rhs, op, env),
o => todo!("{:?}", o) // TODO other operations
},
Expr::Ternary { .. } => todo!(),
Expr::Ternary { arg1, arg2, arg3, op } => eval_ternary(arg1, arg2, arg3, op, env),
Expr::Unary { arg, op } => eval_unary(arg, op, env),
Expr::List { items } => {
let mut list = Vec::with_capacity(items.len());
@ -200,7 +200,7 @@ pub fn eval_ident(token: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
if let Token { ty: TokenType::Ident(name), ..} = token {
env.borrow_mut()
.get(name)
.ok_or_else(|| RuntimeError::new("Variable not defined in scope", token.pos.clone()))
.ok_or_else(|| RuntimeError::new(format!("Variable {} not defined in scope", name), token.pos.clone()))
} else {
unreachable!()
}
@ -407,6 +407,34 @@ fn eval_pipeline_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeE
}
#[allow(clippy::needless_collect)] // collect is necesary to allow for rev() call
pub fn eval_ternary(arg1: &Expr, arg2: &Expr, arg3: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
match op.ty {
TokenType::PipeSlash => {
let iter = eval_expr(arg1, env.clone())?;
let mut result = eval_expr(arg2, env.clone())?;
let func = eval_expr(arg3, env)?;
for v in iter.iter().map_err(|e| RuntimeError::new(e, op.pos.clone()))? {
let v = v.map_err(|e| e.complete(op.pos.clone()))?;
result = func.call(vec![result, v]).map_err(|e| e.complete(op.pos.clone()))?;
}
Ok(result)
},
TokenType::PipeBackslash => {
let iter = eval_expr(arg1, env.clone())?;
let mut result = eval_expr(arg2, env.clone())?;
let func = eval_expr(arg3, env)?;
let lst = iter.iter().map_err(|e| RuntimeError::new(e, op.pos.clone()))?.collect::<Vec<Result<Value, RuntimeError>>>();
for v in lst.into_iter().rev() {
let v = v.map_err(|e| e.complete(op.pos.clone()))?;
result = func.call(vec![v, result]).map_err(|e| e.complete(op.pos.clone()))?;
}
Ok(result)
},
_ => unreachable!()
}
}
pub fn eval_unary(arg: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
let a = eval_expr(arg, env)?;
match op.ty {

View File

@ -63,7 +63,11 @@ impl Lexer {
file: self.filename.clone(),
pos: self.start,
line: self.line,
col: self.col - (self.current - self.start)
col: if self.col < (self.current - self.start) {
0
} else {
self.col - (self.current - self.start)
}
}
});
}
@ -144,7 +148,7 @@ impl Lexer {
Some('=') => self.add_token(TokenType::PlusEqual, "+="),
_ => self.add_token(TokenType::Plus, "+"),
},
'-' => match self.expect(&['=', '>']) {
'-' => match self.expect(&['=']) {
Some('=') => self.add_token(TokenType::MinusEqual, "-="),
_ => self.add_token(TokenType::Minus, "-"),
},
@ -198,12 +202,12 @@ impl Lexer {
Some('>') => self.add_token(TokenType::PipePoint, "|>"),
Some('&') => self.add_token(TokenType::PipeAmper, "|&"),
Some('/') => match self.expect(&['/']) {
Some(_) => self.add_token(TokenType::PipeDoubleSlash, "|//"),
None => self.add_token(TokenType::PipeSlash, "|/")
Some('/') => self.add_token(TokenType::PipeDoubleSlash, "|//"),
_ => self.add_token(TokenType::PipeSlash, "|/")
},
Some('\\') => match self.expect(&['\\']) {
Some(_) => self.add_token(TokenType::PipeDoubleBackslash, "|\\\\"),
None => self.add_token(TokenType::PipeBackslash, "|\\")
Some('\\') => self.add_token(TokenType::PipeDoubleBackslash, "|\\\\"),
_ => self.add_token(TokenType::PipeBackslash, "|\\")
},
_ => self.add_token(TokenType::Pipe, "|"),
},

View File

@ -91,7 +91,7 @@ impl From<&str> for RuntimeError {
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error: {}\n In {} at {},{}\n",
write!(f, "{}\n In {} at {},{}",
self.message,
self.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
self.pos.line,
@ -102,9 +102,9 @@ impl fmt::Display for ParserError {
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Error: {}", self.message)?;
writeln!(f, "{}", self.message)?;
for frame in &self.stacktrace {
writeln!(f, " In {} at {}:{}:{}",
write!(f, "\n In {} at {}:{}:{}",
frame.fn_name.as_ref().map(|o| o.as_ref()).unwrap_or("<anonymous fn>"),
frame.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
frame.pos.line,

View File

@ -277,6 +277,7 @@ impl Parser {
let op = self.next();
let right = self.logical_or()?;
if op.ty == TokenType::PipeSlash || op.ty == TokenType::PipeBackslash {
self.err_on_eof()?;
let next = self.next();
if next.ty != TokenType::Comma {
return Err(self.mk_error("Expected comma after first argument"))

View File

@ -36,6 +36,16 @@ pub fn load(env: &mut Environment) {
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_list, arg_count: 1, name }));
name = Rc::from("take");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_take, arg_count: 2, name }));
name = Rc::from("skip");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_skip, arg_count: 2, name }));
name = Rc::from("forall");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_forall, arg_count: 2, name }));
name = Rc::from("exists");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_exists, arg_count: 2, name }));
name = Rc::from("min");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_min, arg_count: 2, name }));
name = Rc::from("max");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_max, arg_count: 2, name }));
}
fn fn_str(args: Vec<Value>) -> Result<Value, RuntimeError> {
@ -185,3 +195,71 @@ fn fn_take(args: Vec<Value>) -> Result<Value, RuntimeError> {
func: take_inner
}))
}
fn skip_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
let mut d = if let Value::Int(d) = data.borrow()[0] { d } else {
unreachable!() // checked by fn_skip()
};
while d > 0 {
iter_data.borrow_mut()[0].next();
d -= 1;
}
data.borrow_mut()[0] = Value::Int(d);
match iter_data.borrow_mut()[0].next() {
None => Ok(Value::Nil),
Some(x) => x
}
}
fn fn_skip(args: Vec<Value>) -> Result<Value, RuntimeError> {
let n = match args[0] {
Value::Int(n) if n <= 0 => return Err(RuntimeError::new_incomplete("First argument to skip must be nonnegative")),
Value::Int(n) => n,
_ => return Err(RuntimeError::new_incomplete("First argument to skip must be an integer"))
};
let it = args[1].iter()?;
Ok(Value::Func(Func::BuiltinClosure {
arg_count: 0,
data: Rc::new(RefCell::new(vec![Value::Int(n)])),
iter_data: Rc::new(RefCell::new(vec![it])),
func: skip_inner
}))
}
fn fn_forall(args: Vec<Value>) -> Result<Value, RuntimeError> {
let func = &args[0];
for item in args[1].iter()? {
let item = item?;
if !func.call(vec![item])?.truthy() {
return Ok(Value::Bool(false))
}
}
Ok(Value::Bool(true))
}
fn fn_exists(args: Vec<Value>) -> Result<Value, RuntimeError> {
let func = &args[0];
for item in args[1].iter()? {
let item = item?;
if func.call(vec![item])?.truthy() {
return Ok(Value::Bool(true))
}
}
Ok(Value::Bool(false))
}
fn fn_min(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0].partial_cmp(&args[1]) {
None => Err("Arguments to min must be comparable".into()),
Some(Ordering::Greater) => Ok(args[1].clone()),
_ => Ok(args[0].clone())
}
}
fn fn_max(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0].partial_cmp(&args[1]) {
None => Err("Arguments to max must be comparable".into()),
Some(Ordering::Less) => Ok(args[1].clone()),
_ => Ok(args[0].clone())
}
}

View File

@ -108,9 +108,17 @@ impl Func {
filled_args.append(&mut arg_values);
inner.call(filled_args)
}
}
Ordering::Less => {
Ok(Value::Func(Func::Partial { inner: Box::new(self.clone()), filled_args: arg_values }))
},
Ordering::Less if arg_values.is_empty() => Err(RuntimeError::new_incomplete(
format!("Cannot call this function with zero arguments: expected {}", self.arg_count())
)),
Ordering::Less => match self {
Self::Partial { inner, filled_args } => {
let mut args = filled_args.clone();
args.append(&mut arg_values);
Ok(Value::Func(Func::Partial { inner: inner.clone(), filled_args: args }))
}
f => Ok(Value::Func(Func::Partial { inner: Box::new(f.clone()), filled_args: arg_values }))
},
Ordering::Greater => Err(RuntimeError::new_incomplete(
format!("Too many arguments for function: expected {}, got {}", self.arg_count(), arg_values.len())
@ -323,6 +331,10 @@ impl Value {
v => Err(format!("{:?} has no length", v))
}
}
pub fn is_empty(&self) -> Result<bool, String> {
Ok(self.len()? == 0)
}
pub fn fracdiv(&self, other: &Value) -> Result<Value, String> {
use Value::*;