feat(tvix/eval): add initial representation of builtins

Builtins are represented as a Rust function pointer that accepts a
vector of arguments, which represents variable arity builtins.

Change-Id: Ibab7e662a646caf1172695d876d2f55e187c03dd
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6251
Tested-by: BuildkiteCI
Reviewed-by: grfn <grfn@gws.fyi>
This commit is contained in:
Vincent Ambo 2022-08-24 11:00:30 +03:00 committed by tazjin
parent 64746388e2
commit e0f1356ae3
3 changed files with 76 additions and 1 deletions

View file

@ -0,0 +1,66 @@
//! This module implements the runtime representation of a Nix
//! builtin.
//!
//! Builtins are directly backed by Rust code operating on Nix values.
use crate::errors::EvalResult;
use super::Value;
use std::fmt::{Debug, Display};
pub type BuiltinFn = fn(arg: Vec<Value>) -> EvalResult<Value>;
/// Represents a single built-in function which directly executes Rust
/// code that operates on a Nix value.
///
/// Builtins are the only functions in Nix that have varying arities
/// (for example, `hasAttr` has an arity of 2, but `isAttrs` an arity
/// of 1). To facilitate this generically, builtins expect to be
/// called with a vector of Nix values corresponding to their
/// arguments in order.
///
/// Partially applied builtins act similar to closures in that they
/// "capture" the partially applied arguments, and are treated
/// specially when printing their representation etc.
#[derive(Clone)]
pub struct Builtin {
name: &'static str,
arity: usize,
func: BuiltinFn,
// Partially applied function arguments.
partials: Vec<Value>,
}
impl Builtin {
/// Apply an additional argument to the builtin, which will either
/// lead to execution of the function or to returning a partial
/// builtin.
pub fn apply(mut self, arg: Value) -> EvalResult<Value> {
self.partials.push(arg);
if self.partials.len() == self.arity {
return (self.func)(self.partials);
}
// Function is not yet ready to be called.
return Ok(Value::Builtin(self));
}
}
impl Debug for Builtin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "builtin[{}]", self.name)
}
}
impl Display for Builtin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if !self.partials.is_empty() {
f.write_str("<<primop-app>>")
} else {
f.write_str("<<primop>>")
}
}
}