feat: list concat & attrs update
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -69,7 +69,7 @@ dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
@@ -481,7 +481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -856,6 +856,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
@@ -1022,6 +1031,7 @@ dependencies = [
|
||||
"deno_core",
|
||||
"derive_more",
|
||||
"hashbrown 0.16.1",
|
||||
"itertools 0.14.0",
|
||||
"mimalloc",
|
||||
"nix-js-macros",
|
||||
"regex",
|
||||
@@ -1344,7 +1354,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -15,6 +15,7 @@ hashbrown = "0.16"
|
||||
derive_more = { version = "2", features = ["full"] }
|
||||
thiserror = "2"
|
||||
string-interner = "0.19"
|
||||
itertools = "0.14"
|
||||
|
||||
v8 = "142.2"
|
||||
deno_core = "0.376"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use itertools::Itertools as _;
|
||||
|
||||
use crate::ir::*;
|
||||
|
||||
pub trait Compile<Ctx: CodegenContext> {
|
||||
@@ -28,17 +30,18 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
Ir::UnOp(x) => x.compile(ctx),
|
||||
Ir::Func(x) => x.compile(ctx),
|
||||
Ir::AttrSet(x) => x.compile(ctx),
|
||||
Ir::List(x) => x.compile(ctx),
|
||||
&Ir::Call(Call { func, arg }) => {
|
||||
let func = ctx.get_ir(func).compile(ctx);
|
||||
let arg = ctx.get_ir(arg).compile(ctx);
|
||||
format!("NixRuntime.force({func})({arg})")
|
||||
format!("Nix.force({func})({arg})")
|
||||
}
|
||||
Ir::Arg(x) => format!("arg{}", x.0),
|
||||
Ir::Let(x) => x.compile(ctx),
|
||||
Ir::Select(x) => x.compile(ctx),
|
||||
&Ir::Thunk(expr_id) => {
|
||||
let inner = ctx.get_ir(expr_id).compile(ctx);
|
||||
format!("NixRuntime.create_thunk(()=>({}))", inner)
|
||||
format!("Nix.create_thunk(()=>({}))", inner)
|
||||
}
|
||||
&Ir::ExprRef(expr_id) => {
|
||||
format!("expr{}", expr_id.0)
|
||||
@@ -54,20 +57,23 @@ impl<Ctx: CodegenContext> Compile<Ctx> for BinOp {
|
||||
let lhs = ctx.get_ir(self.lhs).compile(ctx);
|
||||
let rhs = ctx.get_ir(self.rhs).compile(ctx);
|
||||
match self.kind {
|
||||
Add => format!("NixRuntime.op.add({},{})", lhs, rhs),
|
||||
Sub => format!("NixRuntime.op.sub({},{})", lhs, rhs),
|
||||
Mul => format!("NixRuntime.op.mul({},{})", lhs, rhs),
|
||||
Div => format!("NixRuntime.op.div({},{})", lhs, rhs),
|
||||
Eq => format!("NixRuntime.op.eq({},{})", lhs, rhs),
|
||||
Neq => format!("NixRuntime.op.neq({},{})", lhs, rhs),
|
||||
Lt => format!("NixRuntime.op.lt({},{})", lhs, rhs),
|
||||
Gt => format!("NixRuntime.op.gt({},{})", lhs, rhs),
|
||||
Leq => format!("NixRuntime.op.lte({},{})", lhs, rhs),
|
||||
Geq => format!("NixRuntime.op.gte({},{})", lhs, rhs),
|
||||
And => format!("NixRuntime.op.band({},{})", lhs, rhs),
|
||||
Or => format!("NixRuntime.op.bor({},{})", lhs, rhs),
|
||||
Impl => format!("NixRuntime.op.bor(NixRuntime.op.bnot({}),{})", lhs, rhs),
|
||||
_ => todo!("BinOpKind::{:?}", self.kind),
|
||||
Add => format!("Nix.op.add({},{})", lhs, rhs),
|
||||
Sub => format!("Nix.op.sub({},{})", lhs, rhs),
|
||||
Mul => format!("Nix.op.mul({},{})", lhs, rhs),
|
||||
Div => format!("Nix.op.div({},{})", lhs, rhs),
|
||||
Eq => format!("Nix.op.eq({},{})", lhs, rhs),
|
||||
Neq => format!("Nix.op.neq({},{})", lhs, rhs),
|
||||
Lt => format!("Nix.op.lt({},{})", lhs, rhs),
|
||||
Gt => format!("Nix.op.gt({},{})", lhs, rhs),
|
||||
Leq => format!("Nix.op.lte({},{})", lhs, rhs),
|
||||
Geq => format!("Nix.op.gte({},{})", lhs, rhs),
|
||||
And => format!("Nix.op.band({},{})", lhs, rhs),
|
||||
Or => format!("Nix.op.bor({},{})", lhs, rhs),
|
||||
Impl => format!("Nix.op.bor(Nix.op.bnot({}),{})", lhs, rhs),
|
||||
Con => format!("Nix.op.concat({},{})", lhs, rhs),
|
||||
Upd => format!("Nix.op.update({},{})", lhs, rhs),
|
||||
PipeL => format!("Nix.force({})({})", rhs, lhs),
|
||||
PipeR => format!("Nix.force({})({})", lhs, rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,8 +83,8 @@ impl<Ctx: CodegenContext> Compile<Ctx> for UnOp {
|
||||
use UnOpKind::*;
|
||||
let rhs = ctx.get_ir(self.rhs).compile(ctx);
|
||||
match self.kind {
|
||||
Neg => format!("NixRuntime.op.sub(0,{rhs})"),
|
||||
Not => format!("NixRuntime.op.bnot({rhs})")
|
||||
Neg => format!("Nix.op.sub(0,{rhs})"),
|
||||
Not => format!("Nix.op.bnot({rhs})")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,8 +140,8 @@ impl Func {
|
||||
"null".to_string()
|
||||
};
|
||||
|
||||
// Call NixRuntime.validate_params and store the result
|
||||
format!("NixRuntime.validate_params(arg{},{},{});", id, required, allowed)
|
||||
// Call Nix.validate_params and store the result
|
||||
format!("Nix.validate_params(arg{},{},{});", id, required, allowed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,18 +189,18 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
||||
let key = ctx.get_sym(*sym);
|
||||
if has_default {
|
||||
let default_val = ctx.get_ir(self.default.unwrap()).compile(ctx);
|
||||
format!("NixRuntime.select_with_default({}, \"{}\", {})", result, key, default_val)
|
||||
format!("Nix.select_with_default({}, \"{}\", {})", result, key, default_val)
|
||||
} else {
|
||||
format!("NixRuntime.select({}, \"{}\")", result, key)
|
||||
format!("Nix.select({}, \"{}\")", result, key)
|
||||
}
|
||||
}
|
||||
Attr::Dynamic(expr_id) => {
|
||||
let key = ctx.get_ir(*expr_id).compile(ctx);
|
||||
if has_default {
|
||||
let default_val = ctx.get_ir(self.default.unwrap()).compile(ctx);
|
||||
format!("NixRuntime.select_with_default({}, {}, {})", result, key, default_val)
|
||||
format!("Nix.select_with_default({}, {}, {})", result, key, default_val)
|
||||
} else {
|
||||
format!("NixRuntime.select({}, {})", result, key)
|
||||
format!("Nix.select({}, {})", result, key)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -223,3 +229,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||
format!("{{{}}}", attrs.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for List {
|
||||
fn compile(&self, ctx: &Ctx) -> String {
|
||||
let list = self.items.iter().map(|item| ctx.get_ir(*item).compile(ctx)).join(",");
|
||||
format!("[{list}]")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ impl Context {
|
||||
.downgrade_ctx()
|
||||
.downgrade(root.tree().expr().unwrap())?;
|
||||
let code = self.get_ir(root).compile(self);
|
||||
let code = format!("NixRuntime.force({})", code);
|
||||
let code = format!("Nix.force({})", code);
|
||||
println!("[DEBUG] generated code: {}", &code);
|
||||
crate::runtime::run(&code)
|
||||
}
|
||||
@@ -81,8 +81,10 @@ impl CodegenContext for Context {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::value::Const;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::*;
|
||||
use crate::value::{AttrSet, Const, List, Symbol};
|
||||
|
||||
#[test]
|
||||
fn basic_eval() {
|
||||
@@ -118,11 +120,56 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_binop() {
|
||||
let tests = [
|
||||
("1 + 1", Value::Const(Const::Int(2))),
|
||||
("2 - 1", Value::Const(Const::Int(1))),
|
||||
// FIXME: Floating point
|
||||
// ("1. * 1", Value::Const(Const::Float(1.))),
|
||||
// ("1 / 1.", Value::Const(Const::Float(1.))),
|
||||
("1 == 1", Value::Const(Const::Bool(true))),
|
||||
("1 != 1", Value::Const(Const::Bool(false))),
|
||||
("2 < 1", Value::Const(Const::Bool(false))),
|
||||
("2 > 1", Value::Const(Const::Bool(true))),
|
||||
("1 <= 1", Value::Const(Const::Bool(true))),
|
||||
("1 >= 1", Value::Const(Const::Bool(true))),
|
||||
("1 == 1 || (1 / 0)", Value::Const(Const::Bool(true))),
|
||||
("1 == 1 && 1 == 0", Value::Const(Const::Bool(false))),
|
||||
// ("true || (1 / 0)", Value::Const(Const::Bool(true))),
|
||||
// ("true && 1 == 0", Value::Const(Const::Bool(false))),
|
||||
(
|
||||
"[ 1 2 3 ] ++ [ 4 5 6 ]",
|
||||
Value::List(List::new(
|
||||
(1..=6).map(Const::Int).map(Value::Const).collect(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"{ a.b = 1; b = 2; } // { a.c = 2; }",
|
||||
Value::AttrSet(AttrSet::new(BTreeMap::from([
|
||||
(
|
||||
Symbol::from("a"),
|
||||
Value::AttrSet(AttrSet::new(BTreeMap::from([(
|
||||
Symbol::from("c"),
|
||||
Value::Const(Const::Int(2)),
|
||||
)]))),
|
||||
),
|
||||
(Symbol::from("b"), Value::Const(Const::Int(2))),
|
||||
]))),
|
||||
),
|
||||
];
|
||||
for (expr, expected) in tests {
|
||||
assert_eq!(Context::new().eval(expr).unwrap(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_param_check_required() {
|
||||
// Test function with required parameters
|
||||
assert_eq!(
|
||||
Context::new().eval("({ a, b }: a + b) { a = 1; b = 2; }").unwrap(),
|
||||
Context::new()
|
||||
.eval("({ a, b }: a + b) { a = 1; b = 2; }")
|
||||
.unwrap(),
|
||||
Value::Const(Const::Int(3))
|
||||
);
|
||||
|
||||
@@ -132,7 +179,9 @@ mod test {
|
||||
|
||||
// Test all required parameters present
|
||||
assert_eq!(
|
||||
Context::new().eval("({ x, y, z }: x + y + z) { x = 1; y = 2; z = 3; }").unwrap(),
|
||||
Context::new()
|
||||
.eval("({ x, y, z }: x + y + z) { x = 1; y = 2; z = 3; }")
|
||||
.unwrap(),
|
||||
Value::Const(Const::Int(6))
|
||||
);
|
||||
}
|
||||
@@ -145,7 +194,9 @@ mod test {
|
||||
|
||||
// Test function with ellipsis - should accept extra arguments
|
||||
assert_eq!(
|
||||
Context::new().eval("({ a, b, ... }: a + b) { a = 1; b = 2; c = 3; }").unwrap(),
|
||||
Context::new()
|
||||
.eval("({ a, b, ... }: a + b) { a = 1; b = 2; c = 3; }")
|
||||
.unwrap(),
|
||||
Value::Const(Const::Int(3))
|
||||
);
|
||||
}
|
||||
@@ -154,13 +205,17 @@ mod test {
|
||||
fn test_param_check_with_default() {
|
||||
// Test function with default parameters
|
||||
assert_eq!(
|
||||
Context::new().eval("({ a, b ? 5 }: a + b) { a = 1; }").unwrap(),
|
||||
Context::new()
|
||||
.eval("({ a, b ? 5 }: a + b) { a = 1; }")
|
||||
.unwrap(),
|
||||
Value::Const(Const::Int(6))
|
||||
);
|
||||
|
||||
// Test overriding default parameter
|
||||
assert_eq!(
|
||||
Context::new().eval("({ a, b ? 5 }: a + b) { a = 1; b = 10; }").unwrap(),
|
||||
Context::new()
|
||||
.eval("({ a, b ? 5 }: a + b) { a = 1; b = 10; }")
|
||||
.unwrap(),
|
||||
Value::Const(Const::Int(11))
|
||||
);
|
||||
}
|
||||
@@ -169,7 +224,9 @@ mod test {
|
||||
fn test_param_check_with_alias() {
|
||||
// Test function with @ pattern (alias)
|
||||
assert_eq!(
|
||||
Context::new().eval("(args@{ a, b }: args.a + args.b) { a = 1; b = 2; }").unwrap(),
|
||||
Context::new()
|
||||
.eval("(args@{ a, b }: args.a + args.b) { a = 1; b = 2; }")
|
||||
.unwrap(),
|
||||
Value::Const(Const::Int(3))
|
||||
);
|
||||
}
|
||||
@@ -178,7 +235,9 @@ mod test {
|
||||
fn test_simple_param_no_check() {
|
||||
// Test simple parameter (no pattern) should not have validation
|
||||
assert_eq!(
|
||||
Context::new().eval("(x: x.a + x.b) { a = 1; b = 2; }").unwrap(),
|
||||
Context::new()
|
||||
.eval("(x: x.a + x.b) { a = 1; b = 2; }")
|
||||
.unwrap(),
|
||||
Value::Const(Const::Int(3))
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const NixRuntime = (() => {
|
||||
const Nix = (() => {
|
||||
const IS_THUNK = Symbol("is_thunk");
|
||||
|
||||
class NixThunk {
|
||||
@@ -128,7 +128,10 @@ const NixRuntime = (() => {
|
||||
|
||||
band: (a, b) => force(a) && force(b),
|
||||
bor: (a, b) => force(a) || force(b),
|
||||
bnot: (a) => !force(a)
|
||||
bnot: (a) => !force(a),
|
||||
|
||||
concat: (a, b) => Array.prototype.concat.apply(force(a), force(b)),
|
||||
update: (a, b) => ({...force(a), ...force(b)}),
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user