|
@@ -1,7 +1,19 @@
|
|
|
use crate::ast::*;
|
|
|
use crate::interp::*;
|
|
|
+use crate::errors::MatzoError;
|
|
|
|
|
|
-use anyhow::{bail, Error};
|
|
|
+fn arity_error(func: &str, expected: usize, actual: &[ExprRef]) -> Result<Value, MatzoError> {
|
|
|
+ let msg = format!("`{}`: expected {} arguments, got {}", func, expected, actual.len());
|
|
|
+ if actual.is_empty() {
|
|
|
+ Err(MatzoError::new(Span::empty(), msg))
|
|
|
+ } else {
|
|
|
+ let span = Span {
|
|
|
+ start: actual[0].span.start,
|
|
|
+ end: actual[actual.len()-1].span.end,
|
|
|
+ };
|
|
|
+ Err(MatzoError::new(span, msg))
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
/// The list of builtins provided at startup.
|
|
|
pub fn builtins() -> Vec<BuiltinFunc> {
|
|
@@ -9,7 +21,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "rep",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [rep, expr] = exprs {
|
|
|
let mut buf = String::new();
|
|
|
let num = state.eval(*rep, env)?.as_num(&state.ast.borrow())?;
|
|
@@ -18,7 +30,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
}
|
|
|
Ok(Value::Lit(Literal::Str(buf)))
|
|
|
} else {
|
|
|
- bail!("`rep`: expected two arguments, got {}", exprs.len())
|
|
|
+ arity_error("rep", 2, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -26,17 +38,14 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "str/upper",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [expr] = exprs {
|
|
|
let s = state.eval(*expr, env)?;
|
|
|
Ok(Value::Lit(Literal::Str(
|
|
|
s.as_str(&state.ast.borrow())?.to_uppercase(),
|
|
|
)))
|
|
|
} else {
|
|
|
- bail!(
|
|
|
- "`str/capitalize`: expected 1 argument1, got {}",
|
|
|
- exprs.len()
|
|
|
- );
|
|
|
+ arity_error("str/upper", 1, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -44,17 +53,14 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "str/capitalize",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [expr] = exprs {
|
|
|
let s = state.eval(*expr, env)?;
|
|
|
Ok(Value::Lit(Literal::Str(titlecase::titlecase(
|
|
|
s.as_str(&state.ast.borrow())?,
|
|
|
))))
|
|
|
} else {
|
|
|
- bail!(
|
|
|
- "`str/capitalize`: expected 1 argument1, got {}",
|
|
|
- exprs.len()
|
|
|
- );
|
|
|
+ arity_error("str/capitalize", 1, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -62,14 +68,14 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "str/lower",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [expr] = exprs {
|
|
|
let s = state.eval(*expr, env)?;
|
|
|
Ok(Value::Lit(Literal::Str(
|
|
|
s.as_str(&state.ast.borrow())?.to_lowercase(),
|
|
|
)))
|
|
|
} else {
|
|
|
- bail!("`str/lower`: expected 1 argument1, got {}", exprs.len());
|
|
|
+ arity_error("str/lower", 1, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -77,13 +83,13 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "add",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [x, y] = exprs {
|
|
|
let x = state.eval(*x, env)?.as_num(&state.ast.borrow())?;
|
|
|
let y = state.eval(*y, env)?.as_num(&state.ast.borrow())?;
|
|
|
Ok(Value::Lit(Literal::Num(x + y)))
|
|
|
} else {
|
|
|
- bail!("`add`: expected 2 arguments, got {}", exprs.len());
|
|
|
+ arity_error("add", 2, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -91,13 +97,13 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "sub",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [x, y] = exprs {
|
|
|
let x = state.eval(*x, env)?.as_num(&state.ast.borrow())?;
|
|
|
let y = state.eval(*y, env)?.as_num(&state.ast.borrow())?;
|
|
|
Ok(Value::Lit(Literal::Num(x - y)))
|
|
|
} else {
|
|
|
- bail!("`sub`: expected 2 arguments, got {}", exprs.len());
|
|
|
+ arity_error("sub", 2, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -105,13 +111,13 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "mul",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [x, y] = exprs {
|
|
|
let x = state.eval(*x, env)?.as_num(&state.ast.borrow())?;
|
|
|
let y = state.eval(*y, env)?.as_num(&state.ast.borrow())?;
|
|
|
Ok(Value::Lit(Literal::Num(x * y)))
|
|
|
} else {
|
|
|
- bail!("`mul`: expected 2 arguments, got {}", exprs.len());
|
|
|
+ arity_error("mul", 2, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -119,14 +125,14 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "tuple/len",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [expr] = exprs {
|
|
|
let tup = state.eval(*expr, env)?;
|
|
|
Ok(Value::Lit(Literal::Num(
|
|
|
tup.as_tup(&state.ast.borrow())?.len() as i64,
|
|
|
)))
|
|
|
} else {
|
|
|
- bail!("`tuple/len`: expected 1 argument, got {}", exprs.len())
|
|
|
+ arity_error("tuple/len", 1, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -134,7 +140,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "tuple/concat",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [expr] = exprs {
|
|
|
let val = state.eval(*expr, env)?;
|
|
|
let tup = val.as_tup(&state.ast.borrow())?;
|
|
@@ -146,7 +152,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
}
|
|
|
Ok(Value::Tup(contents))
|
|
|
} else {
|
|
|
- bail!("tuple/concat: expected 1 argument, got {}", exprs.len());
|
|
|
+ arity_error("tuple/concat", 1, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -154,7 +160,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "tuple/index",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [tup, idx] = exprs {
|
|
|
let tup = state.eval(*tup, env)?;
|
|
|
let idx = state.eval(*idx, env)?;
|
|
@@ -163,7 +169,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
[idx.as_num(&state.ast.borrow())? as usize],
|
|
|
)
|
|
|
} else {
|
|
|
- bail!("`tuple/index`: expected 2 arguments, got {}", exprs.len());
|
|
|
+ arity_error("tuple/index", 1, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -171,7 +177,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "tuple/replace",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [tup, idx, new] = exprs {
|
|
|
let tup_val = state.eval(*tup, env)?;
|
|
|
let tup = tup_val.as_tup(&state.ast.borrow())?;
|
|
@@ -187,7 +193,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
}
|
|
|
Ok(Value::Tup(modified))
|
|
|
} else {
|
|
|
- bail!("`tuple/replace`: expected 3 arguments, got {}", exprs.len());
|
|
|
+ arity_error("tuple/replace", 3, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -195,7 +201,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "tuple/fold",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [func, init, tup] = exprs {
|
|
|
let func = state.eval(*func, env)?;
|
|
|
let tup = state.eval(*tup, env)?;
|
|
@@ -210,7 +216,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
|
|
|
state.hnf(&result)
|
|
|
} else {
|
|
|
- bail!("`tuple/fold`: expected 3 arguments, got {}", exprs.len());
|
|
|
+ arity_error("tuple/fold", 3, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|
|
@@ -218,7 +224,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
BuiltinFunc {
|
|
|
name: "tuple/map",
|
|
|
callback: Box::new(
|
|
|
- |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, Error> {
|
|
|
+ |state: &State, exprs: &[ExprRef], env: &Env| -> Result<Value, MatzoError> {
|
|
|
if let [func, tup] = exprs {
|
|
|
let func = state.eval(*func, env)?;
|
|
|
let tup = state.eval(*tup, env)?;
|
|
@@ -232,7 +238,7 @@ pub fn builtins() -> Vec<BuiltinFunc> {
|
|
|
|
|
|
Ok(Value::Tup(new_tup))
|
|
|
} else {
|
|
|
- bail!("`tuple/map`: expected 2 arguments, got {}", exprs.len());
|
|
|
+ arity_error("tuple/map", 2, exprs)
|
|
|
}
|
|
|
},
|
|
|
),
|