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_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,
|
||||
|
|
|
@ -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(()) },
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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, "|"),
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,9 +108,17 @@ impl Func {
|
|||
filled_args.append(&mut arg_values);
|
||||
inner.call(filled_args)
|
||||
}
|
||||
},
|
||||
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 }))
|
||||
}
|
||||
Ordering::Less => {
|
||||
Ok(Value::Func(Func::Partial { inner: Box::new(self.clone()), filled_args: arg_values }))
|
||||
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())
|
||||
|
@ -324,6 +332,10 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> Result<bool, String> {
|
||||
Ok(self.len()? == 0)
|
||||
}
|
||||
|
||||
pub fn fracdiv(&self, other: &Value) -> Result<Value, String> {
|
||||
use Value::*;
|
||||
match (self, other) {
|
||||
|
|
Loading…
Reference in a new issue