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
1901fn eval_apply(app: &ast::Apply, env: &Env) -> Result<Value, EvalError> {
1902 let func_expr = app
1903 .lambda()
1904 .ok_or_else(|| EvalError::ParseError("apply missing function".to_string()))?;
1905 let arg_expr = app
1906 .argument()
1907 .ok_or_else(|| EvalError::ParseError("apply missing argument".to_string()))?;
1908 let func = force_value(&eval_expr(&func_expr, env)?)?;
1909 let arg = match &func {
1917 Value::Lambda(_) => {
1918 if let Some(v) = eval_pure_constant_arg(&arg_expr) {
1927 v
1928 } else {
1929 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1930 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1931 }
1932 }
1933 Value::Builtin(b) if b.name == "tryEval" => {
1934 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1935 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1936 }
1937 _ => eval_expr(&arg_expr, env)?,
1938 };
1939 apply(func, arg)
1940}
1941
1942fn eval_pure_constant_arg(arg_expr: &ast::Expr) -> Option<Value> {
1957 match arg_expr {
1958 ast::Expr::Literal(lit) => eval_literal(lit).ok(),
1959 ast::Expr::Str(st) if !str_has_interpolation(st) => {
1960 eval_str(st, &Env::new()).ok()
1962 }
1963 ast::Expr::PathAbs(p) if !parts_have_interpolation(&p.parts()) => {
1964 let text = crate::path::canon_abs(&p.syntax().text().to_string());
1965 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
1966 }
1967 ast::Expr::PathHome(p) if !parts_have_interpolation(&p.parts()) => {
1968 let text = p.syntax().text().to_string();
1969 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
1970 }
1971 _ => None,
1972 }
1973}
1974
1975fn eval_str(s: &ast::Str, env: &Env) -> Result<Value, EvalError> {
1976 let mut result = String::new();
1977 let mut ctx = StringContext::new();
1978 for part in s.normalized_parts() {
1979 match part {
1980 InterpolPart::Literal(text) => result.push_str(&text),
1981 InterpolPart::Interpolation(interpol) => {
1982 let expr = interpol.expr().ok_or_else(|| {
1983 EvalError::ParseError("interpolation missing expr".to_string())
1984 })?;
1985 let val = force_value(&eval_expr(&expr, env)?)?;
1986 let (s, c) = val.coerce_to_string_copy_to_store()?;
1991 result.push_str(&s);
1992 ctx.merge(&c);
1993 }
1994 }
1995 }
1996 Ok(Value::String(Rc::new(NixString::with_context(result, ctx))))
1997}
1998
1999fn parts_have_interpolation(parts: &[InterpolPart<rnix::ast::PathContent>]) -> bool {
2003 parts
2004 .iter()
2005 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2006}
2007
2008fn str_has_interpolation(s: &ast::Str) -> bool {
2012 s.normalized_parts()
2013 .iter()
2014 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2015}
2016
2017fn eval_interpol_path_parts(
2032 parts: &[InterpolPart<rnix::ast::PathContent>],
2033 kind: PathKind,
2034 env: &Env,
2035) -> Result<Value, EvalError> {
2036 let mut text = String::new();
2037 for part in parts {
2038 match part {
2039 InterpolPart::Literal(content) => text.push_str(content.text()),
2040 InterpolPart::Interpolation(interpol) => {
2041 let expr = interpol.expr().ok_or_else(|| {
2042 EvalError::ParseError("path interpolation missing expr".to_string())
2043 })?;
2044 let val = force_value(&eval_expr(&expr, env)?)?;
2045 let (s, _ctx) = val.coerce_to_string()?;
2049 text.push_str(&s);
2050 }
2051 }
2052 }
2053 let resolved = match kind {
2054 PathKind::Rel => {
2057 if let Some(dir) = current_eval_dir() {
2058 let norm = normalize_path(&dir.join(&text));
2059 crate::path::dematerialize(&norm).to_string_lossy().into_owned()
2068 } else {
2069 text
2073 }
2074 }
2075 PathKind::Abs => crate::path::canon_abs(&text),
2083 PathKind::Home => normalize_path(std::path::Path::new(&text))
2086 .to_string_lossy()
2087 .into_owned(),
2088 };
2089 Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))))
2090}
2091
2092#[derive(Clone, Copy)]
2095enum PathKind {
2096 Abs,
2097 Rel,
2098 Home,
2099}
2100
2101fn eval_attr(attr: &ast::Attr, env: &Env) -> Result<String, EvalError> {
2104 eval_attr_maybe_null(attr, env)?
2105 .ok_or_else(|| EvalError::TypeError("null dynamic attribute name".into()))
2106}
2107
2108fn eval_attr_maybe_null(attr: &ast::Attr, env: &Env) -> Result<Option<String>, EvalError> {
2111 match attr {
2112 ast::Attr::Ident(ident) => Ok(Some(ident_text(ident))),
2113 ast::Attr::Dynamic(dyn_) => {
2114 let expr = dyn_
2115 .expr()
2116 .ok_or_else(|| EvalError::ParseError("dynamic attr missing expr".to_string()))?;
2117 let val = force_value(&eval_expr(&expr, env)?)?;
2118 if val == Value::Null {
2121 return Ok(None);
2122 }
2123 Ok(Some(val.as_string()?.to_string()))
2124 }
2125 ast::Attr::Str(s) => {
2126 let val = eval_str(s, env)?;
2127 Ok(Some(val.as_string()?.to_string()))
2128 }
2129 }
2130}
2131
2132fn ident_text(ident: &ast::Ident) -> String {
2134 match ident.ident_token() {
2142 Some(tok) => tok.text().to_string(),
2143 None => ident.syntax().text().to_string(),
2144 }
2145}
2146
2147fn static_attr_offset(attr: &ast::Attr) -> Option<u32> {
2154 let node = match attr {
2155 ast::Attr::Ident(i) => i.syntax(),
2156 ast::Attr::Str(s) => s.syntax(),
2157 ast::Attr::Dynamic(_) => return None,
2158 };
2159 Some(u32::from(node.text_range().start()))
2160}
2161
2162fn attach_attrset_positions(set: &ast::AttrSet, attrs: &mut NixAttrs, env: &Env) {
2169 let mut table = crate::pos::AttrPositions::new(current_eval_file());
2176 for entry in set.entries() {
2177 if let ast::Entry::AttrpathValue(apv) = entry {
2178 let Some(attrpath) = apv.attrpath() else { continue };
2179 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2180 if path_attrs.len() != 1 {
2185 continue;
2186 }
2187 let Some(offset) = static_attr_offset(&path_attrs[0]) else { continue };
2188 if let Ok(Some(name)) = eval_attr_maybe_null(&path_attrs[0], env) {
2191 table.insert(intern(&name), offset);
2192 }
2193 }
2194 }
2195 if !table.is_empty() {
2196 attrs.set_positions(std::rc::Rc::new(table));
2197 }
2198}
2199
2200fn eval_attrset(set: &ast::AttrSet, env: &Env) -> Result<Value, EvalError> {
2201 crate::perf::inc(crate::perf::Counter::Attrset);
2202 let mut attrs = NixAttrs::new();
2203 let is_rec = set.rec_token().is_some();
2204
2205 if is_rec {
2206 let mut rec_env = env.child();
2207 let mut thunks: Vec<(String, Thunk)> = Vec::new();
2208
2209 let mut defined_so_far: HashSet<String> = HashSet::new();
2213
2214 let mut dotted_attrs: NixAttrs = NixAttrs::new();
2220
2221 for entry in set.entries() {
2223 match entry {
2224 ast::Entry::AttrpathValue(apv) => {
2225 let attrpath = apv.attrpath().ok_or_else(|| {
2226 EvalError::ParseError("binding missing attrpath".to_string())
2227 })?;
2228 let value_expr = apv.value().ok_or_else(|| {
2229 EvalError::ParseError("binding missing value".to_string())
2230 })?;
2231 let mut path_keys: Vec<String> = attrpath
2232 .attrs()
2233 .filter_map(|a| eval_attr_maybe_null(&a, env).transpose())
2234 .collect::<Result<_, _>>()?;
2235 if path_keys.is_empty() { continue; }
2237 if path_keys.len() == 1 {
2238 let key = path_keys.pop().unwrap();
2239 let referenced = referenced_idents(&value_expr);
2256 let is_recursive_binding = referenced.contains(key.as_str())
2257 || defined_so_far
2258 .iter()
2259 .any(|n| referenced.contains(n.as_str()));
2260 let value = if is_recursive_binding {
2261 Value::Thunk(Thunk::new_suspended_recursive(
2262 value_expr.clone(),
2263 env.clone(),
2264 ))
2265 } else {
2266 maybe_thunk(&value_expr, env, true, Some(&defined_so_far))
2272 };
2273 rec_env.bind(key.clone(), value.clone());
2274 attrs.insert(key.clone(), value.clone());
2275 if let Value::Thunk(t) = &value {
2276 thunks.push((key.clone(), t.clone()));
2277 }
2278 defined_so_far.insert(key);
2279 } else {
2280 let key = path_keys[0].clone();
2284 let value =
2285 build_nested_attr_thunk(&path_keys[1..], &value_expr, env, &mut thunks);
2286 merge_nested_insert(&mut dotted_attrs, key, value);
2287 }
2288 }
2289 ast::Entry::Inherit(inherit) => {
2290 eval_inherit(&inherit, env, &mut attrs, Some(&mut rec_env), Some(&mut thunks))?;
2291 }
2292 }
2293 }
2294
2295 for (key, value) in dotted_attrs.iter() {
2300 attrs.insert(key.clone(), value.clone());
2301 rec_env.bind(key.clone(), value.clone());
2302 }
2303
2304 for (_key, thunk) in &thunks {
2307 thunk.update_env(&rec_env);
2308 }
2309 } else {
2310 for entry in set.entries() {
2311 match entry {
2312 ast::Entry::AttrpathValue(apv) => {
2313 let attrpath = apv.attrpath().ok_or_else(|| {
2314 EvalError::ParseError("binding missing attrpath".to_string())
2315 })?;
2316 let value_expr = apv.value().ok_or_else(|| {
2317 EvalError::ParseError("binding missing value".to_string())
2318 })?;
2319 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2320 let tail_is_dynamic =
2330 path_attrs.len() > 1 && attrs_have_dynamic(&path_attrs[1..]);
2331 let head_key = match eval_attr_maybe_null(&path_attrs[0], env)? {
2332 Some(k) => k,
2333 None => continue,
2335 };
2336 if tail_is_dynamic && attrs.get(&head_key).is_none() {
2337 let value =
2338 build_deferred_tail_attr(&path_attrs[1..], &value_expr, env);
2339 attrs.insert(head_key, value);
2340 continue;
2341 }
2342 if tail_is_dynamic {
2356 if let Some(existing) = attrs.get(&head_key).cloned() {
2357 let merged = merge_deferred_dynamic_tail(
2358 existing,
2359 &path_attrs[1..],
2360 &value_expr,
2361 env,
2362 )?;
2363 attrs.insert(head_key, merged);
2364 continue;
2365 }
2366 }
2367 let mut path_keys: Vec<String> = {
2370 let mut v = Vec::with_capacity(path_attrs.len());
2371 v.push(head_key);
2372 let mut skip = false;
2373 for a in &path_attrs[1..] {
2374 match eval_attr_maybe_null(a, env)? {
2375 Some(k) => v.push(k),
2376 None => { skip = true; break; }
2377 }
2378 }
2379 if skip { v.clear(); }
2380 v
2381 };
2382 if path_keys.is_empty() { continue; }
2384 if path_keys.len() == 1 {
2385 let key = path_keys.pop().unwrap();
2386 let value = maybe_thunk(&value_expr, env, false, None);
2389 if matches!(attrs.get(&key), Some(Value::Attrs(_))) {
2402 let forced = force_value(&value)?;
2403 merge_nested_insert(&mut attrs, key, forced);
2404 } else {
2405 attrs.insert(key, value);
2406 }
2407 } else {
2408 let key = path_keys[0].clone();
2409 let value = build_nested_attr(&path_keys[1..], &value_expr, env)?;
2410 if matches!(attrs.get(&key), Some(Value::Thunk(_))) {
2424 let existing = attrs.get(&key).cloned().unwrap();
2425 let forced = force_value(&existing)?;
2426 attrs.insert(key.clone(), forced);
2427 }
2428 merge_nested_insert(&mut attrs, key, value);
2429 }
2430 }
2431 ast::Entry::Inherit(inherit) => {
2432 eval_inherit(&inherit, env, &mut attrs, None, None)?;
2433 }
2434 }
2435 }
2436 }
2437
2438 attach_attrset_positions(set, &mut attrs, env);
2444
2445 Ok(Value::Attrs(Rc::new(attrs)))
2446}
2447
2448fn eval_inherit(
2449 inherit: &ast::Inherit,
2450 env: &Env,
2451 attrs: &mut NixAttrs,
2452 bind_env: Option<&mut Env>,
2453 mut thunks: Option<&mut Vec<(String, Thunk)>>,
2454) -> Result<(), EvalError> {
2455 if let Some(from) = inherit.from() {
2456 let source_expr = from
2476 .expr()
2477 .ok_or_else(|| EvalError::ParseError("inherit from missing expr".to_string()))?;
2478 let source_thunk = Thunk::new_suspended(source_expr, env.clone());
2482 let mut be = bind_env;
2483 for attr in inherit.attrs() {
2484 let name = eval_attr(&attr, env)?;
2485 let thunk = Thunk::new_inherit_select(source_thunk.clone(), name.clone());
2486 let value = Value::Thunk(thunk.clone());
2487 attrs.insert(name.clone(), value.clone());
2488 if let Some(ref mut e) = be {
2489 e.bind(name.clone(), value);
2490 }
2491 if let Some(ref mut t) = thunks {
2492 t.push((name, thunk));
2493 }
2494 }
2495 } else {
2496 let mut be = bind_env;
2512 for attr in inherit.attrs() {
2513 let name = eval_attr(&attr, env)?;
2514 let sym = crate::value::intern(&name);
2515 let value = if let Some(v) = env.lookup_fast(sym, &name) {
2516 v
2517 } else if let Some((scope_cache, scope_value)) =
2518 env.innermost_with_scope()
2519 {
2520 Value::Thunk(Thunk::new_with_ident(
2521 SmolStr::from(name.as_str()),
2522 scope_cache,
2523 scope_value,
2524 env.clone(),
2525 ))
2526 } else {
2527 return Err(EvalError::UndefinedVar(format!(
2528 "'{name}'{}",
2529 eval_file_ctx()
2530 )));
2531 };
2532 attrs.insert(name.clone(), value.clone());
2533 if let Some(ref mut e) = be {
2534 e.bind(name, value);
2535 }
2536 }
2537 }
2538 Ok(())
2539}
2540
2541fn build_nested_attr(
2542 path: &[String],
2543 expr: &ast::Expr,
2544 env: &Env,
2545) -> Result<Value, EvalError> {
2546 if path.is_empty() {
2547 return Ok(maybe_thunk(expr, env, false, None));
2552 }
2553 let key = path[0].clone();
2554 let inner = build_nested_attr(&path[1..], expr, env)?;
2555 let mut attrs = NixAttrs::new();
2556 attrs.insert(key, inner);
2557 Ok(Value::Attrs(Rc::new(attrs)))
2558}
2559
2560fn attr_is_dynamic(attr: &ast::Attr) -> bool {
2581 match attr {
2582 ast::Attr::Dynamic(_) => true,
2583 ast::Attr::Str(s) => s
2586 .normalized_parts()
2587 .iter()
2588 .any(|p| matches!(p, InterpolPart::Interpolation(_))),
2589 ast::Attr::Ident(_) => false,
2590 }
2591}
2592
2593fn attrs_have_dynamic(attrs: &[ast::Attr]) -> bool {
2601 attrs.iter().any(attr_is_dynamic)
2602}
2603
2604fn build_deferred_tail_attr(
2617 tail: &[ast::Attr],
2618 value_expr: &ast::Expr,
2619 env: &Env,
2620) -> Value {
2621 let tail: Vec<ast::Attr> = tail.to_vec();
2622 let value_expr = value_expr.clone();
2623 let env = env.clone();
2624 Value::Thunk(Thunk::new_native(move || {
2625 build_tail_attrs_now(&tail, &value_expr, &env)
2626 }))
2627}
2628
2629fn build_tail_attrs_now(
2650 tail: &[ast::Attr],
2651 value_expr: &ast::Expr,
2652 env: &Env,
2653) -> Result<Value, EvalError> {
2654 if tail.is_empty() {
2655 return Ok(maybe_thunk(value_expr, env, false, None));
2656 }
2657 if std::env::var_os("SUI_M26_TAILTRACE").is_some() {
2658 let t: String = tail[0].syntax().text().to_string().chars().take(40).collect();
2659 eprintln!("[M26 TAIL-RESOLVE] forcing dynamic tail key `{t}`");
2660 if attrs_have_dynamic(&tail[..1]) {
2661 crate::trace::dump_force_stack_ids();
2662 }
2663 }
2664 let key = match eval_attr_maybe_null(&tail[0], env)? {
2665 Some(k) => k,
2666 None => return Ok(Value::Attrs(Rc::new(NixAttrs::new()))),
2669 };
2670 let inner = if tail.len() == 1 {
2676 maybe_thunk(value_expr, env, false, None)
2677 } else {
2678 build_deferred_tail_attr(&tail[1..], value_expr, env)
2679 };
2680 let mut attrs = NixAttrs::new();
2681 attrs.insert(key, inner);
2682 Ok(Value::Attrs(Rc::new(attrs)))
2683}
2684
2685fn merge_deferred_dynamic_tail(
2703 existing: Value,
2704 tail: &[ast::Attr],
2705 value_expr: &ast::Expr,
2706 env: &Env,
2707) -> Result<Value, EvalError> {
2708 debug_assert!(!tail.is_empty());
2711
2712 if attr_is_dynamic(&tail[0]) {
2717 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2718 return Ok(lazy_overlay_merge(existing, deferred));
2719 }
2720
2721 let key = match eval_attr_maybe_null(&tail[0], env)? {
2724 Some(k) => k,
2725 None => return Ok(existing),
2726 };
2727
2728 let existing_forced = force_value(&existing)?;
2732 let mut base = match existing_forced {
2733 Value::Attrs(a) => (*a).clone(),
2734 _ => {
2739 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2740 return Ok(deferred);
2741 }
2742 };
2743
2744 let child_existing = base.get(&key).cloned();
2746 let new_child = match child_existing {
2747 Some(child) if tail.len() > 1 => {
2748 merge_deferred_dynamic_tail(child, &tail[1..], value_expr, env)?
2750 }
2751 Some(child) => {
2752 let leaf = maybe_thunk(value_expr, env, false, None);
2755 lazy_overlay_merge(child, leaf)
2756 }
2757 None if tail.len() > 1 => {
2758 build_deferred_tail_attr(&tail[1..], value_expr, env)
2762 }
2763 None => maybe_thunk(value_expr, env, false, None),
2764 };
2765 base.insert(key, new_child);
2766 Ok(Value::Attrs(Rc::new(base)))
2767}
2768
2769fn lazy_overlay_merge(left: Value, right: Value) -> Value {
2776 match (&left, &right) {
2777 (Value::Attrs(la), Value::Attrs(_)) => {
2778 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2779 let mut merged = (**la).clone();
2780 if let Value::Attrs(ra) = &right {
2781 for (k, v) in ra.iter_unsorted() {
2785 merge_nested_insert(&mut merged, k.clone(), v.clone());
2786 }
2787 }
2788 Value::Attrs(Rc::new(merged))
2789 }
2790 _ => {
2791 Value::Thunk(Thunk::new_native(move || {
2795 let lf = force_value(&left)?;
2796 let rf = force_value(&right)?;
2797 let la = lf.as_attrs()?;
2798 let ra = rf.as_attrs()?;
2799 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2800 let mut merged = (*la).clone();
2801 for (k, v) in ra.iter_unsorted() {
2802 merge_nested_insert(&mut merged, k.clone(), v.clone());
2803 }
2804 Ok(Value::Attrs(Rc::new(merged)))
2805 }))
2806 }
2807 }
2808}
2809
2810fn build_nested_attr_thunk(
2818 path: &[String],
2819 expr: &ast::Expr,
2820 env: &Env,
2821 thunks: &mut Vec<(String, Thunk)>,
2822) -> Value {
2823 if path.is_empty() {
2824 let thunk = Thunk::new_suspended(expr.clone(), env.clone());
2825 let val = Value::Thunk(thunk.clone());
2826 thunks.push((String::new(), thunk));
2827 return val;
2828 }
2829 let key = path[0].clone();
2830 let inner = build_nested_attr_thunk(&path[1..], expr, env, thunks);
2831 let mut attrs = NixAttrs::new();
2832 attrs.insert(key, inner);
2833 Value::Attrs(Rc::new(attrs))
2834}
2835
2836fn merge_nested_insert(target: &mut NixAttrs, key: String, value: Value) {
2843 let existing = match target.get(&key) {
2847 Some(e) => e.clone(),
2848 None => {
2849 target.insert(key, value);
2850 return;
2851 }
2852 };
2853 let value = match value {
2877 Value::Thunk(_) => match force_value(&value) {
2878 Ok(v @ Value::Attrs(_)) => v,
2879 _ => value,
2880 },
2881 other => other,
2882 };
2883 if !matches!(value, Value::Attrs(_)) {
2884 target.insert(key, value);
2885 return;
2886 }
2887 let existing_concrete = match &existing {
2890 Value::Attrs(_) => existing.clone(),
2891 Value::Thunk(_) => match force_value(&existing) {
2892 Ok(v @ Value::Attrs(_)) => v,
2893 _ => {
2894 target.insert(key, value);
2895 return;
2896 }
2897 },
2898 _ => {
2899 target.insert(key, value);
2900 return;
2901 }
2902 };
2903 let mut existing_attrs = match existing_concrete {
2907 Value::Attrs(a) => (*a).clone(),
2908 _ => unreachable!(),
2909 };
2910 let new_attrs = match value {
2911 Value::Attrs(ref a) => a,
2912 _ => unreachable!(),
2913 };
2914 for (k, v) in new_attrs.iter_unsorted() {
2915 merge_nested_insert(&mut existing_attrs, k.clone(), v.clone());
2916 }
2917 target.insert(key, Value::Attrs(Rc::new(existing_attrs)));
2918}
2919
2920fn eval_entries<N: HasEntry + AstNode>(node: &N, env: &mut Env) -> Result<(), EvalError> {
2922 for entry in node.entries() {
2923 match entry {
2924 ast::Entry::AttrpathValue(apv) => {
2925 let attrpath = apv.attrpath().ok_or_else(|| {
2926 EvalError::ParseError("binding missing attrpath".to_string())
2927 })?;
2928 let value_expr = apv.value().ok_or_else(|| {
2929 EvalError::ParseError("binding missing value".to_string())
2930 })?;
2931 let mut path_keys: Vec<String> = attrpath
2932 .attrs()
2933 .map(|a| eval_attr(&a, env))
2934 .collect::<Result<_, _>>()?;
2935 if path_keys.len() == 1 {
2936 let key = path_keys.pop().unwrap();
2937 let value = eval_expr(&value_expr, env)?;
2938 env.bind(key, value);
2939 }
2940 }
2942 ast::Entry::Inherit(inherit) => {
2943 if let Some(from) = inherit.from() {
2944 let source_expr = from.expr().ok_or_else(|| {
2945 EvalError::ParseError("inherit from missing expr".to_string())
2946 })?;
2947 let source = force_value(&eval_expr(&source_expr, env)?)?;
2948 let source_attrs = source.as_attrs()?;
2949 for attr in inherit.attrs() {
2950 let name = eval_attr(&attr, env)?;
2951 let value = source_attrs
2952 .get(&name)
2953 .cloned()
2954 .ok_or_else(|| EvalError::AttrNotFound(
2955 format!("'{name}' in inherit{}", eval_file_ctx()),
2956 ))?;
2957 env.bind(name, value);
2958 }
2959 } else {
2960 for attr in inherit.attrs() {
2961 let name = eval_attr(&attr, env)?;
2962 let value = env
2963 .lookup(&name)
2964 .ok_or_else(|| EvalError::UndefinedVar(
2965 format!("'{name}'{}", eval_file_ctx()),
2966 ))?;
2967 env.bind(name, value);
2968 }
2969 }
2970 }
2971 }
2972 }
2973 Ok(())
2974}
2975
2976fn eval_binop(
2977 op: ast::BinOpKind,
2978 lhs: &ast::Expr,
2979 rhs: &ast::Expr,
2980 env: &Env,
2981) -> Result<Value, EvalError> {
2982 match op {
2984 ast::BinOpKind::And => {
2985 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
2986 if !l {
2987 return Ok(Value::Bool(false));
2988 }
2989 return eval_expr(rhs, env);
2990 }
2991 ast::BinOpKind::Or => {
2992 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
2993 if l {
2994 return Ok(Value::Bool(true));
2995 }
2996 return eval_expr(rhs, env);
2997 }
2998 ast::BinOpKind::Implication => {
2999 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3000 if !l {
3001 return Ok(Value::Bool(true));
3002 }
3003 return eval_expr(rhs, env);
3004 }
3005 _ => {}
3006 }
3007
3008 let lc = force_concrete(&eval_expr(lhs, env)?)?;
3009 let rc = force_concrete(&eval_expr(rhs, env)?)?;
3010 let l = lc.into_value();
3017 let r = rc.into_value();
3018
3019 match op {
3020 ast::BinOpKind::Add => match (&l, &r) {
3021 (Value::Int(a), Value::Int(b)) => a
3022 .checked_add(*b)
3023 .map(Value::Int)
3024 .ok_or_else(|| int_overflow("adding", *a, '+', *b)),
3025 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
3026 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
3027 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a + *b as f64)),
3028 (Value::String(a), Value::String(b)) => {
3029 let mut ctx = a.context.clone();
3030 ctx.merge(&b.context);
3031 let mut s = String::with_capacity(a.chars.len() + b.chars.len());
3039 s.push_str(&a.chars);
3040 s.push_str(&b.chars);
3041 Ok(Value::String(Rc::new(NixString::with_context(s, ctx))))
3042 }
3043 (Value::Path(a), Value::String(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}{}", b.chars).as_str())))),
3044 (Value::Path(a), Value::Path(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}/{b}").as_str())))),
3045 (Value::Attrs(_), _) | (_, Value::Attrs(_)) => {
3047 let (ls, lctx) = l.coerce_to_string()?;
3048 let (rs, rctx) = r.coerce_to_string()?;
3049 let mut ctx = lctx;
3050 ctx.merge(&rctx);
3051 Ok(Value::String(Rc::new(NixString::with_context(
3052 format!("{ls}{rs}"),
3053 ctx,
3054 ))))
3055 }
3056 _ => Err(EvalError::op_type("add", l.type_name(), r.type_name())),
3057 },
3058 ast::BinOpKind::Sub => num_op(
3059 &l,
3060 &r,
3061 |a, b| a.checked_sub(b),
3062 |a, b| a - b,
3063 |a, b| int_overflow("subtracting", a, '-', b),
3064 ),
3065 ast::BinOpKind::Mul => num_op(
3066 &l,
3067 &r,
3068 |a, b| a.checked_mul(b),
3069 |a, b| a * b,
3070 |a, b| int_overflow("multiplying", a, '*', b),
3071 ),
3072 ast::BinOpKind::Div => {
3073 let rhs_is_zero = match &r {
3082 Value::Int(0) => true,
3083 Value::Float(f) => *f == 0.0,
3084 _ => false,
3085 };
3086 if rhs_is_zero {
3087 return Err(EvalError::DivisionByZero);
3088 }
3089 num_op(
3090 &l,
3091 &r,
3092 |a, b| a.checked_div(b),
3093 |a, b| a / b,
3094 |a, b| int_overflow("dividing", a, '/', b),
3095 )
3096 }
3097 ast::BinOpKind::Equal => Ok(Value::Bool(l == r)),
3098 ast::BinOpKind::NotEqual => Ok(Value::Bool(l != r)),
3099 ast::BinOpKind::Less => compare(&l, &r, |o| o == std::cmp::Ordering::Less),
3100 ast::BinOpKind::LessOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Greater),
3101 ast::BinOpKind::More => compare(&l, &r, |o| o == std::cmp::Ordering::Greater),
3102 ast::BinOpKind::MoreOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Less),
3103 ast::BinOpKind::Update => {
3104 let la = l.to_attrs()?;
3105 let ra = r.to_attrs()?;
3106 Ok(Value::Attrs(Rc::new(la.overlay(ra))))
3108 }
3109 ast::BinOpKind::Concat => {
3110 crate::value::concat_lists(l, r.as_list()?)
3120 }
3121 ast::BinOpKind::And | ast::BinOpKind::Or | ast::BinOpKind::Implication => {
3122 unreachable!("handled above")
3123 }
3124 ast::BinOpKind::PipeRight | ast::BinOpKind::PipeLeft => {
3125 Err(EvalError::NotImplemented("pipe operators".to_string()))
3126 }
3127 }
3128}
3129
3130#[inline]
3135fn int_overflow(verb: &str, a: i64, sym: char, b: i64) -> EvalError {
3136 EvalError::Abort(format!("integer overflow in {verb} {a} {sym} {b}"))
3137}
3138
3139fn num_op(
3140 l: &Value,
3141 r: &Value,
3142 int_op: impl Fn(i64, i64) -> Option<i64>,
3143 float_op: impl Fn(f64, f64) -> f64,
3144 overflow: impl Fn(i64, i64) -> EvalError,
3145) -> Result<Value, EvalError> {
3146 match (l, r) {
3147 (Value::Int(a), Value::Int(b)) => {
3148 int_op(*a, *b).map(Value::Int).ok_or_else(|| overflow(*a, *b))
3149 }
3150 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(float_op(*a, *b))),
3151 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(float_op(*a as f64, *b))),
3152 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(float_op(*a, *b as f64))),
3153 _ => Err(EvalError::op_type("perform arithmetic on", l.type_name(), r.type_name())),
3154 }
3155}
3156
3157fn compare(
3158 l: &Value,
3159 r: &Value,
3160 pred: impl Fn(std::cmp::Ordering) -> bool,
3161) -> Result<Value, EvalError> {
3162 let ord = match (l, r) {
3163 (Value::Int(a), Value::Int(b)) => a.cmp(b),
3164 (Value::Float(a), Value::Float(b)) => {
3165 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
3166 }
3167 (Value::Int(a), Value::Float(b)) => (*a as f64)
3168 .partial_cmp(b)
3169 .unwrap_or(std::cmp::Ordering::Equal),
3170 (Value::Float(a), Value::Int(b)) => a
3171 .partial_cmp(&(*b as f64))
3172 .unwrap_or(std::cmp::Ordering::Equal),
3173 (Value::String(a), Value::String(b)) => a.chars.cmp(&b.chars),
3174 _ => {
3175 return Err(EvalError::op_type("compare", l.type_name(), r.type_name()));
3176 }
3177 };
3178 Ok(Value::Bool(pred(ord)))
3179}
3180
3181pub fn apply_and_force(func: Value, arg: Value) -> Result<Value, EvalError> {
3195 force_value(&apply(func, arg)?)
3196}
3197
3198pub fn apply(func: Value, arg: Value) -> Result<Value, EvalError> {
3199 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || apply_inner(func, arg))
3200}
3201
3202fn apply_inner(func: Value, arg: Value) -> Result<Value, EvalError> {
3203 crate::perf::inc(crate::perf::Counter::Apply);
3204 let func = force_concrete(&func)?.into_value();
3205 match func {
3206 Value::Lambda(closure) => {
3207 if crate::perf::enabled() {
3209 APPLY_SITES.with(|sites| {
3210 let file = closure.env.eval_file()
3211 .map(|p| p.display().to_string())
3212 .unwrap_or_else(|| "<eval>".into());
3213 let param_name = match &closure.param {
3215 rnix::ast::Param::IdentParam(ip) => ip.ident().map(|i| ident_text(&i)).unwrap_or_default(),
3216 rnix::ast::Param::Pattern(pat) => {
3217 let mut names: Vec<String> = pat.pat_entries()
3218 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3219 .take(3)
3220 .collect();
3221 if pat.pat_entries().count() > 3 { names.push("...".to_string()); }
3222 format!("{{{}}}", names.join(","))
3223 }
3224 };
3225 let key = format!("{}:{}", file.rsplit_once("-source/").map_or(file.as_str(), |(_,s)| s), param_name);
3226 *sites.borrow_mut().entry(key).or_insert(0u64) += 1;
3227 });
3228 }
3229 let mut call_env = closure.env.child();
3230 let _file_guard = closure
3231 .env
3232 .eval_file()
3233 .cloned()
3234 .map(push_eval_file);
3235 let _trace = push_nix_trace_lambda(&closure.env);
3241 match &closure.param {
3242 rnix::ast::Param::IdentParam(_) => {
3243 bind_param(&closure.param, &arg, &mut call_env)?;
3246 }
3247 rnix::ast::Param::Pattern(_) => {
3248 let forced_arg = force_concrete(&arg)?.into_value();
3250 bind_param(&closure.param, &forced_arg, &mut call_env)?;
3251 }
3252 }
3253 eval_expr(&closure.body, &call_env)
3254 }
3255 Value::Builtin(b) => {
3256 let _trace = push_nix_trace(format!("while calling the '{}' builtin", b.name));
3257 if b.name == "tryEval"
3264 || b.name == "addErrorContext<partial>"
3265 || b.name == "seq<partial>"
3266 || b.name == "deepSeq<partial>"
3267 {
3268 (b.func)(&[arg])
3269 } else {
3270 let forced_arg = force_value(&arg)?;
3271 (b.func)(&[forced_arg])
3272 }
3273 }
3274 Value::Attrs(ref attrs) => {
3275 if let Some(functor) = attrs.get("__functor") {
3276 let functor = force_value(functor)?;
3277 let partial = apply(functor, func.clone())?;
3279 apply(partial, arg)
3280 } else if crate::value::in_promise_eval() {
3281 Ok(Value::Null)
3286 } else {
3287 Err(EvalError::type_error(
3288 format!("cannot call {} (missing __functor){}", func.type_name(), eval_file_ctx()),
3289 ))
3290 }
3291 }
3292 _ if crate::value::in_promise_eval() => {
3293 Ok(Value::Null)
3298 }
3299 _ => Err(EvalError::type_error(
3300 format!("cannot call {}{}", func.type_name(), eval_file_ctx()),
3301 )),
3302 }
3303}
3304
3305fn bind_param(param: &ast::Param, arg: &Value, env: &mut Env) -> Result<(), EvalError> {
3306 match param {
3307 ast::Param::IdentParam(ip) => {
3308 let ident = ip
3309 .ident()
3310 .ok_or_else(|| EvalError::ParseError("ident param missing ident".to_string()))?;
3311 let name = ident_text(&ident);
3312 env.bind(name, arg.clone());
3313 }
3314 ast::Param::Pattern(pat) => {
3315 let attrs = arg.as_attrs()?;
3316
3317 if let Some(pat_bind) = pat.pat_bind()
3319 && let Some(ident) = pat_bind.ident()
3320 {
3321 let name = ident_text(&ident);
3322 env.bind(name, arg.clone());
3323 }
3324
3325 let has_ellipsis = pat.ellipsis_token().is_some();
3326 let entries: Vec<ast::PatEntry> = pat.pat_entries().collect();
3327
3328 let mut default_thunks: Vec<Thunk> = Vec::new();
3335
3336 for entry in &entries {
3337 let ident = entry.ident().ok_or_else(|| {
3338 EvalError::ParseError("pat entry missing ident".to_string())
3339 })?;
3340 let name = ident_text(&ident);
3341 let value = if let Some(v) = attrs.get(&name) {
3342 v.clone()
3343 } else if let Some(default_expr) = entry.default() {
3344 let thunk = Thunk::new_suspended(
3350 ast::Expr::cast(default_expr.syntax().clone()).unwrap(),
3351 env.clone(),
3352 );
3353 default_thunks.push(thunk.clone());
3354 Value::Thunk(thunk)
3355 } else {
3356 return Err(EvalError::type_error(
3357 format!("missing argument '{name}'{}", eval_file_ctx()),
3358 ));
3359 };
3360 env.bind(name, value);
3361 }
3362
3363 for thunk in &default_thunks {
3365 thunk.update_env(env);
3366 }
3367
3368 if !has_ellipsis {
3369 let entry_names: std::collections::HashSet<String> = entries
3370 .iter()
3371 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3372 .collect();
3373 for key in attrs.keys() {
3374 if !entry_names.contains(key.as_str()) {
3375 return Err(EvalError::type_error(
3376 format!("unexpected argument '{key}'{}", eval_file_ctx()),
3377 ));
3378 }
3379 }
3380 }
3381 }
3382 }
3383 Ok(())
3384}
3385
3386#[cfg(test)]
3387mod tests {
3388 use super::*;
3389
3390 fn ev(input: &str) -> Value {
3391 eval(input).unwrap()
3392 }
3393
3394 #[test]
3401 fn is_self_recursive_binding_ignores_attribute_names() {
3402 fn expr(s: &str) -> ast::Expr {
3403 rnix::Root::parse(s).tree().expr().expect("parse")
3404 }
3405 assert!(!is_self_recursive_binding(&expr("lhs.placeholder"), "placeholder"));
3407 assert!(!is_self_recursive_binding(&expr("{ placeholder = 1; }"), "placeholder"));
3408 assert!(!is_self_recursive_binding(
3409 &expr("if lhs.placeholder == rhs.placeholder then lhs.placeholder else null"),
3410 "placeholder",
3411 ));
3412 assert!(is_self_recursive_binding(&expr("placeholder + 1"), "placeholder"));
3414 assert!(is_self_recursive_binding(
3415 &expr("if placeholder then 1 else 2"),
3416 "placeholder"
3417 ));
3418 }
3419
3420 #[test]
3424 fn maybe_thunk_eager_constant_str_is_byte_identical() {
3425 fn expr(s: &str) -> ast::Expr {
3426 rnix::Root::parse(s).tree().expr().expect("parse")
3427 }
3428 let env = Env::new();
3429 let v = maybe_thunk(&expr(r#""abc""#), &env, false, None);
3431 assert!(matches!(v, Value::String(_)), "constant str should be eager, got {v:?}");
3432 assert_eq!(force_value(&v).unwrap(), Value::string("abc"));
3433 let vi = maybe_thunk(&expr(r#""a${b}c""#), &env, false, None);
3435 assert!(matches!(vi, Value::Thunk(_)), "interpolated str must stay thunked");
3436 }
3437
3438 #[test]
3442 fn eval_pure_constant_arg_classification() {
3443 fn expr(s: &str) -> ast::Expr {
3444 rnix::Root::parse(s).tree().expr().expect("parse")
3445 }
3446 assert!(eval_pure_constant_arg(&expr("42")).is_some());
3448 assert!(eval_pure_constant_arg(&expr("3.14")).is_some());
3449 assert!(eval_pure_constant_arg(&expr(r#""const""#)).is_some());
3450 assert!(eval_pure_constant_arg(&expr("/abs/path")).is_some());
3451 assert!(eval_pure_constant_arg(&expr(r#""a${b}c""#)).is_none(), "interpolated str");
3453 assert!(eval_pure_constant_arg(&expr("true")).is_none(), "bool is an ident");
3456 assert!(eval_pure_constant_arg(&expr("x")).is_none(), "ident (with-scope force)");
3457 assert!(eval_pure_constant_arg(&expr("a.b")).is_none(), "select (fixpoint)");
3458 assert!(eval_pure_constant_arg(&expr("f x")).is_none(), "apply (may throw)");
3459 assert!(eval_pure_constant_arg(&expr("1 + 1")).is_none(), "binop (may throw)");
3460 assert!(eval_pure_constant_arg(&expr("throw \"x\"")).is_none(), "throw stays lazy");
3461 }
3462
3463 #[test]
3467 fn ignored_throwing_arg_stays_lazy() {
3468 assert_eq!(ev(r#"(x: 7) (throw "boom")"#), Value::Int(7));
3469 assert_eq!(ev(r#"(x: 7) "const""#), Value::Int(7));
3471 assert_eq!(ev(r#"(x: x) "used""#), Value::string("used"));
3473 }
3474
3475 #[test]
3476 fn eval_int() { assert_eq!(ev("42"), Value::Int(42)); }
3477
3478 #[test]
3479 fn eval_float() { assert_eq!(ev("3.14"), Value::Float(3.14)); }
3480
3481 #[test]
3482 fn eval_string() { assert_eq!(ev(r#""hello""#), Value::string("hello")); }
3483
3484 #[test]
3485 fn eval_bool() { assert_eq!(ev("true"), Value::Bool(true)); }
3486
3487 #[test]
3488 fn eval_null() { assert_eq!(ev("null"), Value::Null); }
3489
3490 #[test]
3491 fn eval_arithmetic() {
3492 assert_eq!(ev("1 + 2"), Value::Int(3));
3493 assert_eq!(ev("10 - 3"), Value::Int(7));
3494 assert_eq!(ev("2 * 3"), Value::Int(6));
3495 assert_eq!(ev("10 / 3"), Value::Int(3));
3496 }
3497
3498 #[test]
3499 fn eval_precedence() {
3500 assert_eq!(ev("1 + 2 * 3"), Value::Int(7));
3501 assert_eq!(ev("(1 + 2) * 3"), Value::Int(9));
3502 }
3503
3504 #[test]
3505 fn eval_comparison() {
3506 assert_eq!(ev("1 == 1"), Value::Bool(true));
3507 assert_eq!(ev("1 == 2"), Value::Bool(false));
3508 assert_eq!(ev("1 < 2"), Value::Bool(true));
3509 assert_eq!(ev("2 <= 2"), Value::Bool(true));
3510 }
3511
3512 #[test]
3513 fn eval_logic() {
3514 assert_eq!(ev("true && false"), Value::Bool(false));
3515 assert_eq!(ev("true || false"), Value::Bool(true));
3516 assert_eq!(ev("!true"), Value::Bool(false));
3517 }
3518
3519 #[test]
3520 fn eval_string_concat() {
3521 assert_eq!(ev(r#""hello" + " " + "world""#), Value::string("hello world"));
3522 }
3523
3524 #[test]
3525 fn eval_if() {
3526 assert_eq!(ev("if true then 1 else 2"), Value::Int(1));
3527 assert_eq!(ev("if false then 1 else 2"), Value::Int(2));
3528 }
3529
3530 #[test]
3531 fn eval_let() {
3532 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
3533 assert_eq!(ev("let x = 1; y = 2; in x + y"), Value::Int(3));
3534 }
3535
3536 #[test]
3537 fn eval_let_dotted_simple() {
3538 assert_eq!(ev("let a.b = 1; a.c = 2; in a.b + a.c"), Value::Int(3));
3540 }
3541
3542 #[test]
3543 fn eval_let_dotted_deep() {
3544 assert_eq!(ev("let a.b.c = 1; in a.b.c"), Value::Int(1));
3546 }
3547
3548 #[test]
3549 fn eval_let_dotted_mixed() {
3550 assert_eq!(
3552 ev("let a.x = 1; b = 2; a.y = 3; in a.x + a.y + b"),
3553 Value::Int(6),
3554 );
3555 }
3556
3557 #[test]
3558 fn eval_let_dotted_produces_attrset() {
3559 let v = ev("let a.b = 1; a.c = 2; in a");
3561 if let Value::Attrs(attrs) = v {
3562 assert_eq!(attrs.get("b"), Some(&Value::Int(1)));
3563 assert_eq!(attrs.get("c"), Some(&Value::Int(2)));
3564 } else {
3565 panic!("expected Attrs, got {v:?}");
3566 }
3567 }
3568
3569 #[test]
3577 fn dynamic_inner_attr_key_is_lazy_on_sibling_read() {
3578 assert_eq!(
3580 ev(r#"let s = { a.${throw "KEYFORCED"} = 7; other = 9; }; in s.other"#),
3581 Value::Int(9),
3582 );
3583 }
3584
3585 #[test]
3586 fn dynamic_inner_attr_key_resolves_on_head_demand() {
3587 let v = ev(r#"let u = "bob"; s = { homes.${u} = 7; }; in s.homes"#);
3589 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3590 assert_eq!(attrs.get("bob"), Some(&Value::Int(7)));
3591 } else {
3592 panic!("expected Attrs");
3593 }
3594 }
3595
3596 #[test]
3597 fn dynamic_inner_attr_key_merges_with_static_sibling() {
3598 let v = ev(r#"let u = "x"; s = { a.${u} = 1; a.b = 2; }; in s.a"#);
3600 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3601 assert_eq!(attrs.get("x"), Some(&Value::Int(1)));
3602 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3603 } else {
3604 panic!("expected Attrs");
3605 }
3606 }
3607
3608 #[test]
3609 fn dynamic_inner_attr_key_null_skips_binding() {
3610 let v = ev(
3613 r#"let c = true; s = { a.${if c then null else "n"} = 5; b = 1; }; in s.b"#,
3614 );
3615 assert_eq!(v, Value::Int(1));
3616 }
3617
3618 #[test]
3624 fn interpolated_string_attr_key_is_lazy_on_sibling_read() {
3625 assert_eq!(
3626 ev(r#"let s = { a."p/${throw "KEYFORCED"}" = 7; other = 9; }; in s.other"#),
3627 Value::Int(9),
3628 );
3629 }
3630
3631 #[test]
3632 fn interpolated_string_attr_key_resolves_on_head_demand() {
3633 let v = ev(r#"let u = "bob"; s = { homes."u/${u}" = 7; }; in s.homes"#);
3635 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3636 assert_eq!(attrs.get("u/bob"), Some(&Value::Int(7)));
3637 } else {
3638 panic!("expected Attrs");
3639 }
3640 }
3641
3642 #[test]
3643 fn purely_literal_string_attr_key_stays_eager_static() {
3644 let v = ev(r#"let s = { a."foo bar" = 1; a.b = 2; }; in s.a"#);
3647 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3648 assert_eq!(attrs.get("foo bar"), Some(&Value::Int(1)));
3649 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3650 } else {
3651 panic!("expected Attrs");
3652 }
3653 }
3654
3655 #[test]
3658 fn dynamic_tail_key_under_colliding_head_is_lazy() {
3659 let v = ev(
3662 r#"let s = { sd.services.x = 1; sd.tmpfiles.${throw "KEYFORCED"}.d = 2; }; in s.sd.services.x"#,
3663 );
3664 assert_eq!(v, Value::Int(1));
3665 }
3666
3667 #[test]
3668 fn dynamic_tail_key_under_colliding_head_resolves_and_merges() {
3669 let v = ev(
3672 r#"let k = "z"; s = { sd.services.x = 1; sd.tmpfiles.${k}.d = 2; }; in s.sd"#,
3673 );
3674 let sd = force_value(&v).unwrap();
3675 if let Value::Attrs(sd_attrs) = &sd {
3676 let services = force_value(sd_attrs.get("services").unwrap()).unwrap();
3678 if let Value::Attrs(a) = &services {
3679 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3680 } else { panic!("expected services attrs"); }
3681 let tmpfiles = force_value(sd_attrs.get("tmpfiles").unwrap()).unwrap();
3683 if let Value::Attrs(a) = &tmpfiles {
3684 let z = force_value(a.get("z").unwrap()).unwrap();
3685 if let Value::Attrs(zd) = &z {
3686 assert_eq!(force_value(zd.get("d").unwrap()).unwrap(), Value::Int(2));
3687 } else { panic!("expected z attrs"); }
3688 } else { panic!("expected tmpfiles attrs"); }
3689 } else {
3690 panic!("expected sd attrs");
3691 }
3692 }
3693
3694 #[test]
3703 fn with_namespace_is_lazy_on_body_whnf() {
3704 let v = ev(r#"builtins.attrNames (with (throw "WITH-FORCED"); { a = 1; b = 2; })"#);
3705 if let Value::List(items) = force_value(&v).unwrap() {
3706 let names: Vec<String> = items
3707 .iter()
3708 .map(|i| match force_value(i).unwrap() {
3709 Value::String(s) => s.as_str().to_string(),
3710 other => panic!("expected string, got {}", other.type_name()),
3711 })
3712 .collect();
3713 assert_eq!(names, vec!["a".to_string(), "b".to_string()]);
3714 } else {
3715 panic!("expected list");
3716 }
3717 }
3718
3719 #[test]
3720 fn with_namespace_forces_only_on_fallthrough() {
3721 assert_eq!(ev(r#"with { x = 42; }; x"#), Value::Int(42));
3725 assert_eq!(ev(r#"let x = 7; in with (throw "NS"); x"#), Value::Int(7));
3728 }
3729
3730 #[test]
3741 fn dotted_fullset_leaf_deep_merges_with_deeper_sibling() {
3742 let v = ev(r#"{ o.a = { x = 1; }; o.a.y = 2; }.o.a"#);
3743 if let Value::Attrs(a) = force_value(&v).unwrap() {
3744 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3745 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3746 } else {
3747 panic!("expected attrs");
3748 }
3749 }
3750
3751 #[test]
3752 fn dotted_fullset_leaf_deep_merge_reverse_order() {
3753 let v = ev(r#"{ o.a.y = 2; o.a = { x = 1; }; }.o.a"#);
3756 if let Value::Attrs(a) = force_value(&v).unwrap() {
3757 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3758 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3759 } else {
3760 panic!("expected attrs");
3761 }
3762 }
3763
3764 #[test]
3765 fn dotted_fullset_leaf_merge_preserves_leaf_laziness() {
3766 assert_eq!(ev(r#"{ o.a = { x = throw "X-NEVER"; }; o.a.y = 2; }.o.a.y"#), Value::Int(2));
3770 }
3771
3772 #[test]
3773 fn eval_nested_let() {
3774 assert_eq!(ev("let a = 1; b = let c = 2; in c; in a + b"), Value::Int(3));
3775 }
3776
3777 #[test]
3778 fn eval_lambda() {
3779 assert_eq!(ev("(x: x + 1) 41"), Value::Int(42));
3780 }
3781
3782 #[test]
3783 fn eval_lambda_multi_arg() {
3784 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
3785 }
3786
3787 #[test]
3788 fn eval_list() {
3789 let v = ev("[1 2 3]");
3790 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]));
3791 }
3792
3793 #[test]
3794 fn eval_list_concat() {
3795 let v = ev("[1 2] ++ [3 4]");
3796 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]));
3797 }
3798
3799 #[test]
3800 fn eval_attrset() {
3801 let v = ev("{ a = 1; b = 2; }");
3802 if let Value::Attrs(attrs) = v {
3803 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3804 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3805 } else {
3806 panic!("expected attrset");
3807 }
3808 }
3809
3810 #[test]
3811 fn eval_select() {
3812 assert_eq!(ev("{ a = 42; }.a"), Value::Int(42));
3813 }
3814
3815 #[test]
3816 fn eval_select_or() {
3817 assert_eq!(ev("{ a = 42; }.b or 0"), Value::Int(0));
3818 }
3819
3820 #[test]
3821 fn eval_has_attr() {
3822 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
3823 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
3824 }
3825
3826 #[test]
3827 fn eval_update() {
3828 let v = ev("{ a = 1; b = 2; } // { b = 3; c = 4; }");
3829 if let Value::Attrs(attrs) = v {
3830 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3831 assert_eq!(attrs.get("b"), Some(&Value::Int(3)));
3832 assert_eq!(attrs.get("c"), Some(&Value::Int(4)));
3833 } else {
3834 panic!("expected attrset");
3835 }
3836 }
3837
3838 #[test]
3839 fn eval_with() {
3840 assert_eq!(ev("with { x = 42; }; x"), Value::Int(42));
3841 }
3842
3843 #[test]
3844 fn eval_assert() {
3845 assert_eq!(ev("assert true; 42"), Value::Int(42));
3846 assert!(eval("assert false; 42").is_err());
3847 }
3848
3849 #[test]
3850 fn eval_formals() {
3851 assert_eq!(ev("({ a, b }: a + b) { a = 1; b = 2; }"), Value::Int(3));
3852 }
3853
3854 #[test]
3855 fn eval_formals_default() {
3856 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 1; }"), Value::Int(11));
3857 }
3858
3859 #[test]
3860 fn eval_formals_ellipsis() {
3861 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; }"), Value::Int(1));
3862 }
3863
3864 #[test]
3865 fn eval_named_formals() {
3866 assert_eq!(ev("(args @ { a }: args.a) { a = 42; }"), Value::Int(42));
3867 }
3868
3869 #[test]
3870 fn eval_rec_attrset() {
3871 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
3872 }
3873
3874 #[test]
3875 fn eval_negation() {
3876 assert_eq!(ev("-42"), Value::Int(-42));
3877 }
3878
3879 #[test]
3880 fn eval_float_arithmetic() {
3881 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
3882 assert_eq!(ev("1 + 1.5"), Value::Float(2.5));
3883 }
3884
3885 #[test]
3886 fn eval_division_by_zero() {
3887 assert!(eval("1 / 0").is_err());
3888 }
3889
3890 #[test]
3891 fn eval_builtins_available() {
3892 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
3893 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
3894 }
3895
3896 #[test]
3897 fn eval_builtins_length() {
3898 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
3899 }
3900
3901 #[test]
3902 fn eval_builtins_head_tail() {
3903 assert_eq!(ev("builtins.head [1 2 3]"), Value::Int(1));
3904 assert_eq!(ev("builtins.length (builtins.tail [1 2 3])"), Value::Int(2));
3905 }
3906
3907 #[test]
3908 fn eval_builtins_add() {
3909 assert_eq!(ev("builtins.add 1 2"), Value::Int(3));
3910 }
3911
3912 #[test]
3913 fn eval_builtins_to_string() {
3914 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
3915 }
3916
3917 #[test]
3918 fn eval_implication() {
3919 assert_eq!(ev("false -> true"), Value::Bool(true));
3920 assert_eq!(ev("true -> false"), Value::Bool(false));
3921 assert_eq!(ev("true -> true"), Value::Bool(true));
3922 }
3923
3924 #[test]
3927 fn eval_error_undefined_variable() {
3928 let result = eval("nonexistent");
3929 assert!(result.is_err());
3930 let msg = format!("{}", result.unwrap_err());
3931 assert!(msg.contains("undefined variable"));
3932 }
3933
3934 #[test]
3935 fn eval_error_type_mismatch_arithmetic() {
3936 let result = eval(r#"1 + "hello""#);
3937 assert!(result.is_err());
3938 let msg = format!("{}", result.unwrap_err());
3939 assert!(msg.contains("cannot add") || msg.contains("type"));
3940 }
3941
3942 #[test]
3943 fn eval_error_unexpected_argument() {
3944 let result = eval("({ a }: a) { a = 1; b = 2; }");
3945 assert!(result.is_err());
3946 let msg = format!("{}", result.unwrap_err());
3947 assert!(msg.contains("unexpected argument"));
3948 }
3949
3950 #[test]
3951 fn eval_error_missing_required_argument() {
3952 let result = eval("({ a, b }: a + b) { a = 1; }");
3953 assert!(result.is_err());
3954 let msg = format!("{}", result.unwrap_err());
3955 assert!(msg.contains("missing argument"));
3956 }
3957
3958 #[test]
3959 fn eval_builtins_attr_names_sorted() {
3960 let v = ev("builtins.attrNames { z = 1; a = 2; m = 3; }");
3961 assert_eq!(
3963 v,
3964 Value::list(vec![
3965 Value::string("a"),
3966 Value::string("m"),
3967 Value::string("z"),
3968 ]),
3969 );
3970 }
3971
3972 #[test]
3973 fn eval_builtins_attr_values() {
3974 let v = ev("builtins.attrValues { a = 1; b = 2; }");
3975 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2)]));
3977 }
3978
3979 #[test]
3980 fn eval_builtins_is_null() {
3981 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
3982 assert_eq!(ev("builtins.isNull 1"), Value::Bool(false));
3983 }
3984
3985 #[test]
3986 fn eval_builtins_is_int() {
3987 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
3988 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
3989 }
3990
3991 #[test]
3992 fn eval_builtins_is_bool() {
3993 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
3994 assert_eq!(ev("builtins.isBool 0"), Value::Bool(false));
3995 }
3996
3997 #[test]
3998 fn eval_builtins_is_string() {
3999 assert_eq!(ev(r#"builtins.isString "hi""#), Value::Bool(true));
4000 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
4001 }
4002
4003 #[test]
4004 fn eval_builtins_is_list() {
4005 assert_eq!(ev("builtins.isList [1 2]"), Value::Bool(true));
4006 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
4007 }
4008
4009 #[test]
4010 fn eval_builtins_is_attrs() {
4011 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
4012 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
4013 }
4014
4015 #[test]
4016 fn eval_builtins_string_length() {
4017 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
4018 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
4019 }
4020
4021 #[test]
4022 fn eval_builtins_to_json_roundtrip() {
4023 assert_eq!(
4025 ev(r#"builtins.fromJSON (builtins.toJSON 42)"#),
4026 Value::Int(42),
4027 );
4028 assert_eq!(
4029 ev(r#"builtins.fromJSON (builtins.toJSON [1 2 3])"#),
4030 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4031 );
4032 }
4033
4034 #[test]
4035 fn eval_builtins_from_json() {
4036 assert_eq!(
4037 ev(r#"builtins.fromJSON "{\"a\": 1}""#),
4038 {
4039 let mut attrs = NixAttrs::new();
4040 attrs.insert("a".to_string(), Value::Int(1));
4041 Value::Attrs(Rc::new(attrs))
4042 },
4043 );
4044 assert_eq!(ev(r#"builtins.fromJSON "null""#), Value::Null);
4045 assert_eq!(ev(r#"builtins.fromJSON "true""#), Value::Bool(true));
4046 }
4047
4048 #[test]
4049 fn eval_nested_function_application() {
4050 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
4052 assert_eq!(ev("((x: y: x + y) 1) 2"), Value::Int(3));
4054 }
4055
4056 #[test]
4057 fn eval_recursive_let() {
4058 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4059 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4060 }
4061
4062 #[test]
4063 fn eval_string_comparison() {
4064 assert_eq!(ev(r#""a" < "b""#), Value::Bool(true));
4065 assert_eq!(ev(r#""b" < "a""#), Value::Bool(false));
4066 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4067 assert_eq!(ev(r#""abc" != "def""#), Value::Bool(true));
4068 }
4069
4070 #[test]
4071 fn eval_list_in_attrset() {
4072 let v = ev("{ x = [1 2 3]; }.x");
4073 assert_eq!(
4074 v,
4075 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4076 );
4077 }
4078
4079 #[test]
4080 fn eval_nested_attrset_select() {
4081 assert_eq!(ev("{ a = { b = 42; }; }.a.b"), Value::Int(42));
4082 }
4083
4084 #[test]
4085 fn eval_let_shadows_outer() {
4086 assert_eq!(
4087 ev("let x = 1; in let x = 2; in x"),
4088 Value::Int(2),
4089 );
4090 }
4091
4092 #[test]
4093 fn eval_with_provides_scope() {
4094 assert_eq!(
4096 ev("with { x = 42; y = 10; }; x + y"),
4097 Value::Int(52),
4098 );
4099 }
4100
4101 #[test]
4102 fn eval_list_equality() {
4103 assert_eq!(ev("[1 2] == [1 2]"), Value::Bool(true));
4104 assert_eq!(ev("[1 2] == [1 3]"), Value::Bool(false));
4105 }
4106
4107 #[test]
4108 fn eval_attrset_equality() {
4109 assert_eq!(ev("{ a = 1; } == { a = 1; }"), Value::Bool(true));
4110 assert_eq!(ev("{ a = 1; } == { a = 2; }"), Value::Bool(false));
4111 }
4112
4113 #[test]
4118 fn literal_int_large_zero_negative() {
4119 assert_eq!(ev("9223372036854775807"), Value::Int(i64::MAX));
4121 assert_eq!(ev("0"), Value::Int(0));
4123 assert_eq!(ev("-1"), Value::Int(-1));
4125 assert_eq!(ev("-999999"), Value::Int(-999999));
4126 }
4127
4128 #[test]
4129 fn literal_float_small_large() {
4130 assert_eq!(ev("0.001"), Value::Float(0.001));
4131 assert_eq!(ev("999999.999"), Value::Float(999999.999));
4132 assert_eq!(ev("1.0e3"), Value::Float(1000.0));
4134 assert_eq!(ev("1.5e2"), Value::Float(150.0));
4135 }
4136
4137 #[test]
4138 fn literal_string_empty_and_escapes() {
4139 assert_eq!(ev(r#""""#), Value::string(""));
4140 assert_eq!(ev(r#""hello\nworld""#), Value::string("hello\nworld"));
4142 assert_eq!(ev(r#""tab\there""#), Value::string("tab\there"));
4143 }
4144
4145 #[test]
4146 fn literal_multiline_string() {
4147 assert_eq!(
4149 ev("''hello''"),
4150 Value::string("hello"),
4151 );
4152 assert_eq!(
4154 ev("''\n line1\n line2\n''"),
4155 Value::string("line1\nline2\n"),
4156 );
4157 }
4158
4159 #[test]
4160 fn literal_paths() {
4161 assert_eq!(ev("./foo"), Value::Path(Box::new(SmolStr::from("./foo"))));
4163 assert_eq!(ev("/nix/store/abc"), Value::Path(Box::new(SmolStr::from("/nix/store/abc"))));
4165 assert_eq!(ev("~/myfile"), Value::Path(Box::new(SmolStr::from("~/myfile"))));
4167 }
4168
4169 #[test]
4179 fn interp_path_abs_splices_and_types_path() {
4180 let v = ev(r#"let x = "foo"; in /a/${x}/b"#);
4182 assert_eq!(v, Value::Path(Box::new(SmolStr::from("/a/foo/b"))));
4183 }
4184
4185 #[test]
4186 fn interp_path_abs_multi_and_slash_in_value() {
4187 assert_eq!(
4189 ev(r#"let a = "x"; b = "y/z"; in /p/${a}/${b}.nix"#),
4190 Value::Path(Box::new(SmolStr::from("/p/x/y/z.nix"))),
4191 );
4192 }
4193
4194 #[test]
4195 fn interp_path_abs_normalizes_double_slash_seam() {
4196 assert_eq!(
4199 ev(r#"/bar/${/tmp/foo}"#),
4200 Value::Path(Box::new(SmolStr::from("/bar/tmp/foo"))),
4201 );
4202 }
4203
4204 #[test]
4205 fn interp_path_rel_resolves_against_eval_dir() {
4206 let _g = push_eval_file(std::path::PathBuf::from("/tmp/example/default.nix"));
4210 assert_eq!(
4211 ev(r#"let x = "foo"; in ./${x}.nix"#),
4212 Value::Path(Box::new(SmolStr::from("/tmp/example/foo.nix"))),
4213 );
4214 }
4215
4216 #[test]
4217 fn interp_path_rel_no_eval_dir_keeps_relative_text() {
4218 assert_eq!(
4221 ev(r#"let x = "foo"; in ./${x}.nix"#),
4222 Value::Path(Box::new(SmolStr::from("./foo.nix"))),
4223 );
4224 }
4225
4226 #[test]
4227 fn interp_path_home_splices_leading_tilde_preserved() {
4228 assert_eq!(
4232 ev(r#"let x = "foo"; in ~/${x}/bar"#),
4233 Value::Path(Box::new(SmolStr::from("~/foo/bar"))),
4234 );
4235 }
4236
4237 #[test]
4238 fn interp_path_non_interpolated_still_raw() {
4239 assert_eq!(ev("/a/b/c"), Value::Path(Box::new(SmolStr::from("/a/b/c"))));
4242 assert_eq!(ev("~/plain"), Value::Path(Box::new(SmolStr::from("~/plain"))));
4243 }
4244
4245 #[test]
4246 fn literal_null_true_false_standalone() {
4247 assert_eq!(ev("null"), Value::Null);
4248 assert_eq!(ev("true"), Value::Bool(true));
4249 assert_eq!(ev("false"), Value::Bool(false));
4250 }
4251
4252 #[test]
4257 fn op_arithmetic_int() {
4258 assert_eq!(ev("100 + 200"), Value::Int(300));
4259 assert_eq!(ev("50 - 30"), Value::Int(20));
4260 assert_eq!(ev("7 * 8"), Value::Int(56));
4261 assert_eq!(ev("17 / 3"), Value::Int(5)); }
4263
4264 #[test]
4265 fn op_arithmetic_float() {
4266 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
4267 assert_eq!(ev("5.0 - 1.5"), Value::Float(3.5));
4268 assert_eq!(ev("2.0 * 3.0"), Value::Float(6.0));
4269 assert_eq!(ev("7.0 / 2.0"), Value::Float(3.5));
4270 }
4271
4272 #[test]
4273 fn op_arithmetic_mixed_int_float() {
4274 assert_eq!(ev("1 + 2.5"), Value::Float(3.5));
4276 assert_eq!(ev("2.5 + 1"), Value::Float(3.5));
4277 assert_eq!(ev("2 * 1.5"), Value::Float(3.0));
4279 assert_eq!(ev("5.5 - 2"), Value::Float(3.5));
4281 }
4282
4283 #[test]
4284 fn op_string_concat() {
4285 assert_eq!(ev(r#""foo" + "bar""#), Value::string("foobar"));
4286 assert_eq!(ev(r#""" + "x""#), Value::string("x"));
4287 assert_eq!(ev(r#""a" + "" + "b""#), Value::string("ab"));
4288 }
4289
4290 #[test]
4291 fn op_path_concat() {
4292 assert_eq!(ev(r#"./foo + "/bar""#), Value::Path(Box::new(SmolStr::from("./foo/bar"))));
4294 assert_eq!(ev("./a + ./b"), Value::Path(Box::new(SmolStr::from("./a/./b"))));
4296 }
4297
4298 #[test]
4299 fn op_comparison_ints() {
4300 assert_eq!(ev("1 < 2"), Value::Bool(true));
4301 assert_eq!(ev("2 < 1"), Value::Bool(false));
4302 assert_eq!(ev("2 > 1"), Value::Bool(true));
4303 assert_eq!(ev("1 > 2"), Value::Bool(false));
4304 assert_eq!(ev("2 <= 2"), Value::Bool(true));
4305 assert_eq!(ev("3 <= 2"), Value::Bool(false));
4306 assert_eq!(ev("2 >= 2"), Value::Bool(true));
4307 assert_eq!(ev("1 >= 2"), Value::Bool(false));
4308 }
4309
4310 #[test]
4311 fn op_comparison_floats() {
4312 assert_eq!(ev("1.5 < 2.5"), Value::Bool(true));
4313 assert_eq!(ev("2.5 > 1.5"), Value::Bool(true));
4314 assert_eq!(ev("1.5 <= 1.5"), Value::Bool(true));
4315 assert_eq!(ev("1.5 >= 1.5"), Value::Bool(true));
4316 }
4317
4318 #[test]
4319 fn op_comparison_strings() {
4320 assert_eq!(ev(r#""apple" < "banana""#), Value::Bool(true));
4321 assert_eq!(ev(r#""banana" > "apple""#), Value::Bool(true));
4322 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4323 assert_eq!(ev(r#""abc" != "xyz""#), Value::Bool(true));
4324 assert_eq!(ev(r#""abc" <= "abd""#), Value::Bool(true));
4325 assert_eq!(ev(r#""abc" >= "abb""#), Value::Bool(true));
4326 }
4327
4328 #[test]
4329 fn op_equality_various_types() {
4330 assert_eq!(ev("null == null"), Value::Bool(true));
4331 assert_eq!(ev("true == true"), Value::Bool(true));
4332 assert_eq!(ev("false == false"), Value::Bool(true));
4333 assert_eq!(ev("true == false"), Value::Bool(false));
4334 assert_eq!(ev("1 == 1"), Value::Bool(true));
4335 assert_eq!(ev("1 != 2"), Value::Bool(true));
4336 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
4338 assert_eq!(ev("null == false"), Value::Bool(false));
4339 }
4340
4341 #[test]
4342 fn op_logic_short_circuit() {
4343 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
4345 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
4347 }
4348
4349 #[test]
4350 fn op_logic_full() {
4351 assert_eq!(ev("true && true"), Value::Bool(true));
4352 assert_eq!(ev("true && false"), Value::Bool(false));
4353 assert_eq!(ev("false && true"), Value::Bool(false));
4354 assert_eq!(ev("false && false"), Value::Bool(false));
4355 assert_eq!(ev("true || true"), Value::Bool(true));
4356 assert_eq!(ev("true || false"), Value::Bool(true));
4357 assert_eq!(ev("false || true"), Value::Bool(true));
4358 assert_eq!(ev("false || false"), Value::Bool(false));
4359 assert_eq!(ev("!true"), Value::Bool(false));
4360 assert_eq!(ev("!false"), Value::Bool(true));
4361 }
4362
4363 #[test]
4364 fn op_implication_truth_table() {
4365 assert_eq!(ev("false -> false"), Value::Bool(true));
4367 assert_eq!(ev("false -> true"), Value::Bool(true));
4368 assert_eq!(ev("true -> true"), Value::Bool(true));
4370 assert_eq!(ev("true -> false"), Value::Bool(false));
4371 }
4372
4373 #[test]
4374 fn op_implication_short_circuit() {
4375 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
4377 }
4378
4379 #[test]
4380 fn op_update_merge() {
4381 let v = ev("{ a = 1; } // { b = 2; }");
4382 if let Value::Attrs(attrs) = v {
4383 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4384 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4385 } else {
4386 panic!("expected attrs");
4387 }
4388 }
4389
4390 #[test]
4391 fn op_update_right_wins() {
4392 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4393 }
4394
4395 #[test]
4396 fn op_list_concat() {
4397 assert_eq!(
4398 ev("[1 2] ++ [3 4]"),
4399 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]),
4400 );
4401 assert_eq!(ev("[] ++ [1]"), Value::list(vec![Value::Int(1)]));
4403 assert_eq!(ev("[1] ++ []"), Value::list(vec![Value::Int(1)]));
4404 }
4405
4406 #[test]
4407 fn op_has_attr_present_and_absent() {
4408 assert_eq!(ev("{ x = 1; y = 2; } ? x"), Value::Bool(true));
4409 assert_eq!(ev("{ x = 1; } ? z"), Value::Bool(false));
4410 assert_eq!(ev("{} ? anything"), Value::Bool(false));
4411 }
4412
4413 #[test]
4414 fn op_unary_negate() {
4415 assert_eq!(ev("-42"), Value::Int(-42));
4416 assert_eq!(ev("-3.14"), Value::Float(-3.14));
4417 assert_eq!(ev("- -5"), Value::Int(5));
4419 }
4420
4421 #[test]
4426 fn control_if_true_branch() {
4427 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
4428 }
4429
4430 #[test]
4431 fn control_if_false_branch() {
4432 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
4433 }
4434
4435 #[test]
4436 fn control_if_nested() {
4437 assert_eq!(
4438 ev("if true then (if false then 1 else 2) else 3"),
4439 Value::Int(2),
4440 );
4441 assert_eq!(
4442 ev("if false then 1 else (if true then 2 else 3)"),
4443 Value::Int(2),
4444 );
4445 }
4446
4447 #[test]
4448 fn control_assert_passing() {
4449 assert_eq!(ev("assert 1 == 1; 42"), Value::Int(42));
4450 assert_eq!(ev("assert true; true"), Value::Bool(true));
4451 }
4452
4453 #[test]
4454 fn control_assert_failing() {
4455 assert!(eval("assert false; 42").is_err());
4456 assert!(eval("assert 1 == 2; 42").is_err());
4457 }
4458
4459 #[test]
4460 fn control_with_basic_scope() {
4461 assert_eq!(ev("with { a = 1; b = 2; }; a + b"), Value::Int(3));
4462 }
4463
4464 #[test]
4465 fn control_with_lexical_precedence() {
4466 assert_eq!(
4468 ev("let x = 10; in with { x = 99; }; x"),
4469 Value::Int(10),
4470 );
4471 }
4472
4473 #[test]
4474 fn control_with_nested() {
4475 assert_eq!(
4476 ev("with { a = 1; }; with { b = 2; }; a + b"),
4477 Value::Int(3),
4478 );
4479 }
4480
4481 #[test]
4482 fn control_with_lazy_fix_self() {
4483 let result = eval(
4488 "let fix = f: let x = f x; in x; in fix (self: with self; { a = 1; b = a + 1; })"
4489 );
4490 assert!(result.is_ok(), "fix with self should work: {:?}", result);
4491 if let Ok(Value::Attrs(attrs)) = result {
4492 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4493 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4494 } else {
4495 panic!("expected Attrs, got {:?}", result);
4496 }
4497 }
4498
4499 #[test]
4500 fn control_with_lazy_fix_self_lib_pattern() {
4501 let result = eval(r#"
4504 let fix = f: let x = f x; in x;
4505 in (fix (self: with self; {
4506 lib = { version = "1.0"; };
4507 hello = "hello ${lib.version}";
4508 })).hello
4509 "#);
4510 assert!(result.is_ok(), "nixpkgs-style lib pattern: {:?}", result);
4511 assert_eq!(
4512 result.unwrap(),
4513 Value::String(Rc::new(NixString::plain("hello 1.0"))),
4514 );
4515 }
4516
4517 #[test]
4518 fn control_with_non_attrset_errors() {
4519 let result = eval("with 42; 1");
4521 assert_eq!(result.unwrap(), Value::Int(1));
4524 }
4525
4526 #[test]
4527 fn control_with_non_attrset_lookup_falls_through() {
4528 let result = eval("let x = 1; in with 42; x");
4531 assert_eq!(result.unwrap(), Value::Int(1));
4532 }
4533
4534 #[test]
4535 fn control_let_simple_and_multiple() {
4536 assert_eq!(ev("let x = 5; in x"), Value::Int(5));
4537 assert_eq!(ev("let x = 1; y = 2; z = 3; in x + y + z"), Value::Int(6));
4538 }
4539
4540 #[test]
4541 fn control_let_shadow_outer() {
4542 assert_eq!(
4543 ev("let x = 1; in let x = 2; in x"),
4544 Value::Int(2),
4545 );
4546 }
4547
4548 #[test]
4549 fn control_let_recursive_reference() {
4550 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4551 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4552 }
4553
4554 #[test]
4555 fn control_nested_let_expression() {
4556 assert_eq!(
4557 ev("let a = let b = 1; in b; in a"),
4558 Value::Int(1),
4559 );
4560 assert_eq!(
4561 ev("let a = let b = 10; in b + 5; in a * 2"),
4562 Value::Int(30),
4563 );
4564 }
4565
4566 #[test]
4571 fn func_identity_lambda() {
4572 assert_eq!(ev("(x: x) 42"), Value::Int(42));
4573 assert_eq!(ev(r#"(x: x) "hello""#), Value::string("hello"));
4574 }
4575
4576 #[test]
4577 fn func_curried_two_args() {
4578 assert_eq!(ev("(x: y: x + y) 3 4"), Value::Int(7));
4579 }
4580
4581 #[test]
4582 fn func_curried_three_args() {
4583 assert_eq!(ev("(a: b: c: a + b + c) 1 2 3"), Value::Int(6));
4584 }
4585
4586 #[test]
4587 fn func_formals_basic() {
4588 assert_eq!(ev("({ a, b }: a + b) { a = 3; b = 7; }"), Value::Int(10));
4589 }
4590
4591 #[test]
4592 fn func_formals_with_defaults() {
4593 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; }"), Value::Int(15));
4594 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; b = 20; }"), Value::Int(25));
4596 }
4597
4598 #[test]
4599 fn func_formals_with_ellipsis() {
4600 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; c = 3; }"), Value::Int(1));
4601 }
4602
4603 #[test]
4604 fn func_named_formals_at_before() {
4605 assert_eq!(
4607 ev("(args @ { a, b }: args.a + args.b) { a = 3; b = 4; }"),
4608 Value::Int(7),
4609 );
4610 }
4611
4612 #[test]
4613 fn func_named_formals_at_after() {
4614 assert_eq!(
4616 ev("({ a, b } @ args: args.a + args.b) { a = 10; b = 20; }"),
4617 Value::Int(30),
4618 );
4619 }
4620
4621 #[test]
4622 fn func_nested_application() {
4623 assert_eq!(ev("((x: y: x * y) 3) 4"), Value::Int(12));
4625 }
4626
4627 #[test]
4628 fn func_higher_order_map() {
4629 assert_eq!(
4630 ev("builtins.map (x: x * 2) [1 2 3]"),
4631 Value::list(vec![Value::Int(2), Value::Int(4), Value::Int(6)]),
4632 );
4633 }
4634
4635 #[test]
4636 fn func_higher_order_filter() {
4637 assert_eq!(
4638 ev("builtins.filter (x: x > 2) [1 2 3 4 5]"),
4639 Value::list(vec![Value::Int(3), Value::Int(4), Value::Int(5)]),
4640 );
4641 }
4642
4643 #[test]
4644 fn func_higher_order_foldl() {
4645 assert_eq!(
4647 ev("builtins.foldl' (acc: x: acc + x) 0 [1 2 3 4]"),
4648 Value::Int(10),
4649 );
4650 }
4651
4652 #[test]
4653 fn func_as_attrset_value() {
4654 assert_eq!(
4655 ev("let s = { f = x: x + 1; }; in s.f 5"),
4656 Value::Int(6),
4657 );
4658 }
4659
4660 #[test]
4661 fn func_immediate_application() {
4662 assert_eq!(ev("(x: x * x) 7"), Value::Int(49));
4663 }
4664
4665 #[test]
4666 fn func_in_let_binding() {
4667 assert_eq!(
4668 ev("let double = x: x * 2; in double 21"),
4669 Value::Int(42),
4670 );
4671 }
4672
4673 #[test]
4678 fn attrs_empty_set() {
4679 let v = ev("{}");
4680 if let Value::Attrs(attrs) = v {
4681 assert!(attrs.is_empty());
4682 } else {
4683 panic!("expected attrs");
4684 }
4685 }
4686
4687 #[test]
4688 fn attrs_simple() {
4689 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
4690 }
4691
4692 #[test]
4693 fn attrs_nested_access() {
4694 assert_eq!(ev("{ a = { b = { c = 42; }; }; }.a.b.c"), Value::Int(42));
4695 }
4696
4697 #[test]
4698 fn attrs_recursive_set() {
4699 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
4700 }
4701
4702 #[test]
4703 fn attrs_update_disjoint() {
4704 let v = ev("{ a = 1; } // { b = 2; }");
4705 if let Value::Attrs(attrs) = v {
4706 assert_eq!(attrs.len(), 2);
4707 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4708 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4709 } else {
4710 panic!("expected attrs");
4711 }
4712 }
4713
4714 #[test]
4715 fn attrs_update_override() {
4716 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4717 }
4718
4719 #[test]
4720 fn attrs_has_attr_operator() {
4721 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
4722 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
4723 }
4724
4725 #[test]
4726 fn attrs_select_with_default() {
4727 assert_eq!(ev("{ a = 1; }.a or 99"), Value::Int(1));
4728 assert_eq!(ev("{}.missing or 99"), Value::Int(99));
4729 assert_eq!(ev("{ a = 1; }.b or 42"), Value::Int(42));
4730 }
4731
4732 #[test]
4733 fn attrs_nested_attr_path_in_binding() {
4734 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
4736 }
4737
4738 #[test]
4739 fn attrs_inherit_from_scope() {
4740 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.x"), Value::Int(1));
4741 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.y"), Value::Int(2));
4742 }
4743
4744 #[test]
4745 fn attrs_inherit_from_expr() {
4746 assert_eq!(
4747 ev("{ inherit ({ a = 42; b = 10; }) a; }.a"),
4748 Value::Int(42),
4749 );
4750 }
4751
4752 #[test]
4753 fn attrs_dynamic_attr_name() {
4754 assert_eq!(
4755 ev(r#"let name = "x"; in { ${name} = 42; }.x"#),
4756 Value::Int(42),
4757 );
4758 }
4759
4760 #[test]
4761 fn attrs_attr_names_sorted() {
4762 assert_eq!(
4763 ev("builtins.attrNames { z = 1; m = 2; a = 3; }"),
4764 Value::list(vec![
4765 Value::string("a"),
4766 Value::string("m"),
4767 Value::string("z"),
4768 ]),
4769 );
4770 }
4771
4772 #[test]
4773 fn attrs_attr_values_follow_key_order() {
4774 assert_eq!(
4776 ev("builtins.attrValues { c = 3; a = 1; b = 2; }"),
4777 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4778 );
4779 }
4780
4781 #[test]
4782 fn attrs_update_is_shallow() {
4783 assert_eq!(
4785 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a ? x"),
4786 Value::Bool(false),
4787 );
4788 assert_eq!(
4789 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a.y"),
4790 Value::Int(2),
4791 );
4792 }
4793
4794 #[test]
4799 fn list_empty() {
4800 assert_eq!(ev("[]"), Value::list(vec![]));
4801 }
4802
4803 #[test]
4804 fn list_single_element() {
4805 assert_eq!(ev("[1]"), Value::list(vec![Value::Int(1)]));
4806 }
4807
4808 #[test]
4809 fn list_mixed_types() {
4810 assert_eq!(
4811 ev(r#"[1 "two" true null]"#),
4812 Value::list(vec![
4813 Value::Int(1),
4814 Value::string("two"),
4815 Value::Bool(true),
4816 Value::Null,
4817 ]),
4818 );
4819 }
4820
4821 #[test]
4822 fn list_nested() {
4823 assert_eq!(
4824 ev("[[1 2] [3 4]]"),
4825 Value::list(vec![
4826 Value::list(vec![Value::Int(1), Value::Int(2)]),
4827 Value::list(vec![Value::Int(3), Value::Int(4)]),
4828 ]),
4829 );
4830 }
4831
4832 #[test]
4833 fn list_concat_operator() {
4834 assert_eq!(
4835 ev("[1] ++ [2] ++ [3]"),
4836 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4837 );
4838 }
4839
4840 #[test]
4841 fn list_builtins_length() {
4842 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
4843 assert_eq!(ev("builtins.length []"), Value::Int(0));
4844 }
4845
4846 #[test]
4847 fn list_builtins_elem_at() {
4848 assert_eq!(ev("builtins.elemAt [10 20 30] 0"), Value::Int(10));
4849 assert_eq!(ev("builtins.elemAt [10 20 30] 1"), Value::Int(20));
4850 assert_eq!(ev("builtins.elemAt [10 20 30] 2"), Value::Int(30));
4851 }
4852
4853 #[test]
4854 fn list_equality() {
4855 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
4856 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
4857 assert_eq!(ev("[] == []"), Value::Bool(true));
4858 }
4859
4860 #[test]
4865 fn interp_simple_variable() {
4866 assert_eq!(
4867 ev(r#"let name = "world"; in "hello ${name}""#),
4868 Value::string("hello world"),
4869 );
4870 }
4871
4872 #[test]
4873 fn interp_nested_expression() {
4874 assert_eq!(
4875 ev(r#""result: ${builtins.toString (1 + 2)}""#),
4876 Value::string("result: 3"),
4877 );
4878 }
4879
4880 #[test]
4881 fn interp_int_coercion() {
4882 assert_eq!(
4884 ev(r#"let x = 42; in "count: ${builtins.toString x}""#),
4885 Value::string("count: 42"),
4886 );
4887 }
4888
4889 #[test]
4890 fn interp_multiple() {
4891 assert_eq!(
4892 ev(r#"let a = "foo"; b = "bar"; in "${a} and ${b}""#),
4893 Value::string("foo and bar"),
4894 );
4895 }
4896
4897 #[test]
4898 fn interp_in_let() {
4899 assert_eq!(
4900 ev(r#"let x = "world"; in "hello ${x}""#),
4901 Value::string("hello world"),
4902 );
4903 }
4904
4905 #[test]
4906 fn interp_empty_result() {
4907 assert_eq!(
4908 ev(r#"let x = ""; in "a${x}b""#),
4909 Value::string("ab"),
4910 );
4911 }
4912
4913 #[test]
4914 fn interp_path_in_string_context() {
4915 assert!(eval(r#""path: ${./foo-nonexistent-xyz}""#).is_err());
4921 }
4922
4923 #[test]
4924 fn interp_adjacent_interpolations() {
4925 assert_eq!(
4926 ev(r#"let a = "x"; b = "y"; in "${a}${b}""#),
4927 Value::string("xy"),
4928 );
4929 }
4930
4931 #[test]
4936 fn builtins_map_filter_foldl() {
4937 assert_eq!(
4939 ev("builtins.map (x: x + 10) [1 2 3]"),
4940 Value::list(vec![Value::Int(11), Value::Int(12), Value::Int(13)]),
4941 );
4942 assert_eq!(
4944 ev("builtins.filter (x: x > 1) [1 2 3]"),
4945 Value::list(vec![Value::Int(2), Value::Int(3)]),
4946 );
4947 assert_eq!(
4949 ev("builtins.foldl' (a: b: a * b) 1 [2 3 4]"),
4950 Value::Int(24),
4951 );
4952 }
4953
4954 #[test]
4955 fn builtins_map_attrs() {
4956 assert_eq!(
4957 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).a"),
4958 Value::Int(2),
4959 );
4960 assert_eq!(
4961 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).b"),
4962 Value::Int(4),
4963 );
4964 }
4965
4966 #[test]
4967 fn builtins_list_to_attrs() {
4968 assert_eq!(
4969 ev(r#"(builtins.listToAttrs [{ name = "x"; value = 1; } { name = "y"; value = 2; }]).x"#),
4970 Value::Int(1),
4971 );
4972 }
4973
4974 #[test]
4975 fn builtins_list_to_attrs_duplicate_key_first_wins() {
4976 assert_eq!(
4985 ev(r#"(builtins.listToAttrs [{ name = "k"; value = 1; } { name = "k"; value = 2; }]).k"#),
4986 Value::Int(1),
4987 );
4988 }
4989
4990 #[test]
4991 fn builtins_concat_map() {
4992 assert_eq!(
4993 ev("builtins.concatMap (x: [x (x * 2)]) [1 2 3]"),
4994 Value::list(vec![
4995 Value::Int(1), Value::Int(2),
4996 Value::Int(2), Value::Int(4),
4997 Value::Int(3), Value::Int(6),
4998 ]),
4999 );
5000 }
5001
5002 #[test]
5003 fn builtins_concat_lists() {
5004 assert_eq!(
5005 ev("builtins.concatLists [[1 2] [3] [4 5]]"),
5006 Value::list(vec![
5007 Value::Int(1), Value::Int(2), Value::Int(3),
5008 Value::Int(4), Value::Int(5),
5009 ]),
5010 );
5011 }
5012
5013 #[test]
5014 fn builtins_concat_strings_sep() {
5015 assert_eq!(
5016 ev(r#"builtins.concatStringsSep ", " ["a" "b" "c"]"#),
5017 Value::string("a, b, c"),
5018 );
5019 assert_eq!(
5020 ev(r#"builtins.concatStringsSep "" ["x" "y"]"#),
5021 Value::string("xy"),
5022 );
5023 }
5024
5025 #[test]
5026 fn builtins_replace_strings() {
5027 assert_eq!(
5028 ev(r#"builtins.replaceStrings ["o"] ["0"] "foobar""#),
5029 Value::string("f00bar"),
5030 );
5031 assert_eq!(
5032 ev(r#"builtins.replaceStrings ["hello"] ["goodbye"] "hello world""#),
5033 Value::string("goodbye world"),
5034 );
5035 }
5036
5037 #[test]
5038 fn builtins_has_prefix_has_suffix() {
5039 assert_eq!(ev(r#"builtins.hasPrefix "he" "hello""#), Value::Bool(true));
5040 assert_eq!(ev(r#"builtins.hasPrefix "xx" "hello""#), Value::Bool(false));
5041 assert_eq!(ev(r#"builtins.hasSuffix "lo" "hello""#), Value::Bool(true));
5042 assert_eq!(ev(r#"builtins.hasSuffix "xx" "hello""#), Value::Bool(false));
5043 }
5044
5045 #[test]
5046 fn builtins_all_any() {
5047 assert_eq!(ev("builtins.all (x: x > 0) [1 2 3]"), Value::Bool(true));
5048 assert_eq!(ev("builtins.all (x: x > 1) [1 2 3]"), Value::Bool(false));
5049 assert_eq!(ev("builtins.any (x: x > 2) [1 2 3]"), Value::Bool(true));
5050 assert_eq!(ev("builtins.any (x: x > 5) [1 2 3]"), Value::Bool(false));
5051 }
5052
5053 #[test]
5054 fn builtins_sort() {
5055 assert_eq!(
5056 ev("builtins.sort (a: b: a < b) [3 1 2]"),
5057 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5058 );
5059 }
5060
5061 #[test]
5062 fn builtins_remove_attrs() {
5063 let v = ev(r#"builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b" "c"]"#);
5064 if let Value::Attrs(attrs) = v {
5065 assert_eq!(attrs.len(), 1);
5066 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
5067 assert!(attrs.get("b").is_none());
5068 } else {
5069 panic!("expected attrs");
5070 }
5071 }
5072
5073 #[test]
5074 fn builtins_intersect_attrs() {
5075 let v = ev("builtins.intersectAttrs { a = 1; b = 2; } { b = 20; c = 30; }");
5076 if let Value::Attrs(attrs) = v {
5077 assert_eq!(attrs.len(), 1);
5078 assert_eq!(attrs.get("b"), Some(&Value::Int(20)));
5080 } else {
5081 panic!("expected attrs");
5082 }
5083 }
5084
5085 #[test]
5086 fn builtins_type_of_all_types() {
5087 assert_eq!(ev("builtins.typeOf null"), Value::string("null"));
5088 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
5089 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
5090 assert_eq!(ev("builtins.typeOf 3.14"), Value::string("float"));
5091 assert_eq!(ev(r#"builtins.typeOf "hi""#), Value::string("string"));
5092 assert_eq!(ev("builtins.typeOf [1]"), Value::string("list"));
5093 assert_eq!(ev("builtins.typeOf {}"), Value::string("set"));
5094 assert_eq!(ev("builtins.typeOf (x: x)"), Value::string("lambda"));
5095 }
5096
5097 #[test]
5098 fn builtins_is_type_checks() {
5099 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
5100 assert_eq!(ev("builtins.isNull 0"), Value::Bool(false));
5101 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
5102 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
5103 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
5104 assert_eq!(ev("builtins.isBool 1"), Value::Bool(false));
5105 assert_eq!(ev(r#"builtins.isString "x""#), Value::Bool(true));
5106 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
5107 assert_eq!(ev("builtins.isList []"), Value::Bool(true));
5108 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
5109 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
5110 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
5111 assert_eq!(ev("builtins.isFunction (x: x)"), Value::Bool(true));
5112 assert_eq!(ev("builtins.isFunction 1"), Value::Bool(false));
5113 assert_eq!(ev("builtins.isFloat 3.14"), Value::Bool(true));
5114 assert_eq!(ev("builtins.isFloat 1"), Value::Bool(false));
5115 }
5116
5117 #[test]
5118 fn builtins_to_json_from_json_roundtrip() {
5119 assert_eq!(ev("builtins.fromJSON (builtins.toJSON 42)"), Value::Int(42));
5121 assert_eq!(
5123 ev(r#"builtins.fromJSON (builtins.toJSON "hello")"#),
5124 Value::string("hello"),
5125 );
5126 assert_eq!(
5128 ev("builtins.fromJSON (builtins.toJSON [1 2 3])"),
5129 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5130 );
5131 assert_eq!(ev("builtins.fromJSON (builtins.toJSON null)"), Value::Null);
5133 assert_eq!(ev("builtins.fromJSON (builtins.toJSON true)"), Value::Bool(true));
5135 }
5136
5137 #[test]
5138 fn builtins_to_string_various() {
5139 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
5140 assert_eq!(ev("builtins.toString true"), Value::string("1"));
5141 assert_eq!(ev("builtins.toString false"), Value::string(""));
5142 assert_eq!(ev("builtins.toString null"), Value::string(""));
5143 assert_eq!(ev(r#"builtins.toString "hello""#), Value::string("hello"));
5144 }
5145
5146 #[test]
5147 fn builtins_function_args() {
5148 let v = ev("builtins.functionArgs ({ a, b ? 1 }: a)");
5149 if let Value::Attrs(attrs) = v {
5150 assert_eq!(attrs.get("a"), Some(&Value::Bool(false))); assert_eq!(attrs.get("b"), Some(&Value::Bool(true))); } else {
5153 panic!("expected attrs");
5154 }
5155 }
5156
5157 #[test]
5158 fn builtins_gen_list() {
5159 assert_eq!(
5160 ev("builtins.genList (x: x * x) 5"),
5161 Value::list(vec![
5162 Value::Int(0), Value::Int(1), Value::Int(4),
5163 Value::Int(9), Value::Int(16),
5164 ]),
5165 );
5166 assert_eq!(ev("builtins.genList (x: x) 0"), Value::list(vec![]));
5167 }
5168
5169 #[test]
5170 fn builtins_elem() {
5171 assert_eq!(ev("builtins.elem 2 [1 2 3]"), Value::Bool(true));
5172 assert_eq!(ev("builtins.elem 5 [1 2 3]"), Value::Bool(false));
5173 assert_eq!(ev("builtins.elem 1 []"), Value::Bool(false));
5174 }
5175
5176 #[test]
5177 fn builtins_head_tail() {
5178 assert_eq!(ev("builtins.head [10 20 30]"), Value::Int(10));
5179 assert_eq!(
5180 ev("builtins.tail [10 20 30]"),
5181 Value::list(vec![Value::Int(20), Value::Int(30)]),
5182 );
5183 }
5184
5185 #[test]
5186 fn builtins_string_length() {
5187 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
5188 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
5189 assert_eq!(ev(r#"builtins.stringLength "abc def""#), Value::Int(7));
5190 }
5191
5192 #[test]
5193 fn builtins_ceil_floor() {
5194 assert_eq!(ev("builtins.ceil 2.3"), Value::Int(3));
5195 assert_eq!(ev("builtins.ceil 2.0"), Value::Int(2));
5196 assert_eq!(ev("builtins.floor 2.9"), Value::Int(2));
5197 assert_eq!(ev("builtins.floor 2.0"), Value::Int(2));
5198 assert_eq!(ev("builtins.ceil 5"), Value::Int(5));
5200 assert_eq!(ev("builtins.floor 5"), Value::Int(5));
5201 }
5202
5203 #[test]
5204 fn builtins_try_eval() {
5205 let v = ev("builtins.tryEval 42");
5206 if let Value::Attrs(attrs) = v {
5207 assert_eq!(attrs.get("success"), Some(&Value::Bool(true)));
5208 assert_eq!(attrs.get("value"), Some(&Value::Int(42)));
5209 } else {
5210 panic!("expected attrs");
5211 }
5212 }
5213
5214 #[test]
5215 fn builtins_throw() {
5216 let result = eval(r#"builtins.throw "oops""#);
5217 assert!(result.is_err());
5218 let msg = format!("{}", result.unwrap_err());
5219 assert!(msg.contains("oops"));
5220 }
5221
5222 #[test]
5223 fn builtins_seq_deep_seq() {
5224 assert_eq!(ev("builtins.seq 1 42"), Value::Int(42));
5226 assert_eq!(ev("builtins.deepSeq [1 2 3] 99"), Value::Int(99));
5228 }
5229
5230 #[test]
5231 fn builtins_current_system() {
5232 let v = ev("builtins.currentSystem");
5233 if let Value::String(ns) = v {
5234 let s = &ns.chars;
5235 assert!(
5237 s == "aarch64-darwin"
5238 || s == "x86_64-darwin"
5239 || s == "aarch64-linux"
5240 || s == "x86_64-linux",
5241 "unexpected system: {s}",
5242 );
5243 } else {
5244 panic!("expected string");
5245 }
5246 }
5247
5248 #[test]
5253 fn pattern_mkif_like() {
5254 assert_eq!(
5256 ev("(if true then { x = 1; } else {}).x"),
5257 Value::Int(1),
5258 );
5259 let v = ev("if false then { x = 1; } else {}");
5260 if let Value::Attrs(attrs) = v {
5261 assert!(attrs.is_empty());
5262 } else {
5263 panic!("expected attrs");
5264 }
5265 }
5266
5267 #[test]
5268 fn pattern_optional_attrs() {
5269 assert_eq!(
5271 ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in (optionalAttrs true { a = 1; }).a"),
5272 Value::Int(1),
5273 );
5274 let v = ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in optionalAttrs false { a = 1; }");
5275 if let Value::Attrs(attrs) = v {
5276 assert!(attrs.is_empty());
5277 } else {
5278 panic!("expected attrs");
5279 }
5280 }
5281
5282 #[test]
5283 fn pattern_filter_attrs_via_remove() {
5284 assert_eq!(
5286 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]).a"#),
5287 Value::Int(1),
5288 );
5289 assert_eq!(
5290 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]) ? b"#),
5291 Value::Bool(false),
5292 );
5293 }
5294
5295 #[test]
5296 fn pattern_override() {
5297 let v = ev(r#"
5299 let
5300 defaults = { debug = false; port = 8080; host = "localhost"; };
5301 overrides = { debug = true; port = 9090; };
5302 in defaults // overrides
5303 "#);
5304 if let Value::Attrs(attrs) = v {
5305 assert_eq!(attrs.get("debug"), Some(&Value::Bool(true)));
5306 assert_eq!(attrs.get("port"), Some(&Value::Int(9090)));
5307 assert_eq!(attrs.get("host"), Some(&Value::string("localhost")));
5308 } else {
5309 panic!("expected attrs");
5310 }
5311 }
5312
5313 #[test]
5314 fn pattern_functor() {
5315 assert_eq!(
5317 ev("let s = { __functor = self: x: self.value + x; value = 10; }; in s 5"),
5318 Value::Int(15),
5319 );
5320 }
5321
5322 #[test]
5323 fn pattern_platform_check() {
5324 let v = ev(r#"if builtins.currentSystem == "aarch64-darwin" then "arm" else "other""#);
5326 if let Value::String(_) = v {
5328 } else {
5330 panic!("expected string");
5331 }
5332 }
5333
5334 #[test]
5335 fn pattern_recursive_overlay_lambda_structure() {
5336 let v = ev("let overlay = self: super: { pkg = 42; }; in overlay {} {}");
5338 if let Value::Attrs(attrs) = v {
5339 assert_eq!(attrs.get("pkg"), Some(&Value::Int(42)));
5340 } else {
5341 panic!("expected attrs");
5342 }
5343 }
5344
5345 #[test]
5346 fn pattern_call_package_simplified() {
5347 assert_eq!(
5349 ev("let callPkg = f: f { lib = { id = x: x; }; }; lib = { id = x: x; }; in callPkg ({ lib }: lib.id 42)"),
5350 Value::Int(42),
5351 );
5352 }
5353
5354 #[test]
5355 fn pattern_derivation_like_attrset() {
5356 let v = ev(r#"{ type = "derivation"; name = "hello"; system = builtins.currentSystem; builder = "/bin/sh"; }"#);
5357 if let Value::Attrs(attrs) = v {
5358 assert_eq!(attrs.get("type"), Some(&Value::string("derivation")));
5359 assert_eq!(attrs.get("name"), Some(&Value::string("hello")));
5360 assert_eq!(attrs.get("builder"), Some(&Value::string("/bin/sh")));
5361 let system = force_value(attrs.get("system").unwrap()).unwrap();
5363 assert!(matches!(system, Value::String(_)), "expected string, got {system:?}");
5364 } else {
5365 panic!("expected attrs");
5366 }
5367 }
5368
5369 #[test]
5370 fn pattern_module_system_simplified() {
5371 assert_eq!(
5373 ev(r#"
5374 let
5375 eval = m: m { config = {}; lib = { mkDefault = x: x; }; };
5376 in eval ({ config, lib }: { result = lib.mkDefault 42; })
5377 "#),
5378 {
5379 let mut attrs = NixAttrs::new();
5380 attrs.insert("result".to_string(), Value::Int(42));
5381 Value::Attrs(Rc::new(attrs))
5382 },
5383 );
5384 }
5385
5386 #[test]
5391 fn error_undefined_variable() {
5392 let result = eval("nonexistent_var");
5393 assert!(result.is_err());
5394 let msg = format!("{}", result.unwrap_err());
5395 assert!(msg.contains("undefined variable") || msg.contains("nonexistent_var"));
5396 }
5397
5398 #[test]
5399 fn error_type_mismatch_arithmetic() {
5400 let result = eval(r#"1 + "hello""#);
5401 assert!(result.is_err());
5402 }
5403
5404 #[test]
5405 fn error_missing_attribute() {
5406 let result = eval("{}.nonexistent");
5407 assert!(result.is_err());
5408 let msg = format!("{}", result.unwrap_err());
5409 assert!(msg.contains("nonexistent") || msg.contains("not found"));
5410 }
5411
5412 #[test]
5413 fn error_division_by_zero() {
5414 assert!(eval("1 / 0").is_err());
5415 assert!(eval("100 / 0").is_err());
5416 }
5417
5418 #[test]
5419 fn error_missing_required_function_arg() {
5420 let result = eval("({ a, b }: a + b) { a = 1; }");
5421 assert!(result.is_err());
5422 let msg = format!("{}", result.unwrap_err());
5423 assert!(msg.contains("missing argument"));
5424 }
5425
5426 #[test]
5427 fn error_unexpected_function_arg() {
5428 let result = eval("({ a }: a) { a = 1; b = 2; }");
5429 assert!(result.is_err());
5430 let msg = format!("{}", result.unwrap_err());
5431 assert!(msg.contains("unexpected argument"));
5432 }
5433
5434 #[test]
5435 fn error_assertion_failure() {
5436 assert!(eval("assert false; 1").is_err());
5437 assert!(eval("assert 1 == 2; 1").is_err());
5438 }
5439
5440 #[test]
5441 fn error_infinite_recursion() {
5442 let result = eval("let x = x; in x");
5445 assert!(result.is_err());
5446 }
5447
5448 #[test]
5449 fn error_infinite_recursion_via_lambda() {
5450 let result = eval("let f = x: f x; in f 1");
5452 assert!(result.is_err());
5453 let msg = format!("{}", result.unwrap_err());
5454 assert!(
5455 msg.contains("infinite recursion") || msg.contains("eval depth") || msg.contains("undefined"),
5456 );
5457 }
5458
5459 #[test]
5464 fn integration_let_with_function_returning_attrset() {
5465 assert_eq!(
5466 ev("let mkPkg = name: { inherit name; version = 1; }; in (mkPkg \"hello\").name"),
5467 Value::string("hello"),
5468 );
5469 }
5470
5471 #[test]
5472 fn integration_chained_updates() {
5473 assert_eq!(
5474 ev("({ a = 1; } // { b = 2; } // { c = 3; }).c"),
5475 Value::Int(3),
5476 );
5477 }
5478
5479 #[test]
5480 fn integration_map_over_attrnames() {
5481 assert_eq!(
5483 ev(r#"
5484 let
5485 set = { a = 1; b = 2; };
5486 names = builtins.attrNames set;
5487 in builtins.length names
5488 "#),
5489 Value::Int(2),
5490 );
5491 }
5492
5493 #[test]
5494 fn integration_compose_functions() {
5495 assert_eq!(
5497 ev("let compose = f: g: x: f (g x); double = x: x * 2; inc = x: x + 1; in compose double inc 5"),
5498 Value::Int(12), );
5500 }
5501
5502 #[test]
5503 fn integration_recursive_list_building() {
5504 assert_eq!(
5506 ev("builtins.map (x: x * x) (builtins.genList (x: x + 1) 4)"),
5507 Value::list(vec![Value::Int(1), Value::Int(4), Value::Int(9), Value::Int(16)]),
5508 );
5509 }
5510
5511 #[test]
5512 fn integration_attrset_from_list() {
5513 let v = ev(r#"
5515 builtins.listToAttrs (builtins.map (x: { name = x; value = true; }) ["a" "b" "c"])
5516 "#);
5517 if let Value::Attrs(attrs) = v {
5518 assert_eq!(attrs.get("a"), Some(&Value::Bool(true)));
5519 assert_eq!(attrs.get("b"), Some(&Value::Bool(true)));
5520 assert_eq!(attrs.get("c"), Some(&Value::Bool(true)));
5521 } else {
5522 panic!("expected attrs");
5523 }
5524 }
5525
5526 #[test]
5527 fn integration_nested_with_and_let() {
5528 assert_eq!(
5529 ev("let x = 10; in with { y = 20; }; x + y"),
5530 Value::Int(30),
5531 );
5532 }
5533
5534 #[test]
5535 fn integration_complex_pattern_match() {
5536 assert_eq!(
5538 ev("(args @ { a, b ? 5, ... }: a + b + (if args ? c then args.c else 0)) { a = 1; c = 10; }"),
5539 Value::Int(16), );
5541 }
5542
5543 #[test]
5544 fn integration_substring() {
5545 assert_eq!(
5546 ev(r#"builtins.substring 0 5 "hello world""#),
5547 Value::string("hello"),
5548 );
5549 assert_eq!(
5550 ev(r#"builtins.substring 6 5 "hello world""#),
5551 Value::string("world"),
5552 );
5553 }
5554
5555 #[test]
5556 fn integration_has_attr_on_nested() {
5557 assert_eq!(ev("{ a = { b = 1; }; } ? a"), Value::Bool(true));
5559 assert_eq!(
5560 ev("({ a = { b = 1; }; }.a) ? b"),
5561 Value::Bool(true),
5562 );
5563 }
5564
5565 #[test]
5566 fn integration_cat_attrs() {
5567 assert_eq!(
5568 ev(r#"builtins.catAttrs "x" [{ x = 1; } { y = 2; } { x = 3; }]"#),
5569 Value::list(vec![Value::Int(1), Value::Int(3)]),
5570 );
5571 }
5572
5573 #[test]
5574 fn integration_get_attr_builtin() {
5575 assert_eq!(
5576 ev(r#"builtins.getAttr "a" { a = 42; b = 10; }"#),
5577 Value::Int(42),
5578 );
5579 }
5580
5581 #[test]
5582 fn integration_has_attr_builtin() {
5583 assert_eq!(
5584 ev(r#"builtins.hasAttr "a" { a = 1; }"#),
5585 Value::Bool(true),
5586 );
5587 assert_eq!(
5588 ev(r#"builtins.hasAttr "z" { a = 1; }"#),
5589 Value::Bool(false),
5590 );
5591 }
5592
5593 #[test]
5594 fn integration_is_path() {
5595 assert_eq!(ev("builtins.isPath ./foo"), Value::Bool(true));
5596 assert_eq!(ev("builtins.isPath 42"), Value::Bool(false));
5597 }
5598
5599 #[test]
5600 fn integration_builtins_trace() {
5601 assert_eq!(ev(r#"builtins.trace "debug msg" 42"#), Value::Int(42));
5603 }
5604
5605 #[test]
5606 fn integration_builtins_split() {
5607 assert_eq!(
5611 ev(r#"builtins.split "/" "a/b/c""#),
5612 Value::list(vec![
5613 Value::string("a"),
5614 Value::list(vec![]),
5615 Value::string("b"),
5616 Value::list(vec![]),
5617 Value::string("c"),
5618 ]),
5619 );
5620 assert_eq!(
5623 ev(r#"builtins.split "(/)" "a/b/c""#),
5624 Value::list(vec![
5625 Value::string("a"),
5626 Value::list(vec![Value::string("/")]),
5627 Value::string("b"),
5628 Value::list(vec![Value::string("/")]),
5629 Value::string("c"),
5630 ]),
5631 );
5632 }
5633
5634 #[test]
5635 fn integration_builtins_split_no_capture_groups() {
5636 assert_eq!(
5641 ev(r#"builtins.split "-" "aarch64-darwin""#),
5642 Value::list(vec![
5643 Value::string("aarch64"),
5644 Value::list(vec![]),
5645 Value::string("darwin"),
5646 ]),
5647 );
5648 }
5649
5650 #[test]
5651 fn integration_builtins_split_system_string_filter() {
5652 assert_eq!(
5655 ev(r#"builtins.filter builtins.isString (builtins.split "-" "aarch64-darwin")"#),
5656 Value::list(vec![
5657 Value::string("aarch64"),
5658 Value::string("darwin"),
5659 ]),
5660 );
5661 }
5662
5663 #[test]
5664 fn integration_deeply_nested_let() {
5665 assert_eq!(
5667 ev("let a = let b = let c = 10; in c * 2; in b + 1; in a"),
5668 Value::Int(21),
5669 );
5670 }
5671
5672 #[test]
5673 fn integration_if_in_attrset_value() {
5674 assert_eq!(
5675 ev("{ x = if true then 1 else 2; }.x"),
5676 Value::Int(1),
5677 );
5678 }
5679
5680 #[test]
5681 fn integration_lambda_in_list() {
5682 assert_eq!(
5684 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 0) 5"),
5685 Value::Int(6),
5686 );
5687 assert_eq!(
5688 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 1) 5"),
5689 Value::Int(10),
5690 );
5691 }
5692
5693 #[test]
5694 fn integration_nixpkgs_lib_id() {
5695 assert_eq!(
5697 ev("let lib = { id = x: x; const = a: b: a; }; in lib.id 42"),
5698 Value::Int(42),
5699 );
5700 assert_eq!(
5701 ev("let lib = { id = x: x; const = a: b: a; }; in lib.const 1 2"),
5702 Value::Int(1),
5703 );
5704 }
5705
5706 #[test]
5707 fn integration_multiple_inherit() {
5708 assert_eq!(
5709 ev("let a = 1; b = 2; c = 3; in { inherit a b c; }.b"),
5710 Value::Int(2),
5711 );
5712 }
5713
5714 #[test]
5715 fn integration_rec_set_with_builtins() {
5716 assert_eq!(
5717 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
5718 Value::Int(5),
5719 );
5720 }
5721
5722 #[test]
5727 fn functor_simple_callable_attrset() {
5728 assert_eq!(
5729 ev("let s = { __functor = self: x: x + 1; }; in s 41"),
5730 Value::Int(42),
5731 );
5732 }
5733
5734 #[test]
5735 fn functor_with_self_reference() {
5736 assert_eq!(
5737 ev("let s = { __functor = self: x: self.base + x; base = 100; }; in s 23"),
5738 Value::Int(123),
5739 );
5740 }
5741
5742 #[test]
5743 fn functor_updated_attrset() {
5744 assert_eq!(
5746 ev(r#"
5747 let
5748 mk = { __functor = self: x: self.n + x; n = 0; };
5749 s = mk // { n = 50; };
5750 in s 7
5751 "#),
5752 Value::Int(57),
5753 );
5754 }
5755
5756 #[test]
5757 fn functor_error_on_non_callable_attrset() {
5758 let result = eval("let s = { a = 1; }; in s 5");
5760 assert!(result.is_err());
5761 }
5762
5763 #[test]
5768 fn to_string_protocol_in_interpolation() {
5769 assert_eq!(
5770 ev(r#"let s = { __toString = self: "world"; }; in "hello ${s}""#),
5771 Value::string("hello world"),
5772 );
5773 }
5774
5775 #[test]
5776 fn to_string_protocol_accesses_self() {
5777 assert_eq!(
5778 ev(r#"let s = { __toString = self: self.val; val = "abc"; }; in "${s}""#),
5779 Value::string("abc"),
5780 );
5781 }
5782
5783 #[test]
5784 fn to_string_protocol_via_builtin_to_string() {
5785 assert_eq!(
5786 ev(r#"builtins.toString { __toString = self: "via-builtin"; }"#),
5787 Value::string("via-builtin"),
5788 );
5789 }
5790
5791 #[test]
5792 fn to_string_protocol_attrset_without_toString_fails() {
5793 let result = eval(r#""${{}}"#);
5795 assert!(result.is_err());
5796 }
5797
5798 #[test]
5803 fn eval_builtins_concat_strings() {
5804 assert_eq!(
5805 ev(r#"builtins.concatStrings ["a" "b" "c"]"#),
5806 Value::string("abc"),
5807 );
5808 assert_eq!(
5809 ev(r#"builtins.concatStrings []"#),
5810 Value::string(""),
5811 );
5812 }
5813
5814 #[test]
5815 fn eval_builtins_partition() {
5816 let v = ev("builtins.partition (x: x > 3) [1 2 3 4 5]");
5817 if let Value::Attrs(a) = v {
5818 assert_eq!(a.get("right"), Some(&Value::list(vec![Value::Int(4), Value::Int(5)])));
5819 assert_eq!(a.get("wrong"), Some(&Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)])));
5820 } else {
5821 panic!("expected attrs");
5822 }
5823 }
5824
5825 #[test]
5826 fn eval_builtins_group_by() {
5827 let v = ev(r#"builtins.groupBy (x: if x > 0 then "pos" else "neg") [1 (0 - 2) 3 (0 - 4)]"#);
5828 if let Value::Attrs(a) = v {
5829 assert_eq!(a.get("pos"), Some(&Value::list(vec![Value::Int(1), Value::Int(3)])));
5830 assert_eq!(a.get("neg"), Some(&Value::list(vec![Value::Int(-2), Value::Int(-4)])));
5831 } else {
5832 panic!("expected attrs");
5833 }
5834 }
5835
5836 #[test]
5837 fn eval_builtins_zip_attrs_with() {
5838 let v = ev("builtins.zipAttrsWith (n: vs: builtins.head vs) [{ a = 1; } { a = 2; b = 3; }]");
5839 if let Value::Attrs(a) = v {
5840 assert_eq!(a.get("a"), Some(&Value::Int(1)));
5841 assert_eq!(a.get("b"), Some(&Value::Int(3)));
5842 } else {
5843 panic!("expected attrs");
5844 }
5845 }
5846
5847 #[test]
5848 fn eval_builtins_compare_versions() {
5849 assert_eq!(ev(r#"builtins.compareVersions "2.0" "1.0""#), Value::Int(1));
5850 assert_eq!(ev(r#"builtins.compareVersions "1.0" "2.0""#), Value::Int(-1));
5851 assert_eq!(ev(r#"builtins.compareVersions "1.0" "1.0""#), Value::Int(0));
5852 }
5853
5854 #[test]
5855 fn eval_builtins_parse_drv_name() {
5856 let v = ev(r#"builtins.parseDrvName "nix-2.3.4""#);
5857 if let Value::Attrs(a) = v {
5858 assert_eq!(a.get("name"), Some(&Value::string("nix")));
5859 assert_eq!(a.get("version"), Some(&Value::string("2.3.4")));
5860 } else {
5861 panic!("expected attrs");
5862 }
5863 }
5864
5865 #[test]
5866 fn eval_builtins_base_name_of() {
5867 assert_eq!(
5868 ev(r#"builtins.baseNameOf "/foo/bar/baz""#),
5869 Value::string("baz"),
5870 );
5871 }
5872
5873 #[test]
5874 fn eval_builtins_dir_of() {
5875 assert_eq!(
5876 ev(r#"builtins.dirOf "/foo/bar/baz""#),
5877 Value::string("/foo/bar"),
5878 );
5879 }
5880
5881 #[test]
5882 fn eval_builtins_add_error_context() {
5883 assert_eq!(
5884 ev(r#"builtins.addErrorContext "some context" 42"#),
5885 Value::Int(42),
5886 );
5887 }
5888
5889 #[test]
5890 fn eval_builtins_abort() {
5891 let result = eval(r#"builtins.abort "fatal error""#);
5892 assert!(result.is_err());
5893 let msg = format!("{}", result.unwrap_err());
5894 assert!(msg.contains("fatal error"));
5895 }
5896
5897 #[test]
5902 fn indented_string_simple() {
5903 assert_eq!(ev("''hello''"), Value::string("hello"));
5904 }
5905
5906 #[test]
5907 fn indented_string_multiline_strips_indent() {
5908 assert_eq!(
5909 ev("''\n line1\n line2\n''"),
5910 Value::string("line1\nline2\n"),
5911 );
5912 }
5913
5914 #[test]
5915 fn indented_string_with_interpolation() {
5916 let code = "let x = \"world\"; in ''hello ${x}''";
5917 assert_eq!(
5918 ev(code),
5919 Value::string("hello world"),
5920 );
5921 }
5922
5923 #[test]
5924 fn indented_string_deeper_indent_preserved() {
5925 assert_eq!(
5927 ev("''\n a\n b\n''"),
5928 Value::string("a\n b\n"),
5929 );
5930 }
5931
5932 #[test]
5937 fn dynamic_attr_name_in_set() {
5938 assert_eq!(
5939 ev(r#"let key = "mykey"; in { ${key} = 42; }.mykey"#),
5940 Value::Int(42),
5941 );
5942 }
5943
5944 #[test]
5945 fn dynamic_attr_name_with_expression() {
5946 assert_eq!(
5947 ev(r#"let prefix = "foo"; in { ${"${prefix}bar"} = 1; }.foobar"#),
5948 Value::Int(1),
5949 );
5950 }
5951
5952 #[test]
5957 fn eval_builtins_match() {
5958 assert_eq!(
5959 ev(r#"builtins.match "([0-9]+)" "42""#),
5960 Value::list(vec![Value::string("42")]),
5961 );
5962 }
5963
5964 #[test]
5965 fn eval_builtins_hash_string() {
5966 let v = ev(r#"builtins.hashString "sha256" "hello""#);
5967 if let Value::String(ns) = v {
5968 assert_eq!(ns.chars.len(), 64);
5969 } else {
5970 panic!("expected string");
5971 }
5972 }
5973
5974 #[test]
5975 fn eval_builtins_import() {
5976 let dir = std::env::temp_dir();
5977 let path = dir.join("sui_eval_test_import_eval.nix");
5978 std::fs::write(&path, "42").unwrap();
5979 let expr = format!(r#"import "{}""#, path.display());
5980 let v = eval(&expr).unwrap();
5981 assert_eq!(v, Value::Int(42));
5982 std::fs::remove_file(&path).ok();
5983 }
5984
5985 #[test]
5986 fn eval_builtins_derivation() {
5987 let v = eval(r#"builtins.derivation { name = "test"; system = "x86_64-linux"; builder = "/bin/sh"; }"#).unwrap();
5988 if let Value::Attrs(a) = v {
5989 assert_eq!(a.get("type"), Some(&Value::string("derivation")));
5990 } else {
5991 panic!("expected attrs");
5992 }
5993 }
5994
5995 #[test]
5996 fn eval_mutual_recursive_let() {
5997 let v = eval("let a = { x = b; }; b = { y = a; }; in a.x.y");
6004 assert!(v.is_ok(), "mutual recursive let should not error: {v:?}");
6005 let val = v.unwrap();
6007 assert!(
6008 matches!(val, Value::Attrs(_)),
6009 "a.x.y should be an attrset, got: {val:?}",
6010 );
6011 }
6012
6013 #[test]
6014 fn eval_mutual_recursive_let_simple() {
6015 let v = eval("let a = b; b = 42; in a");
6017 assert!(v.is_ok());
6018 assert_eq!(v.unwrap(), Value::Int(42));
6021 }
6022
6023 #[test]
6024 fn eval_builtins_read_dir() {
6025 let dir = std::env::temp_dir().join("sui_eval_test_readdir_eval");
6026 let _ = std::fs::remove_dir_all(&dir);
6027 std::fs::create_dir_all(&dir).unwrap();
6028 std::fs::write(dir.join("a.txt"), "").unwrap();
6029 let expr = format!(r#"builtins.readDir "{}""#, dir.display());
6030 let v = eval(&expr).unwrap();
6031 if let Value::Attrs(a) = v {
6032 assert_eq!(a.get("a.txt"), Some(&Value::string("regular")));
6033 } else {
6034 panic!("expected attrs");
6035 }
6036 let _ = std::fs::remove_dir_all(&dir);
6037 }
6038
6039 #[test]
6044 fn thunk_basic_let() {
6045 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
6047 }
6048
6049 #[test]
6050 fn thunk_forward_ref() {
6051 assert_eq!(ev("let a = b; b = 1; in a"), Value::Int(1));
6053 }
6054
6055 #[test]
6056 fn thunk_mutual_rec_attrset_in_let() {
6057 assert_eq!(ev("let a = { x = b; }; b = { y = 1; }; in a.x.y"), Value::Int(1));
6059 }
6060
6061 #[test]
6062 fn thunk_rec_attrset() {
6063 assert_eq!(ev("(rec { a = b; b = 1; }).a"), Value::Int(1));
6065 }
6066
6067 #[test]
6068 fn thunk_rec_attrset_chain() {
6069 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
6071 }
6072
6073 #[test]
6074 fn thunk_fixpoint() {
6075 assert_eq!(
6077 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; })).b"),
6078 Value::Int(2),
6079 );
6080 }
6081
6082 #[test]
6083 fn thunk_blackhole_self_reference() {
6084 let result = eval("let x = x; in x");
6086 assert!(result.is_err());
6087 let msg = format!("{}", result.unwrap_err());
6088 assert!(
6089 msg.contains("infinite recursion") || msg.contains("blackhole"),
6090 "expected blackhole error, got: {msg}",
6091 );
6092 }
6093
6094 #[test]
6095 fn thunk_mutual_blackhole() {
6096 let result = eval("let a = b; b = a; in a");
6098 assert!(result.is_err());
6099 }
6100
6101 #[test]
6102 fn thunk_let_body_forces_correctly() {
6103 assert_eq!(ev("let a = 10; b = 20; in a + b"), Value::Int(30));
6105 }
6106
6107 #[test]
6108 fn thunk_only_forced_when_needed() {
6109 assert_eq!(ev("let bad = 1 / 0; good = 42; in good"), Value::Int(42));
6111 }
6112
6113 #[test]
6114 fn thunk_forward_ref_in_function_body() {
6115 assert_eq!(
6117 ev("let f = x: x + b; b = 10; in f 5"),
6118 Value::Int(15),
6119 );
6120 }
6121
6122 #[test]
6123 fn thunk_rec_set_self_ref_through_self() {
6124 assert_eq!(
6126 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
6127 Value::Int(5),
6128 );
6129 }
6130
6131 #[test]
6132 fn thunk_nested_let_forward_ref() {
6133 assert_eq!(
6135 ev("let a = b + 1; b = 2; in a"),
6136 Value::Int(3),
6137 );
6138 }
6139
6140 #[test]
6141 fn thunk_deep_chain() {
6142 assert_eq!(
6144 ev("let a = 1; b = a; c = b; d = c; e = d; in e"),
6145 Value::Int(1),
6146 );
6147 }
6148
6149 #[test]
6150 fn thunk_rec_set_fixpoint() {
6151 assert_eq!(
6153 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; c = self.b + 1; })).c"),
6154 Value::Int(3),
6155 );
6156 }
6157
6158 #[test]
6159 fn thunk_let_with_inherit() {
6160 assert_eq!(
6162 ev("let a = 1; in let inherit a; b = a + 1; in b"),
6163 Value::Int(2),
6164 );
6165 }
6166
6167 #[test]
6168 fn thunk_attrset_value_lazy() {
6169 assert_eq!(
6172 ev("let x = 42; in { a = x; }.a"),
6173 Value::Int(42),
6174 );
6175 }
6176
6177 #[test]
6178 fn thunk_unused_error_not_forced() {
6179 assert_eq!(
6181 ev(r#"let bad = builtins.throw "boom"; ok = 1; in ok"#),
6182 Value::Int(1),
6183 );
6184 }
6185
6186 #[test]
6187 fn thunk_rec_set_mutual_reference() {
6188 let v = ev("rec { a = { val = b.val + 1; }; b = { val = 10; }; }");
6190 if let Value::Attrs(attrs) = v {
6191 let a = attrs.get("a").unwrap();
6192 let a_forced = force_value(a).unwrap();
6193 if let Value::Attrs(a_attrs) = a_forced {
6194 assert_eq!(a_attrs.get("val"), Some(&Value::Int(11)));
6195 } else {
6196 panic!("expected attrs for a");
6197 }
6198 } else {
6199 panic!("expected attrs");
6200 }
6201 }
6202
6203 #[test]
6206 fn let_rec_self_reference_simple() {
6207 assert_eq!(
6208 ev("let x = 1; y = x + 1; in y"),
6209 Value::Int(2),
6210 );
6211 }
6212
6213 #[test]
6214 fn let_rec_self_reference_chain() {
6215 assert_eq!(
6216 ev("let a = 1; b = a + 1; c = b + 1; in c"),
6217 Value::Int(3),
6218 );
6219 }
6220
6221 #[test]
6222 fn let_rec_self_reference_with_function() {
6223 assert_eq!(
6224 ev("let f = x: x + 1; y = f 10; in y"),
6225 Value::Int(11),
6226 );
6227 }
6228
6229 #[test]
6230 fn let_rec_mutual_recursion_via_if() {
6231 assert_eq!(
6232 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"),
6233 Value::Bool(true),
6234 );
6235 }
6236
6237 #[test]
6238 fn let_rec_forward_ref_in_list() {
6239 assert_eq!(
6240 ev("let xs = [a b]; a = 1; b = 2; in builtins.length xs"),
6241 Value::Int(2),
6242 );
6243 }
6244
6245 #[test]
6248 fn with_shadowing_let_wins_over_with() {
6249 assert_eq!(
6250 ev("let x = 1; in with { x = 2; }; x"),
6251 Value::Int(1),
6252 );
6253 }
6254
6255 #[test]
6256 fn with_shadowing_inner_with_wins() {
6257 assert_eq!(
6258 ev("with { x = 1; }; with { x = 2; }; x"),
6259 Value::Int(2),
6260 );
6261 }
6262
6263 #[test]
6264 fn with_shadowing_outer_provides_missing() {
6265 assert_eq!(
6266 ev("with { x = 1; y = 10; }; with { x = 2; }; x + y"),
6267 Value::Int(12),
6268 );
6269 }
6270
6271 #[test]
6272 fn with_shadowing_lambda_arg_wins() {
6273 assert_eq!(
6274 ev("(x: with { x = 99; }; x) 42"),
6275 Value::Int(42),
6276 );
6277 }
6278
6279 #[test]
6280 fn with_shadowing_nested_let_wins_over_with() {
6281 assert_eq!(
6282 ev("with { x = 1; }; let x = 2; in x"),
6283 Value::Int(2),
6284 );
6285 }
6286
6287 #[test]
6288 fn with_scope_dynamic_attrs() {
6289 assert_eq!(
6290 ev(r#"with { x = 1; y = 2; z = 3; }; x + y + z"#),
6291 Value::Int(6),
6292 );
6293 }
6294
6295 #[test]
6296 fn with_scope_over_lazy_thunk_chain_resolves() {
6297 assert_eq!(
6306 ev(r#"let outer = if true then (if true then { unix = 42; } else {}) else {};
6307 # force a two-deep lazy wrap of the with-head
6308 head = (x: x) ((y: y) outer);
6309 in with head; unix"#),
6310 Value::Int(42),
6311 );
6312 }
6313
6314 #[test]
6315 fn with_scope_head_from_deep_select_resolves() {
6316 assert_eq!(
6319 ev(r#"let a = { b = { c = { key = 7; }; }; }; in with a.b.c; key"#),
6320 Value::Int(7),
6321 );
6322 }
6323
6324 #[test]
6327 fn attrset_deep_merge_simple() {
6328 let v = ev("{ a.b = 1; a.c = 2; }");
6329 if let Value::Attrs(attrs) = v {
6330 let a = force_value(attrs.get("a").unwrap()).unwrap();
6331 if let Value::Attrs(inner) = a {
6332 assert_eq!(force_value(inner.get("b").unwrap()).unwrap(), Value::Int(1));
6333 assert_eq!(force_value(inner.get("c").unwrap()).unwrap(), Value::Int(2));
6334 } else {
6335 panic!("expected nested attrs");
6336 }
6337 } else {
6338 panic!("expected attrs");
6339 }
6340 }
6341
6342 #[test]
6343 fn attrset_deep_merge_three_levels() {
6344 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
6345 if let Value::Attrs(attrs) = v {
6346 let a = force_value(attrs.get("a").unwrap()).unwrap();
6347 if let Value::Attrs(a_inner) = a {
6348 let e = force_value(a_inner.get("e").unwrap()).unwrap();
6349 assert_eq!(e, Value::Int(3));
6350 let b = force_value(a_inner.get("b").unwrap()).unwrap();
6351 if let Value::Attrs(b_inner) = b {
6352 assert_eq!(force_value(b_inner.get("c").unwrap()).unwrap(), Value::Int(1));
6353 assert_eq!(force_value(b_inner.get("d").unwrap()).unwrap(), Value::Int(2));
6354 } else {
6355 panic!("expected nested attrs for b");
6356 }
6357 } else {
6358 panic!("expected nested attrs for a");
6359 }
6360 } else {
6361 panic!("expected attrs");
6362 }
6363 }
6364
6365 #[test]
6366 fn attrset_deep_merge_preserves_siblings() {
6367 assert_eq!(
6368 ev("{ a.x = 1; b = 2; a.y = 3; }.b"),
6369 Value::Int(2),
6370 );
6371 }
6372
6373 #[test]
6374 fn attrset_deep_merge_in_let() {
6375 let v = ev("let s = { a.b = 1; a.c = 2; }; in s.a.b + s.a.c");
6376 assert_eq!(v, Value::Int(3));
6377 }
6378
6379 #[test]
6380 fn attrset_deep_merge_fullset_then_dotted() {
6381 let v = ev("let s = { a = { x = 1; }; a.y = 2; }; in s.a.x + s.a.y");
6388 assert_eq!(v, Value::Int(3));
6389 let both = ev("let s = { a = { x = 1; }; a.y = 2; }; in [ s.a.x s.a.y ]");
6391 if let Value::List(items) = both {
6392 assert_eq!(force_value(&items[0]).unwrap(), Value::Int(1));
6393 assert_eq!(force_value(&items[1]).unwrap(), Value::Int(2));
6394 } else {
6395 panic!("expected list");
6396 }
6397 }
6398
6399 #[test]
6402 fn inherit_from_basic() {
6403 assert_eq!(
6404 ev("let s = { x = 1; y = 2; }; in let inherit (s) x y; in x + y"),
6405 Value::Int(3),
6406 );
6407 }
6408
6409 #[test]
6410 fn inherit_from_with_shadowing() {
6411 assert_eq!(
6412 ev("let x = 10; in let inherit ({ x = 20; }) x; in x"),
6413 Value::Int(20),
6414 );
6415 }
6416
6417 #[test]
6418 fn inherit_from_in_attrset() {
6419 let v = ev(r#"let s = { a = 1; b = 2; }; in { inherit (s) a b; c = 3; }"#);
6420 if let Value::Attrs(attrs) = v {
6421 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
6422 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
6423 assert_eq!(force_value(attrs.get("c").unwrap()).unwrap(), Value::Int(3));
6424 } else {
6425 panic!("expected attrs");
6426 }
6427 }
6428
6429 #[test]
6430 fn inherit_from_rec_set() {
6431 assert_eq!(
6432 ev("rec { inherit ({ x = 42; }) x; y = x; }.y"),
6433 Value::Int(42),
6434 );
6435 }
6436
6437 #[test]
6438 fn inherit_plain_from_scope() {
6439 assert_eq!(
6440 ev("let x = 1; in { inherit x; }.x"),
6441 Value::Int(1),
6442 );
6443 }
6444
6445 #[test]
6454 fn inherit_plain_from_with_scope_lazy() {
6455 assert_eq!(
6459 ev("let fix = f: let x = f x; in x;
6460 self = fix (self: with self; {
6461 a = use { inherit cp; };
6462 use = { cp }: cp 5;
6463 cp = x: x + 100;
6464 });
6465 in self.a"),
6466 Value::Int(105),
6467 );
6468 assert_eq!(
6470 ev("with { y = 7; }; { inherit y; }.y"),
6471 Value::Int(7),
6472 );
6473 }
6474
6475 #[test]
6476 fn inherit_multiple_from_expr() {
6477 assert_eq!(
6478 ev("let s = { a = 10; b = 20; c = 30; }; in let inherit (s) a b c; in a + b + c"),
6479 Value::Int(60),
6480 );
6481 }
6482
6483 #[test]
6486 fn interp_nested_attrset_access() {
6487 assert_eq!(
6488 ev(r#"let x = { a = "hello"; }; in "${x.a} world""#),
6489 Value::string("hello world"),
6490 );
6491 }
6492
6493 #[test]
6494 fn interp_with_let_expression() {
6495 assert_eq!(
6496 ev(r#""${let x = "inner"; in x}""#),
6497 Value::string("inner"),
6498 );
6499 }
6500
6501 #[test]
6502 fn interp_float_coercion() {
6503 assert_eq!(
6505 ev(r#""${toString 3.14}""#),
6506 Value::string("3.140000"),
6507 );
6508 }
6509
6510 #[test]
6513 fn compare_mixed_int_float() {
6514 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
6515 assert_eq!(ev("1.5 > 1"), Value::Bool(true));
6516 assert_eq!(ev("2.0 == 2"), Value::Bool(true));
6517 }
6518
6519 #[test]
6520 fn compare_string_lexicographic() {
6521 assert_eq!(ev(r#""abc" < "abd""#), Value::Bool(true));
6522 assert_eq!(ev(r#""abc" < "abc""#), Value::Bool(false));
6523 assert_eq!(ev(r#""abc" <= "abc""#), Value::Bool(true));
6524 }
6525
6526 #[test]
6529 fn update_empty_sets() {
6530 let v = ev("{} // {}");
6531 if let Value::Attrs(a) = v { assert!(a.is_empty()); } else { panic!(); }
6532 }
6533
6534 #[test]
6535 fn update_right_overrides_completely() {
6536 assert_eq!(
6537 ev("{ a = 1; b = 2; } // { a = 10; c = 30; }"),
6538 ev("{ a = 10; b = 2; c = 30; }"),
6539 );
6540 }
6541
6542 #[test]
6543 fn update_chained() {
6544 assert_eq!(
6545 ev("{ a = 1; } // { b = 2; } // { c = 3; }"),
6546 ev("{ a = 1; b = 2; c = 3; }"),
6547 );
6548 }
6549
6550 #[test]
6553 fn force_value_concrete_unchanged() {
6554 let v = Value::Int(42);
6555 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
6556 }
6557
6558 #[test]
6559 fn force_value_null() {
6560 assert_eq!(force_value(&Value::Null).unwrap(), Value::Null);
6561 }
6562
6563 #[test]
6566 fn eval_with_file_none() {
6567 let result = eval_with_file("1 + 2", None).unwrap();
6568 assert_eq!(result, Value::Int(3));
6569 }
6570
6571 #[test]
6574 fn error_type_mismatch_in_comparison() {
6575 let result = eval(r#"1 < "a""#);
6576 assert!(result.is_err());
6577 }
6578
6579 #[test]
6580 fn error_select_from_non_set() {
6581 let result = eval("42.x");
6582 assert!(result.is_err());
6583 }
6584
6585 #[test]
6586 fn error_call_non_function() {
6587 let result = eval("42 1");
6588 assert!(result.is_err());
6589 }
6590
6591 #[test]
6592 fn error_negate_string() {
6593 let result = eval(r#"-"hello""#);
6594 assert!(result.is_err());
6595 }
6596
6597 #[test]
6600 fn multiline_string_empty() {
6601 assert_eq!(ev("''''"), Value::string(""));
6602 }
6603
6604 #[test]
6605 fn multiline_string_with_trailing_newline() {
6606 let v = ev("''\n hello\n''");
6607 assert_eq!(v, Value::string("hello\n"));
6608 }
6609
6610 #[test]
6613 fn list_concat_empty_left() {
6614 assert_eq!(ev("[] ++ [1 2]"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6615 }
6616
6617 #[test]
6618 fn list_concat_empty_right() {
6619 assert_eq!(ev("[1 2] ++ []"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6620 }
6621
6622 #[test]
6623 fn list_concat_both_empty() {
6624 assert_eq!(ev("[] ++ []"), Value::list(vec![]));
6625 }
6626
6627 #[test]
6630 fn formals_at_pattern_accessible() {
6631 assert_eq!(
6632 ev("({ x, ... } @ args: builtins.length (builtins.attrNames args)) { x = 1; y = 2; z = 3; }"),
6633 Value::Int(3),
6634 );
6635 }
6636
6637 #[test]
6638 fn formals_default_uses_other_arg() {
6639 assert_eq!(
6640 ev("({ x, y ? x + 1 }: y) { x = 10; }"),
6641 Value::Int(11),
6642 );
6643 }
6644
6645 #[test]
6646 fn formals_default_lazy_assert_false() {
6647 assert_eq!(
6651 ev("({ cpu, vendor ? assert false; null, kernel } @ args: if args ? vendor then vendor else \"inferred\") { cpu = \"x86_64\"; kernel = \"linux\"; }"),
6652 Value::String(Rc::new(NixString::plain("inferred"))),
6653 );
6654 }
6655
6656 #[test]
6657 fn formals_default_lazy_only_forced_when_accessed() {
6658 assert_eq!(
6660 ev("({ a, b ? 42 }: b) { a = 1; }"),
6661 Value::Int(42),
6662 );
6663 }
6664
6665 #[test]
6666 fn formals_ellipsis_ignores_extra() {
6667 assert_eq!(
6668 ev("({ x, ... }: x) { x = 1; y = 2; z = 3; }"),
6669 Value::Int(1),
6670 );
6671 }
6672
6673 #[test]
6676 fn pure_mode_roundtrip() {
6677 let was_pure = is_pure_mode();
6678 set_pure_mode(true);
6679 assert!(is_pure_mode());
6680 set_pure_mode(false);
6681 assert!(!is_pure_mode());
6682 set_pure_mode(was_pure);
6683 }
6684
6685 #[test]
6688 fn path_concat_with_string() {
6689 assert_eq!(
6690 ev(r#"/foo + "bar""#),
6691 Value::Path(Box::new(SmolStr::from("/foobar"))),
6692 );
6693 }
6694
6695 #[test]
6696 fn path_concat_with_path() {
6697 assert_eq!(
6698 ev("/foo + /bar"),
6699 Value::Path(Box::new(SmolStr::from("/foo//bar"))),
6700 );
6701 }
6702
6703 #[test]
6706 fn current_eval_dir_empty_when_no_file_pushed() {
6707 let snapshot = current_eval_dir();
6711 let _ = snapshot;
6713 }
6714
6715 #[test]
6716 fn push_eval_file_sets_current_dir() {
6717 let p = std::path::PathBuf::from("/tmp/example/file.nix");
6718 {
6719 let _g = push_eval_file(p.clone());
6720 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/tmp/example")));
6721 }
6722 }
6726
6727 #[test]
6728 fn push_eval_file_nested_stack() {
6729 let outer = std::path::PathBuf::from("/a/x.nix");
6730 let inner = std::path::PathBuf::from("/b/y.nix");
6731 {
6732 let _g_outer = push_eval_file(outer.clone());
6733 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6734 {
6735 let _g_inner = push_eval_file(inner.clone());
6736 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/b")));
6737 }
6738 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6740 }
6741 }
6742
6743 #[test]
6746 fn error_undefined_var_includes_file_context() {
6747 let p = std::path::PathBuf::from("/nix/store/abc-default.nix");
6748 let _g = push_eval_file(p);
6749 let result = eval("nonexistent_xyz");
6750 let msg = format!("{}", result.unwrap_err());
6751 assert!(msg.contains("undefined variable"), "msg: {msg}");
6752 assert!(msg.contains("nonexistent_xyz"), "msg: {msg}");
6753 assert!(msg.contains("abc-default.nix"), "msg: {msg}");
6754 }
6755
6756 #[test]
6757 fn error_attr_not_found_includes_file_context() {
6758 let p = std::path::PathBuf::from("/nix/store/xyz-module.nix");
6759 let _g = push_eval_file(p);
6760 let result = eval("{}.missing_key");
6761 let msg = format!("{}", result.unwrap_err());
6762 assert!(msg.contains("not found") || msg.contains("missing_key"), "msg: {msg}");
6763 assert!(msg.contains("xyz-module.nix"), "msg: {msg}");
6764 }
6765
6766 #[test]
6767 fn error_assertion_failed_includes_file_context() {
6768 let p = std::path::PathBuf::from("/nix/store/test-assert.nix");
6769 let _g = push_eval_file(p);
6770 let result = eval("assert false; 1");
6771 let msg = format!("{}", result.unwrap_err());
6772 assert!(msg.contains("assertion failed"), "msg: {msg}");
6773 assert!(msg.contains("test-assert.nix"), "msg: {msg}");
6774 }
6775
6776 #[test]
6777 fn error_missing_argument_includes_file_context() {
6778 let p = std::path::PathBuf::from("/nix/store/func.nix");
6779 let _g = push_eval_file(p);
6780 let result = eval("({ a, b }: a) { a = 1; }");
6781 let msg = format!("{}", result.unwrap_err());
6782 assert!(msg.contains("missing argument"), "msg: {msg}");
6783 assert!(msg.contains("func.nix"), "msg: {msg}");
6784 }
6785
6786 #[test]
6787 fn error_cannot_call_includes_file_context() {
6788 let p = std::path::PathBuf::from("/nix/store/call.nix");
6789 let _g = push_eval_file(p);
6790 let result = eval("42 99");
6791 let msg = format!("{}", result.unwrap_err());
6792 assert!(msg.contains("cannot call"), "msg: {msg}");
6793 assert!(msg.contains("call.nix"), "msg: {msg}");
6794 }
6795
6796 #[test]
6797 fn error_without_file_has_no_in_prefix() {
6798 let result = eval("nonexistent_xyz");
6801 let msg = format!("{}", result.unwrap_err());
6802 assert!(msg.contains("undefined variable"), "msg: {msg}");
6803 assert!(!msg.contains(", in"), "msg should not contain file context: {msg}");
6804 }
6805
6806 #[test]
6809 fn pure_mode_set_get_independence() {
6810 let was = is_pure_mode();
6811 set_pure_mode(true);
6812 assert!(is_pure_mode());
6813 set_pure_mode(false);
6814 assert!(!is_pure_mode());
6815 set_pure_mode(was);
6816 }
6817
6818 #[test]
6821 fn eval_with_file_some_path_arithmetic() {
6822 let p = std::path::PathBuf::from("/tmp/imaginary.nix");
6823 let result = eval_with_file("1 + 2", Some(p)).unwrap();
6824 assert_eq!(result, Value::Int(3));
6825 }
6826
6827 #[test]
6835 fn unsafe_get_attr_pos_reports_file_and_offset_column() {
6836 let dir = tempfile::tempdir().unwrap();
6842 let file_body = "{ a = 1;\n b = 2; }\n";
6844 let f = dir.path().join("lit.nix");
6845 std::fs::write(&f, file_body).unwrap();
6846 let src = format!("builtins.unsafeGetAttrPos \"b\" (import {})", f.display());
6847 let v = eval(&src).unwrap();
6848 let attrs = match v { Value::Attrs(a) => a, other => panic!("expected attrs, got {other:?}") };
6849 assert_eq!(
6850 attrs.get("file").unwrap().as_string().unwrap(),
6851 f.to_string_lossy(),
6852 );
6853 assert_eq!(*attrs.get("line").unwrap(), Value::Int(1));
6854 let expected_col = (file_body.find("b = 2").unwrap() as i64) + 1;
6856 let col = match attrs.get("column").unwrap() { Value::Int(n) => *n, o => panic!("{o:?}") };
6857 assert_eq!(col, expected_col, "column must be the 1-based byte offset");
6858 }
6859
6860 #[test]
6861 fn unsafe_get_attr_pos_null_for_string_origin() {
6862 let v = eval("builtins.unsafeGetAttrPos \"a\" { a = 1; }").unwrap();
6864 assert_eq!(v, Value::Null);
6865 }
6866
6867 #[test]
6868 fn unsafe_get_attr_pos_null_for_missing_key() {
6869 let dir = tempfile::tempdir().unwrap();
6871 let f = dir.path().join("lit.nix");
6872 std::fs::write(&f, "{ a = 1; }\n").unwrap();
6873 let src = format!("builtins.unsafeGetAttrPos \"zzz\" (import {})", f.display());
6874 let v = eval(&src).unwrap();
6875 assert_eq!(v, Value::Null);
6876 }
6877
6878 #[test]
6881 fn interp_int_into_string() {
6882 assert_eq!(ev(r#""val=${toString 42}""#), Value::string("val=42"));
6884 }
6885
6886 #[test]
6887 fn interp_bool_true_becomes_one() {
6888 let v = ev(r#"let x = true; in "${builtins.toString x}""#);
6890 assert_eq!(v, Value::string("1"));
6891 }
6892
6893 #[test]
6894 fn interp_null_becomes_empty() {
6895 let v = ev(r#"let x = null; in "${builtins.toString x}""#);
6897 assert_eq!(v, Value::string(""));
6898 }
6899
6900 #[test]
6901 fn interp_attrset_without_to_string_errors() {
6902 let result = eval(r#"let s = { x = 1; }; in "${s}""#);
6904 assert!(result.is_err());
6905 }
6906
6907 #[test]
6908 fn interp_attrset_with_to_string_protocol() {
6909 let v = ev(r#""${{ __toString = self: "ok"; }}""#);
6911 assert_eq!(v, Value::string("ok"));
6912 }
6913
6914 #[test]
6917 fn eval_path_absolute_literal() {
6918 let v = ev("/tmp/foo");
6919 match v {
6920 Value::Path(p) => assert!(p.contains("/tmp/foo")),
6921 _ => panic!("expected Path"),
6922 }
6923 }
6924
6925 #[test]
6926 fn eval_path_home_literal() {
6927 let v = ev("~/foo.nix");
6928 match v {
6929 Value::Path(p) => assert!(p.contains("~/foo.nix") || p.ends_with("foo.nix")),
6930 _ => panic!("expected Path"),
6931 }
6932 }
6933
6934 #[test]
6937 fn path_search_unmatched_errors() {
6938 let saved = std::env::var("NIX_PATH").ok();
6941 unsafe {
6945 std::env::remove_var("NIX_PATH");
6946 }
6947 let result = eval("<this_should_not_resolve>");
6948 if let Some(v) = saved {
6949 unsafe {
6950 std::env::set_var("NIX_PATH", v);
6951 }
6952 }
6953 assert!(result.is_err());
6954 }
6955
6956 #[test]
6959 fn unary_negate_int() {
6960 assert_eq!(ev("-7"), Value::Int(-7));
6961 }
6962
6963 #[test]
6964 fn unary_negate_float() {
6965 assert_eq!(ev("-2.5"), Value::Float(-2.5));
6966 }
6967
6968 #[test]
6969 fn unary_invert_true() {
6970 assert_eq!(ev("!true"), Value::Bool(false));
6971 }
6972
6973 #[test]
6974 fn unary_invert_false() {
6975 assert_eq!(ev("!false"), Value::Bool(true));
6976 }
6977
6978 #[test]
6979 fn unary_negate_bool_errors() {
6980 let result = eval("-true");
6981 assert!(result.is_err());
6982 }
6983
6984 #[test]
6985 fn unary_invert_int_errors() {
6986 let result = eval("!42");
6987 assert!(result.is_err());
6988 }
6989
6990 #[test]
6993 fn binop_add_attrs_errors() {
6994 let result = eval("{a=1;} + {b=2;}");
6995 assert!(result.is_err());
6996 }
6997
6998 #[test]
6999 fn binop_sub_string_errors() {
7000 let result = eval(r#""a" - "b""#);
7001 assert!(result.is_err());
7002 }
7003
7004 #[test]
7005 fn binop_mul_string_errors() {
7006 let result = eval(r#""a" * "b""#);
7007 assert!(result.is_err());
7008 }
7009
7010 #[test]
7011 fn binop_div_string_errors() {
7012 let result = eval(r#""a" / "b""#);
7013 assert!(result.is_err());
7014 }
7015
7016 #[test]
7017 fn binop_compare_attrs_errors() {
7018 let result = eval("{a=1;} < {b=2;}");
7019 assert!(result.is_err());
7020 }
7021
7022 #[test]
7023 fn binop_div_float_by_zero_int() {
7024 let result = eval("1.0 / 0");
7028 let _ = result;
7031 }
7032
7033 #[test]
7034 fn binop_int_div_zero_is_division_by_zero() {
7035 let result = eval("5 / 0");
7036 match result {
7037 Err(EvalError::DivisionByZero) => {}
7038 other => panic!("expected DivisionByZero, got {other:?}"),
7039 }
7040 }
7041
7042 #[test]
7045 fn if_else_only_chosen_branch_evaluated_then() {
7046 assert_eq!(ev("if true then 42 else 1 / 0"), Value::Int(42));
7049 }
7050
7051 #[test]
7052 fn if_else_only_chosen_branch_evaluated_else() {
7053 assert_eq!(ev("if false then 1 / 0 else 99"), Value::Int(99));
7054 }
7055
7056 #[test]
7057 fn if_condition_must_be_bool() {
7058 let result = eval("if 1 then 1 else 2");
7059 assert!(result.is_err());
7060 }
7061
7062 #[test]
7063 fn if_condition_lazy_does_not_force_unused() {
7064 assert_eq!(
7067 ev("let bad = 1 / 0; in if true then 42 else bad"),
7068 Value::Int(42),
7069 );
7070 }
7071
7072 #[test]
7075 fn and_short_circuits_on_false() {
7076 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
7078 }
7079
7080 #[test]
7081 fn or_short_circuits_on_true() {
7082 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
7083 }
7084
7085 #[test]
7086 fn implication_short_circuits_on_false_lhs() {
7087 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
7089 }
7090
7091 #[test]
7094 fn lambda_fix_combinator_returns_attrset() {
7095 let v = ev(
7097 "let fix = f: let x = f x; in x; in
7098 (fix (self: { val = 1; double = self.val * 2; })).double",
7099 );
7100 assert_eq!(v, Value::Int(2));
7101 }
7102
7103 #[test]
7106 fn rec_attrset_self_reference() {
7107 let v = ev("(rec { a = b; b = 1; }).a");
7109 assert_eq!(v, Value::Int(1));
7110 }
7111
7112 #[test]
7113 fn rec_attrset_inherit_from_uses_outer_scope() {
7114 let v = ev(
7118 "let src = { a = 10; }; in
7119 rec {
7120 inherit (src) a;
7121 b = a + 1;
7122 }",
7123 );
7124 if let Value::Attrs(attrs) = v {
7125 let b = attrs.get("b").unwrap();
7126 let b_forced = force_value(b).unwrap();
7127 assert_eq!(b_forced, Value::Int(11));
7128 } else {
7129 panic!("expected attrs");
7130 }
7131 }
7132
7133 #[test]
7134 fn nonrec_attrset_no_self_reference() {
7135 let result = eval("({ a = 1; b = a + 1; }).b");
7138 assert!(result.is_err());
7139 }
7140
7141 #[test]
7144 fn dotted_binding_three_segments_then_sibling() {
7145 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
7146 if let Value::Attrs(attrs) = v {
7147 let a = attrs.get("a").unwrap();
7148 let a_forced = force_value(a).unwrap();
7149 if let Value::Attrs(a_attrs) = a_forced {
7150 let b = a_attrs.get("b").unwrap();
7151 let b_forced = force_value(b).unwrap();
7152 if let Value::Attrs(b_attrs) = b_forced {
7153 assert_eq!(force_value(b_attrs.get("c").unwrap()).unwrap(), Value::Int(1));
7154 assert_eq!(force_value(b_attrs.get("d").unwrap()).unwrap(), Value::Int(2));
7155 } else {
7156 panic!("expected b to be attrs");
7157 }
7158 assert_eq!(force_value(a_attrs.get("e").unwrap()).unwrap(), Value::Int(3));
7159 } else {
7160 panic!("expected a to be attrs");
7161 }
7162 } else {
7163 panic!("expected outer attrs");
7164 }
7165 }
7166
7167 #[test]
7170 fn rec_dotted_bindings_visible_to_siblings() {
7171 let v = ev("rec { types.openSB = 1; types.openCpu = 2; foo = types.openSB; }.foo");
7174 assert_eq!(v, Value::Int(1));
7175 }
7176
7177 #[test]
7178 fn rec_dotted_leaf_uses_rec_scope() {
7179 let v = ev("rec { types.a = f 1; f = x: x + 1; }.types.a");
7182 assert_eq!(v, Value::Int(2));
7183 }
7184
7185 #[test]
7186 fn rec_dotted_multiple_keys_merge() {
7187 let v = ev("rec { types.a = 1; types.b = 2; x = types; }.x");
7189 if let Value::Attrs(attrs) = v {
7190 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
7191 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
7192 } else {
7193 panic!("expected attrs");
7194 }
7195 }
7196
7197 #[test]
7198 fn rec_nixpkgs_parse_pattern() {
7199 let v = ev(r#"
7203 let
7204 mkOptionType = x: x;
7205 mergeOneOption = "merge";
7206 attrValues = builtins.attrValues;
7207 setType = name: value: { __type = name; } // value;
7208 mapAttrs = builtins.mapAttrs;
7209 enum = xs: mkOptionType { name = "enum"; check = x: builtins.elem x xs; };
7210 setTypes = type: mapAttrs (name: value: setType type.name ({ inherit name; } // value));
7211 in
7212 rec {
7213 types.openSB = mkOptionType { name = "sb"; merge = mergeOneOption; };
7214 types.significantByte = enum (attrValues significantBytes);
7215 significantBytes = setTypes types.openSB { bigEndian = {}; littleEndian = {}; };
7216 types.openCpuType = mkOptionType { name = "cpu-type"; };
7217 types.cpuType = enum (attrValues cpuTypes);
7218 cpuTypes = setTypes types.openCpuType { arm = { bits = 32; }; };
7219 }.types.openCpuType
7220 "#);
7221 if let Value::Attrs(attrs) = v {
7222 assert_eq!(
7223 force_value(attrs.get("name").unwrap()).unwrap(),
7224 Value::string("cpu-type")
7225 );
7226 } else {
7227 panic!("expected attrs");
7228 }
7229 }
7230
7231 #[test]
7232 fn let_dotted_leaf_uses_let_scope() {
7233 let v = ev("let a.x = f 1; f = x: x + 1; in a.x");
7235 assert_eq!(v, Value::Int(2));
7236 }
7237
7238 #[test]
7239 fn let_inherit_from_plus_dotted_overrides() {
7240 let v = ev(r#"
7246 let
7247 src = { types = { existing = true; }; };
7248 inherit (src) types;
7249 types.added = true;
7250 in types
7251 "#);
7252 if let Value::Attrs(attrs) = v {
7253 assert_eq!(
7255 force_value(attrs.get("added").unwrap()).unwrap(),
7256 Value::Bool(true)
7257 );
7258 assert!(attrs.get("existing").is_none());
7260 } else {
7261 panic!("expected attrs");
7262 }
7263 }
7264
7265 #[test]
7268 fn pattern_empty_no_args_no_ellipsis() {
7269 assert_eq!(ev("({}: 1) {}"), Value::Int(1));
7271 }
7272
7273 #[test]
7274 fn pattern_empty_with_ellipsis_accepts_extra() {
7275 assert_eq!(ev("({...}: 1) { a = 1; b = 2; }"), Value::Int(1));
7276 }
7277
7278 #[test]
7279 fn pattern_all_defaults() {
7280 assert_eq!(
7281 ev("({a ? 1, b ? 2}: a + b) {}"),
7282 Value::Int(3),
7283 );
7284 }
7285
7286 #[test]
7287 fn pattern_at_bind_before() {
7288 assert_eq!(ev("(args @ { x }: args.x) { x = 7; }"), Value::Int(7));
7290 }
7291
7292 #[test]
7293 fn pattern_at_bind_after() {
7294 assert_eq!(ev("({ x } @ args: args.x) { x = 7; }"), Value::Int(7));
7296 }
7297
7298 #[test]
7299 fn pattern_default_references_other_arg() {
7300 assert_eq!(ev("({a, b ? a + 1}: b) {a = 10;}"), Value::Int(11));
7302 }
7303
7304 #[test]
7305 fn pattern_required_missing_errors() {
7306 let result = eval("({ a, b }: a) { a = 1; }");
7307 assert!(result.is_err());
7308 }
7309
7310 #[test]
7311 fn pattern_unexpected_errors_without_ellipsis() {
7312 let result = eval("({ a }: a) { a = 1; b = 2; }");
7313 assert!(result.is_err());
7314 }
7315
7316 #[test]
7319 fn apply_int_errors() {
7320 let result = eval("42 5");
7321 assert!(result.is_err());
7322 }
7323
7324 #[test]
7325 fn apply_string_errors() {
7326 let result = eval(r#""hi" 5"#);
7327 assert!(result.is_err());
7328 }
7329
7330 #[test]
7331 fn apply_attrset_without_functor_errors() {
7332 let result = eval("{ x = 1; } 5");
7333 assert!(result.is_err());
7334 let msg = format!("{}", result.unwrap_err());
7335 assert!(msg.contains("__functor") || msg.contains("cannot call"));
7336 }
7337
7338 #[test]
7341 fn select_multi_segment_with_default() {
7342 assert_eq!(ev("{ a = { b = 1; }; }.a.c or 99"), Value::Int(99));
7344 }
7345
7346 #[test]
7347 fn select_from_int_errors() {
7348 let result = eval("(1).x");
7349 assert!(result.is_err());
7350 }
7351
7352 #[test]
7355 fn has_attr_on_non_set_returns_false() {
7356 assert_eq!(ev("1 ? x"), Value::Bool(false));
7358 }
7359
7360 #[test]
7361 fn has_attr_nested_path_present() {
7362 assert_eq!(ev("{ a = { b = 1; }; } ? a.b"), Value::Bool(true));
7363 }
7364
7365 #[test]
7366 fn has_attr_nested_path_missing() {
7367 assert_eq!(ev("{ a = { b = 1; }; } ? a.c"), Value::Bool(false));
7368 }
7369
7370 #[test]
7371 fn has_attr_intermediate_missing_returns_false() {
7372 assert_eq!(ev("{} ? a.b.c"), Value::Bool(false));
7373 }
7374
7375 #[test]
7378 fn list_with_function_value() {
7379 let v = ev("[(x: x + 1)]");
7380 if let Value::List(items) = v {
7381 assert_eq!(items.len(), 1);
7382 let forced = force_value(&items[0]).unwrap();
7384 assert!(matches!(forced, Value::Lambda(_)));
7385 } else {
7386 panic!("expected list");
7387 }
7388 }
7389
7390 #[test]
7393 fn inherit_unknown_name_errors() {
7394 let result = eval("let x = 1; in let inherit nonexistent; in nonexistent");
7395 assert!(result.is_err());
7396 }
7397
7398 #[test]
7401 fn string_concat_no_context_when_both_plain() {
7402 let v = ev(r#""abc" + "def""#);
7403 if let Value::String(ns) = v {
7404 assert_eq!(ns.chars, "abcdef");
7405 assert!(!ns.has_context());
7406 } else {
7407 panic!("expected string");
7408 }
7409 }
7410
7411 #[test]
7414 fn parens_around_expression() {
7415 assert_eq!(ev("(1 + 2)"), Value::Int(3));
7416 }
7417
7418 #[test]
7419 fn nested_parens() {
7420 assert_eq!(ev("(((42)))"), Value::Int(42));
7421 }
7422
7423 #[test]
7426 fn throw_propagates_as_error() {
7427 let result = eval(r#"builtins.throw "kaboom""#);
7428 match result {
7429 Err(EvalError::Throw(s)) => assert!(s.contains("kaboom")),
7430 other => panic!("expected Throw, got {other:?}"),
7431 }
7432 }
7433
7434 #[test]
7435 fn assert_failed_propagates_as_error() {
7436 let result = eval("assert false; 1");
7437 match result {
7438 Err(EvalError::AssertionFailed(_)) => {}
7439 other => panic!("expected AssertionFailed, got {other:?}"),
7440 }
7441 }
7442
7443 #[test]
7446 fn string_no_interp_yields_no_context() {
7447 let v = ev(r#""just literal""#);
7448 if let Value::String(ns) = v {
7449 assert!(!ns.has_context());
7450 } else {
7451 panic!("expected string");
7452 }
7453 }
7454
7455 #[test]
7464 fn interp_path_copies_to_store_byte_matches_cppnix() {
7465 let dir = std::env::temp_dir().join(format!("sui-r5-interp-{}", std::process::id()));
7466 let _ = std::fs::remove_dir_all(&dir);
7467 std::fs::create_dir_all(&dir).unwrap();
7468 let f = dir.join("data.txt");
7469 std::fs::write(&f, b"hello\n").unwrap();
7470 let expr = format!(r#""${{{}}}""#, f.display());
7471 let v = eval(&expr).unwrap();
7472 if let Value::String(ns) = v {
7473 assert_eq!(
7474 ns.chars.to_string(),
7475 "/nix/store/y9dmvfhip31hg8ia4njwjz9vfa3ndphr-data.txt",
7476 );
7477 assert!(ns.has_context());
7478 } else {
7479 panic!("expected string");
7480 }
7481 let _ = std::fs::remove_dir_all(&dir);
7482 }
7483
7484 #[test]
7493 fn parse_error_unbalanced_braces() {
7494 let result = eval("{ a = 1");
7495 assert!(result.is_err());
7496 let err = result.unwrap_err();
7497 assert!(matches!(err, EvalError::ParseError(_)));
7498 }
7499
7500 #[test]
7501 fn parse_error_dangling_let() {
7502 let result = eval("let in");
7503 assert!(result.is_err());
7504 }
7505
7506 #[test]
7507 fn parse_error_empty_input() {
7508 let result = eval("");
7509 assert!(result.is_err());
7510 }
7511
7512 #[test]
7515 fn float_int_subtraction() {
7516 assert_eq!(ev("3.5 - 1"), Value::Float(2.5));
7517 }
7518
7519 #[test]
7520 fn int_float_subtraction() {
7521 assert_eq!(ev("3 - 0.5"), Value::Float(2.5));
7522 }
7523
7524 #[test]
7525 fn float_float_division() {
7526 assert_eq!(ev("6.0 / 2.0"), Value::Float(3.0));
7527 }
7528
7529 #[test]
7530 fn int_float_multiplication() {
7531 assert_eq!(ev("3 * 2.5"), Value::Float(7.5));
7532 }
7533
7534 #[test]
7537 fn compare_int_float_less() {
7538 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
7539 }
7540
7541 #[test]
7542 fn compare_float_int_more() {
7543 assert_eq!(ev("3.5 > 3"), Value::Bool(true));
7544 }
7545
7546 #[test]
7547 fn compare_equal_int_float() {
7548 assert_eq!(ev("3 <= 3.0"), Value::Bool(true));
7549 }
7550
7551 #[test]
7554 fn equal_lists_same() {
7555 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
7556 }
7557
7558 #[test]
7559 fn equal_lists_diff_length() {
7560 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
7561 }
7562
7563 #[test]
7564 fn not_equal_lists() {
7565 assert_eq!(ev("[1] != [2]"), Value::Bool(true));
7566 }
7567
7568 #[test]
7569 fn equal_attrsets_same() {
7570 assert_eq!(ev("{a = 1; b = 2;} == {b = 2; a = 1;}"), Value::Bool(true));
7571 }
7572
7573 #[test]
7580 fn lambda_self_equality_in_attrset() {
7581 assert_eq!(
7583 ev("let f = x: x; in { a = 1; inherit f; } == { a = 1; inherit f; }"),
7584 Value::Bool(true),
7585 );
7586 }
7587
7588 #[test]
7589 fn lambda_self_reference_attrset_equality() {
7590 assert_eq!(
7592 ev("let x = { a = 1; f = y: y; }; in x == x"),
7593 Value::Bool(true),
7594 );
7595 }
7596
7597 #[test]
7598 fn lambda_different_closures_not_equal() {
7599 assert_eq!(
7601 ev("{ f = x: x; } == { f = x: x; }"),
7602 Value::Bool(false),
7603 );
7604 }
7605
7606 #[test]
7607 fn lambda_ne_does_not_force_unused_branch() {
7608 assert_eq!(
7611 ev("let ls = { a = 1; f = x: x; }; in if ls != ls then builtins.throw \"bug\" else 42"),
7612 Value::Int(42),
7613 );
7614 }
7615
7616 #[test]
7619 fn force_value_through_thunk() {
7620 let root = rnix::Root::parse("1 + 2");
7621 let expr = root.tree().expr().unwrap();
7622 let thunk = Thunk::new_suspended(expr, Env::new());
7623 let val = Value::Thunk(thunk);
7624 assert_eq!(force_value(&val).unwrap(), Value::Int(3));
7625 }
7626
7627 #[test]
7630 fn try_eval_catches_thrown_error() {
7631 let v = ev(r#"(builtins.tryEval (builtins.throw "oops")).success"#);
7633 assert_eq!(v, Value::Bool(false));
7634 }
7635
7636 #[test]
7637 fn try_eval_returns_value_on_success() {
7638 let v = ev("(builtins.tryEval 42).value");
7639 assert_eq!(v, Value::Int(42));
7640 }
7641
7642 #[test]
7645 fn legacy_let_returns_body_attr() {
7646 assert_eq!(ev("let { x = 1; body = x + 41; }"), Value::Int(42));
7650 }
7651
7652 #[test]
7653 fn legacy_let_missing_body_errors() {
7654 let result = eval("let { x = 1; }");
7655 assert!(result.is_err());
7656 }
7657
7658 #[test]
7659 fn legacy_let_with_inherit_from_scope() {
7660 assert_eq!(
7661 ev("let outer = 5; in let { inherit outer; body = outer * 2; }"),
7662 Value::Int(10),
7663 );
7664 }
7665
7666 #[test]
7669 fn interp_with_string_concat_preserves_order() {
7670 assert_eq!(
7671 ev(r#"let a = "x"; b = "y"; in "${a}-${b}""#),
7672 Value::string("x-y"),
7673 );
7674 }
7675
7676 #[test]
7677 fn interp_only_literal_part() {
7678 assert_eq!(ev(r#""no interp here""#), Value::string("no interp here"));
7679 }
7680
7681 #[test]
7684 fn dynamic_attr_via_string_key_in_set() {
7685 assert_eq!(ev(r#"{ "a" = 1; }.a"#), Value::Int(1));
7687 }
7688
7689 #[test]
7690 fn dynamic_attr_via_interpolated_key() {
7691 let v = ev(r#"let k = "foo"; in { ${k} = 99; }.foo"#);
7692 assert_eq!(v, Value::Int(99));
7693 }
7694
7695 #[test]
7698 fn select_with_string_key() {
7699 let v = ev(r#"{ a = 42; }."a""#);
7700 assert_eq!(v, Value::Int(42));
7701 }
7702
7703 #[test]
7706 fn apply_attrset_with_functor_works() {
7707 let v = ev("let s = { __functor = self: x: x + 1; }; in s 5");
7708 assert_eq!(v, Value::Int(6));
7709 }
7710
7711 #[test]
7714 fn double_negate_int() {
7715 assert_eq!(ev("- (-5)"), Value::Int(5));
7716 }
7717
7718 #[test]
7721 fn inherit_in_let_makes_name_available() {
7722 assert_eq!(
7723 ev("let src = { a = 7; }; in let inherit (src) a; in a"),
7724 Value::Int(7),
7725 );
7726 }
7727
7728 #[test]
7731 fn path_plus_string_yields_path() {
7732 let v = ev(r#"/foo + "/bar""#);
7733 match v {
7734 Value::Path(p) => assert_eq!(&*p, "/foo/bar"),
7735 _ => panic!("expected path"),
7736 }
7737 }
7738
7739 #[test]
7742 fn attrset_value_not_forced_unless_selected() {
7743 assert_eq!(
7746 ev(r#"{ bad = builtins.throw "boom"; good = 42; }.good"#),
7747 Value::Int(42),
7748 );
7749 }
7750
7751 #[test]
7754 fn lambda_recursive_via_let() {
7755 assert_eq!(
7757 ev("let fact = n: if n == 0 then 1 else n * fact (n - 1); in fact 5"),
7758 Value::Int(120),
7759 );
7760 }
7761
7762 #[test]
7765 fn select_with_dynamic_key_via_var() {
7766 assert_eq!(ev(r#"let k = { x = 1; }; in k.x"#), Value::Int(1));
7769 }
7770
7771 #[test]
7774 fn compare_string_lex_greater_or_equal() {
7775 assert_eq!(ev(r#""b" >= "a""#), Value::Bool(true));
7776 assert_eq!(ev(r#""a" >= "a""#), Value::Bool(true));
7777 assert_eq!(ev(r#""a" >= "b""#), Value::Bool(false));
7778 }
7779
7780 #[test]
7783 fn equal_int_string_false() {
7784 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
7785 }
7786
7787 #[test]
7788 fn equal_null_int_false() {
7789 assert_eq!(ev("null == 0"), Value::Bool(false));
7790 }
7791
7792 #[test]
7795 fn update_with_let_bound_operands() {
7796 assert_eq!(
7797 ev("let a = { x = 1; }; b = { y = 2; }; in (a // b).y"),
7798 Value::Int(2),
7799 );
7800 }
7801
7802 #[test]
7805 fn concat_lists_from_let() {
7806 assert_eq!(
7807 ev("let a = [1 2]; b = [3 4]; in builtins.length (a ++ b)"),
7808 Value::Int(4),
7809 );
7810 }
7811
7812 #[test]
7815 fn interp_list_coerces_with_spaces() {
7816 assert_eq!(
7819 ev(r#""${toString [1 2 3]}""#),
7820 Value::string("1 2 3"),
7821 );
7822 }
7823
7824 #[test]
7825 fn interp_list_directly_coerces() {
7826 assert_eq!(
7828 ev(r#""${[1 2]}""#),
7829 Value::string("1 2"),
7830 );
7831 }
7832
7833 #[test]
7836 fn interp_outpath_attrset() {
7837 assert_eq!(
7838 ev(r#"let x = { outPath = "/nix/store/abc"; }; in "${x}""#),
7839 Value::string("/nix/store/abc"),
7840 );
7841 }
7842
7843 #[test]
7844 fn interp_tostring_takes_priority_over_outpath() {
7845 assert_eq!(
7846 ev(r#"let x = { __toString = self: "custom"; outPath = "/ignored"; }; in "${x}""#),
7847 Value::string("custom"),
7848 );
7849 }
7850
7851 #[test]
7852 fn interp_derivation_coerces_to_outpath() {
7853 let result = eval(r#"
7855 let drv = builtins.derivation {
7856 name = "test";
7857 system = "x86_64-linux";
7858 builder = "/bin/sh";
7859 };
7860 in "${drv}"
7861 "#).unwrap();
7862 if let Value::String(s) = result {
7863 assert!(s.chars.starts_with("/nix/store/"), "got: {}", s.chars);
7864 } else {
7865 panic!("expected string");
7866 }
7867 }
7868
7869 #[test]
7872 fn interp_lambda_errors() {
7873 let result = eval(r#""${x: x}""#);
7874 assert!(result.is_err());
7875 }
7876
7877 #[test]
7880 fn force_value_int_returns_same() {
7881 let v = Value::Int(42);
7882 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
7883 }
7884
7885 #[test]
7886 fn force_value_bool_returns_same() {
7887 let v = Value::Bool(true);
7888 assert_eq!(force_value(&v).unwrap(), Value::Bool(true));
7889 }
7890
7891 #[test]
7892 fn force_value_string_returns_same() {
7893 let v = Value::string("hello");
7894 assert_eq!(force_value(&v).unwrap(), Value::string("hello"));
7895 }
7896
7897 #[test]
7898 fn force_value_attrs_returns_same() {
7899 let mut a = NixAttrs::new();
7900 a.insert("x".to_string(), Value::Int(1));
7901 let v = Value::Attrs(Rc::new(a.clone()));
7902 assert_eq!(force_value(&v).unwrap(), Value::Attrs(Rc::new(a)));
7903 }
7904
7905 #[test]
7906 fn force_value_list_returns_same() {
7907 let v = Value::list(vec![Value::Int(1), Value::Int(2)]);
7908 assert_eq!(
7909 force_value(&v).unwrap(),
7910 Value::list(vec![Value::Int(1), Value::Int(2)]),
7911 );
7912 }
7913
7914 #[test]
7915 fn force_value_null_returns_null() {
7916 let v = Value::Null;
7917 assert_eq!(force_value(&v).unwrap(), Value::Null);
7918 }
7919
7920 #[test]
7921 fn force_value_evaluated_thunk_returns_cached() {
7922 let v = ev("let x = 1 + 2; in x");
7924 assert_eq!(v, Value::Int(3));
7925 assert_eq!(force_value(&v).unwrap(), Value::Int(3));
7927 }
7928
7929 #[test]
7932 fn tco_if_true_condition() {
7933 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
7934 }
7935
7936 #[test]
7937 fn tco_if_false_condition() {
7938 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
7939 }
7940
7941 #[test]
7942 fn tco_deeply_nested_if_else_chain() {
7943 let mut expr = String::from("150");
7946 for i in (1..150).rev() {
7947 expr = format!("if false then {} else {}", i, expr);
7948 }
7949 let v = ev(&expr);
7950 assert_eq!(v, Value::Int(150));
7951 }
7952
7953 #[test]
7954 fn tco_assert_true_passes_through() {
7955 assert_eq!(ev("assert true; 42"), Value::Int(42));
7956 }
7957
7958 #[test]
7959 fn tco_assert_false_throws_assertion_failed() {
7960 let result = eval("assert false; 42");
7961 assert!(result.is_err());
7962 let err = result.unwrap_err();
7963 assert!(
7964 matches!(err, EvalError::AssertionFailed(_)),
7965 "expected AssertionFailed, got: {err}",
7966 );
7967 }
7968
7969 #[test]
7970 fn tco_with_makes_scope_available() {
7971 assert_eq!(ev("with { x = 10; y = 20; }; x + y"), Value::Int(30));
7972 }
7973
7974 #[test]
7975 fn tco_let_in_creates_bindings() {
7976 assert_eq!(ev("let a = 5; in a"), Value::Int(5));
7977 }
7978
7979 #[test]
7980 fn tco_let_in_multiple_bindings() {
7981 assert_eq!(ev("let a = 1; b = 2; c = 3; in a + b + c"), Value::Int(6));
7982 }
7983
7984 #[test]
7987 fn eval_attrset_empty() {
7988 let v = ev("{}");
7989 if let Value::Attrs(attrs) = v {
7990 assert!(attrs.is_empty(), "expected empty attrset");
7991 } else {
7992 panic!("expected attrset, got {v:?}");
7993 }
7994 }
7995
7996 #[test]
7997 fn eval_attrset_simple_kv() {
7998 let v = ev("{ a = 1; b = 2; }");
7999 if let Value::Attrs(attrs) = v {
8000 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8001 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8002 } else {
8003 panic!("expected attrset, got {v:?}");
8004 }
8005 }
8006
8007 #[test]
8008 fn eval_attrset_recursive() {
8009 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
8010 assert_eq!(ev("(rec { a = 1; b = a + 1; }).a"), Value::Int(1));
8011 }
8012
8013 #[test]
8014 fn eval_attrset_inherit_from_scope() {
8015 assert_eq!(ev("let x = 1; in { inherit x; }.x"), Value::Int(1));
8016 }
8017
8018 #[test]
8019 fn eval_attrset_inherit_from_expr() {
8020 assert_eq!(
8021 ev("{ inherit (builtins) true; }.true"),
8022 Value::Bool(true),
8023 );
8024 }
8025
8026 #[test]
8027 fn eval_attrset_dotted_path() {
8028 assert_eq!(ev("{ a.b.c = 1; }.a.b.c"), Value::Int(1));
8029 }
8030
8031 #[test]
8032 fn eval_attrset_update_merge() {
8033 let v = ev("{ a = 1; } // { b = 2; }");
8034 if let Value::Attrs(attrs) = v {
8035 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8036 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8037 } else {
8038 panic!("expected attrset, got {v:?}");
8039 }
8040 }
8041
8042 #[test]
8045 fn eval_apply_simple_function() {
8046 assert_eq!(ev("(x: x + 1) 2"), Value::Int(3));
8047 }
8048
8049 #[test]
8050 fn eval_apply_pattern_destructuring() {
8051 assert_eq!(ev("({a, b}: a + b) { a = 1; b = 2; }"), Value::Int(3));
8052 }
8053
8054 #[test]
8055 fn eval_apply_default_arguments() {
8056 assert_eq!(ev("({a, b ? 0}: a + b) { a = 1; }"), Value::Int(1));
8057 }
8058
8059 #[test]
8060 fn eval_apply_ellipsis() {
8061 assert_eq!(ev("({a, ...}: a) { a = 1; b = 2; }"), Value::Int(1));
8062 }
8063
8064 #[test]
8067 fn eval_select_single_key() {
8068 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
8069 }
8070
8071 #[test]
8072 fn eval_select_multi_level() {
8073 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
8074 }
8075
8076 #[test]
8077 fn eval_select_with_or_default() {
8078 assert_eq!(ev("{}.a or 42"), Value::Int(42));
8079 }
8080
8081 #[test]
8082 fn eval_select_missing_key_without_default_throws() {
8083 let result = eval("{}.a");
8084 assert!(result.is_err());
8085 }
8086
8087 #[test]
8090 fn binop_add_ints() {
8091 assert_eq!(ev("1 + 2"), Value::Int(3));
8092 }
8093
8094 #[test]
8095 fn binop_sub_ints() {
8096 assert_eq!(ev("3 - 1"), Value::Int(2));
8097 }
8098
8099 #[test]
8100 fn binop_mul_ints() {
8101 assert_eq!(ev("2 * 3"), Value::Int(6));
8102 }
8103
8104 #[test]
8105 fn binop_div_ints() {
8106 assert_eq!(ev("6 / 2"), Value::Int(3));
8107 }
8108
8109 #[test]
8110 fn binop_float_arithmetic() {
8111 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
8112 }
8113
8114 #[test]
8115 fn binop_string_concat() {
8116 assert_eq!(
8117 ev(r#""hello" + " " + "world""#),
8118 Value::string("hello world"),
8119 );
8120 }
8121
8122 #[test]
8123 fn binop_list_concat() {
8124 assert_eq!(
8125 ev("[1 2] ++ [3 4]"),
8126 Value::list(vec![
8127 Value::Int(1),
8128 Value::Int(2),
8129 Value::Int(3),
8130 Value::Int(4),
8131 ]),
8132 );
8133 }
8134
8135 #[test]
8136 fn binop_attrset_update() {
8137 let v = ev("{ a = 1; } // { b = 2; }");
8138 if let Value::Attrs(attrs) = v {
8139 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8140 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8141 } else {
8142 panic!("expected attrset, got {v:?}");
8143 }
8144 }
8145
8146 #[test]
8147 fn binop_less_than() {
8148 assert_eq!(ev("1 < 2"), Value::Bool(true));
8149 assert_eq!(ev("2 < 1"), Value::Bool(false));
8150 }
8151
8152 #[test]
8153 fn binop_greater_than() {
8154 assert_eq!(ev("2 > 1"), Value::Bool(true));
8155 assert_eq!(ev("1 > 2"), Value::Bool(false));
8156 }
8157
8158 #[test]
8159 fn binop_equal() {
8160 assert_eq!(ev("1 == 1"), Value::Bool(true));
8161 assert_eq!(ev("1 == 2"), Value::Bool(false));
8162 }
8163
8164 #[test]
8165 fn binop_not_equal() {
8166 assert_eq!(ev("1 != 2"), Value::Bool(true));
8167 assert_eq!(ev("1 != 1"), Value::Bool(false));
8168 }
8169
8170 #[test]
8171 fn binop_logical_and() {
8172 assert_eq!(ev("true && false"), Value::Bool(false));
8173 assert_eq!(ev("true && true"), Value::Bool(true));
8174 }
8175
8176 #[test]
8177 fn binop_logical_or() {
8178 assert_eq!(ev("true || false"), Value::Bool(true));
8179 assert_eq!(ev("false || false"), Value::Bool(false));
8180 }
8181
8182 #[test]
8183 fn binop_logical_not() {
8184 assert_eq!(ev("!true"), Value::Bool(false));
8185 assert_eq!(ev("!false"), Value::Bool(true));
8186 }
8187
8188 #[test]
8189 fn binop_implication() {
8190 assert_eq!(ev("false -> true"), Value::Bool(true));
8191 assert_eq!(ev("false -> false"), Value::Bool(true));
8192 assert_eq!(ev("true -> true"), Value::Bool(true));
8193 assert_eq!(ev("true -> false"), Value::Bool(false));
8194 }
8195}