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<Option<PathBuf>>> = const { RefCell::new(Vec::new()) };
49 static NIX_TRACE_STACK: RefCell<Vec<NixTraceFrame>> = const { RefCell::new(Vec::new()) };
53}
54
55#[derive(Debug, Clone)]
65pub enum NixTraceFrame {
66 Eager {
70 file: Option<String>,
71 description: String,
72 },
73 Lambda {
83 closure_env: Env,
84 current_file: Option<PathBuf>,
85 },
86}
87
88fn strip_source_prefix(p: &std::path::Path) -> String {
91 let s = p.display().to_string();
92 s.rsplit_once("-source/")
93 .map_or_else(|| p.display().to_string(), |(_, tail)| tail.to_string())
94}
95
96impl NixTraceFrame {
97 fn file(&self) -> Option<String> {
100 match self {
101 NixTraceFrame::Eager { file, .. } => file.clone(),
102 NixTraceFrame::Lambda { current_file, .. } => {
103 current_file.as_deref().map(strip_source_prefix)
104 }
105 }
106 }
107
108 fn description(&self) -> String {
113 self.to_string()
114 }
115}
116
117impl std::fmt::Display for NixTraceFrame {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 match self {
123 NixTraceFrame::Eager { description, .. } => f.write_str(description),
124 NixTraceFrame::Lambda { closure_env, .. } => {
125 let file = closure_env.eval_file().map(|p| strip_source_prefix(p));
126 write!(
127 f,
128 "while calling function defined in {}",
129 file.as_deref().unwrap_or("<eval>")
130 )
131 }
132 }
133 }
134}
135
136fn push_nix_trace(desc: impl Into<String>) -> NixTraceGuard {
138 let frame = NixTraceFrame::Eager {
139 file: current_eval_file().map(|p| {
140 p.display().to_string()
141 .rsplit_once("-source/")
142 .map_or_else(|| p.display().to_string(), |(_, s)| s.to_string())
143 }),
144 description: desc.into(),
145 };
146 NIX_TRACE_STACK.with(|s| s.borrow_mut().push(frame));
147 NixTraceGuard
148}
149
150fn push_nix_trace_lambda(closure_env: &Env) -> NixTraceGuard {
156 let frame = NixTraceFrame::Lambda {
157 closure_env: closure_env.clone(),
158 current_file: current_eval_file(),
159 };
160 NIX_TRACE_STACK.with(|s| s.borrow_mut().push(frame));
161 NixTraceGuard
162}
163
164struct NixTraceGuard;
165impl Drop for NixTraceGuard {
166 fn drop(&mut self) {
167 NIX_TRACE_STACK.with(|s| s.borrow_mut().pop());
168 }
169}
170
171pub fn attach_trace(err: EvalError) -> EvalError {
173 NIX_TRACE_STACK.with(|s| {
174 let stack = s.borrow();
175 if stack.is_empty() {
176 return err;
177 }
178 let max_frames = std::env::var("SUI_M26_MAXFRAMES").ok()
179 .and_then(|s| s.parse::<usize>().ok()).unwrap_or(15);
180 let mut trace = format!("{err}");
181 for (i, frame) in stack.iter().rev().take(max_frames).enumerate() {
182 let file = frame.file();
183 let loc = file.as_deref().unwrap_or("<eval>");
184 trace.push_str(&format!("\n {} ({loc})", frame.description()));
185 if i + 1 >= max_frames && stack.len() > max_frames {
186 trace.push_str(&format!("\n ... ({} more frames)", stack.len() - max_frames));
187 }
188 }
189 match err {
192 EvalError::Throw(_) => EvalError::Throw(trace),
193 EvalError::AssertionFailed(_) => EvalError::AssertionFailed(trace),
194 _ => EvalError::TypeError(trace),
195 }
196 })
197}
198
199#[must_use]
202pub fn current_eval_dir() -> Option<PathBuf> {
203 EVAL_FILE_STACK
204 .with(|s| s.borrow().last().cloned())
205 .flatten()
206 .and_then(|p| p.parent().map(PathBuf::from))
207}
208
209pub fn push_eval_file(file: PathBuf) -> EvalFileGuard {
213 push_eval_frame(Some(file))
214}
215
216pub fn push_eval_frame(file: Option<PathBuf>) -> EvalFileGuard {
221 EVAL_FILE_STACK.with(|s| s.borrow_mut().push(file));
222 EvalFileGuard
223}
224
225#[must_use]
228pub fn current_eval_file() -> Option<PathBuf> {
229 EVAL_FILE_STACK.with(|s| s.borrow().last().cloned()).flatten()
230}
231
232
233pub fn eval_file_stack_snapshot() -> Vec<String> {
235 EVAL_FILE_STACK.with(|s| {
236 s.borrow().iter().map(|p| {
237 let Some(p) = p else { return "<no-file>".to_string() };
238 let s = p.display().to_string();
239 s.rsplit_once("-source/").map_or(s.clone(), |(_, r)| r.to_string())
240 }).collect()
241 })
242}
243
244pub(crate) fn eval_file_ctx() -> String {
247 current_eval_file()
248 .map(|p| format!(", in '{}'", p.display()))
249 .unwrap_or_default()
250}
251
252pub struct EvalFileGuard;
254
255impl Drop for EvalFileGuard {
256 fn drop(&mut self) {
257 EVAL_FILE_STACK.with(|s| {
258 s.borrow_mut().pop();
259 });
260 }
261}
262
263pub fn push_source_id(id: u32) -> SourceIdGuard {
269 let prev = CURRENT_SOURCE_ID.with(|s| {
270 let old = s.get();
271 s.set(id);
272 old
273 });
274 SourceIdGuard(prev)
275}
276
277pub struct SourceIdGuard(u32);
279
280impl Drop for SourceIdGuard {
281 fn drop(&mut self) {
282 CURRENT_SOURCE_ID.with(|s| s.set(self.0));
283 }
284}
285
286pub fn normalize_path(path: &std::path::Path) -> std::path::PathBuf {
299 crate::path::normalize(path)
300}
301
302thread_local! {
310 static PURE_MODE: Cell<bool> = const { Cell::new(false) };
311}
312
313pub fn set_pure_mode(pure: bool) {
315 PURE_MODE.with(|p| p.set(pure));
316}
317
318#[must_use]
320pub fn is_pure_mode() -> bool {
321 PURE_MODE.with(Cell::get)
322}
323
324#[cfg(test)]
340const MAX_EVAL_DEPTH: usize = 2_048;
341#[cfg(not(test))]
342const MAX_EVAL_DEPTH: usize = usize::MAX;
343
344struct DepthGuard;
350
351const PROMOTION_RUNAWAY_EVAL_DEPTH: usize = 500;
368
369impl DepthGuard {
370 #[inline(always)]
371 fn enter() -> Result<Self, EvalError> {
372 EVAL_DEPTH.with(|d| {
373 let depth = d.get();
374 if MAX_EVAL_DEPTH != usize::MAX && depth > MAX_EVAL_DEPTH {
375 return Err(EvalError::InfiniteRecursion(
376 "eval depth exceeded".into(),
377 ));
378 }
379 if depth > PROMOTION_RUNAWAY_EVAL_DEPTH
380 && crate::value::promotion_occurred()
381 {
382 return Err(EvalError::InfiniteRecursion(
383 "overlay-fixpoint promotion runaway (eval depth exceeded)".into(),
384 ));
385 }
386 d.set(depth + 1);
387 Ok(DepthGuard)
388 })
389 }
390}
391
392impl Drop for DepthGuard {
393 #[inline(always)]
394 fn drop(&mut self) {
395 EVAL_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
396 }
397}
398
399fn collect_referenced_names(expr: &ast::Expr) -> HashSet<String> {
416 let mut names = HashSet::new();
417 for node in expr.syntax().descendants() {
418 if let Some(ident) = ast::Ident::cast(node) {
419 names.insert(ident_text(&ident));
420 }
421 }
422 names
423}
424
425fn compute_needed_bindings(
439 body: &ast::Expr,
440 binding_info: &[(String, Option<ast::Expr>)], ) -> HashSet<String> {
442 let body_refs = collect_referenced_names(body);
444
445 let mut all_names: HashSet<String> = HashSet::with_capacity(binding_info.len());
447 let mut deps: HashMap<String, HashSet<String>> = HashMap::with_capacity(binding_info.len());
448
449 for (name, value_expr) in binding_info {
450 all_names.insert(name.clone());
451 if let Some(expr) = value_expr {
452 deps.insert(name.clone(), collect_referenced_names(expr));
453 }
454 }
455
456 let mut needed: HashSet<String> = body_refs.intersection(&all_names).cloned().collect();
458 let mut queue: VecDeque<String> = needed.iter().cloned().collect();
459
460 while let Some(name) = queue.pop_front() {
461 if let Some(name_deps) = deps.get(&name) {
462 for dep in name_deps {
463 if all_names.contains(dep) && needed.insert(dep.clone()) {
464 queue.push_back(dep.clone());
465 }
466 }
467 }
468 }
469
470 needed
471}
472
473#[must_use = "evaluation result should be used"]
475pub fn eval(input: &str) -> Result<Value, EvalError> {
476 eval_with_file(input, None)
477}
478
479thread_local! {
481 static EVAL_NESTING: Cell<usize> = const { Cell::new(0) };
482}
483
484pub fn eval_with_file(input: &str, file: Option<std::path::PathBuf>) -> Result<Value, EvalError> {
491 let nesting = EVAL_NESTING.with(|n| {
492 let v = n.get();
493 n.set(v + 1);
494 v
495 });
496 if nesting == 0 {
497 crate::perf::init();
498 crate::perf::start();
499 crate::trace::init_trace();
500 clear_ident_cache();
503 crate::resolve_env::clear();
507 }
526 let parse = rnix::Root::parse(input);
527 if !parse.errors().is_empty() {
528 let msgs: Vec<String> = parse.errors().iter().map(|e| e.to_string()).collect();
529 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
530 return Err(EvalError::ParseError(msgs.join("; ")));
531 }
532
533 let src_id = next_source_id();
537 if crate::resolve_env::enabled() {
544 let table = sui_resolve::resolve(&parse.tree());
545 crate::resolve_env::populate(src_id, &table);
546 }
547 crate::pos::register_source(file.as_deref(), input);
553 let prev_src_id = CURRENT_SOURCE_ID.with(|s| {
554 let old = s.get();
555 s.set(src_id);
556 old
557 });
558
559 let root = parse.tree();
560 let expr = match root.expr() {
561 Some(e) => e,
562 None => {
563 CURRENT_SOURCE_ID.with(|s| s.set(prev_src_id));
564 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
565 return Err(EvalError::ParseError("empty expression".to_string()));
566 }
567 };
568 let mut env = Env::new();
569 env.set_eval_file(file);
570 env.set_source_id(src_id);
575 builtins::register(&mut env);
576 let result = eval_expr(&expr, &env).map_err(|e| attach_trace(e))?;
577 let final_result = force_value(&result).map_err(|e| attach_trace(e));
579 CURRENT_SOURCE_ID.with(|s| s.set(prev_src_id));
581 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
582 if nesting == 0 {
583 crate::perf::report();
584 }
585 final_result
586}
587
588#[inline(always)]
596pub fn force_concrete(value: &Value) -> Result<Concrete, EvalError> {
601 value.demand()
602}
603
604pub fn force_value(value: &Value) -> Result<Value, EvalError> {
608 crate::perf::inc(crate::perf::Counter::ForceValue);
609 if !matches!(value, Value::Thunk(_)) {
612 return Ok(value.clone());
613 }
614 let mut v = value.clone();
629 let mut depth = 0u32;
630 loop {
631 match v {
632 Value::Thunk(ref thunk) => {
633 v = force_thunk(thunk)?;
634 depth += 1;
635 if depth > 100 {
636 return Err(EvalError::InfiniteRecursion(
637 "force_value: thunk chain exceeded depth 100 (cycle or runaway lazy wrap)".into(),
638 ));
639 }
640 }
641 _ => return Ok(v),
642 }
643 }
644}
645
646pub fn force_value_tracked(value: &Value, site: &str) -> Result<Value, EvalError> {
648 crate::perf::inc(crate::perf::Counter::ForceValue);
649 if let Value::Thunk(thunk) = value {
650 FORCE_SITES.with(|sites| {
651 *sites.borrow_mut().entry(site.to_string()).or_insert(0) += 1;
652 });
653 force_thunk(thunk)
654 } else {
655 Ok(value.clone())
656 }
657}
658
659thread_local! {
660 static FORCE_SITES: std::cell::RefCell<std::collections::HashMap<String, u64>> =
661 std::cell::RefCell::new(std::collections::HashMap::new());
662 static APPLY_SITES: std::cell::RefCell<std::collections::HashMap<String, u64>> =
663 std::cell::RefCell::new(std::collections::HashMap::new());
664}
665
666pub fn dump_force_sites() {
668 FORCE_SITES.with(|sites| {
669 let sites = sites.borrow();
670 let mut sorted: Vec<_> = sites.iter().collect();
671 sorted.sort_by(|a, b| b.1.cmp(a.1));
672 eprintln!("[force-sites] top thunk force call sites:");
673 for (site, count) in sorted.iter().take(10) {
674 eprintln!(" {count:>8} {site}");
675 }
676 });
677 APPLY_SITES.with(|sites| {
678 let sites = sites.borrow();
679 let mut sorted: Vec<_> = sites.iter().collect();
680 sorted.sort_by(|a, b| b.1.cmp(a.1));
681 eprintln!("[apply-sites] top lambda call sites by source file:");
682 for (site, count) in sorted.iter().take(15) {
683 let short = site.rsplit_once("-source/").map_or(site.as_str(), |(_,s)| s);
685 eprintln!(" {count:>8} {short}");
686 }
687 });
688}
689
690fn force_thunk(thunk: &Thunk) -> Result<Value, EvalError> {
694 if let Some(cached) = thunk.peek() {
696 crate::perf::inc(crate::perf::Counter::ThunkHit);
697 return Ok(cached.clone().into_value());
698 }
699 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || {
700 thunk.force(&|expr, env| eval_expr(expr, env))
706 })
707}
708
709fn referenced_idents(value_expr: &ast::Expr) -> HashSet<SmolStr> {
776 use rnix::SyntaxKind;
777 let perf_on = crate::perf::enabled();
783 let t0 = if perf_on {
784 Some(std::time::Instant::now())
785 } else {
786 None
787 };
788 crate::perf::inc(crate::perf::Counter::SelfRecWalkCalls);
789 let mut nodes_walked: u64 = 0;
790 let mut set: HashSet<SmolStr> = HashSet::new();
791 for node in value_expr.syntax().descendants() {
792 nodes_walked += 1;
793 if node.kind() == SyntaxKind::NODE_IDENT
794 && node
795 .parent()
796 .is_none_or(|p| p.kind() != SyntaxKind::NODE_ATTRPATH)
797 && let Some(i) = ast::Ident::cast(node)
798 {
799 set.insert(SmolStr::from(ident_text(&i).as_str()));
800 }
801 }
802 crate::perf::add(crate::perf::Counter::SelfRecWalkNodes, nodes_walked);
803 if let Some(t0) = t0 {
804 crate::trace::add_self_rec_walk_nanos(t0.elapsed().as_nanos());
805 }
806 set
807}
808
809fn is_self_recursive_binding(value_expr: &ast::Expr, name: &str) -> bool {
813 referenced_idents(value_expr).contains(name)
814}
815
816fn maybe_thunk(
817 expr: &ast::Expr,
818 env: &Env,
819 is_rec: bool,
820 defined_so_far: Option<&HashSet<String>>,
821) -> Value {
822 match expr {
823 ast::Expr::Literal(lit) => eval_literal(lit).unwrap_or_else(|_| {
825 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
826 }),
827 ast::Expr::Ident(ident) if !is_rec => {
834 let sym = {
844 let src_id = env.source_id();
845 let offset = u32::from(ident.syntax().text_range().start());
846 crate::value::intern_cached_with(src_id, offset, || {
847 crate::value::intern(&ident_text(ident))
848 })
849 };
850 if let Some(kw) = crate::value::with_resolved(sym, |s| match s {
852 "true" => Some(Value::Bool(true)),
853 "false" => Some(Value::Bool(false)),
854 "null" => Some(Value::Null),
855 _ => None,
856 }) {
857 return kw;
858 }
859 {
860 {
861 if let Some(v) = env.lookup_fast(sym, "") {
865 return v;
866 }
867 if let Some((scope_cache, scope_value)) = env.innermost_with_scope() {
870 return Value::Thunk(Thunk::new_with_ident(
871 SmolStr::from(ident_text(ident).as_str()),
872 scope_cache,
873 scope_value,
874 env.clone(),
875 ));
876 }
877 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
878 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
879 }
880 }
881 }
882 ast::Expr::Ident(ident) if is_rec => {
886 let name = ident_text(ident);
887 match name.as_str() {
888 "true" => Value::Bool(true),
889 "false" => Value::Bool(false),
890 "null" => Value::Null,
891 _ => {
892 if defined_so_far.map_or(false, |d| d.contains(&name)) {
895 env.lookup(&name).unwrap_or_else(|| {
896 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
897 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
898 })
899 } else {
900 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
902 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
903 }
904 }
905 }
906 }
907 ast::Expr::PathAbs(p) if !parts_have_interpolation(&p.parts()) => {
912 let text = crate::path::canon_abs(&p.syntax().text().to_string());
918 Value::Path(Box::new(SmolStr::from(text.as_str())))
919 }
920 ast::Expr::PathHome(p) if !parts_have_interpolation(&p.parts()) => {
921 let text = p.syntax().text().to_string();
922 Value::Path(Box::new(SmolStr::from(text.as_str())))
923 }
924 ast::Expr::Str(st) if !str_has_interpolation(st) => {
937 eval_str(st, env).unwrap_or_else(|_| {
938 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
939 })
940 }
941 ast::Expr::Lambda(lam) if !is_rec => {
945 if let (Some(param), Some(body)) = (lam.param(), lam.body()) {
946 Value::Lambda(Rc::new(Closure {
947 param,
948 body,
949 env: env.clone(),
950 }))
951 } else {
952 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
953 }
954 }
955 _ => {
966 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeOther);
967 if crate::perf::enabled() {
968 let kind = match expr {
969 ast::Expr::Select(_) => "Select",
970 ast::Expr::Apply(_) => "Apply",
971 ast::Expr::BinOp(_) => "BinOp",
972 ast::Expr::IfElse(_) => "IfElse",
973 ast::Expr::Str(_) => "Str",
974 ast::Expr::List(_) => "List",
975 ast::Expr::With(_) => "With",
976 ast::Expr::Assert(_) => "Assert",
977 ast::Expr::HasAttr(_) => "HasAttr",
978 ast::Expr::UnaryOp(_) => "UnaryOp",
979 ast::Expr::Paren(_) => "Paren",
980 ast::Expr::LetIn(_) => "LetIn",
981 ast::Expr::AttrSet(_) => "AttrSet",
982 ast::Expr::Ident(_) => "Ident(rec)",
983 ast::Expr::Lambda(_) => "Lambda(rec)",
984 ast::Expr::LegacyLet(_) => "LegacyLet",
985 ast::Expr::PathAbs(_)
986 | ast::Expr::PathHome(_)
987 | ast::Expr::PathRel(_)
988 | ast::Expr::PathSearch(_) => "Path(interp)",
989 _ => "Other",
990 };
991 crate::trace::inc_maybe_other_kind(kind);
992 }
993 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
994 }
995 }
996}
997
998#[inline(always)]
1009pub fn eval_expr(expr: &ast::Expr, env: &Env) -> Result<Value, EvalError> {
1010 match expr {
1013 ast::Expr::Ident(ident) => {
1014 crate::perf::inc(crate::perf::Counter::EvalExpr);
1015 if crate::perf::enabled() {
1016 crate::perf::inc(crate::perf::Counter::ExprIdent);
1017 }
1018 if crate::resolve_env::enabled() {
1031 let src_id = CURRENT_SOURCE_ID.with(std::cell::Cell::get);
1032 let offset = u32::from(ident.syntax().text_range().start());
1033 if let sui_resolve::Resolution::Lexical { sym } =
1034 crate::resolve_env::resolution_for(src_id, offset)
1035 {
1036 if let Some(v) = env.lookup_lexical_sym(sym) {
1037 return Ok(v);
1038 }
1039 }
1040 }
1042 let sym = {
1076 let src_id = env.source_id();
1077 let offset = u32::from(ident.syntax().text_range().start());
1078 crate::value::intern_cached_with(src_id, offset, || {
1079 crate::value::intern(&ident_text(ident))
1080 })
1081 };
1082 if let Some(kw) = crate::value::with_resolved(sym, |s| match s {
1086 "true" => Some(Value::Bool(true)),
1087 "false" => Some(Value::Bool(false)),
1088 "null" => Some(Value::Null),
1089 _ => None,
1090 }) {
1091 return Ok(kw);
1092 }
1093 return {
1094 {
1095 if let Some(v) = env.lookup_fast(sym, "") {
1099 Ok(v)
1100 } else {
1101 let name = ident_text(ident);
1102 let fresh = crate::value::intern(name.as_str());
1121 if fresh != sym {
1122 if let Some(v) = env.lookup_fast(fresh, name.as_str()) {
1123 return Ok(v);
1124 }
1125 }
1126 if env.with_scope_count() > 0 {
1127 if let Some((scope_cache, scope_value)) = env.innermost_with_scope() {
1131 Ok(Value::Thunk(Thunk::new_with_ident(
1132 SmolStr::from(name.as_str()),
1133 scope_cache,
1134 scope_value,
1135 env.clone(),
1136 )))
1137 } else if crate::value::in_promise_eval() {
1138 Ok(Value::Null)
1148 } else {
1149 Err(EvalError::UndefinedVar(
1150 format!("'{name}'{}", eval_file_ctx()),
1151 ))
1152 }
1153 } else {
1154 if let Ok(dbg_var) = std::env::var("SUI_DEBUG_VAR") {
1155 if dbg_var == name || dbg_var == "*" {
1156 eprintln!(
1157 "[sui-debug] UndefinedVar '{name}' in {}\n\
1158 [sui-debug] env bindings ({} total): {:?}\n\
1159 [sui-debug] with_scopes: {}",
1160 eval_file_ctx(),
1161 env.binding_count(),
1162 env.binding_names_preview(20),
1163 env.with_scope_count(),
1164 );
1165 }
1166 }
1167 if crate::value::in_promise_eval() {
1168 return Ok(Value::Null);
1171 }
1172 Err(EvalError::UndefinedVar(
1173 format!("'{name}'{}", eval_file_ctx()),
1174 ))
1175 }
1176 }
1177 }
1178 };
1179 }
1180 ast::Expr::Literal(lit) => {
1181 crate::perf::inc(crate::perf::Counter::EvalExpr);
1182 if crate::perf::enabled() {
1183 crate::perf::inc(crate::perf::Counter::ExprLiteral);
1184 }
1185 return eval_literal(lit);
1186 }
1187 ast::Expr::Paren(p) => {
1188 if let Some(inner) = p.expr() {
1189 return eval_expr(&inner, env);
1190 }
1191 }
1192 ast::Expr::Root(r) => {
1193 if let Some(inner) = r.expr() {
1194 return eval_expr(&inner, env);
1195 }
1196 }
1197 ast::Expr::Lambda(lam) => {
1199 crate::perf::inc(crate::perf::Counter::EvalExpr);
1200 if crate::perf::enabled() {
1201 crate::perf::inc(crate::perf::Counter::ExprLambda);
1202 }
1203 if let (Some(param), Some(body)) = (lam.param(), lam.body()) {
1204 return Ok(Value::Lambda(Rc::new(Closure {
1205 param,
1206 body,
1207 env: env.clone(),
1208 })));
1209 }
1210 }
1211 _ => {}
1212 }
1213 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || {
1215 eval_expr_inner(expr, env)
1216 })
1217}
1218
1219fn eval_expr_inner(expr: &ast::Expr, env: &Env) -> Result<Value, EvalError> {
1227 let mut cur_expr = expr.clone();
1230 let mut cur_env = env.clone();
1231
1232 loop {
1233 crate::perf::inc(crate::perf::Counter::EvalExpr);
1234 if crate::perf::enabled() {
1236 use crate::perf::Counter;
1237 let c = match &cur_expr {
1238 ast::Expr::Ident(_) => Counter::ExprIdent,
1239 ast::Expr::Literal(_) => Counter::ExprLiteral,
1240 ast::Expr::Str(_) => Counter::ExprStr,
1241 ast::Expr::List(_) => Counter::ExprList,
1242 ast::Expr::AttrSet(_) => Counter::ExprAttrs,
1243 ast::Expr::Select(_) => Counter::ExprSelect,
1244 ast::Expr::Apply(_) => Counter::ExprApply,
1245 ast::Expr::LetIn(_) => Counter::ExprLetIn,
1246 ast::Expr::IfElse(_) => Counter::ExprIfElse,
1247 ast::Expr::With(_) => Counter::ExprWith,
1248 ast::Expr::Lambda(_) => Counter::ExprLambda,
1249 ast::Expr::BinOp(_) => Counter::ExprBinOp,
1250 ast::Expr::HasAttr(_) => Counter::ExprHasAttr,
1251 ast::Expr::UnaryOp(_) => Counter::ExprUnaryOp,
1252 ast::Expr::Assert(_) => Counter::ExprAssert,
1253 ast::Expr::PathAbs(_) | ast::Expr::PathRel(_)
1254 | ast::Expr::PathHome(_) | ast::Expr::PathSearch(_) => Counter::ExprPath,
1255 _ => Counter::ExprOther,
1256 };
1257 crate::perf::inc(c);
1258 }
1259 let _guard = DepthGuard::enter()?;
1260 let env = &cur_env;
1261 match &cur_expr {
1262 ast::Expr::Literal(lit) => return eval_literal(lit),
1263
1264 ast::Expr::Str(s) => return eval_str(s, env),
1265
1266 ast::Expr::PathAbs(p) => {
1267 let parts = p.parts();
1270 if parts_have_interpolation(&parts) {
1271 return eval_interpol_path_parts(&parts, PathKind::Abs, env);
1272 }
1273 let text = crate::path::canon_abs(&p.syntax().text().to_string());
1276 return Ok(Value::Path(Box::new(SmolStr::from(text.as_str()))));
1277 }
1278 ast::Expr::PathRel(p) => {
1279 let parts = p.parts();
1290 if parts_have_interpolation(&parts) {
1291 return eval_interpol_path_parts(&parts, PathKind::Rel, env);
1292 }
1293 let text = p.syntax().text().to_string();
1294 let resolved = if let Some(dir) = current_eval_dir() {
1295 let joined = dir.join(&text);
1296 let norm = normalize_path(&joined);
1300 crate::path::dematerialize(&norm)
1310 .to_string_lossy()
1311 .into_owned()
1312 } else {
1313 text.clone()
1314 };
1315 return Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))));
1316 }
1317 ast::Expr::PathHome(p) => {
1318 let parts = p.parts();
1319 if parts_have_interpolation(&parts) {
1320 return eval_interpol_path_parts(&parts, PathKind::Home, env);
1321 }
1322 let text = p.syntax().text().to_string();
1323 return Ok(Value::Path(Box::new(SmolStr::from(text.as_str()))));
1324 }
1325 ast::Expr::PathSearch(p) => {
1326 let text = p.syntax().text().to_string();
1331 let inner = text
1332 .strip_prefix('<')
1333 .and_then(|s| s.strip_suffix('>'))
1334 .unwrap_or(&text);
1335 if let Some(resolved) = crate::builtins::resolve_search_path(inner) {
1336 return Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))));
1337 }
1338 return Err(EvalError::Throw(
1342 format!("search path '{text}' not in NIX_PATH"),
1343 ));
1344 }
1345
1346 ast::Expr::Ident(ident) => {
1347 let name = ident_text(ident);
1348 return match name.as_str() {
1349 "true" => Ok(Value::Bool(true)),
1350 "false" => Ok(Value::Bool(false)),
1351 "null" => Ok(Value::Null),
1352 _ => {
1353 env.lookup(&name)
1354 .ok_or_else(|| EvalError::UndefinedVar(
1355 format!("'{name}'{}", eval_file_ctx()),
1356 ))
1357 }
1358 };
1359 }
1360
1361 ast::Expr::List(list) => {
1362 let values: Vec<Value> = list.items()
1367 .map(|e| maybe_thunk(&e, env, false, None))
1368 .collect();
1369 return Ok(Value::list(values));
1370 }
1371
1372 ast::Expr::AttrSet(set) => return eval_attrset(set, env),
1373
1374 ast::Expr::Select(sel) => return eval_select(sel, env),
1375
1376 ast::Expr::HasAttr(ha) => return eval_has_attr(ha, env),
1377
1378 ast::Expr::UnaryOp(op) => return eval_unary_op(op, env),
1379
1380 ast::Expr::BinOp(binop) => {
1381 let lhs_expr = binop
1382 .lhs()
1383 .ok_or_else(|| EvalError::ParseError("binop missing lhs".to_string()))?;
1384 let rhs_expr = binop
1385 .rhs()
1386 .ok_or_else(|| EvalError::ParseError("binop missing rhs".to_string()))?;
1387 let kind = binop
1388 .operator()
1389 .ok_or_else(|| EvalError::ParseError("binop missing operator".to_string()))?;
1390 return eval_binop(kind, &lhs_expr, &rhs_expr, env);
1391 }
1392
1393 ast::Expr::Apply(app) => return eval_apply(app, env),
1394
1395 ast::Expr::IfElse(ie) => {
1396 let cond = ie
1397 .condition()
1398 .ok_or_else(|| EvalError::ParseError("if missing condition".to_string()))?;
1399 let body = ie
1400 .body()
1401 .ok_or_else(|| EvalError::ParseError("if missing then body".to_string()))?;
1402 let else_body = ie
1403 .else_body()
1404 .ok_or_else(|| EvalError::ParseError("if missing else body".to_string()))?;
1405 if force_concrete(&eval_expr(&cond, env)?)?.as_bool()? {
1406 cur_expr = body;
1407 } else {
1408 cur_expr = else_body;
1409 }
1410 continue;
1412 }
1413
1414 ast::Expr::Assert(assert) => {
1415 let cond = assert
1416 .condition()
1417 .ok_or_else(|| EvalError::ParseError("assert missing condition".to_string()))?;
1418 let body = assert
1419 .body()
1420 .ok_or_else(|| EvalError::ParseError("assert missing body".to_string()))?;
1421 if !force_concrete(&eval_expr(&cond, env)?)?.as_bool()? {
1422 return Err(EvalError::AssertionFailed(eval_file_ctx()));
1423 }
1424 cur_expr = body;
1425 continue;
1426 }
1427
1428 ast::Expr::With(with) => {
1429 let ns = with
1430 .namespace()
1431 .ok_or_else(|| EvalError::ParseError("with missing namespace".to_string()))?;
1432 let body = with
1433 .body()
1434 .ok_or_else(|| EvalError::ParseError("with missing body".to_string()))?;
1435 let scope_val = maybe_thunk(&ns, env, false, None);
1461 let new_env = env.child().with_scope(scope_val);
1462 cur_expr = body;
1463 cur_env = new_env;
1464 continue;
1465 }
1466
1467 ast::Expr::LetIn(letin) => {
1468 let mut new_env = env.child();
1469
1470 let mut thunks: Vec<(String, Thunk)> = Vec::new();
1473
1474 let mut defined_so_far: HashSet<String> = HashSet::new();
1478
1479 let mut dotted_attrs: NixAttrs = NixAttrs::new();
1483
1484 let let_scope_names: HashSet<String> = {
1490 let mut s = HashSet::new();
1491 for entry in letin.entries() {
1492 match entry {
1493 ast::Entry::AttrpathValue(apv) => {
1494 if let Some(attrpath) = apv.attrpath() {
1495 if let Some(first) = attrpath.attrs().next() {
1496 if let Ok(name) = eval_attr(&first, env) {
1497 s.insert(name);
1498 }
1499 }
1500 }
1501 }
1502 ast::Entry::Inherit(inherit) => {
1503 for attr in inherit.attrs() {
1504 if let Ok(name) = eval_attr(&attr, env) {
1505 s.insert(name);
1506 }
1507 }
1508 }
1509 }
1510 }
1511 s
1512 };
1513
1514 for entry in letin.entries() {
1515 match entry {
1516 ast::Entry::AttrpathValue(ref apv) => {
1517 let attrpath = apv.attrpath().ok_or_else(|| {
1518 EvalError::ParseError("binding missing attrpath".to_string())
1519 })?;
1520 let value_expr = apv.value().ok_or_else(|| {
1521 EvalError::ParseError("binding missing value".to_string())
1522 })?;
1523 let mut path_keys: Vec<String> = attrpath
1524 .attrs()
1525 .map(|a| eval_attr(&a, env))
1526 .collect::<Result<_, _>>()?;
1527 if path_keys.len() == 1 {
1528 let key = path_keys.pop().unwrap();
1529 let referenced = referenced_idents(&value_expr);
1552 let in_mutual_cycle = std::iter::once(&key)
1553 .chain(let_scope_names.iter())
1554 .any(|n| referenced.contains(n.as_str()));
1555 let value = if in_mutual_cycle {
1556 Value::Thunk(Thunk::new_suspended_recursive(
1557 value_expr.clone(),
1558 env.clone(),
1559 ))
1560 } else {
1561 maybe_thunk(&value_expr, env, true, Some(&defined_so_far))
1562 };
1563 new_env.bind(key.clone(), value.clone());
1564 if let Value::Thunk(t) = &value {
1565 thunks.push((key.clone(), t.clone()));
1566 }
1567 defined_so_far.insert(key);
1568 } else if path_keys.len() > 1 {
1569 let key = path_keys[0].clone();
1574 let value = build_nested_attr_thunk(
1575 &path_keys[1..],
1576 &value_expr,
1577 env,
1578 &mut thunks,
1579 );
1580 merge_nested_insert(&mut dotted_attrs, key, value);
1581 }
1582 }
1583 ast::Entry::Inherit(ref inherit) => {
1584 if let Some(from) = inherit.from() {
1585 let source_expr = from.expr().ok_or_else(|| {
1586 EvalError::ParseError(
1587 "inherit from missing expr".to_string(),
1588 )
1589 })?;
1590 let source_thunk = Thunk::new_suspended(
1595 source_expr, env.clone(),
1596 );
1597 for attr in inherit.attrs() {
1598 let name = eval_attr(&attr, env)?;
1599 let thunk = Thunk::new_inherit_select(
1600 source_thunk.clone(),
1601 name.clone(),
1602 );
1603 new_env.bind(name.clone(), Value::Thunk(thunk.clone()));
1604 thunks.push((name, thunk));
1605 }
1606 } else {
1607 for attr in inherit.attrs() {
1612 let name = eval_attr(&attr, env)?;
1613 let value = env.lookup(&name).ok_or_else(|| {
1614 EvalError::UndefinedVar(
1615 format!("'{name}'{}", eval_file_ctx()),
1616 )
1617 })?;
1618 new_env.bind(name, value);
1619 }
1620 }
1621 }
1622 }
1623 }
1624
1625 for (key, value) in dotted_attrs.iter() {
1630 new_env.bind(key.clone(), value.clone());
1631 }
1632
1633 for (_key, thunk) in &thunks {
1636 thunk.update_env(&new_env);
1637 }
1638
1639 let body = letin
1640 .body()
1641 .ok_or_else(|| EvalError::ParseError("let missing body".to_string()))?;
1642 cur_expr = body;
1643 cur_env = new_env;
1644 continue;
1645 }
1646
1647 ast::Expr::Lambda(lam) => {
1648 let param = lam
1649 .param()
1650 .ok_or_else(|| EvalError::ParseError("lambda missing param".to_string()))?;
1651 let body = lam
1652 .body()
1653 .ok_or_else(|| EvalError::ParseError("lambda missing body".to_string()))?;
1654 return Ok(Value::Lambda(Rc::new(Closure {
1655 param,
1656 body,
1657 env: env.clone(),
1658 })));
1659 }
1660
1661 ast::Expr::Paren(p) => {
1662 let inner = p
1663 .expr()
1664 .ok_or_else(|| EvalError::ParseError("paren missing expr".to_string()))?;
1665 cur_expr = inner;
1666 continue;
1667 }
1668
1669 ast::Expr::Root(r) => {
1670 let inner = r
1671 .expr()
1672 .ok_or_else(|| EvalError::ParseError("root missing expr".to_string()))?;
1673 cur_expr = inner;
1674 continue;
1675 }
1676
1677 ast::Expr::LegacyLet(ll) => {
1678 let mut new_env = env.child();
1679 eval_entries(ll, &mut new_env)?;
1680 return new_env
1682 .lookup("body")
1683 .ok_or_else(|| EvalError::AttrNotFound(
1684 format!("'body' in legacy let{}", eval_file_ctx()),
1685 ));
1686 }
1687
1688 ast::Expr::CurPos(_) => return Err(EvalError::NotImplemented("__curPos".to_string())),
1689 ast::Expr::Error(_) => return Err(EvalError::ParseError("parse error node".to_string())),
1690 } } }
1693
1694fn eval_literal(lit: &ast::Literal) -> Result<Value, EvalError> {
1695 use ast::LiteralKind;
1696 match lit.kind() {
1697 LiteralKind::Integer(tok) => {
1698 let n = tok
1699 .value()
1700 .map_err(|e| EvalError::ParseError(format!("invalid integer: {e}")))?;
1701 Ok(Value::Int(n))
1702 }
1703 LiteralKind::Float(tok) => {
1704 let f = tok
1705 .value()
1706 .map_err(|e| EvalError::ParseError(format!("invalid float: {e}")))?;
1707 Ok(Value::Float(f))
1708 }
1709 LiteralKind::Uri(tok) => Ok(Value::string(tok.syntax().text().to_string())),
1710 }
1711}
1712
1713enum TraverseResult {
1715 Found(Value),
1717 Missing(String),
1719 NotAttrs(Value),
1721}
1722
1723fn traverse_attrpath(
1728 base: Value,
1729 attrpath: &rnix::ast::Attrpath,
1730 env: &Env,
1731) -> Result<TraverseResult, EvalError> {
1732 let attrs: Vec<_> = attrpath.attrs().collect();
1733 let mut value = base;
1734 for (i, attr) in attrs.iter().enumerate() {
1735 let key = eval_attr(attr, env)?;
1736 let forced = force_value(&value)?;
1738 match forced {
1739 Value::Attrs(ref a) => match a.get(&key) {
1740 Some(v) => {
1741 if i < attrs.len() - 1 {
1742 value = force_value(v)?;
1744 } else {
1745 value = v.clone();
1748 }
1749 }
1750 None => return Ok(TraverseResult::Missing(key)),
1751 },
1752 _ => return Ok(TraverseResult::NotAttrs(forced)),
1753 }
1754 }
1755 Ok(TraverseResult::Found(value))
1756}
1757
1758fn eval_select(sel: &ast::Select, env: &Env) -> Result<Value, EvalError> {
1759 crate::perf::inc(crate::perf::Counter::Select);
1760 let base_expr = sel.expr().ok_or_else(|| {
1761 EvalError::ParseError("select missing expression".to_string())
1762 })?;
1763 let base_result = eval_expr(&base_expr, env)
1772 .and_then(|v| force_concrete(&v).map(Concrete::into_value));
1773 let base = match base_result {
1774 Ok(v) => v,
1775 Err(EvalError::InfiniteRecursion(_)) if sel.default_expr().is_some() => {
1776 return eval_expr(&sel.default_expr().expect("checked"), env);
1777 }
1778 Err(e) => return Err(e),
1779 };
1780 let base_type = base.type_name();
1781 let attrpath = sel.attrpath().ok_or_else(|| {
1782 EvalError::ParseError("select missing attrpath".to_string())
1783 })?;
1784 let bridge_active = std::env::var_os("SUI_BLACKHOLE_AS_EMPTY_ATTRS").is_some()
1806 || std::env::var_os("SUI_BLACKHOLE_AS_NULL").is_some();
1807 let traversal = traverse_attrpath(base, &attrpath, env);
1808 match traversal {
1809 Ok(TraverseResult::Found(v)) => Ok(v),
1810 Ok(TraverseResult::Missing(key)) => {
1811 if let Some(def) = sel.default_expr() {
1812 eval_expr(&def, env)
1813 } else if bridge_active {
1814 if std::env::var_os("SUI_M26_SELTRACE").is_some() {
1815 let path: Vec<String> = sel.attrpath().map(|ap|
1816 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1817 ).unwrap_or_default();
1818 eprintln!("[M26 SEL-MISS→null] base_type={base_type} path={path:?} missing-key={key}{}", eval_file_ctx());
1819 }
1820 if let Ok(filt) = std::env::var("SUI_M26_HARDSOFTEN") {
1821 let path: Vec<String> = sel.attrpath().map(|ap|
1822 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1823 ).unwrap_or_default();
1824 if path.iter().any(|p| p.contains(&filt)) {
1825 return Err(EvalError::type_error(format!(
1826 "M26-HARDSOFTEN path={path:?} key={key}"
1827 )));
1828 }
1829 }
1830 Ok(Value::Null)
1831 } else {
1832 Err(EvalError::AttrNotFound(
1833 format!("'{key}'{}", eval_file_ctx()),
1834 ))
1835 }
1836 }
1837 Ok(TraverseResult::NotAttrs(forced)) => {
1838 if let Some(def) = sel.default_expr() {
1844 eval_expr(&def, env)
1845 } else if bridge_active {
1846 if let Ok(filt) = std::env::var("SUI_M26_HARDSOFTEN") {
1847 let path: Vec<String> = sel.attrpath().map(|ap|
1848 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1849 ).unwrap_or_default();
1850 if path.iter().any(|p| p.contains(&filt)) {
1851 return Err(EvalError::type_error(format!(
1852 "M26-HARDSOFTEN-NOTATTRS path={path:?} base_type={base_type}"
1853 )));
1854 }
1855 }
1856 return Ok(Value::Null);
1857 } else {
1858 if std::env::var("SUI_DEBUG_SELECT").is_ok() {
1859 let path: Vec<String> = sel.attrpath().map(|ap|
1860 ap.attrs().filter_map(|a| match a {
1861 ast::Attr::Ident(i) => Some(i.to_string()),
1862 ast::Attr::Str(s) => Some(format!("\"{}\"", s.syntax().text())),
1863 ast::Attr::Dynamic(_) => Some("<dyn>".into()),
1864 }).collect()
1865 ).unwrap_or_default();
1866 let dbg = format!("{:?}", forced);
1867 let truncated = if dbg.len() > 200 { format!("{}…", &dbg[..200]) } else { dbg };
1868 eprintln!("[SUI_DEBUG_SELECT] base_type={base_type} path={path:?} base={truncated}{}", eval_file_ctx());
1869 }
1870 Err(attach_trace(EvalError::type_error(
1871 format!("cannot select from {base_type}"),
1872 )))
1873 }
1874 }
1875 Err(EvalError::InfiniteRecursion(_)) if sel.default_expr().is_some() => {
1880 eval_expr(&sel.default_expr().expect("checked"), env)
1881 }
1882 Err(e) => Err(e),
1883 }
1884}
1885
1886fn eval_has_attr(ha: &ast::HasAttr, env: &Env) -> Result<Value, EvalError> {
1888 let base_expr = ha.expr().ok_or_else(|| {
1889 EvalError::ParseError("hasattr missing expression".to_string())
1890 })?;
1891 let base = force_concrete(&eval_expr(&base_expr, env)?)?.into_value();
1892 let attrpath = ha.attrpath().ok_or_else(|| {
1893 EvalError::ParseError("hasattr missing attrpath".to_string())
1894 })?;
1895 match traverse_attrpath(base, &attrpath, env)? {
1896 TraverseResult::Found(_) => Ok(Value::Bool(true)),
1897 TraverseResult::Missing(_) | TraverseResult::NotAttrs(_) => Ok(Value::Bool(false)),
1898 }
1899}
1900
1901fn eval_unary_op(op: &ast::UnaryOp, env: &Env) -> Result<Value, EvalError> {
1902 let inner = op
1903 .expr()
1904 .ok_or_else(|| EvalError::ParseError("unary op missing expr".to_string()))?;
1905 let val = force_value(&eval_expr(&inner, env)?)?;
1906 let kind = op
1907 .operator()
1908 .ok_or_else(|| EvalError::ParseError("unary op missing operator".to_string()))?;
1909 match kind {
1910 ast::UnaryOpKind::Negate => match val {
1911 Value::Int(n) => Ok(Value::Int(-n)),
1912 Value::Float(f) => Ok(Value::Float(-f)),
1913 _ => Err(EvalError::type_error(
1914 format!("cannot negate {}", val.type_name()),
1915 )),
1916 },
1917 ast::UnaryOpKind::Invert => Ok(Value::Bool(!val.as_bool()?)),
1918 }
1919}
1920
1921#[inline]
1932pub(crate) fn builtin_takes_lazy_arg(name: &str) -> bool {
1933 matches!(
1934 name,
1935 "tryEval" | "addErrorContext<partial>" | "seq<partial>" | "deepSeq<partial>" | "foldl'<p1>"
1936 )
1937}
1938
1939fn eval_apply(app: &ast::Apply, env: &Env) -> Result<Value, EvalError> {
1940 let func_expr = app
1941 .lambda()
1942 .ok_or_else(|| EvalError::ParseError("apply missing function".to_string()))?;
1943 let arg_expr = app
1944 .argument()
1945 .ok_or_else(|| EvalError::ParseError("apply missing argument".to_string()))?;
1946 let func = force_value(&eval_expr(&func_expr, env)?)?;
1947 let arg = match &func {
1955 Value::Lambda(_) => {
1956 if let Some(v) = eval_pure_constant_arg(&arg_expr) {
1965 v
1966 } else {
1967 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1968 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1969 }
1970 }
1971 Value::Builtin(b) if builtin_takes_lazy_arg(&b.name) => {
1972 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1977 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1978 }
1979 _ => eval_expr(&arg_expr, env)?,
1980 };
1981 apply(func, arg)
1982}
1983
1984fn eval_pure_constant_arg(arg_expr: &ast::Expr) -> Option<Value> {
1999 match arg_expr {
2000 ast::Expr::Literal(lit) => eval_literal(lit).ok(),
2001 ast::Expr::Str(st) if !str_has_interpolation(st) => {
2002 eval_str(st, &Env::new()).ok()
2004 }
2005 ast::Expr::PathAbs(p) if !parts_have_interpolation(&p.parts()) => {
2006 let text = crate::path::canon_abs(&p.syntax().text().to_string());
2007 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
2008 }
2009 ast::Expr::PathHome(p) if !parts_have_interpolation(&p.parts()) => {
2010 let text = p.syntax().text().to_string();
2011 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
2012 }
2013 _ => None,
2014 }
2015}
2016
2017fn eval_str(s: &ast::Str, env: &Env) -> Result<Value, EvalError> {
2018 let mut result = String::new();
2019 let mut ctx = StringContext::new();
2020 for part in s.normalized_parts() {
2021 match part {
2022 InterpolPart::Literal(text) => result.push_str(&text),
2023 InterpolPart::Interpolation(interpol) => {
2024 let expr = interpol.expr().ok_or_else(|| {
2025 EvalError::ParseError("interpolation missing expr".to_string())
2026 })?;
2027 let val = force_value(&eval_expr(&expr, env)?)?;
2028 let (s, c) = val.coerce_to_string_copy_to_store()?;
2033 result.push_str(&s);
2034 ctx.merge(&c);
2035 }
2036 }
2037 }
2038 Ok(Value::String(Rc::new(NixString::with_context(result, ctx))))
2039}
2040
2041fn parts_have_interpolation(parts: &[InterpolPart<rnix::ast::PathContent>]) -> bool {
2045 parts
2046 .iter()
2047 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2048}
2049
2050fn str_has_interpolation(s: &ast::Str) -> bool {
2054 s.normalized_parts()
2055 .iter()
2056 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2057}
2058
2059fn eval_interpol_path_parts(
2074 parts: &[InterpolPart<rnix::ast::PathContent>],
2075 kind: PathKind,
2076 env: &Env,
2077) -> Result<Value, EvalError> {
2078 let mut text = String::new();
2079 for part in parts {
2080 match part {
2081 InterpolPart::Literal(content) => text.push_str(content.text()),
2082 InterpolPart::Interpolation(interpol) => {
2083 let expr = interpol.expr().ok_or_else(|| {
2084 EvalError::ParseError("path interpolation missing expr".to_string())
2085 })?;
2086 let val = force_value(&eval_expr(&expr, env)?)?;
2087 let (s, _ctx) = val.coerce_to_string()?;
2091 text.push_str(&s);
2092 }
2093 }
2094 }
2095 let resolved = match kind {
2096 PathKind::Rel => {
2099 if let Some(dir) = current_eval_dir() {
2100 let norm = normalize_path(&dir.join(&text));
2101 crate::path::dematerialize(&norm).to_string_lossy().into_owned()
2110 } else {
2111 text
2115 }
2116 }
2117 PathKind::Abs => crate::path::canon_abs(&text),
2125 PathKind::Home => normalize_path(std::path::Path::new(&text))
2128 .to_string_lossy()
2129 .into_owned(),
2130 };
2131 Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))))
2132}
2133
2134#[derive(Clone, Copy)]
2137enum PathKind {
2138 Abs,
2139 Rel,
2140 Home,
2141}
2142
2143fn eval_attr(attr: &ast::Attr, env: &Env) -> Result<String, EvalError> {
2146 eval_attr_maybe_null(attr, env)?
2147 .ok_or_else(|| EvalError::TypeError("null dynamic attribute name".into()))
2148}
2149
2150fn eval_attr_maybe_null(attr: &ast::Attr, env: &Env) -> Result<Option<String>, EvalError> {
2153 match attr {
2154 ast::Attr::Ident(ident) => Ok(Some(ident_text(ident))),
2155 ast::Attr::Dynamic(dyn_) => {
2156 let expr = dyn_
2157 .expr()
2158 .ok_or_else(|| EvalError::ParseError("dynamic attr missing expr".to_string()))?;
2159 let val = force_value(&eval_expr(&expr, env)?)?;
2160 if val == Value::Null {
2163 return Ok(None);
2164 }
2165 Ok(Some(val.as_string()?.to_string()))
2166 }
2167 ast::Attr::Str(s) => {
2168 let val = eval_str(s, env)?;
2169 Ok(Some(val.as_string()?.to_string()))
2170 }
2171 }
2172}
2173
2174fn ident_text(ident: &ast::Ident) -> String {
2176 match ident.ident_token() {
2184 Some(tok) => tok.text().to_string(),
2185 None => ident.syntax().text().to_string(),
2186 }
2187}
2188
2189fn static_attr_offset(attr: &ast::Attr) -> Option<u32> {
2196 let node = match attr {
2197 ast::Attr::Ident(i) => i.syntax(),
2198 ast::Attr::Str(s) => s.syntax(),
2199 ast::Attr::Dynamic(_) => return None,
2200 };
2201 Some(u32::from(node.text_range().start()))
2202}
2203
2204fn attach_attrset_positions(set: &ast::AttrSet, attrs: &mut NixAttrs, env: &Env) {
2211 let mut table = crate::pos::AttrPositions::new(current_eval_file());
2218 for entry in set.entries() {
2219 if let ast::Entry::AttrpathValue(apv) = entry {
2220 let Some(attrpath) = apv.attrpath() else { continue };
2221 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2222 if path_attrs.len() != 1 {
2227 continue;
2228 }
2229 let Some(offset) = static_attr_offset(&path_attrs[0]) else { continue };
2230 if let Ok(Some(name)) = eval_attr_maybe_null(&path_attrs[0], env) {
2233 table.insert(intern(&name), offset);
2234 }
2235 }
2236 }
2237 if !table.is_empty() {
2238 attrs.set_positions(std::rc::Rc::new(table));
2239 }
2240}
2241
2242fn eval_attrset(set: &ast::AttrSet, env: &Env) -> Result<Value, EvalError> {
2243 crate::perf::inc(crate::perf::Counter::Attrset);
2244 let mut attrs = NixAttrs::new();
2245 let is_rec = set.rec_token().is_some();
2246
2247 if is_rec {
2248 let mut rec_env = env.child();
2249 let mut thunks: Vec<(String, Thunk)> = Vec::new();
2250
2251 let mut defined_so_far: HashSet<String> = HashSet::new();
2255
2256 let mut dotted_attrs: NixAttrs = NixAttrs::new();
2262
2263 for entry in set.entries() {
2265 match entry {
2266 ast::Entry::AttrpathValue(apv) => {
2267 let attrpath = apv.attrpath().ok_or_else(|| {
2268 EvalError::ParseError("binding missing attrpath".to_string())
2269 })?;
2270 let value_expr = apv.value().ok_or_else(|| {
2271 EvalError::ParseError("binding missing value".to_string())
2272 })?;
2273 let mut path_keys: Vec<String> = attrpath
2274 .attrs()
2275 .filter_map(|a| eval_attr_maybe_null(&a, env).transpose())
2276 .collect::<Result<_, _>>()?;
2277 if path_keys.is_empty() { continue; }
2279 if path_keys.len() == 1 {
2280 let key = path_keys.pop().unwrap();
2281 let referenced = referenced_idents(&value_expr);
2298 let is_recursive_binding = referenced.contains(key.as_str())
2299 || defined_so_far
2300 .iter()
2301 .any(|n| referenced.contains(n.as_str()));
2302 let value = if is_recursive_binding {
2303 Value::Thunk(Thunk::new_suspended_recursive(
2304 value_expr.clone(),
2305 env.clone(),
2306 ))
2307 } else {
2308 maybe_thunk(&value_expr, env, true, Some(&defined_so_far))
2314 };
2315 rec_env.bind(key.clone(), value.clone());
2316 attrs.insert(key.clone(), value.clone());
2317 if let Value::Thunk(t) = &value {
2318 thunks.push((key.clone(), t.clone()));
2319 }
2320 defined_so_far.insert(key);
2321 } else {
2322 let key = path_keys[0].clone();
2326 let value =
2327 build_nested_attr_thunk(&path_keys[1..], &value_expr, env, &mut thunks);
2328 merge_nested_insert(&mut dotted_attrs, key, value);
2329 }
2330 }
2331 ast::Entry::Inherit(inherit) => {
2332 eval_inherit(&inherit, env, &mut attrs, Some(&mut rec_env), Some(&mut thunks))?;
2333 }
2334 }
2335 }
2336
2337 for (key, value) in dotted_attrs.iter() {
2342 attrs.insert(key.clone(), value.clone());
2343 rec_env.bind(key.clone(), value.clone());
2344 }
2345
2346 for (_key, thunk) in &thunks {
2349 thunk.update_env(&rec_env);
2350 }
2351 } else {
2352 for entry in set.entries() {
2353 match entry {
2354 ast::Entry::AttrpathValue(apv) => {
2355 let attrpath = apv.attrpath().ok_or_else(|| {
2356 EvalError::ParseError("binding missing attrpath".to_string())
2357 })?;
2358 let value_expr = apv.value().ok_or_else(|| {
2359 EvalError::ParseError("binding missing value".to_string())
2360 })?;
2361 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2362 let tail_is_dynamic =
2372 path_attrs.len() > 1 && attrs_have_dynamic(&path_attrs[1..]);
2373 let head_key = match eval_attr_maybe_null(&path_attrs[0], env)? {
2374 Some(k) => k,
2375 None => continue,
2377 };
2378 if tail_is_dynamic && attrs.get(&head_key).is_none() {
2379 let value =
2380 build_deferred_tail_attr(&path_attrs[1..], &value_expr, env);
2381 attrs.insert(head_key, value);
2382 continue;
2383 }
2384 if tail_is_dynamic {
2398 if let Some(existing) = attrs.get(&head_key).cloned() {
2399 let merged = merge_deferred_dynamic_tail(
2400 existing,
2401 &path_attrs[1..],
2402 &value_expr,
2403 env,
2404 )?;
2405 attrs.insert(head_key, merged);
2406 continue;
2407 }
2408 }
2409 let mut path_keys: Vec<String> = {
2412 let mut v = Vec::with_capacity(path_attrs.len());
2413 v.push(head_key);
2414 let mut skip = false;
2415 for a in &path_attrs[1..] {
2416 match eval_attr_maybe_null(a, env)? {
2417 Some(k) => v.push(k),
2418 None => { skip = true; break; }
2419 }
2420 }
2421 if skip { v.clear(); }
2422 v
2423 };
2424 if path_keys.is_empty() { continue; }
2426 if path_keys.len() == 1 {
2427 let key = path_keys.pop().unwrap();
2428 let value = maybe_thunk(&value_expr, env, false, None);
2431 if matches!(attrs.get(&key), Some(Value::Thunk(_))) {
2456 let existing = attrs.get(&key).cloned().unwrap();
2457 let forced_existing = force_value(&existing)?;
2458 attrs.insert(key.clone(), forced_existing);
2459 }
2460 if matches!(attrs.get(&key), Some(Value::Attrs(_))) {
2461 let forced = force_value(&value)?;
2462 merge_nested_insert(&mut attrs, key, forced);
2463 } else {
2464 attrs.insert(key, value);
2465 }
2466 } else {
2467 let key = path_keys[0].clone();
2468 let value = build_nested_attr(&path_keys[1..], &value_expr, env)?;
2469 if matches!(attrs.get(&key), Some(Value::Thunk(_))) {
2483 let existing = attrs.get(&key).cloned().unwrap();
2484 let forced = force_value(&existing)?;
2485 attrs.insert(key.clone(), forced);
2486 }
2487 merge_nested_insert(&mut attrs, key, value);
2488 }
2489 }
2490 ast::Entry::Inherit(inherit) => {
2491 eval_inherit(&inherit, env, &mut attrs, None, None)?;
2492 }
2493 }
2494 }
2495 }
2496
2497 attach_attrset_positions(set, &mut attrs, env);
2503
2504 Ok(Value::Attrs(Rc::new(attrs)))
2505}
2506
2507fn eval_inherit(
2508 inherit: &ast::Inherit,
2509 env: &Env,
2510 attrs: &mut NixAttrs,
2511 bind_env: Option<&mut Env>,
2512 mut thunks: Option<&mut Vec<(String, Thunk)>>,
2513) -> Result<(), EvalError> {
2514 if let Some(from) = inherit.from() {
2515 let source_expr = from
2535 .expr()
2536 .ok_or_else(|| EvalError::ParseError("inherit from missing expr".to_string()))?;
2537 let source_thunk = Thunk::new_suspended(source_expr, env.clone());
2541 let mut be = bind_env;
2542 for attr in inherit.attrs() {
2543 let name = eval_attr(&attr, env)?;
2544 let thunk = Thunk::new_inherit_select(source_thunk.clone(), name.clone());
2545 let value = Value::Thunk(thunk.clone());
2546 attrs.insert(name.clone(), value.clone());
2547 if let Some(ref mut e) = be {
2548 e.bind(name.clone(), value);
2549 }
2550 if let Some(ref mut t) = thunks {
2551 t.push((name, thunk));
2552 }
2553 }
2554 } else {
2555 let mut be = bind_env;
2571 for attr in inherit.attrs() {
2572 let name = eval_attr(&attr, env)?;
2573 let sym = crate::value::intern(&name);
2574 let value = if let Some(v) = env.lookup_fast(sym, &name) {
2575 v
2576 } else if let Some((scope_cache, scope_value)) =
2577 env.innermost_with_scope()
2578 {
2579 Value::Thunk(Thunk::new_with_ident(
2580 SmolStr::from(name.as_str()),
2581 scope_cache,
2582 scope_value,
2583 env.clone(),
2584 ))
2585 } else {
2586 return Err(EvalError::UndefinedVar(format!(
2587 "'{name}'{}",
2588 eval_file_ctx()
2589 )));
2590 };
2591 attrs.insert(name.clone(), value.clone());
2592 if let Some(ref mut e) = be {
2593 e.bind(name, value);
2594 }
2595 }
2596 }
2597 Ok(())
2598}
2599
2600fn build_nested_attr(
2601 path: &[String],
2602 expr: &ast::Expr,
2603 env: &Env,
2604) -> Result<Value, EvalError> {
2605 if path.is_empty() {
2606 return Ok(maybe_thunk(expr, env, false, None));
2611 }
2612 let key = path[0].clone();
2613 let inner = build_nested_attr(&path[1..], expr, env)?;
2614 let mut attrs = NixAttrs::new();
2615 attrs.insert(key, inner);
2616 Ok(Value::Attrs(Rc::new(attrs)))
2617}
2618
2619fn attr_is_dynamic(attr: &ast::Attr) -> bool {
2640 match attr {
2641 ast::Attr::Dynamic(_) => true,
2642 ast::Attr::Str(s) => s
2645 .normalized_parts()
2646 .iter()
2647 .any(|p| matches!(p, InterpolPart::Interpolation(_))),
2648 ast::Attr::Ident(_) => false,
2649 }
2650}
2651
2652fn attrs_have_dynamic(attrs: &[ast::Attr]) -> bool {
2660 attrs.iter().any(attr_is_dynamic)
2661}
2662
2663fn build_deferred_tail_attr(
2676 tail: &[ast::Attr],
2677 value_expr: &ast::Expr,
2678 env: &Env,
2679) -> Value {
2680 let tail: Vec<ast::Attr> = tail.to_vec();
2681 let value_expr = value_expr.clone();
2682 let env = env.clone();
2683 Value::Thunk(Thunk::new_native(move || {
2684 build_tail_attrs_now(&tail, &value_expr, &env)
2685 }))
2686}
2687
2688fn build_tail_attrs_now(
2709 tail: &[ast::Attr],
2710 value_expr: &ast::Expr,
2711 env: &Env,
2712) -> Result<Value, EvalError> {
2713 if tail.is_empty() {
2714 return Ok(maybe_thunk(value_expr, env, false, None));
2715 }
2716 if std::env::var_os("SUI_M26_TAILTRACE").is_some() {
2717 let t: String = tail[0].syntax().text().to_string().chars().take(40).collect();
2718 eprintln!("[M26 TAIL-RESOLVE] forcing dynamic tail key `{t}`");
2719 if attrs_have_dynamic(&tail[..1]) {
2720 crate::trace::dump_force_stack_ids();
2721 }
2722 }
2723 let key = match eval_attr_maybe_null(&tail[0], env)? {
2724 Some(k) => k,
2725 None => return Ok(Value::Attrs(Rc::new(NixAttrs::new()))),
2728 };
2729 let inner = if tail.len() == 1 {
2735 maybe_thunk(value_expr, env, false, None)
2736 } else {
2737 build_deferred_tail_attr(&tail[1..], value_expr, env)
2738 };
2739 let mut attrs = NixAttrs::new();
2740 attrs.insert(key, inner);
2741 Ok(Value::Attrs(Rc::new(attrs)))
2742}
2743
2744fn merge_deferred_dynamic_tail(
2762 existing: Value,
2763 tail: &[ast::Attr],
2764 value_expr: &ast::Expr,
2765 env: &Env,
2766) -> Result<Value, EvalError> {
2767 debug_assert!(!tail.is_empty());
2770
2771 if attr_is_dynamic(&tail[0]) {
2776 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2777 return Ok(lazy_overlay_merge(existing, deferred));
2778 }
2779
2780 let key = match eval_attr_maybe_null(&tail[0], env)? {
2783 Some(k) => k,
2784 None => return Ok(existing),
2785 };
2786
2787 let existing_forced = force_value(&existing)?;
2791 let mut base = match existing_forced {
2792 Value::Attrs(a) => (*a).clone(),
2793 _ => {
2798 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2799 return Ok(deferred);
2800 }
2801 };
2802
2803 let child_existing = base.get(&key).cloned();
2805 let new_child = match child_existing {
2806 Some(child) if tail.len() > 1 => {
2807 merge_deferred_dynamic_tail(child, &tail[1..], value_expr, env)?
2809 }
2810 Some(child) => {
2811 let leaf = maybe_thunk(value_expr, env, false, None);
2814 lazy_overlay_merge(child, leaf)
2815 }
2816 None if tail.len() > 1 => {
2817 build_deferred_tail_attr(&tail[1..], value_expr, env)
2821 }
2822 None => maybe_thunk(value_expr, env, false, None),
2823 };
2824 base.insert(key, new_child);
2825 Ok(Value::Attrs(Rc::new(base)))
2826}
2827
2828fn lazy_overlay_merge(left: Value, right: Value) -> Value {
2835 match (&left, &right) {
2836 (Value::Attrs(la), Value::Attrs(_)) => {
2837 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2838 let mut merged = (**la).clone();
2839 if let Value::Attrs(ra) = &right {
2840 for (k, v) in ra.iter_unsorted() {
2844 merge_nested_insert(&mut merged, k.clone(), v.clone());
2845 }
2846 }
2847 Value::Attrs(Rc::new(merged))
2848 }
2849 _ => {
2850 Value::Thunk(Thunk::new_native(move || {
2854 let lf = force_value(&left)?;
2855 let rf = force_value(&right)?;
2856 let la = lf.as_attrs()?;
2857 let ra = rf.as_attrs()?;
2858 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2859 let mut merged = (*la).clone();
2860 for (k, v) in ra.iter_unsorted() {
2861 merge_nested_insert(&mut merged, k.clone(), v.clone());
2862 }
2863 Ok(Value::Attrs(Rc::new(merged)))
2864 }))
2865 }
2866 }
2867}
2868
2869fn build_nested_attr_thunk(
2877 path: &[String],
2878 expr: &ast::Expr,
2879 env: &Env,
2880 thunks: &mut Vec<(String, Thunk)>,
2881) -> Value {
2882 if path.is_empty() {
2883 let thunk = Thunk::new_suspended(expr.clone(), env.clone());
2884 let val = Value::Thunk(thunk.clone());
2885 thunks.push((String::new(), thunk));
2886 return val;
2887 }
2888 let key = path[0].clone();
2889 let inner = build_nested_attr_thunk(&path[1..], expr, env, thunks);
2890 let mut attrs = NixAttrs::new();
2891 attrs.insert(key, inner);
2892 Value::Attrs(Rc::new(attrs))
2893}
2894
2895fn merge_nested_insert(target: &mut NixAttrs, key: String, value: Value) {
2902 let existing = match target.get(&key) {
2906 Some(e) => e.clone(),
2907 None => {
2908 target.insert(key, value);
2909 return;
2910 }
2911 };
2912 let value = match value {
2936 Value::Thunk(_) => match force_value(&value) {
2937 Ok(v @ Value::Attrs(_)) => v,
2938 _ => value,
2939 },
2940 other => other,
2941 };
2942 if !matches!(value, Value::Attrs(_)) {
2943 target.insert(key, value);
2944 return;
2945 }
2946 let existing_concrete = match &existing {
2949 Value::Attrs(_) => existing.clone(),
2950 Value::Thunk(_) => match force_value(&existing) {
2951 Ok(v @ Value::Attrs(_)) => v,
2952 _ => {
2953 target.insert(key, value);
2954 return;
2955 }
2956 },
2957 _ => {
2958 target.insert(key, value);
2959 return;
2960 }
2961 };
2962 let mut existing_attrs = match existing_concrete {
2966 Value::Attrs(a) => (*a).clone(),
2967 _ => unreachable!(),
2968 };
2969 let new_attrs = match value {
2970 Value::Attrs(ref a) => a,
2971 _ => unreachable!(),
2972 };
2973 for (k, v) in new_attrs.iter_unsorted() {
2974 merge_nested_insert(&mut existing_attrs, k.clone(), v.clone());
2975 }
2976 target.insert(key, Value::Attrs(Rc::new(existing_attrs)));
2977}
2978
2979fn eval_entries<N: HasEntry + AstNode>(node: &N, env: &mut Env) -> Result<(), EvalError> {
2981 for entry in node.entries() {
2982 match entry {
2983 ast::Entry::AttrpathValue(apv) => {
2984 let attrpath = apv.attrpath().ok_or_else(|| {
2985 EvalError::ParseError("binding missing attrpath".to_string())
2986 })?;
2987 let value_expr = apv.value().ok_or_else(|| {
2988 EvalError::ParseError("binding missing value".to_string())
2989 })?;
2990 let mut path_keys: Vec<String> = attrpath
2991 .attrs()
2992 .map(|a| eval_attr(&a, env))
2993 .collect::<Result<_, _>>()?;
2994 if path_keys.len() == 1 {
2995 let key = path_keys.pop().unwrap();
2996 let value = eval_expr(&value_expr, env)?;
2997 env.bind(key, value);
2998 }
2999 }
3001 ast::Entry::Inherit(inherit) => {
3002 if let Some(from) = inherit.from() {
3003 let source_expr = from.expr().ok_or_else(|| {
3004 EvalError::ParseError("inherit from missing expr".to_string())
3005 })?;
3006 let source = force_value(&eval_expr(&source_expr, env)?)?;
3007 let source_attrs = source.as_attrs()?;
3008 for attr in inherit.attrs() {
3009 let name = eval_attr(&attr, env)?;
3010 let value = source_attrs
3011 .get(&name)
3012 .cloned()
3013 .ok_or_else(|| EvalError::AttrNotFound(
3014 format!("'{name}' in inherit{}", eval_file_ctx()),
3015 ))?;
3016 env.bind(name, value);
3017 }
3018 } else {
3019 for attr in inherit.attrs() {
3020 let name = eval_attr(&attr, env)?;
3021 let value = env
3022 .lookup(&name)
3023 .ok_or_else(|| EvalError::UndefinedVar(
3024 format!("'{name}'{}", eval_file_ctx()),
3025 ))?;
3026 env.bind(name, value);
3027 }
3028 }
3029 }
3030 }
3031 }
3032 Ok(())
3033}
3034
3035fn eval_binop(
3036 op: ast::BinOpKind,
3037 lhs: &ast::Expr,
3038 rhs: &ast::Expr,
3039 env: &Env,
3040) -> Result<Value, EvalError> {
3041 match op {
3043 ast::BinOpKind::And => {
3044 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3045 if !l {
3046 return Ok(Value::Bool(false));
3047 }
3048 return eval_expr(rhs, env);
3049 }
3050 ast::BinOpKind::Or => {
3051 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3052 if l {
3053 return Ok(Value::Bool(true));
3054 }
3055 return eval_expr(rhs, env);
3056 }
3057 ast::BinOpKind::Implication => {
3058 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3059 if !l {
3060 return Ok(Value::Bool(true));
3061 }
3062 return eval_expr(rhs, env);
3063 }
3064 _ => {}
3065 }
3066
3067 let lc = force_concrete(&eval_expr(lhs, env)?)?;
3068 let rc = force_concrete(&eval_expr(rhs, env)?)?;
3069 let l = lc.into_value();
3076 let r = rc.into_value();
3077
3078 match op {
3079 ast::BinOpKind::Add => match (&l, &r) {
3080 (Value::Int(a), Value::Int(b)) => a
3081 .checked_add(*b)
3082 .map(Value::Int)
3083 .ok_or_else(|| int_overflow("adding", *a, '+', *b)),
3084 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
3085 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
3086 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a + *b as f64)),
3087 (Value::String(a), Value::String(b)) => {
3088 let mut ctx = a.context.clone();
3089 ctx.merge(&b.context);
3090 let mut s = String::with_capacity(a.chars.len() + b.chars.len());
3098 s.push_str(&a.chars);
3099 s.push_str(&b.chars);
3100 Ok(Value::String(Rc::new(NixString::with_context(s, ctx))))
3101 }
3102 (Value::Path(a), Value::String(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}{}", b.chars).as_str())))),
3103 (Value::Path(a), Value::Path(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}/{b}").as_str())))),
3104 (Value::Attrs(_), _) | (_, Value::Attrs(_)) => {
3106 let (ls, lctx) = l.coerce_to_string()?;
3107 let (rs, rctx) = r.coerce_to_string()?;
3108 let mut ctx = lctx;
3109 ctx.merge(&rctx);
3110 Ok(Value::String(Rc::new(NixString::with_context(
3111 format!("{ls}{rs}"),
3112 ctx,
3113 ))))
3114 }
3115 _ => Err(EvalError::op_type("add", l.type_name(), r.type_name())),
3116 },
3117 ast::BinOpKind::Sub => num_op(
3118 &l,
3119 &r,
3120 |a, b| a.checked_sub(b),
3121 |a, b| a - b,
3122 |a, b| int_overflow("subtracting", a, '-', b),
3123 ),
3124 ast::BinOpKind::Mul => num_op(
3125 &l,
3126 &r,
3127 |a, b| a.checked_mul(b),
3128 |a, b| a * b,
3129 |a, b| int_overflow("multiplying", a, '*', b),
3130 ),
3131 ast::BinOpKind::Div => {
3132 let rhs_is_zero = match &r {
3141 Value::Int(0) => true,
3142 Value::Float(f) => *f == 0.0,
3143 _ => false,
3144 };
3145 if rhs_is_zero {
3146 return Err(EvalError::DivisionByZero);
3147 }
3148 num_op(
3149 &l,
3150 &r,
3151 |a, b| a.checked_div(b),
3152 |a, b| a / b,
3153 |a, b| int_overflow("dividing", a, '/', b),
3154 )
3155 }
3156 ast::BinOpKind::Equal => Ok(Value::Bool(l == r)),
3157 ast::BinOpKind::NotEqual => Ok(Value::Bool(l != r)),
3158 ast::BinOpKind::Less => compare(&l, &r, |o| o == std::cmp::Ordering::Less),
3159 ast::BinOpKind::LessOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Greater),
3160 ast::BinOpKind::More => compare(&l, &r, |o| o == std::cmp::Ordering::Greater),
3161 ast::BinOpKind::MoreOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Less),
3162 ast::BinOpKind::Update => {
3163 let la = l.to_attrs()?;
3164 let ra = r.to_attrs()?;
3165 Ok(Value::Attrs(Rc::new(la.overlay(ra))))
3167 }
3168 ast::BinOpKind::Concat => {
3169 crate::value::concat_lists(l, r.as_list()?)
3179 }
3180 ast::BinOpKind::And | ast::BinOpKind::Or | ast::BinOpKind::Implication => {
3181 unreachable!("handled above")
3182 }
3183 ast::BinOpKind::PipeRight | ast::BinOpKind::PipeLeft => {
3184 Err(EvalError::NotImplemented("pipe operators".to_string()))
3185 }
3186 }
3187}
3188
3189#[inline]
3194fn int_overflow(verb: &str, a: i64, sym: char, b: i64) -> EvalError {
3195 EvalError::Abort(format!("integer overflow in {verb} {a} {sym} {b}"))
3196}
3197
3198fn num_op(
3199 l: &Value,
3200 r: &Value,
3201 int_op: impl Fn(i64, i64) -> Option<i64>,
3202 float_op: impl Fn(f64, f64) -> f64,
3203 overflow: impl Fn(i64, i64) -> EvalError,
3204) -> Result<Value, EvalError> {
3205 match (l, r) {
3206 (Value::Int(a), Value::Int(b)) => {
3207 int_op(*a, *b).map(Value::Int).ok_or_else(|| overflow(*a, *b))
3208 }
3209 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(float_op(*a, *b))),
3210 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(float_op(*a as f64, *b))),
3211 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(float_op(*a, *b as f64))),
3212 _ => Err(EvalError::op_type("perform arithmetic on", l.type_name(), r.type_name())),
3213 }
3214}
3215
3216fn compare(
3217 l: &Value,
3218 r: &Value,
3219 pred: impl Fn(std::cmp::Ordering) -> bool,
3220) -> Result<Value, EvalError> {
3221 let ord = match (l, r) {
3222 (Value::Int(a), Value::Int(b)) => a.cmp(b),
3223 (Value::Float(a), Value::Float(b)) => {
3224 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
3225 }
3226 (Value::Int(a), Value::Float(b)) => (*a as f64)
3227 .partial_cmp(b)
3228 .unwrap_or(std::cmp::Ordering::Equal),
3229 (Value::Float(a), Value::Int(b)) => a
3230 .partial_cmp(&(*b as f64))
3231 .unwrap_or(std::cmp::Ordering::Equal),
3232 (Value::String(a), Value::String(b)) => a.chars.cmp(&b.chars),
3233 _ => {
3234 return Err(EvalError::op_type("compare", l.type_name(), r.type_name()));
3235 }
3236 };
3237 Ok(Value::Bool(pred(ord)))
3238}
3239
3240pub fn apply_and_force(func: Value, arg: Value) -> Result<Value, EvalError> {
3254 force_value(&apply(func, arg)?)
3255}
3256
3257pub fn apply(func: Value, arg: Value) -> Result<Value, EvalError> {
3258 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || apply_inner(func, arg))
3259}
3260
3261fn apply_inner(func: Value, arg: Value) -> Result<Value, EvalError> {
3262 crate::perf::inc(crate::perf::Counter::Apply);
3263 let func = force_concrete(&func)?.into_value();
3264 match func {
3265 Value::Lambda(closure) => {
3266 if crate::perf::enabled() {
3268 APPLY_SITES.with(|sites| {
3269 let file = closure.env.eval_file()
3270 .map(|p| p.display().to_string())
3271 .unwrap_or_else(|| "<eval>".into());
3272 let param_name = match &closure.param {
3274 rnix::ast::Param::IdentParam(ip) => ip.ident().map(|i| ident_text(&i)).unwrap_or_default(),
3275 rnix::ast::Param::Pattern(pat) => {
3276 let mut names: Vec<String> = pat.pat_entries()
3277 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3278 .take(3)
3279 .collect();
3280 if pat.pat_entries().count() > 3 { names.push("...".to_string()); }
3281 format!("{{{}}}", names.join(","))
3282 }
3283 };
3284 let key = format!("{}:{}", file.rsplit_once("-source/").map_or(file.as_str(), |(_,s)| s), param_name);
3285 *sites.borrow_mut().entry(key).or_insert(0u64) += 1;
3286 });
3287 }
3288 let mut call_env = closure.env.child();
3289 let _file_guard = push_eval_frame(closure.env.eval_file().cloned());
3295 let _trace = push_nix_trace_lambda(&closure.env);
3301 match &closure.param {
3302 rnix::ast::Param::IdentParam(_) => {
3303 bind_param(&closure.param, &arg, &mut call_env)?;
3306 }
3307 rnix::ast::Param::Pattern(_) => {
3308 let forced_arg = force_concrete(&arg)?.into_value();
3310 bind_param(&closure.param, &forced_arg, &mut call_env)?;
3311 }
3312 }
3313 eval_expr(&closure.body, &call_env)
3314 }
3315 Value::Builtin(b) => {
3316 let _trace = push_nix_trace(format!("while calling the '{}' builtin", b.name));
3317 if builtin_takes_lazy_arg(&b.name) {
3327 (b.func)(&[arg])
3328 } else {
3329 let forced_arg = force_value(&arg)?;
3330 (b.func)(&[forced_arg])
3331 }
3332 }
3333 Value::Attrs(ref attrs) => {
3334 if let Some(functor) = attrs.get("__functor") {
3335 let functor = force_value(functor)?;
3336 let partial = apply(functor, func.clone())?;
3338 apply(partial, arg)
3339 } else if crate::value::in_promise_eval() {
3340 Ok(Value::Null)
3345 } else {
3346 Err(EvalError::type_error(
3347 format!("cannot call {} (missing __functor){}", func.type_name(), eval_file_ctx()),
3348 ))
3349 }
3350 }
3351 _ if crate::value::in_promise_eval() => {
3352 Ok(Value::Null)
3357 }
3358 _ => Err(EvalError::type_error(
3359 format!("cannot call {}{}", func.type_name(), eval_file_ctx()),
3360 )),
3361 }
3362}
3363
3364static SUI_BATCH_BIND: std::sync::LazyLock<bool> =
3374 std::sync::LazyLock::new(|| std::env::var_os("SUI_BATCH_BIND").is_some());
3375
3376fn bind_param(param: &ast::Param, arg: &Value, env: &mut Env) -> Result<(), EvalError> {
3377 match param {
3378 ast::Param::IdentParam(ip) => {
3379 let ident = ip
3380 .ident()
3381 .ok_or_else(|| EvalError::ParseError("ident param missing ident".to_string()))?;
3382 let name = ident_text(&ident);
3383 env.bind(name, arg.clone());
3384 }
3385 ast::Param::Pattern(pat) => {
3386 let attrs = arg.as_attrs()?;
3387
3388 if let Some(pat_bind) = pat.pat_bind()
3390 && let Some(ident) = pat_bind.ident()
3391 {
3392 let name = ident_text(&ident);
3393 env.bind(name, arg.clone());
3394 }
3395
3396 let has_ellipsis = pat.ellipsis_token().is_some();
3397 let entries: Vec<ast::PatEntry> = pat.pat_entries().collect();
3398
3399 let mut default_thunks: Vec<Thunk> = Vec::new();
3406 let use_batch = *SUI_BATCH_BIND;
3416 let mut pairs: Vec<(String, Value)> =
3417 if use_batch { Vec::with_capacity(entries.len()) } else { Vec::new() };
3418
3419 for entry in &entries {
3420 let ident = entry.ident().ok_or_else(|| {
3421 EvalError::ParseError("pat entry missing ident".to_string())
3422 })?;
3423 let name = ident_text(&ident);
3424 let value = if let Some(v) = attrs.get(&name) {
3425 v.clone()
3426 } else if let Some(default_expr) = entry.default() {
3427 let thunk = Thunk::new_suspended(
3433 ast::Expr::cast(default_expr.syntax().clone()).unwrap(),
3434 env.clone(),
3435 );
3436 default_thunks.push(thunk.clone());
3437 Value::Thunk(thunk)
3438 } else {
3439 return Err(EvalError::type_error(
3440 format!("missing argument '{name}'{}", eval_file_ctx()),
3441 ));
3442 };
3443 if use_batch {
3444 pairs.push((name, value));
3445 } else {
3446 env.bind(name, value);
3447 }
3448 }
3449 if use_batch {
3450 env.bind_many(pairs);
3451 }
3452
3453 for thunk in &default_thunks {
3455 thunk.update_env(env);
3456 }
3457
3458 if !has_ellipsis {
3459 let entry_names: std::collections::HashSet<String> = entries
3460 .iter()
3461 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3462 .collect();
3463 for key in attrs.keys() {
3464 if !entry_names.contains(key.as_str()) {
3465 return Err(EvalError::type_error(
3466 format!("unexpected argument '{key}'{}", eval_file_ctx()),
3467 ));
3468 }
3469 }
3470 }
3471 }
3472 }
3473 Ok(())
3474}
3475
3476#[cfg(test)]
3477mod tests {
3478 use super::*;
3479
3480 fn ev(input: &str) -> Value {
3481 eval(input).unwrap()
3482 }
3483
3484 #[test]
3491 fn is_self_recursive_binding_ignores_attribute_names() {
3492 fn expr(s: &str) -> ast::Expr {
3493 rnix::Root::parse(s).tree().expr().expect("parse")
3494 }
3495 assert!(!is_self_recursive_binding(&expr("lhs.placeholder"), "placeholder"));
3497 assert!(!is_self_recursive_binding(&expr("{ placeholder = 1; }"), "placeholder"));
3498 assert!(!is_self_recursive_binding(
3499 &expr("if lhs.placeholder == rhs.placeholder then lhs.placeholder else null"),
3500 "placeholder",
3501 ));
3502 assert!(is_self_recursive_binding(&expr("placeholder + 1"), "placeholder"));
3504 assert!(is_self_recursive_binding(
3505 &expr("if placeholder then 1 else 2"),
3506 "placeholder"
3507 ));
3508 }
3509
3510 #[test]
3514 fn maybe_thunk_eager_constant_str_is_byte_identical() {
3515 fn expr(s: &str) -> ast::Expr {
3516 rnix::Root::parse(s).tree().expr().expect("parse")
3517 }
3518 let env = Env::new();
3519 let v = maybe_thunk(&expr(r#""abc""#), &env, false, None);
3521 assert!(matches!(v, Value::String(_)), "constant str should be eager, got {v:?}");
3522 assert_eq!(force_value(&v).unwrap(), Value::string("abc"));
3523 let vi = maybe_thunk(&expr(r#""a${b}c""#), &env, false, None);
3525 assert!(matches!(vi, Value::Thunk(_)), "interpolated str must stay thunked");
3526 }
3527
3528 #[test]
3532 fn eval_pure_constant_arg_classification() {
3533 fn expr(s: &str) -> ast::Expr {
3534 rnix::Root::parse(s).tree().expr().expect("parse")
3535 }
3536 assert!(eval_pure_constant_arg(&expr("42")).is_some());
3538 assert!(eval_pure_constant_arg(&expr("3.14")).is_some());
3539 assert!(eval_pure_constant_arg(&expr(r#""const""#)).is_some());
3540 assert!(eval_pure_constant_arg(&expr("/abs/path")).is_some());
3541 assert!(eval_pure_constant_arg(&expr(r#""a${b}c""#)).is_none(), "interpolated str");
3543 assert!(eval_pure_constant_arg(&expr("true")).is_none(), "bool is an ident");
3546 assert!(eval_pure_constant_arg(&expr("x")).is_none(), "ident (with-scope force)");
3547 assert!(eval_pure_constant_arg(&expr("a.b")).is_none(), "select (fixpoint)");
3548 assert!(eval_pure_constant_arg(&expr("f x")).is_none(), "apply (may throw)");
3549 assert!(eval_pure_constant_arg(&expr("1 + 1")).is_none(), "binop (may throw)");
3550 assert!(eval_pure_constant_arg(&expr("throw \"x\"")).is_none(), "throw stays lazy");
3551 }
3552
3553 #[test]
3557 fn ignored_throwing_arg_stays_lazy() {
3558 assert_eq!(ev(r#"(x: 7) (throw "boom")"#), Value::Int(7));
3559 assert_eq!(ev(r#"(x: 7) "const""#), Value::Int(7));
3561 assert_eq!(ev(r#"(x: x) "used""#), Value::string("used"));
3563 }
3564
3565 #[test]
3566 fn eval_int() { assert_eq!(ev("42"), Value::Int(42)); }
3567
3568 #[test]
3569 fn eval_float() { assert_eq!(ev("3.14"), Value::Float(3.14)); }
3570
3571 #[test]
3572 fn eval_string() { assert_eq!(ev(r#""hello""#), Value::string("hello")); }
3573
3574 #[test]
3575 fn eval_bool() { assert_eq!(ev("true"), Value::Bool(true)); }
3576
3577 #[test]
3578 fn eval_null() { assert_eq!(ev("null"), Value::Null); }
3579
3580 #[test]
3581 fn eval_arithmetic() {
3582 assert_eq!(ev("1 + 2"), Value::Int(3));
3583 assert_eq!(ev("10 - 3"), Value::Int(7));
3584 assert_eq!(ev("2 * 3"), Value::Int(6));
3585 assert_eq!(ev("10 / 3"), Value::Int(3));
3586 }
3587
3588 #[test]
3589 fn eval_precedence() {
3590 assert_eq!(ev("1 + 2 * 3"), Value::Int(7));
3591 assert_eq!(ev("(1 + 2) * 3"), Value::Int(9));
3592 }
3593
3594 #[test]
3595 fn eval_comparison() {
3596 assert_eq!(ev("1 == 1"), Value::Bool(true));
3597 assert_eq!(ev("1 == 2"), Value::Bool(false));
3598 assert_eq!(ev("1 < 2"), Value::Bool(true));
3599 assert_eq!(ev("2 <= 2"), Value::Bool(true));
3600 }
3601
3602 #[test]
3603 fn eval_logic() {
3604 assert_eq!(ev("true && false"), Value::Bool(false));
3605 assert_eq!(ev("true || false"), Value::Bool(true));
3606 assert_eq!(ev("!true"), Value::Bool(false));
3607 }
3608
3609 #[test]
3610 fn eval_string_concat() {
3611 assert_eq!(ev(r#""hello" + " " + "world""#), Value::string("hello world"));
3612 }
3613
3614 #[test]
3615 fn eval_if() {
3616 assert_eq!(ev("if true then 1 else 2"), Value::Int(1));
3617 assert_eq!(ev("if false then 1 else 2"), Value::Int(2));
3618 }
3619
3620 #[test]
3621 fn eval_let() {
3622 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
3623 assert_eq!(ev("let x = 1; y = 2; in x + y"), Value::Int(3));
3624 }
3625
3626 #[test]
3627 fn eval_let_dotted_simple() {
3628 assert_eq!(ev("let a.b = 1; a.c = 2; in a.b + a.c"), Value::Int(3));
3630 }
3631
3632 #[test]
3633 fn eval_let_dotted_deep() {
3634 assert_eq!(ev("let a.b.c = 1; in a.b.c"), Value::Int(1));
3636 }
3637
3638 #[test]
3639 fn eval_let_dotted_mixed() {
3640 assert_eq!(
3642 ev("let a.x = 1; b = 2; a.y = 3; in a.x + a.y + b"),
3643 Value::Int(6),
3644 );
3645 }
3646
3647 #[test]
3648 fn eval_let_dotted_produces_attrset() {
3649 let v = ev("let a.b = 1; a.c = 2; in a");
3651 if let Value::Attrs(attrs) = v {
3652 assert_eq!(attrs.get("b"), Some(&Value::Int(1)));
3653 assert_eq!(attrs.get("c"), Some(&Value::Int(2)));
3654 } else {
3655 panic!("expected Attrs, got {v:?}");
3656 }
3657 }
3658
3659 #[test]
3667 fn dynamic_inner_attr_key_is_lazy_on_sibling_read() {
3668 assert_eq!(
3670 ev(r#"let s = { a.${throw "KEYFORCED"} = 7; other = 9; }; in s.other"#),
3671 Value::Int(9),
3672 );
3673 }
3674
3675 #[test]
3676 fn dynamic_inner_attr_key_resolves_on_head_demand() {
3677 let v = ev(r#"let u = "bob"; s = { homes.${u} = 7; }; in s.homes"#);
3679 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3680 assert_eq!(attrs.get("bob"), Some(&Value::Int(7)));
3681 } else {
3682 panic!("expected Attrs");
3683 }
3684 }
3685
3686 #[test]
3687 fn dynamic_inner_attr_key_merges_with_static_sibling() {
3688 let v = ev(r#"let u = "x"; s = { a.${u} = 1; a.b = 2; }; in s.a"#);
3690 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3691 assert_eq!(attrs.get("x"), Some(&Value::Int(1)));
3692 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3693 } else {
3694 panic!("expected Attrs");
3695 }
3696 }
3697
3698 #[test]
3699 fn dynamic_inner_attr_key_null_skips_binding() {
3700 let v = ev(
3703 r#"let c = true; s = { a.${if c then null else "n"} = 5; b = 1; }; in s.b"#,
3704 );
3705 assert_eq!(v, Value::Int(1));
3706 }
3707
3708 #[test]
3714 fn interpolated_string_attr_key_is_lazy_on_sibling_read() {
3715 assert_eq!(
3716 ev(r#"let s = { a."p/${throw "KEYFORCED"}" = 7; other = 9; }; in s.other"#),
3717 Value::Int(9),
3718 );
3719 }
3720
3721 #[test]
3722 fn interpolated_string_attr_key_resolves_on_head_demand() {
3723 let v = ev(r#"let u = "bob"; s = { homes."u/${u}" = 7; }; in s.homes"#);
3725 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3726 assert_eq!(attrs.get("u/bob"), Some(&Value::Int(7)));
3727 } else {
3728 panic!("expected Attrs");
3729 }
3730 }
3731
3732 #[test]
3733 fn purely_literal_string_attr_key_stays_eager_static() {
3734 let v = ev(r#"let s = { a."foo bar" = 1; a.b = 2; }; in s.a"#);
3737 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3738 assert_eq!(attrs.get("foo bar"), Some(&Value::Int(1)));
3739 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3740 } else {
3741 panic!("expected Attrs");
3742 }
3743 }
3744
3745 #[test]
3748 fn dynamic_tail_key_under_colliding_head_is_lazy() {
3749 let v = ev(
3752 r#"let s = { sd.services.x = 1; sd.tmpfiles.${throw "KEYFORCED"}.d = 2; }; in s.sd.services.x"#,
3753 );
3754 assert_eq!(v, Value::Int(1));
3755 }
3756
3757 #[test]
3758 fn dynamic_tail_key_under_colliding_head_resolves_and_merges() {
3759 let v = ev(
3762 r#"let k = "z"; s = { sd.services.x = 1; sd.tmpfiles.${k}.d = 2; }; in s.sd"#,
3763 );
3764 let sd = force_value(&v).unwrap();
3765 if let Value::Attrs(sd_attrs) = &sd {
3766 let services = force_value(sd_attrs.get("services").unwrap()).unwrap();
3768 if let Value::Attrs(a) = &services {
3769 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3770 } else { panic!("expected services attrs"); }
3771 let tmpfiles = force_value(sd_attrs.get("tmpfiles").unwrap()).unwrap();
3773 if let Value::Attrs(a) = &tmpfiles {
3774 let z = force_value(a.get("z").unwrap()).unwrap();
3775 if let Value::Attrs(zd) = &z {
3776 assert_eq!(force_value(zd.get("d").unwrap()).unwrap(), Value::Int(2));
3777 } else { panic!("expected z attrs"); }
3778 } else { panic!("expected tmpfiles attrs"); }
3779 } else {
3780 panic!("expected sd attrs");
3781 }
3782 }
3783
3784 #[test]
3793 fn with_namespace_is_lazy_on_body_whnf() {
3794 let v = ev(r#"builtins.attrNames (with (throw "WITH-FORCED"); { a = 1; b = 2; })"#);
3795 if let Value::List(items) = force_value(&v).unwrap() {
3796 let names: Vec<String> = items
3797 .iter()
3798 .map(|i| match force_value(i).unwrap() {
3799 Value::String(s) => s.as_str().to_string(),
3800 other => panic!("expected string, got {}", other.type_name()),
3801 })
3802 .collect();
3803 assert_eq!(names, vec!["a".to_string(), "b".to_string()]);
3804 } else {
3805 panic!("expected list");
3806 }
3807 }
3808
3809 #[test]
3810 fn with_namespace_forces_only_on_fallthrough() {
3811 assert_eq!(ev(r#"with { x = 42; }; x"#), Value::Int(42));
3815 assert_eq!(ev(r#"let x = 7; in with (throw "NS"); x"#), Value::Int(7));
3818 }
3819
3820 #[test]
3831 fn dotted_fullset_leaf_deep_merges_with_deeper_sibling() {
3832 let v = ev(r#"{ o.a = { x = 1; }; o.a.y = 2; }.o.a"#);
3833 if let Value::Attrs(a) = force_value(&v).unwrap() {
3834 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3835 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3836 } else {
3837 panic!("expected attrs");
3838 }
3839 }
3840
3841 #[test]
3842 fn dotted_fullset_leaf_deep_merge_reverse_order() {
3843 let v = ev(r#"{ o.a.y = 2; o.a = { x = 1; }; }.o.a"#);
3846 if let Value::Attrs(a) = force_value(&v).unwrap() {
3847 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3848 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3849 } else {
3850 panic!("expected attrs");
3851 }
3852 }
3853
3854 #[test]
3855 fn dotted_fullset_leaf_merge_preserves_leaf_laziness() {
3856 assert_eq!(ev(r#"{ o.a = { x = throw "X-NEVER"; }; o.a.y = 2; }.o.a.y"#), Value::Int(2));
3860 }
3861
3862 #[test]
3863 fn eval_nested_let() {
3864 assert_eq!(ev("let a = 1; b = let c = 2; in c; in a + b"), Value::Int(3));
3865 }
3866
3867 #[test]
3868 fn eval_lambda() {
3869 assert_eq!(ev("(x: x + 1) 41"), Value::Int(42));
3870 }
3871
3872 #[test]
3873 fn eval_lambda_multi_arg() {
3874 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
3875 }
3876
3877 #[test]
3878 fn eval_list() {
3879 let v = ev("[1 2 3]");
3880 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]));
3881 }
3882
3883 #[test]
3884 fn eval_list_concat() {
3885 let v = ev("[1 2] ++ [3 4]");
3886 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]));
3887 }
3888
3889 #[test]
3890 fn eval_attrset() {
3891 let v = ev("{ a = 1; b = 2; }");
3892 if let Value::Attrs(attrs) = v {
3893 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3894 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3895 } else {
3896 panic!("expected attrset");
3897 }
3898 }
3899
3900 #[test]
3901 fn eval_select() {
3902 assert_eq!(ev("{ a = 42; }.a"), Value::Int(42));
3903 }
3904
3905 #[test]
3906 fn eval_select_or() {
3907 assert_eq!(ev("{ a = 42; }.b or 0"), Value::Int(0));
3908 }
3909
3910 #[test]
3911 fn eval_has_attr() {
3912 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
3913 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
3914 }
3915
3916 #[test]
3917 fn eval_update() {
3918 let v = ev("{ a = 1; b = 2; } // { b = 3; c = 4; }");
3919 if let Value::Attrs(attrs) = v {
3920 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3921 assert_eq!(attrs.get("b"), Some(&Value::Int(3)));
3922 assert_eq!(attrs.get("c"), Some(&Value::Int(4)));
3923 } else {
3924 panic!("expected attrset");
3925 }
3926 }
3927
3928 #[test]
3929 fn eval_with() {
3930 assert_eq!(ev("with { x = 42; }; x"), Value::Int(42));
3931 }
3932
3933 #[test]
3934 fn eval_assert() {
3935 assert_eq!(ev("assert true; 42"), Value::Int(42));
3936 assert!(eval("assert false; 42").is_err());
3937 }
3938
3939 #[test]
3940 fn eval_formals() {
3941 assert_eq!(ev("({ a, b }: a + b) { a = 1; b = 2; }"), Value::Int(3));
3942 }
3943
3944 #[test]
3945 fn eval_formals_default() {
3946 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 1; }"), Value::Int(11));
3947 }
3948
3949 #[test]
3950 fn eval_formals_ellipsis() {
3951 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; }"), Value::Int(1));
3952 }
3953
3954 #[test]
3955 fn eval_named_formals() {
3956 assert_eq!(ev("(args @ { a }: args.a) { a = 42; }"), Value::Int(42));
3957 }
3958
3959 #[test]
3960 fn eval_rec_attrset() {
3961 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
3962 }
3963
3964 #[test]
3965 fn eval_negation() {
3966 assert_eq!(ev("-42"), Value::Int(-42));
3967 }
3968
3969 #[test]
3970 fn eval_float_arithmetic() {
3971 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
3972 assert_eq!(ev("1 + 1.5"), Value::Float(2.5));
3973 }
3974
3975 #[test]
3976 fn eval_division_by_zero() {
3977 assert!(eval("1 / 0").is_err());
3978 }
3979
3980 #[test]
3981 fn eval_builtins_available() {
3982 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
3983 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
3984 }
3985
3986 #[test]
3987 fn eval_builtins_length() {
3988 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
3989 }
3990
3991 #[test]
3992 fn eval_builtins_head_tail() {
3993 assert_eq!(ev("builtins.head [1 2 3]"), Value::Int(1));
3994 assert_eq!(ev("builtins.length (builtins.tail [1 2 3])"), Value::Int(2));
3995 }
3996
3997 #[test]
3998 fn eval_builtins_add() {
3999 assert_eq!(ev("builtins.add 1 2"), Value::Int(3));
4000 }
4001
4002 #[test]
4003 fn eval_builtins_to_string() {
4004 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
4005 }
4006
4007 #[test]
4008 fn eval_implication() {
4009 assert_eq!(ev("false -> true"), Value::Bool(true));
4010 assert_eq!(ev("true -> false"), Value::Bool(false));
4011 assert_eq!(ev("true -> true"), Value::Bool(true));
4012 }
4013
4014 #[test]
4017 fn eval_error_undefined_variable() {
4018 let result = eval("nonexistent");
4019 assert!(result.is_err());
4020 let msg = format!("{}", result.unwrap_err());
4021 assert!(msg.contains("undefined variable"));
4022 }
4023
4024 #[test]
4025 fn eval_error_type_mismatch_arithmetic() {
4026 let result = eval(r#"1 + "hello""#);
4027 assert!(result.is_err());
4028 let msg = format!("{}", result.unwrap_err());
4029 assert!(msg.contains("cannot add") || msg.contains("type"));
4030 }
4031
4032 #[test]
4033 fn eval_error_unexpected_argument() {
4034 let result = eval("({ a }: a) { a = 1; b = 2; }");
4035 assert!(result.is_err());
4036 let msg = format!("{}", result.unwrap_err());
4037 assert!(msg.contains("unexpected argument"));
4038 }
4039
4040 #[test]
4041 fn eval_error_missing_required_argument() {
4042 let result = eval("({ a, b }: a + b) { a = 1; }");
4043 assert!(result.is_err());
4044 let msg = format!("{}", result.unwrap_err());
4045 assert!(msg.contains("missing argument"));
4046 }
4047
4048 #[test]
4049 fn eval_builtins_attr_names_sorted() {
4050 let v = ev("builtins.attrNames { z = 1; a = 2; m = 3; }");
4051 assert_eq!(
4053 v,
4054 Value::list(vec![
4055 Value::string("a"),
4056 Value::string("m"),
4057 Value::string("z"),
4058 ]),
4059 );
4060 }
4061
4062 #[test]
4063 fn eval_builtins_attr_values() {
4064 let v = ev("builtins.attrValues { a = 1; b = 2; }");
4065 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2)]));
4067 }
4068
4069 #[test]
4070 fn eval_builtins_is_null() {
4071 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
4072 assert_eq!(ev("builtins.isNull 1"), Value::Bool(false));
4073 }
4074
4075 #[test]
4076 fn eval_builtins_is_int() {
4077 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
4078 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
4079 }
4080
4081 #[test]
4082 fn eval_builtins_is_bool() {
4083 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
4084 assert_eq!(ev("builtins.isBool 0"), Value::Bool(false));
4085 }
4086
4087 #[test]
4088 fn eval_builtins_is_string() {
4089 assert_eq!(ev(r#"builtins.isString "hi""#), Value::Bool(true));
4090 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
4091 }
4092
4093 #[test]
4094 fn eval_builtins_is_list() {
4095 assert_eq!(ev("builtins.isList [1 2]"), Value::Bool(true));
4096 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
4097 }
4098
4099 #[test]
4100 fn eval_builtins_is_attrs() {
4101 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
4102 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
4103 }
4104
4105 #[test]
4106 fn eval_builtins_string_length() {
4107 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
4108 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
4109 }
4110
4111 #[test]
4112 fn eval_builtins_to_json_roundtrip() {
4113 assert_eq!(
4115 ev(r#"builtins.fromJSON (builtins.toJSON 42)"#),
4116 Value::Int(42),
4117 );
4118 assert_eq!(
4119 ev(r#"builtins.fromJSON (builtins.toJSON [1 2 3])"#),
4120 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4121 );
4122 }
4123
4124 #[test]
4125 fn eval_builtins_from_json() {
4126 assert_eq!(
4127 ev(r#"builtins.fromJSON "{\"a\": 1}""#),
4128 {
4129 let mut attrs = NixAttrs::new();
4130 attrs.insert("a".to_string(), Value::Int(1));
4131 Value::Attrs(Rc::new(attrs))
4132 },
4133 );
4134 assert_eq!(ev(r#"builtins.fromJSON "null""#), Value::Null);
4135 assert_eq!(ev(r#"builtins.fromJSON "true""#), Value::Bool(true));
4136 }
4137
4138 #[test]
4139 fn eval_nested_function_application() {
4140 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
4142 assert_eq!(ev("((x: y: x + y) 1) 2"), Value::Int(3));
4144 }
4145
4146 #[test]
4147 fn eval_recursive_let() {
4148 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4149 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4150 }
4151
4152 #[test]
4153 fn eval_string_comparison() {
4154 assert_eq!(ev(r#""a" < "b""#), Value::Bool(true));
4155 assert_eq!(ev(r#""b" < "a""#), Value::Bool(false));
4156 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4157 assert_eq!(ev(r#""abc" != "def""#), Value::Bool(true));
4158 }
4159
4160 #[test]
4161 fn eval_list_in_attrset() {
4162 let v = ev("{ x = [1 2 3]; }.x");
4163 assert_eq!(
4164 v,
4165 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4166 );
4167 }
4168
4169 #[test]
4170 fn eval_nested_attrset_select() {
4171 assert_eq!(ev("{ a = { b = 42; }; }.a.b"), Value::Int(42));
4172 }
4173
4174 #[test]
4175 fn eval_let_shadows_outer() {
4176 assert_eq!(
4177 ev("let x = 1; in let x = 2; in x"),
4178 Value::Int(2),
4179 );
4180 }
4181
4182 #[test]
4183 fn eval_with_provides_scope() {
4184 assert_eq!(
4186 ev("with { x = 42; y = 10; }; x + y"),
4187 Value::Int(52),
4188 );
4189 }
4190
4191 #[test]
4192 fn eval_list_equality() {
4193 assert_eq!(ev("[1 2] == [1 2]"), Value::Bool(true));
4194 assert_eq!(ev("[1 2] == [1 3]"), Value::Bool(false));
4195 }
4196
4197 #[test]
4198 fn eval_attrset_equality() {
4199 assert_eq!(ev("{ a = 1; } == { a = 1; }"), Value::Bool(true));
4200 assert_eq!(ev("{ a = 1; } == { a = 2; }"), Value::Bool(false));
4201 }
4202
4203 #[test]
4208 fn literal_int_large_zero_negative() {
4209 assert_eq!(ev("9223372036854775807"), Value::Int(i64::MAX));
4211 assert_eq!(ev("0"), Value::Int(0));
4213 assert_eq!(ev("-1"), Value::Int(-1));
4215 assert_eq!(ev("-999999"), Value::Int(-999999));
4216 }
4217
4218 #[test]
4219 fn literal_float_small_large() {
4220 assert_eq!(ev("0.001"), Value::Float(0.001));
4221 assert_eq!(ev("999999.999"), Value::Float(999999.999));
4222 assert_eq!(ev("1.0e3"), Value::Float(1000.0));
4224 assert_eq!(ev("1.5e2"), Value::Float(150.0));
4225 }
4226
4227 #[test]
4228 fn literal_string_empty_and_escapes() {
4229 assert_eq!(ev(r#""""#), Value::string(""));
4230 assert_eq!(ev(r#""hello\nworld""#), Value::string("hello\nworld"));
4232 assert_eq!(ev(r#""tab\there""#), Value::string("tab\there"));
4233 }
4234
4235 #[test]
4236 fn literal_multiline_string() {
4237 assert_eq!(
4239 ev("''hello''"),
4240 Value::string("hello"),
4241 );
4242 assert_eq!(
4244 ev("''\n line1\n line2\n''"),
4245 Value::string("line1\nline2\n"),
4246 );
4247 }
4248
4249 #[test]
4250 fn literal_paths() {
4251 assert_eq!(ev("./foo"), Value::Path(Box::new(SmolStr::from("./foo"))));
4253 assert_eq!(ev("/nix/store/abc"), Value::Path(Box::new(SmolStr::from("/nix/store/abc"))));
4255 assert_eq!(ev("~/myfile"), Value::Path(Box::new(SmolStr::from("~/myfile"))));
4257 }
4258
4259 #[test]
4269 fn interp_path_abs_splices_and_types_path() {
4270 let v = ev(r#"let x = "foo"; in /a/${x}/b"#);
4272 assert_eq!(v, Value::Path(Box::new(SmolStr::from("/a/foo/b"))));
4273 }
4274
4275 #[test]
4276 fn interp_path_abs_multi_and_slash_in_value() {
4277 assert_eq!(
4279 ev(r#"let a = "x"; b = "y/z"; in /p/${a}/${b}.nix"#),
4280 Value::Path(Box::new(SmolStr::from("/p/x/y/z.nix"))),
4281 );
4282 }
4283
4284 #[test]
4285 fn interp_path_abs_normalizes_double_slash_seam() {
4286 assert_eq!(
4289 ev(r#"/bar/${/tmp/foo}"#),
4290 Value::Path(Box::new(SmolStr::from("/bar/tmp/foo"))),
4291 );
4292 }
4293
4294 #[test]
4295 fn interp_path_rel_resolves_against_eval_dir() {
4296 let _g = push_eval_file(std::path::PathBuf::from("/tmp/example/default.nix"));
4300 assert_eq!(
4301 ev(r#"let x = "foo"; in ./${x}.nix"#),
4302 Value::Path(Box::new(SmolStr::from("/tmp/example/foo.nix"))),
4303 );
4304 }
4305
4306 #[test]
4307 fn interp_path_rel_no_eval_dir_keeps_relative_text() {
4308 assert_eq!(
4311 ev(r#"let x = "foo"; in ./${x}.nix"#),
4312 Value::Path(Box::new(SmolStr::from("./foo.nix"))),
4313 );
4314 }
4315
4316 #[test]
4317 fn interp_path_home_splices_leading_tilde_preserved() {
4318 assert_eq!(
4322 ev(r#"let x = "foo"; in ~/${x}/bar"#),
4323 Value::Path(Box::new(SmolStr::from("~/foo/bar"))),
4324 );
4325 }
4326
4327 #[test]
4328 fn interp_path_non_interpolated_still_raw() {
4329 assert_eq!(ev("/a/b/c"), Value::Path(Box::new(SmolStr::from("/a/b/c"))));
4332 assert_eq!(ev("~/plain"), Value::Path(Box::new(SmolStr::from("~/plain"))));
4333 }
4334
4335 #[test]
4336 fn literal_null_true_false_standalone() {
4337 assert_eq!(ev("null"), Value::Null);
4338 assert_eq!(ev("true"), Value::Bool(true));
4339 assert_eq!(ev("false"), Value::Bool(false));
4340 }
4341
4342 #[test]
4347 fn op_arithmetic_int() {
4348 assert_eq!(ev("100 + 200"), Value::Int(300));
4349 assert_eq!(ev("50 - 30"), Value::Int(20));
4350 assert_eq!(ev("7 * 8"), Value::Int(56));
4351 assert_eq!(ev("17 / 3"), Value::Int(5)); }
4353
4354 #[test]
4355 fn op_arithmetic_float() {
4356 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
4357 assert_eq!(ev("5.0 - 1.5"), Value::Float(3.5));
4358 assert_eq!(ev("2.0 * 3.0"), Value::Float(6.0));
4359 assert_eq!(ev("7.0 / 2.0"), Value::Float(3.5));
4360 }
4361
4362 #[test]
4363 fn op_arithmetic_mixed_int_float() {
4364 assert_eq!(ev("1 + 2.5"), Value::Float(3.5));
4366 assert_eq!(ev("2.5 + 1"), Value::Float(3.5));
4367 assert_eq!(ev("2 * 1.5"), Value::Float(3.0));
4369 assert_eq!(ev("5.5 - 2"), Value::Float(3.5));
4371 }
4372
4373 #[test]
4374 fn op_string_concat() {
4375 assert_eq!(ev(r#""foo" + "bar""#), Value::string("foobar"));
4376 assert_eq!(ev(r#""" + "x""#), Value::string("x"));
4377 assert_eq!(ev(r#""a" + "" + "b""#), Value::string("ab"));
4378 }
4379
4380 #[test]
4381 fn op_path_concat() {
4382 assert_eq!(ev(r#"./foo + "/bar""#), Value::Path(Box::new(SmolStr::from("./foo/bar"))));
4384 assert_eq!(ev("./a + ./b"), Value::Path(Box::new(SmolStr::from("./a/./b"))));
4386 }
4387
4388 #[test]
4389 fn op_comparison_ints() {
4390 assert_eq!(ev("1 < 2"), Value::Bool(true));
4391 assert_eq!(ev("2 < 1"), Value::Bool(false));
4392 assert_eq!(ev("2 > 1"), Value::Bool(true));
4393 assert_eq!(ev("1 > 2"), Value::Bool(false));
4394 assert_eq!(ev("2 <= 2"), Value::Bool(true));
4395 assert_eq!(ev("3 <= 2"), Value::Bool(false));
4396 assert_eq!(ev("2 >= 2"), Value::Bool(true));
4397 assert_eq!(ev("1 >= 2"), Value::Bool(false));
4398 }
4399
4400 #[test]
4401 fn op_comparison_floats() {
4402 assert_eq!(ev("1.5 < 2.5"), Value::Bool(true));
4403 assert_eq!(ev("2.5 > 1.5"), Value::Bool(true));
4404 assert_eq!(ev("1.5 <= 1.5"), Value::Bool(true));
4405 assert_eq!(ev("1.5 >= 1.5"), Value::Bool(true));
4406 }
4407
4408 #[test]
4409 fn op_comparison_strings() {
4410 assert_eq!(ev(r#""apple" < "banana""#), Value::Bool(true));
4411 assert_eq!(ev(r#""banana" > "apple""#), Value::Bool(true));
4412 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4413 assert_eq!(ev(r#""abc" != "xyz""#), Value::Bool(true));
4414 assert_eq!(ev(r#""abc" <= "abd""#), Value::Bool(true));
4415 assert_eq!(ev(r#""abc" >= "abb""#), Value::Bool(true));
4416 }
4417
4418 #[test]
4419 fn op_equality_various_types() {
4420 assert_eq!(ev("null == null"), Value::Bool(true));
4421 assert_eq!(ev("true == true"), Value::Bool(true));
4422 assert_eq!(ev("false == false"), Value::Bool(true));
4423 assert_eq!(ev("true == false"), Value::Bool(false));
4424 assert_eq!(ev("1 == 1"), Value::Bool(true));
4425 assert_eq!(ev("1 != 2"), Value::Bool(true));
4426 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
4428 assert_eq!(ev("null == false"), Value::Bool(false));
4429 }
4430
4431 #[test]
4432 fn op_logic_short_circuit() {
4433 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
4435 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
4437 }
4438
4439 #[test]
4440 fn op_logic_full() {
4441 assert_eq!(ev("true && true"), Value::Bool(true));
4442 assert_eq!(ev("true && false"), Value::Bool(false));
4443 assert_eq!(ev("false && true"), Value::Bool(false));
4444 assert_eq!(ev("false && false"), Value::Bool(false));
4445 assert_eq!(ev("true || true"), Value::Bool(true));
4446 assert_eq!(ev("true || false"), Value::Bool(true));
4447 assert_eq!(ev("false || true"), Value::Bool(true));
4448 assert_eq!(ev("false || false"), Value::Bool(false));
4449 assert_eq!(ev("!true"), Value::Bool(false));
4450 assert_eq!(ev("!false"), Value::Bool(true));
4451 }
4452
4453 #[test]
4454 fn op_implication_truth_table() {
4455 assert_eq!(ev("false -> false"), Value::Bool(true));
4457 assert_eq!(ev("false -> true"), Value::Bool(true));
4458 assert_eq!(ev("true -> true"), Value::Bool(true));
4460 assert_eq!(ev("true -> false"), Value::Bool(false));
4461 }
4462
4463 #[test]
4464 fn op_implication_short_circuit() {
4465 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
4467 }
4468
4469 #[test]
4470 fn op_update_merge() {
4471 let v = ev("{ a = 1; } // { b = 2; }");
4472 if let Value::Attrs(attrs) = v {
4473 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4474 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4475 } else {
4476 panic!("expected attrs");
4477 }
4478 }
4479
4480 #[test]
4481 fn op_update_right_wins() {
4482 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4483 }
4484
4485 #[test]
4486 fn op_list_concat() {
4487 assert_eq!(
4488 ev("[1 2] ++ [3 4]"),
4489 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]),
4490 );
4491 assert_eq!(ev("[] ++ [1]"), Value::list(vec![Value::Int(1)]));
4493 assert_eq!(ev("[1] ++ []"), Value::list(vec![Value::Int(1)]));
4494 }
4495
4496 #[test]
4497 fn op_has_attr_present_and_absent() {
4498 assert_eq!(ev("{ x = 1; y = 2; } ? x"), Value::Bool(true));
4499 assert_eq!(ev("{ x = 1; } ? z"), Value::Bool(false));
4500 assert_eq!(ev("{} ? anything"), Value::Bool(false));
4501 }
4502
4503 #[test]
4504 fn op_unary_negate() {
4505 assert_eq!(ev("-42"), Value::Int(-42));
4506 assert_eq!(ev("-3.14"), Value::Float(-3.14));
4507 assert_eq!(ev("- -5"), Value::Int(5));
4509 }
4510
4511 #[test]
4516 fn control_if_true_branch() {
4517 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
4518 }
4519
4520 #[test]
4521 fn control_if_false_branch() {
4522 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
4523 }
4524
4525 #[test]
4526 fn control_if_nested() {
4527 assert_eq!(
4528 ev("if true then (if false then 1 else 2) else 3"),
4529 Value::Int(2),
4530 );
4531 assert_eq!(
4532 ev("if false then 1 else (if true then 2 else 3)"),
4533 Value::Int(2),
4534 );
4535 }
4536
4537 #[test]
4538 fn control_assert_passing() {
4539 assert_eq!(ev("assert 1 == 1; 42"), Value::Int(42));
4540 assert_eq!(ev("assert true; true"), Value::Bool(true));
4541 }
4542
4543 #[test]
4544 fn control_assert_failing() {
4545 assert!(eval("assert false; 42").is_err());
4546 assert!(eval("assert 1 == 2; 42").is_err());
4547 }
4548
4549 #[test]
4550 fn control_with_basic_scope() {
4551 assert_eq!(ev("with { a = 1; b = 2; }; a + b"), Value::Int(3));
4552 }
4553
4554 #[test]
4555 fn control_with_lexical_precedence() {
4556 assert_eq!(
4558 ev("let x = 10; in with { x = 99; }; x"),
4559 Value::Int(10),
4560 );
4561 }
4562
4563 #[test]
4564 fn control_with_nested() {
4565 assert_eq!(
4566 ev("with { a = 1; }; with { b = 2; }; a + b"),
4567 Value::Int(3),
4568 );
4569 }
4570
4571 #[test]
4572 fn control_with_lazy_fix_self() {
4573 let result = eval(
4578 "let fix = f: let x = f x; in x; in fix (self: with self; { a = 1; b = a + 1; })"
4579 );
4580 assert!(result.is_ok(), "fix with self should work: {:?}", result);
4581 if let Ok(Value::Attrs(attrs)) = result {
4582 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4583 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4584 } else {
4585 panic!("expected Attrs, got {:?}", result);
4586 }
4587 }
4588
4589 #[test]
4590 fn control_with_lazy_fix_self_lib_pattern() {
4591 let result = eval(r#"
4594 let fix = f: let x = f x; in x;
4595 in (fix (self: with self; {
4596 lib = { version = "1.0"; };
4597 hello = "hello ${lib.version}";
4598 })).hello
4599 "#);
4600 assert!(result.is_ok(), "nixpkgs-style lib pattern: {:?}", result);
4601 assert_eq!(
4602 result.unwrap(),
4603 Value::String(Rc::new(NixString::plain("hello 1.0"))),
4604 );
4605 }
4606
4607 #[test]
4608 fn control_with_non_attrset_errors() {
4609 let result = eval("with 42; 1");
4611 assert_eq!(result.unwrap(), Value::Int(1));
4614 }
4615
4616 #[test]
4617 fn control_with_non_attrset_lookup_falls_through() {
4618 let result = eval("let x = 1; in with 42; x");
4621 assert_eq!(result.unwrap(), Value::Int(1));
4622 }
4623
4624 #[test]
4625 fn control_let_simple_and_multiple() {
4626 assert_eq!(ev("let x = 5; in x"), Value::Int(5));
4627 assert_eq!(ev("let x = 1; y = 2; z = 3; in x + y + z"), Value::Int(6));
4628 }
4629
4630 #[test]
4631 fn control_let_shadow_outer() {
4632 assert_eq!(
4633 ev("let x = 1; in let x = 2; in x"),
4634 Value::Int(2),
4635 );
4636 }
4637
4638 #[test]
4639 fn control_let_recursive_reference() {
4640 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4641 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4642 }
4643
4644 #[test]
4645 fn control_nested_let_expression() {
4646 assert_eq!(
4647 ev("let a = let b = 1; in b; in a"),
4648 Value::Int(1),
4649 );
4650 assert_eq!(
4651 ev("let a = let b = 10; in b + 5; in a * 2"),
4652 Value::Int(30),
4653 );
4654 }
4655
4656 #[test]
4661 fn func_identity_lambda() {
4662 assert_eq!(ev("(x: x) 42"), Value::Int(42));
4663 assert_eq!(ev(r#"(x: x) "hello""#), Value::string("hello"));
4664 }
4665
4666 #[test]
4667 fn func_curried_two_args() {
4668 assert_eq!(ev("(x: y: x + y) 3 4"), Value::Int(7));
4669 }
4670
4671 #[test]
4672 fn func_curried_three_args() {
4673 assert_eq!(ev("(a: b: c: a + b + c) 1 2 3"), Value::Int(6));
4674 }
4675
4676 #[test]
4677 fn func_formals_basic() {
4678 assert_eq!(ev("({ a, b }: a + b) { a = 3; b = 7; }"), Value::Int(10));
4679 }
4680
4681 #[test]
4682 fn func_formals_with_defaults() {
4683 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; }"), Value::Int(15));
4684 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; b = 20; }"), Value::Int(25));
4686 }
4687
4688 #[test]
4689 fn func_formals_with_ellipsis() {
4690 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; c = 3; }"), Value::Int(1));
4691 }
4692
4693 #[test]
4694 fn func_named_formals_at_before() {
4695 assert_eq!(
4697 ev("(args @ { a, b }: args.a + args.b) { a = 3; b = 4; }"),
4698 Value::Int(7),
4699 );
4700 }
4701
4702 #[test]
4703 fn func_named_formals_at_after() {
4704 assert_eq!(
4706 ev("({ a, b } @ args: args.a + args.b) { a = 10; b = 20; }"),
4707 Value::Int(30),
4708 );
4709 }
4710
4711 #[test]
4712 fn func_nested_application() {
4713 assert_eq!(ev("((x: y: x * y) 3) 4"), Value::Int(12));
4715 }
4716
4717 #[test]
4718 fn func_higher_order_map() {
4719 assert_eq!(
4720 ev("builtins.map (x: x * 2) [1 2 3]"),
4721 Value::list(vec![Value::Int(2), Value::Int(4), Value::Int(6)]),
4722 );
4723 }
4724
4725 #[test]
4726 fn func_higher_order_filter() {
4727 assert_eq!(
4728 ev("builtins.filter (x: x > 2) [1 2 3 4 5]"),
4729 Value::list(vec![Value::Int(3), Value::Int(4), Value::Int(5)]),
4730 );
4731 }
4732
4733 #[test]
4734 fn func_higher_order_foldl() {
4735 assert_eq!(
4737 ev("builtins.foldl' (acc: x: acc + x) 0 [1 2 3 4]"),
4738 Value::Int(10),
4739 );
4740 }
4741
4742 #[test]
4743 fn func_as_attrset_value() {
4744 assert_eq!(
4745 ev("let s = { f = x: x + 1; }; in s.f 5"),
4746 Value::Int(6),
4747 );
4748 }
4749
4750 #[test]
4751 fn func_immediate_application() {
4752 assert_eq!(ev("(x: x * x) 7"), Value::Int(49));
4753 }
4754
4755 #[test]
4756 fn func_in_let_binding() {
4757 assert_eq!(
4758 ev("let double = x: x * 2; in double 21"),
4759 Value::Int(42),
4760 );
4761 }
4762
4763 #[test]
4768 fn attrs_empty_set() {
4769 let v = ev("{}");
4770 if let Value::Attrs(attrs) = v {
4771 assert!(attrs.is_empty());
4772 } else {
4773 panic!("expected attrs");
4774 }
4775 }
4776
4777 #[test]
4778 fn attrs_simple() {
4779 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
4780 }
4781
4782 #[test]
4783 fn attrs_nested_access() {
4784 assert_eq!(ev("{ a = { b = { c = 42; }; }; }.a.b.c"), Value::Int(42));
4785 }
4786
4787 #[test]
4788 fn attrs_recursive_set() {
4789 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
4790 }
4791
4792 #[test]
4793 fn attrs_update_disjoint() {
4794 let v = ev("{ a = 1; } // { b = 2; }");
4795 if let Value::Attrs(attrs) = v {
4796 assert_eq!(attrs.len(), 2);
4797 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4798 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4799 } else {
4800 panic!("expected attrs");
4801 }
4802 }
4803
4804 #[test]
4805 fn attrs_update_override() {
4806 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4807 }
4808
4809 #[test]
4810 fn attrs_has_attr_operator() {
4811 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
4812 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
4813 }
4814
4815 #[test]
4816 fn attrs_select_with_default() {
4817 assert_eq!(ev("{ a = 1; }.a or 99"), Value::Int(1));
4818 assert_eq!(ev("{}.missing or 99"), Value::Int(99));
4819 assert_eq!(ev("{ a = 1; }.b or 42"), Value::Int(42));
4820 }
4821
4822 #[test]
4823 fn attrs_nested_attr_path_in_binding() {
4824 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
4826 }
4827
4828 #[test]
4829 fn attrs_inherit_from_scope() {
4830 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.x"), Value::Int(1));
4831 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.y"), Value::Int(2));
4832 }
4833
4834 #[test]
4835 fn attrs_inherit_from_expr() {
4836 assert_eq!(
4837 ev("{ inherit ({ a = 42; b = 10; }) a; }.a"),
4838 Value::Int(42),
4839 );
4840 }
4841
4842 #[test]
4843 fn attrs_dynamic_attr_name() {
4844 assert_eq!(
4845 ev(r#"let name = "x"; in { ${name} = 42; }.x"#),
4846 Value::Int(42),
4847 );
4848 }
4849
4850 #[test]
4851 fn attrs_attr_names_sorted() {
4852 assert_eq!(
4853 ev("builtins.attrNames { z = 1; m = 2; a = 3; }"),
4854 Value::list(vec![
4855 Value::string("a"),
4856 Value::string("m"),
4857 Value::string("z"),
4858 ]),
4859 );
4860 }
4861
4862 #[test]
4863 fn attrs_attr_values_follow_key_order() {
4864 assert_eq!(
4866 ev("builtins.attrValues { c = 3; a = 1; b = 2; }"),
4867 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4868 );
4869 }
4870
4871 #[test]
4872 fn attrs_update_is_shallow() {
4873 assert_eq!(
4875 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a ? x"),
4876 Value::Bool(false),
4877 );
4878 assert_eq!(
4879 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a.y"),
4880 Value::Int(2),
4881 );
4882 }
4883
4884 #[test]
4889 fn list_empty() {
4890 assert_eq!(ev("[]"), Value::list(vec![]));
4891 }
4892
4893 #[test]
4894 fn list_single_element() {
4895 assert_eq!(ev("[1]"), Value::list(vec![Value::Int(1)]));
4896 }
4897
4898 #[test]
4899 fn list_mixed_types() {
4900 assert_eq!(
4901 ev(r#"[1 "two" true null]"#),
4902 Value::list(vec![
4903 Value::Int(1),
4904 Value::string("two"),
4905 Value::Bool(true),
4906 Value::Null,
4907 ]),
4908 );
4909 }
4910
4911 #[test]
4912 fn list_nested() {
4913 assert_eq!(
4914 ev("[[1 2] [3 4]]"),
4915 Value::list(vec![
4916 Value::list(vec![Value::Int(1), Value::Int(2)]),
4917 Value::list(vec![Value::Int(3), Value::Int(4)]),
4918 ]),
4919 );
4920 }
4921
4922 #[test]
4923 fn list_concat_operator() {
4924 assert_eq!(
4925 ev("[1] ++ [2] ++ [3]"),
4926 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4927 );
4928 }
4929
4930 #[test]
4931 fn list_builtins_length() {
4932 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
4933 assert_eq!(ev("builtins.length []"), Value::Int(0));
4934 }
4935
4936 #[test]
4937 fn list_builtins_elem_at() {
4938 assert_eq!(ev("builtins.elemAt [10 20 30] 0"), Value::Int(10));
4939 assert_eq!(ev("builtins.elemAt [10 20 30] 1"), Value::Int(20));
4940 assert_eq!(ev("builtins.elemAt [10 20 30] 2"), Value::Int(30));
4941 }
4942
4943 #[test]
4944 fn list_equality() {
4945 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
4946 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
4947 assert_eq!(ev("[] == []"), Value::Bool(true));
4948 }
4949
4950 #[test]
4955 fn interp_simple_variable() {
4956 assert_eq!(
4957 ev(r#"let name = "world"; in "hello ${name}""#),
4958 Value::string("hello world"),
4959 );
4960 }
4961
4962 #[test]
4963 fn interp_nested_expression() {
4964 assert_eq!(
4965 ev(r#""result: ${builtins.toString (1 + 2)}""#),
4966 Value::string("result: 3"),
4967 );
4968 }
4969
4970 #[test]
4971 fn interp_int_coercion() {
4972 assert_eq!(
4974 ev(r#"let x = 42; in "count: ${builtins.toString x}""#),
4975 Value::string("count: 42"),
4976 );
4977 }
4978
4979 #[test]
4980 fn interp_multiple() {
4981 assert_eq!(
4982 ev(r#"let a = "foo"; b = "bar"; in "${a} and ${b}""#),
4983 Value::string("foo and bar"),
4984 );
4985 }
4986
4987 #[test]
4988 fn interp_in_let() {
4989 assert_eq!(
4990 ev(r#"let x = "world"; in "hello ${x}""#),
4991 Value::string("hello world"),
4992 );
4993 }
4994
4995 #[test]
4996 fn interp_empty_result() {
4997 assert_eq!(
4998 ev(r#"let x = ""; in "a${x}b""#),
4999 Value::string("ab"),
5000 );
5001 }
5002
5003 #[test]
5004 fn interp_path_in_string_context() {
5005 assert!(eval(r#""path: ${./foo-nonexistent-xyz}""#).is_err());
5011 }
5012
5013 #[test]
5014 fn interp_adjacent_interpolations() {
5015 assert_eq!(
5016 ev(r#"let a = "x"; b = "y"; in "${a}${b}""#),
5017 Value::string("xy"),
5018 );
5019 }
5020
5021 #[test]
5026 fn builtins_map_filter_foldl() {
5027 assert_eq!(
5029 ev("builtins.map (x: x + 10) [1 2 3]"),
5030 Value::list(vec![Value::Int(11), Value::Int(12), Value::Int(13)]),
5031 );
5032 assert_eq!(
5034 ev("builtins.filter (x: x > 1) [1 2 3]"),
5035 Value::list(vec![Value::Int(2), Value::Int(3)]),
5036 );
5037 assert_eq!(
5039 ev("builtins.foldl' (a: b: a * b) 1 [2 3 4]"),
5040 Value::Int(24),
5041 );
5042 }
5043
5044 #[test]
5045 fn builtins_map_attrs() {
5046 assert_eq!(
5047 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).a"),
5048 Value::Int(2),
5049 );
5050 assert_eq!(
5051 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).b"),
5052 Value::Int(4),
5053 );
5054 }
5055
5056 #[test]
5057 fn builtins_list_to_attrs() {
5058 assert_eq!(
5059 ev(r#"(builtins.listToAttrs [{ name = "x"; value = 1; } { name = "y"; value = 2; }]).x"#),
5060 Value::Int(1),
5061 );
5062 }
5063
5064 #[test]
5065 fn builtins_list_to_attrs_duplicate_key_first_wins() {
5066 assert_eq!(
5075 ev(r#"(builtins.listToAttrs [{ name = "k"; value = 1; } { name = "k"; value = 2; }]).k"#),
5076 Value::Int(1),
5077 );
5078 }
5079
5080 #[test]
5081 fn builtins_concat_map() {
5082 assert_eq!(
5083 ev("builtins.concatMap (x: [x (x * 2)]) [1 2 3]"),
5084 Value::list(vec![
5085 Value::Int(1), Value::Int(2),
5086 Value::Int(2), Value::Int(4),
5087 Value::Int(3), Value::Int(6),
5088 ]),
5089 );
5090 }
5091
5092 #[test]
5093 fn builtins_concat_lists() {
5094 assert_eq!(
5095 ev("builtins.concatLists [[1 2] [3] [4 5]]"),
5096 Value::list(vec![
5097 Value::Int(1), Value::Int(2), Value::Int(3),
5098 Value::Int(4), Value::Int(5),
5099 ]),
5100 );
5101 }
5102
5103 #[test]
5104 fn builtins_concat_strings_sep() {
5105 assert_eq!(
5106 ev(r#"builtins.concatStringsSep ", " ["a" "b" "c"]"#),
5107 Value::string("a, b, c"),
5108 );
5109 assert_eq!(
5110 ev(r#"builtins.concatStringsSep "" ["x" "y"]"#),
5111 Value::string("xy"),
5112 );
5113 }
5114
5115 #[test]
5116 fn builtins_replace_strings() {
5117 assert_eq!(
5118 ev(r#"builtins.replaceStrings ["o"] ["0"] "foobar""#),
5119 Value::string("f00bar"),
5120 );
5121 assert_eq!(
5122 ev(r#"builtins.replaceStrings ["hello"] ["goodbye"] "hello world""#),
5123 Value::string("goodbye world"),
5124 );
5125 }
5126
5127 #[test]
5128 fn builtins_has_prefix_has_suffix() {
5129 assert_eq!(ev(r#"builtins.hasPrefix "he" "hello""#), Value::Bool(true));
5130 assert_eq!(ev(r#"builtins.hasPrefix "xx" "hello""#), Value::Bool(false));
5131 assert_eq!(ev(r#"builtins.hasSuffix "lo" "hello""#), Value::Bool(true));
5132 assert_eq!(ev(r#"builtins.hasSuffix "xx" "hello""#), Value::Bool(false));
5133 }
5134
5135 #[test]
5136 fn builtins_all_any() {
5137 assert_eq!(ev("builtins.all (x: x > 0) [1 2 3]"), Value::Bool(true));
5138 assert_eq!(ev("builtins.all (x: x > 1) [1 2 3]"), Value::Bool(false));
5139 assert_eq!(ev("builtins.any (x: x > 2) [1 2 3]"), Value::Bool(true));
5140 assert_eq!(ev("builtins.any (x: x > 5) [1 2 3]"), Value::Bool(false));
5141 }
5142
5143 #[test]
5144 fn builtins_sort() {
5145 assert_eq!(
5146 ev("builtins.sort (a: b: a < b) [3 1 2]"),
5147 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5148 );
5149 }
5150
5151 #[test]
5152 fn builtins_remove_attrs() {
5153 let v = ev(r#"builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b" "c"]"#);
5154 if let Value::Attrs(attrs) = v {
5155 assert_eq!(attrs.len(), 1);
5156 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
5157 assert!(attrs.get("b").is_none());
5158 } else {
5159 panic!("expected attrs");
5160 }
5161 }
5162
5163 #[test]
5164 fn builtins_intersect_attrs() {
5165 let v = ev("builtins.intersectAttrs { a = 1; b = 2; } { b = 20; c = 30; }");
5166 if let Value::Attrs(attrs) = v {
5167 assert_eq!(attrs.len(), 1);
5168 assert_eq!(attrs.get("b"), Some(&Value::Int(20)));
5170 } else {
5171 panic!("expected attrs");
5172 }
5173 }
5174
5175 #[test]
5176 fn builtins_type_of_all_types() {
5177 assert_eq!(ev("builtins.typeOf null"), Value::string("null"));
5178 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
5179 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
5180 assert_eq!(ev("builtins.typeOf 3.14"), Value::string("float"));
5181 assert_eq!(ev(r#"builtins.typeOf "hi""#), Value::string("string"));
5182 assert_eq!(ev("builtins.typeOf [1]"), Value::string("list"));
5183 assert_eq!(ev("builtins.typeOf {}"), Value::string("set"));
5184 assert_eq!(ev("builtins.typeOf (x: x)"), Value::string("lambda"));
5185 }
5186
5187 #[test]
5188 fn builtins_is_type_checks() {
5189 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
5190 assert_eq!(ev("builtins.isNull 0"), Value::Bool(false));
5191 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
5192 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
5193 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
5194 assert_eq!(ev("builtins.isBool 1"), Value::Bool(false));
5195 assert_eq!(ev(r#"builtins.isString "x""#), Value::Bool(true));
5196 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
5197 assert_eq!(ev("builtins.isList []"), Value::Bool(true));
5198 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
5199 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
5200 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
5201 assert_eq!(ev("builtins.isFunction (x: x)"), Value::Bool(true));
5202 assert_eq!(ev("builtins.isFunction 1"), Value::Bool(false));
5203 assert_eq!(ev("builtins.isFloat 3.14"), Value::Bool(true));
5204 assert_eq!(ev("builtins.isFloat 1"), Value::Bool(false));
5205 }
5206
5207 #[test]
5208 fn builtins_to_json_from_json_roundtrip() {
5209 assert_eq!(ev("builtins.fromJSON (builtins.toJSON 42)"), Value::Int(42));
5211 assert_eq!(
5213 ev(r#"builtins.fromJSON (builtins.toJSON "hello")"#),
5214 Value::string("hello"),
5215 );
5216 assert_eq!(
5218 ev("builtins.fromJSON (builtins.toJSON [1 2 3])"),
5219 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5220 );
5221 assert_eq!(ev("builtins.fromJSON (builtins.toJSON null)"), Value::Null);
5223 assert_eq!(ev("builtins.fromJSON (builtins.toJSON true)"), Value::Bool(true));
5225 }
5226
5227 #[test]
5228 fn builtins_to_string_various() {
5229 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
5230 assert_eq!(ev("builtins.toString true"), Value::string("1"));
5231 assert_eq!(ev("builtins.toString false"), Value::string(""));
5232 assert_eq!(ev("builtins.toString null"), Value::string(""));
5233 assert_eq!(ev(r#"builtins.toString "hello""#), Value::string("hello"));
5234 }
5235
5236 #[test]
5237 fn builtins_function_args() {
5238 let v = ev("builtins.functionArgs ({ a, b ? 1 }: a)");
5239 if let Value::Attrs(attrs) = v {
5240 assert_eq!(attrs.get("a"), Some(&Value::Bool(false))); assert_eq!(attrs.get("b"), Some(&Value::Bool(true))); } else {
5243 panic!("expected attrs");
5244 }
5245 }
5246
5247 #[test]
5248 fn builtins_gen_list() {
5249 assert_eq!(
5250 ev("builtins.genList (x: x * x) 5"),
5251 Value::list(vec![
5252 Value::Int(0), Value::Int(1), Value::Int(4),
5253 Value::Int(9), Value::Int(16),
5254 ]),
5255 );
5256 assert_eq!(ev("builtins.genList (x: x) 0"), Value::list(vec![]));
5257 }
5258
5259 #[test]
5260 fn builtins_elem() {
5261 assert_eq!(ev("builtins.elem 2 [1 2 3]"), Value::Bool(true));
5262 assert_eq!(ev("builtins.elem 5 [1 2 3]"), Value::Bool(false));
5263 assert_eq!(ev("builtins.elem 1 []"), Value::Bool(false));
5264 }
5265
5266 #[test]
5267 fn builtins_head_tail() {
5268 assert_eq!(ev("builtins.head [10 20 30]"), Value::Int(10));
5269 assert_eq!(
5270 ev("builtins.tail [10 20 30]"),
5271 Value::list(vec![Value::Int(20), Value::Int(30)]),
5272 );
5273 }
5274
5275 #[test]
5276 fn builtins_string_length() {
5277 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
5278 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
5279 assert_eq!(ev(r#"builtins.stringLength "abc def""#), Value::Int(7));
5280 }
5281
5282 #[test]
5283 fn builtins_ceil_floor() {
5284 assert_eq!(ev("builtins.ceil 2.3"), Value::Int(3));
5285 assert_eq!(ev("builtins.ceil 2.0"), Value::Int(2));
5286 assert_eq!(ev("builtins.floor 2.9"), Value::Int(2));
5287 assert_eq!(ev("builtins.floor 2.0"), Value::Int(2));
5288 assert_eq!(ev("builtins.ceil 5"), Value::Int(5));
5290 assert_eq!(ev("builtins.floor 5"), Value::Int(5));
5291 }
5292
5293 #[test]
5294 fn builtins_try_eval() {
5295 let v = ev("builtins.tryEval 42");
5296 if let Value::Attrs(attrs) = v {
5297 assert_eq!(attrs.get("success"), Some(&Value::Bool(true)));
5298 assert_eq!(attrs.get("value"), Some(&Value::Int(42)));
5299 } else {
5300 panic!("expected attrs");
5301 }
5302 }
5303
5304 #[test]
5305 fn builtins_throw() {
5306 let result = eval(r#"builtins.throw "oops""#);
5307 assert!(result.is_err());
5308 let msg = format!("{}", result.unwrap_err());
5309 assert!(msg.contains("oops"));
5310 }
5311
5312 #[test]
5313 fn builtins_seq_deep_seq() {
5314 assert_eq!(ev("builtins.seq 1 42"), Value::Int(42));
5316 assert_eq!(ev("builtins.deepSeq [1 2 3] 99"), Value::Int(99));
5318 }
5319
5320 #[test]
5321 fn builtins_current_system() {
5322 let v = ev("builtins.currentSystem");
5323 if let Value::String(ns) = v {
5324 let s = &ns.chars;
5325 assert!(
5327 s == "aarch64-darwin"
5328 || s == "x86_64-darwin"
5329 || s == "aarch64-linux"
5330 || s == "x86_64-linux",
5331 "unexpected system: {s}",
5332 );
5333 } else {
5334 panic!("expected string");
5335 }
5336 }
5337
5338 #[test]
5343 fn pattern_mkif_like() {
5344 assert_eq!(
5346 ev("(if true then { x = 1; } else {}).x"),
5347 Value::Int(1),
5348 );
5349 let v = ev("if false then { x = 1; } else {}");
5350 if let Value::Attrs(attrs) = v {
5351 assert!(attrs.is_empty());
5352 } else {
5353 panic!("expected attrs");
5354 }
5355 }
5356
5357 #[test]
5358 fn pattern_optional_attrs() {
5359 assert_eq!(
5361 ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in (optionalAttrs true { a = 1; }).a"),
5362 Value::Int(1),
5363 );
5364 let v = ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in optionalAttrs false { a = 1; }");
5365 if let Value::Attrs(attrs) = v {
5366 assert!(attrs.is_empty());
5367 } else {
5368 panic!("expected attrs");
5369 }
5370 }
5371
5372 #[test]
5373 fn pattern_filter_attrs_via_remove() {
5374 assert_eq!(
5376 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]).a"#),
5377 Value::Int(1),
5378 );
5379 assert_eq!(
5380 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]) ? b"#),
5381 Value::Bool(false),
5382 );
5383 }
5384
5385 #[test]
5386 fn pattern_override() {
5387 let v = ev(r#"
5389 let
5390 defaults = { debug = false; port = 8080; host = "localhost"; };
5391 overrides = { debug = true; port = 9090; };
5392 in defaults // overrides
5393 "#);
5394 if let Value::Attrs(attrs) = v {
5395 assert_eq!(attrs.get("debug"), Some(&Value::Bool(true)));
5396 assert_eq!(attrs.get("port"), Some(&Value::Int(9090)));
5397 assert_eq!(attrs.get("host"), Some(&Value::string("localhost")));
5398 } else {
5399 panic!("expected attrs");
5400 }
5401 }
5402
5403 #[test]
5404 fn pattern_functor() {
5405 assert_eq!(
5407 ev("let s = { __functor = self: x: self.value + x; value = 10; }; in s 5"),
5408 Value::Int(15),
5409 );
5410 }
5411
5412 #[test]
5413 fn pattern_platform_check() {
5414 let v = ev(r#"if builtins.currentSystem == "aarch64-darwin" then "arm" else "other""#);
5416 if let Value::String(_) = v {
5418 } else {
5420 panic!("expected string");
5421 }
5422 }
5423
5424 #[test]
5425 fn pattern_recursive_overlay_lambda_structure() {
5426 let v = ev("let overlay = self: super: { pkg = 42; }; in overlay {} {}");
5428 if let Value::Attrs(attrs) = v {
5429 assert_eq!(attrs.get("pkg"), Some(&Value::Int(42)));
5430 } else {
5431 panic!("expected attrs");
5432 }
5433 }
5434
5435 #[test]
5436 fn pattern_call_package_simplified() {
5437 assert_eq!(
5439 ev("let callPkg = f: f { lib = { id = x: x; }; }; lib = { id = x: x; }; in callPkg ({ lib }: lib.id 42)"),
5440 Value::Int(42),
5441 );
5442 }
5443
5444 #[test]
5445 fn pattern_derivation_like_attrset() {
5446 let v = ev(r#"{ type = "derivation"; name = "hello"; system = builtins.currentSystem; builder = "/bin/sh"; }"#);
5447 if let Value::Attrs(attrs) = v {
5448 assert_eq!(attrs.get("type"), Some(&Value::string("derivation")));
5449 assert_eq!(attrs.get("name"), Some(&Value::string("hello")));
5450 assert_eq!(attrs.get("builder"), Some(&Value::string("/bin/sh")));
5451 let system = force_value(attrs.get("system").unwrap()).unwrap();
5453 assert!(matches!(system, Value::String(_)), "expected string, got {system:?}");
5454 } else {
5455 panic!("expected attrs");
5456 }
5457 }
5458
5459 #[test]
5460 fn pattern_module_system_simplified() {
5461 assert_eq!(
5463 ev(r#"
5464 let
5465 eval = m: m { config = {}; lib = { mkDefault = x: x; }; };
5466 in eval ({ config, lib }: { result = lib.mkDefault 42; })
5467 "#),
5468 {
5469 let mut attrs = NixAttrs::new();
5470 attrs.insert("result".to_string(), Value::Int(42));
5471 Value::Attrs(Rc::new(attrs))
5472 },
5473 );
5474 }
5475
5476 #[test]
5481 fn error_undefined_variable() {
5482 let result = eval("nonexistent_var");
5483 assert!(result.is_err());
5484 let msg = format!("{}", result.unwrap_err());
5485 assert!(msg.contains("undefined variable") || msg.contains("nonexistent_var"));
5486 }
5487
5488 #[test]
5489 fn error_type_mismatch_arithmetic() {
5490 let result = eval(r#"1 + "hello""#);
5491 assert!(result.is_err());
5492 }
5493
5494 #[test]
5495 fn error_missing_attribute() {
5496 let result = eval("{}.nonexistent");
5497 assert!(result.is_err());
5498 let msg = format!("{}", result.unwrap_err());
5499 assert!(msg.contains("nonexistent") || msg.contains("not found"));
5500 }
5501
5502 #[test]
5503 fn error_division_by_zero() {
5504 assert!(eval("1 / 0").is_err());
5505 assert!(eval("100 / 0").is_err());
5506 }
5507
5508 #[test]
5509 fn error_missing_required_function_arg() {
5510 let result = eval("({ a, b }: a + b) { a = 1; }");
5511 assert!(result.is_err());
5512 let msg = format!("{}", result.unwrap_err());
5513 assert!(msg.contains("missing argument"));
5514 }
5515
5516 #[test]
5517 fn error_unexpected_function_arg() {
5518 let result = eval("({ a }: a) { a = 1; b = 2; }");
5519 assert!(result.is_err());
5520 let msg = format!("{}", result.unwrap_err());
5521 assert!(msg.contains("unexpected argument"));
5522 }
5523
5524 #[test]
5525 fn error_assertion_failure() {
5526 assert!(eval("assert false; 1").is_err());
5527 assert!(eval("assert 1 == 2; 1").is_err());
5528 }
5529
5530 #[test]
5531 fn error_infinite_recursion() {
5532 let result = eval("let x = x; in x");
5535 assert!(result.is_err());
5536 }
5537
5538 #[test]
5539 fn error_infinite_recursion_via_lambda() {
5540 let result = eval("let f = x: f x; in f 1");
5542 assert!(result.is_err());
5543 let msg = format!("{}", result.unwrap_err());
5544 assert!(
5545 msg.contains("infinite recursion") || msg.contains("eval depth") || msg.contains("undefined"),
5546 );
5547 }
5548
5549 #[test]
5554 fn integration_let_with_function_returning_attrset() {
5555 assert_eq!(
5556 ev("let mkPkg = name: { inherit name; version = 1; }; in (mkPkg \"hello\").name"),
5557 Value::string("hello"),
5558 );
5559 }
5560
5561 #[test]
5562 fn integration_chained_updates() {
5563 assert_eq!(
5564 ev("({ a = 1; } // { b = 2; } // { c = 3; }).c"),
5565 Value::Int(3),
5566 );
5567 }
5568
5569 #[test]
5570 fn integration_map_over_attrnames() {
5571 assert_eq!(
5573 ev(r#"
5574 let
5575 set = { a = 1; b = 2; };
5576 names = builtins.attrNames set;
5577 in builtins.length names
5578 "#),
5579 Value::Int(2),
5580 );
5581 }
5582
5583 #[test]
5584 fn integration_compose_functions() {
5585 assert_eq!(
5587 ev("let compose = f: g: x: f (g x); double = x: x * 2; inc = x: x + 1; in compose double inc 5"),
5588 Value::Int(12), );
5590 }
5591
5592 #[test]
5593 fn integration_recursive_list_building() {
5594 assert_eq!(
5596 ev("builtins.map (x: x * x) (builtins.genList (x: x + 1) 4)"),
5597 Value::list(vec![Value::Int(1), Value::Int(4), Value::Int(9), Value::Int(16)]),
5598 );
5599 }
5600
5601 #[test]
5602 fn integration_attrset_from_list() {
5603 let v = ev(r#"
5605 builtins.listToAttrs (builtins.map (x: { name = x; value = true; }) ["a" "b" "c"])
5606 "#);
5607 if let Value::Attrs(attrs) = v {
5608 assert_eq!(attrs.get("a"), Some(&Value::Bool(true)));
5609 assert_eq!(attrs.get("b"), Some(&Value::Bool(true)));
5610 assert_eq!(attrs.get("c"), Some(&Value::Bool(true)));
5611 } else {
5612 panic!("expected attrs");
5613 }
5614 }
5615
5616 #[test]
5617 fn integration_nested_with_and_let() {
5618 assert_eq!(
5619 ev("let x = 10; in with { y = 20; }; x + y"),
5620 Value::Int(30),
5621 );
5622 }
5623
5624 #[test]
5625 fn integration_complex_pattern_match() {
5626 assert_eq!(
5628 ev("(args @ { a, b ? 5, ... }: a + b + (if args ? c then args.c else 0)) { a = 1; c = 10; }"),
5629 Value::Int(16), );
5631 }
5632
5633 #[test]
5634 fn integration_substring() {
5635 assert_eq!(
5636 ev(r#"builtins.substring 0 5 "hello world""#),
5637 Value::string("hello"),
5638 );
5639 assert_eq!(
5640 ev(r#"builtins.substring 6 5 "hello world""#),
5641 Value::string("world"),
5642 );
5643 }
5644
5645 #[test]
5646 fn integration_has_attr_on_nested() {
5647 assert_eq!(ev("{ a = { b = 1; }; } ? a"), Value::Bool(true));
5649 assert_eq!(
5650 ev("({ a = { b = 1; }; }.a) ? b"),
5651 Value::Bool(true),
5652 );
5653 }
5654
5655 #[test]
5656 fn integration_cat_attrs() {
5657 assert_eq!(
5658 ev(r#"builtins.catAttrs "x" [{ x = 1; } { y = 2; } { x = 3; }]"#),
5659 Value::list(vec![Value::Int(1), Value::Int(3)]),
5660 );
5661 }
5662
5663 #[test]
5664 fn integration_get_attr_builtin() {
5665 assert_eq!(
5666 ev(r#"builtins.getAttr "a" { a = 42; b = 10; }"#),
5667 Value::Int(42),
5668 );
5669 }
5670
5671 #[test]
5672 fn integration_has_attr_builtin() {
5673 assert_eq!(
5674 ev(r#"builtins.hasAttr "a" { a = 1; }"#),
5675 Value::Bool(true),
5676 );
5677 assert_eq!(
5678 ev(r#"builtins.hasAttr "z" { a = 1; }"#),
5679 Value::Bool(false),
5680 );
5681 }
5682
5683 #[test]
5684 fn integration_is_path() {
5685 assert_eq!(ev("builtins.isPath ./foo"), Value::Bool(true));
5686 assert_eq!(ev("builtins.isPath 42"), Value::Bool(false));
5687 }
5688
5689 #[test]
5690 fn integration_builtins_trace() {
5691 assert_eq!(ev(r#"builtins.trace "debug msg" 42"#), Value::Int(42));
5693 }
5694
5695 #[test]
5696 fn integration_builtins_split() {
5697 assert_eq!(
5701 ev(r#"builtins.split "/" "a/b/c""#),
5702 Value::list(vec![
5703 Value::string("a"),
5704 Value::list(vec![]),
5705 Value::string("b"),
5706 Value::list(vec![]),
5707 Value::string("c"),
5708 ]),
5709 );
5710 assert_eq!(
5713 ev(r#"builtins.split "(/)" "a/b/c""#),
5714 Value::list(vec![
5715 Value::string("a"),
5716 Value::list(vec![Value::string("/")]),
5717 Value::string("b"),
5718 Value::list(vec![Value::string("/")]),
5719 Value::string("c"),
5720 ]),
5721 );
5722 }
5723
5724 #[test]
5725 fn integration_builtins_split_no_capture_groups() {
5726 assert_eq!(
5731 ev(r#"builtins.split "-" "aarch64-darwin""#),
5732 Value::list(vec![
5733 Value::string("aarch64"),
5734 Value::list(vec![]),
5735 Value::string("darwin"),
5736 ]),
5737 );
5738 }
5739
5740 #[test]
5741 fn integration_builtins_split_system_string_filter() {
5742 assert_eq!(
5745 ev(r#"builtins.filter builtins.isString (builtins.split "-" "aarch64-darwin")"#),
5746 Value::list(vec![
5747 Value::string("aarch64"),
5748 Value::string("darwin"),
5749 ]),
5750 );
5751 }
5752
5753 #[test]
5754 fn integration_deeply_nested_let() {
5755 assert_eq!(
5757 ev("let a = let b = let c = 10; in c * 2; in b + 1; in a"),
5758 Value::Int(21),
5759 );
5760 }
5761
5762 #[test]
5763 fn integration_if_in_attrset_value() {
5764 assert_eq!(
5765 ev("{ x = if true then 1 else 2; }.x"),
5766 Value::Int(1),
5767 );
5768 }
5769
5770 #[test]
5771 fn integration_lambda_in_list() {
5772 assert_eq!(
5774 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 0) 5"),
5775 Value::Int(6),
5776 );
5777 assert_eq!(
5778 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 1) 5"),
5779 Value::Int(10),
5780 );
5781 }
5782
5783 #[test]
5784 fn integration_nixpkgs_lib_id() {
5785 assert_eq!(
5787 ev("let lib = { id = x: x; const = a: b: a; }; in lib.id 42"),
5788 Value::Int(42),
5789 );
5790 assert_eq!(
5791 ev("let lib = { id = x: x; const = a: b: a; }; in lib.const 1 2"),
5792 Value::Int(1),
5793 );
5794 }
5795
5796 #[test]
5797 fn integration_multiple_inherit() {
5798 assert_eq!(
5799 ev("let a = 1; b = 2; c = 3; in { inherit a b c; }.b"),
5800 Value::Int(2),
5801 );
5802 }
5803
5804 #[test]
5805 fn integration_rec_set_with_builtins() {
5806 assert_eq!(
5807 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
5808 Value::Int(5),
5809 );
5810 }
5811
5812 #[test]
5817 fn functor_simple_callable_attrset() {
5818 assert_eq!(
5819 ev("let s = { __functor = self: x: x + 1; }; in s 41"),
5820 Value::Int(42),
5821 );
5822 }
5823
5824 #[test]
5825 fn functor_with_self_reference() {
5826 assert_eq!(
5827 ev("let s = { __functor = self: x: self.base + x; base = 100; }; in s 23"),
5828 Value::Int(123),
5829 );
5830 }
5831
5832 #[test]
5833 fn functor_updated_attrset() {
5834 assert_eq!(
5836 ev(r#"
5837 let
5838 mk = { __functor = self: x: self.n + x; n = 0; };
5839 s = mk // { n = 50; };
5840 in s 7
5841 "#),
5842 Value::Int(57),
5843 );
5844 }
5845
5846 #[test]
5847 fn functor_error_on_non_callable_attrset() {
5848 let result = eval("let s = { a = 1; }; in s 5");
5850 assert!(result.is_err());
5851 }
5852
5853 #[test]
5858 fn to_string_protocol_in_interpolation() {
5859 assert_eq!(
5860 ev(r#"let s = { __toString = self: "world"; }; in "hello ${s}""#),
5861 Value::string("hello world"),
5862 );
5863 }
5864
5865 #[test]
5866 fn to_string_protocol_accesses_self() {
5867 assert_eq!(
5868 ev(r#"let s = { __toString = self: self.val; val = "abc"; }; in "${s}""#),
5869 Value::string("abc"),
5870 );
5871 }
5872
5873 #[test]
5874 fn to_string_protocol_via_builtin_to_string() {
5875 assert_eq!(
5876 ev(r#"builtins.toString { __toString = self: "via-builtin"; }"#),
5877 Value::string("via-builtin"),
5878 );
5879 }
5880
5881 #[test]
5882 fn to_string_protocol_attrset_without_toString_fails() {
5883 let result = eval(r#""${{}}"#);
5885 assert!(result.is_err());
5886 }
5887
5888 #[test]
5893 fn eval_builtins_concat_strings() {
5894 assert_eq!(
5895 ev(r#"builtins.concatStrings ["a" "b" "c"]"#),
5896 Value::string("abc"),
5897 );
5898 assert_eq!(
5899 ev(r#"builtins.concatStrings []"#),
5900 Value::string(""),
5901 );
5902 }
5903
5904 #[test]
5905 fn eval_builtins_partition() {
5906 let v = ev("builtins.partition (x: x > 3) [1 2 3 4 5]");
5907 if let Value::Attrs(a) = v {
5908 assert_eq!(a.get("right"), Some(&Value::list(vec![Value::Int(4), Value::Int(5)])));
5909 assert_eq!(a.get("wrong"), Some(&Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)])));
5910 } else {
5911 panic!("expected attrs");
5912 }
5913 }
5914
5915 #[test]
5916 fn eval_builtins_group_by() {
5917 let v = ev(r#"builtins.groupBy (x: if x > 0 then "pos" else "neg") [1 (0 - 2) 3 (0 - 4)]"#);
5918 if let Value::Attrs(a) = v {
5919 assert_eq!(a.get("pos"), Some(&Value::list(vec![Value::Int(1), Value::Int(3)])));
5920 assert_eq!(a.get("neg"), Some(&Value::list(vec![Value::Int(-2), Value::Int(-4)])));
5921 } else {
5922 panic!("expected attrs");
5923 }
5924 }
5925
5926 #[test]
5927 fn eval_builtins_zip_attrs_with() {
5928 let v = ev("builtins.zipAttrsWith (n: vs: builtins.head vs) [{ a = 1; } { a = 2; b = 3; }]");
5929 if let Value::Attrs(a) = v {
5930 assert_eq!(a.get("a"), Some(&Value::Int(1)));
5931 assert_eq!(a.get("b"), Some(&Value::Int(3)));
5932 } else {
5933 panic!("expected attrs");
5934 }
5935 }
5936
5937 #[test]
5938 fn eval_builtins_compare_versions() {
5939 assert_eq!(ev(r#"builtins.compareVersions "2.0" "1.0""#), Value::Int(1));
5940 assert_eq!(ev(r#"builtins.compareVersions "1.0" "2.0""#), Value::Int(-1));
5941 assert_eq!(ev(r#"builtins.compareVersions "1.0" "1.0""#), Value::Int(0));
5942 }
5943
5944 #[test]
5945 fn eval_builtins_parse_drv_name() {
5946 let v = ev(r#"builtins.parseDrvName "nix-2.3.4""#);
5947 if let Value::Attrs(a) = v {
5948 assert_eq!(a.get("name"), Some(&Value::string("nix")));
5949 assert_eq!(a.get("version"), Some(&Value::string("2.3.4")));
5950 } else {
5951 panic!("expected attrs");
5952 }
5953 }
5954
5955 #[test]
5956 fn eval_builtins_base_name_of() {
5957 assert_eq!(
5958 ev(r#"builtins.baseNameOf "/foo/bar/baz""#),
5959 Value::string("baz"),
5960 );
5961 }
5962
5963 #[test]
5964 fn eval_builtins_dir_of() {
5965 assert_eq!(
5966 ev(r#"builtins.dirOf "/foo/bar/baz""#),
5967 Value::string("/foo/bar"),
5968 );
5969 }
5970
5971 #[test]
5972 fn eval_builtins_add_error_context() {
5973 assert_eq!(
5974 ev(r#"builtins.addErrorContext "some context" 42"#),
5975 Value::Int(42),
5976 );
5977 }
5978
5979 #[test]
5980 fn eval_builtins_abort() {
5981 let result = eval(r#"builtins.abort "fatal error""#);
5982 assert!(result.is_err());
5983 let msg = format!("{}", result.unwrap_err());
5984 assert!(msg.contains("fatal error"));
5985 }
5986
5987 #[test]
5992 fn indented_string_simple() {
5993 assert_eq!(ev("''hello''"), Value::string("hello"));
5994 }
5995
5996 #[test]
5997 fn indented_string_multiline_strips_indent() {
5998 assert_eq!(
5999 ev("''\n line1\n line2\n''"),
6000 Value::string("line1\nline2\n"),
6001 );
6002 }
6003
6004 #[test]
6005 fn indented_string_with_interpolation() {
6006 let code = "let x = \"world\"; in ''hello ${x}''";
6007 assert_eq!(
6008 ev(code),
6009 Value::string("hello world"),
6010 );
6011 }
6012
6013 #[test]
6014 fn indented_string_deeper_indent_preserved() {
6015 assert_eq!(
6017 ev("''\n a\n b\n''"),
6018 Value::string("a\n b\n"),
6019 );
6020 }
6021
6022 #[test]
6027 fn dynamic_attr_name_in_set() {
6028 assert_eq!(
6029 ev(r#"let key = "mykey"; in { ${key} = 42; }.mykey"#),
6030 Value::Int(42),
6031 );
6032 }
6033
6034 #[test]
6035 fn dynamic_attr_name_with_expression() {
6036 assert_eq!(
6037 ev(r#"let prefix = "foo"; in { ${"${prefix}bar"} = 1; }.foobar"#),
6038 Value::Int(1),
6039 );
6040 }
6041
6042 #[test]
6047 fn eval_builtins_match() {
6048 assert_eq!(
6049 ev(r#"builtins.match "([0-9]+)" "42""#),
6050 Value::list(vec![Value::string("42")]),
6051 );
6052 }
6053
6054 #[test]
6055 fn eval_builtins_hash_string() {
6056 let v = ev(r#"builtins.hashString "sha256" "hello""#);
6057 if let Value::String(ns) = v {
6058 assert_eq!(ns.chars.len(), 64);
6059 } else {
6060 panic!("expected string");
6061 }
6062 }
6063
6064 #[test]
6065 fn eval_builtins_import() {
6066 let dir = std::env::temp_dir();
6067 let path = dir.join("sui_eval_test_import_eval.nix");
6068 std::fs::write(&path, "42").unwrap();
6069 let expr = format!(r#"import "{}""#, path.display());
6070 let v = eval(&expr).unwrap();
6071 assert_eq!(v, Value::Int(42));
6072 std::fs::remove_file(&path).ok();
6073 }
6074
6075 #[test]
6076 fn eval_builtins_derivation() {
6077 let v = eval(r#"builtins.derivation { name = "test"; system = "x86_64-linux"; builder = "/bin/sh"; }"#).unwrap();
6078 if let Value::Attrs(a) = v {
6079 assert_eq!(a.get("type"), Some(&Value::string("derivation")));
6080 } else {
6081 panic!("expected attrs");
6082 }
6083 }
6084
6085 #[test]
6086 fn eval_mutual_recursive_let() {
6087 let v = eval("let a = { x = b; }; b = { y = a; }; in a.x.y");
6094 assert!(v.is_ok(), "mutual recursive let should not error: {v:?}");
6095 let val = v.unwrap();
6097 assert!(
6098 matches!(val, Value::Attrs(_)),
6099 "a.x.y should be an attrset, got: {val:?}",
6100 );
6101 }
6102
6103 #[test]
6104 fn eval_mutual_recursive_let_simple() {
6105 let v = eval("let a = b; b = 42; in a");
6107 assert!(v.is_ok());
6108 assert_eq!(v.unwrap(), Value::Int(42));
6111 }
6112
6113 #[test]
6114 fn eval_builtins_read_dir() {
6115 let dir = std::env::temp_dir().join("sui_eval_test_readdir_eval");
6116 let _ = std::fs::remove_dir_all(&dir);
6117 std::fs::create_dir_all(&dir).unwrap();
6118 std::fs::write(dir.join("a.txt"), "").unwrap();
6119 let expr = format!(r#"builtins.readDir "{}""#, dir.display());
6120 let v = eval(&expr).unwrap();
6121 if let Value::Attrs(a) = v {
6122 assert_eq!(a.get("a.txt"), Some(&Value::string("regular")));
6123 } else {
6124 panic!("expected attrs");
6125 }
6126 let _ = std::fs::remove_dir_all(&dir);
6127 }
6128
6129 #[test]
6134 fn thunk_basic_let() {
6135 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
6137 }
6138
6139 #[test]
6140 fn thunk_forward_ref() {
6141 assert_eq!(ev("let a = b; b = 1; in a"), Value::Int(1));
6143 }
6144
6145 #[test]
6146 fn thunk_mutual_rec_attrset_in_let() {
6147 assert_eq!(ev("let a = { x = b; }; b = { y = 1; }; in a.x.y"), Value::Int(1));
6149 }
6150
6151 #[test]
6152 fn thunk_rec_attrset() {
6153 assert_eq!(ev("(rec { a = b; b = 1; }).a"), Value::Int(1));
6155 }
6156
6157 #[test]
6158 fn thunk_rec_attrset_chain() {
6159 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
6161 }
6162
6163 #[test]
6164 fn thunk_fixpoint() {
6165 assert_eq!(
6167 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; })).b"),
6168 Value::Int(2),
6169 );
6170 }
6171
6172 #[test]
6173 fn thunk_blackhole_self_reference() {
6174 let result = eval("let x = x; in x");
6176 assert!(result.is_err());
6177 let msg = format!("{}", result.unwrap_err());
6178 assert!(
6179 msg.contains("infinite recursion") || msg.contains("blackhole"),
6180 "expected blackhole error, got: {msg}",
6181 );
6182 }
6183
6184 #[test]
6185 fn thunk_mutual_blackhole() {
6186 let result = eval("let a = b; b = a; in a");
6188 assert!(result.is_err());
6189 }
6190
6191 #[test]
6192 fn thunk_let_body_forces_correctly() {
6193 assert_eq!(ev("let a = 10; b = 20; in a + b"), Value::Int(30));
6195 }
6196
6197 #[test]
6198 fn thunk_only_forced_when_needed() {
6199 assert_eq!(ev("let bad = 1 / 0; good = 42; in good"), Value::Int(42));
6201 }
6202
6203 #[test]
6204 fn thunk_forward_ref_in_function_body() {
6205 assert_eq!(
6207 ev("let f = x: x + b; b = 10; in f 5"),
6208 Value::Int(15),
6209 );
6210 }
6211
6212 #[test]
6213 fn thunk_rec_set_self_ref_through_self() {
6214 assert_eq!(
6216 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
6217 Value::Int(5),
6218 );
6219 }
6220
6221 #[test]
6222 fn thunk_nested_let_forward_ref() {
6223 assert_eq!(
6225 ev("let a = b + 1; b = 2; in a"),
6226 Value::Int(3),
6227 );
6228 }
6229
6230 #[test]
6231 fn thunk_deep_chain() {
6232 assert_eq!(
6234 ev("let a = 1; b = a; c = b; d = c; e = d; in e"),
6235 Value::Int(1),
6236 );
6237 }
6238
6239 #[test]
6240 fn thunk_rec_set_fixpoint() {
6241 assert_eq!(
6243 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; c = self.b + 1; })).c"),
6244 Value::Int(3),
6245 );
6246 }
6247
6248 #[test]
6249 fn thunk_let_with_inherit() {
6250 assert_eq!(
6252 ev("let a = 1; in let inherit a; b = a + 1; in b"),
6253 Value::Int(2),
6254 );
6255 }
6256
6257 #[test]
6258 fn thunk_attrset_value_lazy() {
6259 assert_eq!(
6262 ev("let x = 42; in { a = x; }.a"),
6263 Value::Int(42),
6264 );
6265 }
6266
6267 #[test]
6268 fn thunk_unused_error_not_forced() {
6269 assert_eq!(
6271 ev(r#"let bad = builtins.throw "boom"; ok = 1; in ok"#),
6272 Value::Int(1),
6273 );
6274 }
6275
6276 #[test]
6277 fn thunk_rec_set_mutual_reference() {
6278 let v = ev("rec { a = { val = b.val + 1; }; b = { val = 10; }; }");
6280 if let Value::Attrs(attrs) = v {
6281 let a = attrs.get("a").unwrap();
6282 let a_forced = force_value(a).unwrap();
6283 if let Value::Attrs(a_attrs) = a_forced {
6284 assert_eq!(a_attrs.get("val"), Some(&Value::Int(11)));
6285 } else {
6286 panic!("expected attrs for a");
6287 }
6288 } else {
6289 panic!("expected attrs");
6290 }
6291 }
6292
6293 #[test]
6296 fn let_rec_self_reference_simple() {
6297 assert_eq!(
6298 ev("let x = 1; y = x + 1; in y"),
6299 Value::Int(2),
6300 );
6301 }
6302
6303 #[test]
6304 fn let_rec_self_reference_chain() {
6305 assert_eq!(
6306 ev("let a = 1; b = a + 1; c = b + 1; in c"),
6307 Value::Int(3),
6308 );
6309 }
6310
6311 #[test]
6312 fn let_rec_self_reference_with_function() {
6313 assert_eq!(
6314 ev("let f = x: x + 1; y = f 10; in y"),
6315 Value::Int(11),
6316 );
6317 }
6318
6319 #[test]
6320 fn let_rec_mutual_recursion_via_if() {
6321 assert_eq!(
6322 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"),
6323 Value::Bool(true),
6324 );
6325 }
6326
6327 #[test]
6328 fn let_rec_forward_ref_in_list() {
6329 assert_eq!(
6330 ev("let xs = [a b]; a = 1; b = 2; in builtins.length xs"),
6331 Value::Int(2),
6332 );
6333 }
6334
6335 #[test]
6338 fn with_shadowing_let_wins_over_with() {
6339 assert_eq!(
6340 ev("let x = 1; in with { x = 2; }; x"),
6341 Value::Int(1),
6342 );
6343 }
6344
6345 #[test]
6346 fn with_shadowing_inner_with_wins() {
6347 assert_eq!(
6348 ev("with { x = 1; }; with { x = 2; }; x"),
6349 Value::Int(2),
6350 );
6351 }
6352
6353 #[test]
6354 fn with_shadowing_outer_provides_missing() {
6355 assert_eq!(
6356 ev("with { x = 1; y = 10; }; with { x = 2; }; x + y"),
6357 Value::Int(12),
6358 );
6359 }
6360
6361 #[test]
6362 fn with_shadowing_lambda_arg_wins() {
6363 assert_eq!(
6364 ev("(x: with { x = 99; }; x) 42"),
6365 Value::Int(42),
6366 );
6367 }
6368
6369 #[test]
6370 fn with_shadowing_nested_let_wins_over_with() {
6371 assert_eq!(
6372 ev("with { x = 1; }; let x = 2; in x"),
6373 Value::Int(2),
6374 );
6375 }
6376
6377 #[test]
6378 fn with_scope_dynamic_attrs() {
6379 assert_eq!(
6380 ev(r#"with { x = 1; y = 2; z = 3; }; x + y + z"#),
6381 Value::Int(6),
6382 );
6383 }
6384
6385 #[test]
6386 fn with_scope_over_lazy_thunk_chain_resolves() {
6387 assert_eq!(
6396 ev(r#"let outer = if true then (if true then { unix = 42; } else {}) else {};
6397 # force a two-deep lazy wrap of the with-head
6398 head = (x: x) ((y: y) outer);
6399 in with head; unix"#),
6400 Value::Int(42),
6401 );
6402 }
6403
6404 #[test]
6405 fn with_scope_head_from_deep_select_resolves() {
6406 assert_eq!(
6409 ev(r#"let a = { b = { c = { key = 7; }; }; }; in with a.b.c; key"#),
6410 Value::Int(7),
6411 );
6412 }
6413
6414 #[test]
6417 fn attrset_deep_merge_simple() {
6418 let v = ev("{ a.b = 1; a.c = 2; }");
6419 if let Value::Attrs(attrs) = v {
6420 let a = force_value(attrs.get("a").unwrap()).unwrap();
6421 if let Value::Attrs(inner) = a {
6422 assert_eq!(force_value(inner.get("b").unwrap()).unwrap(), Value::Int(1));
6423 assert_eq!(force_value(inner.get("c").unwrap()).unwrap(), Value::Int(2));
6424 } else {
6425 panic!("expected nested attrs");
6426 }
6427 } else {
6428 panic!("expected attrs");
6429 }
6430 }
6431
6432 #[test]
6433 fn attrset_deep_merge_three_levels() {
6434 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
6435 if let Value::Attrs(attrs) = v {
6436 let a = force_value(attrs.get("a").unwrap()).unwrap();
6437 if let Value::Attrs(a_inner) = a {
6438 let e = force_value(a_inner.get("e").unwrap()).unwrap();
6439 assert_eq!(e, Value::Int(3));
6440 let b = force_value(a_inner.get("b").unwrap()).unwrap();
6441 if let Value::Attrs(b_inner) = b {
6442 assert_eq!(force_value(b_inner.get("c").unwrap()).unwrap(), Value::Int(1));
6443 assert_eq!(force_value(b_inner.get("d").unwrap()).unwrap(), Value::Int(2));
6444 } else {
6445 panic!("expected nested attrs for b");
6446 }
6447 } else {
6448 panic!("expected nested attrs for a");
6449 }
6450 } else {
6451 panic!("expected attrs");
6452 }
6453 }
6454
6455 #[test]
6456 fn attrset_deep_merge_preserves_siblings() {
6457 assert_eq!(
6458 ev("{ a.x = 1; b = 2; a.y = 3; }.b"),
6459 Value::Int(2),
6460 );
6461 }
6462
6463 #[test]
6464 fn attrset_deep_merge_in_let() {
6465 let v = ev("let s = { a.b = 1; a.c = 2; }; in s.a.b + s.a.c");
6466 assert_eq!(v, Value::Int(3));
6467 }
6468
6469 #[test]
6470 fn attrset_deep_merge_fullset_then_dotted() {
6471 let v = ev("let s = { a = { x = 1; }; a.y = 2; }; in s.a.x + s.a.y");
6478 assert_eq!(v, Value::Int(3));
6479 let both = ev("let s = { a = { x = 1; }; a.y = 2; }; in [ s.a.x s.a.y ]");
6481 if let Value::List(items) = both {
6482 assert_eq!(force_value(&items[0]).unwrap(), Value::Int(1));
6483 assert_eq!(force_value(&items[1]).unwrap(), Value::Int(2));
6484 } else {
6485 panic!("expected list");
6486 }
6487 }
6488
6489 #[test]
6492 fn inherit_from_basic() {
6493 assert_eq!(
6494 ev("let s = { x = 1; y = 2; }; in let inherit (s) x y; in x + y"),
6495 Value::Int(3),
6496 );
6497 }
6498
6499 #[test]
6500 fn inherit_from_with_shadowing() {
6501 assert_eq!(
6502 ev("let x = 10; in let inherit ({ x = 20; }) x; in x"),
6503 Value::Int(20),
6504 );
6505 }
6506
6507 #[test]
6508 fn inherit_from_in_attrset() {
6509 let v = ev(r#"let s = { a = 1; b = 2; }; in { inherit (s) a b; c = 3; }"#);
6510 if let Value::Attrs(attrs) = v {
6511 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
6512 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
6513 assert_eq!(force_value(attrs.get("c").unwrap()).unwrap(), Value::Int(3));
6514 } else {
6515 panic!("expected attrs");
6516 }
6517 }
6518
6519 #[test]
6520 fn inherit_from_rec_set() {
6521 assert_eq!(
6522 ev("rec { inherit ({ x = 42; }) x; y = x; }.y"),
6523 Value::Int(42),
6524 );
6525 }
6526
6527 #[test]
6528 fn inherit_plain_from_scope() {
6529 assert_eq!(
6530 ev("let x = 1; in { inherit x; }.x"),
6531 Value::Int(1),
6532 );
6533 }
6534
6535 #[test]
6544 fn inherit_plain_from_with_scope_lazy() {
6545 assert_eq!(
6549 ev("let fix = f: let x = f x; in x;
6550 self = fix (self: with self; {
6551 a = use { inherit cp; };
6552 use = { cp }: cp 5;
6553 cp = x: x + 100;
6554 });
6555 in self.a"),
6556 Value::Int(105),
6557 );
6558 assert_eq!(
6560 ev("with { y = 7; }; { inherit y; }.y"),
6561 Value::Int(7),
6562 );
6563 }
6564
6565 #[test]
6566 fn inherit_multiple_from_expr() {
6567 assert_eq!(
6568 ev("let s = { a = 10; b = 20; c = 30; }; in let inherit (s) a b c; in a + b + c"),
6569 Value::Int(60),
6570 );
6571 }
6572
6573 #[test]
6576 fn interp_nested_attrset_access() {
6577 assert_eq!(
6578 ev(r#"let x = { a = "hello"; }; in "${x.a} world""#),
6579 Value::string("hello world"),
6580 );
6581 }
6582
6583 #[test]
6584 fn interp_with_let_expression() {
6585 assert_eq!(
6586 ev(r#""${let x = "inner"; in x}""#),
6587 Value::string("inner"),
6588 );
6589 }
6590
6591 #[test]
6592 fn interp_float_coercion() {
6593 assert_eq!(
6595 ev(r#""${toString 3.14}""#),
6596 Value::string("3.140000"),
6597 );
6598 }
6599
6600 #[test]
6603 fn compare_mixed_int_float() {
6604 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
6605 assert_eq!(ev("1.5 > 1"), Value::Bool(true));
6606 assert_eq!(ev("2.0 == 2"), Value::Bool(true));
6607 }
6608
6609 #[test]
6610 fn compare_string_lexicographic() {
6611 assert_eq!(ev(r#""abc" < "abd""#), Value::Bool(true));
6612 assert_eq!(ev(r#""abc" < "abc""#), Value::Bool(false));
6613 assert_eq!(ev(r#""abc" <= "abc""#), Value::Bool(true));
6614 }
6615
6616 #[test]
6619 fn update_empty_sets() {
6620 let v = ev("{} // {}");
6621 if let Value::Attrs(a) = v { assert!(a.is_empty()); } else { panic!(); }
6622 }
6623
6624 #[test]
6625 fn update_right_overrides_completely() {
6626 assert_eq!(
6627 ev("{ a = 1; b = 2; } // { a = 10; c = 30; }"),
6628 ev("{ a = 10; b = 2; c = 30; }"),
6629 );
6630 }
6631
6632 #[test]
6633 fn update_chained() {
6634 assert_eq!(
6635 ev("{ a = 1; } // { b = 2; } // { c = 3; }"),
6636 ev("{ a = 1; b = 2; c = 3; }"),
6637 );
6638 }
6639
6640 #[test]
6643 fn force_value_concrete_unchanged() {
6644 let v = Value::Int(42);
6645 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
6646 }
6647
6648 #[test]
6649 fn force_value_null() {
6650 assert_eq!(force_value(&Value::Null).unwrap(), Value::Null);
6651 }
6652
6653 #[test]
6656 fn eval_with_file_none() {
6657 let result = eval_with_file("1 + 2", None).unwrap();
6658 assert_eq!(result, Value::Int(3));
6659 }
6660
6661 #[test]
6664 fn error_type_mismatch_in_comparison() {
6665 let result = eval(r#"1 < "a""#);
6666 assert!(result.is_err());
6667 }
6668
6669 #[test]
6670 fn error_select_from_non_set() {
6671 let result = eval("42.x");
6672 assert!(result.is_err());
6673 }
6674
6675 #[test]
6676 fn error_call_non_function() {
6677 let result = eval("42 1");
6678 assert!(result.is_err());
6679 }
6680
6681 #[test]
6682 fn error_negate_string() {
6683 let result = eval(r#"-"hello""#);
6684 assert!(result.is_err());
6685 }
6686
6687 #[test]
6690 fn multiline_string_empty() {
6691 assert_eq!(ev("''''"), Value::string(""));
6692 }
6693
6694 #[test]
6695 fn multiline_string_with_trailing_newline() {
6696 let v = ev("''\n hello\n''");
6697 assert_eq!(v, Value::string("hello\n"));
6698 }
6699
6700 #[test]
6703 fn list_concat_empty_left() {
6704 assert_eq!(ev("[] ++ [1 2]"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6705 }
6706
6707 #[test]
6708 fn list_concat_empty_right() {
6709 assert_eq!(ev("[1 2] ++ []"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6710 }
6711
6712 #[test]
6713 fn list_concat_both_empty() {
6714 assert_eq!(ev("[] ++ []"), Value::list(vec![]));
6715 }
6716
6717 #[test]
6720 fn formals_at_pattern_accessible() {
6721 assert_eq!(
6722 ev("({ x, ... } @ args: builtins.length (builtins.attrNames args)) { x = 1; y = 2; z = 3; }"),
6723 Value::Int(3),
6724 );
6725 }
6726
6727 #[test]
6728 fn formals_default_uses_other_arg() {
6729 assert_eq!(
6730 ev("({ x, y ? x + 1 }: y) { x = 10; }"),
6731 Value::Int(11),
6732 );
6733 }
6734
6735 #[test]
6736 fn formals_default_lazy_assert_false() {
6737 assert_eq!(
6741 ev("({ cpu, vendor ? assert false; null, kernel } @ args: if args ? vendor then vendor else \"inferred\") { cpu = \"x86_64\"; kernel = \"linux\"; }"),
6742 Value::String(Rc::new(NixString::plain("inferred"))),
6743 );
6744 }
6745
6746 #[test]
6747 fn formals_default_lazy_only_forced_when_accessed() {
6748 assert_eq!(
6750 ev("({ a, b ? 42 }: b) { a = 1; }"),
6751 Value::Int(42),
6752 );
6753 }
6754
6755 #[test]
6756 fn formals_ellipsis_ignores_extra() {
6757 assert_eq!(
6758 ev("({ x, ... }: x) { x = 1; y = 2; z = 3; }"),
6759 Value::Int(1),
6760 );
6761 }
6762
6763 #[test]
6766 fn pure_mode_roundtrip() {
6767 let was_pure = is_pure_mode();
6768 set_pure_mode(true);
6769 assert!(is_pure_mode());
6770 set_pure_mode(false);
6771 assert!(!is_pure_mode());
6772 set_pure_mode(was_pure);
6773 }
6774
6775 #[test]
6778 fn path_concat_with_string() {
6779 assert_eq!(
6780 ev(r#"/foo + "bar""#),
6781 Value::Path(Box::new(SmolStr::from("/foobar"))),
6782 );
6783 }
6784
6785 #[test]
6786 fn path_concat_with_path() {
6787 assert_eq!(
6788 ev("/foo + /bar"),
6789 Value::Path(Box::new(SmolStr::from("/foo//bar"))),
6790 );
6791 }
6792
6793 #[test]
6796 fn current_eval_dir_empty_when_no_file_pushed() {
6797 let snapshot = current_eval_dir();
6801 let _ = snapshot;
6803 }
6804
6805 #[test]
6806 fn push_eval_file_sets_current_dir() {
6807 let p = std::path::PathBuf::from("/tmp/example/file.nix");
6808 {
6809 let _g = push_eval_file(p.clone());
6810 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/tmp/example")));
6811 }
6812 }
6816
6817 #[test]
6818 fn push_eval_file_nested_stack() {
6819 let outer = std::path::PathBuf::from("/a/x.nix");
6820 let inner = std::path::PathBuf::from("/b/y.nix");
6821 {
6822 let _g_outer = push_eval_file(outer.clone());
6823 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6824 {
6825 let _g_inner = push_eval_file(inner.clone());
6826 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/b")));
6827 }
6828 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6830 }
6831 }
6832
6833 #[test]
6841 fn fileless_frame_masks_parent_file() {
6842 let outer = std::path::PathBuf::from("/a/x.nix");
6843 let _g_outer = push_eval_file(outer.clone());
6844 assert_eq!(current_eval_file(), Some(outer.clone()));
6845 {
6846 let _g_none = push_eval_frame(None);
6847 assert_eq!(current_eval_file(), None);
6849 assert_eq!(current_eval_dir(), None);
6850 assert_eq!(eval_file_stack_snapshot().last().map(String::as_str), Some("<no-file>"));
6851 }
6852 assert_eq!(current_eval_file(), Some(outer));
6854 }
6855
6856 #[test]
6859 fn error_undefined_var_includes_file_context() {
6860 let p = std::path::PathBuf::from("/nix/store/abc-default.nix");
6861 let _g = push_eval_file(p);
6862 let result = eval("nonexistent_xyz");
6863 let msg = format!("{}", result.unwrap_err());
6864 assert!(msg.contains("undefined variable"), "msg: {msg}");
6865 assert!(msg.contains("nonexistent_xyz"), "msg: {msg}");
6866 assert!(msg.contains("abc-default.nix"), "msg: {msg}");
6867 }
6868
6869 #[test]
6870 fn error_attr_not_found_includes_file_context() {
6871 let p = std::path::PathBuf::from("/nix/store/xyz-module.nix");
6872 let _g = push_eval_file(p);
6873 let result = eval("{}.missing_key");
6874 let msg = format!("{}", result.unwrap_err());
6875 assert!(msg.contains("not found") || msg.contains("missing_key"), "msg: {msg}");
6876 assert!(msg.contains("xyz-module.nix"), "msg: {msg}");
6877 }
6878
6879 #[test]
6880 fn error_assertion_failed_includes_file_context() {
6881 let p = std::path::PathBuf::from("/nix/store/test-assert.nix");
6882 let _g = push_eval_file(p);
6883 let result = eval("assert false; 1");
6884 let msg = format!("{}", result.unwrap_err());
6885 assert!(msg.contains("assertion failed"), "msg: {msg}");
6886 assert!(msg.contains("test-assert.nix"), "msg: {msg}");
6887 }
6888
6889 #[test]
6902 fn error_missing_argument_includes_file_context() {
6903 let p = std::path::PathBuf::from("/nix/store/func.nix");
6904 let result = eval_with_file("({ a, b }: a) { a = 1; }", Some(p));
6905 let msg = format!("{}", result.unwrap_err());
6906 assert!(msg.contains("missing argument"), "msg: {msg}");
6907 assert!(msg.contains("func.nix"), "msg: {msg}");
6908 }
6909
6910 #[test]
6911 fn error_cannot_call_includes_file_context() {
6912 let p = std::path::PathBuf::from("/nix/store/call.nix");
6913 let _g = push_eval_file(p);
6914 let result = eval("42 99");
6915 let msg = format!("{}", result.unwrap_err());
6916 assert!(msg.contains("cannot call"), "msg: {msg}");
6917 assert!(msg.contains("call.nix"), "msg: {msg}");
6918 }
6919
6920 #[test]
6921 fn error_without_file_has_no_in_prefix() {
6922 let result = eval("nonexistent_xyz");
6925 let msg = format!("{}", result.unwrap_err());
6926 assert!(msg.contains("undefined variable"), "msg: {msg}");
6927 assert!(!msg.contains(", in"), "msg should not contain file context: {msg}");
6928 }
6929
6930 #[test]
6933 fn pure_mode_set_get_independence() {
6934 let was = is_pure_mode();
6935 set_pure_mode(true);
6936 assert!(is_pure_mode());
6937 set_pure_mode(false);
6938 assert!(!is_pure_mode());
6939 set_pure_mode(was);
6940 }
6941
6942 #[test]
6945 fn eval_with_file_some_path_arithmetic() {
6946 let p = std::path::PathBuf::from("/tmp/imaginary.nix");
6947 let result = eval_with_file("1 + 2", Some(p)).unwrap();
6948 assert_eq!(result, Value::Int(3));
6949 }
6950
6951 #[test]
6959 fn unsafe_get_attr_pos_reports_file_and_offset_column() {
6960 let dir = tempfile::tempdir().unwrap();
6972 let file_body = "{ a = 1;\n b = 2; }\n";
6974 let f = dir.path().join("lit.nix");
6975 std::fs::write(&f, file_body).unwrap();
6976 let src = format!("builtins.unsafeGetAttrPos \"b\" (import {})", f.display());
6977 let v = eval(&src).unwrap();
6978 let attrs = match v { Value::Attrs(a) => a, other => panic!("expected attrs, got {other:?}") };
6979 assert_eq!(
6980 attrs.get("file").unwrap().as_string().unwrap(),
6981 f.to_string_lossy(),
6982 );
6983 let off = file_body.find("b = 2").unwrap();
6985 let bol = file_body[..off].rfind('\n').map_or(0, |i| i + 1);
6986 let expected_line = 1 + file_body[..off].matches('\n').count() as i64;
6987 let expected_col = (off - bol) as i64 + 1;
6988 assert_eq!(expected_line, 2, "fixture must put `b` on line 2");
6989 assert_eq!(*attrs.get("line").unwrap(), Value::Int(expected_line));
6990 let col = match attrs.get("column").unwrap() { Value::Int(n) => *n, o => panic!("{o:?}") };
6991 assert_eq!(col, expected_col, "column must be the 1-based BYTE column");
6992 }
6993
6994 #[test]
6995 fn unsafe_get_attr_pos_null_for_string_origin() {
6996 let v = eval("builtins.unsafeGetAttrPos \"a\" { a = 1; }").unwrap();
6998 assert_eq!(v, Value::Null);
6999 }
7000
7001 #[test]
7002 fn unsafe_get_attr_pos_null_for_missing_key() {
7003 let dir = tempfile::tempdir().unwrap();
7005 let f = dir.path().join("lit.nix");
7006 std::fs::write(&f, "{ a = 1; }\n").unwrap();
7007 let src = format!("builtins.unsafeGetAttrPos \"zzz\" (import {})", f.display());
7008 let v = eval(&src).unwrap();
7009 assert_eq!(v, Value::Null);
7010 }
7011
7012 #[test]
7015 fn interp_int_into_string() {
7016 assert_eq!(ev(r#""val=${toString 42}""#), Value::string("val=42"));
7018 }
7019
7020 #[test]
7021 fn interp_bool_true_becomes_one() {
7022 let v = ev(r#"let x = true; in "${builtins.toString x}""#);
7024 assert_eq!(v, Value::string("1"));
7025 }
7026
7027 #[test]
7028 fn interp_null_becomes_empty() {
7029 let v = ev(r#"let x = null; in "${builtins.toString x}""#);
7031 assert_eq!(v, Value::string(""));
7032 }
7033
7034 #[test]
7035 fn interp_attrset_without_to_string_errors() {
7036 let result = eval(r#"let s = { x = 1; }; in "${s}""#);
7038 assert!(result.is_err());
7039 }
7040
7041 #[test]
7042 fn interp_attrset_with_to_string_protocol() {
7043 let v = ev(r#""${{ __toString = self: "ok"; }}""#);
7045 assert_eq!(v, Value::string("ok"));
7046 }
7047
7048 #[test]
7051 fn eval_path_absolute_literal() {
7052 let v = ev("/tmp/foo");
7053 match v {
7054 Value::Path(p) => assert!(p.contains("/tmp/foo")),
7055 _ => panic!("expected Path"),
7056 }
7057 }
7058
7059 #[test]
7060 fn eval_path_home_literal() {
7061 let v = ev("~/foo.nix");
7062 match v {
7063 Value::Path(p) => assert!(p.contains("~/foo.nix") || p.ends_with("foo.nix")),
7064 _ => panic!("expected Path"),
7065 }
7066 }
7067
7068 #[test]
7071 fn path_search_unmatched_errors() {
7072 let saved = std::env::var("NIX_PATH").ok();
7075 unsafe {
7079 std::env::remove_var("NIX_PATH");
7080 }
7081 let result = eval("<this_should_not_resolve>");
7082 if let Some(v) = saved {
7083 unsafe {
7084 std::env::set_var("NIX_PATH", v);
7085 }
7086 }
7087 assert!(result.is_err());
7088 }
7089
7090 #[test]
7093 fn unary_negate_int() {
7094 assert_eq!(ev("-7"), Value::Int(-7));
7095 }
7096
7097 #[test]
7098 fn unary_negate_float() {
7099 assert_eq!(ev("-2.5"), Value::Float(-2.5));
7100 }
7101
7102 #[test]
7103 fn unary_invert_true() {
7104 assert_eq!(ev("!true"), Value::Bool(false));
7105 }
7106
7107 #[test]
7108 fn unary_invert_false() {
7109 assert_eq!(ev("!false"), Value::Bool(true));
7110 }
7111
7112 #[test]
7113 fn unary_negate_bool_errors() {
7114 let result = eval("-true");
7115 assert!(result.is_err());
7116 }
7117
7118 #[test]
7119 fn unary_invert_int_errors() {
7120 let result = eval("!42");
7121 assert!(result.is_err());
7122 }
7123
7124 #[test]
7127 fn binop_add_attrs_errors() {
7128 let result = eval("{a=1;} + {b=2;}");
7129 assert!(result.is_err());
7130 }
7131
7132 #[test]
7133 fn binop_sub_string_errors() {
7134 let result = eval(r#""a" - "b""#);
7135 assert!(result.is_err());
7136 }
7137
7138 #[test]
7139 fn binop_mul_string_errors() {
7140 let result = eval(r#""a" * "b""#);
7141 assert!(result.is_err());
7142 }
7143
7144 #[test]
7145 fn binop_div_string_errors() {
7146 let result = eval(r#""a" / "b""#);
7147 assert!(result.is_err());
7148 }
7149
7150 #[test]
7151 fn binop_compare_attrs_errors() {
7152 let result = eval("{a=1;} < {b=2;}");
7153 assert!(result.is_err());
7154 }
7155
7156 #[test]
7157 fn binop_div_float_by_zero_int() {
7158 let result = eval("1.0 / 0");
7162 let _ = result;
7165 }
7166
7167 #[test]
7168 fn binop_int_div_zero_is_division_by_zero() {
7169 let result = eval("5 / 0");
7170 match result {
7171 Err(EvalError::DivisionByZero) => {}
7172 other => panic!("expected DivisionByZero, got {other:?}"),
7173 }
7174 }
7175
7176 #[test]
7179 fn if_else_only_chosen_branch_evaluated_then() {
7180 assert_eq!(ev("if true then 42 else 1 / 0"), Value::Int(42));
7183 }
7184
7185 #[test]
7186 fn if_else_only_chosen_branch_evaluated_else() {
7187 assert_eq!(ev("if false then 1 / 0 else 99"), Value::Int(99));
7188 }
7189
7190 #[test]
7191 fn if_condition_must_be_bool() {
7192 let result = eval("if 1 then 1 else 2");
7193 assert!(result.is_err());
7194 }
7195
7196 #[test]
7197 fn if_condition_lazy_does_not_force_unused() {
7198 assert_eq!(
7201 ev("let bad = 1 / 0; in if true then 42 else bad"),
7202 Value::Int(42),
7203 );
7204 }
7205
7206 #[test]
7209 fn and_short_circuits_on_false() {
7210 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
7212 }
7213
7214 #[test]
7215 fn or_short_circuits_on_true() {
7216 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
7217 }
7218
7219 #[test]
7220 fn implication_short_circuits_on_false_lhs() {
7221 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
7223 }
7224
7225 #[test]
7228 fn lambda_fix_combinator_returns_attrset() {
7229 let v = ev(
7231 "let fix = f: let x = f x; in x; in
7232 (fix (self: { val = 1; double = self.val * 2; })).double",
7233 );
7234 assert_eq!(v, Value::Int(2));
7235 }
7236
7237 #[test]
7240 fn rec_attrset_self_reference() {
7241 let v = ev("(rec { a = b; b = 1; }).a");
7243 assert_eq!(v, Value::Int(1));
7244 }
7245
7246 #[test]
7247 fn rec_attrset_inherit_from_uses_outer_scope() {
7248 let v = ev(
7252 "let src = { a = 10; }; in
7253 rec {
7254 inherit (src) a;
7255 b = a + 1;
7256 }",
7257 );
7258 if let Value::Attrs(attrs) = v {
7259 let b = attrs.get("b").unwrap();
7260 let b_forced = force_value(b).unwrap();
7261 assert_eq!(b_forced, Value::Int(11));
7262 } else {
7263 panic!("expected attrs");
7264 }
7265 }
7266
7267 #[test]
7268 fn nonrec_attrset_no_self_reference() {
7269 let result = eval("({ a = 1; b = a + 1; }).b");
7272 assert!(result.is_err());
7273 }
7274
7275 #[test]
7278 fn dotted_binding_three_segments_then_sibling() {
7279 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
7280 if let Value::Attrs(attrs) = v {
7281 let a = attrs.get("a").unwrap();
7282 let a_forced = force_value(a).unwrap();
7283 if let Value::Attrs(a_attrs) = a_forced {
7284 let b = a_attrs.get("b").unwrap();
7285 let b_forced = force_value(b).unwrap();
7286 if let Value::Attrs(b_attrs) = b_forced {
7287 assert_eq!(force_value(b_attrs.get("c").unwrap()).unwrap(), Value::Int(1));
7288 assert_eq!(force_value(b_attrs.get("d").unwrap()).unwrap(), Value::Int(2));
7289 } else {
7290 panic!("expected b to be attrs");
7291 }
7292 assert_eq!(force_value(a_attrs.get("e").unwrap()).unwrap(), Value::Int(3));
7293 } else {
7294 panic!("expected a to be attrs");
7295 }
7296 } else {
7297 panic!("expected outer attrs");
7298 }
7299 }
7300
7301 #[test]
7304 fn rec_dotted_bindings_visible_to_siblings() {
7305 let v = ev("rec { types.openSB = 1; types.openCpu = 2; foo = types.openSB; }.foo");
7308 assert_eq!(v, Value::Int(1));
7309 }
7310
7311 #[test]
7312 fn rec_dotted_leaf_uses_rec_scope() {
7313 let v = ev("rec { types.a = f 1; f = x: x + 1; }.types.a");
7316 assert_eq!(v, Value::Int(2));
7317 }
7318
7319 #[test]
7320 fn rec_dotted_multiple_keys_merge() {
7321 let v = ev("rec { types.a = 1; types.b = 2; x = types; }.x");
7323 if let Value::Attrs(attrs) = v {
7324 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
7325 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
7326 } else {
7327 panic!("expected attrs");
7328 }
7329 }
7330
7331 #[test]
7332 fn rec_nixpkgs_parse_pattern() {
7333 let v = ev(r#"
7337 let
7338 mkOptionType = x: x;
7339 mergeOneOption = "merge";
7340 attrValues = builtins.attrValues;
7341 setType = name: value: { __type = name; } // value;
7342 mapAttrs = builtins.mapAttrs;
7343 enum = xs: mkOptionType { name = "enum"; check = x: builtins.elem x xs; };
7344 setTypes = type: mapAttrs (name: value: setType type.name ({ inherit name; } // value));
7345 in
7346 rec {
7347 types.openSB = mkOptionType { name = "sb"; merge = mergeOneOption; };
7348 types.significantByte = enum (attrValues significantBytes);
7349 significantBytes = setTypes types.openSB { bigEndian = {}; littleEndian = {}; };
7350 types.openCpuType = mkOptionType { name = "cpu-type"; };
7351 types.cpuType = enum (attrValues cpuTypes);
7352 cpuTypes = setTypes types.openCpuType { arm = { bits = 32; }; };
7353 }.types.openCpuType
7354 "#);
7355 if let Value::Attrs(attrs) = v {
7356 assert_eq!(
7357 force_value(attrs.get("name").unwrap()).unwrap(),
7358 Value::string("cpu-type")
7359 );
7360 } else {
7361 panic!("expected attrs");
7362 }
7363 }
7364
7365 #[test]
7366 fn let_dotted_leaf_uses_let_scope() {
7367 let v = ev("let a.x = f 1; f = x: x + 1; in a.x");
7369 assert_eq!(v, Value::Int(2));
7370 }
7371
7372 #[test]
7373 fn let_inherit_from_plus_dotted_overrides() {
7374 let v = ev(r#"
7380 let
7381 src = { types = { existing = true; }; };
7382 inherit (src) types;
7383 types.added = true;
7384 in types
7385 "#);
7386 if let Value::Attrs(attrs) = v {
7387 assert_eq!(
7389 force_value(attrs.get("added").unwrap()).unwrap(),
7390 Value::Bool(true)
7391 );
7392 assert!(attrs.get("existing").is_none());
7394 } else {
7395 panic!("expected attrs");
7396 }
7397 }
7398
7399 #[test]
7402 fn pattern_empty_no_args_no_ellipsis() {
7403 assert_eq!(ev("({}: 1) {}"), Value::Int(1));
7405 }
7406
7407 #[test]
7408 fn pattern_empty_with_ellipsis_accepts_extra() {
7409 assert_eq!(ev("({...}: 1) { a = 1; b = 2; }"), Value::Int(1));
7410 }
7411
7412 #[test]
7413 fn pattern_all_defaults() {
7414 assert_eq!(
7415 ev("({a ? 1, b ? 2}: a + b) {}"),
7416 Value::Int(3),
7417 );
7418 }
7419
7420 #[test]
7421 fn pattern_at_bind_before() {
7422 assert_eq!(ev("(args @ { x }: args.x) { x = 7; }"), Value::Int(7));
7424 }
7425
7426 #[test]
7427 fn pattern_at_bind_after() {
7428 assert_eq!(ev("({ x } @ args: args.x) { x = 7; }"), Value::Int(7));
7430 }
7431
7432 #[test]
7433 fn pattern_default_references_other_arg() {
7434 assert_eq!(ev("({a, b ? a + 1}: b) {a = 10;}"), Value::Int(11));
7436 }
7437
7438 #[test]
7439 fn pattern_required_missing_errors() {
7440 let result = eval("({ a, b }: a) { a = 1; }");
7441 assert!(result.is_err());
7442 }
7443
7444 #[test]
7445 fn pattern_unexpected_errors_without_ellipsis() {
7446 let result = eval("({ a }: a) { a = 1; b = 2; }");
7447 assert!(result.is_err());
7448 }
7449
7450 #[test]
7453 fn apply_int_errors() {
7454 let result = eval("42 5");
7455 assert!(result.is_err());
7456 }
7457
7458 #[test]
7459 fn apply_string_errors() {
7460 let result = eval(r#""hi" 5"#);
7461 assert!(result.is_err());
7462 }
7463
7464 #[test]
7465 fn apply_attrset_without_functor_errors() {
7466 let result = eval("{ x = 1; } 5");
7467 assert!(result.is_err());
7468 let msg = format!("{}", result.unwrap_err());
7469 assert!(msg.contains("__functor") || msg.contains("cannot call"));
7470 }
7471
7472 #[test]
7475 fn select_multi_segment_with_default() {
7476 assert_eq!(ev("{ a = { b = 1; }; }.a.c or 99"), Value::Int(99));
7478 }
7479
7480 #[test]
7481 fn select_from_int_errors() {
7482 let result = eval("(1).x");
7483 assert!(result.is_err());
7484 }
7485
7486 #[test]
7489 fn has_attr_on_non_set_returns_false() {
7490 assert_eq!(ev("1 ? x"), Value::Bool(false));
7492 }
7493
7494 #[test]
7495 fn has_attr_nested_path_present() {
7496 assert_eq!(ev("{ a = { b = 1; }; } ? a.b"), Value::Bool(true));
7497 }
7498
7499 #[test]
7500 fn has_attr_nested_path_missing() {
7501 assert_eq!(ev("{ a = { b = 1; }; } ? a.c"), Value::Bool(false));
7502 }
7503
7504 #[test]
7505 fn has_attr_intermediate_missing_returns_false() {
7506 assert_eq!(ev("{} ? a.b.c"), Value::Bool(false));
7507 }
7508
7509 #[test]
7512 fn list_with_function_value() {
7513 let v = ev("[(x: x + 1)]");
7514 if let Value::List(items) = v {
7515 assert_eq!(items.len(), 1);
7516 let forced = force_value(&items[0]).unwrap();
7518 assert!(matches!(forced, Value::Lambda(_)));
7519 } else {
7520 panic!("expected list");
7521 }
7522 }
7523
7524 #[test]
7527 fn inherit_unknown_name_errors() {
7528 let result = eval("let x = 1; in let inherit nonexistent; in nonexistent");
7529 assert!(result.is_err());
7530 }
7531
7532 #[test]
7535 fn string_concat_no_context_when_both_plain() {
7536 let v = ev(r#""abc" + "def""#);
7537 if let Value::String(ns) = v {
7538 assert_eq!(ns.chars, "abcdef");
7539 assert!(!ns.has_context());
7540 } else {
7541 panic!("expected string");
7542 }
7543 }
7544
7545 #[test]
7548 fn parens_around_expression() {
7549 assert_eq!(ev("(1 + 2)"), Value::Int(3));
7550 }
7551
7552 #[test]
7553 fn nested_parens() {
7554 assert_eq!(ev("(((42)))"), Value::Int(42));
7555 }
7556
7557 #[test]
7560 fn throw_propagates_as_error() {
7561 let result = eval(r#"builtins.throw "kaboom""#);
7562 match result {
7563 Err(EvalError::Throw(s)) => assert!(s.contains("kaboom")),
7564 other => panic!("expected Throw, got {other:?}"),
7565 }
7566 }
7567
7568 #[test]
7569 fn assert_failed_propagates_as_error() {
7570 let result = eval("assert false; 1");
7571 match result {
7572 Err(EvalError::AssertionFailed(_)) => {}
7573 other => panic!("expected AssertionFailed, got {other:?}"),
7574 }
7575 }
7576
7577 #[test]
7580 fn string_no_interp_yields_no_context() {
7581 let v = ev(r#""just literal""#);
7582 if let Value::String(ns) = v {
7583 assert!(!ns.has_context());
7584 } else {
7585 panic!("expected string");
7586 }
7587 }
7588
7589 #[test]
7598 fn interp_path_copies_to_store_byte_matches_cppnix() {
7599 let dir = std::env::temp_dir().join(format!("sui-r5-interp-{}", std::process::id()));
7600 let _ = std::fs::remove_dir_all(&dir);
7601 std::fs::create_dir_all(&dir).unwrap();
7602 let f = dir.join("data.txt");
7603 std::fs::write(&f, b"hello\n").unwrap();
7604 let expr = format!(r#""${{{}}}""#, f.display());
7605 let v = eval(&expr).unwrap();
7606 if let Value::String(ns) = v {
7607 assert_eq!(
7608 ns.chars.to_string(),
7609 "/nix/store/y9dmvfhip31hg8ia4njwjz9vfa3ndphr-data.txt",
7610 );
7611 assert!(ns.has_context());
7612 } else {
7613 panic!("expected string");
7614 }
7615 let _ = std::fs::remove_dir_all(&dir);
7616 }
7617
7618 #[test]
7627 fn parse_error_unbalanced_braces() {
7628 let result = eval("{ a = 1");
7629 assert!(result.is_err());
7630 let err = result.unwrap_err();
7631 assert!(matches!(err, EvalError::ParseError(_)));
7632 }
7633
7634 #[test]
7635 fn parse_error_dangling_let() {
7636 let result = eval("let in");
7637 assert!(result.is_err());
7638 }
7639
7640 #[test]
7641 fn parse_error_empty_input() {
7642 let result = eval("");
7643 assert!(result.is_err());
7644 }
7645
7646 #[test]
7649 fn float_int_subtraction() {
7650 assert_eq!(ev("3.5 - 1"), Value::Float(2.5));
7651 }
7652
7653 #[test]
7654 fn int_float_subtraction() {
7655 assert_eq!(ev("3 - 0.5"), Value::Float(2.5));
7656 }
7657
7658 #[test]
7659 fn float_float_division() {
7660 assert_eq!(ev("6.0 / 2.0"), Value::Float(3.0));
7661 }
7662
7663 #[test]
7664 fn int_float_multiplication() {
7665 assert_eq!(ev("3 * 2.5"), Value::Float(7.5));
7666 }
7667
7668 #[test]
7671 fn compare_int_float_less() {
7672 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
7673 }
7674
7675 #[test]
7676 fn compare_float_int_more() {
7677 assert_eq!(ev("3.5 > 3"), Value::Bool(true));
7678 }
7679
7680 #[test]
7681 fn compare_equal_int_float() {
7682 assert_eq!(ev("3 <= 3.0"), Value::Bool(true));
7683 }
7684
7685 #[test]
7688 fn equal_lists_same() {
7689 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
7690 }
7691
7692 #[test]
7693 fn equal_lists_diff_length() {
7694 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
7695 }
7696
7697 #[test]
7698 fn not_equal_lists() {
7699 assert_eq!(ev("[1] != [2]"), Value::Bool(true));
7700 }
7701
7702 #[test]
7703 fn equal_attrsets_same() {
7704 assert_eq!(ev("{a = 1; b = 2;} == {b = 2; a = 1;}"), Value::Bool(true));
7705 }
7706
7707 #[test]
7714 fn lambda_self_equality_in_attrset() {
7715 assert_eq!(
7717 ev("let f = x: x; in { a = 1; inherit f; } == { a = 1; inherit f; }"),
7718 Value::Bool(true),
7719 );
7720 }
7721
7722 #[test]
7723 fn lambda_self_reference_attrset_equality() {
7724 assert_eq!(
7726 ev("let x = { a = 1; f = y: y; }; in x == x"),
7727 Value::Bool(true),
7728 );
7729 }
7730
7731 #[test]
7732 fn lambda_different_closures_not_equal() {
7733 assert_eq!(
7735 ev("{ f = x: x; } == { f = x: x; }"),
7736 Value::Bool(false),
7737 );
7738 }
7739
7740 #[test]
7741 fn lambda_ne_does_not_force_unused_branch() {
7742 assert_eq!(
7745 ev("let ls = { a = 1; f = x: x; }; in if ls != ls then builtins.throw \"bug\" else 42"),
7746 Value::Int(42),
7747 );
7748 }
7749
7750 #[test]
7753 fn force_value_through_thunk() {
7754 let root = rnix::Root::parse("1 + 2");
7755 let expr = root.tree().expr().unwrap();
7756 let thunk = Thunk::new_suspended(expr, Env::new());
7757 let val = Value::Thunk(thunk);
7758 assert_eq!(force_value(&val).unwrap(), Value::Int(3));
7759 }
7760
7761 #[test]
7764 fn try_eval_catches_thrown_error() {
7765 let v = ev(r#"(builtins.tryEval (builtins.throw "oops")).success"#);
7767 assert_eq!(v, Value::Bool(false));
7768 }
7769
7770 #[test]
7771 fn try_eval_returns_value_on_success() {
7772 let v = ev("(builtins.tryEval 42).value");
7773 assert_eq!(v, Value::Int(42));
7774 }
7775
7776 #[test]
7779 fn legacy_let_returns_body_attr() {
7780 assert_eq!(ev("let { x = 1; body = x + 41; }"), Value::Int(42));
7784 }
7785
7786 #[test]
7787 fn legacy_let_missing_body_errors() {
7788 let result = eval("let { x = 1; }");
7789 assert!(result.is_err());
7790 }
7791
7792 #[test]
7793 fn legacy_let_with_inherit_from_scope() {
7794 assert_eq!(
7795 ev("let outer = 5; in let { inherit outer; body = outer * 2; }"),
7796 Value::Int(10),
7797 );
7798 }
7799
7800 #[test]
7803 fn interp_with_string_concat_preserves_order() {
7804 assert_eq!(
7805 ev(r#"let a = "x"; b = "y"; in "${a}-${b}""#),
7806 Value::string("x-y"),
7807 );
7808 }
7809
7810 #[test]
7811 fn interp_only_literal_part() {
7812 assert_eq!(ev(r#""no interp here""#), Value::string("no interp here"));
7813 }
7814
7815 #[test]
7818 fn dynamic_attr_via_string_key_in_set() {
7819 assert_eq!(ev(r#"{ "a" = 1; }.a"#), Value::Int(1));
7821 }
7822
7823 #[test]
7824 fn dynamic_attr_via_interpolated_key() {
7825 let v = ev(r#"let k = "foo"; in { ${k} = 99; }.foo"#);
7826 assert_eq!(v, Value::Int(99));
7827 }
7828
7829 #[test]
7832 fn select_with_string_key() {
7833 let v = ev(r#"{ a = 42; }."a""#);
7834 assert_eq!(v, Value::Int(42));
7835 }
7836
7837 #[test]
7840 fn apply_attrset_with_functor_works() {
7841 let v = ev("let s = { __functor = self: x: x + 1; }; in s 5");
7842 assert_eq!(v, Value::Int(6));
7843 }
7844
7845 #[test]
7848 fn double_negate_int() {
7849 assert_eq!(ev("- (-5)"), Value::Int(5));
7850 }
7851
7852 #[test]
7855 fn inherit_in_let_makes_name_available() {
7856 assert_eq!(
7857 ev("let src = { a = 7; }; in let inherit (src) a; in a"),
7858 Value::Int(7),
7859 );
7860 }
7861
7862 #[test]
7865 fn path_plus_string_yields_path() {
7866 let v = ev(r#"/foo + "/bar""#);
7867 match v {
7868 Value::Path(p) => assert_eq!(&*p, "/foo/bar"),
7869 _ => panic!("expected path"),
7870 }
7871 }
7872
7873 #[test]
7876 fn attrset_value_not_forced_unless_selected() {
7877 assert_eq!(
7880 ev(r#"{ bad = builtins.throw "boom"; good = 42; }.good"#),
7881 Value::Int(42),
7882 );
7883 }
7884
7885 #[test]
7888 fn lambda_recursive_via_let() {
7889 assert_eq!(
7891 ev("let fact = n: if n == 0 then 1 else n * fact (n - 1); in fact 5"),
7892 Value::Int(120),
7893 );
7894 }
7895
7896 #[test]
7899 fn select_with_dynamic_key_via_var() {
7900 assert_eq!(ev(r#"let k = { x = 1; }; in k.x"#), Value::Int(1));
7903 }
7904
7905 #[test]
7908 fn compare_string_lex_greater_or_equal() {
7909 assert_eq!(ev(r#""b" >= "a""#), Value::Bool(true));
7910 assert_eq!(ev(r#""a" >= "a""#), Value::Bool(true));
7911 assert_eq!(ev(r#""a" >= "b""#), Value::Bool(false));
7912 }
7913
7914 #[test]
7917 fn equal_int_string_false() {
7918 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
7919 }
7920
7921 #[test]
7922 fn equal_null_int_false() {
7923 assert_eq!(ev("null == 0"), Value::Bool(false));
7924 }
7925
7926 #[test]
7929 fn update_with_let_bound_operands() {
7930 assert_eq!(
7931 ev("let a = { x = 1; }; b = { y = 2; }; in (a // b).y"),
7932 Value::Int(2),
7933 );
7934 }
7935
7936 #[test]
7939 fn concat_lists_from_let() {
7940 assert_eq!(
7941 ev("let a = [1 2]; b = [3 4]; in builtins.length (a ++ b)"),
7942 Value::Int(4),
7943 );
7944 }
7945
7946 #[test]
7949 fn interp_list_coerces_with_spaces() {
7950 assert_eq!(
7953 ev(r#""${toString [1 2 3]}""#),
7954 Value::string("1 2 3"),
7955 );
7956 }
7957
7958 #[test]
7959 fn interp_list_directly_coerces() {
7960 assert_eq!(
7962 ev(r#""${[1 2]}""#),
7963 Value::string("1 2"),
7964 );
7965 }
7966
7967 #[test]
7970 fn interp_outpath_attrset() {
7971 assert_eq!(
7972 ev(r#"let x = { outPath = "/nix/store/abc"; }; in "${x}""#),
7973 Value::string("/nix/store/abc"),
7974 );
7975 }
7976
7977 #[test]
7978 fn interp_tostring_takes_priority_over_outpath() {
7979 assert_eq!(
7980 ev(r#"let x = { __toString = self: "custom"; outPath = "/ignored"; }; in "${x}""#),
7981 Value::string("custom"),
7982 );
7983 }
7984
7985 #[test]
7986 fn interp_derivation_coerces_to_outpath() {
7987 let result = eval(r#"
7989 let drv = builtins.derivation {
7990 name = "test";
7991 system = "x86_64-linux";
7992 builder = "/bin/sh";
7993 };
7994 in "${drv}"
7995 "#).unwrap();
7996 if let Value::String(s) = result {
7997 assert!(s.chars.starts_with("/nix/store/"), "got: {}", s.chars);
7998 } else {
7999 panic!("expected string");
8000 }
8001 }
8002
8003 #[test]
8006 fn interp_lambda_errors() {
8007 let result = eval(r#""${x: x}""#);
8008 assert!(result.is_err());
8009 }
8010
8011 #[test]
8014 fn force_value_int_returns_same() {
8015 let v = Value::Int(42);
8016 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
8017 }
8018
8019 #[test]
8020 fn force_value_bool_returns_same() {
8021 let v = Value::Bool(true);
8022 assert_eq!(force_value(&v).unwrap(), Value::Bool(true));
8023 }
8024
8025 #[test]
8026 fn force_value_string_returns_same() {
8027 let v = Value::string("hello");
8028 assert_eq!(force_value(&v).unwrap(), Value::string("hello"));
8029 }
8030
8031 #[test]
8032 fn force_value_attrs_returns_same() {
8033 let mut a = NixAttrs::new();
8034 a.insert("x".to_string(), Value::Int(1));
8035 let v = Value::Attrs(Rc::new(a.clone()));
8036 assert_eq!(force_value(&v).unwrap(), Value::Attrs(Rc::new(a)));
8037 }
8038
8039 #[test]
8040 fn force_value_list_returns_same() {
8041 let v = Value::list(vec![Value::Int(1), Value::Int(2)]);
8042 assert_eq!(
8043 force_value(&v).unwrap(),
8044 Value::list(vec![Value::Int(1), Value::Int(2)]),
8045 );
8046 }
8047
8048 #[test]
8049 fn force_value_null_returns_null() {
8050 let v = Value::Null;
8051 assert_eq!(force_value(&v).unwrap(), Value::Null);
8052 }
8053
8054 #[test]
8055 fn force_value_evaluated_thunk_returns_cached() {
8056 let v = ev("let x = 1 + 2; in x");
8058 assert_eq!(v, Value::Int(3));
8059 assert_eq!(force_value(&v).unwrap(), Value::Int(3));
8061 }
8062
8063 #[test]
8066 fn tco_if_true_condition() {
8067 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
8068 }
8069
8070 #[test]
8071 fn tco_if_false_condition() {
8072 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
8073 }
8074
8075 #[test]
8076 fn tco_deeply_nested_if_else_chain() {
8077 let mut expr = String::from("150");
8080 for i in (1..150).rev() {
8081 expr = format!("if false then {} else {}", i, expr);
8082 }
8083 let v = ev(&expr);
8084 assert_eq!(v, Value::Int(150));
8085 }
8086
8087 #[test]
8088 fn tco_assert_true_passes_through() {
8089 assert_eq!(ev("assert true; 42"), Value::Int(42));
8090 }
8091
8092 #[test]
8093 fn tco_assert_false_throws_assertion_failed() {
8094 let result = eval("assert false; 42");
8095 assert!(result.is_err());
8096 let err = result.unwrap_err();
8097 assert!(
8098 matches!(err, EvalError::AssertionFailed(_)),
8099 "expected AssertionFailed, got: {err}",
8100 );
8101 }
8102
8103 #[test]
8104 fn tco_with_makes_scope_available() {
8105 assert_eq!(ev("with { x = 10; y = 20; }; x + y"), Value::Int(30));
8106 }
8107
8108 #[test]
8109 fn tco_let_in_creates_bindings() {
8110 assert_eq!(ev("let a = 5; in a"), Value::Int(5));
8111 }
8112
8113 #[test]
8114 fn tco_let_in_multiple_bindings() {
8115 assert_eq!(ev("let a = 1; b = 2; c = 3; in a + b + c"), Value::Int(6));
8116 }
8117
8118 #[test]
8121 fn eval_attrset_empty() {
8122 let v = ev("{}");
8123 if let Value::Attrs(attrs) = v {
8124 assert!(attrs.is_empty(), "expected empty attrset");
8125 } else {
8126 panic!("expected attrset, got {v:?}");
8127 }
8128 }
8129
8130 #[test]
8131 fn eval_attrset_simple_kv() {
8132 let v = ev("{ a = 1; b = 2; }");
8133 if let Value::Attrs(attrs) = v {
8134 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8135 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8136 } else {
8137 panic!("expected attrset, got {v:?}");
8138 }
8139 }
8140
8141 #[test]
8142 fn eval_attrset_recursive() {
8143 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
8144 assert_eq!(ev("(rec { a = 1; b = a + 1; }).a"), Value::Int(1));
8145 }
8146
8147 #[test]
8148 fn eval_attrset_inherit_from_scope() {
8149 assert_eq!(ev("let x = 1; in { inherit x; }.x"), Value::Int(1));
8150 }
8151
8152 #[test]
8153 fn eval_attrset_inherit_from_expr() {
8154 assert_eq!(
8155 ev("{ inherit (builtins) true; }.true"),
8156 Value::Bool(true),
8157 );
8158 }
8159
8160 #[test]
8161 fn eval_attrset_dotted_path() {
8162 assert_eq!(ev("{ a.b.c = 1; }.a.b.c"), Value::Int(1));
8163 }
8164
8165 #[test]
8166 fn eval_attrset_update_merge() {
8167 let v = ev("{ a = 1; } // { b = 2; }");
8168 if let Value::Attrs(attrs) = v {
8169 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8170 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8171 } else {
8172 panic!("expected attrset, got {v:?}");
8173 }
8174 }
8175
8176 #[test]
8179 fn eval_apply_simple_function() {
8180 assert_eq!(ev("(x: x + 1) 2"), Value::Int(3));
8181 }
8182
8183 #[test]
8184 fn eval_apply_pattern_destructuring() {
8185 assert_eq!(ev("({a, b}: a + b) { a = 1; b = 2; }"), Value::Int(3));
8186 }
8187
8188 #[test]
8189 fn eval_apply_default_arguments() {
8190 assert_eq!(ev("({a, b ? 0}: a + b) { a = 1; }"), Value::Int(1));
8191 }
8192
8193 #[test]
8194 fn eval_apply_ellipsis() {
8195 assert_eq!(ev("({a, ...}: a) { a = 1; b = 2; }"), Value::Int(1));
8196 }
8197
8198 #[test]
8201 fn eval_select_single_key() {
8202 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
8203 }
8204
8205 #[test]
8206 fn eval_select_multi_level() {
8207 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
8208 }
8209
8210 #[test]
8211 fn eval_select_with_or_default() {
8212 assert_eq!(ev("{}.a or 42"), Value::Int(42));
8213 }
8214
8215 #[test]
8216 fn eval_select_missing_key_without_default_throws() {
8217 let result = eval("{}.a");
8218 assert!(result.is_err());
8219 }
8220
8221 #[test]
8224 fn binop_add_ints() {
8225 assert_eq!(ev("1 + 2"), Value::Int(3));
8226 }
8227
8228 #[test]
8229 fn binop_sub_ints() {
8230 assert_eq!(ev("3 - 1"), Value::Int(2));
8231 }
8232
8233 #[test]
8234 fn binop_mul_ints() {
8235 assert_eq!(ev("2 * 3"), Value::Int(6));
8236 }
8237
8238 #[test]
8239 fn binop_div_ints() {
8240 assert_eq!(ev("6 / 2"), Value::Int(3));
8241 }
8242
8243 #[test]
8244 fn binop_float_arithmetic() {
8245 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
8246 }
8247
8248 #[test]
8249 fn binop_string_concat() {
8250 assert_eq!(
8251 ev(r#""hello" + " " + "world""#),
8252 Value::string("hello world"),
8253 );
8254 }
8255
8256 #[test]
8257 fn binop_list_concat() {
8258 assert_eq!(
8259 ev("[1 2] ++ [3 4]"),
8260 Value::list(vec![
8261 Value::Int(1),
8262 Value::Int(2),
8263 Value::Int(3),
8264 Value::Int(4),
8265 ]),
8266 );
8267 }
8268
8269 #[test]
8270 fn binop_attrset_update() {
8271 let v = ev("{ a = 1; } // { b = 2; }");
8272 if let Value::Attrs(attrs) = v {
8273 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8274 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8275 } else {
8276 panic!("expected attrset, got {v:?}");
8277 }
8278 }
8279
8280 #[test]
8281 fn binop_less_than() {
8282 assert_eq!(ev("1 < 2"), Value::Bool(true));
8283 assert_eq!(ev("2 < 1"), Value::Bool(false));
8284 }
8285
8286 #[test]
8287 fn binop_greater_than() {
8288 assert_eq!(ev("2 > 1"), Value::Bool(true));
8289 assert_eq!(ev("1 > 2"), Value::Bool(false));
8290 }
8291
8292 #[test]
8293 fn binop_equal() {
8294 assert_eq!(ev("1 == 1"), Value::Bool(true));
8295 assert_eq!(ev("1 == 2"), Value::Bool(false));
8296 }
8297
8298 #[test]
8299 fn binop_not_equal() {
8300 assert_eq!(ev("1 != 2"), Value::Bool(true));
8301 assert_eq!(ev("1 != 1"), Value::Bool(false));
8302 }
8303
8304 #[test]
8305 fn binop_logical_and() {
8306 assert_eq!(ev("true && false"), Value::Bool(false));
8307 assert_eq!(ev("true && true"), Value::Bool(true));
8308 }
8309
8310 #[test]
8311 fn binop_logical_or() {
8312 assert_eq!(ev("true || false"), Value::Bool(true));
8313 assert_eq!(ev("false || false"), Value::Bool(false));
8314 }
8315
8316 #[test]
8317 fn binop_logical_not() {
8318 assert_eq!(ev("!true"), Value::Bool(false));
8319 assert_eq!(ev("!false"), Value::Bool(true));
8320 }
8321
8322 #[test]
8323 fn binop_implication() {
8324 assert_eq!(ev("false -> true"), Value::Bool(true));
8325 assert_eq!(ev("false -> false"), Value::Bool(true));
8326 assert_eq!(ev("true -> true"), Value::Bool(true));
8327 assert_eq!(ev("true -> false"), Value::Bool(false));
8328 }
8329}