2022-08-24 15:03:17 +02:00
|
|
|
//! This module implements the builtins exposed in the Nix language.
|
|
|
|
//!
|
|
|
|
//! See //tvix/eval/docs/builtins.md for a some context on the
|
|
|
|
//! available builtins in Nix.
|
|
|
|
|
2022-10-03 10:26:32 +02:00
|
|
|
use std::cmp;
|
2022-10-10 20:48:05 +02:00
|
|
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
2022-10-03 10:26:32 +02:00
|
|
|
use std::path::PathBuf;
|
2022-08-24 15:03:17 +02:00
|
|
|
|
2022-10-13 00:45:52 +02:00
|
|
|
use regex::Regex;
|
|
|
|
|
2022-10-22 16:57:02 +02:00
|
|
|
use crate::warnings::WarningKind;
|
2022-08-24 16:13:00 +02:00
|
|
|
use crate::{
|
|
|
|
errors::ErrorKind,
|
2022-09-20 23:32:49 +02:00
|
|
|
value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Value},
|
2022-09-13 20:11:07 +02:00
|
|
|
vm::VM,
|
2022-08-24 16:13:00 +02:00
|
|
|
};
|
2022-08-24 15:03:17 +02:00
|
|
|
|
2022-09-06 23:55:13 +02:00
|
|
|
use crate::{arithmetic_op, cmp_op};
|
2022-08-29 21:57:31 +02:00
|
|
|
|
2022-09-19 10:14:05 +02:00
|
|
|
use self::versions::{VersionPart, VersionPartsIter};
|
2022-09-17 09:23:50 +02:00
|
|
|
|
2022-09-18 22:34:41 +02:00
|
|
|
#[cfg(feature = "impure")]
|
2022-10-04 17:27:49 +02:00
|
|
|
pub mod impure;
|
2022-09-17 09:23:50 +02:00
|
|
|
pub mod versions;
|
|
|
|
|
2022-10-13 08:13:10 +02:00
|
|
|
/// Coerce a Nix Value to a plain path, e.g. in order to access the
|
|
|
|
/// file it points to via either `builtins.toPath` or an impure
|
|
|
|
/// builtin. This coercion can _never_ be performed in a Nix program
|
|
|
|
/// without using builtins (i.e. the trick `path: /. + path` to
|
|
|
|
/// convert from a string to a path wouldn't hit this code).
|
2022-09-13 20:11:07 +02:00
|
|
|
pub fn coerce_value_to_path(v: &Value, vm: &mut VM) -> Result<PathBuf, ErrorKind> {
|
2022-09-18 22:53:08 +02:00
|
|
|
let value = v.force(vm)?;
|
|
|
|
match &*value {
|
|
|
|
Value::Thunk(t) => coerce_value_to_path(&t.value(), vm),
|
|
|
|
Value::Path(p) => Ok(p.clone()),
|
|
|
|
_ => value
|
|
|
|
.coerce_to_string(CoercionKind::Weak, vm)
|
|
|
|
.map(|s| PathBuf::from(s.as_str()))
|
|
|
|
.and_then(|path| {
|
|
|
|
if path.is_absolute() {
|
|
|
|
Ok(path)
|
|
|
|
} else {
|
|
|
|
Err(ErrorKind::NotAnAbsolutePath(path))
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
}
|
2022-09-13 20:11:07 +02:00
|
|
|
}
|
|
|
|
|
2022-09-05 00:30:58 +02:00
|
|
|
/// Return all pure builtins, that is all builtins that do not rely on
|
|
|
|
/// I/O outside of the VM and which can be used in any contexts (e.g.
|
|
|
|
/// WASM).
|
2022-08-24 15:58:55 +02:00
|
|
|
fn pure_builtins() -> Vec<Builtin> {
|
|
|
|
vec![
|
2022-10-13 23:29:12 +02:00
|
|
|
Builtin::new("abort", &[true], |args: Vec<Value>, _: &mut VM| {
|
|
|
|
Err(ErrorKind::Abort(args[0].to_str()?.to_string()))
|
|
|
|
}),
|
2022-09-21 22:20:37 +02:00
|
|
|
Builtin::new(
|
|
|
|
"add",
|
|
|
|
&[false, false],
|
2022-10-04 17:20:06 +02:00
|
|
|
|args: Vec<Value>, vm: &mut VM| arithmetic_op!(&*args[0].force(vm)?, &*args[1].force(vm)?, +),
|
2022-09-21 22:20:37 +02:00
|
|
|
),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("all", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
2022-10-02 18:59:37 +02:00
|
|
|
for value in args[1].to_list()?.into_iter() {
|
2022-10-09 19:18:32 +02:00
|
|
|
let pred_result = vm.call_with(&args[0], [value])?;
|
2022-10-02 18:59:37 +02:00
|
|
|
|
|
|
|
if !pred_result.force(vm)?.as_bool()? {
|
|
|
|
return Ok(Value::Bool(false));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Value::Bool(true))
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("any", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
2022-10-02 19:02:05 +02:00
|
|
|
for value in args[1].to_list()?.into_iter() {
|
2022-10-09 19:18:32 +02:00
|
|
|
let pred_result = vm.call_with(&args[0], [value])?;
|
2022-10-02 19:02:05 +02:00
|
|
|
|
|
|
|
if pred_result.force(vm)?.as_bool()? {
|
|
|
|
return Ok(Value::Bool(true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Value::Bool(false))
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("attrNames", &[true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
let xs = args[0].to_attrs()?;
|
2022-09-18 22:53:08 +02:00
|
|
|
let mut output = Vec::with_capacity(xs.len());
|
2022-09-05 20:43:17 +02:00
|
|
|
|
2022-09-18 22:53:08 +02:00
|
|
|
for (key, _val) in xs.iter() {
|
|
|
|
output.push(Value::String(key.clone()));
|
|
|
|
}
|
2022-09-05 20:43:17 +02:00
|
|
|
|
2022-09-18 22:53:08 +02:00
|
|
|
Ok(Value::List(NixList::construct(output.len(), output)))
|
2022-09-05 20:43:17 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("attrValues", &[true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
let xs = args[0].to_attrs()?;
|
2022-09-18 22:53:08 +02:00
|
|
|
let mut output = Vec::with_capacity(xs.len());
|
2022-09-05 20:43:57 +02:00
|
|
|
|
2022-09-18 22:53:08 +02:00
|
|
|
for (_key, val) in xs.iter() {
|
|
|
|
output.push(val.clone());
|
|
|
|
}
|
2022-09-05 20:43:57 +02:00
|
|
|
|
2022-09-18 22:53:08 +02:00
|
|
|
Ok(Value::List(NixList::construct(output.len(), output)))
|
2022-09-05 20:43:57 +02:00
|
|
|
}),
|
2022-10-13 08:58:53 +02:00
|
|
|
Builtin::new("baseNameOf", &[true], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let s = args[0].coerce_to_string(CoercionKind::Weak, vm)?;
|
|
|
|
let result: String = s.rsplit_once('/').map(|(_, x)| x).unwrap_or(&s).into();
|
|
|
|
Ok(result.into())
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("bitAnd", &[true, true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
Ok(Value::Integer(args[0].as_int()? & args[1].as_int()?))
|
2022-09-06 07:16:17 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("bitOr", &[true, true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
Ok(Value::Integer(args[0].as_int()? | args[1].as_int()?))
|
2022-09-06 07:17:04 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("bitXor", &[true, true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
Ok(Value::Integer(args[0].as_int()? ^ args[1].as_int()?))
|
2022-09-06 07:18:43 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new(
|
|
|
|
"catAttrs",
|
|
|
|
&[true, true],
|
|
|
|
|args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let key = args[0].to_str()?;
|
|
|
|
let list = args[1].to_list()?;
|
|
|
|
let mut output = vec![];
|
|
|
|
|
|
|
|
for item in list.into_iter() {
|
|
|
|
let set = item.force(vm)?.to_attrs()?;
|
|
|
|
if let Some(value) = set.select(key.as_str()) {
|
|
|
|
output.push(value.clone());
|
|
|
|
}
|
2022-08-24 16:41:23 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 17:20:06 +02:00
|
|
|
Ok(Value::List(NixList::construct(output.len(), output)))
|
|
|
|
},
|
|
|
|
),
|
|
|
|
Builtin::new(
|
|
|
|
"compareVersions",
|
|
|
|
&[true, true],
|
|
|
|
|args: Vec<Value>, _: &mut VM| {
|
|
|
|
let s1 = args[0].to_str()?;
|
|
|
|
let s1 = VersionPartsIter::new_for_cmp(s1.as_str());
|
|
|
|
let s2 = args[1].to_str()?;
|
|
|
|
let s2 = VersionPartsIter::new_for_cmp(s2.as_str());
|
|
|
|
|
|
|
|
match s1.cmp(s2) {
|
|
|
|
std::cmp::Ordering::Less => Ok(Value::Integer(-1)),
|
|
|
|
std::cmp::Ordering::Equal => Ok(Value::Integer(0)),
|
|
|
|
std::cmp::Ordering::Greater => Ok(Value::Integer(1)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
),
|
|
|
|
Builtin::new("concatLists", &[true], |args: Vec<Value>, vm: &mut VM| {
|
2022-10-02 18:41:39 +02:00
|
|
|
let list = args[0].to_list()?;
|
|
|
|
let lists = list
|
|
|
|
.into_iter()
|
|
|
|
.map(|elem| {
|
|
|
|
let value = elem.force(vm)?;
|
|
|
|
value.to_list()
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<NixList>, ErrorKind>>()?;
|
|
|
|
|
|
|
|
Ok(Value::List(NixList::from(
|
|
|
|
lists.into_iter().flatten().collect::<Vec<Value>>(),
|
|
|
|
)))
|
|
|
|
}),
|
2022-10-08 21:12:32 +02:00
|
|
|
Builtin::new(
|
|
|
|
"concatMap",
|
|
|
|
&[true, true],
|
|
|
|
|args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let list = args[1].to_list()?;
|
|
|
|
let mut res = Vec::new();
|
|
|
|
for val in list {
|
2022-10-09 19:18:32 +02:00
|
|
|
res.extend(vm.call_with(&args[0], [val])?.force(vm)?.to_list()?);
|
2022-10-08 21:12:32 +02:00
|
|
|
}
|
|
|
|
Ok(Value::List(res.into()))
|
|
|
|
},
|
|
|
|
),
|
2022-10-09 19:09:11 +02:00
|
|
|
Builtin::new(
|
|
|
|
"concatStringsSep",
|
|
|
|
&[true, true],
|
|
|
|
|args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let separator = args[0].to_str()?;
|
|
|
|
let list = args[1].to_list()?;
|
|
|
|
let mut res = String::new();
|
|
|
|
for (i, val) in list.into_iter().enumerate() {
|
|
|
|
if i != 0 {
|
|
|
|
res.push_str(&separator);
|
|
|
|
}
|
|
|
|
res.push_str(&val.force(vm)?.coerce_to_string(CoercionKind::Weak, vm)?);
|
|
|
|
}
|
|
|
|
Ok(res.into())
|
|
|
|
},
|
|
|
|
),
|
2022-10-13 04:47:23 +02:00
|
|
|
Builtin::new(
|
|
|
|
"deepSeq",
|
|
|
|
&[true, true],
|
|
|
|
|mut args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let arg2 = args.pop().unwrap();
|
|
|
|
let arg1 = args.pop().unwrap();
|
|
|
|
arg1.deep_force(vm, &mut Default::default())?;
|
|
|
|
Ok(arg2)
|
|
|
|
},
|
|
|
|
),
|
2022-09-21 22:20:37 +02:00
|
|
|
Builtin::new(
|
|
|
|
"div",
|
|
|
|
&[false, false],
|
2022-10-04 17:20:06 +02:00
|
|
|
|args: Vec<Value>, vm: &mut VM| arithmetic_op!(&*args[0].force(vm)?, &*args[1].force(vm)?, /),
|
2022-09-21 22:20:37 +02:00
|
|
|
),
|
2022-10-12 11:46:20 +02:00
|
|
|
Builtin::new("dirOf", &[true], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let s = args[0].coerce_to_string(CoercionKind::Weak, vm)?;
|
|
|
|
let result = s
|
|
|
|
.rsplit_once('/')
|
|
|
|
.map(|(x, _)| match x {
|
|
|
|
"" => "/",
|
|
|
|
_ => x,
|
|
|
|
})
|
|
|
|
.unwrap_or(".");
|
|
|
|
if args[0].is_path() {
|
|
|
|
Ok(Value::Path(result.into()))
|
|
|
|
} else {
|
|
|
|
Ok(result.into())
|
|
|
|
}
|
|
|
|
}),
|
2022-10-10 04:11:32 +02:00
|
|
|
Builtin::new("elem", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
for val in args[1].to_list()? {
|
|
|
|
if val.nix_eq(&args[0], vm)? {
|
|
|
|
return Ok(true.into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(false.into())
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("elemAt", &[true, true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
let xs = args[0].to_list()?;
|
2022-09-18 22:53:08 +02:00
|
|
|
let i = args[1].as_int()?;
|
|
|
|
if i < 0 {
|
|
|
|
Err(ErrorKind::IndexOutOfBounds { index: i })
|
|
|
|
} else {
|
|
|
|
match xs.get(i as usize) {
|
|
|
|
Some(x) => Ok(x.clone()),
|
|
|
|
None => Err(ErrorKind::IndexOutOfBounds { index: i }),
|
2022-09-06 20:58:28 +02:00
|
|
|
}
|
2022-09-18 22:53:08 +02:00
|
|
|
}
|
2022-09-06 20:58:28 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("filter", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-23 02:14:34 +02:00
|
|
|
let list: NixList = args[1].to_list()?;
|
|
|
|
|
|
|
|
list.into_iter()
|
|
|
|
.filter_map(|elem| {
|
2022-10-09 19:18:32 +02:00
|
|
|
let result = match vm.call_with(&args[0], [elem.clone()]) {
|
2022-09-23 02:14:34 +02:00
|
|
|
Err(err) => return Some(Err(err)),
|
|
|
|
Ok(result) => result,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Must be assigned to a local to avoid a borrowcheck
|
|
|
|
// failure related to the ForceResult destructor.
|
|
|
|
let result = match result.force(vm) {
|
|
|
|
Err(err) => Some(Err(vm.error(err))),
|
|
|
|
Ok(value) => match value.as_bool() {
|
|
|
|
Ok(true) => Some(Ok(elem)),
|
|
|
|
Ok(false) => None,
|
|
|
|
Err(err) => Some(Err(vm.error(err))),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
result
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<Value>, _>>()
|
|
|
|
.map(|list| Value::List(NixList::from(list)))
|
|
|
|
.map_err(Into::into)
|
|
|
|
}),
|
2022-10-09 18:59:23 +02:00
|
|
|
Builtin::new(
|
|
|
|
"foldl'",
|
|
|
|
&[true, false, true],
|
|
|
|
|mut args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let list = args.pop().unwrap().to_list()?;
|
|
|
|
let mut res = args.pop().unwrap();
|
|
|
|
let op = args.pop().unwrap();
|
|
|
|
for val in list {
|
2022-10-23 18:25:44 +02:00
|
|
|
res = vm.call_with(&op, [res, val])?;
|
2022-10-15 15:52:37 +02:00
|
|
|
res.force(vm)?;
|
2022-10-09 18:59:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
},
|
|
|
|
),
|
2022-10-13 06:12:25 +02:00
|
|
|
Builtin::new("functionArgs", &[true], |args: Vec<Value>, _: &mut VM| {
|
|
|
|
let lambda = args[0].to_closure()?.lambda();
|
|
|
|
let formals = if let Some(formals) = &lambda.formals {
|
|
|
|
formals
|
|
|
|
} else {
|
|
|
|
return Ok(Value::attrs(NixAttrs::empty()));
|
|
|
|
};
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(
|
|
|
|
formals
|
|
|
|
.arguments
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| (k.clone(), (*v).into()))
|
|
|
|
.collect(),
|
|
|
|
)))
|
|
|
|
}),
|
2022-10-10 06:32:57 +02:00
|
|
|
Builtin::new("fromJSON", &[true], |args: Vec<Value>, _: &mut VM| {
|
|
|
|
let json_str = args[0].to_str()?;
|
|
|
|
let json: serde_json::Value = serde_json::from_str(&json_str)?;
|
|
|
|
json.try_into()
|
|
|
|
}),
|
2022-10-08 21:20:27 +02:00
|
|
|
Builtin::new("genList", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let len = args[1].as_int()?;
|
|
|
|
(0..len)
|
2022-10-09 19:18:32 +02:00
|
|
|
.map(|i| vm.call_with(&args[0], [i.into()]))
|
2022-10-08 21:20:27 +02:00
|
|
|
.collect::<Result<Vec<Value>, _>>()
|
|
|
|
.map(|list| Value::List(NixList::from(list)))
|
|
|
|
.map_err(Into::into)
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("getAttr", &[true, true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-06 23:20:14 +02:00
|
|
|
let k = args[0].to_str()?;
|
|
|
|
let xs = args[1].to_attrs()?;
|
|
|
|
|
|
|
|
match xs.select(k.as_str()) {
|
|
|
|
Some(x) => Ok(x.clone()),
|
|
|
|
None => Err(ErrorKind::AttributeNotFound {
|
|
|
|
name: k.to_string(),
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}),
|
2022-10-18 12:32:35 +02:00
|
|
|
Builtin::new("groupBy", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let mut res: BTreeMap<NixString, Value> = BTreeMap::new();
|
|
|
|
for val in args[1].to_list()? {
|
|
|
|
let key = vm.call_with(&args[0], [val.clone()])?.force(vm)?.to_str()?;
|
|
|
|
res.entry(key)
|
|
|
|
.or_insert_with(|| Value::List(NixList::new()))
|
|
|
|
.as_list_mut()?
|
|
|
|
.push(val);
|
|
|
|
}
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(res)))
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("hasAttr", &[true, true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-06 23:13:06 +02:00
|
|
|
let k = args[0].to_str()?;
|
|
|
|
let xs = args[1].to_attrs()?;
|
|
|
|
|
|
|
|
Ok(Value::Bool(xs.contains(k.as_str())))
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("head", &[true], |args: Vec<Value>, _: &mut VM| {
|
|
|
|
match args[0].to_list()?.get(0) {
|
|
|
|
Some(x) => Ok(x.clone()),
|
|
|
|
None => Err(ErrorKind::IndexOutOfBounds { index: 0 }),
|
|
|
|
}
|
2022-09-05 20:41:50 +02:00
|
|
|
}),
|
2022-10-13 06:04:55 +02:00
|
|
|
Builtin::new(
|
|
|
|
"intersectAttrs",
|
|
|
|
&[true, true],
|
|
|
|
|args: Vec<Value>, _: &mut VM| {
|
|
|
|
let mut res = BTreeMap::new();
|
|
|
|
let attrs1 = args[0].to_attrs()?;
|
|
|
|
let attrs2 = args[1].to_attrs()?;
|
|
|
|
for (k, v) in attrs2.iter() {
|
|
|
|
if attrs1.contains(k) {
|
|
|
|
res.insert(k.clone(), v.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(res)))
|
|
|
|
},
|
|
|
|
),
|
2022-09-19 11:47:34 +02:00
|
|
|
// For `is*` predicates we force manually, as Value::force also unwraps any Thunks
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isAttrs", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::Attrs(_))))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isBool", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::Bool(_))))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isFloat", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::Float(_))))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isFunction", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
2022-08-25 17:39:27 +02:00
|
|
|
Ok(Value::Bool(matches!(
|
2022-09-19 11:47:34 +02:00
|
|
|
*value,
|
2022-08-25 17:39:27 +02:00
|
|
|
Value::Closure(_) | Value::Builtin(_)
|
|
|
|
)))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isInt", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::Integer(_))))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isList", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::List(_))))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isNull", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::Null)))
|
2022-08-24 15:58:55 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isPath", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::Path(_))))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("isString", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 11:47:34 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::Bool(matches!(*value, Value::String(_))))
|
2022-08-24 16:21:26 +02:00
|
|
|
}),
|
2022-10-13 23:29:12 +02:00
|
|
|
Builtin::new("length", &[true], |args: Vec<Value>, _: &mut VM| {
|
|
|
|
Ok(Value::Integer(args[0].to_list()?.len() as i64))
|
|
|
|
}),
|
|
|
|
Builtin::new(
|
|
|
|
"lessThan",
|
|
|
|
&[false, false],
|
|
|
|
|args: Vec<Value>, vm: &mut VM| cmp_op!(&*args[0].force(vm)?, &*args[1].force(vm)?, <),
|
|
|
|
),
|
2022-10-08 20:17:47 +02:00
|
|
|
Builtin::new("listToAttrs", &[true], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let list = args[0].to_list()?;
|
|
|
|
let mut map = BTreeMap::new();
|
|
|
|
for val in list {
|
|
|
|
let attrs = val.force(vm)?.to_attrs()?;
|
|
|
|
let get = |key| {
|
|
|
|
attrs
|
|
|
|
.select(key)
|
|
|
|
.ok_or(ErrorKind::AttributeNotFound { name: key.into() })
|
|
|
|
};
|
|
|
|
let name = get("name")?.to_str()?;
|
|
|
|
let value = get("value")?.clone();
|
|
|
|
// Map entries earlier in the list take precedence over entries later in the list
|
|
|
|
map.entry(name).or_insert(value);
|
|
|
|
}
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(map)))
|
|
|
|
}),
|
2022-10-13 23:29:12 +02:00
|
|
|
Builtin::new("map", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let list: NixList = args[1].to_list()?;
|
|
|
|
|
|
|
|
list.into_iter()
|
|
|
|
.map(|val| vm.call_with(&args[0], [val]))
|
|
|
|
.collect::<Result<Vec<Value>, _>>()
|
|
|
|
.map(|list| Value::List(NixList::from(list)))
|
|
|
|
.map_err(Into::into)
|
|
|
|
}),
|
2022-10-13 00:45:52 +02:00
|
|
|
Builtin::new(
|
|
|
|
"match",
|
|
|
|
&[true, true],
|
|
|
|
|mut args: Vec<Value>, _: &mut VM| {
|
|
|
|
let s = args.pop().unwrap().to_str()?;
|
|
|
|
let re = args.pop().unwrap().to_str()?;
|
|
|
|
let re: Regex = Regex::new(&format!("^{}$", re.as_str())).unwrap();
|
|
|
|
match re.captures(&s) {
|
|
|
|
Some(caps) => Ok(caps
|
|
|
|
.iter()
|
|
|
|
.skip(1)
|
|
|
|
.map(|grp| grp.map(|g| Value::from(g.as_str())).unwrap_or(Value::Null))
|
|
|
|
.collect::<Vec<Value>>()
|
|
|
|
.into()),
|
|
|
|
None => Ok(Value::Null),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
),
|
2022-09-21 22:20:37 +02:00
|
|
|
Builtin::new(
|
|
|
|
"mul",
|
|
|
|
&[false, false],
|
2022-10-04 17:20:06 +02:00
|
|
|
|args: Vec<Value>, vm: &mut VM| arithmetic_op!(&*args[0].force(vm)?, &*args[1].force(vm)?, *),
|
2022-09-21 22:20:37 +02:00
|
|
|
),
|
2022-10-13 05:11:48 +02:00
|
|
|
Builtin::new("parseDrvName", &[true], |args: Vec<Value>, _vm: &mut VM| {
|
2022-10-09 21:46:21 +02:00
|
|
|
// This replicates cppnix's (mis?)handling of codepoints
|
|
|
|
// above U+007f following 0x2d ('-')
|
2022-10-13 05:11:48 +02:00
|
|
|
let s = args[0].to_str()?;
|
2022-10-09 21:46:21 +02:00
|
|
|
let slice: &[u8] = s.as_str().as_ref();
|
|
|
|
let (name, dash_and_version) = slice.split_at(
|
|
|
|
slice
|
|
|
|
.windows(2)
|
|
|
|
.enumerate()
|
|
|
|
.find_map(|x| match x {
|
|
|
|
(idx, [b'-', c1]) if !c1.is_ascii_alphabetic() => Some(idx),
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.unwrap_or(slice.len()),
|
|
|
|
);
|
|
|
|
let version = dash_and_version
|
|
|
|
.split_first()
|
|
|
|
.map(|x| core::str::from_utf8(x.1))
|
|
|
|
.unwrap_or(Ok(""))?
|
|
|
|
.into();
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(BTreeMap::from([
|
|
|
|
(NixString::NAME, core::str::from_utf8(name)?.into()),
|
|
|
|
("version".into(), version),
|
|
|
|
]))))
|
|
|
|
}),
|
2022-10-14 13:49:20 +02:00
|
|
|
Builtin::new(
|
|
|
|
"partition",
|
|
|
|
&[true, true],
|
|
|
|
|args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let mut right: Vec<Value> = vec![];
|
|
|
|
let mut wrong: Vec<Value> = vec![];
|
|
|
|
|
|
|
|
let list: NixList = args[1].to_list()?;
|
|
|
|
for elem in list.into_iter() {
|
|
|
|
let result = vm.call_with(&args[0], [elem.clone()])?;
|
|
|
|
|
|
|
|
if result.force(vm)?.as_bool()? {
|
|
|
|
right.push(elem);
|
|
|
|
} else {
|
|
|
|
wrong.push(elem);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut res: BTreeMap<NixString, Value> = BTreeMap::new();
|
|
|
|
res.insert("right".into(), Value::List(right.into()));
|
|
|
|
res.insert("wrong".into(), Value::List(wrong.into()));
|
|
|
|
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(res)))
|
|
|
|
},
|
|
|
|
),
|
2022-10-13 23:29:12 +02:00
|
|
|
Builtin::new(
|
|
|
|
"removeAttrs",
|
|
|
|
&[true, true],
|
|
|
|
|args: Vec<Value>, _: &mut VM| {
|
|
|
|
let attrs = args[0].to_attrs()?;
|
|
|
|
let keys = args[1]
|
|
|
|
.to_list()?
|
|
|
|
.into_iter()
|
|
|
|
.map(|v| v.to_str())
|
|
|
|
.collect::<Result<HashSet<_>, _>>()?;
|
|
|
|
let mut res = BTreeMap::new();
|
|
|
|
for (k, v) in attrs.iter() {
|
|
|
|
if !keys.contains(k) {
|
|
|
|
res.insert(k.clone(), v.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(res)))
|
|
|
|
},
|
|
|
|
),
|
2022-10-13 04:36:10 +02:00
|
|
|
Builtin::new("seq", &[true, true], |mut args: Vec<Value>, _: &mut VM| {
|
|
|
|
// The builtin calling infra has already forced both args for us, so we just return the
|
|
|
|
// second and ignore the first
|
|
|
|
Ok(args.pop().unwrap())
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("splitVersion", &[true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 10:14:05 +02:00
|
|
|
let s = args[0].to_str()?;
|
|
|
|
let s = VersionPartsIter::new(s.as_str());
|
|
|
|
|
|
|
|
let parts = s
|
|
|
|
.map(|s| {
|
|
|
|
Value::String(match s {
|
2022-09-20 12:51:02 +02:00
|
|
|
VersionPart::Number(n) => n.into(),
|
2022-09-19 10:14:05 +02:00
|
|
|
VersionPart::Word(w) => w.into(),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect::<Vec<Value>>();
|
|
|
|
Ok(Value::List(NixList::construct(parts.len(), parts)))
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("stringLength", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-21 22:30:39 +02:00
|
|
|
// also forces the value
|
|
|
|
let s = args[0].coerce_to_string(CoercionKind::Weak, vm)?;
|
|
|
|
Ok(Value::Integer(s.as_str().len() as i64))
|
|
|
|
}),
|
2022-09-21 22:20:37 +02:00
|
|
|
Builtin::new(
|
|
|
|
"sub",
|
|
|
|
&[false, false],
|
2022-10-04 17:20:06 +02:00
|
|
|
|args: Vec<Value>, vm: &mut VM| arithmetic_op!(&*args[0].force(vm)?, &*args[1].force(vm)?, -),
|
2022-09-21 22:20:37 +02:00
|
|
|
),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new(
|
|
|
|
"substring",
|
|
|
|
&[true, true, true],
|
|
|
|
|args: Vec<Value>, _: &mut VM| {
|
|
|
|
let beg = args[0].as_int()?;
|
|
|
|
let len = args[1].as_int()?;
|
|
|
|
let x = args[2].to_str()?;
|
|
|
|
|
|
|
|
if beg < 0 {
|
|
|
|
return Err(ErrorKind::IndexOutOfBounds { index: beg });
|
|
|
|
}
|
|
|
|
let beg = beg as usize;
|
2022-09-06 23:33:10 +02:00
|
|
|
|
2022-10-04 17:20:06 +02:00
|
|
|
// Nix doesn't assert that the length argument is
|
|
|
|
// non-negative when the starting index is GTE the
|
|
|
|
// string's length.
|
|
|
|
if beg >= x.as_str().len() {
|
|
|
|
return Ok(Value::String("".into()));
|
|
|
|
}
|
2022-09-06 23:33:10 +02:00
|
|
|
|
2022-10-04 17:20:06 +02:00
|
|
|
if len < 0 {
|
|
|
|
return Err(ErrorKind::NegativeLength { length: len });
|
|
|
|
}
|
2022-09-06 23:33:10 +02:00
|
|
|
|
2022-10-04 17:20:06 +02:00
|
|
|
let len = len as usize;
|
|
|
|
let end = cmp::min(beg + len, x.as_str().len());
|
2022-09-06 23:33:10 +02:00
|
|
|
|
2022-10-04 17:20:06 +02:00
|
|
|
Ok(Value::String(
|
|
|
|
x.as_str()[(beg as usize)..(end as usize)].into(),
|
|
|
|
))
|
|
|
|
},
|
|
|
|
),
|
|
|
|
Builtin::new("tail", &[true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
let xs = args[0].to_list()?;
|
2022-09-05 20:42:53 +02:00
|
|
|
|
|
|
|
if xs.len() == 0 {
|
|
|
|
Err(ErrorKind::TailEmptyList)
|
|
|
|
} else {
|
|
|
|
let output = xs.into_iter().skip(1).collect::<Vec<_>>();
|
|
|
|
Ok(Value::List(NixList::construct(output.len(), output)))
|
|
|
|
}
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("throw", &[true], |args: Vec<Value>, _: &mut VM| {
|
2022-09-24 15:38:16 +02:00
|
|
|
Err(ErrorKind::Throw(args[0].to_str()?.to_string()))
|
2022-08-24 16:13:00 +02:00
|
|
|
}),
|
2022-10-13 23:29:12 +02:00
|
|
|
// coerce_to_string forces for us
|
|
|
|
Builtin::new("toString", &[false], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
args[0]
|
|
|
|
.coerce_to_string(CoercionKind::Strong, vm)
|
|
|
|
.map(Value::String)
|
|
|
|
}),
|
2022-10-11 01:16:30 +02:00
|
|
|
Builtin::new(
|
|
|
|
"trace",
|
|
|
|
&[true, true],
|
|
|
|
|mut args: Vec<Value>, _: &mut VM| {
|
|
|
|
let value = args.pop().unwrap();
|
|
|
|
let trace_value = args.pop().unwrap();
|
|
|
|
// TODO(grfn): `trace` should be pluggable and capturable, probably via a method on
|
|
|
|
// the VM
|
|
|
|
println!("trace: {} :: {}", trace_value, trace_value.type_of());
|
|
|
|
Ok(value)
|
|
|
|
},
|
|
|
|
),
|
2022-10-10 17:54:53 +02:00
|
|
|
Builtin::new("tryEval", &[false], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let mut res = BTreeMap::new();
|
|
|
|
match args[0].force(vm) {
|
|
|
|
Ok(value) => {
|
|
|
|
res.insert("value".into(), (*value).clone());
|
|
|
|
res.insert("success".into(), true.into());
|
|
|
|
}
|
|
|
|
Err(e) if e.is_catchable() => {
|
|
|
|
res.insert("value".into(), false.into());
|
|
|
|
res.insert("success".into(), false.into());
|
|
|
|
}
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
}
|
|
|
|
Ok(Value::attrs(NixAttrs::from_map(res)))
|
|
|
|
}),
|
2022-10-13 08:13:10 +02:00
|
|
|
Builtin::new("toPath", &[false], |args: Vec<Value>, vm: &mut VM| {
|
|
|
|
let path: Value = crate::value::canon_path(coerce_value_to_path(&args[0], vm)?).into();
|
|
|
|
Ok(path.coerce_to_string(CoercionKind::Weak, vm)?.into())
|
|
|
|
}),
|
2022-10-04 17:20:06 +02:00
|
|
|
Builtin::new("typeOf", &[false], |args: Vec<Value>, vm: &mut VM| {
|
2022-09-19 00:48:08 +02:00
|
|
|
// We force manually here because it also unwraps the Thunk
|
|
|
|
// representation, if any.
|
|
|
|
// TODO(sterni): it'd be nice if we didn't have to worry about this
|
2022-09-18 22:53:08 +02:00
|
|
|
let value = args[0].force(vm)?;
|
|
|
|
Ok(Value::String(value.type_of().into()))
|
2022-08-24 16:23:23 +02:00
|
|
|
}),
|
2022-08-24 15:58:55 +02:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2022-10-22 16:57:02 +02:00
|
|
|
/// Placeholder builtins that technically have a function which we do
|
|
|
|
/// not yet implement, but which is also not easily observable from
|
|
|
|
/// within a pure evaluation context.
|
|
|
|
///
|
|
|
|
/// These are used as a crutch to make progress on nixpkgs evaluation.
|
|
|
|
fn placeholders() -> Vec<Builtin> {
|
|
|
|
vec![Builtin::new(
|
|
|
|
"addErrorContext",
|
|
|
|
&[false, false],
|
|
|
|
|mut args: Vec<Value>, vm: &mut VM| {
|
|
|
|
vm.emit_warning(WarningKind::NotImplemented("builtins.addErrorContext"));
|
|
|
|
Ok(args.pop().unwrap())
|
|
|
|
},
|
|
|
|
)]
|
|
|
|
}
|
2022-10-12 11:26:40 +02:00
|
|
|
// we set TVIX_CURRENT_SYSTEM in build.rs
|
|
|
|
pub const CURRENT_PLATFORM: &str = env!("TVIX_CURRENT_SYSTEM");
|
2022-10-22 16:57:02 +02:00
|
|
|
|
2022-08-24 15:58:55 +02:00
|
|
|
fn builtins_set() -> NixAttrs {
|
|
|
|
let mut map: BTreeMap<NixString, Value> = BTreeMap::new();
|
|
|
|
|
2022-10-04 17:40:57 +02:00
|
|
|
// Pure-value builtins
|
|
|
|
map.insert(
|
|
|
|
"nixVersion".into(),
|
|
|
|
Value::String("2.3-compat-tvix-0.1".into()),
|
|
|
|
);
|
|
|
|
|
2022-10-24 14:42:31 +02:00
|
|
|
map.insert("langVersion".into(), Value::Integer(6));
|
|
|
|
|
2022-10-12 11:26:40 +02:00
|
|
|
map.insert(
|
|
|
|
"currentSystem".into(),
|
|
|
|
crate::systems::llvm_triple_to_nix_double(CURRENT_PLATFORM).into(),
|
|
|
|
);
|
|
|
|
|
2022-09-18 22:34:41 +02:00
|
|
|
let mut add_builtins = |builtins: Vec<Builtin>| {
|
|
|
|
for builtin in builtins {
|
|
|
|
map.insert(builtin.name().into(), Value::Builtin(builtin));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
add_builtins(pure_builtins());
|
2022-10-22 16:57:02 +02:00
|
|
|
add_builtins(placeholders());
|
|
|
|
|
2022-09-18 22:34:41 +02:00
|
|
|
#[cfg(feature = "impure")]
|
|
|
|
{
|
2022-10-03 10:26:32 +02:00
|
|
|
map.extend(impure::builtins());
|
2022-08-24 15:58:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
NixAttrs::from_map(map)
|
2022-08-24 15:03:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set of Nix builtins that are globally available.
|
|
|
|
pub fn global_builtins() -> HashMap<&'static str, Value> {
|
2022-08-24 15:58:55 +02:00
|
|
|
let builtins = builtins_set();
|
|
|
|
let mut globals: HashMap<&'static str, Value> = HashMap::new();
|
2022-08-24 15:03:17 +02:00
|
|
|
|
2022-08-24 15:58:55 +02:00
|
|
|
// known global builtins from the builtins set.
|
|
|
|
for global in &[
|
|
|
|
"abort",
|
|
|
|
"baseNameOf",
|
|
|
|
"derivation",
|
|
|
|
"derivationStrict",
|
|
|
|
"dirOf",
|
|
|
|
"fetchGit",
|
|
|
|
"fetchMercurial",
|
|
|
|
"fetchTarball",
|
|
|
|
"fromTOML",
|
|
|
|
"import",
|
|
|
|
"isNull",
|
|
|
|
"map",
|
|
|
|
"placeholder",
|
|
|
|
"removeAttrs",
|
|
|
|
"scopedImport",
|
|
|
|
"throw",
|
|
|
|
"toString",
|
|
|
|
] {
|
|
|
|
if let Some(builtin) = builtins.select(global) {
|
|
|
|
globals.insert(global, builtin.clone());
|
|
|
|
}
|
|
|
|
}
|
2022-08-24 15:24:01 +02:00
|
|
|
|
2022-10-08 19:33:33 +02:00
|
|
|
globals.insert("builtins", Value::attrs(builtins));
|
2022-08-24 15:03:17 +02:00
|
|
|
|
|
|
|
globals
|
|
|
|
}
|