improved repl & errors
This commit is contained in:
parent
48cc98a6ed
commit
674d2ced7d
9 changed files with 291 additions and 57 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -83,6 +83,7 @@ dependencies = [
|
|||
"backtrace",
|
||||
"complexpr",
|
||||
"rustyline",
|
||||
"rustyline-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -384,6 +385,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustyline-derive"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "107c3d5d7f370ac09efa62a78375f94d94b8a33c61d8c278b96683fb4dbf2d8d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
|
|
@ -9,3 +9,4 @@ edition = "2021"
|
|||
complexpr = { path = "../complexpr" }
|
||||
rustyline = "10.0.0"
|
||||
backtrace = "0.3.66"
|
||||
rustyline-derive = "0.7.0"
|
||||
|
|
185
complexpr-bin/src/helper.rs
Normal file
185
complexpr-bin/src/helper.rs
Normal file
|
@ -0,0 +1,185 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use complexpr::env::EnvRef;
|
||||
use rustyline::{completion::Completer, validate::Validator};
|
||||
use rustyline::highlight::Highlighter;
|
||||
use rustyline::hint::HistoryHinter;
|
||||
use rustyline::validate::{MatchingBracketValidator, ValidationResult, ValidationContext};
|
||||
use rustyline_derive::{Helper, Hinter};
|
||||
|
||||
#[derive(Helper, Hinter)]
|
||||
pub struct CxprHelper {
|
||||
#[rustyline(Validator)]
|
||||
pub validator: MatchingBracketValidator,
|
||||
#[rustyline(Hinter)]
|
||||
pub hinter: HistoryHinter,
|
||||
pub colored_prompt: String,
|
||||
pub env: EnvRef
|
||||
}
|
||||
|
||||
fn find_paired_bracket(line: &str, pos: usize) -> Result<usize, bool> {
|
||||
if pos >= line.len() {
|
||||
return Err(false)
|
||||
}
|
||||
let c = line.as_bytes()[pos];
|
||||
let (target, fwd) = match c {
|
||||
b'(' => (b')', true),
|
||||
b')' => (b'(', false),
|
||||
b'[' => (b']', true),
|
||||
b']' => (b'[', false),
|
||||
b'{' => (b'}', true),
|
||||
b'}' => (b'{', false),
|
||||
_ => return Err(false),
|
||||
};
|
||||
let mut depth = 0;
|
||||
let mut idx = 0;
|
||||
if fwd {
|
||||
let bytes = &line.as_bytes()[pos+1..];
|
||||
for &b in bytes {
|
||||
if b == c {
|
||||
depth += 1;
|
||||
} else if b == target {
|
||||
if depth == 0 {
|
||||
return Ok(pos + idx + 1)
|
||||
} else {
|
||||
depth -= 1;
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
} else {
|
||||
let bytes = &line.as_bytes()[..pos];
|
||||
for &b in bytes.iter().rev() {
|
||||
if b == c {
|
||||
depth += 1;
|
||||
} else if b == target {
|
||||
if depth == 0 {
|
||||
return Ok(pos - idx - 1)
|
||||
} else {
|
||||
depth -= 1;
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
Err(true)
|
||||
}
|
||||
|
||||
impl Highlighter for CxprHelper {
|
||||
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
|
||||
&'s self,
|
||||
prompt: &'p str,
|
||||
default: bool,
|
||||
) -> Cow<'b, str> {
|
||||
if default {
|
||||
Cow::Borrowed(&self.colored_prompt)
|
||||
} else {
|
||||
Cow::Borrowed(prompt)
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
|
||||
match find_paired_bracket(line, pos) {
|
||||
Err(false) => Cow::Borrowed(line),
|
||||
Err(true) => {
|
||||
let mut line = line.to_owned();
|
||||
line.replace_range(pos..=pos, &format!("\x1b[91m{}\x1b[0m", line.as_bytes()[pos] as char));
|
||||
Cow::Owned(line)
|
||||
},
|
||||
Ok(match_pos) => {
|
||||
let fst = pos.min(match_pos);
|
||||
let snd = pos.max(match_pos);
|
||||
let mut line = line.to_owned();
|
||||
line.replace_range(snd..=snd, &format!("\x1b[92m{}\x1b[0m", line.as_bytes()[snd] as char));
|
||||
line.replace_range(fst..=fst, &format!("\x1b[92m{}\x1b[0m", line.as_bytes()[fst] as char));
|
||||
Cow::Owned(line)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight_char(&self, line: &str, _: usize) -> bool {
|
||||
!line.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_brackets(input: &str) -> ValidationResult {
|
||||
let mut stack = vec![];
|
||||
let mut in_string = false;
|
||||
let mut in_char = false;
|
||||
let mut in_escape = false;
|
||||
for c in input.chars() {
|
||||
if in_string {
|
||||
if in_escape {
|
||||
in_escape = false
|
||||
} else if c == '\\' {
|
||||
in_escape = true
|
||||
} else if c == '"' {
|
||||
in_string = false
|
||||
}
|
||||
} else if in_char {
|
||||
if in_escape {
|
||||
in_escape = false
|
||||
} else if c == '\\' {
|
||||
in_escape = true
|
||||
} else if c == '\'' {
|
||||
in_char = false
|
||||
}
|
||||
} else {
|
||||
match c {
|
||||
'(' | '[' | '{' => stack.push(c),
|
||||
')' | ']' | '}' => match (stack.pop(), c) {
|
||||
(Some('('), ')') | (Some('['), ']') | (Some('{'), '}') => (),
|
||||
(Some(c), _) => return ValidationResult::Invalid(
|
||||
Some(format!(" << Mismatched brackets: {:?} is not properly closed", c))
|
||||
),
|
||||
(None, c) => return ValidationResult::Invalid(
|
||||
Some(format!(" << Mismatched brackets: {:?} is unpaired", c))
|
||||
),
|
||||
},
|
||||
'"' => in_string = true,
|
||||
'\'' => in_char = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if in_string {
|
||||
ValidationResult::Incomplete
|
||||
} else if stack.is_empty() {
|
||||
ValidationResult::Valid(None)
|
||||
} else {
|
||||
ValidationResult::Incomplete
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for CxprHelper {
|
||||
fn validate(&self, ctx: &mut ValidationContext) -> rustyline::Result<ValidationResult> {
|
||||
Ok(validate_brackets(ctx.input()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for CxprHelper {
|
||||
fn complete(&self, line: &str, pos: usize, _: &rustyline::Context<'_>)
|
||||
-> rustyline::Result<(usize, Vec<Self::Candidate>)> {
|
||||
let mut res = String::new();
|
||||
for ch in line[..pos].chars().rev() {
|
||||
match ch {
|
||||
'0'..='9' | 'a'..='z' | 'A'..='Z' | '_' => res.push(ch),
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
let res: String = res.chars().rev().collect();
|
||||
let mut keys = self.env.borrow().items().keys()
|
||||
.filter(|x| x.starts_with(&res))
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
keys.sort();
|
||||
Ok((pos - res.len(), keys))
|
||||
}
|
||||
|
||||
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {
|
||||
let end = line.pos();
|
||||
line.replace(start..end, elected);
|
||||
}
|
||||
|
||||
type Candidate = String;
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
use std::{rc::Rc, cell::RefCell, fs, panic::{self, PanicInfo}};
|
||||
use std::{fs, panic::{self, PanicInfo}};
|
||||
|
||||
use backtrace::Backtrace;
|
||||
use complexpr::{env::Environment, interpreter::interpret, value::Value, stdlib};
|
||||
use rustyline::{self, error::ReadlineError};
|
||||
use rustyline::{self, error::ReadlineError, Config, CompletionType, EditMode, hint::HistoryHinter, validate::MatchingBracketValidator, Editor};
|
||||
|
||||
mod helper;
|
||||
use helper::CxprHelper;
|
||||
|
||||
const C_RESET: &str = "\x1b[0m";
|
||||
const C_BLUE: &str = "\x1b[94m";
|
||||
|
@ -42,12 +45,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
|
||||
fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = Config::builder()
|
||||
.history_ignore_space(true)
|
||||
.completion_type(CompletionType::List)
|
||||
.edit_mode(EditMode::Emacs)
|
||||
.build();
|
||||
let env = Environment::new().wrap();
|
||||
let h = CxprHelper {
|
||||
hinter: HistoryHinter {},
|
||||
colored_prompt: PROMPT.to_owned(),
|
||||
validator: MatchingBracketValidator::new(),
|
||||
env: env.clone(),
|
||||
};
|
||||
let mut rl = Editor::with_config(config)?;
|
||||
rl.set_helper(Some(h));
|
||||
println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET);
|
||||
let mut rl = rustyline::Editor::<()>::new()?;
|
||||
let env = Rc::new(RefCell::new(Environment::new()));
|
||||
stdlib::load(&mut env.borrow_mut());
|
||||
loop {
|
||||
let readline = rl.readline(PROMPT);
|
||||
let readline = rl.readline(">> ");
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
let result = interpret(&line, None, Some(env.clone()), true);
|
||||
|
|
|
@ -47,6 +47,10 @@ impl Environment {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn items(&self) -> &HashMap<Rc<str>, Value> {
|
||||
&self.map
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Environment {
|
||||
|
|
|
@ -4,6 +4,12 @@ use num_traits::Pow;
|
|||
|
||||
use crate::{value::{Value, Complex, Func, CIterator}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position, env::{EnvRef, Environment}};
|
||||
|
||||
thread_local!(static PIPE_NAME: Option<Rc<str>> = Some(Rc::from("<pipeline>")));
|
||||
thread_local!(static FORLOOP_NAME: Option<Rc<str>> = Some(Rc::from("<for loop>")));
|
||||
fn exit_pipe(pos: &Position) -> impl FnOnce(RuntimeError) -> RuntimeError + '_ {
|
||||
|e: RuntimeError| e.exit_fn(PIPE_NAME.with(|x| x.clone()), pos.clone())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Unwind {
|
||||
|
@ -75,7 +81,7 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
|||
}
|
||||
if let Ok(i) = iterator {
|
||||
for v in i {
|
||||
let v = v.map_err(|e| e.complete(iter_pos.clone()))?;
|
||||
let v = v.map_err(|e| e.exit_fn(FORLOOP_NAME.with(|x| x.clone()), iter_pos.clone()))?;
|
||||
let env = env.clone();
|
||||
env.borrow_mut().set(name.clone(), v).expect("unreachable");
|
||||
match eval_stmt(stmt, env) {
|
||||
|
@ -157,13 +163,15 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
|
|||
Ok(Value::from(map))
|
||||
},
|
||||
Expr::FuncCall { func, args, pos } => {
|
||||
let func = eval_expr(func, env.clone())?;
|
||||
let lhs = eval_expr(func, env.clone())?;
|
||||
let mut arg_values = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
let result = eval_expr(arg, env.clone())?;
|
||||
arg_values.push(result);
|
||||
}
|
||||
func.call(arg_values).map_err(|e| e.complete(pos.clone()))
|
||||
let func = lhs.as_func()
|
||||
.map_err(|e| RuntimeError::new(e, pos.clone()))?;
|
||||
func.call(arg_values).map_err(|e| e.exit_fn(func.name(), pos.clone()))
|
||||
},
|
||||
Expr::Index { lhs, index, pos } => {
|
||||
let l = eval_expr(lhs, env.clone())?;
|
||||
|
@ -325,7 +333,8 @@ pub fn eval_comp(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Valu
|
|||
fn pipecolon_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
|
||||
let f = &data.borrow()[0];
|
||||
if let Some(next) = iter_data.borrow_mut()[0].next() {
|
||||
f.call(vec![next?])
|
||||
let func = f.as_func()?;
|
||||
func.call(vec![next?])
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
@ -337,7 +346,8 @@ fn pipequestion_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: R
|
|||
let next = iter_data.borrow_mut()[0].next();
|
||||
if let Some(next) = next {
|
||||
let next = next?;
|
||||
let success = f.call(vec![next.clone()])?.truthy();
|
||||
let func = f.as_func()?;
|
||||
let success = func.call(vec![next.clone()])?.truthy();
|
||||
if success {
|
||||
return Ok(next)
|
||||
}
|
||||
|
@ -347,20 +357,22 @@ fn pipequestion_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: R
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn eval_pipeline(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||
let l = eval_expr(lhs, env.clone())?;
|
||||
let r = eval_expr(rhs, env)?;
|
||||
eval_pipeline_inner(l, r, op).map_err(|e| e.complete(op.pos.clone()))
|
||||
let f = r.as_func().map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
|
||||
eval_pipeline_inner(l, f, op).map_err(exit_pipe(&op.pos))
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_collect)] // collect is necesary to allow for rev() call
|
||||
fn eval_pipeline_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeError> {
|
||||
fn eval_pipeline_inner(l: Value, r: &Func, op: &Token) -> Result<Value, RuntimeError> {
|
||||
match op.ty {
|
||||
TokenType::PipePoint => r.call(vec![l]),
|
||||
TokenType::PipeColon => {
|
||||
Ok(Value::Func(Func::BuiltinClosure {
|
||||
arg_count: 0,
|
||||
data: Rc::new(RefCell::new(vec![r])),
|
||||
data: Rc::new(RefCell::new(vec![Value::Func(r.clone())])),
|
||||
iter_data: Rc::new(RefCell::new(vec![l.iter()?])),
|
||||
func: pipecolon_inner,
|
||||
}))
|
||||
|
@ -368,7 +380,7 @@ fn eval_pipeline_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeE
|
|||
TokenType::PipeQuestion => {
|
||||
Ok(Value::Func(Func::BuiltinClosure {
|
||||
arg_count: 0,
|
||||
data: Rc::new(RefCell::new(vec![r])),
|
||||
data: Rc::new(RefCell::new(vec![Value::Func(r.clone())])),
|
||||
iter_data: Rc::new(RefCell::new(vec![l.iter()?])),
|
||||
func: pipequestion_inner,
|
||||
}))
|
||||
|
@ -377,12 +389,12 @@ fn eval_pipeline_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeE
|
|||
let mut result = Value::Nil;
|
||||
let mut first_iter = true;
|
||||
for v in l.iter().map_err(|e| RuntimeError::new(e, op.pos.clone()))? {
|
||||
let v = v.map_err(|e| e.complete(op.pos.clone()))?;
|
||||
let v = v.map_err(exit_pipe(&op.pos))?;
|
||||
if first_iter {
|
||||
result = v;
|
||||
first_iter = false;
|
||||
} else {
|
||||
result = r.call(vec![result, v]).map_err(|e| e.complete(op.pos.clone()))?;
|
||||
result = r.call(vec![result, v]).map_err(exit_pipe(&op.pos))?;
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
|
@ -392,12 +404,12 @@ fn eval_pipeline_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeE
|
|||
let mut first_iter = true;
|
||||
let lst = l.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()))?;
|
||||
let v = v.map_err(exit_pipe(&op.pos))?;
|
||||
if first_iter {
|
||||
result = v;
|
||||
first_iter = false;
|
||||
} else {
|
||||
result = r.call(vec![v, result]).map_err(|e| e.complete(op.pos.clone()))?;
|
||||
result = r.call(vec![v, result]).map_err(exit_pipe(&op.pos))?;
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
|
@ -414,9 +426,11 @@ pub fn eval_ternary(arg1: &Expr, arg2: &Expr, arg3: &Expr, op: &Token, env: EnvR
|
|||
let iter = eval_expr(arg1, env.clone())?;
|
||||
let mut result = eval_expr(arg2, env.clone())?;
|
||||
let func = eval_expr(arg3, env)?;
|
||||
let func = func.as_func()
|
||||
.map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
|
||||
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()))?;
|
||||
let v = v.map_err(exit_pipe(&op.pos))?;
|
||||
result = func.call(vec![result, v]).map_err(exit_pipe(&op.pos))?;
|
||||
}
|
||||
Ok(result)
|
||||
},
|
||||
|
@ -424,10 +438,12 @@ pub fn eval_ternary(arg1: &Expr, arg2: &Expr, arg3: &Expr, op: &Token, env: EnvR
|
|||
let iter = eval_expr(arg1, env.clone())?;
|
||||
let mut result = eval_expr(arg2, env.clone())?;
|
||||
let func = eval_expr(arg3, env)?;
|
||||
let func = func.as_func()
|
||||
.map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
|
||||
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()))?;
|
||||
let v = v.map_err(exit_pipe(&op.pos))?;
|
||||
result = func.call(vec![v, result]).map_err(exit_pipe(&op.pos))?;
|
||||
}
|
||||
Ok(result)
|
||||
},
|
||||
|
|
|
@ -18,6 +18,16 @@ pub struct Position {
|
|||
pub file: Option<Rc<str>>
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}:{}",
|
||||
self.file.as_ref().map(|x| x.as_ref()).unwrap_or("<unknown>"),
|
||||
self.line,
|
||||
self.col
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParserError {
|
||||
|
@ -27,7 +37,7 @@ pub struct ParserError {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Stackframe {
|
||||
pub pos: Position,
|
||||
pub pos: Option<Position>,
|
||||
pub fn_name: Option<Rc<str>>,
|
||||
}
|
||||
|
||||
|
@ -48,7 +58,7 @@ impl RuntimeError {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_incomplete<S>(message: S) -> Self
|
||||
pub fn new_no_pos<S>(message: S) -> Self
|
||||
where S: Into<String> {
|
||||
Self {
|
||||
message: message.into(),
|
||||
|
@ -57,21 +67,14 @@ impl RuntimeError {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn complete(mut self, last_pos: Position) -> Self {
|
||||
if self.last_pos.is_none() {
|
||||
self.last_pos = Some(last_pos);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn exit_fn(mut self, fn_name: Option<Rc<str>>, pos: Position) -> Self {
|
||||
self.stacktrace.push(Stackframe { pos: self.last_pos.expect("RuntimeError never completed after construction"), fn_name });
|
||||
self.stacktrace.push(Stackframe { pos: self.last_pos, fn_name });
|
||||
self.last_pos = Some(pos);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn finish(mut self, ctx_name: Option<Rc<str>>) -> Self {
|
||||
self.stacktrace.push(Stackframe { pos: self.last_pos.expect("RuntimeError never completed after construction"), fn_name: ctx_name });
|
||||
self.stacktrace.push(Stackframe { pos: self.last_pos, fn_name: ctx_name });
|
||||
self.last_pos = None;
|
||||
self
|
||||
}
|
||||
|
@ -79,13 +82,13 @@ impl RuntimeError {
|
|||
|
||||
impl From<String> for RuntimeError {
|
||||
fn from(s: String) -> Self {
|
||||
Self::new_incomplete(s)
|
||||
Self::new_no_pos(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RuntimeError {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::new_incomplete(s)
|
||||
Self::new_no_pos(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,14 +105,13 @@ impl fmt::Display for ParserError {
|
|||
|
||||
impl fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "{}", self.message)?;
|
||||
write!(f, "{}", self.message)?;
|
||||
for frame in &self.stacktrace {
|
||||
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,
|
||||
frame.pos.col
|
||||
)?;
|
||||
let fn_name = frame.fn_name.as_ref().map(|o| o.as_ref()).unwrap_or("<anonymous fn>");
|
||||
match &frame.pos {
|
||||
Some(pos) => write!(f, "\n In {} at {}", fn_name, pos)?,
|
||||
None => write!(f, "\n In {} at <unknown>", fn_name)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ fn fn_range(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
}
|
||||
|
||||
fn fn_len(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::Int(args[0].len().map_err(RuntimeError::new_incomplete)? as i64))
|
||||
Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64))
|
||||
}
|
||||
|
||||
fn fn_has(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
|
@ -213,9 +213,9 @@ fn skip_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCel
|
|||
|
||||
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) if n <= 0 => return Err(RuntimeError::new_no_pos("First argument to skip must be nonnegative")),
|
||||
Value::Int(n) => n,
|
||||
_ => return Err(RuntimeError::new_incomplete("First argument to skip must be an integer"))
|
||||
_ => return Err(RuntimeError::new_no_pos("First argument to skip must be an integer"))
|
||||
};
|
||||
let it = args[1].iter()?;
|
||||
Ok(Value::Func(Func::BuiltinClosure {
|
||||
|
@ -227,7 +227,7 @@ fn fn_skip(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
}
|
||||
|
||||
fn fn_forall(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
let func = &args[0];
|
||||
let func = &args[0].as_func()?;
|
||||
for item in args[1].iter()? {
|
||||
let item = item?;
|
||||
if !func.call(vec![item])?.truthy() {
|
||||
|
@ -238,7 +238,7 @@ fn fn_forall(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
}
|
||||
|
||||
fn fn_exists(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
let func = &args[0];
|
||||
let func = &args[0].as_func()?;
|
||||
for item in args[1].iter()? {
|
||||
let item = item?;
|
||||
if func.call(vec![item])?.truthy() {
|
||||
|
|
|
@ -72,11 +72,11 @@ impl Func {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
pub fn name(&self) -> Option<Rc<str>> {
|
||||
match self {
|
||||
Self::Builtin { name, .. } => Some(name.as_ref()),
|
||||
Self::Builtin { name, .. } => Some(name.clone()),
|
||||
Self::BuiltinClosure { .. } => None,
|
||||
Self::Func { name, .. } => name.as_ref().map(|s| s.as_ref()),
|
||||
Self::Func { name, .. } => name.clone(),
|
||||
Self::Partial { inner, .. } => inner.name()
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ impl Func {
|
|||
inner.call(filled_args)
|
||||
}
|
||||
},
|
||||
Ordering::Less if arg_values.is_empty() => Err(RuntimeError::new_incomplete(
|
||||
Ordering::Less if arg_values.is_empty() => Err(RuntimeError::new_no_pos(
|
||||
format!("Cannot call this function with zero arguments: expected {}", self.arg_count())
|
||||
)),
|
||||
Ordering::Less => match self {
|
||||
|
@ -120,7 +120,7 @@ impl Func {
|
|||
}
|
||||
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_no_pos(
|
||||
format!("Too many arguments for function: expected {}, got {}", self.arg_count(), arg_values.len())
|
||||
))
|
||||
}
|
||||
|
@ -236,11 +236,10 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if let Value::Func(f) = self {
|
||||
f.call(args)
|
||||
} else {
|
||||
Err(RuntimeError::new_incomplete("Cannot call"))
|
||||
pub fn as_func(&self) -> Result<&Func, String> {
|
||||
match self {
|
||||
Value::Func(f) => Ok(f),
|
||||
v => Err(format!("{:?} is not a function", v))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue