1use std::cell::{Cell, RefCell};
8use std::collections::{HashSet, HashMap, VecDeque};
9use std::path::PathBuf;
10
11use rnix::ast::{self, AstToken, HasEntry, InterpolPart};
12use rowan::ast::AstNode;
13
14use crate::builtins;
15use crate::value::*;
16
17thread_local! { static EVAL_DEPTH: Cell<usize> = const { Cell::new(0) }; }
18
19
20thread_local! {
29 static CURRENT_SOURCE_ID: Cell<u32> = const { Cell::new(0) };
30}
31
32thread_local! {
40 static EVAL_FILE_STACK: RefCell<Vec<PathBuf>> = const { RefCell::new(Vec::new()) };
41 static NIX_TRACE_STACK: RefCell<Vec<NixTraceFrame>> = const { RefCell::new(Vec::new()) };
45}
46
47#[derive(Debug, Clone)]
57pub enum NixTraceFrame {
58 Eager {
62 file: Option<String>,
63 description: String,
64 },
65 Lambda {
75 closure_env: Env,
76 current_file: Option<PathBuf>,
77 },
78}
79
80fn strip_source_prefix(p: &std::path::Path) -> String {
83 let s = p.display().to_string();
84 s.rsplit_once("-source/")
85 .map_or_else(|| p.display().to_string(), |(_, tail)| tail.to_string())
86}
87
88impl NixTraceFrame {
89 fn file(&self) -> Option<String> {
92 match self {
93 NixTraceFrame::Eager { file, .. } => file.clone(),
94 NixTraceFrame::Lambda { current_file, .. } => {
95 current_file.as_deref().map(strip_source_prefix)
96 }
97 }
98 }
99
100 fn description(&self) -> String {
105 self.to_string()
106 }
107}
108
109impl std::fmt::Display for NixTraceFrame {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 match self {
115 NixTraceFrame::Eager { description, .. } => f.write_str(description),
116 NixTraceFrame::Lambda { closure_env, .. } => {
117 let file = closure_env.eval_file().map(|p| strip_source_prefix(p));
118 write!(
119 f,
120 "while calling function defined in {}",
121 file.as_deref().unwrap_or("<eval>")
122 )
123 }
124 }
125 }
126}
127
128fn push_nix_trace(desc: impl Into<String>) -> NixTraceGuard {
130 let frame = NixTraceFrame::Eager {
131 file: current_eval_file().map(|p| {
132 p.display().to_string()
133 .rsplit_once("-source/")
134 .map_or_else(|| p.display().to_string(), |(_, s)| s.to_string())
135 }),
136 description: desc.into(),
137 };
138 NIX_TRACE_STACK.with(|s| s.borrow_mut().push(frame));
139 NixTraceGuard
140}
141
142fn push_nix_trace_lambda(closure_env: &Env) -> NixTraceGuard {
148 let frame = NixTraceFrame::Lambda {
149 closure_env: closure_env.clone(),
150 current_file: current_eval_file(),
151 };
152 NIX_TRACE_STACK.with(|s| s.borrow_mut().push(frame));
153 NixTraceGuard
154}
155
156struct NixTraceGuard;
157impl Drop for NixTraceGuard {
158 fn drop(&mut self) {
159 NIX_TRACE_STACK.with(|s| s.borrow_mut().pop());
160 }
161}
162
163pub fn attach_trace(err: EvalError) -> EvalError {
165 NIX_TRACE_STACK.with(|s| {
166 let stack = s.borrow();
167 if stack.is_empty() {
168 return err;
169 }
170 let max_frames = std::env::var("SUI_M26_MAXFRAMES").ok()
171 .and_then(|s| s.parse::<usize>().ok()).unwrap_or(15);
172 let mut trace = format!("{err}");
173 for (i, frame) in stack.iter().rev().take(max_frames).enumerate() {
174 let file = frame.file();
175 let loc = file.as_deref().unwrap_or("<eval>");
176 trace.push_str(&format!("\n {} ({loc})", frame.description()));
177 if i + 1 >= max_frames && stack.len() > max_frames {
178 trace.push_str(&format!("\n ... ({} more frames)", stack.len() - max_frames));
179 }
180 }
181 match err {
184 EvalError::Throw(_) => EvalError::Throw(trace),
185 EvalError::AssertionFailed(_) => EvalError::AssertionFailed(trace),
186 _ => EvalError::TypeError(trace),
187 }
188 })
189}
190
191#[must_use]
194pub fn current_eval_dir() -> Option<PathBuf> {
195 EVAL_FILE_STACK.with(|s| s.borrow().last().and_then(|p| p.parent().map(PathBuf::from)))
196}
197
198pub fn push_eval_file(file: PathBuf) -> EvalFileGuard {
202 EVAL_FILE_STACK.with(|s| s.borrow_mut().push(file));
203 EvalFileGuard
204}
205
206#[must_use]
209pub fn current_eval_file() -> Option<PathBuf> {
210 EVAL_FILE_STACK.with(|s| s.borrow().last().cloned())
211}
212
213
214pub fn eval_file_stack_snapshot() -> Vec<String> {
216 EVAL_FILE_STACK.with(|s| {
217 s.borrow().iter().map(|p| {
218 let s = p.display().to_string();
219 s.rsplit_once("-source/").map_or(s.clone(), |(_, r)| r.to_string())
220 }).collect()
221 })
222}
223
224pub(crate) fn eval_file_ctx() -> String {
227 current_eval_file()
228 .map(|p| format!(", in '{}'", p.display()))
229 .unwrap_or_default()
230}
231
232pub struct EvalFileGuard;
234
235impl Drop for EvalFileGuard {
236 fn drop(&mut self) {
237 EVAL_FILE_STACK.with(|s| {
238 s.borrow_mut().pop();
239 });
240 }
241}
242
243pub fn push_source_id(id: u32) -> SourceIdGuard {
249 let prev = CURRENT_SOURCE_ID.with(|s| {
250 let old = s.get();
251 s.set(id);
252 old
253 });
254 SourceIdGuard(prev)
255}
256
257pub struct SourceIdGuard(u32);
259
260impl Drop for SourceIdGuard {
261 fn drop(&mut self) {
262 CURRENT_SOURCE_ID.with(|s| s.set(self.0));
263 }
264}
265
266pub fn normalize_path(path: &std::path::Path) -> std::path::PathBuf {
279 crate::path::normalize(path)
280}
281
282thread_local! {
290 static PURE_MODE: Cell<bool> = const { Cell::new(false) };
291}
292
293pub fn set_pure_mode(pure: bool) {
295 PURE_MODE.with(|p| p.set(pure));
296}
297
298#[must_use]
300pub fn is_pure_mode() -> bool {
301 PURE_MODE.with(Cell::get)
302}
303
304#[cfg(test)]
320const MAX_EVAL_DEPTH: usize = 2_048;
321#[cfg(not(test))]
322const MAX_EVAL_DEPTH: usize = usize::MAX;
323
324struct DepthGuard;
330
331const PROMOTION_RUNAWAY_EVAL_DEPTH: usize = 500;
348
349impl DepthGuard {
350 #[inline(always)]
351 fn enter() -> Result<Self, EvalError> {
352 EVAL_DEPTH.with(|d| {
353 let depth = d.get();
354 if MAX_EVAL_DEPTH != usize::MAX && depth > MAX_EVAL_DEPTH {
355 return Err(EvalError::InfiniteRecursion(
356 "eval depth exceeded".into(),
357 ));
358 }
359 if depth > PROMOTION_RUNAWAY_EVAL_DEPTH
360 && crate::value::promotion_occurred()
361 {
362 return Err(EvalError::InfiniteRecursion(
363 "overlay-fixpoint promotion runaway (eval depth exceeded)".into(),
364 ));
365 }
366 d.set(depth + 1);
367 Ok(DepthGuard)
368 })
369 }
370}
371
372impl Drop for DepthGuard {
373 #[inline(always)]
374 fn drop(&mut self) {
375 EVAL_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
376 }
377}
378
379fn collect_referenced_names(expr: &ast::Expr) -> HashSet<String> {
396 let mut names = HashSet::new();
397 for node in expr.syntax().descendants() {
398 if let Some(ident) = ast::Ident::cast(node) {
399 names.insert(ident_text(&ident));
400 }
401 }
402 names
403}
404
405fn compute_needed_bindings(
419 body: &ast::Expr,
420 binding_info: &[(String, Option<ast::Expr>)], ) -> HashSet<String> {
422 let body_refs = collect_referenced_names(body);
424
425 let mut all_names: HashSet<String> = HashSet::with_capacity(binding_info.len());
427 let mut deps: HashMap<String, HashSet<String>> = HashMap::with_capacity(binding_info.len());
428
429 for (name, value_expr) in binding_info {
430 all_names.insert(name.clone());
431 if let Some(expr) = value_expr {
432 deps.insert(name.clone(), collect_referenced_names(expr));
433 }
434 }
435
436 let mut needed: HashSet<String> = body_refs.intersection(&all_names).cloned().collect();
438 let mut queue: VecDeque<String> = needed.iter().cloned().collect();
439
440 while let Some(name) = queue.pop_front() {
441 if let Some(name_deps) = deps.get(&name) {
442 for dep in name_deps {
443 if all_names.contains(dep) && needed.insert(dep.clone()) {
444 queue.push_back(dep.clone());
445 }
446 }
447 }
448 }
449
450 needed
451}
452
453#[must_use = "evaluation result should be used"]
455pub fn eval(input: &str) -> Result<Value, EvalError> {
456 eval_with_file(input, None)
457}
458
459thread_local! {
461 static EVAL_NESTING: Cell<usize> = const { Cell::new(0) };
462}
463
464pub fn eval_with_file(input: &str, file: Option<std::path::PathBuf>) -> Result<Value, EvalError> {
471 let nesting = EVAL_NESTING.with(|n| {
472 let v = n.get();
473 n.set(v + 1);
474 v
475 });
476 if nesting == 0 {
477 crate::perf::init();
478 crate::perf::start();
479 crate::trace::init_trace();
480 clear_ident_cache();
483 crate::resolve_env::clear();
487 }
506 let parse = rnix::Root::parse(input);
507 if !parse.errors().is_empty() {
508 let msgs: Vec<String> = parse.errors().iter().map(|e| e.to_string()).collect();
509 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
510 return Err(EvalError::ParseError(msgs.join("; ")));
511 }
512
513 let src_id = next_source_id();
517 if crate::resolve_env::enabled() {
524 let table = sui_resolve::resolve(&parse.tree());
525 crate::resolve_env::populate(src_id, &table);
526 }
527 crate::pos::register_source(file.as_deref(), input);
533 let prev_src_id = CURRENT_SOURCE_ID.with(|s| {
534 let old = s.get();
535 s.set(src_id);
536 old
537 });
538
539 let root = parse.tree();
540 let expr = match root.expr() {
541 Some(e) => e,
542 None => {
543 CURRENT_SOURCE_ID.with(|s| s.set(prev_src_id));
544 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
545 return Err(EvalError::ParseError("empty expression".to_string()));
546 }
547 };
548 let mut env = Env::new();
549 env.set_eval_file(file);
550 env.set_source_id(src_id);
555 builtins::register(&mut env);
556 let result = eval_expr(&expr, &env).map_err(|e| attach_trace(e))?;
557 let final_result = force_value(&result).map_err(|e| attach_trace(e));
559 CURRENT_SOURCE_ID.with(|s| s.set(prev_src_id));
561 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
562 if nesting == 0 {
563 crate::perf::report();
564 }
565 final_result
566}
567
568#[inline(always)]
576pub fn force_concrete(value: &Value) -> Result<Concrete, EvalError> {
581 value.demand()
582}
583
584pub fn force_value(value: &Value) -> Result<Value, EvalError> {
588 crate::perf::inc(crate::perf::Counter::ForceValue);
589 if !matches!(value, Value::Thunk(_)) {
592 return Ok(value.clone());
593 }
594 let mut v = value.clone();
609 let mut depth = 0u32;
610 loop {
611 match v {
612 Value::Thunk(ref thunk) => {
613 v = force_thunk(thunk)?;
614 depth += 1;
615 if depth > 100 {
616 return Err(EvalError::InfiniteRecursion(
617 "force_value: thunk chain exceeded depth 100 (cycle or runaway lazy wrap)".into(),
618 ));
619 }
620 }
621 _ => return Ok(v),
622 }
623 }
624}
625
626pub fn force_value_tracked(value: &Value, site: &str) -> Result<Value, EvalError> {
628 crate::perf::inc(crate::perf::Counter::ForceValue);
629 if let Value::Thunk(thunk) = value {
630 FORCE_SITES.with(|sites| {
631 *sites.borrow_mut().entry(site.to_string()).or_insert(0) += 1;
632 });
633 force_thunk(thunk)
634 } else {
635 Ok(value.clone())
636 }
637}
638
639thread_local! {
640 static FORCE_SITES: std::cell::RefCell<std::collections::HashMap<String, u64>> =
641 std::cell::RefCell::new(std::collections::HashMap::new());
642 static APPLY_SITES: std::cell::RefCell<std::collections::HashMap<String, u64>> =
643 std::cell::RefCell::new(std::collections::HashMap::new());
644}
645
646pub fn dump_force_sites() {
648 FORCE_SITES.with(|sites| {
649 let sites = sites.borrow();
650 let mut sorted: Vec<_> = sites.iter().collect();
651 sorted.sort_by(|a, b| b.1.cmp(a.1));
652 eprintln!("[force-sites] top thunk force call sites:");
653 for (site, count) in sorted.iter().take(10) {
654 eprintln!(" {count:>8} {site}");
655 }
656 });
657 APPLY_SITES.with(|sites| {
658 let sites = sites.borrow();
659 let mut sorted: Vec<_> = sites.iter().collect();
660 sorted.sort_by(|a, b| b.1.cmp(a.1));
661 eprintln!("[apply-sites] top lambda call sites by source file:");
662 for (site, count) in sorted.iter().take(15) {
663 let short = site.rsplit_once("-source/").map_or(site.as_str(), |(_,s)| s);
665 eprintln!(" {count:>8} {short}");
666 }
667 });
668}
669
670fn force_thunk(thunk: &Thunk) -> Result<Value, EvalError> {
674 if let Some(cached) = thunk.peek() {
676 crate::perf::inc(crate::perf::Counter::ThunkHit);
677 return Ok(cached.clone().into_value());
678 }
679 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || {
680 thunk.force(&|expr, env| eval_expr(expr, env))
686 })
687}
688
689fn referenced_idents(value_expr: &ast::Expr) -> HashSet<SmolStr> {
756 use rnix::SyntaxKind;
757 let perf_on = crate::perf::enabled();
763 let t0 = if perf_on {
764 Some(std::time::Instant::now())
765 } else {
766 None
767 };
768 crate::perf::inc(crate::perf::Counter::SelfRecWalkCalls);
769 let mut nodes_walked: u64 = 0;
770 let mut set: HashSet<SmolStr> = HashSet::new();
771 for node in value_expr.syntax().descendants() {
772 nodes_walked += 1;
773 if node.kind() == SyntaxKind::NODE_IDENT
774 && node
775 .parent()
776 .is_none_or(|p| p.kind() != SyntaxKind::NODE_ATTRPATH)
777 && let Some(i) = ast::Ident::cast(node)
778 {
779 set.insert(SmolStr::from(ident_text(&i).as_str()));
780 }
781 }
782 crate::perf::add(crate::perf::Counter::SelfRecWalkNodes, nodes_walked);
783 if let Some(t0) = t0 {
784 crate::trace::add_self_rec_walk_nanos(t0.elapsed().as_nanos());
785 }
786 set
787}
788
789fn is_self_recursive_binding(value_expr: &ast::Expr, name: &str) -> bool {
793 referenced_idents(value_expr).contains(name)
794}
795
796fn maybe_thunk(
797 expr: &ast::Expr,
798 env: &Env,
799 is_rec: bool,
800 defined_so_far: Option<&HashSet<String>>,
801) -> Value {
802 match expr {
803 ast::Expr::Literal(lit) => eval_literal(lit).unwrap_or_else(|_| {
805 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
806 }),
807 ast::Expr::Ident(ident) if !is_rec => {
814 let sym = {
824 let src_id = env.source_id();
825 let offset = u32::from(ident.syntax().text_range().start());
826 crate::value::intern_cached_with(src_id, offset, || {
827 crate::value::intern(&ident_text(ident))
828 })
829 };
830 if let Some(kw) = crate::value::with_resolved(sym, |s| match s {
832 "true" => Some(Value::Bool(true)),
833 "false" => Some(Value::Bool(false)),
834 "null" => Some(Value::Null),
835 _ => None,
836 }) {
837 return kw;
838 }
839 {
840 {
841 if let Some(v) = env.lookup_fast(sym, "") {
845 return v;
846 }
847 if let Some((scope_cache, scope_value)) = env.innermost_with_scope() {
850 return Value::Thunk(Thunk::new_with_ident(
851 SmolStr::from(ident_text(ident).as_str()),
852 scope_cache,
853 scope_value,
854 env.clone(),
855 ));
856 }
857 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
858 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
859 }
860 }
861 }
862 ast::Expr::Ident(ident) if is_rec => {
866 let name = ident_text(ident);
867 match name.as_str() {
868 "true" => Value::Bool(true),
869 "false" => Value::Bool(false),
870 "null" => Value::Null,
871 _ => {
872 if defined_so_far.map_or(false, |d| d.contains(&name)) {
875 env.lookup(&name).unwrap_or_else(|| {
876 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
877 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
878 })
879 } else {
880 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
882 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
883 }
884 }
885 }
886 }
887 ast::Expr::PathAbs(p) if !parts_have_interpolation(&p.parts()) => {
892 let text = crate::path::canon_abs(&p.syntax().text().to_string());
898 Value::Path(Box::new(SmolStr::from(text.as_str())))
899 }
900 ast::Expr::PathHome(p) if !parts_have_interpolation(&p.parts()) => {
901 let text = p.syntax().text().to_string();
902 Value::Path(Box::new(SmolStr::from(text.as_str())))
903 }
904 ast::Expr::Str(st) if !str_has_interpolation(st) => {
917 eval_str(st, env).unwrap_or_else(|_| {
918 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
919 })
920 }
921 ast::Expr::Lambda(lam) if !is_rec => {
925 if let (Some(param), Some(body)) = (lam.param(), lam.body()) {
926 Value::Lambda(Rc::new(Closure {
927 param,
928 body,
929 env: env.clone(),
930 }))
931 } else {
932 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
933 }
934 }
935 _ => {
946 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeOther);
947 if crate::perf::enabled() {
948 let kind = match expr {
949 ast::Expr::Select(_) => "Select",
950 ast::Expr::Apply(_) => "Apply",
951 ast::Expr::BinOp(_) => "BinOp",
952 ast::Expr::IfElse(_) => "IfElse",
953 ast::Expr::Str(_) => "Str",
954 ast::Expr::List(_) => "List",
955 ast::Expr::With(_) => "With",
956 ast::Expr::Assert(_) => "Assert",
957 ast::Expr::HasAttr(_) => "HasAttr",
958 ast::Expr::UnaryOp(_) => "UnaryOp",
959 ast::Expr::Paren(_) => "Paren",
960 ast::Expr::LetIn(_) => "LetIn",
961 ast::Expr::AttrSet(_) => "AttrSet",
962 ast::Expr::Ident(_) => "Ident(rec)",
963 ast::Expr::Lambda(_) => "Lambda(rec)",
964 ast::Expr::LegacyLet(_) => "LegacyLet",
965 ast::Expr::PathAbs(_)
966 | ast::Expr::PathHome(_)
967 | ast::Expr::PathRel(_)
968 | ast::Expr::PathSearch(_) => "Path(interp)",
969 _ => "Other",
970 };
971 crate::trace::inc_maybe_other_kind(kind);
972 }
973 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
974 }
975 }
976}
977
978#[inline(always)]
989pub fn eval_expr(expr: &ast::Expr, env: &Env) -> Result<Value, EvalError> {
990 match expr {
993 ast::Expr::Ident(ident) => {
994 crate::perf::inc(crate::perf::Counter::EvalExpr);
995 if crate::perf::enabled() {
996 crate::perf::inc(crate::perf::Counter::ExprIdent);
997 }
998 if crate::resolve_env::enabled() {
1011 let src_id = CURRENT_SOURCE_ID.with(std::cell::Cell::get);
1012 let offset = u32::from(ident.syntax().text_range().start());
1013 if let sui_resolve::Resolution::Lexical { sym } =
1014 crate::resolve_env::resolution_for(src_id, offset)
1015 {
1016 if let Some(v) = env.lookup_lexical_sym(sym) {
1017 return Ok(v);
1018 }
1019 }
1020 }
1022 let sym = {
1056 let src_id = env.source_id();
1057 let offset = u32::from(ident.syntax().text_range().start());
1058 crate::value::intern_cached_with(src_id, offset, || {
1059 crate::value::intern(&ident_text(ident))
1060 })
1061 };
1062 if let Some(kw) = crate::value::with_resolved(sym, |s| match s {
1066 "true" => Some(Value::Bool(true)),
1067 "false" => Some(Value::Bool(false)),
1068 "null" => Some(Value::Null),
1069 _ => None,
1070 }) {
1071 return Ok(kw);
1072 }
1073 return {
1074 {
1075 if let Some(v) = env.lookup_fast(sym, "") {
1079 Ok(v)
1080 } else {
1081 let name = ident_text(ident);
1082 let fresh = crate::value::intern(name.as_str());
1101 if fresh != sym {
1102 if let Some(v) = env.lookup_fast(fresh, name.as_str()) {
1103 return Ok(v);
1104 }
1105 }
1106 if env.with_scope_count() > 0 {
1107 if let Some((scope_cache, scope_value)) = env.innermost_with_scope() {
1111 Ok(Value::Thunk(Thunk::new_with_ident(
1112 SmolStr::from(name.as_str()),
1113 scope_cache,
1114 scope_value,
1115 env.clone(),
1116 )))
1117 } else if crate::value::in_promise_eval() {
1118 Ok(Value::Null)
1128 } else {
1129 Err(EvalError::UndefinedVar(
1130 format!("'{name}'{}", eval_file_ctx()),
1131 ))
1132 }
1133 } else {
1134 if let Ok(dbg_var) = std::env::var("SUI_DEBUG_VAR") {
1135 if dbg_var == name || dbg_var == "*" {
1136 eprintln!(
1137 "[sui-debug] UndefinedVar '{name}' in {}\n\
1138 [sui-debug] env bindings ({} total): {:?}\n\
1139 [sui-debug] with_scopes: {}",
1140 eval_file_ctx(),
1141 env.binding_count(),
1142 env.binding_names_preview(20),
1143 env.with_scope_count(),
1144 );
1145 }
1146 }
1147 if crate::value::in_promise_eval() {
1148 return Ok(Value::Null);
1151 }
1152 Err(EvalError::UndefinedVar(
1153 format!("'{name}'{}", eval_file_ctx()),
1154 ))
1155 }
1156 }
1157 }
1158 };
1159 }
1160 ast::Expr::Literal(lit) => {
1161 crate::perf::inc(crate::perf::Counter::EvalExpr);
1162 if crate::perf::enabled() {
1163 crate::perf::inc(crate::perf::Counter::ExprLiteral);
1164 }
1165 return eval_literal(lit);
1166 }
1167 ast::Expr::Paren(p) => {
1168 if let Some(inner) = p.expr() {
1169 return eval_expr(&inner, env);
1170 }
1171 }
1172 ast::Expr::Root(r) => {
1173 if let Some(inner) = r.expr() {
1174 return eval_expr(&inner, env);
1175 }
1176 }
1177 ast::Expr::Lambda(lam) => {
1179 crate::perf::inc(crate::perf::Counter::EvalExpr);
1180 if crate::perf::enabled() {
1181 crate::perf::inc(crate::perf::Counter::ExprLambda);
1182 }
1183 if let (Some(param), Some(body)) = (lam.param(), lam.body()) {
1184 return Ok(Value::Lambda(Rc::new(Closure {
1185 param,
1186 body,
1187 env: env.clone(),
1188 })));
1189 }
1190 }
1191 _ => {}
1192 }
1193 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || {
1195 eval_expr_inner(expr, env)
1196 })
1197}
1198
1199fn eval_expr_inner(expr: &ast::Expr, env: &Env) -> Result<Value, EvalError> {
1207 let mut cur_expr = expr.clone();
1210 let mut cur_env = env.clone();
1211
1212 loop {
1213 crate::perf::inc(crate::perf::Counter::EvalExpr);
1214 if crate::perf::enabled() {
1216 use crate::perf::Counter;
1217 let c = match &cur_expr {
1218 ast::Expr::Ident(_) => Counter::ExprIdent,
1219 ast::Expr::Literal(_) => Counter::ExprLiteral,
1220 ast::Expr::Str(_) => Counter::ExprStr,
1221 ast::Expr::List(_) => Counter::ExprList,
1222 ast::Expr::AttrSet(_) => Counter::ExprAttrs,
1223 ast::Expr::Select(_) => Counter::ExprSelect,
1224 ast::Expr::Apply(_) => Counter::ExprApply,
1225 ast::Expr::LetIn(_) => Counter::ExprLetIn,
1226 ast::Expr::IfElse(_) => Counter::ExprIfElse,
1227 ast::Expr::With(_) => Counter::ExprWith,
1228 ast::Expr::Lambda(_) => Counter::ExprLambda,
1229 ast::Expr::BinOp(_) => Counter::ExprBinOp,
1230 ast::Expr::HasAttr(_) => Counter::ExprHasAttr,
1231 ast::Expr::UnaryOp(_) => Counter::ExprUnaryOp,
1232 ast::Expr::Assert(_) => Counter::ExprAssert,
1233 ast::Expr::PathAbs(_) | ast::Expr::PathRel(_)
1234 | ast::Expr::PathHome(_) | ast::Expr::PathSearch(_) => Counter::ExprPath,
1235 _ => Counter::ExprOther,
1236 };
1237 crate::perf::inc(c);
1238 }
1239 let _guard = DepthGuard::enter()?;
1240 let env = &cur_env;
1241 match &cur_expr {
1242 ast::Expr::Literal(lit) => return eval_literal(lit),
1243
1244 ast::Expr::Str(s) => return eval_str(s, env),
1245
1246 ast::Expr::PathAbs(p) => {
1247 let parts = p.parts();
1250 if parts_have_interpolation(&parts) {
1251 return eval_interpol_path_parts(&parts, PathKind::Abs, env);
1252 }
1253 let text = crate::path::canon_abs(&p.syntax().text().to_string());
1256 return Ok(Value::Path(Box::new(SmolStr::from(text.as_str()))));
1257 }
1258 ast::Expr::PathRel(p) => {
1259 let parts = p.parts();
1270 if parts_have_interpolation(&parts) {
1271 return eval_interpol_path_parts(&parts, PathKind::Rel, env);
1272 }
1273 let text = p.syntax().text().to_string();
1274 let resolved = if let Some(dir) = current_eval_dir() {
1275 let joined = dir.join(&text);
1276 let norm = normalize_path(&joined);
1280 crate::path::dematerialize(&norm)
1290 .to_string_lossy()
1291 .into_owned()
1292 } else {
1293 text.clone()
1294 };
1295 return Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))));
1296 }
1297 ast::Expr::PathHome(p) => {
1298 let parts = p.parts();
1299 if parts_have_interpolation(&parts) {
1300 return eval_interpol_path_parts(&parts, PathKind::Home, env);
1301 }
1302 let text = p.syntax().text().to_string();
1303 return Ok(Value::Path(Box::new(SmolStr::from(text.as_str()))));
1304 }
1305 ast::Expr::PathSearch(p) => {
1306 let text = p.syntax().text().to_string();
1311 let inner = text
1312 .strip_prefix('<')
1313 .and_then(|s| s.strip_suffix('>'))
1314 .unwrap_or(&text);
1315 if let Some(resolved) = crate::builtins::resolve_search_path(inner) {
1316 return Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))));
1317 }
1318 return Err(EvalError::Throw(
1322 format!("search path '{text}' not in NIX_PATH"),
1323 ));
1324 }
1325
1326 ast::Expr::Ident(ident) => {
1327 let name = ident_text(ident);
1328 return match name.as_str() {
1329 "true" => Ok(Value::Bool(true)),
1330 "false" => Ok(Value::Bool(false)),
1331 "null" => Ok(Value::Null),
1332 _ => {
1333 env.lookup(&name)
1334 .ok_or_else(|| EvalError::UndefinedVar(
1335 format!("'{name}'{}", eval_file_ctx()),
1336 ))
1337 }
1338 };
1339 }
1340
1341 ast::Expr::List(list) => {
1342 let values: Vec<Value> = list.items()
1347 .map(|e| maybe_thunk(&e, env, false, None))
1348 .collect();
1349 return Ok(Value::list(values));
1350 }
1351
1352 ast::Expr::AttrSet(set) => return eval_attrset(set, env),
1353
1354 ast::Expr::Select(sel) => return eval_select(sel, env),
1355
1356 ast::Expr::HasAttr(ha) => return eval_has_attr(ha, env),
1357
1358 ast::Expr::UnaryOp(op) => return eval_unary_op(op, env),
1359
1360 ast::Expr::BinOp(binop) => {
1361 let lhs_expr = binop
1362 .lhs()
1363 .ok_or_else(|| EvalError::ParseError("binop missing lhs".to_string()))?;
1364 let rhs_expr = binop
1365 .rhs()
1366 .ok_or_else(|| EvalError::ParseError("binop missing rhs".to_string()))?;
1367 let kind = binop
1368 .operator()
1369 .ok_or_else(|| EvalError::ParseError("binop missing operator".to_string()))?;
1370 return eval_binop(kind, &lhs_expr, &rhs_expr, env);
1371 }
1372
1373 ast::Expr::Apply(app) => return eval_apply(app, env),
1374
1375 ast::Expr::IfElse(ie) => {
1376 let cond = ie
1377 .condition()
1378 .ok_or_else(|| EvalError::ParseError("if missing condition".to_string()))?;
1379 let body = ie
1380 .body()
1381 .ok_or_else(|| EvalError::ParseError("if missing then body".to_string()))?;
1382 let else_body = ie
1383 .else_body()
1384 .ok_or_else(|| EvalError::ParseError("if missing else body".to_string()))?;
1385 if force_concrete(&eval_expr(&cond, env)?)?.as_bool()? {
1386 cur_expr = body;
1387 } else {
1388 cur_expr = else_body;
1389 }
1390 continue;
1392 }
1393
1394 ast::Expr::Assert(assert) => {
1395 let cond = assert
1396 .condition()
1397 .ok_or_else(|| EvalError::ParseError("assert missing condition".to_string()))?;
1398 let body = assert
1399 .body()
1400 .ok_or_else(|| EvalError::ParseError("assert missing body".to_string()))?;
1401 if !force_concrete(&eval_expr(&cond, env)?)?.as_bool()? {
1402 return Err(EvalError::AssertionFailed(eval_file_ctx()));
1403 }
1404 cur_expr = body;
1405 continue;
1406 }
1407
1408 ast::Expr::With(with) => {
1409 let ns = with
1410 .namespace()
1411 .ok_or_else(|| EvalError::ParseError("with missing namespace".to_string()))?;
1412 let body = with
1413 .body()
1414 .ok_or_else(|| EvalError::ParseError("with missing body".to_string()))?;
1415 let scope_val = maybe_thunk(&ns, env, false, None);
1441 let new_env = env.child().with_scope(scope_val);
1442 cur_expr = body;
1443 cur_env = new_env;
1444 continue;
1445 }
1446
1447 ast::Expr::LetIn(letin) => {
1448 let mut new_env = env.child();
1449
1450 let mut thunks: Vec<(String, Thunk)> = Vec::new();
1453
1454 let mut defined_so_far: HashSet<String> = HashSet::new();
1458
1459 let mut dotted_attrs: NixAttrs = NixAttrs::new();
1463
1464 let let_scope_names: HashSet<String> = {
1470 let mut s = HashSet::new();
1471 for entry in letin.entries() {
1472 match entry {
1473 ast::Entry::AttrpathValue(apv) => {
1474 if let Some(attrpath) = apv.attrpath() {
1475 if let Some(first) = attrpath.attrs().next() {
1476 if let Ok(name) = eval_attr(&first, env) {
1477 s.insert(name);
1478 }
1479 }
1480 }
1481 }
1482 ast::Entry::Inherit(inherit) => {
1483 for attr in inherit.attrs() {
1484 if let Ok(name) = eval_attr(&attr, env) {
1485 s.insert(name);
1486 }
1487 }
1488 }
1489 }
1490 }
1491 s
1492 };
1493
1494 for entry in letin.entries() {
1495 match entry {
1496 ast::Entry::AttrpathValue(ref apv) => {
1497 let attrpath = apv.attrpath().ok_or_else(|| {
1498 EvalError::ParseError("binding missing attrpath".to_string())
1499 })?;
1500 let value_expr = apv.value().ok_or_else(|| {
1501 EvalError::ParseError("binding missing value".to_string())
1502 })?;
1503 let mut path_keys: Vec<String> = attrpath
1504 .attrs()
1505 .map(|a| eval_attr(&a, env))
1506 .collect::<Result<_, _>>()?;
1507 if path_keys.len() == 1 {
1508 let key = path_keys.pop().unwrap();
1509 let referenced = referenced_idents(&value_expr);
1532 let in_mutual_cycle = std::iter::once(&key)
1533 .chain(let_scope_names.iter())
1534 .any(|n| referenced.contains(n.as_str()));
1535 let value = if in_mutual_cycle {
1536 Value::Thunk(Thunk::new_suspended_recursive(
1537 value_expr.clone(),
1538 env.clone(),
1539 ))
1540 } else {
1541 maybe_thunk(&value_expr, env, true, Some(&defined_so_far))
1542 };
1543 new_env.bind(key.clone(), value.clone());
1544 if let Value::Thunk(t) = &value {
1545 thunks.push((key.clone(), t.clone()));
1546 }
1547 defined_so_far.insert(key);
1548 } else if path_keys.len() > 1 {
1549 let key = path_keys[0].clone();
1554 let value = build_nested_attr_thunk(
1555 &path_keys[1..],
1556 &value_expr,
1557 env,
1558 &mut thunks,
1559 );
1560 merge_nested_insert(&mut dotted_attrs, key, value);
1561 }
1562 }
1563 ast::Entry::Inherit(ref inherit) => {
1564 if let Some(from) = inherit.from() {
1565 let source_expr = from.expr().ok_or_else(|| {
1566 EvalError::ParseError(
1567 "inherit from missing expr".to_string(),
1568 )
1569 })?;
1570 let source_thunk = Thunk::new_suspended(
1575 source_expr, env.clone(),
1576 );
1577 for attr in inherit.attrs() {
1578 let name = eval_attr(&attr, env)?;
1579 let thunk = Thunk::new_inherit_select(
1580 source_thunk.clone(),
1581 name.clone(),
1582 );
1583 new_env.bind(name.clone(), Value::Thunk(thunk.clone()));
1584 thunks.push((name, thunk));
1585 }
1586 } else {
1587 for attr in inherit.attrs() {
1592 let name = eval_attr(&attr, env)?;
1593 let value = env.lookup(&name).ok_or_else(|| {
1594 EvalError::UndefinedVar(
1595 format!("'{name}'{}", eval_file_ctx()),
1596 )
1597 })?;
1598 new_env.bind(name, value);
1599 }
1600 }
1601 }
1602 }
1603 }
1604
1605 for (key, value) in dotted_attrs.iter() {
1610 new_env.bind(key.clone(), value.clone());
1611 }
1612
1613 for (_key, thunk) in &thunks {
1616 thunk.update_env(&new_env);
1617 }
1618
1619 let body = letin
1620 .body()
1621 .ok_or_else(|| EvalError::ParseError("let missing body".to_string()))?;
1622 cur_expr = body;
1623 cur_env = new_env;
1624 continue;
1625 }
1626
1627 ast::Expr::Lambda(lam) => {
1628 let param = lam
1629 .param()
1630 .ok_or_else(|| EvalError::ParseError("lambda missing param".to_string()))?;
1631 let body = lam
1632 .body()
1633 .ok_or_else(|| EvalError::ParseError("lambda missing body".to_string()))?;
1634 return Ok(Value::Lambda(Rc::new(Closure {
1635 param,
1636 body,
1637 env: env.clone(),
1638 })));
1639 }
1640
1641 ast::Expr::Paren(p) => {
1642 let inner = p
1643 .expr()
1644 .ok_or_else(|| EvalError::ParseError("paren missing expr".to_string()))?;
1645 cur_expr = inner;
1646 continue;
1647 }
1648
1649 ast::Expr::Root(r) => {
1650 let inner = r
1651 .expr()
1652 .ok_or_else(|| EvalError::ParseError("root missing expr".to_string()))?;
1653 cur_expr = inner;
1654 continue;
1655 }
1656
1657 ast::Expr::LegacyLet(ll) => {
1658 let mut new_env = env.child();
1659 eval_entries(ll, &mut new_env)?;
1660 return new_env
1662 .lookup("body")
1663 .ok_or_else(|| EvalError::AttrNotFound(
1664 format!("'body' in legacy let{}", eval_file_ctx()),
1665 ));
1666 }
1667
1668 ast::Expr::CurPos(_) => return Err(EvalError::NotImplemented("__curPos".to_string())),
1669 ast::Expr::Error(_) => return Err(EvalError::ParseError("parse error node".to_string())),
1670 } } }
1673
1674fn eval_literal(lit: &ast::Literal) -> Result<Value, EvalError> {
1675 use ast::LiteralKind;
1676 match lit.kind() {
1677 LiteralKind::Integer(tok) => {
1678 let n = tok
1679 .value()
1680 .map_err(|e| EvalError::ParseError(format!("invalid integer: {e}")))?;
1681 Ok(Value::Int(n))
1682 }
1683 LiteralKind::Float(tok) => {
1684 let f = tok
1685 .value()
1686 .map_err(|e| EvalError::ParseError(format!("invalid float: {e}")))?;
1687 Ok(Value::Float(f))
1688 }
1689 LiteralKind::Uri(tok) => Ok(Value::string(tok.syntax().text().to_string())),
1690 }
1691}
1692
1693enum TraverseResult {
1695 Found(Value),
1697 Missing(String),
1699 NotAttrs(Value),
1701}
1702
1703fn traverse_attrpath(
1708 base: Value,
1709 attrpath: &rnix::ast::Attrpath,
1710 env: &Env,
1711) -> Result<TraverseResult, EvalError> {
1712 let attrs: Vec<_> = attrpath.attrs().collect();
1713 let mut value = base;
1714 for (i, attr) in attrs.iter().enumerate() {
1715 let key = eval_attr(attr, env)?;
1716 let forced = force_value(&value)?;
1718 match forced {
1719 Value::Attrs(ref a) => match a.get(&key) {
1720 Some(v) => {
1721 if i < attrs.len() - 1 {
1722 value = force_value(v)?;
1724 } else {
1725 value = v.clone();
1728 }
1729 }
1730 None => return Ok(TraverseResult::Missing(key)),
1731 },
1732 _ => return Ok(TraverseResult::NotAttrs(forced)),
1733 }
1734 }
1735 Ok(TraverseResult::Found(value))
1736}
1737
1738fn eval_select(sel: &ast::Select, env: &Env) -> Result<Value, EvalError> {
1739 crate::perf::inc(crate::perf::Counter::Select);
1740 let base_expr = sel.expr().ok_or_else(|| {
1741 EvalError::ParseError("select missing expression".to_string())
1742 })?;
1743 let base_result = eval_expr(&base_expr, env)
1752 .and_then(|v| force_concrete(&v).map(Concrete::into_value));
1753 let base = match base_result {
1754 Ok(v) => v,
1755 Err(EvalError::InfiniteRecursion(_)) if sel.default_expr().is_some() => {
1756 return eval_expr(&sel.default_expr().expect("checked"), env);
1757 }
1758 Err(e) => return Err(e),
1759 };
1760 let base_type = base.type_name();
1761 let attrpath = sel.attrpath().ok_or_else(|| {
1762 EvalError::ParseError("select missing attrpath".to_string())
1763 })?;
1764 let bridge_active = std::env::var_os("SUI_BLACKHOLE_AS_EMPTY_ATTRS").is_some()
1786 || std::env::var_os("SUI_BLACKHOLE_AS_NULL").is_some();
1787 let traversal = traverse_attrpath(base, &attrpath, env);
1788 match traversal {
1789 Ok(TraverseResult::Found(v)) => Ok(v),
1790 Ok(TraverseResult::Missing(key)) => {
1791 if let Some(def) = sel.default_expr() {
1792 eval_expr(&def, env)
1793 } else if bridge_active {
1794 if std::env::var_os("SUI_M26_SELTRACE").is_some() {
1795 let path: Vec<String> = sel.attrpath().map(|ap|
1796 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1797 ).unwrap_or_default();
1798 eprintln!("[M26 SEL-MISS→null] base_type={base_type} path={path:?} missing-key={key}{}", eval_file_ctx());
1799 }
1800 if let Ok(filt) = std::env::var("SUI_M26_HARDSOFTEN") {
1801 let path: Vec<String> = sel.attrpath().map(|ap|
1802 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1803 ).unwrap_or_default();
1804 if path.iter().any(|p| p.contains(&filt)) {
1805 return Err(EvalError::type_error(format!(
1806 "M26-HARDSOFTEN path={path:?} key={key}"
1807 )));
1808 }
1809 }
1810 Ok(Value::Null)
1811 } else {
1812 Err(EvalError::AttrNotFound(
1813 format!("'{key}'{}", eval_file_ctx()),
1814 ))
1815 }
1816 }
1817 Ok(TraverseResult::NotAttrs(forced)) => {
1818 if let Some(def) = sel.default_expr() {
1824 eval_expr(&def, env)
1825 } else if bridge_active {
1826 if let Ok(filt) = std::env::var("SUI_M26_HARDSOFTEN") {
1827 let path: Vec<String> = sel.attrpath().map(|ap|
1828 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1829 ).unwrap_or_default();
1830 if path.iter().any(|p| p.contains(&filt)) {
1831 return Err(EvalError::type_error(format!(
1832 "M26-HARDSOFTEN-NOTATTRS path={path:?} base_type={base_type}"
1833 )));
1834 }
1835 }
1836 return Ok(Value::Null);
1837 } else {
1838 if std::env::var("SUI_DEBUG_SELECT").is_ok() {
1839 let path: Vec<String> = sel.attrpath().map(|ap|
1840 ap.attrs().filter_map(|a| match a {
1841 ast::Attr::Ident(i) => Some(i.to_string()),
1842 ast::Attr::Str(s) => Some(format!("\"{}\"", s.syntax().text())),
1843 ast::Attr::Dynamic(_) => Some("<dyn>".into()),
1844 }).collect()
1845 ).unwrap_or_default();
1846 let dbg = format!("{:?}", forced);
1847 let truncated = if dbg.len() > 200 { format!("{}…", &dbg[..200]) } else { dbg };
1848 eprintln!("[SUI_DEBUG_SELECT] base_type={base_type} path={path:?} base={truncated}{}", eval_file_ctx());
1849 }
1850 Err(attach_trace(EvalError::type_error(
1851 format!("cannot select from {base_type}"),
1852 )))
1853 }
1854 }
1855 Err(EvalError::InfiniteRecursion(_)) if sel.default_expr().is_some() => {
1860 eval_expr(&sel.default_expr().expect("checked"), env)
1861 }
1862 Err(e) => Err(e),
1863 }
1864}
1865
1866fn eval_has_attr(ha: &ast::HasAttr, env: &Env) -> Result<Value, EvalError> {
1868 let base_expr = ha.expr().ok_or_else(|| {
1869 EvalError::ParseError("hasattr missing expression".to_string())
1870 })?;
1871 let base = force_concrete(&eval_expr(&base_expr, env)?)?.into_value();
1872 let attrpath = ha.attrpath().ok_or_else(|| {
1873 EvalError::ParseError("hasattr missing attrpath".to_string())
1874 })?;
1875 match traverse_attrpath(base, &attrpath, env)? {
1876 TraverseResult::Found(_) => Ok(Value::Bool(true)),
1877 TraverseResult::Missing(_) | TraverseResult::NotAttrs(_) => Ok(Value::Bool(false)),
1878 }
1879}
1880
1881fn eval_unary_op(op: &ast::UnaryOp, env: &Env) -> Result<Value, EvalError> {
1882 let inner = op
1883 .expr()
1884 .ok_or_else(|| EvalError::ParseError("unary op missing expr".to_string()))?;
1885 let val = force_value(&eval_expr(&inner, env)?)?;
1886 let kind = op
1887 .operator()
1888 .ok_or_else(|| EvalError::ParseError("unary op missing operator".to_string()))?;
1889 match kind {
1890 ast::UnaryOpKind::Negate => match val {
1891 Value::Int(n) => Ok(Value::Int(-n)),
1892 Value::Float(f) => Ok(Value::Float(-f)),
1893 _ => Err(EvalError::type_error(
1894 format!("cannot negate {}", val.type_name()),
1895 )),
1896 },
1897 ast::UnaryOpKind::Invert => Ok(Value::Bool(!val.as_bool()?)),
1898 }
1899}
1900
1901#[inline]
1912pub(crate) fn builtin_takes_lazy_arg(name: &str) -> bool {
1913 matches!(
1914 name,
1915 "tryEval" | "addErrorContext<partial>" | "seq<partial>" | "deepSeq<partial>" | "foldl'<p1>"
1916 )
1917}
1918
1919fn eval_apply(app: &ast::Apply, env: &Env) -> Result<Value, EvalError> {
1920 let func_expr = app
1921 .lambda()
1922 .ok_or_else(|| EvalError::ParseError("apply missing function".to_string()))?;
1923 let arg_expr = app
1924 .argument()
1925 .ok_or_else(|| EvalError::ParseError("apply missing argument".to_string()))?;
1926 let func = force_value(&eval_expr(&func_expr, env)?)?;
1927 let arg = match &func {
1935 Value::Lambda(_) => {
1936 if let Some(v) = eval_pure_constant_arg(&arg_expr) {
1945 v
1946 } else {
1947 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1948 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1949 }
1950 }
1951 Value::Builtin(b) if builtin_takes_lazy_arg(&b.name) => {
1952 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1957 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1958 }
1959 _ => eval_expr(&arg_expr, env)?,
1960 };
1961 apply(func, arg)
1962}
1963
1964fn eval_pure_constant_arg(arg_expr: &ast::Expr) -> Option<Value> {
1979 match arg_expr {
1980 ast::Expr::Literal(lit) => eval_literal(lit).ok(),
1981 ast::Expr::Str(st) if !str_has_interpolation(st) => {
1982 eval_str(st, &Env::new()).ok()
1984 }
1985 ast::Expr::PathAbs(p) if !parts_have_interpolation(&p.parts()) => {
1986 let text = crate::path::canon_abs(&p.syntax().text().to_string());
1987 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
1988 }
1989 ast::Expr::PathHome(p) if !parts_have_interpolation(&p.parts()) => {
1990 let text = p.syntax().text().to_string();
1991 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
1992 }
1993 _ => None,
1994 }
1995}
1996
1997fn eval_str(s: &ast::Str, env: &Env) -> Result<Value, EvalError> {
1998 let mut result = String::new();
1999 let mut ctx = StringContext::new();
2000 for part in s.normalized_parts() {
2001 match part {
2002 InterpolPart::Literal(text) => result.push_str(&text),
2003 InterpolPart::Interpolation(interpol) => {
2004 let expr = interpol.expr().ok_or_else(|| {
2005 EvalError::ParseError("interpolation missing expr".to_string())
2006 })?;
2007 let val = force_value(&eval_expr(&expr, env)?)?;
2008 let (s, c) = val.coerce_to_string_copy_to_store()?;
2013 result.push_str(&s);
2014 ctx.merge(&c);
2015 }
2016 }
2017 }
2018 Ok(Value::String(Rc::new(NixString::with_context(result, ctx))))
2019}
2020
2021fn parts_have_interpolation(parts: &[InterpolPart<rnix::ast::PathContent>]) -> bool {
2025 parts
2026 .iter()
2027 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2028}
2029
2030fn str_has_interpolation(s: &ast::Str) -> bool {
2034 s.normalized_parts()
2035 .iter()
2036 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2037}
2038
2039fn eval_interpol_path_parts(
2054 parts: &[InterpolPart<rnix::ast::PathContent>],
2055 kind: PathKind,
2056 env: &Env,
2057) -> Result<Value, EvalError> {
2058 let mut text = String::new();
2059 for part in parts {
2060 match part {
2061 InterpolPart::Literal(content) => text.push_str(content.text()),
2062 InterpolPart::Interpolation(interpol) => {
2063 let expr = interpol.expr().ok_or_else(|| {
2064 EvalError::ParseError("path interpolation missing expr".to_string())
2065 })?;
2066 let val = force_value(&eval_expr(&expr, env)?)?;
2067 let (s, _ctx) = val.coerce_to_string()?;
2071 text.push_str(&s);
2072 }
2073 }
2074 }
2075 let resolved = match kind {
2076 PathKind::Rel => {
2079 if let Some(dir) = current_eval_dir() {
2080 let norm = normalize_path(&dir.join(&text));
2081 crate::path::dematerialize(&norm).to_string_lossy().into_owned()
2090 } else {
2091 text
2095 }
2096 }
2097 PathKind::Abs => crate::path::canon_abs(&text),
2105 PathKind::Home => normalize_path(std::path::Path::new(&text))
2108 .to_string_lossy()
2109 .into_owned(),
2110 };
2111 Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))))
2112}
2113
2114#[derive(Clone, Copy)]
2117enum PathKind {
2118 Abs,
2119 Rel,
2120 Home,
2121}
2122
2123fn eval_attr(attr: &ast::Attr, env: &Env) -> Result<String, EvalError> {
2126 eval_attr_maybe_null(attr, env)?
2127 .ok_or_else(|| EvalError::TypeError("null dynamic attribute name".into()))
2128}
2129
2130fn eval_attr_maybe_null(attr: &ast::Attr, env: &Env) -> Result<Option<String>, EvalError> {
2133 match attr {
2134 ast::Attr::Ident(ident) => Ok(Some(ident_text(ident))),
2135 ast::Attr::Dynamic(dyn_) => {
2136 let expr = dyn_
2137 .expr()
2138 .ok_or_else(|| EvalError::ParseError("dynamic attr missing expr".to_string()))?;
2139 let val = force_value(&eval_expr(&expr, env)?)?;
2140 if val == Value::Null {
2143 return Ok(None);
2144 }
2145 Ok(Some(val.as_string()?.to_string()))
2146 }
2147 ast::Attr::Str(s) => {
2148 let val = eval_str(s, env)?;
2149 Ok(Some(val.as_string()?.to_string()))
2150 }
2151 }
2152}
2153
2154fn ident_text(ident: &ast::Ident) -> String {
2156 match ident.ident_token() {
2164 Some(tok) => tok.text().to_string(),
2165 None => ident.syntax().text().to_string(),
2166 }
2167}
2168
2169fn static_attr_offset(attr: &ast::Attr) -> Option<u32> {
2176 let node = match attr {
2177 ast::Attr::Ident(i) => i.syntax(),
2178 ast::Attr::Str(s) => s.syntax(),
2179 ast::Attr::Dynamic(_) => return None,
2180 };
2181 Some(u32::from(node.text_range().start()))
2182}
2183
2184fn attach_attrset_positions(set: &ast::AttrSet, attrs: &mut NixAttrs, env: &Env) {
2191 let mut table = crate::pos::AttrPositions::new(current_eval_file());
2198 for entry in set.entries() {
2199 if let ast::Entry::AttrpathValue(apv) = entry {
2200 let Some(attrpath) = apv.attrpath() else { continue };
2201 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2202 if path_attrs.len() != 1 {
2207 continue;
2208 }
2209 let Some(offset) = static_attr_offset(&path_attrs[0]) else { continue };
2210 if let Ok(Some(name)) = eval_attr_maybe_null(&path_attrs[0], env) {
2213 table.insert(intern(&name), offset);
2214 }
2215 }
2216 }
2217 if !table.is_empty() {
2218 attrs.set_positions(std::rc::Rc::new(table));
2219 }
2220}
2221
2222fn eval_attrset(set: &ast::AttrSet, env: &Env) -> Result<Value, EvalError> {
2223 crate::perf::inc(crate::perf::Counter::Attrset);
2224 let mut attrs = NixAttrs::new();
2225 let is_rec = set.rec_token().is_some();
2226
2227 if is_rec {
2228 let mut rec_env = env.child();
2229 let mut thunks: Vec<(String, Thunk)> = Vec::new();
2230
2231 let mut defined_so_far: HashSet<String> = HashSet::new();
2235
2236 let mut dotted_attrs: NixAttrs = NixAttrs::new();
2242
2243 for entry in set.entries() {
2245 match entry {
2246 ast::Entry::AttrpathValue(apv) => {
2247 let attrpath = apv.attrpath().ok_or_else(|| {
2248 EvalError::ParseError("binding missing attrpath".to_string())
2249 })?;
2250 let value_expr = apv.value().ok_or_else(|| {
2251 EvalError::ParseError("binding missing value".to_string())
2252 })?;
2253 let mut path_keys: Vec<String> = attrpath
2254 .attrs()
2255 .filter_map(|a| eval_attr_maybe_null(&a, env).transpose())
2256 .collect::<Result<_, _>>()?;
2257 if path_keys.is_empty() { continue; }
2259 if path_keys.len() == 1 {
2260 let key = path_keys.pop().unwrap();
2261 let referenced = referenced_idents(&value_expr);
2278 let is_recursive_binding = referenced.contains(key.as_str())
2279 || defined_so_far
2280 .iter()
2281 .any(|n| referenced.contains(n.as_str()));
2282 let value = if is_recursive_binding {
2283 Value::Thunk(Thunk::new_suspended_recursive(
2284 value_expr.clone(),
2285 env.clone(),
2286 ))
2287 } else {
2288 maybe_thunk(&value_expr, env, true, Some(&defined_so_far))
2294 };
2295 rec_env.bind(key.clone(), value.clone());
2296 attrs.insert(key.clone(), value.clone());
2297 if let Value::Thunk(t) = &value {
2298 thunks.push((key.clone(), t.clone()));
2299 }
2300 defined_so_far.insert(key);
2301 } else {
2302 let key = path_keys[0].clone();
2306 let value =
2307 build_nested_attr_thunk(&path_keys[1..], &value_expr, env, &mut thunks);
2308 merge_nested_insert(&mut dotted_attrs, key, value);
2309 }
2310 }
2311 ast::Entry::Inherit(inherit) => {
2312 eval_inherit(&inherit, env, &mut attrs, Some(&mut rec_env), Some(&mut thunks))?;
2313 }
2314 }
2315 }
2316
2317 for (key, value) in dotted_attrs.iter() {
2322 attrs.insert(key.clone(), value.clone());
2323 rec_env.bind(key.clone(), value.clone());
2324 }
2325
2326 for (_key, thunk) in &thunks {
2329 thunk.update_env(&rec_env);
2330 }
2331 } else {
2332 for entry in set.entries() {
2333 match entry {
2334 ast::Entry::AttrpathValue(apv) => {
2335 let attrpath = apv.attrpath().ok_or_else(|| {
2336 EvalError::ParseError("binding missing attrpath".to_string())
2337 })?;
2338 let value_expr = apv.value().ok_or_else(|| {
2339 EvalError::ParseError("binding missing value".to_string())
2340 })?;
2341 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2342 let tail_is_dynamic =
2352 path_attrs.len() > 1 && attrs_have_dynamic(&path_attrs[1..]);
2353 let head_key = match eval_attr_maybe_null(&path_attrs[0], env)? {
2354 Some(k) => k,
2355 None => continue,
2357 };
2358 if tail_is_dynamic && attrs.get(&head_key).is_none() {
2359 let value =
2360 build_deferred_tail_attr(&path_attrs[1..], &value_expr, env);
2361 attrs.insert(head_key, value);
2362 continue;
2363 }
2364 if tail_is_dynamic {
2378 if let Some(existing) = attrs.get(&head_key).cloned() {
2379 let merged = merge_deferred_dynamic_tail(
2380 existing,
2381 &path_attrs[1..],
2382 &value_expr,
2383 env,
2384 )?;
2385 attrs.insert(head_key, merged);
2386 continue;
2387 }
2388 }
2389 let mut path_keys: Vec<String> = {
2392 let mut v = Vec::with_capacity(path_attrs.len());
2393 v.push(head_key);
2394 let mut skip = false;
2395 for a in &path_attrs[1..] {
2396 match eval_attr_maybe_null(a, env)? {
2397 Some(k) => v.push(k),
2398 None => { skip = true; break; }
2399 }
2400 }
2401 if skip { v.clear(); }
2402 v
2403 };
2404 if path_keys.is_empty() { continue; }
2406 if path_keys.len() == 1 {
2407 let key = path_keys.pop().unwrap();
2408 let value = maybe_thunk(&value_expr, env, false, None);
2411 if matches!(attrs.get(&key), Some(Value::Thunk(_))) {
2436 let existing = attrs.get(&key).cloned().unwrap();
2437 let forced_existing = force_value(&existing)?;
2438 attrs.insert(key.clone(), forced_existing);
2439 }
2440 if matches!(attrs.get(&key), Some(Value::Attrs(_))) {
2441 let forced = force_value(&value)?;
2442 merge_nested_insert(&mut attrs, key, forced);
2443 } else {
2444 attrs.insert(key, value);
2445 }
2446 } else {
2447 let key = path_keys[0].clone();
2448 let value = build_nested_attr(&path_keys[1..], &value_expr, env)?;
2449 if matches!(attrs.get(&key), Some(Value::Thunk(_))) {
2463 let existing = attrs.get(&key).cloned().unwrap();
2464 let forced = force_value(&existing)?;
2465 attrs.insert(key.clone(), forced);
2466 }
2467 merge_nested_insert(&mut attrs, key, value);
2468 }
2469 }
2470 ast::Entry::Inherit(inherit) => {
2471 eval_inherit(&inherit, env, &mut attrs, None, None)?;
2472 }
2473 }
2474 }
2475 }
2476
2477 attach_attrset_positions(set, &mut attrs, env);
2483
2484 Ok(Value::Attrs(Rc::new(attrs)))
2485}
2486
2487fn eval_inherit(
2488 inherit: &ast::Inherit,
2489 env: &Env,
2490 attrs: &mut NixAttrs,
2491 bind_env: Option<&mut Env>,
2492 mut thunks: Option<&mut Vec<(String, Thunk)>>,
2493) -> Result<(), EvalError> {
2494 if let Some(from) = inherit.from() {
2495 let source_expr = from
2515 .expr()
2516 .ok_or_else(|| EvalError::ParseError("inherit from missing expr".to_string()))?;
2517 let source_thunk = Thunk::new_suspended(source_expr, env.clone());
2521 let mut be = bind_env;
2522 for attr in inherit.attrs() {
2523 let name = eval_attr(&attr, env)?;
2524 let thunk = Thunk::new_inherit_select(source_thunk.clone(), name.clone());
2525 let value = Value::Thunk(thunk.clone());
2526 attrs.insert(name.clone(), value.clone());
2527 if let Some(ref mut e) = be {
2528 e.bind(name.clone(), value);
2529 }
2530 if let Some(ref mut t) = thunks {
2531 t.push((name, thunk));
2532 }
2533 }
2534 } else {
2535 let mut be = bind_env;
2551 for attr in inherit.attrs() {
2552 let name = eval_attr(&attr, env)?;
2553 let sym = crate::value::intern(&name);
2554 let value = if let Some(v) = env.lookup_fast(sym, &name) {
2555 v
2556 } else if let Some((scope_cache, scope_value)) =
2557 env.innermost_with_scope()
2558 {
2559 Value::Thunk(Thunk::new_with_ident(
2560 SmolStr::from(name.as_str()),
2561 scope_cache,
2562 scope_value,
2563 env.clone(),
2564 ))
2565 } else {
2566 return Err(EvalError::UndefinedVar(format!(
2567 "'{name}'{}",
2568 eval_file_ctx()
2569 )));
2570 };
2571 attrs.insert(name.clone(), value.clone());
2572 if let Some(ref mut e) = be {
2573 e.bind(name, value);
2574 }
2575 }
2576 }
2577 Ok(())
2578}
2579
2580fn build_nested_attr(
2581 path: &[String],
2582 expr: &ast::Expr,
2583 env: &Env,
2584) -> Result<Value, EvalError> {
2585 if path.is_empty() {
2586 return Ok(maybe_thunk(expr, env, false, None));
2591 }
2592 let key = path[0].clone();
2593 let inner = build_nested_attr(&path[1..], expr, env)?;
2594 let mut attrs = NixAttrs::new();
2595 attrs.insert(key, inner);
2596 Ok(Value::Attrs(Rc::new(attrs)))
2597}
2598
2599fn attr_is_dynamic(attr: &ast::Attr) -> bool {
2620 match attr {
2621 ast::Attr::Dynamic(_) => true,
2622 ast::Attr::Str(s) => s
2625 .normalized_parts()
2626 .iter()
2627 .any(|p| matches!(p, InterpolPart::Interpolation(_))),
2628 ast::Attr::Ident(_) => false,
2629 }
2630}
2631
2632fn attrs_have_dynamic(attrs: &[ast::Attr]) -> bool {
2640 attrs.iter().any(attr_is_dynamic)
2641}
2642
2643fn build_deferred_tail_attr(
2656 tail: &[ast::Attr],
2657 value_expr: &ast::Expr,
2658 env: &Env,
2659) -> Value {
2660 let tail: Vec<ast::Attr> = tail.to_vec();
2661 let value_expr = value_expr.clone();
2662 let env = env.clone();
2663 Value::Thunk(Thunk::new_native(move || {
2664 build_tail_attrs_now(&tail, &value_expr, &env)
2665 }))
2666}
2667
2668fn build_tail_attrs_now(
2689 tail: &[ast::Attr],
2690 value_expr: &ast::Expr,
2691 env: &Env,
2692) -> Result<Value, EvalError> {
2693 if tail.is_empty() {
2694 return Ok(maybe_thunk(value_expr, env, false, None));
2695 }
2696 if std::env::var_os("SUI_M26_TAILTRACE").is_some() {
2697 let t: String = tail[0].syntax().text().to_string().chars().take(40).collect();
2698 eprintln!("[M26 TAIL-RESOLVE] forcing dynamic tail key `{t}`");
2699 if attrs_have_dynamic(&tail[..1]) {
2700 crate::trace::dump_force_stack_ids();
2701 }
2702 }
2703 let key = match eval_attr_maybe_null(&tail[0], env)? {
2704 Some(k) => k,
2705 None => return Ok(Value::Attrs(Rc::new(NixAttrs::new()))),
2708 };
2709 let inner = if tail.len() == 1 {
2715 maybe_thunk(value_expr, env, false, None)
2716 } else {
2717 build_deferred_tail_attr(&tail[1..], value_expr, env)
2718 };
2719 let mut attrs = NixAttrs::new();
2720 attrs.insert(key, inner);
2721 Ok(Value::Attrs(Rc::new(attrs)))
2722}
2723
2724fn merge_deferred_dynamic_tail(
2742 existing: Value,
2743 tail: &[ast::Attr],
2744 value_expr: &ast::Expr,
2745 env: &Env,
2746) -> Result<Value, EvalError> {
2747 debug_assert!(!tail.is_empty());
2750
2751 if attr_is_dynamic(&tail[0]) {
2756 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2757 return Ok(lazy_overlay_merge(existing, deferred));
2758 }
2759
2760 let key = match eval_attr_maybe_null(&tail[0], env)? {
2763 Some(k) => k,
2764 None => return Ok(existing),
2765 };
2766
2767 let existing_forced = force_value(&existing)?;
2771 let mut base = match existing_forced {
2772 Value::Attrs(a) => (*a).clone(),
2773 _ => {
2778 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2779 return Ok(deferred);
2780 }
2781 };
2782
2783 let child_existing = base.get(&key).cloned();
2785 let new_child = match child_existing {
2786 Some(child) if tail.len() > 1 => {
2787 merge_deferred_dynamic_tail(child, &tail[1..], value_expr, env)?
2789 }
2790 Some(child) => {
2791 let leaf = maybe_thunk(value_expr, env, false, None);
2794 lazy_overlay_merge(child, leaf)
2795 }
2796 None if tail.len() > 1 => {
2797 build_deferred_tail_attr(&tail[1..], value_expr, env)
2801 }
2802 None => maybe_thunk(value_expr, env, false, None),
2803 };
2804 base.insert(key, new_child);
2805 Ok(Value::Attrs(Rc::new(base)))
2806}
2807
2808fn lazy_overlay_merge(left: Value, right: Value) -> Value {
2815 match (&left, &right) {
2816 (Value::Attrs(la), Value::Attrs(_)) => {
2817 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2818 let mut merged = (**la).clone();
2819 if let Value::Attrs(ra) = &right {
2820 for (k, v) in ra.iter_unsorted() {
2824 merge_nested_insert(&mut merged, k.clone(), v.clone());
2825 }
2826 }
2827 Value::Attrs(Rc::new(merged))
2828 }
2829 _ => {
2830 Value::Thunk(Thunk::new_native(move || {
2834 let lf = force_value(&left)?;
2835 let rf = force_value(&right)?;
2836 let la = lf.as_attrs()?;
2837 let ra = rf.as_attrs()?;
2838 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2839 let mut merged = (*la).clone();
2840 for (k, v) in ra.iter_unsorted() {
2841 merge_nested_insert(&mut merged, k.clone(), v.clone());
2842 }
2843 Ok(Value::Attrs(Rc::new(merged)))
2844 }))
2845 }
2846 }
2847}
2848
2849fn build_nested_attr_thunk(
2857 path: &[String],
2858 expr: &ast::Expr,
2859 env: &Env,
2860 thunks: &mut Vec<(String, Thunk)>,
2861) -> Value {
2862 if path.is_empty() {
2863 let thunk = Thunk::new_suspended(expr.clone(), env.clone());
2864 let val = Value::Thunk(thunk.clone());
2865 thunks.push((String::new(), thunk));
2866 return val;
2867 }
2868 let key = path[0].clone();
2869 let inner = build_nested_attr_thunk(&path[1..], expr, env, thunks);
2870 let mut attrs = NixAttrs::new();
2871 attrs.insert(key, inner);
2872 Value::Attrs(Rc::new(attrs))
2873}
2874
2875fn merge_nested_insert(target: &mut NixAttrs, key: String, value: Value) {
2882 let existing = match target.get(&key) {
2886 Some(e) => e.clone(),
2887 None => {
2888 target.insert(key, value);
2889 return;
2890 }
2891 };
2892 let value = match value {
2916 Value::Thunk(_) => match force_value(&value) {
2917 Ok(v @ Value::Attrs(_)) => v,
2918 _ => value,
2919 },
2920 other => other,
2921 };
2922 if !matches!(value, Value::Attrs(_)) {
2923 target.insert(key, value);
2924 return;
2925 }
2926 let existing_concrete = match &existing {
2929 Value::Attrs(_) => existing.clone(),
2930 Value::Thunk(_) => match force_value(&existing) {
2931 Ok(v @ Value::Attrs(_)) => v,
2932 _ => {
2933 target.insert(key, value);
2934 return;
2935 }
2936 },
2937 _ => {
2938 target.insert(key, value);
2939 return;
2940 }
2941 };
2942 let mut existing_attrs = match existing_concrete {
2946 Value::Attrs(a) => (*a).clone(),
2947 _ => unreachable!(),
2948 };
2949 let new_attrs = match value {
2950 Value::Attrs(ref a) => a,
2951 _ => unreachable!(),
2952 };
2953 for (k, v) in new_attrs.iter_unsorted() {
2954 merge_nested_insert(&mut existing_attrs, k.clone(), v.clone());
2955 }
2956 target.insert(key, Value::Attrs(Rc::new(existing_attrs)));
2957}
2958
2959fn eval_entries<N: HasEntry + AstNode>(node: &N, env: &mut Env) -> Result<(), EvalError> {
2961 for entry in node.entries() {
2962 match entry {
2963 ast::Entry::AttrpathValue(apv) => {
2964 let attrpath = apv.attrpath().ok_or_else(|| {
2965 EvalError::ParseError("binding missing attrpath".to_string())
2966 })?;
2967 let value_expr = apv.value().ok_or_else(|| {
2968 EvalError::ParseError("binding missing value".to_string())
2969 })?;
2970 let mut path_keys: Vec<String> = attrpath
2971 .attrs()
2972 .map(|a| eval_attr(&a, env))
2973 .collect::<Result<_, _>>()?;
2974 if path_keys.len() == 1 {
2975 let key = path_keys.pop().unwrap();
2976 let value = eval_expr(&value_expr, env)?;
2977 env.bind(key, value);
2978 }
2979 }
2981 ast::Entry::Inherit(inherit) => {
2982 if let Some(from) = inherit.from() {
2983 let source_expr = from.expr().ok_or_else(|| {
2984 EvalError::ParseError("inherit from missing expr".to_string())
2985 })?;
2986 let source = force_value(&eval_expr(&source_expr, env)?)?;
2987 let source_attrs = source.as_attrs()?;
2988 for attr in inherit.attrs() {
2989 let name = eval_attr(&attr, env)?;
2990 let value = source_attrs
2991 .get(&name)
2992 .cloned()
2993 .ok_or_else(|| EvalError::AttrNotFound(
2994 format!("'{name}' in inherit{}", eval_file_ctx()),
2995 ))?;
2996 env.bind(name, value);
2997 }
2998 } else {
2999 for attr in inherit.attrs() {
3000 let name = eval_attr(&attr, env)?;
3001 let value = env
3002 .lookup(&name)
3003 .ok_or_else(|| EvalError::UndefinedVar(
3004 format!("'{name}'{}", eval_file_ctx()),
3005 ))?;
3006 env.bind(name, value);
3007 }
3008 }
3009 }
3010 }
3011 }
3012 Ok(())
3013}
3014
3015fn eval_binop(
3016 op: ast::BinOpKind,
3017 lhs: &ast::Expr,
3018 rhs: &ast::Expr,
3019 env: &Env,
3020) -> Result<Value, EvalError> {
3021 match op {
3023 ast::BinOpKind::And => {
3024 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3025 if !l {
3026 return Ok(Value::Bool(false));
3027 }
3028 return eval_expr(rhs, env);
3029 }
3030 ast::BinOpKind::Or => {
3031 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3032 if l {
3033 return Ok(Value::Bool(true));
3034 }
3035 return eval_expr(rhs, env);
3036 }
3037 ast::BinOpKind::Implication => {
3038 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3039 if !l {
3040 return Ok(Value::Bool(true));
3041 }
3042 return eval_expr(rhs, env);
3043 }
3044 _ => {}
3045 }
3046
3047 let lc = force_concrete(&eval_expr(lhs, env)?)?;
3048 let rc = force_concrete(&eval_expr(rhs, env)?)?;
3049 let l = lc.into_value();
3056 let r = rc.into_value();
3057
3058 match op {
3059 ast::BinOpKind::Add => match (&l, &r) {
3060 (Value::Int(a), Value::Int(b)) => a
3061 .checked_add(*b)
3062 .map(Value::Int)
3063 .ok_or_else(|| int_overflow("adding", *a, '+', *b)),
3064 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
3065 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
3066 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a + *b as f64)),
3067 (Value::String(a), Value::String(b)) => {
3068 let mut ctx = a.context.clone();
3069 ctx.merge(&b.context);
3070 let mut s = String::with_capacity(a.chars.len() + b.chars.len());
3078 s.push_str(&a.chars);
3079 s.push_str(&b.chars);
3080 Ok(Value::String(Rc::new(NixString::with_context(s, ctx))))
3081 }
3082 (Value::Path(a), Value::String(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}{}", b.chars).as_str())))),
3083 (Value::Path(a), Value::Path(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}/{b}").as_str())))),
3084 (Value::Attrs(_), _) | (_, Value::Attrs(_)) => {
3086 let (ls, lctx) = l.coerce_to_string()?;
3087 let (rs, rctx) = r.coerce_to_string()?;
3088 let mut ctx = lctx;
3089 ctx.merge(&rctx);
3090 Ok(Value::String(Rc::new(NixString::with_context(
3091 format!("{ls}{rs}"),
3092 ctx,
3093 ))))
3094 }
3095 _ => Err(EvalError::op_type("add", l.type_name(), r.type_name())),
3096 },
3097 ast::BinOpKind::Sub => num_op(
3098 &l,
3099 &r,
3100 |a, b| a.checked_sub(b),
3101 |a, b| a - b,
3102 |a, b| int_overflow("subtracting", a, '-', b),
3103 ),
3104 ast::BinOpKind::Mul => num_op(
3105 &l,
3106 &r,
3107 |a, b| a.checked_mul(b),
3108 |a, b| a * b,
3109 |a, b| int_overflow("multiplying", a, '*', b),
3110 ),
3111 ast::BinOpKind::Div => {
3112 let rhs_is_zero = match &r {
3121 Value::Int(0) => true,
3122 Value::Float(f) => *f == 0.0,
3123 _ => false,
3124 };
3125 if rhs_is_zero {
3126 return Err(EvalError::DivisionByZero);
3127 }
3128 num_op(
3129 &l,
3130 &r,
3131 |a, b| a.checked_div(b),
3132 |a, b| a / b,
3133 |a, b| int_overflow("dividing", a, '/', b),
3134 )
3135 }
3136 ast::BinOpKind::Equal => Ok(Value::Bool(l == r)),
3137 ast::BinOpKind::NotEqual => Ok(Value::Bool(l != r)),
3138 ast::BinOpKind::Less => compare(&l, &r, |o| o == std::cmp::Ordering::Less),
3139 ast::BinOpKind::LessOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Greater),
3140 ast::BinOpKind::More => compare(&l, &r, |o| o == std::cmp::Ordering::Greater),
3141 ast::BinOpKind::MoreOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Less),
3142 ast::BinOpKind::Update => {
3143 let la = l.to_attrs()?;
3144 let ra = r.to_attrs()?;
3145 Ok(Value::Attrs(Rc::new(la.overlay(ra))))
3147 }
3148 ast::BinOpKind::Concat => {
3149 crate::value::concat_lists(l, r.as_list()?)
3159 }
3160 ast::BinOpKind::And | ast::BinOpKind::Or | ast::BinOpKind::Implication => {
3161 unreachable!("handled above")
3162 }
3163 ast::BinOpKind::PipeRight | ast::BinOpKind::PipeLeft => {
3164 Err(EvalError::NotImplemented("pipe operators".to_string()))
3165 }
3166 }
3167}
3168
3169#[inline]
3174fn int_overflow(verb: &str, a: i64, sym: char, b: i64) -> EvalError {
3175 EvalError::Abort(format!("integer overflow in {verb} {a} {sym} {b}"))
3176}
3177
3178fn num_op(
3179 l: &Value,
3180 r: &Value,
3181 int_op: impl Fn(i64, i64) -> Option<i64>,
3182 float_op: impl Fn(f64, f64) -> f64,
3183 overflow: impl Fn(i64, i64) -> EvalError,
3184) -> Result<Value, EvalError> {
3185 match (l, r) {
3186 (Value::Int(a), Value::Int(b)) => {
3187 int_op(*a, *b).map(Value::Int).ok_or_else(|| overflow(*a, *b))
3188 }
3189 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(float_op(*a, *b))),
3190 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(float_op(*a as f64, *b))),
3191 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(float_op(*a, *b as f64))),
3192 _ => Err(EvalError::op_type("perform arithmetic on", l.type_name(), r.type_name())),
3193 }
3194}
3195
3196fn compare(
3197 l: &Value,
3198 r: &Value,
3199 pred: impl Fn(std::cmp::Ordering) -> bool,
3200) -> Result<Value, EvalError> {
3201 let ord = match (l, r) {
3202 (Value::Int(a), Value::Int(b)) => a.cmp(b),
3203 (Value::Float(a), Value::Float(b)) => {
3204 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
3205 }
3206 (Value::Int(a), Value::Float(b)) => (*a as f64)
3207 .partial_cmp(b)
3208 .unwrap_or(std::cmp::Ordering::Equal),
3209 (Value::Float(a), Value::Int(b)) => a
3210 .partial_cmp(&(*b as f64))
3211 .unwrap_or(std::cmp::Ordering::Equal),
3212 (Value::String(a), Value::String(b)) => a.chars.cmp(&b.chars),
3213 _ => {
3214 return Err(EvalError::op_type("compare", l.type_name(), r.type_name()));
3215 }
3216 };
3217 Ok(Value::Bool(pred(ord)))
3218}
3219
3220pub fn apply_and_force(func: Value, arg: Value) -> Result<Value, EvalError> {
3234 force_value(&apply(func, arg)?)
3235}
3236
3237pub fn apply(func: Value, arg: Value) -> Result<Value, EvalError> {
3238 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || apply_inner(func, arg))
3239}
3240
3241fn apply_inner(func: Value, arg: Value) -> Result<Value, EvalError> {
3242 crate::perf::inc(crate::perf::Counter::Apply);
3243 let func = force_concrete(&func)?.into_value();
3244 match func {
3245 Value::Lambda(closure) => {
3246 if crate::perf::enabled() {
3248 APPLY_SITES.with(|sites| {
3249 let file = closure.env.eval_file()
3250 .map(|p| p.display().to_string())
3251 .unwrap_or_else(|| "<eval>".into());
3252 let param_name = match &closure.param {
3254 rnix::ast::Param::IdentParam(ip) => ip.ident().map(|i| ident_text(&i)).unwrap_or_default(),
3255 rnix::ast::Param::Pattern(pat) => {
3256 let mut names: Vec<String> = pat.pat_entries()
3257 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3258 .take(3)
3259 .collect();
3260 if pat.pat_entries().count() > 3 { names.push("...".to_string()); }
3261 format!("{{{}}}", names.join(","))
3262 }
3263 };
3264 let key = format!("{}:{}", file.rsplit_once("-source/").map_or(file.as_str(), |(_,s)| s), param_name);
3265 *sites.borrow_mut().entry(key).or_insert(0u64) += 1;
3266 });
3267 }
3268 let mut call_env = closure.env.child();
3269 let _file_guard = closure
3270 .env
3271 .eval_file()
3272 .cloned()
3273 .map(push_eval_file);
3274 let _trace = push_nix_trace_lambda(&closure.env);
3280 match &closure.param {
3281 rnix::ast::Param::IdentParam(_) => {
3282 bind_param(&closure.param, &arg, &mut call_env)?;
3285 }
3286 rnix::ast::Param::Pattern(_) => {
3287 let forced_arg = force_concrete(&arg)?.into_value();
3289 bind_param(&closure.param, &forced_arg, &mut call_env)?;
3290 }
3291 }
3292 eval_expr(&closure.body, &call_env)
3293 }
3294 Value::Builtin(b) => {
3295 let _trace = push_nix_trace(format!("while calling the '{}' builtin", b.name));
3296 if builtin_takes_lazy_arg(&b.name) {
3306 (b.func)(&[arg])
3307 } else {
3308 let forced_arg = force_value(&arg)?;
3309 (b.func)(&[forced_arg])
3310 }
3311 }
3312 Value::Attrs(ref attrs) => {
3313 if let Some(functor) = attrs.get("__functor") {
3314 let functor = force_value(functor)?;
3315 let partial = apply(functor, func.clone())?;
3317 apply(partial, arg)
3318 } else if crate::value::in_promise_eval() {
3319 Ok(Value::Null)
3324 } else {
3325 Err(EvalError::type_error(
3326 format!("cannot call {} (missing __functor){}", func.type_name(), eval_file_ctx()),
3327 ))
3328 }
3329 }
3330 _ if crate::value::in_promise_eval() => {
3331 Ok(Value::Null)
3336 }
3337 _ => Err(EvalError::type_error(
3338 format!("cannot call {}{}", func.type_name(), eval_file_ctx()),
3339 )),
3340 }
3341}
3342
3343static SUI_BATCH_BIND: std::sync::LazyLock<bool> =
3353 std::sync::LazyLock::new(|| std::env::var_os("SUI_BATCH_BIND").is_some());
3354
3355fn bind_param(param: &ast::Param, arg: &Value, env: &mut Env) -> Result<(), EvalError> {
3356 match param {
3357 ast::Param::IdentParam(ip) => {
3358 let ident = ip
3359 .ident()
3360 .ok_or_else(|| EvalError::ParseError("ident param missing ident".to_string()))?;
3361 let name = ident_text(&ident);
3362 env.bind(name, arg.clone());
3363 }
3364 ast::Param::Pattern(pat) => {
3365 let attrs = arg.as_attrs()?;
3366
3367 if let Some(pat_bind) = pat.pat_bind()
3369 && let Some(ident) = pat_bind.ident()
3370 {
3371 let name = ident_text(&ident);
3372 env.bind(name, arg.clone());
3373 }
3374
3375 let has_ellipsis = pat.ellipsis_token().is_some();
3376 let entries: Vec<ast::PatEntry> = pat.pat_entries().collect();
3377
3378 let mut default_thunks: Vec<Thunk> = Vec::new();
3385 let use_batch = *SUI_BATCH_BIND;
3395 let mut pairs: Vec<(String, Value)> =
3396 if use_batch { Vec::with_capacity(entries.len()) } else { Vec::new() };
3397
3398 for entry in &entries {
3399 let ident = entry.ident().ok_or_else(|| {
3400 EvalError::ParseError("pat entry missing ident".to_string())
3401 })?;
3402 let name = ident_text(&ident);
3403 let value = if let Some(v) = attrs.get(&name) {
3404 v.clone()
3405 } else if let Some(default_expr) = entry.default() {
3406 let thunk = Thunk::new_suspended(
3412 ast::Expr::cast(default_expr.syntax().clone()).unwrap(),
3413 env.clone(),
3414 );
3415 default_thunks.push(thunk.clone());
3416 Value::Thunk(thunk)
3417 } else {
3418 return Err(EvalError::type_error(
3419 format!("missing argument '{name}'{}", eval_file_ctx()),
3420 ));
3421 };
3422 if use_batch {
3423 pairs.push((name, value));
3424 } else {
3425 env.bind(name, value);
3426 }
3427 }
3428 if use_batch {
3429 env.bind_many(pairs);
3430 }
3431
3432 for thunk in &default_thunks {
3434 thunk.update_env(env);
3435 }
3436
3437 if !has_ellipsis {
3438 let entry_names: std::collections::HashSet<String> = entries
3439 .iter()
3440 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3441 .collect();
3442 for key in attrs.keys() {
3443 if !entry_names.contains(key.as_str()) {
3444 return Err(EvalError::type_error(
3445 format!("unexpected argument '{key}'{}", eval_file_ctx()),
3446 ));
3447 }
3448 }
3449 }
3450 }
3451 }
3452 Ok(())
3453}
3454
3455#[cfg(test)]
3456mod tests {
3457 use super::*;
3458
3459 fn ev(input: &str) -> Value {
3460 eval(input).unwrap()
3461 }
3462
3463 #[test]
3470 fn is_self_recursive_binding_ignores_attribute_names() {
3471 fn expr(s: &str) -> ast::Expr {
3472 rnix::Root::parse(s).tree().expr().expect("parse")
3473 }
3474 assert!(!is_self_recursive_binding(&expr("lhs.placeholder"), "placeholder"));
3476 assert!(!is_self_recursive_binding(&expr("{ placeholder = 1; }"), "placeholder"));
3477 assert!(!is_self_recursive_binding(
3478 &expr("if lhs.placeholder == rhs.placeholder then lhs.placeholder else null"),
3479 "placeholder",
3480 ));
3481 assert!(is_self_recursive_binding(&expr("placeholder + 1"), "placeholder"));
3483 assert!(is_self_recursive_binding(
3484 &expr("if placeholder then 1 else 2"),
3485 "placeholder"
3486 ));
3487 }
3488
3489 #[test]
3493 fn maybe_thunk_eager_constant_str_is_byte_identical() {
3494 fn expr(s: &str) -> ast::Expr {
3495 rnix::Root::parse(s).tree().expr().expect("parse")
3496 }
3497 let env = Env::new();
3498 let v = maybe_thunk(&expr(r#""abc""#), &env, false, None);
3500 assert!(matches!(v, Value::String(_)), "constant str should be eager, got {v:?}");
3501 assert_eq!(force_value(&v).unwrap(), Value::string("abc"));
3502 let vi = maybe_thunk(&expr(r#""a${b}c""#), &env, false, None);
3504 assert!(matches!(vi, Value::Thunk(_)), "interpolated str must stay thunked");
3505 }
3506
3507 #[test]
3511 fn eval_pure_constant_arg_classification() {
3512 fn expr(s: &str) -> ast::Expr {
3513 rnix::Root::parse(s).tree().expr().expect("parse")
3514 }
3515 assert!(eval_pure_constant_arg(&expr("42")).is_some());
3517 assert!(eval_pure_constant_arg(&expr("3.14")).is_some());
3518 assert!(eval_pure_constant_arg(&expr(r#""const""#)).is_some());
3519 assert!(eval_pure_constant_arg(&expr("/abs/path")).is_some());
3520 assert!(eval_pure_constant_arg(&expr(r#""a${b}c""#)).is_none(), "interpolated str");
3522 assert!(eval_pure_constant_arg(&expr("true")).is_none(), "bool is an ident");
3525 assert!(eval_pure_constant_arg(&expr("x")).is_none(), "ident (with-scope force)");
3526 assert!(eval_pure_constant_arg(&expr("a.b")).is_none(), "select (fixpoint)");
3527 assert!(eval_pure_constant_arg(&expr("f x")).is_none(), "apply (may throw)");
3528 assert!(eval_pure_constant_arg(&expr("1 + 1")).is_none(), "binop (may throw)");
3529 assert!(eval_pure_constant_arg(&expr("throw \"x\"")).is_none(), "throw stays lazy");
3530 }
3531
3532 #[test]
3536 fn ignored_throwing_arg_stays_lazy() {
3537 assert_eq!(ev(r#"(x: 7) (throw "boom")"#), Value::Int(7));
3538 assert_eq!(ev(r#"(x: 7) "const""#), Value::Int(7));
3540 assert_eq!(ev(r#"(x: x) "used""#), Value::string("used"));
3542 }
3543
3544 #[test]
3545 fn eval_int() { assert_eq!(ev("42"), Value::Int(42)); }
3546
3547 #[test]
3548 fn eval_float() { assert_eq!(ev("3.14"), Value::Float(3.14)); }
3549
3550 #[test]
3551 fn eval_string() { assert_eq!(ev(r#""hello""#), Value::string("hello")); }
3552
3553 #[test]
3554 fn eval_bool() { assert_eq!(ev("true"), Value::Bool(true)); }
3555
3556 #[test]
3557 fn eval_null() { assert_eq!(ev("null"), Value::Null); }
3558
3559 #[test]
3560 fn eval_arithmetic() {
3561 assert_eq!(ev("1 + 2"), Value::Int(3));
3562 assert_eq!(ev("10 - 3"), Value::Int(7));
3563 assert_eq!(ev("2 * 3"), Value::Int(6));
3564 assert_eq!(ev("10 / 3"), Value::Int(3));
3565 }
3566
3567 #[test]
3568 fn eval_precedence() {
3569 assert_eq!(ev("1 + 2 * 3"), Value::Int(7));
3570 assert_eq!(ev("(1 + 2) * 3"), Value::Int(9));
3571 }
3572
3573 #[test]
3574 fn eval_comparison() {
3575 assert_eq!(ev("1 == 1"), Value::Bool(true));
3576 assert_eq!(ev("1 == 2"), Value::Bool(false));
3577 assert_eq!(ev("1 < 2"), Value::Bool(true));
3578 assert_eq!(ev("2 <= 2"), Value::Bool(true));
3579 }
3580
3581 #[test]
3582 fn eval_logic() {
3583 assert_eq!(ev("true && false"), Value::Bool(false));
3584 assert_eq!(ev("true || false"), Value::Bool(true));
3585 assert_eq!(ev("!true"), Value::Bool(false));
3586 }
3587
3588 #[test]
3589 fn eval_string_concat() {
3590 assert_eq!(ev(r#""hello" + " " + "world""#), Value::string("hello world"));
3591 }
3592
3593 #[test]
3594 fn eval_if() {
3595 assert_eq!(ev("if true then 1 else 2"), Value::Int(1));
3596 assert_eq!(ev("if false then 1 else 2"), Value::Int(2));
3597 }
3598
3599 #[test]
3600 fn eval_let() {
3601 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
3602 assert_eq!(ev("let x = 1; y = 2; in x + y"), Value::Int(3));
3603 }
3604
3605 #[test]
3606 fn eval_let_dotted_simple() {
3607 assert_eq!(ev("let a.b = 1; a.c = 2; in a.b + a.c"), Value::Int(3));
3609 }
3610
3611 #[test]
3612 fn eval_let_dotted_deep() {
3613 assert_eq!(ev("let a.b.c = 1; in a.b.c"), Value::Int(1));
3615 }
3616
3617 #[test]
3618 fn eval_let_dotted_mixed() {
3619 assert_eq!(
3621 ev("let a.x = 1; b = 2; a.y = 3; in a.x + a.y + b"),
3622 Value::Int(6),
3623 );
3624 }
3625
3626 #[test]
3627 fn eval_let_dotted_produces_attrset() {
3628 let v = ev("let a.b = 1; a.c = 2; in a");
3630 if let Value::Attrs(attrs) = v {
3631 assert_eq!(attrs.get("b"), Some(&Value::Int(1)));
3632 assert_eq!(attrs.get("c"), Some(&Value::Int(2)));
3633 } else {
3634 panic!("expected Attrs, got {v:?}");
3635 }
3636 }
3637
3638 #[test]
3646 fn dynamic_inner_attr_key_is_lazy_on_sibling_read() {
3647 assert_eq!(
3649 ev(r#"let s = { a.${throw "KEYFORCED"} = 7; other = 9; }; in s.other"#),
3650 Value::Int(9),
3651 );
3652 }
3653
3654 #[test]
3655 fn dynamic_inner_attr_key_resolves_on_head_demand() {
3656 let v = ev(r#"let u = "bob"; s = { homes.${u} = 7; }; in s.homes"#);
3658 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3659 assert_eq!(attrs.get("bob"), Some(&Value::Int(7)));
3660 } else {
3661 panic!("expected Attrs");
3662 }
3663 }
3664
3665 #[test]
3666 fn dynamic_inner_attr_key_merges_with_static_sibling() {
3667 let v = ev(r#"let u = "x"; s = { a.${u} = 1; a.b = 2; }; in s.a"#);
3669 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3670 assert_eq!(attrs.get("x"), Some(&Value::Int(1)));
3671 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3672 } else {
3673 panic!("expected Attrs");
3674 }
3675 }
3676
3677 #[test]
3678 fn dynamic_inner_attr_key_null_skips_binding() {
3679 let v = ev(
3682 r#"let c = true; s = { a.${if c then null else "n"} = 5; b = 1; }; in s.b"#,
3683 );
3684 assert_eq!(v, Value::Int(1));
3685 }
3686
3687 #[test]
3693 fn interpolated_string_attr_key_is_lazy_on_sibling_read() {
3694 assert_eq!(
3695 ev(r#"let s = { a."p/${throw "KEYFORCED"}" = 7; other = 9; }; in s.other"#),
3696 Value::Int(9),
3697 );
3698 }
3699
3700 #[test]
3701 fn interpolated_string_attr_key_resolves_on_head_demand() {
3702 let v = ev(r#"let u = "bob"; s = { homes."u/${u}" = 7; }; in s.homes"#);
3704 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3705 assert_eq!(attrs.get("u/bob"), Some(&Value::Int(7)));
3706 } else {
3707 panic!("expected Attrs");
3708 }
3709 }
3710
3711 #[test]
3712 fn purely_literal_string_attr_key_stays_eager_static() {
3713 let v = ev(r#"let s = { a."foo bar" = 1; a.b = 2; }; in s.a"#);
3716 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3717 assert_eq!(attrs.get("foo bar"), Some(&Value::Int(1)));
3718 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3719 } else {
3720 panic!("expected Attrs");
3721 }
3722 }
3723
3724 #[test]
3727 fn dynamic_tail_key_under_colliding_head_is_lazy() {
3728 let v = ev(
3731 r#"let s = { sd.services.x = 1; sd.tmpfiles.${throw "KEYFORCED"}.d = 2; }; in s.sd.services.x"#,
3732 );
3733 assert_eq!(v, Value::Int(1));
3734 }
3735
3736 #[test]
3737 fn dynamic_tail_key_under_colliding_head_resolves_and_merges() {
3738 let v = ev(
3741 r#"let k = "z"; s = { sd.services.x = 1; sd.tmpfiles.${k}.d = 2; }; in s.sd"#,
3742 );
3743 let sd = force_value(&v).unwrap();
3744 if let Value::Attrs(sd_attrs) = &sd {
3745 let services = force_value(sd_attrs.get("services").unwrap()).unwrap();
3747 if let Value::Attrs(a) = &services {
3748 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3749 } else { panic!("expected services attrs"); }
3750 let tmpfiles = force_value(sd_attrs.get("tmpfiles").unwrap()).unwrap();
3752 if let Value::Attrs(a) = &tmpfiles {
3753 let z = force_value(a.get("z").unwrap()).unwrap();
3754 if let Value::Attrs(zd) = &z {
3755 assert_eq!(force_value(zd.get("d").unwrap()).unwrap(), Value::Int(2));
3756 } else { panic!("expected z attrs"); }
3757 } else { panic!("expected tmpfiles attrs"); }
3758 } else {
3759 panic!("expected sd attrs");
3760 }
3761 }
3762
3763 #[test]
3772 fn with_namespace_is_lazy_on_body_whnf() {
3773 let v = ev(r#"builtins.attrNames (with (throw "WITH-FORCED"); { a = 1; b = 2; })"#);
3774 if let Value::List(items) = force_value(&v).unwrap() {
3775 let names: Vec<String> = items
3776 .iter()
3777 .map(|i| match force_value(i).unwrap() {
3778 Value::String(s) => s.as_str().to_string(),
3779 other => panic!("expected string, got {}", other.type_name()),
3780 })
3781 .collect();
3782 assert_eq!(names, vec!["a".to_string(), "b".to_string()]);
3783 } else {
3784 panic!("expected list");
3785 }
3786 }
3787
3788 #[test]
3789 fn with_namespace_forces_only_on_fallthrough() {
3790 assert_eq!(ev(r#"with { x = 42; }; x"#), Value::Int(42));
3794 assert_eq!(ev(r#"let x = 7; in with (throw "NS"); x"#), Value::Int(7));
3797 }
3798
3799 #[test]
3810 fn dotted_fullset_leaf_deep_merges_with_deeper_sibling() {
3811 let v = ev(r#"{ o.a = { x = 1; }; o.a.y = 2; }.o.a"#);
3812 if let Value::Attrs(a) = force_value(&v).unwrap() {
3813 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3814 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3815 } else {
3816 panic!("expected attrs");
3817 }
3818 }
3819
3820 #[test]
3821 fn dotted_fullset_leaf_deep_merge_reverse_order() {
3822 let v = ev(r#"{ o.a.y = 2; o.a = { x = 1; }; }.o.a"#);
3825 if let Value::Attrs(a) = force_value(&v).unwrap() {
3826 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3827 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3828 } else {
3829 panic!("expected attrs");
3830 }
3831 }
3832
3833 #[test]
3834 fn dotted_fullset_leaf_merge_preserves_leaf_laziness() {
3835 assert_eq!(ev(r#"{ o.a = { x = throw "X-NEVER"; }; o.a.y = 2; }.o.a.y"#), Value::Int(2));
3839 }
3840
3841 #[test]
3842 fn eval_nested_let() {
3843 assert_eq!(ev("let a = 1; b = let c = 2; in c; in a + b"), Value::Int(3));
3844 }
3845
3846 #[test]
3847 fn eval_lambda() {
3848 assert_eq!(ev("(x: x + 1) 41"), Value::Int(42));
3849 }
3850
3851 #[test]
3852 fn eval_lambda_multi_arg() {
3853 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
3854 }
3855
3856 #[test]
3857 fn eval_list() {
3858 let v = ev("[1 2 3]");
3859 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]));
3860 }
3861
3862 #[test]
3863 fn eval_list_concat() {
3864 let v = ev("[1 2] ++ [3 4]");
3865 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]));
3866 }
3867
3868 #[test]
3869 fn eval_attrset() {
3870 let v = ev("{ a = 1; b = 2; }");
3871 if let Value::Attrs(attrs) = v {
3872 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3873 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3874 } else {
3875 panic!("expected attrset");
3876 }
3877 }
3878
3879 #[test]
3880 fn eval_select() {
3881 assert_eq!(ev("{ a = 42; }.a"), Value::Int(42));
3882 }
3883
3884 #[test]
3885 fn eval_select_or() {
3886 assert_eq!(ev("{ a = 42; }.b or 0"), Value::Int(0));
3887 }
3888
3889 #[test]
3890 fn eval_has_attr() {
3891 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
3892 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
3893 }
3894
3895 #[test]
3896 fn eval_update() {
3897 let v = ev("{ a = 1; b = 2; } // { b = 3; c = 4; }");
3898 if let Value::Attrs(attrs) = v {
3899 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3900 assert_eq!(attrs.get("b"), Some(&Value::Int(3)));
3901 assert_eq!(attrs.get("c"), Some(&Value::Int(4)));
3902 } else {
3903 panic!("expected attrset");
3904 }
3905 }
3906
3907 #[test]
3908 fn eval_with() {
3909 assert_eq!(ev("with { x = 42; }; x"), Value::Int(42));
3910 }
3911
3912 #[test]
3913 fn eval_assert() {
3914 assert_eq!(ev("assert true; 42"), Value::Int(42));
3915 assert!(eval("assert false; 42").is_err());
3916 }
3917
3918 #[test]
3919 fn eval_formals() {
3920 assert_eq!(ev("({ a, b }: a + b) { a = 1; b = 2; }"), Value::Int(3));
3921 }
3922
3923 #[test]
3924 fn eval_formals_default() {
3925 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 1; }"), Value::Int(11));
3926 }
3927
3928 #[test]
3929 fn eval_formals_ellipsis() {
3930 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; }"), Value::Int(1));
3931 }
3932
3933 #[test]
3934 fn eval_named_formals() {
3935 assert_eq!(ev("(args @ { a }: args.a) { a = 42; }"), Value::Int(42));
3936 }
3937
3938 #[test]
3939 fn eval_rec_attrset() {
3940 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
3941 }
3942
3943 #[test]
3944 fn eval_negation() {
3945 assert_eq!(ev("-42"), Value::Int(-42));
3946 }
3947
3948 #[test]
3949 fn eval_float_arithmetic() {
3950 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
3951 assert_eq!(ev("1 + 1.5"), Value::Float(2.5));
3952 }
3953
3954 #[test]
3955 fn eval_division_by_zero() {
3956 assert!(eval("1 / 0").is_err());
3957 }
3958
3959 #[test]
3960 fn eval_builtins_available() {
3961 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
3962 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
3963 }
3964
3965 #[test]
3966 fn eval_builtins_length() {
3967 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
3968 }
3969
3970 #[test]
3971 fn eval_builtins_head_tail() {
3972 assert_eq!(ev("builtins.head [1 2 3]"), Value::Int(1));
3973 assert_eq!(ev("builtins.length (builtins.tail [1 2 3])"), Value::Int(2));
3974 }
3975
3976 #[test]
3977 fn eval_builtins_add() {
3978 assert_eq!(ev("builtins.add 1 2"), Value::Int(3));
3979 }
3980
3981 #[test]
3982 fn eval_builtins_to_string() {
3983 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
3984 }
3985
3986 #[test]
3987 fn eval_implication() {
3988 assert_eq!(ev("false -> true"), Value::Bool(true));
3989 assert_eq!(ev("true -> false"), Value::Bool(false));
3990 assert_eq!(ev("true -> true"), Value::Bool(true));
3991 }
3992
3993 #[test]
3996 fn eval_error_undefined_variable() {
3997 let result = eval("nonexistent");
3998 assert!(result.is_err());
3999 let msg = format!("{}", result.unwrap_err());
4000 assert!(msg.contains("undefined variable"));
4001 }
4002
4003 #[test]
4004 fn eval_error_type_mismatch_arithmetic() {
4005 let result = eval(r#"1 + "hello""#);
4006 assert!(result.is_err());
4007 let msg = format!("{}", result.unwrap_err());
4008 assert!(msg.contains("cannot add") || msg.contains("type"));
4009 }
4010
4011 #[test]
4012 fn eval_error_unexpected_argument() {
4013 let result = eval("({ a }: a) { a = 1; b = 2; }");
4014 assert!(result.is_err());
4015 let msg = format!("{}", result.unwrap_err());
4016 assert!(msg.contains("unexpected argument"));
4017 }
4018
4019 #[test]
4020 fn eval_error_missing_required_argument() {
4021 let result = eval("({ a, b }: a + b) { a = 1; }");
4022 assert!(result.is_err());
4023 let msg = format!("{}", result.unwrap_err());
4024 assert!(msg.contains("missing argument"));
4025 }
4026
4027 #[test]
4028 fn eval_builtins_attr_names_sorted() {
4029 let v = ev("builtins.attrNames { z = 1; a = 2; m = 3; }");
4030 assert_eq!(
4032 v,
4033 Value::list(vec![
4034 Value::string("a"),
4035 Value::string("m"),
4036 Value::string("z"),
4037 ]),
4038 );
4039 }
4040
4041 #[test]
4042 fn eval_builtins_attr_values() {
4043 let v = ev("builtins.attrValues { a = 1; b = 2; }");
4044 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2)]));
4046 }
4047
4048 #[test]
4049 fn eval_builtins_is_null() {
4050 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
4051 assert_eq!(ev("builtins.isNull 1"), Value::Bool(false));
4052 }
4053
4054 #[test]
4055 fn eval_builtins_is_int() {
4056 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
4057 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
4058 }
4059
4060 #[test]
4061 fn eval_builtins_is_bool() {
4062 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
4063 assert_eq!(ev("builtins.isBool 0"), Value::Bool(false));
4064 }
4065
4066 #[test]
4067 fn eval_builtins_is_string() {
4068 assert_eq!(ev(r#"builtins.isString "hi""#), Value::Bool(true));
4069 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
4070 }
4071
4072 #[test]
4073 fn eval_builtins_is_list() {
4074 assert_eq!(ev("builtins.isList [1 2]"), Value::Bool(true));
4075 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
4076 }
4077
4078 #[test]
4079 fn eval_builtins_is_attrs() {
4080 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
4081 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
4082 }
4083
4084 #[test]
4085 fn eval_builtins_string_length() {
4086 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
4087 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
4088 }
4089
4090 #[test]
4091 fn eval_builtins_to_json_roundtrip() {
4092 assert_eq!(
4094 ev(r#"builtins.fromJSON (builtins.toJSON 42)"#),
4095 Value::Int(42),
4096 );
4097 assert_eq!(
4098 ev(r#"builtins.fromJSON (builtins.toJSON [1 2 3])"#),
4099 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4100 );
4101 }
4102
4103 #[test]
4104 fn eval_builtins_from_json() {
4105 assert_eq!(
4106 ev(r#"builtins.fromJSON "{\"a\": 1}""#),
4107 {
4108 let mut attrs = NixAttrs::new();
4109 attrs.insert("a".to_string(), Value::Int(1));
4110 Value::Attrs(Rc::new(attrs))
4111 },
4112 );
4113 assert_eq!(ev(r#"builtins.fromJSON "null""#), Value::Null);
4114 assert_eq!(ev(r#"builtins.fromJSON "true""#), Value::Bool(true));
4115 }
4116
4117 #[test]
4118 fn eval_nested_function_application() {
4119 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
4121 assert_eq!(ev("((x: y: x + y) 1) 2"), Value::Int(3));
4123 }
4124
4125 #[test]
4126 fn eval_recursive_let() {
4127 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4128 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4129 }
4130
4131 #[test]
4132 fn eval_string_comparison() {
4133 assert_eq!(ev(r#""a" < "b""#), Value::Bool(true));
4134 assert_eq!(ev(r#""b" < "a""#), Value::Bool(false));
4135 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4136 assert_eq!(ev(r#""abc" != "def""#), Value::Bool(true));
4137 }
4138
4139 #[test]
4140 fn eval_list_in_attrset() {
4141 let v = ev("{ x = [1 2 3]; }.x");
4142 assert_eq!(
4143 v,
4144 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4145 );
4146 }
4147
4148 #[test]
4149 fn eval_nested_attrset_select() {
4150 assert_eq!(ev("{ a = { b = 42; }; }.a.b"), Value::Int(42));
4151 }
4152
4153 #[test]
4154 fn eval_let_shadows_outer() {
4155 assert_eq!(
4156 ev("let x = 1; in let x = 2; in x"),
4157 Value::Int(2),
4158 );
4159 }
4160
4161 #[test]
4162 fn eval_with_provides_scope() {
4163 assert_eq!(
4165 ev("with { x = 42; y = 10; }; x + y"),
4166 Value::Int(52),
4167 );
4168 }
4169
4170 #[test]
4171 fn eval_list_equality() {
4172 assert_eq!(ev("[1 2] == [1 2]"), Value::Bool(true));
4173 assert_eq!(ev("[1 2] == [1 3]"), Value::Bool(false));
4174 }
4175
4176 #[test]
4177 fn eval_attrset_equality() {
4178 assert_eq!(ev("{ a = 1; } == { a = 1; }"), Value::Bool(true));
4179 assert_eq!(ev("{ a = 1; } == { a = 2; }"), Value::Bool(false));
4180 }
4181
4182 #[test]
4187 fn literal_int_large_zero_negative() {
4188 assert_eq!(ev("9223372036854775807"), Value::Int(i64::MAX));
4190 assert_eq!(ev("0"), Value::Int(0));
4192 assert_eq!(ev("-1"), Value::Int(-1));
4194 assert_eq!(ev("-999999"), Value::Int(-999999));
4195 }
4196
4197 #[test]
4198 fn literal_float_small_large() {
4199 assert_eq!(ev("0.001"), Value::Float(0.001));
4200 assert_eq!(ev("999999.999"), Value::Float(999999.999));
4201 assert_eq!(ev("1.0e3"), Value::Float(1000.0));
4203 assert_eq!(ev("1.5e2"), Value::Float(150.0));
4204 }
4205
4206 #[test]
4207 fn literal_string_empty_and_escapes() {
4208 assert_eq!(ev(r#""""#), Value::string(""));
4209 assert_eq!(ev(r#""hello\nworld""#), Value::string("hello\nworld"));
4211 assert_eq!(ev(r#""tab\there""#), Value::string("tab\there"));
4212 }
4213
4214 #[test]
4215 fn literal_multiline_string() {
4216 assert_eq!(
4218 ev("''hello''"),
4219 Value::string("hello"),
4220 );
4221 assert_eq!(
4223 ev("''\n line1\n line2\n''"),
4224 Value::string("line1\nline2\n"),
4225 );
4226 }
4227
4228 #[test]
4229 fn literal_paths() {
4230 assert_eq!(ev("./foo"), Value::Path(Box::new(SmolStr::from("./foo"))));
4232 assert_eq!(ev("/nix/store/abc"), Value::Path(Box::new(SmolStr::from("/nix/store/abc"))));
4234 assert_eq!(ev("~/myfile"), Value::Path(Box::new(SmolStr::from("~/myfile"))));
4236 }
4237
4238 #[test]
4248 fn interp_path_abs_splices_and_types_path() {
4249 let v = ev(r#"let x = "foo"; in /a/${x}/b"#);
4251 assert_eq!(v, Value::Path(Box::new(SmolStr::from("/a/foo/b"))));
4252 }
4253
4254 #[test]
4255 fn interp_path_abs_multi_and_slash_in_value() {
4256 assert_eq!(
4258 ev(r#"let a = "x"; b = "y/z"; in /p/${a}/${b}.nix"#),
4259 Value::Path(Box::new(SmolStr::from("/p/x/y/z.nix"))),
4260 );
4261 }
4262
4263 #[test]
4264 fn interp_path_abs_normalizes_double_slash_seam() {
4265 assert_eq!(
4268 ev(r#"/bar/${/tmp/foo}"#),
4269 Value::Path(Box::new(SmolStr::from("/bar/tmp/foo"))),
4270 );
4271 }
4272
4273 #[test]
4274 fn interp_path_rel_resolves_against_eval_dir() {
4275 let _g = push_eval_file(std::path::PathBuf::from("/tmp/example/default.nix"));
4279 assert_eq!(
4280 ev(r#"let x = "foo"; in ./${x}.nix"#),
4281 Value::Path(Box::new(SmolStr::from("/tmp/example/foo.nix"))),
4282 );
4283 }
4284
4285 #[test]
4286 fn interp_path_rel_no_eval_dir_keeps_relative_text() {
4287 assert_eq!(
4290 ev(r#"let x = "foo"; in ./${x}.nix"#),
4291 Value::Path(Box::new(SmolStr::from("./foo.nix"))),
4292 );
4293 }
4294
4295 #[test]
4296 fn interp_path_home_splices_leading_tilde_preserved() {
4297 assert_eq!(
4301 ev(r#"let x = "foo"; in ~/${x}/bar"#),
4302 Value::Path(Box::new(SmolStr::from("~/foo/bar"))),
4303 );
4304 }
4305
4306 #[test]
4307 fn interp_path_non_interpolated_still_raw() {
4308 assert_eq!(ev("/a/b/c"), Value::Path(Box::new(SmolStr::from("/a/b/c"))));
4311 assert_eq!(ev("~/plain"), Value::Path(Box::new(SmolStr::from("~/plain"))));
4312 }
4313
4314 #[test]
4315 fn literal_null_true_false_standalone() {
4316 assert_eq!(ev("null"), Value::Null);
4317 assert_eq!(ev("true"), Value::Bool(true));
4318 assert_eq!(ev("false"), Value::Bool(false));
4319 }
4320
4321 #[test]
4326 fn op_arithmetic_int() {
4327 assert_eq!(ev("100 + 200"), Value::Int(300));
4328 assert_eq!(ev("50 - 30"), Value::Int(20));
4329 assert_eq!(ev("7 * 8"), Value::Int(56));
4330 assert_eq!(ev("17 / 3"), Value::Int(5)); }
4332
4333 #[test]
4334 fn op_arithmetic_float() {
4335 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
4336 assert_eq!(ev("5.0 - 1.5"), Value::Float(3.5));
4337 assert_eq!(ev("2.0 * 3.0"), Value::Float(6.0));
4338 assert_eq!(ev("7.0 / 2.0"), Value::Float(3.5));
4339 }
4340
4341 #[test]
4342 fn op_arithmetic_mixed_int_float() {
4343 assert_eq!(ev("1 + 2.5"), Value::Float(3.5));
4345 assert_eq!(ev("2.5 + 1"), Value::Float(3.5));
4346 assert_eq!(ev("2 * 1.5"), Value::Float(3.0));
4348 assert_eq!(ev("5.5 - 2"), Value::Float(3.5));
4350 }
4351
4352 #[test]
4353 fn op_string_concat() {
4354 assert_eq!(ev(r#""foo" + "bar""#), Value::string("foobar"));
4355 assert_eq!(ev(r#""" + "x""#), Value::string("x"));
4356 assert_eq!(ev(r#""a" + "" + "b""#), Value::string("ab"));
4357 }
4358
4359 #[test]
4360 fn op_path_concat() {
4361 assert_eq!(ev(r#"./foo + "/bar""#), Value::Path(Box::new(SmolStr::from("./foo/bar"))));
4363 assert_eq!(ev("./a + ./b"), Value::Path(Box::new(SmolStr::from("./a/./b"))));
4365 }
4366
4367 #[test]
4368 fn op_comparison_ints() {
4369 assert_eq!(ev("1 < 2"), Value::Bool(true));
4370 assert_eq!(ev("2 < 1"), Value::Bool(false));
4371 assert_eq!(ev("2 > 1"), Value::Bool(true));
4372 assert_eq!(ev("1 > 2"), Value::Bool(false));
4373 assert_eq!(ev("2 <= 2"), Value::Bool(true));
4374 assert_eq!(ev("3 <= 2"), Value::Bool(false));
4375 assert_eq!(ev("2 >= 2"), Value::Bool(true));
4376 assert_eq!(ev("1 >= 2"), Value::Bool(false));
4377 }
4378
4379 #[test]
4380 fn op_comparison_floats() {
4381 assert_eq!(ev("1.5 < 2.5"), Value::Bool(true));
4382 assert_eq!(ev("2.5 > 1.5"), Value::Bool(true));
4383 assert_eq!(ev("1.5 <= 1.5"), Value::Bool(true));
4384 assert_eq!(ev("1.5 >= 1.5"), Value::Bool(true));
4385 }
4386
4387 #[test]
4388 fn op_comparison_strings() {
4389 assert_eq!(ev(r#""apple" < "banana""#), Value::Bool(true));
4390 assert_eq!(ev(r#""banana" > "apple""#), Value::Bool(true));
4391 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4392 assert_eq!(ev(r#""abc" != "xyz""#), Value::Bool(true));
4393 assert_eq!(ev(r#""abc" <= "abd""#), Value::Bool(true));
4394 assert_eq!(ev(r#""abc" >= "abb""#), Value::Bool(true));
4395 }
4396
4397 #[test]
4398 fn op_equality_various_types() {
4399 assert_eq!(ev("null == null"), Value::Bool(true));
4400 assert_eq!(ev("true == true"), Value::Bool(true));
4401 assert_eq!(ev("false == false"), Value::Bool(true));
4402 assert_eq!(ev("true == false"), Value::Bool(false));
4403 assert_eq!(ev("1 == 1"), Value::Bool(true));
4404 assert_eq!(ev("1 != 2"), Value::Bool(true));
4405 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
4407 assert_eq!(ev("null == false"), Value::Bool(false));
4408 }
4409
4410 #[test]
4411 fn op_logic_short_circuit() {
4412 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
4414 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
4416 }
4417
4418 #[test]
4419 fn op_logic_full() {
4420 assert_eq!(ev("true && true"), Value::Bool(true));
4421 assert_eq!(ev("true && false"), Value::Bool(false));
4422 assert_eq!(ev("false && true"), Value::Bool(false));
4423 assert_eq!(ev("false && false"), Value::Bool(false));
4424 assert_eq!(ev("true || true"), Value::Bool(true));
4425 assert_eq!(ev("true || false"), Value::Bool(true));
4426 assert_eq!(ev("false || true"), Value::Bool(true));
4427 assert_eq!(ev("false || false"), Value::Bool(false));
4428 assert_eq!(ev("!true"), Value::Bool(false));
4429 assert_eq!(ev("!false"), Value::Bool(true));
4430 }
4431
4432 #[test]
4433 fn op_implication_truth_table() {
4434 assert_eq!(ev("false -> false"), Value::Bool(true));
4436 assert_eq!(ev("false -> true"), Value::Bool(true));
4437 assert_eq!(ev("true -> true"), Value::Bool(true));
4439 assert_eq!(ev("true -> false"), Value::Bool(false));
4440 }
4441
4442 #[test]
4443 fn op_implication_short_circuit() {
4444 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
4446 }
4447
4448 #[test]
4449 fn op_update_merge() {
4450 let v = ev("{ a = 1; } // { b = 2; }");
4451 if let Value::Attrs(attrs) = v {
4452 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4453 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4454 } else {
4455 panic!("expected attrs");
4456 }
4457 }
4458
4459 #[test]
4460 fn op_update_right_wins() {
4461 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4462 }
4463
4464 #[test]
4465 fn op_list_concat() {
4466 assert_eq!(
4467 ev("[1 2] ++ [3 4]"),
4468 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]),
4469 );
4470 assert_eq!(ev("[] ++ [1]"), Value::list(vec![Value::Int(1)]));
4472 assert_eq!(ev("[1] ++ []"), Value::list(vec![Value::Int(1)]));
4473 }
4474
4475 #[test]
4476 fn op_has_attr_present_and_absent() {
4477 assert_eq!(ev("{ x = 1; y = 2; } ? x"), Value::Bool(true));
4478 assert_eq!(ev("{ x = 1; } ? z"), Value::Bool(false));
4479 assert_eq!(ev("{} ? anything"), Value::Bool(false));
4480 }
4481
4482 #[test]
4483 fn op_unary_negate() {
4484 assert_eq!(ev("-42"), Value::Int(-42));
4485 assert_eq!(ev("-3.14"), Value::Float(-3.14));
4486 assert_eq!(ev("- -5"), Value::Int(5));
4488 }
4489
4490 #[test]
4495 fn control_if_true_branch() {
4496 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
4497 }
4498
4499 #[test]
4500 fn control_if_false_branch() {
4501 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
4502 }
4503
4504 #[test]
4505 fn control_if_nested() {
4506 assert_eq!(
4507 ev("if true then (if false then 1 else 2) else 3"),
4508 Value::Int(2),
4509 );
4510 assert_eq!(
4511 ev("if false then 1 else (if true then 2 else 3)"),
4512 Value::Int(2),
4513 );
4514 }
4515
4516 #[test]
4517 fn control_assert_passing() {
4518 assert_eq!(ev("assert 1 == 1; 42"), Value::Int(42));
4519 assert_eq!(ev("assert true; true"), Value::Bool(true));
4520 }
4521
4522 #[test]
4523 fn control_assert_failing() {
4524 assert!(eval("assert false; 42").is_err());
4525 assert!(eval("assert 1 == 2; 42").is_err());
4526 }
4527
4528 #[test]
4529 fn control_with_basic_scope() {
4530 assert_eq!(ev("with { a = 1; b = 2; }; a + b"), Value::Int(3));
4531 }
4532
4533 #[test]
4534 fn control_with_lexical_precedence() {
4535 assert_eq!(
4537 ev("let x = 10; in with { x = 99; }; x"),
4538 Value::Int(10),
4539 );
4540 }
4541
4542 #[test]
4543 fn control_with_nested() {
4544 assert_eq!(
4545 ev("with { a = 1; }; with { b = 2; }; a + b"),
4546 Value::Int(3),
4547 );
4548 }
4549
4550 #[test]
4551 fn control_with_lazy_fix_self() {
4552 let result = eval(
4557 "let fix = f: let x = f x; in x; in fix (self: with self; { a = 1; b = a + 1; })"
4558 );
4559 assert!(result.is_ok(), "fix with self should work: {:?}", result);
4560 if let Ok(Value::Attrs(attrs)) = result {
4561 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4562 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4563 } else {
4564 panic!("expected Attrs, got {:?}", result);
4565 }
4566 }
4567
4568 #[test]
4569 fn control_with_lazy_fix_self_lib_pattern() {
4570 let result = eval(r#"
4573 let fix = f: let x = f x; in x;
4574 in (fix (self: with self; {
4575 lib = { version = "1.0"; };
4576 hello = "hello ${lib.version}";
4577 })).hello
4578 "#);
4579 assert!(result.is_ok(), "nixpkgs-style lib pattern: {:?}", result);
4580 assert_eq!(
4581 result.unwrap(),
4582 Value::String(Rc::new(NixString::plain("hello 1.0"))),
4583 );
4584 }
4585
4586 #[test]
4587 fn control_with_non_attrset_errors() {
4588 let result = eval("with 42; 1");
4590 assert_eq!(result.unwrap(), Value::Int(1));
4593 }
4594
4595 #[test]
4596 fn control_with_non_attrset_lookup_falls_through() {
4597 let result = eval("let x = 1; in with 42; x");
4600 assert_eq!(result.unwrap(), Value::Int(1));
4601 }
4602
4603 #[test]
4604 fn control_let_simple_and_multiple() {
4605 assert_eq!(ev("let x = 5; in x"), Value::Int(5));
4606 assert_eq!(ev("let x = 1; y = 2; z = 3; in x + y + z"), Value::Int(6));
4607 }
4608
4609 #[test]
4610 fn control_let_shadow_outer() {
4611 assert_eq!(
4612 ev("let x = 1; in let x = 2; in x"),
4613 Value::Int(2),
4614 );
4615 }
4616
4617 #[test]
4618 fn control_let_recursive_reference() {
4619 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4620 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4621 }
4622
4623 #[test]
4624 fn control_nested_let_expression() {
4625 assert_eq!(
4626 ev("let a = let b = 1; in b; in a"),
4627 Value::Int(1),
4628 );
4629 assert_eq!(
4630 ev("let a = let b = 10; in b + 5; in a * 2"),
4631 Value::Int(30),
4632 );
4633 }
4634
4635 #[test]
4640 fn func_identity_lambda() {
4641 assert_eq!(ev("(x: x) 42"), Value::Int(42));
4642 assert_eq!(ev(r#"(x: x) "hello""#), Value::string("hello"));
4643 }
4644
4645 #[test]
4646 fn func_curried_two_args() {
4647 assert_eq!(ev("(x: y: x + y) 3 4"), Value::Int(7));
4648 }
4649
4650 #[test]
4651 fn func_curried_three_args() {
4652 assert_eq!(ev("(a: b: c: a + b + c) 1 2 3"), Value::Int(6));
4653 }
4654
4655 #[test]
4656 fn func_formals_basic() {
4657 assert_eq!(ev("({ a, b }: a + b) { a = 3; b = 7; }"), Value::Int(10));
4658 }
4659
4660 #[test]
4661 fn func_formals_with_defaults() {
4662 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; }"), Value::Int(15));
4663 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; b = 20; }"), Value::Int(25));
4665 }
4666
4667 #[test]
4668 fn func_formals_with_ellipsis() {
4669 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; c = 3; }"), Value::Int(1));
4670 }
4671
4672 #[test]
4673 fn func_named_formals_at_before() {
4674 assert_eq!(
4676 ev("(args @ { a, b }: args.a + args.b) { a = 3; b = 4; }"),
4677 Value::Int(7),
4678 );
4679 }
4680
4681 #[test]
4682 fn func_named_formals_at_after() {
4683 assert_eq!(
4685 ev("({ a, b } @ args: args.a + args.b) { a = 10; b = 20; }"),
4686 Value::Int(30),
4687 );
4688 }
4689
4690 #[test]
4691 fn func_nested_application() {
4692 assert_eq!(ev("((x: y: x * y) 3) 4"), Value::Int(12));
4694 }
4695
4696 #[test]
4697 fn func_higher_order_map() {
4698 assert_eq!(
4699 ev("builtins.map (x: x * 2) [1 2 3]"),
4700 Value::list(vec![Value::Int(2), Value::Int(4), Value::Int(6)]),
4701 );
4702 }
4703
4704 #[test]
4705 fn func_higher_order_filter() {
4706 assert_eq!(
4707 ev("builtins.filter (x: x > 2) [1 2 3 4 5]"),
4708 Value::list(vec![Value::Int(3), Value::Int(4), Value::Int(5)]),
4709 );
4710 }
4711
4712 #[test]
4713 fn func_higher_order_foldl() {
4714 assert_eq!(
4716 ev("builtins.foldl' (acc: x: acc + x) 0 [1 2 3 4]"),
4717 Value::Int(10),
4718 );
4719 }
4720
4721 #[test]
4722 fn func_as_attrset_value() {
4723 assert_eq!(
4724 ev("let s = { f = x: x + 1; }; in s.f 5"),
4725 Value::Int(6),
4726 );
4727 }
4728
4729 #[test]
4730 fn func_immediate_application() {
4731 assert_eq!(ev("(x: x * x) 7"), Value::Int(49));
4732 }
4733
4734 #[test]
4735 fn func_in_let_binding() {
4736 assert_eq!(
4737 ev("let double = x: x * 2; in double 21"),
4738 Value::Int(42),
4739 );
4740 }
4741
4742 #[test]
4747 fn attrs_empty_set() {
4748 let v = ev("{}");
4749 if let Value::Attrs(attrs) = v {
4750 assert!(attrs.is_empty());
4751 } else {
4752 panic!("expected attrs");
4753 }
4754 }
4755
4756 #[test]
4757 fn attrs_simple() {
4758 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
4759 }
4760
4761 #[test]
4762 fn attrs_nested_access() {
4763 assert_eq!(ev("{ a = { b = { c = 42; }; }; }.a.b.c"), Value::Int(42));
4764 }
4765
4766 #[test]
4767 fn attrs_recursive_set() {
4768 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
4769 }
4770
4771 #[test]
4772 fn attrs_update_disjoint() {
4773 let v = ev("{ a = 1; } // { b = 2; }");
4774 if let Value::Attrs(attrs) = v {
4775 assert_eq!(attrs.len(), 2);
4776 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4777 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4778 } else {
4779 panic!("expected attrs");
4780 }
4781 }
4782
4783 #[test]
4784 fn attrs_update_override() {
4785 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4786 }
4787
4788 #[test]
4789 fn attrs_has_attr_operator() {
4790 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
4791 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
4792 }
4793
4794 #[test]
4795 fn attrs_select_with_default() {
4796 assert_eq!(ev("{ a = 1; }.a or 99"), Value::Int(1));
4797 assert_eq!(ev("{}.missing or 99"), Value::Int(99));
4798 assert_eq!(ev("{ a = 1; }.b or 42"), Value::Int(42));
4799 }
4800
4801 #[test]
4802 fn attrs_nested_attr_path_in_binding() {
4803 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
4805 }
4806
4807 #[test]
4808 fn attrs_inherit_from_scope() {
4809 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.x"), Value::Int(1));
4810 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.y"), Value::Int(2));
4811 }
4812
4813 #[test]
4814 fn attrs_inherit_from_expr() {
4815 assert_eq!(
4816 ev("{ inherit ({ a = 42; b = 10; }) a; }.a"),
4817 Value::Int(42),
4818 );
4819 }
4820
4821 #[test]
4822 fn attrs_dynamic_attr_name() {
4823 assert_eq!(
4824 ev(r#"let name = "x"; in { ${name} = 42; }.x"#),
4825 Value::Int(42),
4826 );
4827 }
4828
4829 #[test]
4830 fn attrs_attr_names_sorted() {
4831 assert_eq!(
4832 ev("builtins.attrNames { z = 1; m = 2; a = 3; }"),
4833 Value::list(vec![
4834 Value::string("a"),
4835 Value::string("m"),
4836 Value::string("z"),
4837 ]),
4838 );
4839 }
4840
4841 #[test]
4842 fn attrs_attr_values_follow_key_order() {
4843 assert_eq!(
4845 ev("builtins.attrValues { c = 3; a = 1; b = 2; }"),
4846 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4847 );
4848 }
4849
4850 #[test]
4851 fn attrs_update_is_shallow() {
4852 assert_eq!(
4854 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a ? x"),
4855 Value::Bool(false),
4856 );
4857 assert_eq!(
4858 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a.y"),
4859 Value::Int(2),
4860 );
4861 }
4862
4863 #[test]
4868 fn list_empty() {
4869 assert_eq!(ev("[]"), Value::list(vec![]));
4870 }
4871
4872 #[test]
4873 fn list_single_element() {
4874 assert_eq!(ev("[1]"), Value::list(vec![Value::Int(1)]));
4875 }
4876
4877 #[test]
4878 fn list_mixed_types() {
4879 assert_eq!(
4880 ev(r#"[1 "two" true null]"#),
4881 Value::list(vec![
4882 Value::Int(1),
4883 Value::string("two"),
4884 Value::Bool(true),
4885 Value::Null,
4886 ]),
4887 );
4888 }
4889
4890 #[test]
4891 fn list_nested() {
4892 assert_eq!(
4893 ev("[[1 2] [3 4]]"),
4894 Value::list(vec![
4895 Value::list(vec![Value::Int(1), Value::Int(2)]),
4896 Value::list(vec![Value::Int(3), Value::Int(4)]),
4897 ]),
4898 );
4899 }
4900
4901 #[test]
4902 fn list_concat_operator() {
4903 assert_eq!(
4904 ev("[1] ++ [2] ++ [3]"),
4905 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4906 );
4907 }
4908
4909 #[test]
4910 fn list_builtins_length() {
4911 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
4912 assert_eq!(ev("builtins.length []"), Value::Int(0));
4913 }
4914
4915 #[test]
4916 fn list_builtins_elem_at() {
4917 assert_eq!(ev("builtins.elemAt [10 20 30] 0"), Value::Int(10));
4918 assert_eq!(ev("builtins.elemAt [10 20 30] 1"), Value::Int(20));
4919 assert_eq!(ev("builtins.elemAt [10 20 30] 2"), Value::Int(30));
4920 }
4921
4922 #[test]
4923 fn list_equality() {
4924 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
4925 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
4926 assert_eq!(ev("[] == []"), Value::Bool(true));
4927 }
4928
4929 #[test]
4934 fn interp_simple_variable() {
4935 assert_eq!(
4936 ev(r#"let name = "world"; in "hello ${name}""#),
4937 Value::string("hello world"),
4938 );
4939 }
4940
4941 #[test]
4942 fn interp_nested_expression() {
4943 assert_eq!(
4944 ev(r#""result: ${builtins.toString (1 + 2)}""#),
4945 Value::string("result: 3"),
4946 );
4947 }
4948
4949 #[test]
4950 fn interp_int_coercion() {
4951 assert_eq!(
4953 ev(r#"let x = 42; in "count: ${builtins.toString x}""#),
4954 Value::string("count: 42"),
4955 );
4956 }
4957
4958 #[test]
4959 fn interp_multiple() {
4960 assert_eq!(
4961 ev(r#"let a = "foo"; b = "bar"; in "${a} and ${b}""#),
4962 Value::string("foo and bar"),
4963 );
4964 }
4965
4966 #[test]
4967 fn interp_in_let() {
4968 assert_eq!(
4969 ev(r#"let x = "world"; in "hello ${x}""#),
4970 Value::string("hello world"),
4971 );
4972 }
4973
4974 #[test]
4975 fn interp_empty_result() {
4976 assert_eq!(
4977 ev(r#"let x = ""; in "a${x}b""#),
4978 Value::string("ab"),
4979 );
4980 }
4981
4982 #[test]
4983 fn interp_path_in_string_context() {
4984 assert!(eval(r#""path: ${./foo-nonexistent-xyz}""#).is_err());
4990 }
4991
4992 #[test]
4993 fn interp_adjacent_interpolations() {
4994 assert_eq!(
4995 ev(r#"let a = "x"; b = "y"; in "${a}${b}""#),
4996 Value::string("xy"),
4997 );
4998 }
4999
5000 #[test]
5005 fn builtins_map_filter_foldl() {
5006 assert_eq!(
5008 ev("builtins.map (x: x + 10) [1 2 3]"),
5009 Value::list(vec![Value::Int(11), Value::Int(12), Value::Int(13)]),
5010 );
5011 assert_eq!(
5013 ev("builtins.filter (x: x > 1) [1 2 3]"),
5014 Value::list(vec![Value::Int(2), Value::Int(3)]),
5015 );
5016 assert_eq!(
5018 ev("builtins.foldl' (a: b: a * b) 1 [2 3 4]"),
5019 Value::Int(24),
5020 );
5021 }
5022
5023 #[test]
5024 fn builtins_map_attrs() {
5025 assert_eq!(
5026 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).a"),
5027 Value::Int(2),
5028 );
5029 assert_eq!(
5030 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).b"),
5031 Value::Int(4),
5032 );
5033 }
5034
5035 #[test]
5036 fn builtins_list_to_attrs() {
5037 assert_eq!(
5038 ev(r#"(builtins.listToAttrs [{ name = "x"; value = 1; } { name = "y"; value = 2; }]).x"#),
5039 Value::Int(1),
5040 );
5041 }
5042
5043 #[test]
5044 fn builtins_list_to_attrs_duplicate_key_first_wins() {
5045 assert_eq!(
5054 ev(r#"(builtins.listToAttrs [{ name = "k"; value = 1; } { name = "k"; value = 2; }]).k"#),
5055 Value::Int(1),
5056 );
5057 }
5058
5059 #[test]
5060 fn builtins_concat_map() {
5061 assert_eq!(
5062 ev("builtins.concatMap (x: [x (x * 2)]) [1 2 3]"),
5063 Value::list(vec![
5064 Value::Int(1), Value::Int(2),
5065 Value::Int(2), Value::Int(4),
5066 Value::Int(3), Value::Int(6),
5067 ]),
5068 );
5069 }
5070
5071 #[test]
5072 fn builtins_concat_lists() {
5073 assert_eq!(
5074 ev("builtins.concatLists [[1 2] [3] [4 5]]"),
5075 Value::list(vec![
5076 Value::Int(1), Value::Int(2), Value::Int(3),
5077 Value::Int(4), Value::Int(5),
5078 ]),
5079 );
5080 }
5081
5082 #[test]
5083 fn builtins_concat_strings_sep() {
5084 assert_eq!(
5085 ev(r#"builtins.concatStringsSep ", " ["a" "b" "c"]"#),
5086 Value::string("a, b, c"),
5087 );
5088 assert_eq!(
5089 ev(r#"builtins.concatStringsSep "" ["x" "y"]"#),
5090 Value::string("xy"),
5091 );
5092 }
5093
5094 #[test]
5095 fn builtins_replace_strings() {
5096 assert_eq!(
5097 ev(r#"builtins.replaceStrings ["o"] ["0"] "foobar""#),
5098 Value::string("f00bar"),
5099 );
5100 assert_eq!(
5101 ev(r#"builtins.replaceStrings ["hello"] ["goodbye"] "hello world""#),
5102 Value::string("goodbye world"),
5103 );
5104 }
5105
5106 #[test]
5107 fn builtins_has_prefix_has_suffix() {
5108 assert_eq!(ev(r#"builtins.hasPrefix "he" "hello""#), Value::Bool(true));
5109 assert_eq!(ev(r#"builtins.hasPrefix "xx" "hello""#), Value::Bool(false));
5110 assert_eq!(ev(r#"builtins.hasSuffix "lo" "hello""#), Value::Bool(true));
5111 assert_eq!(ev(r#"builtins.hasSuffix "xx" "hello""#), Value::Bool(false));
5112 }
5113
5114 #[test]
5115 fn builtins_all_any() {
5116 assert_eq!(ev("builtins.all (x: x > 0) [1 2 3]"), Value::Bool(true));
5117 assert_eq!(ev("builtins.all (x: x > 1) [1 2 3]"), Value::Bool(false));
5118 assert_eq!(ev("builtins.any (x: x > 2) [1 2 3]"), Value::Bool(true));
5119 assert_eq!(ev("builtins.any (x: x > 5) [1 2 3]"), Value::Bool(false));
5120 }
5121
5122 #[test]
5123 fn builtins_sort() {
5124 assert_eq!(
5125 ev("builtins.sort (a: b: a < b) [3 1 2]"),
5126 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5127 );
5128 }
5129
5130 #[test]
5131 fn builtins_remove_attrs() {
5132 let v = ev(r#"builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b" "c"]"#);
5133 if let Value::Attrs(attrs) = v {
5134 assert_eq!(attrs.len(), 1);
5135 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
5136 assert!(attrs.get("b").is_none());
5137 } else {
5138 panic!("expected attrs");
5139 }
5140 }
5141
5142 #[test]
5143 fn builtins_intersect_attrs() {
5144 let v = ev("builtins.intersectAttrs { a = 1; b = 2; } { b = 20; c = 30; }");
5145 if let Value::Attrs(attrs) = v {
5146 assert_eq!(attrs.len(), 1);
5147 assert_eq!(attrs.get("b"), Some(&Value::Int(20)));
5149 } else {
5150 panic!("expected attrs");
5151 }
5152 }
5153
5154 #[test]
5155 fn builtins_type_of_all_types() {
5156 assert_eq!(ev("builtins.typeOf null"), Value::string("null"));
5157 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
5158 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
5159 assert_eq!(ev("builtins.typeOf 3.14"), Value::string("float"));
5160 assert_eq!(ev(r#"builtins.typeOf "hi""#), Value::string("string"));
5161 assert_eq!(ev("builtins.typeOf [1]"), Value::string("list"));
5162 assert_eq!(ev("builtins.typeOf {}"), Value::string("set"));
5163 assert_eq!(ev("builtins.typeOf (x: x)"), Value::string("lambda"));
5164 }
5165
5166 #[test]
5167 fn builtins_is_type_checks() {
5168 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
5169 assert_eq!(ev("builtins.isNull 0"), Value::Bool(false));
5170 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
5171 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
5172 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
5173 assert_eq!(ev("builtins.isBool 1"), Value::Bool(false));
5174 assert_eq!(ev(r#"builtins.isString "x""#), Value::Bool(true));
5175 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
5176 assert_eq!(ev("builtins.isList []"), Value::Bool(true));
5177 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
5178 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
5179 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
5180 assert_eq!(ev("builtins.isFunction (x: x)"), Value::Bool(true));
5181 assert_eq!(ev("builtins.isFunction 1"), Value::Bool(false));
5182 assert_eq!(ev("builtins.isFloat 3.14"), Value::Bool(true));
5183 assert_eq!(ev("builtins.isFloat 1"), Value::Bool(false));
5184 }
5185
5186 #[test]
5187 fn builtins_to_json_from_json_roundtrip() {
5188 assert_eq!(ev("builtins.fromJSON (builtins.toJSON 42)"), Value::Int(42));
5190 assert_eq!(
5192 ev(r#"builtins.fromJSON (builtins.toJSON "hello")"#),
5193 Value::string("hello"),
5194 );
5195 assert_eq!(
5197 ev("builtins.fromJSON (builtins.toJSON [1 2 3])"),
5198 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5199 );
5200 assert_eq!(ev("builtins.fromJSON (builtins.toJSON null)"), Value::Null);
5202 assert_eq!(ev("builtins.fromJSON (builtins.toJSON true)"), Value::Bool(true));
5204 }
5205
5206 #[test]
5207 fn builtins_to_string_various() {
5208 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
5209 assert_eq!(ev("builtins.toString true"), Value::string("1"));
5210 assert_eq!(ev("builtins.toString false"), Value::string(""));
5211 assert_eq!(ev("builtins.toString null"), Value::string(""));
5212 assert_eq!(ev(r#"builtins.toString "hello""#), Value::string("hello"));
5213 }
5214
5215 #[test]
5216 fn builtins_function_args() {
5217 let v = ev("builtins.functionArgs ({ a, b ? 1 }: a)");
5218 if let Value::Attrs(attrs) = v {
5219 assert_eq!(attrs.get("a"), Some(&Value::Bool(false))); assert_eq!(attrs.get("b"), Some(&Value::Bool(true))); } else {
5222 panic!("expected attrs");
5223 }
5224 }
5225
5226 #[test]
5227 fn builtins_gen_list() {
5228 assert_eq!(
5229 ev("builtins.genList (x: x * x) 5"),
5230 Value::list(vec![
5231 Value::Int(0), Value::Int(1), Value::Int(4),
5232 Value::Int(9), Value::Int(16),
5233 ]),
5234 );
5235 assert_eq!(ev("builtins.genList (x: x) 0"), Value::list(vec![]));
5236 }
5237
5238 #[test]
5239 fn builtins_elem() {
5240 assert_eq!(ev("builtins.elem 2 [1 2 3]"), Value::Bool(true));
5241 assert_eq!(ev("builtins.elem 5 [1 2 3]"), Value::Bool(false));
5242 assert_eq!(ev("builtins.elem 1 []"), Value::Bool(false));
5243 }
5244
5245 #[test]
5246 fn builtins_head_tail() {
5247 assert_eq!(ev("builtins.head [10 20 30]"), Value::Int(10));
5248 assert_eq!(
5249 ev("builtins.tail [10 20 30]"),
5250 Value::list(vec![Value::Int(20), Value::Int(30)]),
5251 );
5252 }
5253
5254 #[test]
5255 fn builtins_string_length() {
5256 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
5257 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
5258 assert_eq!(ev(r#"builtins.stringLength "abc def""#), Value::Int(7));
5259 }
5260
5261 #[test]
5262 fn builtins_ceil_floor() {
5263 assert_eq!(ev("builtins.ceil 2.3"), Value::Int(3));
5264 assert_eq!(ev("builtins.ceil 2.0"), Value::Int(2));
5265 assert_eq!(ev("builtins.floor 2.9"), Value::Int(2));
5266 assert_eq!(ev("builtins.floor 2.0"), Value::Int(2));
5267 assert_eq!(ev("builtins.ceil 5"), Value::Int(5));
5269 assert_eq!(ev("builtins.floor 5"), Value::Int(5));
5270 }
5271
5272 #[test]
5273 fn builtins_try_eval() {
5274 let v = ev("builtins.tryEval 42");
5275 if let Value::Attrs(attrs) = v {
5276 assert_eq!(attrs.get("success"), Some(&Value::Bool(true)));
5277 assert_eq!(attrs.get("value"), Some(&Value::Int(42)));
5278 } else {
5279 panic!("expected attrs");
5280 }
5281 }
5282
5283 #[test]
5284 fn builtins_throw() {
5285 let result = eval(r#"builtins.throw "oops""#);
5286 assert!(result.is_err());
5287 let msg = format!("{}", result.unwrap_err());
5288 assert!(msg.contains("oops"));
5289 }
5290
5291 #[test]
5292 fn builtins_seq_deep_seq() {
5293 assert_eq!(ev("builtins.seq 1 42"), Value::Int(42));
5295 assert_eq!(ev("builtins.deepSeq [1 2 3] 99"), Value::Int(99));
5297 }
5298
5299 #[test]
5300 fn builtins_current_system() {
5301 let v = ev("builtins.currentSystem");
5302 if let Value::String(ns) = v {
5303 let s = &ns.chars;
5304 assert!(
5306 s == "aarch64-darwin"
5307 || s == "x86_64-darwin"
5308 || s == "aarch64-linux"
5309 || s == "x86_64-linux",
5310 "unexpected system: {s}",
5311 );
5312 } else {
5313 panic!("expected string");
5314 }
5315 }
5316
5317 #[test]
5322 fn pattern_mkif_like() {
5323 assert_eq!(
5325 ev("(if true then { x = 1; } else {}).x"),
5326 Value::Int(1),
5327 );
5328 let v = ev("if false then { x = 1; } else {}");
5329 if let Value::Attrs(attrs) = v {
5330 assert!(attrs.is_empty());
5331 } else {
5332 panic!("expected attrs");
5333 }
5334 }
5335
5336 #[test]
5337 fn pattern_optional_attrs() {
5338 assert_eq!(
5340 ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in (optionalAttrs true { a = 1; }).a"),
5341 Value::Int(1),
5342 );
5343 let v = ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in optionalAttrs false { a = 1; }");
5344 if let Value::Attrs(attrs) = v {
5345 assert!(attrs.is_empty());
5346 } else {
5347 panic!("expected attrs");
5348 }
5349 }
5350
5351 #[test]
5352 fn pattern_filter_attrs_via_remove() {
5353 assert_eq!(
5355 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]).a"#),
5356 Value::Int(1),
5357 );
5358 assert_eq!(
5359 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]) ? b"#),
5360 Value::Bool(false),
5361 );
5362 }
5363
5364 #[test]
5365 fn pattern_override() {
5366 let v = ev(r#"
5368 let
5369 defaults = { debug = false; port = 8080; host = "localhost"; };
5370 overrides = { debug = true; port = 9090; };
5371 in defaults // overrides
5372 "#);
5373 if let Value::Attrs(attrs) = v {
5374 assert_eq!(attrs.get("debug"), Some(&Value::Bool(true)));
5375 assert_eq!(attrs.get("port"), Some(&Value::Int(9090)));
5376 assert_eq!(attrs.get("host"), Some(&Value::string("localhost")));
5377 } else {
5378 panic!("expected attrs");
5379 }
5380 }
5381
5382 #[test]
5383 fn pattern_functor() {
5384 assert_eq!(
5386 ev("let s = { __functor = self: x: self.value + x; value = 10; }; in s 5"),
5387 Value::Int(15),
5388 );
5389 }
5390
5391 #[test]
5392 fn pattern_platform_check() {
5393 let v = ev(r#"if builtins.currentSystem == "aarch64-darwin" then "arm" else "other""#);
5395 if let Value::String(_) = v {
5397 } else {
5399 panic!("expected string");
5400 }
5401 }
5402
5403 #[test]
5404 fn pattern_recursive_overlay_lambda_structure() {
5405 let v = ev("let overlay = self: super: { pkg = 42; }; in overlay {} {}");
5407 if let Value::Attrs(attrs) = v {
5408 assert_eq!(attrs.get("pkg"), Some(&Value::Int(42)));
5409 } else {
5410 panic!("expected attrs");
5411 }
5412 }
5413
5414 #[test]
5415 fn pattern_call_package_simplified() {
5416 assert_eq!(
5418 ev("let callPkg = f: f { lib = { id = x: x; }; }; lib = { id = x: x; }; in callPkg ({ lib }: lib.id 42)"),
5419 Value::Int(42),
5420 );
5421 }
5422
5423 #[test]
5424 fn pattern_derivation_like_attrset() {
5425 let v = ev(r#"{ type = "derivation"; name = "hello"; system = builtins.currentSystem; builder = "/bin/sh"; }"#);
5426 if let Value::Attrs(attrs) = v {
5427 assert_eq!(attrs.get("type"), Some(&Value::string("derivation")));
5428 assert_eq!(attrs.get("name"), Some(&Value::string("hello")));
5429 assert_eq!(attrs.get("builder"), Some(&Value::string("/bin/sh")));
5430 let system = force_value(attrs.get("system").unwrap()).unwrap();
5432 assert!(matches!(system, Value::String(_)), "expected string, got {system:?}");
5433 } else {
5434 panic!("expected attrs");
5435 }
5436 }
5437
5438 #[test]
5439 fn pattern_module_system_simplified() {
5440 assert_eq!(
5442 ev(r#"
5443 let
5444 eval = m: m { config = {}; lib = { mkDefault = x: x; }; };
5445 in eval ({ config, lib }: { result = lib.mkDefault 42; })
5446 "#),
5447 {
5448 let mut attrs = NixAttrs::new();
5449 attrs.insert("result".to_string(), Value::Int(42));
5450 Value::Attrs(Rc::new(attrs))
5451 },
5452 );
5453 }
5454
5455 #[test]
5460 fn error_undefined_variable() {
5461 let result = eval("nonexistent_var");
5462 assert!(result.is_err());
5463 let msg = format!("{}", result.unwrap_err());
5464 assert!(msg.contains("undefined variable") || msg.contains("nonexistent_var"));
5465 }
5466
5467 #[test]
5468 fn error_type_mismatch_arithmetic() {
5469 let result = eval(r#"1 + "hello""#);
5470 assert!(result.is_err());
5471 }
5472
5473 #[test]
5474 fn error_missing_attribute() {
5475 let result = eval("{}.nonexistent");
5476 assert!(result.is_err());
5477 let msg = format!("{}", result.unwrap_err());
5478 assert!(msg.contains("nonexistent") || msg.contains("not found"));
5479 }
5480
5481 #[test]
5482 fn error_division_by_zero() {
5483 assert!(eval("1 / 0").is_err());
5484 assert!(eval("100 / 0").is_err());
5485 }
5486
5487 #[test]
5488 fn error_missing_required_function_arg() {
5489 let result = eval("({ a, b }: a + b) { a = 1; }");
5490 assert!(result.is_err());
5491 let msg = format!("{}", result.unwrap_err());
5492 assert!(msg.contains("missing argument"));
5493 }
5494
5495 #[test]
5496 fn error_unexpected_function_arg() {
5497 let result = eval("({ a }: a) { a = 1; b = 2; }");
5498 assert!(result.is_err());
5499 let msg = format!("{}", result.unwrap_err());
5500 assert!(msg.contains("unexpected argument"));
5501 }
5502
5503 #[test]
5504 fn error_assertion_failure() {
5505 assert!(eval("assert false; 1").is_err());
5506 assert!(eval("assert 1 == 2; 1").is_err());
5507 }
5508
5509 #[test]
5510 fn error_infinite_recursion() {
5511 let result = eval("let x = x; in x");
5514 assert!(result.is_err());
5515 }
5516
5517 #[test]
5518 fn error_infinite_recursion_via_lambda() {
5519 let result = eval("let f = x: f x; in f 1");
5521 assert!(result.is_err());
5522 let msg = format!("{}", result.unwrap_err());
5523 assert!(
5524 msg.contains("infinite recursion") || msg.contains("eval depth") || msg.contains("undefined"),
5525 );
5526 }
5527
5528 #[test]
5533 fn integration_let_with_function_returning_attrset() {
5534 assert_eq!(
5535 ev("let mkPkg = name: { inherit name; version = 1; }; in (mkPkg \"hello\").name"),
5536 Value::string("hello"),
5537 );
5538 }
5539
5540 #[test]
5541 fn integration_chained_updates() {
5542 assert_eq!(
5543 ev("({ a = 1; } // { b = 2; } // { c = 3; }).c"),
5544 Value::Int(3),
5545 );
5546 }
5547
5548 #[test]
5549 fn integration_map_over_attrnames() {
5550 assert_eq!(
5552 ev(r#"
5553 let
5554 set = { a = 1; b = 2; };
5555 names = builtins.attrNames set;
5556 in builtins.length names
5557 "#),
5558 Value::Int(2),
5559 );
5560 }
5561
5562 #[test]
5563 fn integration_compose_functions() {
5564 assert_eq!(
5566 ev("let compose = f: g: x: f (g x); double = x: x * 2; inc = x: x + 1; in compose double inc 5"),
5567 Value::Int(12), );
5569 }
5570
5571 #[test]
5572 fn integration_recursive_list_building() {
5573 assert_eq!(
5575 ev("builtins.map (x: x * x) (builtins.genList (x: x + 1) 4)"),
5576 Value::list(vec![Value::Int(1), Value::Int(4), Value::Int(9), Value::Int(16)]),
5577 );
5578 }
5579
5580 #[test]
5581 fn integration_attrset_from_list() {
5582 let v = ev(r#"
5584 builtins.listToAttrs (builtins.map (x: { name = x; value = true; }) ["a" "b" "c"])
5585 "#);
5586 if let Value::Attrs(attrs) = v {
5587 assert_eq!(attrs.get("a"), Some(&Value::Bool(true)));
5588 assert_eq!(attrs.get("b"), Some(&Value::Bool(true)));
5589 assert_eq!(attrs.get("c"), Some(&Value::Bool(true)));
5590 } else {
5591 panic!("expected attrs");
5592 }
5593 }
5594
5595 #[test]
5596 fn integration_nested_with_and_let() {
5597 assert_eq!(
5598 ev("let x = 10; in with { y = 20; }; x + y"),
5599 Value::Int(30),
5600 );
5601 }
5602
5603 #[test]
5604 fn integration_complex_pattern_match() {
5605 assert_eq!(
5607 ev("(args @ { a, b ? 5, ... }: a + b + (if args ? c then args.c else 0)) { a = 1; c = 10; }"),
5608 Value::Int(16), );
5610 }
5611
5612 #[test]
5613 fn integration_substring() {
5614 assert_eq!(
5615 ev(r#"builtins.substring 0 5 "hello world""#),
5616 Value::string("hello"),
5617 );
5618 assert_eq!(
5619 ev(r#"builtins.substring 6 5 "hello world""#),
5620 Value::string("world"),
5621 );
5622 }
5623
5624 #[test]
5625 fn integration_has_attr_on_nested() {
5626 assert_eq!(ev("{ a = { b = 1; }; } ? a"), Value::Bool(true));
5628 assert_eq!(
5629 ev("({ a = { b = 1; }; }.a) ? b"),
5630 Value::Bool(true),
5631 );
5632 }
5633
5634 #[test]
5635 fn integration_cat_attrs() {
5636 assert_eq!(
5637 ev(r#"builtins.catAttrs "x" [{ x = 1; } { y = 2; } { x = 3; }]"#),
5638 Value::list(vec![Value::Int(1), Value::Int(3)]),
5639 );
5640 }
5641
5642 #[test]
5643 fn integration_get_attr_builtin() {
5644 assert_eq!(
5645 ev(r#"builtins.getAttr "a" { a = 42; b = 10; }"#),
5646 Value::Int(42),
5647 );
5648 }
5649
5650 #[test]
5651 fn integration_has_attr_builtin() {
5652 assert_eq!(
5653 ev(r#"builtins.hasAttr "a" { a = 1; }"#),
5654 Value::Bool(true),
5655 );
5656 assert_eq!(
5657 ev(r#"builtins.hasAttr "z" { a = 1; }"#),
5658 Value::Bool(false),
5659 );
5660 }
5661
5662 #[test]
5663 fn integration_is_path() {
5664 assert_eq!(ev("builtins.isPath ./foo"), Value::Bool(true));
5665 assert_eq!(ev("builtins.isPath 42"), Value::Bool(false));
5666 }
5667
5668 #[test]
5669 fn integration_builtins_trace() {
5670 assert_eq!(ev(r#"builtins.trace "debug msg" 42"#), Value::Int(42));
5672 }
5673
5674 #[test]
5675 fn integration_builtins_split() {
5676 assert_eq!(
5680 ev(r#"builtins.split "/" "a/b/c""#),
5681 Value::list(vec![
5682 Value::string("a"),
5683 Value::list(vec![]),
5684 Value::string("b"),
5685 Value::list(vec![]),
5686 Value::string("c"),
5687 ]),
5688 );
5689 assert_eq!(
5692 ev(r#"builtins.split "(/)" "a/b/c""#),
5693 Value::list(vec![
5694 Value::string("a"),
5695 Value::list(vec![Value::string("/")]),
5696 Value::string("b"),
5697 Value::list(vec![Value::string("/")]),
5698 Value::string("c"),
5699 ]),
5700 );
5701 }
5702
5703 #[test]
5704 fn integration_builtins_split_no_capture_groups() {
5705 assert_eq!(
5710 ev(r#"builtins.split "-" "aarch64-darwin""#),
5711 Value::list(vec![
5712 Value::string("aarch64"),
5713 Value::list(vec![]),
5714 Value::string("darwin"),
5715 ]),
5716 );
5717 }
5718
5719 #[test]
5720 fn integration_builtins_split_system_string_filter() {
5721 assert_eq!(
5724 ev(r#"builtins.filter builtins.isString (builtins.split "-" "aarch64-darwin")"#),
5725 Value::list(vec![
5726 Value::string("aarch64"),
5727 Value::string("darwin"),
5728 ]),
5729 );
5730 }
5731
5732 #[test]
5733 fn integration_deeply_nested_let() {
5734 assert_eq!(
5736 ev("let a = let b = let c = 10; in c * 2; in b + 1; in a"),
5737 Value::Int(21),
5738 );
5739 }
5740
5741 #[test]
5742 fn integration_if_in_attrset_value() {
5743 assert_eq!(
5744 ev("{ x = if true then 1 else 2; }.x"),
5745 Value::Int(1),
5746 );
5747 }
5748
5749 #[test]
5750 fn integration_lambda_in_list() {
5751 assert_eq!(
5753 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 0) 5"),
5754 Value::Int(6),
5755 );
5756 assert_eq!(
5757 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 1) 5"),
5758 Value::Int(10),
5759 );
5760 }
5761
5762 #[test]
5763 fn integration_nixpkgs_lib_id() {
5764 assert_eq!(
5766 ev("let lib = { id = x: x; const = a: b: a; }; in lib.id 42"),
5767 Value::Int(42),
5768 );
5769 assert_eq!(
5770 ev("let lib = { id = x: x; const = a: b: a; }; in lib.const 1 2"),
5771 Value::Int(1),
5772 );
5773 }
5774
5775 #[test]
5776 fn integration_multiple_inherit() {
5777 assert_eq!(
5778 ev("let a = 1; b = 2; c = 3; in { inherit a b c; }.b"),
5779 Value::Int(2),
5780 );
5781 }
5782
5783 #[test]
5784 fn integration_rec_set_with_builtins() {
5785 assert_eq!(
5786 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
5787 Value::Int(5),
5788 );
5789 }
5790
5791 #[test]
5796 fn functor_simple_callable_attrset() {
5797 assert_eq!(
5798 ev("let s = { __functor = self: x: x + 1; }; in s 41"),
5799 Value::Int(42),
5800 );
5801 }
5802
5803 #[test]
5804 fn functor_with_self_reference() {
5805 assert_eq!(
5806 ev("let s = { __functor = self: x: self.base + x; base = 100; }; in s 23"),
5807 Value::Int(123),
5808 );
5809 }
5810
5811 #[test]
5812 fn functor_updated_attrset() {
5813 assert_eq!(
5815 ev(r#"
5816 let
5817 mk = { __functor = self: x: self.n + x; n = 0; };
5818 s = mk // { n = 50; };
5819 in s 7
5820 "#),
5821 Value::Int(57),
5822 );
5823 }
5824
5825 #[test]
5826 fn functor_error_on_non_callable_attrset() {
5827 let result = eval("let s = { a = 1; }; in s 5");
5829 assert!(result.is_err());
5830 }
5831
5832 #[test]
5837 fn to_string_protocol_in_interpolation() {
5838 assert_eq!(
5839 ev(r#"let s = { __toString = self: "world"; }; in "hello ${s}""#),
5840 Value::string("hello world"),
5841 );
5842 }
5843
5844 #[test]
5845 fn to_string_protocol_accesses_self() {
5846 assert_eq!(
5847 ev(r#"let s = { __toString = self: self.val; val = "abc"; }; in "${s}""#),
5848 Value::string("abc"),
5849 );
5850 }
5851
5852 #[test]
5853 fn to_string_protocol_via_builtin_to_string() {
5854 assert_eq!(
5855 ev(r#"builtins.toString { __toString = self: "via-builtin"; }"#),
5856 Value::string("via-builtin"),
5857 );
5858 }
5859
5860 #[test]
5861 fn to_string_protocol_attrset_without_toString_fails() {
5862 let result = eval(r#""${{}}"#);
5864 assert!(result.is_err());
5865 }
5866
5867 #[test]
5872 fn eval_builtins_concat_strings() {
5873 assert_eq!(
5874 ev(r#"builtins.concatStrings ["a" "b" "c"]"#),
5875 Value::string("abc"),
5876 );
5877 assert_eq!(
5878 ev(r#"builtins.concatStrings []"#),
5879 Value::string(""),
5880 );
5881 }
5882
5883 #[test]
5884 fn eval_builtins_partition() {
5885 let v = ev("builtins.partition (x: x > 3) [1 2 3 4 5]");
5886 if let Value::Attrs(a) = v {
5887 assert_eq!(a.get("right"), Some(&Value::list(vec![Value::Int(4), Value::Int(5)])));
5888 assert_eq!(a.get("wrong"), Some(&Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)])));
5889 } else {
5890 panic!("expected attrs");
5891 }
5892 }
5893
5894 #[test]
5895 fn eval_builtins_group_by() {
5896 let v = ev(r#"builtins.groupBy (x: if x > 0 then "pos" else "neg") [1 (0 - 2) 3 (0 - 4)]"#);
5897 if let Value::Attrs(a) = v {
5898 assert_eq!(a.get("pos"), Some(&Value::list(vec![Value::Int(1), Value::Int(3)])));
5899 assert_eq!(a.get("neg"), Some(&Value::list(vec![Value::Int(-2), Value::Int(-4)])));
5900 } else {
5901 panic!("expected attrs");
5902 }
5903 }
5904
5905 #[test]
5906 fn eval_builtins_zip_attrs_with() {
5907 let v = ev("builtins.zipAttrsWith (n: vs: builtins.head vs) [{ a = 1; } { a = 2; b = 3; }]");
5908 if let Value::Attrs(a) = v {
5909 assert_eq!(a.get("a"), Some(&Value::Int(1)));
5910 assert_eq!(a.get("b"), Some(&Value::Int(3)));
5911 } else {
5912 panic!("expected attrs");
5913 }
5914 }
5915
5916 #[test]
5917 fn eval_builtins_compare_versions() {
5918 assert_eq!(ev(r#"builtins.compareVersions "2.0" "1.0""#), Value::Int(1));
5919 assert_eq!(ev(r#"builtins.compareVersions "1.0" "2.0""#), Value::Int(-1));
5920 assert_eq!(ev(r#"builtins.compareVersions "1.0" "1.0""#), Value::Int(0));
5921 }
5922
5923 #[test]
5924 fn eval_builtins_parse_drv_name() {
5925 let v = ev(r#"builtins.parseDrvName "nix-2.3.4""#);
5926 if let Value::Attrs(a) = v {
5927 assert_eq!(a.get("name"), Some(&Value::string("nix")));
5928 assert_eq!(a.get("version"), Some(&Value::string("2.3.4")));
5929 } else {
5930 panic!("expected attrs");
5931 }
5932 }
5933
5934 #[test]
5935 fn eval_builtins_base_name_of() {
5936 assert_eq!(
5937 ev(r#"builtins.baseNameOf "/foo/bar/baz""#),
5938 Value::string("baz"),
5939 );
5940 }
5941
5942 #[test]
5943 fn eval_builtins_dir_of() {
5944 assert_eq!(
5945 ev(r#"builtins.dirOf "/foo/bar/baz""#),
5946 Value::string("/foo/bar"),
5947 );
5948 }
5949
5950 #[test]
5951 fn eval_builtins_add_error_context() {
5952 assert_eq!(
5953 ev(r#"builtins.addErrorContext "some context" 42"#),
5954 Value::Int(42),
5955 );
5956 }
5957
5958 #[test]
5959 fn eval_builtins_abort() {
5960 let result = eval(r#"builtins.abort "fatal error""#);
5961 assert!(result.is_err());
5962 let msg = format!("{}", result.unwrap_err());
5963 assert!(msg.contains("fatal error"));
5964 }
5965
5966 #[test]
5971 fn indented_string_simple() {
5972 assert_eq!(ev("''hello''"), Value::string("hello"));
5973 }
5974
5975 #[test]
5976 fn indented_string_multiline_strips_indent() {
5977 assert_eq!(
5978 ev("''\n line1\n line2\n''"),
5979 Value::string("line1\nline2\n"),
5980 );
5981 }
5982
5983 #[test]
5984 fn indented_string_with_interpolation() {
5985 let code = "let x = \"world\"; in ''hello ${x}''";
5986 assert_eq!(
5987 ev(code),
5988 Value::string("hello world"),
5989 );
5990 }
5991
5992 #[test]
5993 fn indented_string_deeper_indent_preserved() {
5994 assert_eq!(
5996 ev("''\n a\n b\n''"),
5997 Value::string("a\n b\n"),
5998 );
5999 }
6000
6001 #[test]
6006 fn dynamic_attr_name_in_set() {
6007 assert_eq!(
6008 ev(r#"let key = "mykey"; in { ${key} = 42; }.mykey"#),
6009 Value::Int(42),
6010 );
6011 }
6012
6013 #[test]
6014 fn dynamic_attr_name_with_expression() {
6015 assert_eq!(
6016 ev(r#"let prefix = "foo"; in { ${"${prefix}bar"} = 1; }.foobar"#),
6017 Value::Int(1),
6018 );
6019 }
6020
6021 #[test]
6026 fn eval_builtins_match() {
6027 assert_eq!(
6028 ev(r#"builtins.match "([0-9]+)" "42""#),
6029 Value::list(vec![Value::string("42")]),
6030 );
6031 }
6032
6033 #[test]
6034 fn eval_builtins_hash_string() {
6035 let v = ev(r#"builtins.hashString "sha256" "hello""#);
6036 if let Value::String(ns) = v {
6037 assert_eq!(ns.chars.len(), 64);
6038 } else {
6039 panic!("expected string");
6040 }
6041 }
6042
6043 #[test]
6044 fn eval_builtins_import() {
6045 let dir = std::env::temp_dir();
6046 let path = dir.join("sui_eval_test_import_eval.nix");
6047 std::fs::write(&path, "42").unwrap();
6048 let expr = format!(r#"import "{}""#, path.display());
6049 let v = eval(&expr).unwrap();
6050 assert_eq!(v, Value::Int(42));
6051 std::fs::remove_file(&path).ok();
6052 }
6053
6054 #[test]
6055 fn eval_builtins_derivation() {
6056 let v = eval(r#"builtins.derivation { name = "test"; system = "x86_64-linux"; builder = "/bin/sh"; }"#).unwrap();
6057 if let Value::Attrs(a) = v {
6058 assert_eq!(a.get("type"), Some(&Value::string("derivation")));
6059 } else {
6060 panic!("expected attrs");
6061 }
6062 }
6063
6064 #[test]
6065 fn eval_mutual_recursive_let() {
6066 let v = eval("let a = { x = b; }; b = { y = a; }; in a.x.y");
6073 assert!(v.is_ok(), "mutual recursive let should not error: {v:?}");
6074 let val = v.unwrap();
6076 assert!(
6077 matches!(val, Value::Attrs(_)),
6078 "a.x.y should be an attrset, got: {val:?}",
6079 );
6080 }
6081
6082 #[test]
6083 fn eval_mutual_recursive_let_simple() {
6084 let v = eval("let a = b; b = 42; in a");
6086 assert!(v.is_ok());
6087 assert_eq!(v.unwrap(), Value::Int(42));
6090 }
6091
6092 #[test]
6093 fn eval_builtins_read_dir() {
6094 let dir = std::env::temp_dir().join("sui_eval_test_readdir_eval");
6095 let _ = std::fs::remove_dir_all(&dir);
6096 std::fs::create_dir_all(&dir).unwrap();
6097 std::fs::write(dir.join("a.txt"), "").unwrap();
6098 let expr = format!(r#"builtins.readDir "{}""#, dir.display());
6099 let v = eval(&expr).unwrap();
6100 if let Value::Attrs(a) = v {
6101 assert_eq!(a.get("a.txt"), Some(&Value::string("regular")));
6102 } else {
6103 panic!("expected attrs");
6104 }
6105 let _ = std::fs::remove_dir_all(&dir);
6106 }
6107
6108 #[test]
6113 fn thunk_basic_let() {
6114 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
6116 }
6117
6118 #[test]
6119 fn thunk_forward_ref() {
6120 assert_eq!(ev("let a = b; b = 1; in a"), Value::Int(1));
6122 }
6123
6124 #[test]
6125 fn thunk_mutual_rec_attrset_in_let() {
6126 assert_eq!(ev("let a = { x = b; }; b = { y = 1; }; in a.x.y"), Value::Int(1));
6128 }
6129
6130 #[test]
6131 fn thunk_rec_attrset() {
6132 assert_eq!(ev("(rec { a = b; b = 1; }).a"), Value::Int(1));
6134 }
6135
6136 #[test]
6137 fn thunk_rec_attrset_chain() {
6138 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
6140 }
6141
6142 #[test]
6143 fn thunk_fixpoint() {
6144 assert_eq!(
6146 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; })).b"),
6147 Value::Int(2),
6148 );
6149 }
6150
6151 #[test]
6152 fn thunk_blackhole_self_reference() {
6153 let result = eval("let x = x; in x");
6155 assert!(result.is_err());
6156 let msg = format!("{}", result.unwrap_err());
6157 assert!(
6158 msg.contains("infinite recursion") || msg.contains("blackhole"),
6159 "expected blackhole error, got: {msg}",
6160 );
6161 }
6162
6163 #[test]
6164 fn thunk_mutual_blackhole() {
6165 let result = eval("let a = b; b = a; in a");
6167 assert!(result.is_err());
6168 }
6169
6170 #[test]
6171 fn thunk_let_body_forces_correctly() {
6172 assert_eq!(ev("let a = 10; b = 20; in a + b"), Value::Int(30));
6174 }
6175
6176 #[test]
6177 fn thunk_only_forced_when_needed() {
6178 assert_eq!(ev("let bad = 1 / 0; good = 42; in good"), Value::Int(42));
6180 }
6181
6182 #[test]
6183 fn thunk_forward_ref_in_function_body() {
6184 assert_eq!(
6186 ev("let f = x: x + b; b = 10; in f 5"),
6187 Value::Int(15),
6188 );
6189 }
6190
6191 #[test]
6192 fn thunk_rec_set_self_ref_through_self() {
6193 assert_eq!(
6195 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
6196 Value::Int(5),
6197 );
6198 }
6199
6200 #[test]
6201 fn thunk_nested_let_forward_ref() {
6202 assert_eq!(
6204 ev("let a = b + 1; b = 2; in a"),
6205 Value::Int(3),
6206 );
6207 }
6208
6209 #[test]
6210 fn thunk_deep_chain() {
6211 assert_eq!(
6213 ev("let a = 1; b = a; c = b; d = c; e = d; in e"),
6214 Value::Int(1),
6215 );
6216 }
6217
6218 #[test]
6219 fn thunk_rec_set_fixpoint() {
6220 assert_eq!(
6222 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; c = self.b + 1; })).c"),
6223 Value::Int(3),
6224 );
6225 }
6226
6227 #[test]
6228 fn thunk_let_with_inherit() {
6229 assert_eq!(
6231 ev("let a = 1; in let inherit a; b = a + 1; in b"),
6232 Value::Int(2),
6233 );
6234 }
6235
6236 #[test]
6237 fn thunk_attrset_value_lazy() {
6238 assert_eq!(
6241 ev("let x = 42; in { a = x; }.a"),
6242 Value::Int(42),
6243 );
6244 }
6245
6246 #[test]
6247 fn thunk_unused_error_not_forced() {
6248 assert_eq!(
6250 ev(r#"let bad = builtins.throw "boom"; ok = 1; in ok"#),
6251 Value::Int(1),
6252 );
6253 }
6254
6255 #[test]
6256 fn thunk_rec_set_mutual_reference() {
6257 let v = ev("rec { a = { val = b.val + 1; }; b = { val = 10; }; }");
6259 if let Value::Attrs(attrs) = v {
6260 let a = attrs.get("a").unwrap();
6261 let a_forced = force_value(a).unwrap();
6262 if let Value::Attrs(a_attrs) = a_forced {
6263 assert_eq!(a_attrs.get("val"), Some(&Value::Int(11)));
6264 } else {
6265 panic!("expected attrs for a");
6266 }
6267 } else {
6268 panic!("expected attrs");
6269 }
6270 }
6271
6272 #[test]
6275 fn let_rec_self_reference_simple() {
6276 assert_eq!(
6277 ev("let x = 1; y = x + 1; in y"),
6278 Value::Int(2),
6279 );
6280 }
6281
6282 #[test]
6283 fn let_rec_self_reference_chain() {
6284 assert_eq!(
6285 ev("let a = 1; b = a + 1; c = b + 1; in c"),
6286 Value::Int(3),
6287 );
6288 }
6289
6290 #[test]
6291 fn let_rec_self_reference_with_function() {
6292 assert_eq!(
6293 ev("let f = x: x + 1; y = f 10; in y"),
6294 Value::Int(11),
6295 );
6296 }
6297
6298 #[test]
6299 fn let_rec_mutual_recursion_via_if() {
6300 assert_eq!(
6301 ev("let isEven = n: if n == 0 then true else isOdd (n - 1); isOdd = n: if n == 0 then false else isEven (n - 1); in isEven 4"),
6302 Value::Bool(true),
6303 );
6304 }
6305
6306 #[test]
6307 fn let_rec_forward_ref_in_list() {
6308 assert_eq!(
6309 ev("let xs = [a b]; a = 1; b = 2; in builtins.length xs"),
6310 Value::Int(2),
6311 );
6312 }
6313
6314 #[test]
6317 fn with_shadowing_let_wins_over_with() {
6318 assert_eq!(
6319 ev("let x = 1; in with { x = 2; }; x"),
6320 Value::Int(1),
6321 );
6322 }
6323
6324 #[test]
6325 fn with_shadowing_inner_with_wins() {
6326 assert_eq!(
6327 ev("with { x = 1; }; with { x = 2; }; x"),
6328 Value::Int(2),
6329 );
6330 }
6331
6332 #[test]
6333 fn with_shadowing_outer_provides_missing() {
6334 assert_eq!(
6335 ev("with { x = 1; y = 10; }; with { x = 2; }; x + y"),
6336 Value::Int(12),
6337 );
6338 }
6339
6340 #[test]
6341 fn with_shadowing_lambda_arg_wins() {
6342 assert_eq!(
6343 ev("(x: with { x = 99; }; x) 42"),
6344 Value::Int(42),
6345 );
6346 }
6347
6348 #[test]
6349 fn with_shadowing_nested_let_wins_over_with() {
6350 assert_eq!(
6351 ev("with { x = 1; }; let x = 2; in x"),
6352 Value::Int(2),
6353 );
6354 }
6355
6356 #[test]
6357 fn with_scope_dynamic_attrs() {
6358 assert_eq!(
6359 ev(r#"with { x = 1; y = 2; z = 3; }; x + y + z"#),
6360 Value::Int(6),
6361 );
6362 }
6363
6364 #[test]
6365 fn with_scope_over_lazy_thunk_chain_resolves() {
6366 assert_eq!(
6375 ev(r#"let outer = if true then (if true then { unix = 42; } else {}) else {};
6376 # force a two-deep lazy wrap of the with-head
6377 head = (x: x) ((y: y) outer);
6378 in with head; unix"#),
6379 Value::Int(42),
6380 );
6381 }
6382
6383 #[test]
6384 fn with_scope_head_from_deep_select_resolves() {
6385 assert_eq!(
6388 ev(r#"let a = { b = { c = { key = 7; }; }; }; in with a.b.c; key"#),
6389 Value::Int(7),
6390 );
6391 }
6392
6393 #[test]
6396 fn attrset_deep_merge_simple() {
6397 let v = ev("{ a.b = 1; a.c = 2; }");
6398 if let Value::Attrs(attrs) = v {
6399 let a = force_value(attrs.get("a").unwrap()).unwrap();
6400 if let Value::Attrs(inner) = a {
6401 assert_eq!(force_value(inner.get("b").unwrap()).unwrap(), Value::Int(1));
6402 assert_eq!(force_value(inner.get("c").unwrap()).unwrap(), Value::Int(2));
6403 } else {
6404 panic!("expected nested attrs");
6405 }
6406 } else {
6407 panic!("expected attrs");
6408 }
6409 }
6410
6411 #[test]
6412 fn attrset_deep_merge_three_levels() {
6413 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
6414 if let Value::Attrs(attrs) = v {
6415 let a = force_value(attrs.get("a").unwrap()).unwrap();
6416 if let Value::Attrs(a_inner) = a {
6417 let e = force_value(a_inner.get("e").unwrap()).unwrap();
6418 assert_eq!(e, Value::Int(3));
6419 let b = force_value(a_inner.get("b").unwrap()).unwrap();
6420 if let Value::Attrs(b_inner) = b {
6421 assert_eq!(force_value(b_inner.get("c").unwrap()).unwrap(), Value::Int(1));
6422 assert_eq!(force_value(b_inner.get("d").unwrap()).unwrap(), Value::Int(2));
6423 } else {
6424 panic!("expected nested attrs for b");
6425 }
6426 } else {
6427 panic!("expected nested attrs for a");
6428 }
6429 } else {
6430 panic!("expected attrs");
6431 }
6432 }
6433
6434 #[test]
6435 fn attrset_deep_merge_preserves_siblings() {
6436 assert_eq!(
6437 ev("{ a.x = 1; b = 2; a.y = 3; }.b"),
6438 Value::Int(2),
6439 );
6440 }
6441
6442 #[test]
6443 fn attrset_deep_merge_in_let() {
6444 let v = ev("let s = { a.b = 1; a.c = 2; }; in s.a.b + s.a.c");
6445 assert_eq!(v, Value::Int(3));
6446 }
6447
6448 #[test]
6449 fn attrset_deep_merge_fullset_then_dotted() {
6450 let v = ev("let s = { a = { x = 1; }; a.y = 2; }; in s.a.x + s.a.y");
6457 assert_eq!(v, Value::Int(3));
6458 let both = ev("let s = { a = { x = 1; }; a.y = 2; }; in [ s.a.x s.a.y ]");
6460 if let Value::List(items) = both {
6461 assert_eq!(force_value(&items[0]).unwrap(), Value::Int(1));
6462 assert_eq!(force_value(&items[1]).unwrap(), Value::Int(2));
6463 } else {
6464 panic!("expected list");
6465 }
6466 }
6467
6468 #[test]
6471 fn inherit_from_basic() {
6472 assert_eq!(
6473 ev("let s = { x = 1; y = 2; }; in let inherit (s) x y; in x + y"),
6474 Value::Int(3),
6475 );
6476 }
6477
6478 #[test]
6479 fn inherit_from_with_shadowing() {
6480 assert_eq!(
6481 ev("let x = 10; in let inherit ({ x = 20; }) x; in x"),
6482 Value::Int(20),
6483 );
6484 }
6485
6486 #[test]
6487 fn inherit_from_in_attrset() {
6488 let v = ev(r#"let s = { a = 1; b = 2; }; in { inherit (s) a b; c = 3; }"#);
6489 if let Value::Attrs(attrs) = v {
6490 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
6491 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
6492 assert_eq!(force_value(attrs.get("c").unwrap()).unwrap(), Value::Int(3));
6493 } else {
6494 panic!("expected attrs");
6495 }
6496 }
6497
6498 #[test]
6499 fn inherit_from_rec_set() {
6500 assert_eq!(
6501 ev("rec { inherit ({ x = 42; }) x; y = x; }.y"),
6502 Value::Int(42),
6503 );
6504 }
6505
6506 #[test]
6507 fn inherit_plain_from_scope() {
6508 assert_eq!(
6509 ev("let x = 1; in { inherit x; }.x"),
6510 Value::Int(1),
6511 );
6512 }
6513
6514 #[test]
6523 fn inherit_plain_from_with_scope_lazy() {
6524 assert_eq!(
6528 ev("let fix = f: let x = f x; in x;
6529 self = fix (self: with self; {
6530 a = use { inherit cp; };
6531 use = { cp }: cp 5;
6532 cp = x: x + 100;
6533 });
6534 in self.a"),
6535 Value::Int(105),
6536 );
6537 assert_eq!(
6539 ev("with { y = 7; }; { inherit y; }.y"),
6540 Value::Int(7),
6541 );
6542 }
6543
6544 #[test]
6545 fn inherit_multiple_from_expr() {
6546 assert_eq!(
6547 ev("let s = { a = 10; b = 20; c = 30; }; in let inherit (s) a b c; in a + b + c"),
6548 Value::Int(60),
6549 );
6550 }
6551
6552 #[test]
6555 fn interp_nested_attrset_access() {
6556 assert_eq!(
6557 ev(r#"let x = { a = "hello"; }; in "${x.a} world""#),
6558 Value::string("hello world"),
6559 );
6560 }
6561
6562 #[test]
6563 fn interp_with_let_expression() {
6564 assert_eq!(
6565 ev(r#""${let x = "inner"; in x}""#),
6566 Value::string("inner"),
6567 );
6568 }
6569
6570 #[test]
6571 fn interp_float_coercion() {
6572 assert_eq!(
6574 ev(r#""${toString 3.14}""#),
6575 Value::string("3.140000"),
6576 );
6577 }
6578
6579 #[test]
6582 fn compare_mixed_int_float() {
6583 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
6584 assert_eq!(ev("1.5 > 1"), Value::Bool(true));
6585 assert_eq!(ev("2.0 == 2"), Value::Bool(true));
6586 }
6587
6588 #[test]
6589 fn compare_string_lexicographic() {
6590 assert_eq!(ev(r#""abc" < "abd""#), Value::Bool(true));
6591 assert_eq!(ev(r#""abc" < "abc""#), Value::Bool(false));
6592 assert_eq!(ev(r#""abc" <= "abc""#), Value::Bool(true));
6593 }
6594
6595 #[test]
6598 fn update_empty_sets() {
6599 let v = ev("{} // {}");
6600 if let Value::Attrs(a) = v { assert!(a.is_empty()); } else { panic!(); }
6601 }
6602
6603 #[test]
6604 fn update_right_overrides_completely() {
6605 assert_eq!(
6606 ev("{ a = 1; b = 2; } // { a = 10; c = 30; }"),
6607 ev("{ a = 10; b = 2; c = 30; }"),
6608 );
6609 }
6610
6611 #[test]
6612 fn update_chained() {
6613 assert_eq!(
6614 ev("{ a = 1; } // { b = 2; } // { c = 3; }"),
6615 ev("{ a = 1; b = 2; c = 3; }"),
6616 );
6617 }
6618
6619 #[test]
6622 fn force_value_concrete_unchanged() {
6623 let v = Value::Int(42);
6624 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
6625 }
6626
6627 #[test]
6628 fn force_value_null() {
6629 assert_eq!(force_value(&Value::Null).unwrap(), Value::Null);
6630 }
6631
6632 #[test]
6635 fn eval_with_file_none() {
6636 let result = eval_with_file("1 + 2", None).unwrap();
6637 assert_eq!(result, Value::Int(3));
6638 }
6639
6640 #[test]
6643 fn error_type_mismatch_in_comparison() {
6644 let result = eval(r#"1 < "a""#);
6645 assert!(result.is_err());
6646 }
6647
6648 #[test]
6649 fn error_select_from_non_set() {
6650 let result = eval("42.x");
6651 assert!(result.is_err());
6652 }
6653
6654 #[test]
6655 fn error_call_non_function() {
6656 let result = eval("42 1");
6657 assert!(result.is_err());
6658 }
6659
6660 #[test]
6661 fn error_negate_string() {
6662 let result = eval(r#"-"hello""#);
6663 assert!(result.is_err());
6664 }
6665
6666 #[test]
6669 fn multiline_string_empty() {
6670 assert_eq!(ev("''''"), Value::string(""));
6671 }
6672
6673 #[test]
6674 fn multiline_string_with_trailing_newline() {
6675 let v = ev("''\n hello\n''");
6676 assert_eq!(v, Value::string("hello\n"));
6677 }
6678
6679 #[test]
6682 fn list_concat_empty_left() {
6683 assert_eq!(ev("[] ++ [1 2]"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6684 }
6685
6686 #[test]
6687 fn list_concat_empty_right() {
6688 assert_eq!(ev("[1 2] ++ []"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6689 }
6690
6691 #[test]
6692 fn list_concat_both_empty() {
6693 assert_eq!(ev("[] ++ []"), Value::list(vec![]));
6694 }
6695
6696 #[test]
6699 fn formals_at_pattern_accessible() {
6700 assert_eq!(
6701 ev("({ x, ... } @ args: builtins.length (builtins.attrNames args)) { x = 1; y = 2; z = 3; }"),
6702 Value::Int(3),
6703 );
6704 }
6705
6706 #[test]
6707 fn formals_default_uses_other_arg() {
6708 assert_eq!(
6709 ev("({ x, y ? x + 1 }: y) { x = 10; }"),
6710 Value::Int(11),
6711 );
6712 }
6713
6714 #[test]
6715 fn formals_default_lazy_assert_false() {
6716 assert_eq!(
6720 ev("({ cpu, vendor ? assert false; null, kernel } @ args: if args ? vendor then vendor else \"inferred\") { cpu = \"x86_64\"; kernel = \"linux\"; }"),
6721 Value::String(Rc::new(NixString::plain("inferred"))),
6722 );
6723 }
6724
6725 #[test]
6726 fn formals_default_lazy_only_forced_when_accessed() {
6727 assert_eq!(
6729 ev("({ a, b ? 42 }: b) { a = 1; }"),
6730 Value::Int(42),
6731 );
6732 }
6733
6734 #[test]
6735 fn formals_ellipsis_ignores_extra() {
6736 assert_eq!(
6737 ev("({ x, ... }: x) { x = 1; y = 2; z = 3; }"),
6738 Value::Int(1),
6739 );
6740 }
6741
6742 #[test]
6745 fn pure_mode_roundtrip() {
6746 let was_pure = is_pure_mode();
6747 set_pure_mode(true);
6748 assert!(is_pure_mode());
6749 set_pure_mode(false);
6750 assert!(!is_pure_mode());
6751 set_pure_mode(was_pure);
6752 }
6753
6754 #[test]
6757 fn path_concat_with_string() {
6758 assert_eq!(
6759 ev(r#"/foo + "bar""#),
6760 Value::Path(Box::new(SmolStr::from("/foobar"))),
6761 );
6762 }
6763
6764 #[test]
6765 fn path_concat_with_path() {
6766 assert_eq!(
6767 ev("/foo + /bar"),
6768 Value::Path(Box::new(SmolStr::from("/foo//bar"))),
6769 );
6770 }
6771
6772 #[test]
6775 fn current_eval_dir_empty_when_no_file_pushed() {
6776 let snapshot = current_eval_dir();
6780 let _ = snapshot;
6782 }
6783
6784 #[test]
6785 fn push_eval_file_sets_current_dir() {
6786 let p = std::path::PathBuf::from("/tmp/example/file.nix");
6787 {
6788 let _g = push_eval_file(p.clone());
6789 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/tmp/example")));
6790 }
6791 }
6795
6796 #[test]
6797 fn push_eval_file_nested_stack() {
6798 let outer = std::path::PathBuf::from("/a/x.nix");
6799 let inner = std::path::PathBuf::from("/b/y.nix");
6800 {
6801 let _g_outer = push_eval_file(outer.clone());
6802 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6803 {
6804 let _g_inner = push_eval_file(inner.clone());
6805 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/b")));
6806 }
6807 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6809 }
6810 }
6811
6812 #[test]
6815 fn error_undefined_var_includes_file_context() {
6816 let p = std::path::PathBuf::from("/nix/store/abc-default.nix");
6817 let _g = push_eval_file(p);
6818 let result = eval("nonexistent_xyz");
6819 let msg = format!("{}", result.unwrap_err());
6820 assert!(msg.contains("undefined variable"), "msg: {msg}");
6821 assert!(msg.contains("nonexistent_xyz"), "msg: {msg}");
6822 assert!(msg.contains("abc-default.nix"), "msg: {msg}");
6823 }
6824
6825 #[test]
6826 fn error_attr_not_found_includes_file_context() {
6827 let p = std::path::PathBuf::from("/nix/store/xyz-module.nix");
6828 let _g = push_eval_file(p);
6829 let result = eval("{}.missing_key");
6830 let msg = format!("{}", result.unwrap_err());
6831 assert!(msg.contains("not found") || msg.contains("missing_key"), "msg: {msg}");
6832 assert!(msg.contains("xyz-module.nix"), "msg: {msg}");
6833 }
6834
6835 #[test]
6836 fn error_assertion_failed_includes_file_context() {
6837 let p = std::path::PathBuf::from("/nix/store/test-assert.nix");
6838 let _g = push_eval_file(p);
6839 let result = eval("assert false; 1");
6840 let msg = format!("{}", result.unwrap_err());
6841 assert!(msg.contains("assertion failed"), "msg: {msg}");
6842 assert!(msg.contains("test-assert.nix"), "msg: {msg}");
6843 }
6844
6845 #[test]
6846 fn error_missing_argument_includes_file_context() {
6847 let p = std::path::PathBuf::from("/nix/store/func.nix");
6848 let _g = push_eval_file(p);
6849 let result = eval("({ a, b }: a) { a = 1; }");
6850 let msg = format!("{}", result.unwrap_err());
6851 assert!(msg.contains("missing argument"), "msg: {msg}");
6852 assert!(msg.contains("func.nix"), "msg: {msg}");
6853 }
6854
6855 #[test]
6856 fn error_cannot_call_includes_file_context() {
6857 let p = std::path::PathBuf::from("/nix/store/call.nix");
6858 let _g = push_eval_file(p);
6859 let result = eval("42 99");
6860 let msg = format!("{}", result.unwrap_err());
6861 assert!(msg.contains("cannot call"), "msg: {msg}");
6862 assert!(msg.contains("call.nix"), "msg: {msg}");
6863 }
6864
6865 #[test]
6866 fn error_without_file_has_no_in_prefix() {
6867 let result = eval("nonexistent_xyz");
6870 let msg = format!("{}", result.unwrap_err());
6871 assert!(msg.contains("undefined variable"), "msg: {msg}");
6872 assert!(!msg.contains(", in"), "msg should not contain file context: {msg}");
6873 }
6874
6875 #[test]
6878 fn pure_mode_set_get_independence() {
6879 let was = is_pure_mode();
6880 set_pure_mode(true);
6881 assert!(is_pure_mode());
6882 set_pure_mode(false);
6883 assert!(!is_pure_mode());
6884 set_pure_mode(was);
6885 }
6886
6887 #[test]
6890 fn eval_with_file_some_path_arithmetic() {
6891 let p = std::path::PathBuf::from("/tmp/imaginary.nix");
6892 let result = eval_with_file("1 + 2", Some(p)).unwrap();
6893 assert_eq!(result, Value::Int(3));
6894 }
6895
6896 #[test]
6904 fn unsafe_get_attr_pos_reports_file_and_offset_column() {
6905 let dir = tempfile::tempdir().unwrap();
6911 let file_body = "{ a = 1;\n b = 2; }\n";
6913 let f = dir.path().join("lit.nix");
6914 std::fs::write(&f, file_body).unwrap();
6915 let src = format!("builtins.unsafeGetAttrPos \"b\" (import {})", f.display());
6916 let v = eval(&src).unwrap();
6917 let attrs = match v { Value::Attrs(a) => a, other => panic!("expected attrs, got {other:?}") };
6918 assert_eq!(
6919 attrs.get("file").unwrap().as_string().unwrap(),
6920 f.to_string_lossy(),
6921 );
6922 assert_eq!(*attrs.get("line").unwrap(), Value::Int(1));
6923 let expected_col = (file_body.find("b = 2").unwrap() as i64) + 1;
6925 let col = match attrs.get("column").unwrap() { Value::Int(n) => *n, o => panic!("{o:?}") };
6926 assert_eq!(col, expected_col, "column must be the 1-based byte offset");
6927 }
6928
6929 #[test]
6930 fn unsafe_get_attr_pos_null_for_string_origin() {
6931 let v = eval("builtins.unsafeGetAttrPos \"a\" { a = 1; }").unwrap();
6933 assert_eq!(v, Value::Null);
6934 }
6935
6936 #[test]
6937 fn unsafe_get_attr_pos_null_for_missing_key() {
6938 let dir = tempfile::tempdir().unwrap();
6940 let f = dir.path().join("lit.nix");
6941 std::fs::write(&f, "{ a = 1; }\n").unwrap();
6942 let src = format!("builtins.unsafeGetAttrPos \"zzz\" (import {})", f.display());
6943 let v = eval(&src).unwrap();
6944 assert_eq!(v, Value::Null);
6945 }
6946
6947 #[test]
6950 fn interp_int_into_string() {
6951 assert_eq!(ev(r#""val=${toString 42}""#), Value::string("val=42"));
6953 }
6954
6955 #[test]
6956 fn interp_bool_true_becomes_one() {
6957 let v = ev(r#"let x = true; in "${builtins.toString x}""#);
6959 assert_eq!(v, Value::string("1"));
6960 }
6961
6962 #[test]
6963 fn interp_null_becomes_empty() {
6964 let v = ev(r#"let x = null; in "${builtins.toString x}""#);
6966 assert_eq!(v, Value::string(""));
6967 }
6968
6969 #[test]
6970 fn interp_attrset_without_to_string_errors() {
6971 let result = eval(r#"let s = { x = 1; }; in "${s}""#);
6973 assert!(result.is_err());
6974 }
6975
6976 #[test]
6977 fn interp_attrset_with_to_string_protocol() {
6978 let v = ev(r#""${{ __toString = self: "ok"; }}""#);
6980 assert_eq!(v, Value::string("ok"));
6981 }
6982
6983 #[test]
6986 fn eval_path_absolute_literal() {
6987 let v = ev("/tmp/foo");
6988 match v {
6989 Value::Path(p) => assert!(p.contains("/tmp/foo")),
6990 _ => panic!("expected Path"),
6991 }
6992 }
6993
6994 #[test]
6995 fn eval_path_home_literal() {
6996 let v = ev("~/foo.nix");
6997 match v {
6998 Value::Path(p) => assert!(p.contains("~/foo.nix") || p.ends_with("foo.nix")),
6999 _ => panic!("expected Path"),
7000 }
7001 }
7002
7003 #[test]
7006 fn path_search_unmatched_errors() {
7007 let saved = std::env::var("NIX_PATH").ok();
7010 unsafe {
7014 std::env::remove_var("NIX_PATH");
7015 }
7016 let result = eval("<this_should_not_resolve>");
7017 if let Some(v) = saved {
7018 unsafe {
7019 std::env::set_var("NIX_PATH", v);
7020 }
7021 }
7022 assert!(result.is_err());
7023 }
7024
7025 #[test]
7028 fn unary_negate_int() {
7029 assert_eq!(ev("-7"), Value::Int(-7));
7030 }
7031
7032 #[test]
7033 fn unary_negate_float() {
7034 assert_eq!(ev("-2.5"), Value::Float(-2.5));
7035 }
7036
7037 #[test]
7038 fn unary_invert_true() {
7039 assert_eq!(ev("!true"), Value::Bool(false));
7040 }
7041
7042 #[test]
7043 fn unary_invert_false() {
7044 assert_eq!(ev("!false"), Value::Bool(true));
7045 }
7046
7047 #[test]
7048 fn unary_negate_bool_errors() {
7049 let result = eval("-true");
7050 assert!(result.is_err());
7051 }
7052
7053 #[test]
7054 fn unary_invert_int_errors() {
7055 let result = eval("!42");
7056 assert!(result.is_err());
7057 }
7058
7059 #[test]
7062 fn binop_add_attrs_errors() {
7063 let result = eval("{a=1;} + {b=2;}");
7064 assert!(result.is_err());
7065 }
7066
7067 #[test]
7068 fn binop_sub_string_errors() {
7069 let result = eval(r#""a" - "b""#);
7070 assert!(result.is_err());
7071 }
7072
7073 #[test]
7074 fn binop_mul_string_errors() {
7075 let result = eval(r#""a" * "b""#);
7076 assert!(result.is_err());
7077 }
7078
7079 #[test]
7080 fn binop_div_string_errors() {
7081 let result = eval(r#""a" / "b""#);
7082 assert!(result.is_err());
7083 }
7084
7085 #[test]
7086 fn binop_compare_attrs_errors() {
7087 let result = eval("{a=1;} < {b=2;}");
7088 assert!(result.is_err());
7089 }
7090
7091 #[test]
7092 fn binop_div_float_by_zero_int() {
7093 let result = eval("1.0 / 0");
7097 let _ = result;
7100 }
7101
7102 #[test]
7103 fn binop_int_div_zero_is_division_by_zero() {
7104 let result = eval("5 / 0");
7105 match result {
7106 Err(EvalError::DivisionByZero) => {}
7107 other => panic!("expected DivisionByZero, got {other:?}"),
7108 }
7109 }
7110
7111 #[test]
7114 fn if_else_only_chosen_branch_evaluated_then() {
7115 assert_eq!(ev("if true then 42 else 1 / 0"), Value::Int(42));
7118 }
7119
7120 #[test]
7121 fn if_else_only_chosen_branch_evaluated_else() {
7122 assert_eq!(ev("if false then 1 / 0 else 99"), Value::Int(99));
7123 }
7124
7125 #[test]
7126 fn if_condition_must_be_bool() {
7127 let result = eval("if 1 then 1 else 2");
7128 assert!(result.is_err());
7129 }
7130
7131 #[test]
7132 fn if_condition_lazy_does_not_force_unused() {
7133 assert_eq!(
7136 ev("let bad = 1 / 0; in if true then 42 else bad"),
7137 Value::Int(42),
7138 );
7139 }
7140
7141 #[test]
7144 fn and_short_circuits_on_false() {
7145 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
7147 }
7148
7149 #[test]
7150 fn or_short_circuits_on_true() {
7151 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
7152 }
7153
7154 #[test]
7155 fn implication_short_circuits_on_false_lhs() {
7156 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
7158 }
7159
7160 #[test]
7163 fn lambda_fix_combinator_returns_attrset() {
7164 let v = ev(
7166 "let fix = f: let x = f x; in x; in
7167 (fix (self: { val = 1; double = self.val * 2; })).double",
7168 );
7169 assert_eq!(v, Value::Int(2));
7170 }
7171
7172 #[test]
7175 fn rec_attrset_self_reference() {
7176 let v = ev("(rec { a = b; b = 1; }).a");
7178 assert_eq!(v, Value::Int(1));
7179 }
7180
7181 #[test]
7182 fn rec_attrset_inherit_from_uses_outer_scope() {
7183 let v = ev(
7187 "let src = { a = 10; }; in
7188 rec {
7189 inherit (src) a;
7190 b = a + 1;
7191 }",
7192 );
7193 if let Value::Attrs(attrs) = v {
7194 let b = attrs.get("b").unwrap();
7195 let b_forced = force_value(b).unwrap();
7196 assert_eq!(b_forced, Value::Int(11));
7197 } else {
7198 panic!("expected attrs");
7199 }
7200 }
7201
7202 #[test]
7203 fn nonrec_attrset_no_self_reference() {
7204 let result = eval("({ a = 1; b = a + 1; }).b");
7207 assert!(result.is_err());
7208 }
7209
7210 #[test]
7213 fn dotted_binding_three_segments_then_sibling() {
7214 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
7215 if let Value::Attrs(attrs) = v {
7216 let a = attrs.get("a").unwrap();
7217 let a_forced = force_value(a).unwrap();
7218 if let Value::Attrs(a_attrs) = a_forced {
7219 let b = a_attrs.get("b").unwrap();
7220 let b_forced = force_value(b).unwrap();
7221 if let Value::Attrs(b_attrs) = b_forced {
7222 assert_eq!(force_value(b_attrs.get("c").unwrap()).unwrap(), Value::Int(1));
7223 assert_eq!(force_value(b_attrs.get("d").unwrap()).unwrap(), Value::Int(2));
7224 } else {
7225 panic!("expected b to be attrs");
7226 }
7227 assert_eq!(force_value(a_attrs.get("e").unwrap()).unwrap(), Value::Int(3));
7228 } else {
7229 panic!("expected a to be attrs");
7230 }
7231 } else {
7232 panic!("expected outer attrs");
7233 }
7234 }
7235
7236 #[test]
7239 fn rec_dotted_bindings_visible_to_siblings() {
7240 let v = ev("rec { types.openSB = 1; types.openCpu = 2; foo = types.openSB; }.foo");
7243 assert_eq!(v, Value::Int(1));
7244 }
7245
7246 #[test]
7247 fn rec_dotted_leaf_uses_rec_scope() {
7248 let v = ev("rec { types.a = f 1; f = x: x + 1; }.types.a");
7251 assert_eq!(v, Value::Int(2));
7252 }
7253
7254 #[test]
7255 fn rec_dotted_multiple_keys_merge() {
7256 let v = ev("rec { types.a = 1; types.b = 2; x = types; }.x");
7258 if let Value::Attrs(attrs) = v {
7259 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
7260 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
7261 } else {
7262 panic!("expected attrs");
7263 }
7264 }
7265
7266 #[test]
7267 fn rec_nixpkgs_parse_pattern() {
7268 let v = ev(r#"
7272 let
7273 mkOptionType = x: x;
7274 mergeOneOption = "merge";
7275 attrValues = builtins.attrValues;
7276 setType = name: value: { __type = name; } // value;
7277 mapAttrs = builtins.mapAttrs;
7278 enum = xs: mkOptionType { name = "enum"; check = x: builtins.elem x xs; };
7279 setTypes = type: mapAttrs (name: value: setType type.name ({ inherit name; } // value));
7280 in
7281 rec {
7282 types.openSB = mkOptionType { name = "sb"; merge = mergeOneOption; };
7283 types.significantByte = enum (attrValues significantBytes);
7284 significantBytes = setTypes types.openSB { bigEndian = {}; littleEndian = {}; };
7285 types.openCpuType = mkOptionType { name = "cpu-type"; };
7286 types.cpuType = enum (attrValues cpuTypes);
7287 cpuTypes = setTypes types.openCpuType { arm = { bits = 32; }; };
7288 }.types.openCpuType
7289 "#);
7290 if let Value::Attrs(attrs) = v {
7291 assert_eq!(
7292 force_value(attrs.get("name").unwrap()).unwrap(),
7293 Value::string("cpu-type")
7294 );
7295 } else {
7296 panic!("expected attrs");
7297 }
7298 }
7299
7300 #[test]
7301 fn let_dotted_leaf_uses_let_scope() {
7302 let v = ev("let a.x = f 1; f = x: x + 1; in a.x");
7304 assert_eq!(v, Value::Int(2));
7305 }
7306
7307 #[test]
7308 fn let_inherit_from_plus_dotted_overrides() {
7309 let v = ev(r#"
7315 let
7316 src = { types = { existing = true; }; };
7317 inherit (src) types;
7318 types.added = true;
7319 in types
7320 "#);
7321 if let Value::Attrs(attrs) = v {
7322 assert_eq!(
7324 force_value(attrs.get("added").unwrap()).unwrap(),
7325 Value::Bool(true)
7326 );
7327 assert!(attrs.get("existing").is_none());
7329 } else {
7330 panic!("expected attrs");
7331 }
7332 }
7333
7334 #[test]
7337 fn pattern_empty_no_args_no_ellipsis() {
7338 assert_eq!(ev("({}: 1) {}"), Value::Int(1));
7340 }
7341
7342 #[test]
7343 fn pattern_empty_with_ellipsis_accepts_extra() {
7344 assert_eq!(ev("({...}: 1) { a = 1; b = 2; }"), Value::Int(1));
7345 }
7346
7347 #[test]
7348 fn pattern_all_defaults() {
7349 assert_eq!(
7350 ev("({a ? 1, b ? 2}: a + b) {}"),
7351 Value::Int(3),
7352 );
7353 }
7354
7355 #[test]
7356 fn pattern_at_bind_before() {
7357 assert_eq!(ev("(args @ { x }: args.x) { x = 7; }"), Value::Int(7));
7359 }
7360
7361 #[test]
7362 fn pattern_at_bind_after() {
7363 assert_eq!(ev("({ x } @ args: args.x) { x = 7; }"), Value::Int(7));
7365 }
7366
7367 #[test]
7368 fn pattern_default_references_other_arg() {
7369 assert_eq!(ev("({a, b ? a + 1}: b) {a = 10;}"), Value::Int(11));
7371 }
7372
7373 #[test]
7374 fn pattern_required_missing_errors() {
7375 let result = eval("({ a, b }: a) { a = 1; }");
7376 assert!(result.is_err());
7377 }
7378
7379 #[test]
7380 fn pattern_unexpected_errors_without_ellipsis() {
7381 let result = eval("({ a }: a) { a = 1; b = 2; }");
7382 assert!(result.is_err());
7383 }
7384
7385 #[test]
7388 fn apply_int_errors() {
7389 let result = eval("42 5");
7390 assert!(result.is_err());
7391 }
7392
7393 #[test]
7394 fn apply_string_errors() {
7395 let result = eval(r#""hi" 5"#);
7396 assert!(result.is_err());
7397 }
7398
7399 #[test]
7400 fn apply_attrset_without_functor_errors() {
7401 let result = eval("{ x = 1; } 5");
7402 assert!(result.is_err());
7403 let msg = format!("{}", result.unwrap_err());
7404 assert!(msg.contains("__functor") || msg.contains("cannot call"));
7405 }
7406
7407 #[test]
7410 fn select_multi_segment_with_default() {
7411 assert_eq!(ev("{ a = { b = 1; }; }.a.c or 99"), Value::Int(99));
7413 }
7414
7415 #[test]
7416 fn select_from_int_errors() {
7417 let result = eval("(1).x");
7418 assert!(result.is_err());
7419 }
7420
7421 #[test]
7424 fn has_attr_on_non_set_returns_false() {
7425 assert_eq!(ev("1 ? x"), Value::Bool(false));
7427 }
7428
7429 #[test]
7430 fn has_attr_nested_path_present() {
7431 assert_eq!(ev("{ a = { b = 1; }; } ? a.b"), Value::Bool(true));
7432 }
7433
7434 #[test]
7435 fn has_attr_nested_path_missing() {
7436 assert_eq!(ev("{ a = { b = 1; }; } ? a.c"), Value::Bool(false));
7437 }
7438
7439 #[test]
7440 fn has_attr_intermediate_missing_returns_false() {
7441 assert_eq!(ev("{} ? a.b.c"), Value::Bool(false));
7442 }
7443
7444 #[test]
7447 fn list_with_function_value() {
7448 let v = ev("[(x: x + 1)]");
7449 if let Value::List(items) = v {
7450 assert_eq!(items.len(), 1);
7451 let forced = force_value(&items[0]).unwrap();
7453 assert!(matches!(forced, Value::Lambda(_)));
7454 } else {
7455 panic!("expected list");
7456 }
7457 }
7458
7459 #[test]
7462 fn inherit_unknown_name_errors() {
7463 let result = eval("let x = 1; in let inherit nonexistent; in nonexistent");
7464 assert!(result.is_err());
7465 }
7466
7467 #[test]
7470 fn string_concat_no_context_when_both_plain() {
7471 let v = ev(r#""abc" + "def""#);
7472 if let Value::String(ns) = v {
7473 assert_eq!(ns.chars, "abcdef");
7474 assert!(!ns.has_context());
7475 } else {
7476 panic!("expected string");
7477 }
7478 }
7479
7480 #[test]
7483 fn parens_around_expression() {
7484 assert_eq!(ev("(1 + 2)"), Value::Int(3));
7485 }
7486
7487 #[test]
7488 fn nested_parens() {
7489 assert_eq!(ev("(((42)))"), Value::Int(42));
7490 }
7491
7492 #[test]
7495 fn throw_propagates_as_error() {
7496 let result = eval(r#"builtins.throw "kaboom""#);
7497 match result {
7498 Err(EvalError::Throw(s)) => assert!(s.contains("kaboom")),
7499 other => panic!("expected Throw, got {other:?}"),
7500 }
7501 }
7502
7503 #[test]
7504 fn assert_failed_propagates_as_error() {
7505 let result = eval("assert false; 1");
7506 match result {
7507 Err(EvalError::AssertionFailed(_)) => {}
7508 other => panic!("expected AssertionFailed, got {other:?}"),
7509 }
7510 }
7511
7512 #[test]
7515 fn string_no_interp_yields_no_context() {
7516 let v = ev(r#""just literal""#);
7517 if let Value::String(ns) = v {
7518 assert!(!ns.has_context());
7519 } else {
7520 panic!("expected string");
7521 }
7522 }
7523
7524 #[test]
7533 fn interp_path_copies_to_store_byte_matches_cppnix() {
7534 let dir = std::env::temp_dir().join(format!("sui-r5-interp-{}", std::process::id()));
7535 let _ = std::fs::remove_dir_all(&dir);
7536 std::fs::create_dir_all(&dir).unwrap();
7537 let f = dir.join("data.txt");
7538 std::fs::write(&f, b"hello\n").unwrap();
7539 let expr = format!(r#""${{{}}}""#, f.display());
7540 let v = eval(&expr).unwrap();
7541 if let Value::String(ns) = v {
7542 assert_eq!(
7543 ns.chars.to_string(),
7544 "/nix/store/y9dmvfhip31hg8ia4njwjz9vfa3ndphr-data.txt",
7545 );
7546 assert!(ns.has_context());
7547 } else {
7548 panic!("expected string");
7549 }
7550 let _ = std::fs::remove_dir_all(&dir);
7551 }
7552
7553 #[test]
7562 fn parse_error_unbalanced_braces() {
7563 let result = eval("{ a = 1");
7564 assert!(result.is_err());
7565 let err = result.unwrap_err();
7566 assert!(matches!(err, EvalError::ParseError(_)));
7567 }
7568
7569 #[test]
7570 fn parse_error_dangling_let() {
7571 let result = eval("let in");
7572 assert!(result.is_err());
7573 }
7574
7575 #[test]
7576 fn parse_error_empty_input() {
7577 let result = eval("");
7578 assert!(result.is_err());
7579 }
7580
7581 #[test]
7584 fn float_int_subtraction() {
7585 assert_eq!(ev("3.5 - 1"), Value::Float(2.5));
7586 }
7587
7588 #[test]
7589 fn int_float_subtraction() {
7590 assert_eq!(ev("3 - 0.5"), Value::Float(2.5));
7591 }
7592
7593 #[test]
7594 fn float_float_division() {
7595 assert_eq!(ev("6.0 / 2.0"), Value::Float(3.0));
7596 }
7597
7598 #[test]
7599 fn int_float_multiplication() {
7600 assert_eq!(ev("3 * 2.5"), Value::Float(7.5));
7601 }
7602
7603 #[test]
7606 fn compare_int_float_less() {
7607 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
7608 }
7609
7610 #[test]
7611 fn compare_float_int_more() {
7612 assert_eq!(ev("3.5 > 3"), Value::Bool(true));
7613 }
7614
7615 #[test]
7616 fn compare_equal_int_float() {
7617 assert_eq!(ev("3 <= 3.0"), Value::Bool(true));
7618 }
7619
7620 #[test]
7623 fn equal_lists_same() {
7624 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
7625 }
7626
7627 #[test]
7628 fn equal_lists_diff_length() {
7629 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
7630 }
7631
7632 #[test]
7633 fn not_equal_lists() {
7634 assert_eq!(ev("[1] != [2]"), Value::Bool(true));
7635 }
7636
7637 #[test]
7638 fn equal_attrsets_same() {
7639 assert_eq!(ev("{a = 1; b = 2;} == {b = 2; a = 1;}"), Value::Bool(true));
7640 }
7641
7642 #[test]
7649 fn lambda_self_equality_in_attrset() {
7650 assert_eq!(
7652 ev("let f = x: x; in { a = 1; inherit f; } == { a = 1; inherit f; }"),
7653 Value::Bool(true),
7654 );
7655 }
7656
7657 #[test]
7658 fn lambda_self_reference_attrset_equality() {
7659 assert_eq!(
7661 ev("let x = { a = 1; f = y: y; }; in x == x"),
7662 Value::Bool(true),
7663 );
7664 }
7665
7666 #[test]
7667 fn lambda_different_closures_not_equal() {
7668 assert_eq!(
7670 ev("{ f = x: x; } == { f = x: x; }"),
7671 Value::Bool(false),
7672 );
7673 }
7674
7675 #[test]
7676 fn lambda_ne_does_not_force_unused_branch() {
7677 assert_eq!(
7680 ev("let ls = { a = 1; f = x: x; }; in if ls != ls then builtins.throw \"bug\" else 42"),
7681 Value::Int(42),
7682 );
7683 }
7684
7685 #[test]
7688 fn force_value_through_thunk() {
7689 let root = rnix::Root::parse("1 + 2");
7690 let expr = root.tree().expr().unwrap();
7691 let thunk = Thunk::new_suspended(expr, Env::new());
7692 let val = Value::Thunk(thunk);
7693 assert_eq!(force_value(&val).unwrap(), Value::Int(3));
7694 }
7695
7696 #[test]
7699 fn try_eval_catches_thrown_error() {
7700 let v = ev(r#"(builtins.tryEval (builtins.throw "oops")).success"#);
7702 assert_eq!(v, Value::Bool(false));
7703 }
7704
7705 #[test]
7706 fn try_eval_returns_value_on_success() {
7707 let v = ev("(builtins.tryEval 42).value");
7708 assert_eq!(v, Value::Int(42));
7709 }
7710
7711 #[test]
7714 fn legacy_let_returns_body_attr() {
7715 assert_eq!(ev("let { x = 1; body = x + 41; }"), Value::Int(42));
7719 }
7720
7721 #[test]
7722 fn legacy_let_missing_body_errors() {
7723 let result = eval("let { x = 1; }");
7724 assert!(result.is_err());
7725 }
7726
7727 #[test]
7728 fn legacy_let_with_inherit_from_scope() {
7729 assert_eq!(
7730 ev("let outer = 5; in let { inherit outer; body = outer * 2; }"),
7731 Value::Int(10),
7732 );
7733 }
7734
7735 #[test]
7738 fn interp_with_string_concat_preserves_order() {
7739 assert_eq!(
7740 ev(r#"let a = "x"; b = "y"; in "${a}-${b}""#),
7741 Value::string("x-y"),
7742 );
7743 }
7744
7745 #[test]
7746 fn interp_only_literal_part() {
7747 assert_eq!(ev(r#""no interp here""#), Value::string("no interp here"));
7748 }
7749
7750 #[test]
7753 fn dynamic_attr_via_string_key_in_set() {
7754 assert_eq!(ev(r#"{ "a" = 1; }.a"#), Value::Int(1));
7756 }
7757
7758 #[test]
7759 fn dynamic_attr_via_interpolated_key() {
7760 let v = ev(r#"let k = "foo"; in { ${k} = 99; }.foo"#);
7761 assert_eq!(v, Value::Int(99));
7762 }
7763
7764 #[test]
7767 fn select_with_string_key() {
7768 let v = ev(r#"{ a = 42; }."a""#);
7769 assert_eq!(v, Value::Int(42));
7770 }
7771
7772 #[test]
7775 fn apply_attrset_with_functor_works() {
7776 let v = ev("let s = { __functor = self: x: x + 1; }; in s 5");
7777 assert_eq!(v, Value::Int(6));
7778 }
7779
7780 #[test]
7783 fn double_negate_int() {
7784 assert_eq!(ev("- (-5)"), Value::Int(5));
7785 }
7786
7787 #[test]
7790 fn inherit_in_let_makes_name_available() {
7791 assert_eq!(
7792 ev("let src = { a = 7; }; in let inherit (src) a; in a"),
7793 Value::Int(7),
7794 );
7795 }
7796
7797 #[test]
7800 fn path_plus_string_yields_path() {
7801 let v = ev(r#"/foo + "/bar""#);
7802 match v {
7803 Value::Path(p) => assert_eq!(&*p, "/foo/bar"),
7804 _ => panic!("expected path"),
7805 }
7806 }
7807
7808 #[test]
7811 fn attrset_value_not_forced_unless_selected() {
7812 assert_eq!(
7815 ev(r#"{ bad = builtins.throw "boom"; good = 42; }.good"#),
7816 Value::Int(42),
7817 );
7818 }
7819
7820 #[test]
7823 fn lambda_recursive_via_let() {
7824 assert_eq!(
7826 ev("let fact = n: if n == 0 then 1 else n * fact (n - 1); in fact 5"),
7827 Value::Int(120),
7828 );
7829 }
7830
7831 #[test]
7834 fn select_with_dynamic_key_via_var() {
7835 assert_eq!(ev(r#"let k = { x = 1; }; in k.x"#), Value::Int(1));
7838 }
7839
7840 #[test]
7843 fn compare_string_lex_greater_or_equal() {
7844 assert_eq!(ev(r#""b" >= "a""#), Value::Bool(true));
7845 assert_eq!(ev(r#""a" >= "a""#), Value::Bool(true));
7846 assert_eq!(ev(r#""a" >= "b""#), Value::Bool(false));
7847 }
7848
7849 #[test]
7852 fn equal_int_string_false() {
7853 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
7854 }
7855
7856 #[test]
7857 fn equal_null_int_false() {
7858 assert_eq!(ev("null == 0"), Value::Bool(false));
7859 }
7860
7861 #[test]
7864 fn update_with_let_bound_operands() {
7865 assert_eq!(
7866 ev("let a = { x = 1; }; b = { y = 2; }; in (a // b).y"),
7867 Value::Int(2),
7868 );
7869 }
7870
7871 #[test]
7874 fn concat_lists_from_let() {
7875 assert_eq!(
7876 ev("let a = [1 2]; b = [3 4]; in builtins.length (a ++ b)"),
7877 Value::Int(4),
7878 );
7879 }
7880
7881 #[test]
7884 fn interp_list_coerces_with_spaces() {
7885 assert_eq!(
7888 ev(r#""${toString [1 2 3]}""#),
7889 Value::string("1 2 3"),
7890 );
7891 }
7892
7893 #[test]
7894 fn interp_list_directly_coerces() {
7895 assert_eq!(
7897 ev(r#""${[1 2]}""#),
7898 Value::string("1 2"),
7899 );
7900 }
7901
7902 #[test]
7905 fn interp_outpath_attrset() {
7906 assert_eq!(
7907 ev(r#"let x = { outPath = "/nix/store/abc"; }; in "${x}""#),
7908 Value::string("/nix/store/abc"),
7909 );
7910 }
7911
7912 #[test]
7913 fn interp_tostring_takes_priority_over_outpath() {
7914 assert_eq!(
7915 ev(r#"let x = { __toString = self: "custom"; outPath = "/ignored"; }; in "${x}""#),
7916 Value::string("custom"),
7917 );
7918 }
7919
7920 #[test]
7921 fn interp_derivation_coerces_to_outpath() {
7922 let result = eval(r#"
7924 let drv = builtins.derivation {
7925 name = "test";
7926 system = "x86_64-linux";
7927 builder = "/bin/sh";
7928 };
7929 in "${drv}"
7930 "#).unwrap();
7931 if let Value::String(s) = result {
7932 assert!(s.chars.starts_with("/nix/store/"), "got: {}", s.chars);
7933 } else {
7934 panic!("expected string");
7935 }
7936 }
7937
7938 #[test]
7941 fn interp_lambda_errors() {
7942 let result = eval(r#""${x: x}""#);
7943 assert!(result.is_err());
7944 }
7945
7946 #[test]
7949 fn force_value_int_returns_same() {
7950 let v = Value::Int(42);
7951 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
7952 }
7953
7954 #[test]
7955 fn force_value_bool_returns_same() {
7956 let v = Value::Bool(true);
7957 assert_eq!(force_value(&v).unwrap(), Value::Bool(true));
7958 }
7959
7960 #[test]
7961 fn force_value_string_returns_same() {
7962 let v = Value::string("hello");
7963 assert_eq!(force_value(&v).unwrap(), Value::string("hello"));
7964 }
7965
7966 #[test]
7967 fn force_value_attrs_returns_same() {
7968 let mut a = NixAttrs::new();
7969 a.insert("x".to_string(), Value::Int(1));
7970 let v = Value::Attrs(Rc::new(a.clone()));
7971 assert_eq!(force_value(&v).unwrap(), Value::Attrs(Rc::new(a)));
7972 }
7973
7974 #[test]
7975 fn force_value_list_returns_same() {
7976 let v = Value::list(vec![Value::Int(1), Value::Int(2)]);
7977 assert_eq!(
7978 force_value(&v).unwrap(),
7979 Value::list(vec![Value::Int(1), Value::Int(2)]),
7980 );
7981 }
7982
7983 #[test]
7984 fn force_value_null_returns_null() {
7985 let v = Value::Null;
7986 assert_eq!(force_value(&v).unwrap(), Value::Null);
7987 }
7988
7989 #[test]
7990 fn force_value_evaluated_thunk_returns_cached() {
7991 let v = ev("let x = 1 + 2; in x");
7993 assert_eq!(v, Value::Int(3));
7994 assert_eq!(force_value(&v).unwrap(), Value::Int(3));
7996 }
7997
7998 #[test]
8001 fn tco_if_true_condition() {
8002 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
8003 }
8004
8005 #[test]
8006 fn tco_if_false_condition() {
8007 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
8008 }
8009
8010 #[test]
8011 fn tco_deeply_nested_if_else_chain() {
8012 let mut expr = String::from("150");
8015 for i in (1..150).rev() {
8016 expr = format!("if false then {} else {}", i, expr);
8017 }
8018 let v = ev(&expr);
8019 assert_eq!(v, Value::Int(150));
8020 }
8021
8022 #[test]
8023 fn tco_assert_true_passes_through() {
8024 assert_eq!(ev("assert true; 42"), Value::Int(42));
8025 }
8026
8027 #[test]
8028 fn tco_assert_false_throws_assertion_failed() {
8029 let result = eval("assert false; 42");
8030 assert!(result.is_err());
8031 let err = result.unwrap_err();
8032 assert!(
8033 matches!(err, EvalError::AssertionFailed(_)),
8034 "expected AssertionFailed, got: {err}",
8035 );
8036 }
8037
8038 #[test]
8039 fn tco_with_makes_scope_available() {
8040 assert_eq!(ev("with { x = 10; y = 20; }; x + y"), Value::Int(30));
8041 }
8042
8043 #[test]
8044 fn tco_let_in_creates_bindings() {
8045 assert_eq!(ev("let a = 5; in a"), Value::Int(5));
8046 }
8047
8048 #[test]
8049 fn tco_let_in_multiple_bindings() {
8050 assert_eq!(ev("let a = 1; b = 2; c = 3; in a + b + c"), Value::Int(6));
8051 }
8052
8053 #[test]
8056 fn eval_attrset_empty() {
8057 let v = ev("{}");
8058 if let Value::Attrs(attrs) = v {
8059 assert!(attrs.is_empty(), "expected empty attrset");
8060 } else {
8061 panic!("expected attrset, got {v:?}");
8062 }
8063 }
8064
8065 #[test]
8066 fn eval_attrset_simple_kv() {
8067 let v = ev("{ a = 1; b = 2; }");
8068 if let Value::Attrs(attrs) = v {
8069 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8070 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8071 } else {
8072 panic!("expected attrset, got {v:?}");
8073 }
8074 }
8075
8076 #[test]
8077 fn eval_attrset_recursive() {
8078 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
8079 assert_eq!(ev("(rec { a = 1; b = a + 1; }).a"), Value::Int(1));
8080 }
8081
8082 #[test]
8083 fn eval_attrset_inherit_from_scope() {
8084 assert_eq!(ev("let x = 1; in { inherit x; }.x"), Value::Int(1));
8085 }
8086
8087 #[test]
8088 fn eval_attrset_inherit_from_expr() {
8089 assert_eq!(
8090 ev("{ inherit (builtins) true; }.true"),
8091 Value::Bool(true),
8092 );
8093 }
8094
8095 #[test]
8096 fn eval_attrset_dotted_path() {
8097 assert_eq!(ev("{ a.b.c = 1; }.a.b.c"), Value::Int(1));
8098 }
8099
8100 #[test]
8101 fn eval_attrset_update_merge() {
8102 let v = ev("{ a = 1; } // { b = 2; }");
8103 if let Value::Attrs(attrs) = v {
8104 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8105 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8106 } else {
8107 panic!("expected attrset, got {v:?}");
8108 }
8109 }
8110
8111 #[test]
8114 fn eval_apply_simple_function() {
8115 assert_eq!(ev("(x: x + 1) 2"), Value::Int(3));
8116 }
8117
8118 #[test]
8119 fn eval_apply_pattern_destructuring() {
8120 assert_eq!(ev("({a, b}: a + b) { a = 1; b = 2; }"), Value::Int(3));
8121 }
8122
8123 #[test]
8124 fn eval_apply_default_arguments() {
8125 assert_eq!(ev("({a, b ? 0}: a + b) { a = 1; }"), Value::Int(1));
8126 }
8127
8128 #[test]
8129 fn eval_apply_ellipsis() {
8130 assert_eq!(ev("({a, ...}: a) { a = 1; b = 2; }"), Value::Int(1));
8131 }
8132
8133 #[test]
8136 fn eval_select_single_key() {
8137 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
8138 }
8139
8140 #[test]
8141 fn eval_select_multi_level() {
8142 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
8143 }
8144
8145 #[test]
8146 fn eval_select_with_or_default() {
8147 assert_eq!(ev("{}.a or 42"), Value::Int(42));
8148 }
8149
8150 #[test]
8151 fn eval_select_missing_key_without_default_throws() {
8152 let result = eval("{}.a");
8153 assert!(result.is_err());
8154 }
8155
8156 #[test]
8159 fn binop_add_ints() {
8160 assert_eq!(ev("1 + 2"), Value::Int(3));
8161 }
8162
8163 #[test]
8164 fn binop_sub_ints() {
8165 assert_eq!(ev("3 - 1"), Value::Int(2));
8166 }
8167
8168 #[test]
8169 fn binop_mul_ints() {
8170 assert_eq!(ev("2 * 3"), Value::Int(6));
8171 }
8172
8173 #[test]
8174 fn binop_div_ints() {
8175 assert_eq!(ev("6 / 2"), Value::Int(3));
8176 }
8177
8178 #[test]
8179 fn binop_float_arithmetic() {
8180 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
8181 }
8182
8183 #[test]
8184 fn binop_string_concat() {
8185 assert_eq!(
8186 ev(r#""hello" + " " + "world""#),
8187 Value::string("hello world"),
8188 );
8189 }
8190
8191 #[test]
8192 fn binop_list_concat() {
8193 assert_eq!(
8194 ev("[1 2] ++ [3 4]"),
8195 Value::list(vec![
8196 Value::Int(1),
8197 Value::Int(2),
8198 Value::Int(3),
8199 Value::Int(4),
8200 ]),
8201 );
8202 }
8203
8204 #[test]
8205 fn binop_attrset_update() {
8206 let v = ev("{ a = 1; } // { b = 2; }");
8207 if let Value::Attrs(attrs) = v {
8208 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8209 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8210 } else {
8211 panic!("expected attrset, got {v:?}");
8212 }
8213 }
8214
8215 #[test]
8216 fn binop_less_than() {
8217 assert_eq!(ev("1 < 2"), Value::Bool(true));
8218 assert_eq!(ev("2 < 1"), Value::Bool(false));
8219 }
8220
8221 #[test]
8222 fn binop_greater_than() {
8223 assert_eq!(ev("2 > 1"), Value::Bool(true));
8224 assert_eq!(ev("1 > 2"), Value::Bool(false));
8225 }
8226
8227 #[test]
8228 fn binop_equal() {
8229 assert_eq!(ev("1 == 1"), Value::Bool(true));
8230 assert_eq!(ev("1 == 2"), Value::Bool(false));
8231 }
8232
8233 #[test]
8234 fn binop_not_equal() {
8235 assert_eq!(ev("1 != 2"), Value::Bool(true));
8236 assert_eq!(ev("1 != 1"), Value::Bool(false));
8237 }
8238
8239 #[test]
8240 fn binop_logical_and() {
8241 assert_eq!(ev("true && false"), Value::Bool(false));
8242 assert_eq!(ev("true && true"), Value::Bool(true));
8243 }
8244
8245 #[test]
8246 fn binop_logical_or() {
8247 assert_eq!(ev("true || false"), Value::Bool(true));
8248 assert_eq!(ev("false || false"), Value::Bool(false));
8249 }
8250
8251 #[test]
8252 fn binop_logical_not() {
8253 assert_eq!(ev("!true"), Value::Bool(false));
8254 assert_eq!(ev("!false"), Value::Bool(true));
8255 }
8256
8257 #[test]
8258 fn binop_implication() {
8259 assert_eq!(ev("false -> true"), Value::Bool(true));
8260 assert_eq!(ev("false -> false"), Value::Bool(true));
8261 assert_eq!(ev("true -> true"), Value::Bool(true));
8262 assert_eq!(ev("true -> false"), Value::Bool(false));
8263 }
8264}