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",
|
"backtrace",
|
||||||
"complexpr",
|
"complexpr",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
|
"rustyline-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -384,6 +385,17 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
|
@ -9,3 +9,4 @@ edition = "2021"
|
||||||
complexpr = { path = "../complexpr" }
|
complexpr = { path = "../complexpr" }
|
||||||
rustyline = "10.0.0"
|
rustyline = "10.0.0"
|
||||||
backtrace = "0.3.66"
|
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 backtrace::Backtrace;
|
||||||
use complexpr::{env::Environment, interpreter::interpret, value::Value, stdlib};
|
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_RESET: &str = "\x1b[0m";
|
||||||
const C_BLUE: &str = "\x1b[94m";
|
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>> {
|
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);
|
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());
|
stdlib::load(&mut env.borrow_mut());
|
||||||
loop {
|
loop {
|
||||||
let readline = rl.readline(PROMPT);
|
let readline = rl.readline(">> ");
|
||||||
match readline {
|
match readline {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
let result = interpret(&line, None, Some(env.clone()), true);
|
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 {
|
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}};
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum Unwind {
|
pub enum Unwind {
|
||||||
|
@ -75,7 +81,7 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
||||||
}
|
}
|
||||||
if let Ok(i) = iterator {
|
if let Ok(i) = iterator {
|
||||||
for v in i {
|
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();
|
let env = env.clone();
|
||||||
env.borrow_mut().set(name.clone(), v).expect("unreachable");
|
env.borrow_mut().set(name.clone(), v).expect("unreachable");
|
||||||
match eval_stmt(stmt, env) {
|
match eval_stmt(stmt, env) {
|
||||||
|
@ -157,13 +163,15 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
Ok(Value::from(map))
|
Ok(Value::from(map))
|
||||||
},
|
},
|
||||||
Expr::FuncCall { func, args, pos } => {
|
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());
|
let mut arg_values = Vec::with_capacity(args.len());
|
||||||
for arg in args {
|
for arg in args {
|
||||||
let result = eval_expr(arg, env.clone())?;
|
let result = eval_expr(arg, env.clone())?;
|
||||||
arg_values.push(result);
|
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 } => {
|
Expr::Index { lhs, index, pos } => {
|
||||||
let l = eval_expr(lhs, env.clone())?;
|
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> {
|
fn pipecolon_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
|
||||||
let f = &data.borrow()[0];
|
let f = &data.borrow()[0];
|
||||||
if let Some(next) = iter_data.borrow_mut()[0].next() {
|
if let Some(next) = iter_data.borrow_mut()[0].next() {
|
||||||
f.call(vec![next?])
|
let func = f.as_func()?;
|
||||||
|
func.call(vec![next?])
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nil)
|
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();
|
let next = iter_data.borrow_mut()[0].next();
|
||||||
if let Some(next) = next {
|
if let Some(next) = next {
|
||||||
let 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 {
|
if success {
|
||||||
return Ok(next)
|
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> {
|
pub fn eval_pipeline(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
let l = eval_expr(lhs, env.clone())?;
|
let l = eval_expr(lhs, env.clone())?;
|
||||||
let r = eval_expr(rhs, env)?;
|
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
|
#[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 {
|
match op.ty {
|
||||||
TokenType::PipePoint => r.call(vec![l]),
|
TokenType::PipePoint => r.call(vec![l]),
|
||||||
TokenType::PipeColon => {
|
TokenType::PipeColon => {
|
||||||
Ok(Value::Func(Func::BuiltinClosure {
|
Ok(Value::Func(Func::BuiltinClosure {
|
||||||
arg_count: 0,
|
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()?])),
|
iter_data: Rc::new(RefCell::new(vec![l.iter()?])),
|
||||||
func: pipecolon_inner,
|
func: pipecolon_inner,
|
||||||
}))
|
}))
|
||||||
|
@ -368,7 +380,7 @@ fn eval_pipeline_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeE
|
||||||
TokenType::PipeQuestion => {
|
TokenType::PipeQuestion => {
|
||||||
Ok(Value::Func(Func::BuiltinClosure {
|
Ok(Value::Func(Func::BuiltinClosure {
|
||||||
arg_count: 0,
|
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()?])),
|
iter_data: Rc::new(RefCell::new(vec![l.iter()?])),
|
||||||
func: pipequestion_inner,
|
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 result = Value::Nil;
|
||||||
let mut first_iter = true;
|
let mut first_iter = true;
|
||||||
for v in l.iter().map_err(|e| RuntimeError::new(e, op.pos.clone()))? {
|
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 {
|
if first_iter {
|
||||||
result = v;
|
result = v;
|
||||||
first_iter = false;
|
first_iter = false;
|
||||||
} else {
|
} 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)
|
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 mut first_iter = true;
|
||||||
let lst = l.iter().map_err(|e| RuntimeError::new(e, op.pos.clone()))?.collect::<Vec<Result<Value, RuntimeError>>>();
|
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() {
|
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 {
|
if first_iter {
|
||||||
result = v;
|
result = v;
|
||||||
first_iter = false;
|
first_iter = false;
|
||||||
} else {
|
} 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)
|
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 iter = eval_expr(arg1, env.clone())?;
|
||||||
let mut result = eval_expr(arg2, env.clone())?;
|
let mut result = eval_expr(arg2, env.clone())?;
|
||||||
let func = eval_expr(arg3, env)?;
|
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()))? {
|
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()))?;
|
let v = v.map_err(exit_pipe(&op.pos))?;
|
||||||
result = func.call(vec![result, v]).map_err(|e| e.complete(op.pos.clone()))?;
|
result = func.call(vec![result, v]).map_err(exit_pipe(&op.pos))?;
|
||||||
}
|
}
|
||||||
Ok(result)
|
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 iter = eval_expr(arg1, env.clone())?;
|
||||||
let mut result = eval_expr(arg2, env.clone())?;
|
let mut result = eval_expr(arg2, env.clone())?;
|
||||||
let func = eval_expr(arg3, env)?;
|
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>>>();
|
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() {
|
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))?;
|
||||||
result = func.call(vec![v, result]).map_err(|e| e.complete(op.pos.clone()))?;
|
result = func.call(vec![v, result]).map_err(exit_pipe(&op.pos))?;
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,6 +18,16 @@ pub struct Position {
|
||||||
pub file: Option<Rc<str>>
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct ParserError {
|
pub struct ParserError {
|
||||||
|
@ -27,7 +37,7 @@ pub struct ParserError {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Stackframe {
|
pub struct Stackframe {
|
||||||
pub pos: Position,
|
pub pos: Option<Position>,
|
||||||
pub fn_name: Option<Rc<str>>,
|
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> {
|
where S: Into<String> {
|
||||||
Self {
|
Self {
|
||||||
message: message.into(),
|
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 {
|
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.last_pos = Some(pos);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(mut self, ctx_name: Option<Rc<str>>) -> 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.last_pos = None;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -79,13 +82,13 @@ impl RuntimeError {
|
||||||
|
|
||||||
impl From<String> for RuntimeError {
|
impl From<String> for RuntimeError {
|
||||||
fn from(s: String) -> Self {
|
fn from(s: String) -> Self {
|
||||||
Self::new_incomplete(s)
|
Self::new_no_pos(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for RuntimeError {
|
impl From<&str> for RuntimeError {
|
||||||
fn from(s: &str) -> Self {
|
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 {
|
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, "{}", self.message)?;
|
write!(f, "{}", self.message)?;
|
||||||
for frame in &self.stacktrace {
|
for frame in &self.stacktrace {
|
||||||
write!(f, "\n In {} at {}:{}:{}",
|
let fn_name = 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>"),
|
match &frame.pos {
|
||||||
frame.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
Some(pos) => write!(f, "\n In {} at {}", fn_name, pos)?,
|
||||||
frame.pos.line,
|
None => write!(f, "\n In {} at <unknown>", fn_name)?,
|
||||||
frame.pos.col
|
}
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ fn fn_range(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_len(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> {
|
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> {
|
fn fn_skip(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
let n = match args[0] {
|
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,
|
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()?;
|
let it = args[1].iter()?;
|
||||||
Ok(Value::Func(Func::BuiltinClosure {
|
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> {
|
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()? {
|
for item in args[1].iter()? {
|
||||||
let item = item?;
|
let item = item?;
|
||||||
if !func.call(vec![item])?.truthy() {
|
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> {
|
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()? {
|
for item in args[1].iter()? {
|
||||||
let item = item?;
|
let item = item?;
|
||||||
if func.call(vec![item])?.truthy() {
|
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 {
|
match self {
|
||||||
Self::Builtin { name, .. } => Some(name.as_ref()),
|
Self::Builtin { name, .. } => Some(name.clone()),
|
||||||
Self::BuiltinClosure { .. } => None,
|
Self::BuiltinClosure { .. } => None,
|
||||||
Self::Func { name, .. } => name.as_ref().map(|s| s.as_ref()),
|
Self::Func { name, .. } => name.clone(),
|
||||||
Self::Partial { inner, .. } => inner.name()
|
Self::Partial { inner, .. } => inner.name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ impl Func {
|
||||||
inner.call(filled_args)
|
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())
|
format!("Cannot call this function with zero arguments: expected {}", self.arg_count())
|
||||||
)),
|
)),
|
||||||
Ordering::Less => match self {
|
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 }))
|
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())
|
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> {
|
pub fn as_func(&self) -> Result<&Func, String> {
|
||||||
if let Value::Func(f) = self {
|
match self {
|
||||||
f.call(args)
|
Value::Func(f) => Ok(f),
|
||||||
} else {
|
v => Err(format!("{:?} is not a function", v))
|
||||||
Err(RuntimeError::new_incomplete("Cannot call"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue