refactoring, stdlib
This commit is contained in:
parent
285c13f03e
commit
48cc98a6ed
8 changed files with 141 additions and 16 deletions
|
@ -6,6 +6,7 @@ use rustyline::{self, error::ReadlineError};
|
||||||
|
|
||||||
const C_RESET: &str = "\x1b[0m";
|
const C_RESET: &str = "\x1b[0m";
|
||||||
const C_BLUE: &str = "\x1b[94m";
|
const C_BLUE: &str = "\x1b[94m";
|
||||||
|
const C_RED: &str = "\x1b[91m";
|
||||||
const PROMPT: &str = "\x1b[94m>> \x1b[0m";
|
const PROMPT: &str = "\x1b[94m>> \x1b[0m";
|
||||||
|
|
||||||
fn panic_hook(info: &PanicInfo) {
|
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 src = fs::read_to_string(fname)?;
|
||||||
let res = interpret(&src, Some(fname.into()), None, false);
|
let res = interpret(&src, Some(fname.into()), None, false);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
println!("{}", e);
|
eprintln!("Error: {}", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
repl()?;
|
repl()?;
|
||||||
|
@ -53,7 +54,7 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match result {
|
match result {
|
||||||
Ok(Value::Nil) => (),
|
Ok(Value::Nil) => (),
|
||||||
Ok(value) => println!("{}", value.repr()),
|
Ok(value) => println!("{}", value.repr()),
|
||||||
Err(e) => print!("{}", e)
|
Err(e) => eprintln!("{}Error: {}{}", C_RED, C_RESET, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Eof) => break,
|
Err(ReadlineError::Eof) => break,
|
||||||
|
|
|
@ -37,6 +37,7 @@ impl Environment {
|
||||||
self.map.insert(name, value);
|
self.map.insert(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
|
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
|
||||||
match self.map.contains_key(&name) {
|
match self.map.contains_key(&name) {
|
||||||
true => { self.map.insert(name, value); Ok(()) },
|
true => { self.map.insert(name, value); Ok(()) },
|
||||||
|
|
|
@ -138,7 +138,7 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
=> eval_pipeline(lhs, rhs, op, env),
|
=> eval_pipeline(lhs, rhs, op, env),
|
||||||
o => todo!("{:?}", o) // TODO other operations
|
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::Unary { arg, op } => eval_unary(arg, op, env),
|
||||||
Expr::List { items } => {
|
Expr::List { items } => {
|
||||||
let mut list = Vec::with_capacity(items.len());
|
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 {
|
if let Token { ty: TokenType::Ident(name), ..} = token {
|
||||||
env.borrow_mut()
|
env.borrow_mut()
|
||||||
.get(name)
|
.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 {
|
} else {
|
||||||
unreachable!()
|
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> {
|
pub fn eval_unary(arg: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
let a = eval_expr(arg, env)?;
|
let a = eval_expr(arg, env)?;
|
||||||
match op.ty {
|
match op.ty {
|
||||||
|
|
|
@ -63,7 +63,11 @@ impl Lexer {
|
||||||
file: self.filename.clone(),
|
file: self.filename.clone(),
|
||||||
pos: self.start,
|
pos: self.start,
|
||||||
line: self.line,
|
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, "+="),
|
Some('=') => self.add_token(TokenType::PlusEqual, "+="),
|
||||||
_ => self.add_token(TokenType::Plus, "+"),
|
_ => self.add_token(TokenType::Plus, "+"),
|
||||||
},
|
},
|
||||||
'-' => match self.expect(&['=', '>']) {
|
'-' => match self.expect(&['=']) {
|
||||||
Some('=') => self.add_token(TokenType::MinusEqual, "-="),
|
Some('=') => self.add_token(TokenType::MinusEqual, "-="),
|
||||||
_ => self.add_token(TokenType::Minus, "-"),
|
_ => self.add_token(TokenType::Minus, "-"),
|
||||||
},
|
},
|
||||||
|
@ -198,12 +202,12 @@ impl Lexer {
|
||||||
Some('>') => self.add_token(TokenType::PipePoint, "|>"),
|
Some('>') => self.add_token(TokenType::PipePoint, "|>"),
|
||||||
Some('&') => self.add_token(TokenType::PipeAmper, "|&"),
|
Some('&') => self.add_token(TokenType::PipeAmper, "|&"),
|
||||||
Some('/') => match self.expect(&['/']) {
|
Some('/') => match self.expect(&['/']) {
|
||||||
Some(_) => self.add_token(TokenType::PipeDoubleSlash, "|//"),
|
Some('/') => self.add_token(TokenType::PipeDoubleSlash, "|//"),
|
||||||
None => self.add_token(TokenType::PipeSlash, "|/")
|
_ => self.add_token(TokenType::PipeSlash, "|/")
|
||||||
},
|
},
|
||||||
Some('\\') => match self.expect(&['\\']) {
|
Some('\\') => match self.expect(&['\\']) {
|
||||||
Some(_) => self.add_token(TokenType::PipeDoubleBackslash, "|\\\\"),
|
Some('\\') => self.add_token(TokenType::PipeDoubleBackslash, "|\\\\"),
|
||||||
None => self.add_token(TokenType::PipeBackslash, "|\\")
|
_ => self.add_token(TokenType::PipeBackslash, "|\\")
|
||||||
},
|
},
|
||||||
_ => self.add_token(TokenType::Pipe, "|"),
|
_ => self.add_token(TokenType::Pipe, "|"),
|
||||||
},
|
},
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl From<&str> for RuntimeError {
|
||||||
|
|
||||||
impl fmt::Display for ParserError {
|
impl fmt::Display for ParserError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Error: {}\n In {} at {},{}\n",
|
write!(f, "{}\n In {} at {},{}",
|
||||||
self.message,
|
self.message,
|
||||||
self.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
self.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
||||||
self.pos.line,
|
self.pos.line,
|
||||||
|
@ -102,9 +102,9 @@ impl fmt::Display for ParserError {
|
||||||
|
|
||||||
impl fmt::Display for RuntimeError {
|
impl fmt::Display for RuntimeError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(f, "Error: {}", self.message)?;
|
writeln!(f, "{}", self.message)?;
|
||||||
for frame in &self.stacktrace {
|
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.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.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
||||||
frame.pos.line,
|
frame.pos.line,
|
||||||
|
|
|
@ -277,6 +277,7 @@ impl Parser {
|
||||||
let op = self.next();
|
let op = self.next();
|
||||||
let right = self.logical_or()?;
|
let right = self.logical_or()?;
|
||||||
if op.ty == TokenType::PipeSlash || op.ty == TokenType::PipeBackslash {
|
if op.ty == TokenType::PipeSlash || op.ty == TokenType::PipeBackslash {
|
||||||
|
self.err_on_eof()?;
|
||||||
let next = self.next();
|
let next = self.next();
|
||||||
if next.ty != TokenType::Comma {
|
if next.ty != TokenType::Comma {
|
||||||
return Err(self.mk_error("Expected comma after first argument"))
|
return Err(self.mk_error("Expected comma after first argument"))
|
||||||
|
|
|
@ -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 }));
|
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_list, arg_count: 1, name }));
|
||||||
name = Rc::from("take");
|
name = Rc::from("take");
|
||||||
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_take, arg_count: 2, name }));
|
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> {
|
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
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -108,9 +108,17 @@ impl Func {
|
||||||
filled_args.append(&mut arg_values);
|
filled_args.append(&mut arg_values);
|
||||||
inner.call(filled_args)
|
inner.call(filled_args)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Ordering::Less => {
|
Ordering::Less if arg_values.is_empty() => Err(RuntimeError::new_incomplete(
|
||||||
Ok(Value::Func(Func::Partial { inner: Box::new(self.clone()), filled_args: arg_values }))
|
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(
|
Ordering::Greater => Err(RuntimeError::new_incomplete(
|
||||||
format!("Too many arguments for function: expected {}, got {}", self.arg_count(), arg_values.len())
|
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))
|
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> {
|
pub fn fracdiv(&self, other: &Value) -> Result<Value, String> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
|
|
Loading…
Reference in a new issue