|
@@ -6,159 +6,174 @@ use anyhow::{bail, Error};
|
|
/// The list of builtins provided at startup.
|
|
/// The list of builtins provided at startup.
|
|
///
|
|
///
|
|
/// TODO: move this to a separate file and clean it up
|
|
/// TODO: move this to a separate file and clean it up
|
|
-pub const BUILTINS: &[BuiltinFunc] = &[
|
|
+pub fn builtins() -> Vec<BuiltinFunc> {
|
|
- BuiltinFunc {
|
|
+ vec![
|
|
- name: "rep",
|
|
+ BuiltinFunc {
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ name: "rep",
|
|
- let (rep, expr) = {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- let ast = state.ast.borrow();
|
|
+ let (rep, expr) = {
|
|
- let args = match &ast[expr] {
|
|
+ let ast = state.ast.borrow();
|
|
- Expr::Tup(tup) => tup,
|
|
+ let args = match &ast[expr] {
|
|
- _ => {
|
|
+ Expr::Tup(tup) => tup,
|
|
|
|
+ _ => {
|
|
|
|
+ let span = state.ast.borrow().get_line(expr.file, expr.span);
|
|
|
|
+ bail!("`rep`: expected tuple\n{}", span)
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ if args.len() != 2 {
|
|
let span = state.ast.borrow().get_line(expr.file, expr.span);
|
|
let span = state.ast.borrow().get_line(expr.file, expr.span);
|
|
- bail!("`rep`: expected tuple\n{}", span)
|
|
+ bail!(
|
|
|
|
+ "`rep`: expected two arguments, got {}\n{}",
|
|
|
|
+ args.len(),
|
|
|
|
+ span
|
|
|
|
+ )
|
|
}
|
|
}
|
|
|
|
+ (args[0], args[1])
|
|
};
|
|
};
|
|
- if args.len() != 2 {
|
|
+ let mut buf = String::new();
|
|
- let span = state.ast.borrow().get_line(expr.file, expr.span);
|
|
+ let num = state.eval(rep, env)?.as_num(&state.ast.borrow())?;
|
|
- bail!("`rep`: expected two arguments, got {}\n{}", args.len(), span)
|
|
+ for _ in 0..num {
|
|
|
|
+ buf.push_str(
|
|
|
|
+ &state
|
|
|
|
+ .eval(expr, env)?
|
|
|
|
+ .as_str(&state.ast.borrow())?
|
|
|
|
+ .to_string(),
|
|
|
|
+ );
|
|
}
|
|
}
|
|
- (args[0], args[1])
|
|
+ Ok(Value::Lit(Literal::Str(buf)))
|
|
- };
|
|
+ },
|
|
- let mut buf = String::new();
|
|
|
|
- let num = state.eval(rep, env)?.as_num(&state.ast.borrow())?;
|
|
|
|
- for _ in 0..num {
|
|
|
|
- buf.push_str(&state.eval(expr, env)?.as_str(&state.ast.borrow())?.to_string());
|
|
|
|
- }
|
|
|
|
- Ok(Value::Lit(Literal::Str(buf)))
|
|
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
-
|
|
+ name: "length",
|
|
- BuiltinFunc {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- name: "length",
|
|
+ let args = match state.eval(expr, env)? {
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ Value::Tup(tup) => tup,
|
|
- let args = match state.eval(expr, env)? {
|
|
+ _ => bail!("`length`: expected tuple"),
|
|
- Value::Tup(tup) => tup,
|
|
+ };
|
|
- _ => bail!("`length`: expected tuple"),
|
|
+ Ok(Value::Lit(Literal::Num(args.len() as i64)))
|
|
- };
|
|
+ },
|
|
- Ok(Value::Lit(Literal::Num(args.len() as i64)))
|
|
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
-
|
|
+ name: "to-upper",
|
|
- BuiltinFunc {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- name: "to-upper",
|
|
+ let s = state.eval(expr, env)?;
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ Ok(Value::Lit(Literal::Str(
|
|
- let s = state.eval(expr, env)?;
|
|
+ s.as_str(&state.ast.borrow())?.to_uppercase(),
|
|
- Ok(Value::Lit(Literal::Str(s.as_str(&state.ast.borrow())?.to_uppercase())))
|
|
+ )))
|
|
|
|
+ },
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
-
|
|
+ name: "capitalize",
|
|
- BuiltinFunc {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- name: "capitalize",
|
|
+ let s = state.eval(expr, env)?;
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ Ok(Value::Lit(Literal::Str(titlecase::titlecase(
|
|
- let s = state.eval(expr, env)?;
|
|
+ s.as_str(&state.ast.borrow())?,
|
|
- Ok(Value::Lit(Literal::Str(titlecase::titlecase(s.as_str(&state.ast.borrow())?))))
|
|
+ ))))
|
|
-
|
|
+ },
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
-
|
|
+ name: "to-lower",
|
|
- BuiltinFunc {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- name: "to-lower",
|
|
+ let s = state.eval(expr, env)?;
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ Ok(Value::Lit(Literal::Str(
|
|
- let s = state.eval(expr, env)?;
|
|
+ s.as_str(&state.ast.borrow())?.to_lowercase(),
|
|
- Ok(Value::Lit(Literal::Str(s.as_str(&state.ast.borrow())?.to_lowercase())))
|
|
+ )))
|
|
|
|
+ },
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
-
|
|
+ name: "sub",
|
|
- BuiltinFunc {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- name: "sub",
|
|
+ let val = state.eval(expr, env)?;
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ let args = val.as_tup(&state.ast.borrow())?;
|
|
- let val = state.eval(expr, env)?;
|
|
+ if let [x, y] = args {
|
|
- let args = val.as_tup(&state.ast.borrow())?;
|
|
+ let x = state.hnf(x)?.as_num(&state.ast.borrow())?;
|
|
- if let [x, y] = args {
|
|
+ let y = state.hnf(y)?.as_num(&state.ast.borrow())?;
|
|
- let x = state.hnf(x)?.as_num(&state.ast.borrow())?;
|
|
+ Ok(Value::Lit(Literal::Num(x - y)))
|
|
- let y = state.hnf(y)?.as_num(&state.ast.borrow())?;
|
|
+ } else {
|
|
- Ok(Value::Lit(Literal::Num(x - y)))
|
|
+ bail!("`sub`: expected 2 arguments, got {}", args.len());
|
|
- } else {
|
|
+ }
|
|
- bail!("`sub`: expected 2 arguments, got {}", args.len());
|
|
+ },
|
|
- }
|
|
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
-
|
|
+ name: "concat",
|
|
- BuiltinFunc {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- name: "concat",
|
|
+ let val = state.eval(expr, env)?;
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ let tup = val.as_tup(&state.ast.borrow())?;
|
|
- let val = state.eval(expr, env)?;
|
|
+ let mut contents = Vec::new();
|
|
- let tup = val.as_tup(&state.ast.borrow())?;
|
|
+ for elem in tup {
|
|
- let mut contents = Vec::new();
|
|
+ for th in state.hnf(elem)?.as_tup(&state.ast.borrow())? {
|
|
- for elem in tup {
|
|
+ contents.push(th.clone());
|
|
- for th in state.hnf(elem)?.as_tup(&state.ast.borrow())? {
|
|
+ }
|
|
- contents.push(th.clone());
|
|
|
|
}
|
|
}
|
|
- }
|
|
+ Ok(Value::Tup(contents))
|
|
- Ok(Value::Tup(contents))
|
|
+ },
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
-
|
|
+ name: "tuple-index",
|
|
- BuiltinFunc {
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
- name: "tuple-index",
|
|
+ let val = state.eval(expr, env)?;
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ let args = val.as_tup(&state.ast.borrow())?;
|
|
- let val = state.eval(expr, env)?;
|
|
+ if let [tup, idx] = args {
|
|
- let args = val.as_tup(&state.ast.borrow())?;
|
|
+ let tup = state.hnf(tup)?;
|
|
- if let [tup, idx] = args {
|
|
+ let idx = state.hnf(idx)?;
|
|
- let tup = state.hnf(tup)?;
|
|
+ state.hnf(
|
|
- let idx = state.hnf(idx)?;
|
|
+ &tup.as_tup(&state.ast.borrow())?
|
|
- state.hnf(&tup.as_tup(&state.ast.borrow())?[idx.as_num(&state.ast.borrow())? as usize])
|
|
+ [idx.as_num(&state.ast.borrow())? as usize],
|
|
- } else {
|
|
+ )
|
|
- bail!("`tuple-index`: expected 2 arguments, got {}", args.len());
|
|
+ } else {
|
|
- }
|
|
+ bail!("`tuple-index`: expected 2 arguments, got {}", args.len());
|
|
|
|
+ }
|
|
|
|
+ },
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
|
|
+ name: "tuple-replace",
|
|
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
|
|
+ let val = state.eval(expr, env)?;
|
|
|
|
+ let args = val.as_tup(&state.ast.borrow())?;
|
|
|
|
+ if let [tup, idx, new] = args {
|
|
|
|
+ let tup_val = state.hnf(tup)?;
|
|
|
|
+ let tup = tup_val.as_tup(&state.ast.borrow())?;
|
|
|
|
+ let idx = state.hnf(idx)?.as_num(&state.ast.borrow())?;
|
|
|
|
|
|
- BuiltinFunc {
|
|
+ let mut modified = Vec::with_capacity(tup.len());
|
|
- name: "tuple-replace",
|
|
+ for i in 0..idx {
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ modified.push(tup[i as usize].clone());
|
|
- let val = state.eval(expr, env)?;
|
|
+ }
|
|
- let args = val.as_tup(&state.ast.borrow())?;
|
|
+ modified.push(new.clone());
|
|
- if let [tup, idx, new] = args {
|
|
+ for i in (idx + 1)..(tup.len() as i64) {
|
|
- let tup_val = state.hnf(tup)?;
|
|
+ modified.push(tup[i as usize].clone());
|
|
- let tup = tup_val.as_tup(&state.ast.borrow())?;
|
|
+ }
|
|
- let idx = state.hnf(idx)?.as_num(&state.ast.borrow())?;
|
|
+ Ok(Value::Tup(modified))
|
|
-
|
|
+ } else {
|
|
- let mut modified = Vec::with_capacity(tup.len());
|
|
+ bail!("`tuple-index`: expected 2 arguments, got {}", args.len());
|
|
- for i in 0..idx {
|
|
|
|
- modified.push(tup[i as usize].clone());
|
|
|
|
- }
|
|
|
|
- modified.push(new.clone());
|
|
|
|
- for i in (idx+1)..(tup.len() as i64) {
|
|
|
|
- modified.push(tup[i as usize].clone());
|
|
|
|
}
|
|
}
|
|
- Ok(Value::Tup(modified))
|
|
+ },
|
|
- } else {
|
|
|
|
- bail!("`tuple-index`: expected 2 arguments, got {}", args.len());
|
|
|
|
- }
|
|
|
|
},
|
|
},
|
|
- },
|
|
+ BuiltinFunc {
|
|
|
|
+ name: "tuple-fold",
|
|
|
|
+ callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
|
|
+ let val = state.eval(expr, env)?;
|
|
|
|
+ let args = val.as_tup(&state.ast.borrow())?;
|
|
|
|
+ if let [func, init, tup] = args {
|
|
|
|
+ let func = state.hnf(func)?;
|
|
|
|
+ let tup = state.hnf(tup)?;
|
|
|
|
|
|
- BuiltinFunc {
|
|
+ let mut result = init.clone();
|
|
- name: "tuple-fold",
|
|
+ for t in tup.as_tup(&state.ast.borrow())? {
|
|
- callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
|
|
+ let partial =
|
|
- let val = state.eval(expr, env)?;
|
|
+ state.eval_closure(func.as_closure(&state.ast.borrow())?, result)?;
|
|
- let args = val.as_tup(&state.ast.borrow())?;
|
|
+ result =
|
|
- if let [func, init, tup] = args {
|
|
+ Thunk::Value(state.eval_closure(
|
|
- let func = state.hnf(func)?;
|
|
+ partial.as_closure(&state.ast.borrow())?,
|
|
- let tup = state.hnf(tup)?;
|
|
+ t.clone(),
|
|
|
|
+ )?);
|
|
|
|
+ }
|
|
|
|
|
|
- let mut result = init.clone();
|
|
+ state.hnf(&result)
|
|
- for t in tup.as_tup(&state.ast.borrow())? {
|
|
+ } else {
|
|
- let partial = state.eval_closure(func.as_closure(&state.ast.borrow())?, result)?;
|
|
+ bail!("`tuple-fold`: expected 3 arguments, got {}", args.len());
|
|
- result = Thunk::Value(state.eval_closure(partial.as_closure(&state.ast.borrow())?, t.clone())?);
|
|
|
|
}
|
|
}
|
|
-
|
|
+ },
|
|
- state.hnf(&result)
|
|
|
|
- } else {
|
|
|
|
- bail!("`tuple-fold`: expected 3 arguments, got {}", args.len());
|
|
|
|
- }
|
|
|
|
},
|
|
},
|
|
- },
|
|
+ ]
|
|
-];
|
|
+}
|