nickel_lang_core/term/mod.rs
1//! AST of a Nickel expression. [Term] used to be the main and single representation across the
2//! whole Nickel pipeline, but is now used only as a runtime representation, and will be phased out
3//! progressively as the implementation of the bytecode virtual machine (RFC007) progresses.
4pub mod pattern;
5pub mod record;
6pub mod string;
7
8use record::{Field, FieldDeps, Include, RecordData, RecordDeps, SharedMetadata};
9use smallvec::SmallVec;
10
11use crate::{
12 cache::InputFormat,
13 combine::Combine,
14 error::{EvalErrorKind, ParseError},
15 eval::{Environment, contract_eq, value::NickelValue},
16 files::FileId,
17 identifier::{Ident, LocIdent},
18 impl_display_from_pretty,
19 label::{Label, MergeLabel},
20 position::{PosIdx, PosTable},
21 pretty::PrettyPrintCap,
22 traverse::*,
23 typ::{Type, UnboundTypeVariableError},
24};
25
26pub use crate::ast::{MergePriority, RecordOpKind, StringChunk as StrChunk};
27
28use crate::metrics::increment;
29
30pub use malachite::{
31 Integer,
32 base::{
33 num::{
34 basic::traits::Zero,
35 conversion::traits::{IsInteger, RoundingFrom, ToSci},
36 },
37 rounding_modes::RoundingMode,
38 },
39 rational::Rational,
40};
41
42pub use crate::ast::Number;
43
44use serde::{Serialize, Serializer};
45
46// Because we use `IndexMap` for records, consumer of Nickel (as a library) might have to
47// manipulate values of this type, so we re-export it.
48pub use indexmap::IndexMap;
49
50use std::{ffi::OsString, fmt, ops::Deref, rc::Rc};
51
52/// The payload of a `Term::ForeignId`.
53pub type ForeignIdPayload = u64;
54
55#[derive(Clone, Debug, PartialEq)]
56pub struct FunData {
57 pub arg: LocIdent,
58 pub body: NickelValue,
59}
60
61#[derive(Clone, Debug, PartialEq)]
62pub struct LetData {
63 pub bindings: SmallVec<[(LocIdent, NickelValue); 4]>,
64 pub body: NickelValue,
65 pub attrs: LetAttrs,
66}
67
68#[derive(Clone, Debug, PartialEq)]
69pub struct RecRecordData {
70 pub record: RecordData,
71 /// Fields defined through `include` expressions.
72 pub includes: Vec<Include>,
73 /// Dynamic fields, whose name is defined by interpolation.
74 pub dyn_fields: Vec<(NickelValue, Field)>,
75 /// Dependency tracking between fields. It is filled by the free var transformation, and is
76 /// `None` before.
77 pub deps: Option<RecordDeps>,
78 /// If the recursive record has been closurized already (currently this is mostly the case for
79 /// records coming out from merging.
80 pub closurized: bool,
81}
82
83#[derive(Clone, Debug, PartialEq)]
84pub struct Op1Data {
85 pub op: UnaryOp,
86 pub arg: NickelValue,
87}
88
89#[derive(Clone, Debug, PartialEq)]
90pub struct Op2Data {
91 pub op: BinaryOp,
92 pub arg1: NickelValue,
93 pub arg2: NickelValue,
94}
95
96#[derive(Clone, Debug, PartialEq)]
97pub struct OpNData {
98 pub op: NAryOp,
99 pub args: Vec<NickelValue>,
100}
101
102#[derive(Clone, Debug, PartialEq)]
103pub struct SealedData {
104 pub key: SealingKey,
105 pub inner: NickelValue,
106 pub label: Rc<Label>,
107}
108
109#[derive(Clone, Debug, PartialEq)]
110pub struct AnnotatedData {
111 pub annot: Rc<TypeAnnotation>,
112 pub inner: NickelValue,
113}
114
115#[derive(Clone, Debug, PartialEq)]
116pub struct AppData {
117 pub head: NickelValue,
118 pub arg: NickelValue,
119}
120
121/// The runtime representation of a Nickel computation.
122///
123/// # History
124///
125/// [Term] used to be the single representation from parsing to execution, but a new, more compact
126/// and closer-to-source AST has been introduced in [nickel_lang_parser::ast] and is now used for the
127/// front-end (parser, typechecker, and LSP).
128///
129/// [Term] remains as a temporary runtime representation until the bytecode virtual machine and
130/// compiler are fully implemented (see
131/// [RFC007](https://github.com/tweag/nickel/blob/master/rfcs/007-bytecode-interpreter.md)). [Term]
132/// is a hybrid representation where values (weak head normal forms) use the the compact
133/// representation described in RFC007. The remaining constructors of [Term] are the equivalent of
134/// "code" in the future VM, that is, computations.
135///
136/// # Memory representation
137///
138/// We try to strike a balance between the memory consumption of an expression, and runtime: having
139/// more data inline and less indirection can make evaluation faster, but it consumes more memory
140/// (and is detrimental past some threshold). In practice, we try to keep the size [Self] to fit in
141/// a modern cache line (smaller or equal to 64 bytes). Some data are boxed, other are reference
142/// counted: this reflects their usage. Typically, patterns in let or function are only seen during
143/// program transformation currently as they are then compiled away; there shouldn't be any sharing
144/// here, so it doesn't make sense to use [std::rc::Rc]. Other values might be cloned a lot on the
145/// other hand, so if they aren't expected to be modified much, they are put behind `Rc`.
146#[derive(Debug, Clone, PartialEq)]
147pub enum Term {
148 /// A string containing interpolated expressions, represented as a list of either literals or
149 /// expressions yet to be concatenated.
150 ///
151 /// /|\ CHUNKS ARE STORED IN REVERSE ORDER. As they will be only popped one by one from the
152 /// head of the list during evaluation, doing so on a `Vec` is costly, and using a more complex
153 /// data structure is not really necessary, as once created, no other than popping is ever
154 /// done. In consequence, we just reverse the vector at parsing time, so that we can then pop
155 /// efficiently from the back of it.
156 StrChunks(Vec<StrChunk<NickelValue>>),
157
158 /// A function. Grabs the value of its parameter on the stack, puts it in the environment and
159 /// proceeds with the valuation of the body.
160 Fun(FunData),
161
162 /// A let binding. Adds the binding to the environment and proceeds with the evaluation of the
163 /// body.
164 Let(Box<LetData>),
165
166 /// An application. Push the argument on the stack and proceed with the evaluation of the head.
167 App(AppData),
168
169 /// A variable. Fetch the corresponding value from the environment.
170 Var(LocIdent),
171
172 /// A recursive record, where the fields can reference each others. Computes the fixpoint and
173 /// produces a record value with the proper recursive environment.
174 RecRecord(Box<RecRecordData>),
175
176 /// A container value (array or record) that has yet to be closurized. This will closurize each
177 /// elements of the container in the current environment.
178 Closurize(NickelValue),
179
180 /// A primitive unary operator.
181 Op1(Op1Data),
182
183 /// A primitive binary operator.
184 Op2(Op2Data),
185
186 /// An primitive n-ary operator.
187 OpN(OpNData),
188
189 /// A sealed term.
190 ///
191 /// Sealed terms are introduced by contracts on polymorphic types. Take the following example:
192 ///
193 /// ```text
194 /// let f | forall a b. a -> b -> a = fun x y => y in
195 /// f true "a"
196 /// ```
197 ///
198 /// This function is ill-typed. To check that, a polymorphic contract will:
199 ///
200 /// - Assign a unique identifier to each type variable: say `a => 1`, `b => 2`
201 /// - For each cast on a negative occurrence of a type variable `a` or `b` (corresponding to an
202 /// argument position), tag the argument with the associated identifier. In our example, `f
203 /// true "a"` will push `Sealed(1, true)` then `Sealed(2, "a")` on the stack.
204 /// - For each cast on a positive occurrence of a type variable, this contract check that the
205 /// term is of the form `Sealed(id, term)` where `id` corresponds to the identifier of the
206 /// type variable. In our example, the last cast to `a` finds `Sealed(2, "a")`, while it
207 /// expected `Sealed(1, _)`, hence it raises a positive blame.
208 Sealed(SealedData),
209
210 /// A term with a type and/or contract annotation.
211 Annotated(AnnotatedData),
212
213 /// An unresolved import.
214 Import(Import),
215
216 /// A resolved import (which has already been loaded and parsed).
217 ResolvedImport(FileId),
218
219 /// A term that couldn't be parsed properly. Used by the LSP to handle partially valid
220 /// programs.
221 ParseError(Box<ParseError>),
222
223 /// A delayed runtime error. Usually, errors are raised and abort the execution right away,
224 /// without the need to store them in the AST. However, some cases require a term which aborts
225 /// with a specific error if evaluated, but is fine being stored and passed around.
226 ///
227 /// The main use-cae is currently missing field definitions: when evaluating a recursive record
228 /// to a normal record with a recursive environment, we might find fields that aren't defined
229 /// currently, eg:
230 ///
231 /// ```nickel
232 /// let r = {
233 /// foo = bar + 1,
234 /// bar | Number,
235 /// baz = 2,
236 /// } in
237 /// r.baz + (r & {bar = 1}).foo
238 /// ```
239 ///
240 /// This program is valid, but when evaluating `r` in `r.baz`, `bar` doesn't have a definition
241 /// yet. This is fine because we don't evaluate `bar` nor `foo`. Still, we have to put
242 /// something in the recursive environment. And if we wrote `r.foo` instead, we should raise a
243 /// missing field definition error. Thus, we need to bind `bar` to a term wich, if ever
244 /// evaluated, will raise a proper missing field definition error. This is precisely the
245 /// behavior of `RuntimeError`.
246 RuntimeError(Box<EvalErrorKind>),
247}
248
249#[derive(Clone, Debug, PartialEq, Eq, Hash)]
250/// Specifies where something should be imported from.
251pub enum Import {
252 Path {
253 path: OsString,
254 format: InputFormat,
255 },
256 /// Importing packges requires a [`crate::package::PackageMap`] to translate the location
257 /// to a path. The format is always Nickel.
258 Package {
259 id: Ident,
260 },
261}
262
263/// A unique sealing key, introduced by polymorphic contracts.
264pub type SealingKey = i32;
265
266/// Type of let-binding. This only affects run-time behavior. Revertible bindings introduce
267/// revertible cache elements at evaluation, which are devices used for the implementation of
268/// recursive records merging. See the [`crate::eval::merge`] and [`crate::eval`] modules for more
269/// details.
270#[derive(Debug, Eq, PartialEq, Clone, Default)]
271pub enum BindingType {
272 #[default]
273 Normal,
274
275 /// In the revertible case, we also store an optional set of dependencies. See
276 /// [`crate::transform::free_vars`] for more details.
277 Revertible(FieldDeps),
278}
279
280/// A runtime representation of a contract, as a term and a label ready to be applied via
281/// [BinaryOp::ContractApply].
282#[derive(Debug, Clone, PartialEq)]
283pub struct RuntimeContract {
284 /// The pending contract, which can be a function, a type, a custom contract or a record.
285 pub contract: NickelValue,
286 /// The blame label.
287 pub label: Label,
288}
289
290impl RuntimeContract {
291 pub fn new(contract: NickelValue, label: Label) -> Self {
292 RuntimeContract { contract, label }
293 }
294
295 /// Generate a runtime contract from a type used as a static type annotation and a label. Use
296 /// the guarantees of the static type system to optimize and simplify the contract.
297 pub fn from_static_type(
298 pos_table: &mut PosTable,
299 labeled_typ: LabeledType,
300 ) -> Result<Self, UnboundTypeVariableError> {
301 Ok(RuntimeContract {
302 contract: labeled_typ.typ.contract_static(pos_table)?,
303 label: labeled_typ.label,
304 })
305 }
306
307 /// Generate a runtime contract from a type used as a contract annotation and a label.
308 pub fn from_type(
309 pos_table: &mut PosTable,
310 labeled_ty: LabeledType,
311 ) -> Result<Self, UnboundTypeVariableError> {
312 Ok(RuntimeContract::new(
313 labeled_ty.typ.contract(pos_table)?,
314 labeled_ty.label,
315 ))
316 }
317
318 /// Map a function over the term representing the underlying contract.
319 pub fn map_contract<F>(self, f: F) -> Self
320 where
321 F: FnOnce(NickelValue) -> NickelValue,
322 {
323 RuntimeContract {
324 contract: f(self.contract),
325 ..self
326 }
327 }
328
329 /// Apply this contract to a value.
330 pub fn apply(self, value: NickelValue, pos_idx: PosIdx) -> NickelValue {
331 use crate::mk_app;
332
333 mk_app!(
334 make::op2(
335 BinaryOp::ContractApply,
336 self.contract,
337 NickelValue::label_posless(self.label),
338 )
339 .with_pos_idx(pos_idx),
340 value
341 )
342 .with_pos_idx(pos_idx)
343 }
344
345 /// Apply a series of contracts to a value, in order.
346 pub fn apply_all<I>(value: NickelValue, contracts: I, pos_idx: PosIdx) -> NickelValue
347 where
348 I: IntoIterator<Item = Self>,
349 {
350 contracts
351 .into_iter()
352 .fold(value, |acc, ctr| ctr.apply(acc, pos_idx))
353 }
354
355 /// Push a pending contract to a vector of contracts if the contract to add isn't already
356 /// present in the vector, according to the notion of contract equality defined in
357 /// [crate::eval::contract_eq].
358 pub fn push_dedup(
359 contracts: &mut Vec<RuntimeContract>,
360 env1: &Environment,
361 ctr: Self,
362 env2: &Environment,
363 ) {
364 for c in contracts.iter() {
365 increment!("contracts:equality-checks");
366
367 if contract_eq::contract_eq(&c.contract, env1, &ctr.contract, env2) {
368 increment!("contracts:deduped");
369 return;
370 }
371 }
372
373 contracts.push(ctr);
374 }
375
376 /// Concatenate two deduplicated contract vectors into a deduplicated contract vector.
377 pub fn combine_dedup(
378 contracts1: Vec<RuntimeContract>,
379 env1: &Environment,
380 contracts2: Vec<RuntimeContract>,
381 env2: &Environment,
382 ) -> Vec<RuntimeContract> {
383 let len1 = contracts1.len();
384 let mut result = contracts1;
385
386 for ctr2 in contracts2 {
387 let is_duplicate = result[..len1].iter().any(|ctr1| {
388 increment!("contracts:equality-checks");
389 contract_eq::contract_eq(&ctr1.contract, env1, &ctr2.contract, env2)
390 });
391
392 if !is_duplicate {
393 result.push(ctr2);
394 } else {
395 increment!("contracts:deduped");
396 }
397 }
398
399 result
400 }
401
402 /// Check if this contract might have polymorphic subcontracts. See
403 /// [crate::label::Label::can_have_poly_ctrs].
404 pub fn can_have_poly_ctrs(&self) -> bool {
405 self.label.can_have_poly_ctrs()
406 }
407}
408
409impl Traverse<NickelValue> for RuntimeContract {
410 fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<Self, E>
411 where
412 F: FnMut(NickelValue) -> Result<NickelValue, E>,
413 {
414 let contract = self.contract.traverse(f, order)?;
415 Ok(RuntimeContract { contract, ..self })
416 }
417
418 fn traverse_ref<S, U>(
419 &self,
420 f: &mut dyn FnMut(&NickelValue, &S) -> TraverseControl<S, U>,
421 state: &S,
422 ) -> Option<U> {
423 self.contract.traverse_ref(f, state)
424 }
425}
426
427/// The attributes of a let binding.
428#[derive(Debug, Default, Eq, PartialEq, Clone)]
429pub struct LetAttrs {
430 /// The type of a let binding. See the documentation of [`BindingType`].
431 pub binding_type: BindingType,
432
433 /// A recursive let binding adds its binding to the environment of the expression.
434 pub rec: bool,
435}
436
437/// The metadata that can be attached to a let.
438#[derive(Debug, Default, Clone, PartialEq)]
439pub struct LetMetadata {
440 pub doc: Option<String>,
441 pub annotation: TypeAnnotation,
442}
443
444impl From<LetMetadata> for record::FieldMetadata {
445 fn from(let_metadata: LetMetadata) -> Self {
446 record::FieldMetadata {
447 annotation: let_metadata.annotation,
448 doc: let_metadata.doc.map(Rc::from),
449 ..Default::default()
450 }
451 }
452}
453
454/// A type or a contract together with its corresponding label.
455#[derive(Debug, PartialEq, Clone)]
456pub struct LabeledType {
457 pub typ: Type,
458 pub label: Label,
459}
460
461impl LabeledType {
462 /// Create a labeled type from a type and a span, which are the minimal information required to
463 /// instantiate the type and the underlying label. All other values are set to the defaults.
464 pub fn new(typ: Type, pos_idx: PosIdx) -> Self {
465 Self {
466 typ: typ.clone(),
467 label: Label {
468 typ: Rc::new(typ),
469 span: pos_idx,
470 ..Default::default()
471 },
472 }
473 }
474
475 /// Modify the label's `field_name` field.
476 pub fn with_field_name(self, ident: Option<LocIdent>) -> Self {
477 LabeledType {
478 label: self.label.with_field_name(ident),
479 ..self
480 }
481 }
482}
483
484impl Serialize for LabeledType {
485 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
486 where
487 S: Serializer,
488 {
489 serializer.serialize_str(&self.label.typ.to_string())
490 }
491}
492
493impl Traverse<NickelValue> for LabeledType {
494 // Note that this function doesn't traverse the label, which is most often what you want. The
495 // terms that may hide in a label are mostly types used for error reporting, but are never
496 // evaluated.
497 fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<LabeledType, E>
498 where
499 F: FnMut(NickelValue) -> Result<NickelValue, E>,
500 {
501 let LabeledType { typ, label } = self;
502 typ.traverse(f, order).map(|typ| LabeledType { typ, label })
503 }
504
505 fn traverse_ref<S, U>(
506 &self,
507 f: &mut dyn FnMut(&NickelValue, &S) -> TraverseControl<S, U>,
508 state: &S,
509 ) -> Option<U> {
510 self.typ.traverse_ref(f, state)
511 }
512}
513
514/// A type and/or contract annotation.
515#[derive(Debug, PartialEq, Clone, Default)]
516pub struct TypeAnnotation {
517 /// The type annotation (using `:`).
518 pub typ: Option<LabeledType>,
519
520 /// The contracts annotation (using `|`).
521 pub contracts: Vec<LabeledType>,
522}
523
524impl TypeAnnotation {
525 /// Return the main annotation, which is either the type annotation if any, or the first
526 /// contract annotation.
527 pub fn first(&self) -> Option<&LabeledType> {
528 self.typ.iter().chain(self.contracts.iter()).next()
529 }
530
531 /// Iterate over the annotations, starting by the type and followed by the contracts.
532 pub fn iter(&self) -> impl Iterator<Item = &LabeledType> {
533 self.typ.iter().chain(self.contracts.iter())
534 }
535
536 /// Mutably iterate over the annotations, starting by the type and followed by the contracts.
537 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut LabeledType> {
538 self.typ.iter_mut().chain(self.contracts.iter_mut())
539 }
540
541 /// Return a string representation of the contracts (without the static type annotation) as a
542 /// comma-separated list.
543 pub fn contracts_to_string(&self) -> Option<String> {
544 (!self.contracts.is_empty()).then(|| {
545 self.contracts
546 .iter()
547 .map(|contract| format!("{}", contract.label.typ,))
548 .collect::<Vec<_>>()
549 .join(",")
550 })
551 }
552
553 /// Build a list of pending contracts from this annotation, to be stored alongside the metadata
554 /// of a field. Similar to [Self::all_contracts], but including the contracts from
555 /// `self.contracts` only, while `types` is excluded. Contracts derived from type annotations
556 /// aren't treated the same since they don't propagate through merging.
557 pub fn pending_contracts(
558 &self,
559 pos_table: &mut PosTable,
560 ) -> Result<Vec<RuntimeContract>, UnboundTypeVariableError> {
561 self.contracts
562 .iter()
563 .cloned()
564 .map(|labeled_ty| RuntimeContract::from_type(pos_table, labeled_ty))
565 .collect::<Result<Vec<_>, _>>()
566 }
567
568 /// Build the contract derived from the static type annotation, applying the specific
569 /// optimizations along the way.
570 pub fn static_contract(
571 &self,
572 pos_table: &mut PosTable,
573 ) -> Option<Result<RuntimeContract, UnboundTypeVariableError>> {
574 self.typ
575 .as_ref()
576 .cloned()
577 .map(|labeled_ty| RuntimeContract::from_static_type(pos_table, labeled_ty))
578 }
579
580 /// Convert all the contracts of this annotation, including the potential type annotation as
581 /// the first element, to a runtime representation. Apply contract optimizations to the static
582 /// type annotation.
583 pub fn all_contracts(
584 &self,
585 pos_table: &mut PosTable,
586 ) -> Result<Vec<RuntimeContract>, UnboundTypeVariableError> {
587 self.typ
588 .as_ref()
589 .cloned()
590 .map(|labeled_ty| RuntimeContract::from_static_type(pos_table, labeled_ty))
591 .into_iter()
592 .chain(
593 self.contracts
594 .iter()
595 .cloned()
596 .map(|labeled_ty| RuntimeContract::from_type(pos_table, labeled_ty)),
597 )
598 .collect::<Result<Vec<_>, _>>()
599 }
600
601 /// Set the `field_name` attribute of the labels of the type and contracts annotations.
602 pub fn with_field_name(self, field_name: Option<LocIdent>) -> Self {
603 TypeAnnotation {
604 typ: self.typ.map(|t| t.with_field_name(field_name)),
605 contracts: self
606 .contracts
607 .into_iter()
608 .map(|t| t.with_field_name(field_name))
609 .collect(),
610 }
611 }
612
613 /// Return `true` if this annotation is empty, i.e. hold neither a type annotation nor
614 /// contracts annotations.
615 pub fn is_empty(&self) -> bool {
616 self.typ.is_none() && self.contracts.is_empty()
617 }
618
619 /// **Warning**: the contract equality check used in this function behaves like syntactic
620 /// equality, and doesn't take the environment into account. It's unsound for execution (it
621 /// could equate contracts that are actually totally distinct), but we use it only to trim
622 /// accumulated contracts before pretty-printing. Do not use prior to any form of evaluation.
623 ///
624 /// Same as [`crate::combine::Combine`], but eliminate duplicate contracts. As there's no
625 /// notion of environment when considering mere annotations, we use an unsound contract
626 /// equality checking which correspond to comparing contracts syntactically.
627 pub fn combine_dedup(left: Self, right: Self) -> Self {
628 let len1 = left.contracts.len();
629 let mut contracts = left.contracts;
630
631 let typ = match (left.typ, right.typ) {
632 (left_ty @ Some(_), Some(right_ty)) => {
633 contracts.push(right_ty);
634 left_ty
635 }
636 (left_ty, right_ty) => left_ty.or(right_ty),
637 };
638
639 for ctr in right.contracts.into_iter() {
640 if !contracts[..len1]
641 .iter()
642 .any(|c| contract_eq::type_eq_noenv(&c.typ, &ctr.typ))
643 {
644 contracts.push(ctr);
645 }
646 }
647
648 TypeAnnotation { typ, contracts }
649 }
650}
651
652impl Combine for TypeAnnotation {
653 fn combine(left: Self, right: Self) -> Self {
654 let (typ, leftover) = match (left.typ, right.typ) {
655 (left_ty @ Some(_), right_ty @ Some(_)) => (left_ty, right_ty),
656 (left_ty, right_ty) => (left_ty.or(right_ty), None),
657 };
658
659 let contracts: Vec<_> = left
660 .contracts
661 .iter()
662 .cloned()
663 .chain(leftover)
664 .chain(right.contracts.iter().cloned())
665 .collect();
666
667 TypeAnnotation { typ, contracts }
668 }
669}
670
671impl From<TypeAnnotation> for LetMetadata {
672 fn from(annotation: TypeAnnotation) -> Self {
673 LetMetadata {
674 annotation,
675 ..Default::default()
676 }
677 }
678}
679
680impl Traverse<NickelValue> for TypeAnnotation {
681 fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<Self, E>
682 where
683 F: FnMut(NickelValue) -> Result<NickelValue, E>,
684 {
685 let TypeAnnotation { typ, contracts } = self;
686
687 let contracts = contracts
688 .into_iter()
689 .map(|labeled_ty| labeled_ty.traverse(f, order))
690 .collect::<Result<Vec<_>, _>>()?;
691
692 let typ = typ
693 .map(|labeled_ty| labeled_ty.traverse(f, order))
694 .transpose()?;
695
696 Ok(TypeAnnotation { typ, contracts })
697 }
698
699 fn traverse_ref<S, U>(
700 &self,
701 f: &mut dyn FnMut(&NickelValue, &S) -> TraverseControl<S, U>,
702 state: &S,
703 ) -> Option<U> {
704 self.contracts
705 .iter()
706 .find_map(|c| c.traverse_ref(f, state))
707 .or_else(|| self.typ.as_ref().and_then(|t| t.traverse_ref(f, state)))
708 }
709}
710
711impl Term {
712 /// Return the class of an expression in WHNF. See
713 /// [crate::eval::value::NickelValue::type_of].
714 pub fn type_of(&self) -> Option<&'static str> {
715 match self {
716 Term::Closurize(value) => value.type_of(),
717 Term::RecRecord(..) => Some("Record"),
718 Term::Fun(_) => Some("Function"),
719 Term::Sealed(..) => Some("Sealed"),
720 Term::Annotated(..) => Some("Annotated"),
721 Term::Let(..)
722 | Term::App(_)
723 | Term::Var(_)
724 | Term::Op1(_)
725 | Term::Op2(_)
726 | Term::OpN(_)
727 | Term::Import(_)
728 | Term::ResolvedImport(_)
729 | Term::StrChunks(_)
730 | Term::ParseError(_)
731 | Term::RuntimeError(_) => None,
732 }
733 }
734
735 /// Determine if a term is in evaluated form, called weak head normal form (WHNF). A weak head
736 /// normal form isn't evaluated further by the virtual machine.
737 pub fn is_whnf(&self) -> bool {
738 match self {
739 Term::Fun(..) => true,
740 Term::Closurize(_)
741 | Term::Let(..)
742 | Term::App(..)
743 | Term::Var(_)
744 | Term::Op1(..)
745 | Term::Op2(..)
746 | Term::OpN(..)
747 | Term::Sealed(..)
748 | Term::Annotated(..)
749 | Term::Import(_)
750 | Term::ResolvedImport(_)
751 | Term::StrChunks(_)
752 | Term::RecRecord(..)
753 | Term::ParseError(_)
754 | Term::RuntimeError(_) => false,
755 }
756 }
757
758 /// Determine if a term is annotated.
759 pub fn is_annotated(&self) -> bool {
760 matches!(self, Term::Annotated(..))
761 }
762
763 /// Determine if a term is an atom of the surface syntax. Atoms are basic elements of the
764 /// syntax that can freely substituted without being parenthesized.
765 pub fn fmt_is_atom(&self) -> bool {
766 match self {
767 Term::Closurize(value) => value.fmt_is_atom(),
768 Term::StrChunks(..) | Term::RecRecord(..) | Term::Var(..) => true,
769 // Those special cases aren't really atoms, but mustn't be parenthesized because they
770 // are really functions taking additional non-strict arguments and printed as "partial"
771 // infix operators.
772 //
773 // For example, `Op1(BoolOr, Var("x"))` is currently printed as `x ||`. Such operators
774 // must never be parenthesized, such as in `(x ||)`.
775 //
776 // We might want a more robust mechanism for pretty printing such operators.
777 Term::Op1(data) => matches!(
778 &data.op,
779 UnaryOp::RecordAccess(_) | UnaryOp::BoolAnd | UnaryOp::BoolOr
780 ),
781 Term::Op2(data) => matches!(&data.op, BinaryOp::RecordGet),
782 // A number with a minus sign as a prefix isn't a proper atom
783 Term::Let(..)
784 | Term::Fun(..)
785 | Term::App(..)
786 | Term::OpN(..)
787 | Term::Sealed(..)
788 | Term::Annotated(..)
789 | Term::Import(_)
790 | Term::ResolvedImport(..)
791 | Term::ParseError(_)
792 | Term::RuntimeError(_) => false,
793 }
794 }
795
796 /// Extract the static literal from string chunk. It only returns a `Some(..)`
797 /// when the term is a `Term::StrChunk` and all the chunks are `StrChunk::Literal(..)`
798 pub fn try_str_chunk_as_static_str(&self) -> Option<String> {
799 match self {
800 Term::StrChunks(chunks) => StrChunk::try_chunks_as_static_str(chunks),
801 _ => None,
802 }
803 }
804
805 /// Builds a term representing a function.
806 pub fn fun(arg: LocIdent, body: NickelValue) -> Self {
807 Term::Fun(FunData { arg, body })
808 }
809
810 pub fn let_in(
811 bindings: SmallVec<[(LocIdent, NickelValue); 4]>,
812 body: NickelValue,
813 attrs: LetAttrs,
814 ) -> Self {
815 Term::Let(Box::new(LetData {
816 bindings,
817 body,
818 attrs,
819 }))
820 }
821
822 pub fn rec_record(
823 record: RecordData,
824 includes: Vec<Include>,
825 dyn_fields: Vec<(NickelValue, Field)>,
826 deps: Option<RecordDeps>,
827 closurized: bool,
828 ) -> Self {
829 Term::RecRecord(Box::new(RecRecordData {
830 record,
831 includes,
832 dyn_fields,
833 deps,
834 closurized,
835 }))
836 }
837
838 pub fn op1(op: UnaryOp, arg: NickelValue) -> Self {
839 Term::Op1(Op1Data { op, arg })
840 }
841
842 pub fn op2(op: BinaryOp, arg1: NickelValue, arg2: NickelValue) -> Self {
843 Term::Op2(Op2Data { op, arg1, arg2 })
844 }
845
846 pub fn opn(op: NAryOp, args: Vec<NickelValue>) -> Self {
847 Term::OpN(OpNData { op, args })
848 }
849
850 pub fn sealed(key: SealingKey, inner: NickelValue, label: Label) -> Self {
851 Term::Sealed(SealedData {
852 key,
853 inner,
854 label: Rc::new(label),
855 })
856 }
857
858 pub fn annotated(annot: TypeAnnotation, inner: NickelValue) -> Self {
859 Term::Annotated(AnnotatedData {
860 annot: Rc::new(annot),
861 inner,
862 })
863 }
864
865 pub fn app(head: NickelValue, arg: NickelValue) -> Self {
866 Term::App(AppData { head, arg })
867 }
868
869 pub fn parse_error(error: ParseError) -> Self {
870 Term::ParseError(Box::new(error))
871 }
872}
873
874/// Primitive unary operators.
875///
876/// Some operators, such as if-then-else or `seq`, actually take several arguments but are only
877/// strict in one (the tested boolean for example, in the case of if-then-else). They are encoded
878/// as unary operators of this argument: indeed, in an expression `if-then-else boolean thenBlock
879/// elseBlock`, `if-then-else` can be seen as a unary operator taking a `Bool` argument and
880/// evaluating to either the first projection `fun x y => x` or the second projection `fun x y =>
881/// y`.
882#[derive(Clone, Debug, PartialEq)]
883pub enum UnaryOp {
884 /// If-then-else.
885 IfThenElse,
886
887 /// Return an enum tag representing the type of the term.
888 Typeof,
889
890 /// Return an enum whose tag represents the type of the term, carrying a payload with a statically
891 /// typed value.
892 Cast,
893
894 // Boolean AND and OR operator are encoded as unary operators so that they can be lazy in their
895 // second argument.
896 /// Boolean AND operator.
897 BoolAnd,
898
899 /// Boolean OR operator.
900 BoolOr,
901
902 /// Boolean NOT operator.
903 BoolNot,
904
905 /// Raise a blame, which stops the execution and prints an error according to the label
906 /// argument.
907 Blame,
908
909 /// Typecast an enum to a larger enum type.
910 ///
911 /// `EnumEmbed` is used to upcast enums. For example, if a value `x` has enum type `a | b`,
912 /// then `%enum/embed% c x` will have enum type `a | b | c`. It only affects typechecking as at
913 /// runtime `%enum/embed% someId` acts like the identity function.
914 EnumEmbed(LocIdent),
915
916 /// A specialized primop for match when all patterns are enum tags. In that case, instead of
917 /// compiling to a generic sequence of if-then-else, we can be much more efficient by indexing
918 /// into a hashmap. [Self::TagsOnlyMatch] takes additional lazy arguments: a record mapping
919 /// tags to the corresponding branches, and the default case when `has_default` is `true`.
920 TagsOnlyMatch {
921 has_default: bool,
922 },
923
924 /// Static record access.
925 ///
926 /// Static means that the field identifier is a statically known string inside the source.
927 RecordAccess(LocIdent),
928
929 /// Map a function on each element of an array.
930 ArrayMap,
931
932 /// Map a function on a record.
933 ///
934 /// The mapped function must take two arguments, the name of the field as a string, and the
935 /// content of the field. `RecordMap` then replaces the content of each field by the result of
936 /// the function: i.e., `%record/map% f {a=2;}` evaluates to `{a=(f "a" 2);}`.
937 RecordMap,
938
939 /// Inverse the polarity of a label.
940 LabelFlipPol,
941
942 /// Get the polarity of a label.
943 LabelPol,
944
945 /// Go to the domain in the type path of a label.
946 ///
947 /// If the argument is a label with a [type path][crate::label::TyPath) representing some
948 /// subtype of the type of the original contract, as in:
949 ///
950 /// ```text
951 /// (Num -> Num) -> Num
952 /// ^^^^^^^^^^ type path
953 /// ------------------- original type
954 /// ```
955 ///
956 /// Then `GoDom` evaluates to a copy of this label, where the path has gone forward into the
957 /// domain:
958 ///
959 /// ```text
960 /// (Num -> Num) -> Num
961 /// ^^^ new type path
962 /// ------------------- original type
963 /// ```
964 LabelGoDom,
965
966 /// Go to the codomain in the type path of a label.
967 ///
968 /// See `GoDom`.
969 LabelGoCodom,
970
971 /// Go to the array in the type path of a label.
972 ///
973 /// See `GoDom`.
974 LabelGoArray,
975
976 /// Go to the type ascribed to every field in a dictionary.
977 ///
978 /// See `GoDom`.
979 LabelGoDict,
980
981 /// Force the evaluation of its argument and proceed with the second.
982 Seq,
983
984 /// Recursively force the evaluation of its first argument then returns the second.
985 ///
986 /// Recursive here means that the evaluation does not stop at a WHNF, but the content of arrays
987 /// and records is also recursively forced.
988 DeepSeq,
989
990 /// Return the length of an array.
991 ArrayLength,
992
993 /// Generate an array of a given length by mapping a `Num -> Num` function onto `[1,..,n]`.
994 ArrayGen,
995
996 /// Generated by the evaluation of a string with interpolated expressions. `ChunksConcat`
997 /// applied to the current chunk to evaluate. As additional state, it uses a string
998 /// accumulator, the indentation of the chunk being evaluated, and the remaining chunks to be
999 /// evaluated, all stored on the stack.
1000 ChunksConcat,
1001
1002 /// Return the names of the fields of a record as a string array.
1003 RecordFields(RecordOpKind),
1004
1005 /// Return the values of the fields of a record as an array.
1006 RecordValues,
1007
1008 /// Remove heading and trailing spaces from a string.
1009 StringTrim,
1010
1011 /// Return the array of characters of a string.
1012 StringChars,
1013
1014 /// Transform a string to uppercase.
1015 StringUppercase,
1016
1017 /// Transform a string to lowercase.
1018 StringLowercase,
1019
1020 /// Return the length of a string.
1021 StringLength,
1022
1023 /// Transform a data to a string.
1024 ToString,
1025
1026 /// Transform a string to a number.
1027 NumberFromString,
1028
1029 /// Transform a string to an enum.
1030 EnumFromString,
1031
1032 /// Test if a regex matches a string.
1033 /// Like [`UnaryOp::StringFind`], this is a unary operator because we would like a way to share
1034 /// the same "compiled regex" for many matching calls. This is done by returning functions
1035 /// wrapping [`UnaryOp::StringIsMatchCompiled`] and [`UnaryOp::StringFindCompiled`]
1036 StringIsMatch,
1037
1038 /// Match a regex on a string, and returns the captured groups together, the index of the
1039 /// match, etc.
1040 StringFind,
1041
1042 /// Returns all matches of a regex on a string, as an array of matches. Each
1043 /// match contains the match groups, the starting index of the match and the
1044 /// matched string.
1045 StringFindAll,
1046
1047 /// Version of [`UnaryOp::StringIsMatch`] which remembers the compiled regex.
1048 StringIsMatchCompiled(CompiledRegex),
1049
1050 /// Version of [`UnaryOp::StringFind`] which remembers the compiled regex.
1051 StringFindCompiled(CompiledRegex),
1052
1053 /// Version of [`UnaryOp::StringFindAll`] which remembers the compiled regex.
1054 StringFindAllCompiled(CompiledRegex),
1055
1056 /// Force full evaluation of a term and return it.
1057 ///
1058 /// This was added in the context of [`BinaryOp::ContractArrayLazyApp`], in particular to make
1059 /// serialization work with lazy array contracts.
1060 ///
1061 /// # `Force` vs. `DeepSeq`
1062 ///
1063 /// [`UnaryOp::Force`] updates at the indices containing arrays with a new version where the
1064 /// lazy contracts have all been applied, whereas [`UnaryOp::DeepSeq`] evaluates the same
1065 /// expressions, but it never updates at the index of an array with lazy contracts with an array
1066 /// where those contracts have been applied. In a way, the result of lazy contract application
1067 /// in arrays is "lost" in [`UnaryOp::DeepSeq`], while it's returned in [`UnaryOp::Force`].
1068 ///
1069 /// This means we can observe different results between `deep_seq x x` and `force x`, in some
1070 /// cases.
1071 ///
1072 /// It's also worth noting that [`UnaryOp::DeepSeq`] should be, in principle, more efficient
1073 /// that [`UnaryOp::Force`] as it does less cloning.
1074 ///
1075 /// # About `for_export`
1076 ///
1077 /// When exporting a Nickel term, we first apply `Force` to the term to evaluate it. If there
1078 /// are record fields that have been marked `not_exported`, they would still be evaluated
1079 /// ordinarily, see [#1230](https://github.com/tweag/nickel/issues/1230). To stop this from
1080 /// happening, we introduce the `for_export` parameter here. When `for_export` is `true`, the
1081 /// evaluation of `Force` will skip fields that are marked as `not_exported`. When `for_export`
1082 /// is `false`, these fields are evaluated.
1083 Force {
1084 ignore_not_exported: bool,
1085 },
1086
1087 /// Recursive default priority operator. Recursively propagates a default priority through a
1088 /// record, stopping whenever a field isn't a record anymore to then turn into a simple
1089 /// `default`.
1090 ///
1091 /// For example:
1092 ///
1093 /// ```nickel
1094 /// {
1095 /// foo | rec default = {
1096 /// bar = 1,
1097 /// baz = "a",
1098 /// }
1099 /// }
1100 /// ```
1101 ///
1102 /// Is evaluated to:
1103 ///
1104 /// ```nickel
1105 /// {
1106 /// foo = {
1107 /// bar | default = 1,
1108 /// baz | default = "a",
1109 /// }
1110 /// }
1111 /// ```
1112 ///
1113 /// If a value has any explicit priority annotation, then the original annotation takes
1114 /// precedence and the default doesn't apply.
1115 RecDefault,
1116
1117 /// Recursive force priority operator. Similar to [UnaryOp::RecDefault], but propagate the
1118 /// `force` annotation.
1119 ///
1120 /// As opposed to `RecDefault`, the `force` takes precedence and erase any prior explicit
1121 /// priority annotation.
1122 RecForce,
1123
1124 /// Creates an "empty" record with the sealed tail of its record argument.
1125 ///
1126 /// Used in the `$record` contract implementation to ensure that we can define a `field_diff`
1127 /// function that preserves the sealed polymorphic tail of its argument.
1128 RecordEmptyWithTail,
1129
1130 /// Freezes a recursive record to make it a static dictionary. Apply all pending lazy contracts
1131 /// (and flush them), and remove all dependency information, so that the value of the fields is
1132 /// fixed in time and subsequent overrides will only impact the overriden field.
1133 RecordFreeze,
1134
1135 /// Print a message when encountered during evaluation and proceed with the evaluation of the
1136 /// argument on the top of the stack. Operationally the same as the identity function
1137 Trace,
1138
1139 /// Push a new, fresh diagnostic on the diagnostic stack of a contract label. This has the
1140 /// effect of saving the current diagnostic, as following calls to primop that modifies the
1141 /// label's current diagnostic will modify the fresh one, istead of the one being stacked.
1142 /// This primop shouldn't be used directly by user a priori, but is used internally during e.g.
1143 /// contract application.
1144 LabelPushDiag,
1145
1146 /// Evaluate a string of nix code into a resulting nickel value. Currently completely
1147 /// (strictly) evaluates the nix code, and must result in a value serializable into JSON.
1148 #[cfg(feature = "nix-experimental")]
1149 EvalNix,
1150
1151 /// Retrive the argument from an enum variant: `%enum/get_arg% ('Foo t) := t`
1152 EnumGetArg,
1153 /// Create an enum variant from a tag and an argument. This operator is strict in tag and
1154 /// return a function that can be further applied to an argument.
1155 EnumMakeVariant,
1156 /// Return true if the given parameter is an enum variant.
1157 EnumIsVariant,
1158 /// Extract the tag from an enum tag or an enum variant.
1159 EnumGetTag,
1160
1161 /// Wrap a contract implementation as a custom contract. You can think of this primop as a
1162 /// type constructor for custom contracts.
1163 ContractCustom,
1164
1165 /// After applying a custom contract (or a builtin contract), the result is either `'Ok value`
1166 /// or `'Error err_data`. This primop post-processes this return value (the first argument) to
1167 /// either produce `value` in the first case or to attach the error data to the label (the
1168 /// second argument, taken from the stack, as this op isn't strict in the label) and blame in
1169 /// the second.
1170 ContractPostprocessResult,
1171
1172 ContractAttachDefaultLabel,
1173
1174 /// The cosinus function.
1175 NumberArcCos,
1176
1177 /// The sinus function.
1178 NumberArcSin,
1179
1180 /// The tangent function.
1181 NumberArcTan,
1182
1183 /// The cosinus function.
1184 NumberCos,
1185
1186 /// The sinus function.
1187 NumberSin,
1188
1189 /// The tangent function.
1190 NumberTan,
1191}
1192
1193impl fmt::Display for UnaryOp {
1194 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1195 use UnaryOp::*;
1196 match self {
1197 IfThenElse => write!(f, "if_then_else"),
1198 Typeof => write!(f, "typeof"),
1199 Cast => write!(f, "cast"),
1200 BoolAnd => write!(f, "(&&)"),
1201 BoolOr => write!(f, "(||)"),
1202 BoolNot => write!(f, "bool/not"),
1203 Blame => write!(f, "blame"),
1204 EnumEmbed(_) => write!(f, "enum/embed"),
1205 TagsOnlyMatch { .. } => write!(f, "match"),
1206 RecordAccess(_) => write!(f, "record/access"),
1207 ArrayMap => write!(f, "array/map"),
1208 RecordMap => write!(f, "record/map"),
1209 LabelFlipPol => write!(f, "label/flip_polarity"),
1210 LabelPol => write!(f, "label/polarity"),
1211 LabelGoDom => write!(f, "label/go_dom"),
1212 LabelGoCodom => write!(f, "label/go_codom"),
1213 LabelGoArray => write!(f, "label/go_array"),
1214 LabelGoDict => write!(f, "label/go_dict"),
1215 Seq => write!(f, "seq"),
1216 DeepSeq => write!(f, "deep_seq"),
1217 ArrayLength => write!(f, "array/length"),
1218 ArrayGen => write!(f, "array/generate"),
1219 ChunksConcat => write!(f, "chunks_concat"),
1220 RecordFields(RecordOpKind::IgnoreEmptyOpt) => write!(f, "record/fields"),
1221 RecordFields(RecordOpKind::ConsiderAllFields) => write!(f, "record/fields_with_opts"),
1222 RecordValues => write!(f, "record/values"),
1223 StringTrim => write!(f, "string/trim"),
1224 StringChars => write!(f, "string/chars"),
1225 StringUppercase => write!(f, "string/uppercase"),
1226 StringLowercase => write!(f, "string/lowercase"),
1227 StringLength => write!(f, "string/length"),
1228 ToString => write!(f, "to_string"),
1229 NumberFromString => write!(f, "number/from_string"),
1230 EnumFromString => write!(f, "enum/from_string"),
1231 StringIsMatch => write!(f, "string/is_match"),
1232 StringFind => write!(f, "string/find"),
1233 StringFindAll => write!(f, "string/find_all"),
1234 StringIsMatchCompiled(_) => write!(f, "string/is_match_compiled"),
1235 StringFindCompiled(_) => write!(f, "string/find_compiled"),
1236 StringFindAllCompiled(_) => write!(f, "string/find_all_compiled"),
1237 Force { .. } => write!(f, "force"),
1238 RecDefault => write!(f, "rec_default"),
1239 RecForce => write!(f, "rec_force"),
1240 RecordEmptyWithTail => write!(f, "record/empty_with_tail"),
1241 RecordFreeze => write!(f, "record/freeze"),
1242 Trace => write!(f, "trace"),
1243 LabelPushDiag => write!(f, "label/push_diag"),
1244
1245 #[cfg(feature = "nix-experimental")]
1246 EvalNix => write!(f, "eval_nix"),
1247
1248 EnumGetArg => write!(f, "enum/get_arg"),
1249 EnumMakeVariant => write!(f, "enum/make_variant"),
1250 EnumIsVariant => write!(f, "enum/is_variant"),
1251 EnumGetTag => write!(f, "enum/get_tag"),
1252
1253 ContractCustom => write!(f, "contract/custom"),
1254 ContractPostprocessResult => write!(f, "contract/postprocess_result"),
1255 ContractAttachDefaultLabel => write!(f, "contract/attach_default_label"),
1256
1257 NumberArcCos => write!(f, "number/arccos"),
1258 NumberArcSin => write!(f, "number/arcsin"),
1259 NumberArcTan => write!(f, "number/arctan"),
1260 NumberCos => write!(f, "number/cos"),
1261 NumberSin => write!(f, "number/sin"),
1262 NumberTan => write!(f, "number/tan"),
1263 }
1264 }
1265}
1266
1267// See: https://github.com/rust-lang/regex/issues/178
1268/// [`regex::Regex`] which implements [`PartialEq`].
1269#[derive(Debug, Clone)]
1270pub struct CompiledRegex(pub regex::Regex);
1271
1272impl PartialEq for CompiledRegex {
1273 fn eq(&self, other: &Self) -> bool {
1274 self.0.as_str() == other.0.as_str()
1275 }
1276}
1277
1278impl Deref for CompiledRegex {
1279 type Target = regex::Regex;
1280
1281 fn deref(&self) -> &Self::Target {
1282 &self.0
1283 }
1284}
1285
1286impl From<regex::Regex> for CompiledRegex {
1287 fn from(item: regex::Regex) -> Self {
1288 CompiledRegex(item)
1289 }
1290}
1291
1292/// Position of a unary operator
1293#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1294pub enum OpPos {
1295 Infix,
1296 Postfix,
1297 Prefix,
1298
1299 /// A special operator like `if ... then ... else ...`
1300 Special,
1301}
1302
1303impl UnaryOp {
1304 pub fn pos(&self) -> OpPos {
1305 use UnaryOp::*;
1306 match self {
1307 BoolAnd | BoolOr | RecordAccess(_) => OpPos::Postfix,
1308 IfThenElse => OpPos::Special,
1309 _ => OpPos::Prefix,
1310 }
1311 }
1312}
1313
1314/// The kind of a dynamic record extension. Kind indicates if a definition is expected for the
1315/// field being inserted, or if the inserted field doesn't have a definition.
1316#[derive(Clone, Debug, PartialEq, Eq, Copy)]
1317pub enum RecordExtKind {
1318 WithValue,
1319 WithoutValue,
1320}
1321
1322/// Primitive binary operators
1323#[derive(Clone, Debug, PartialEq)]
1324pub enum BinaryOp {
1325 /// Addition of numerals.
1326 Plus,
1327
1328 /// Subtraction of numerals.
1329 Sub,
1330
1331 /// Multiplication of numerals.
1332 Mult,
1333
1334 /// Floating-point division of numerals.
1335 Div,
1336
1337 /// Modulo of numerals.
1338 Modulo,
1339
1340 /// Give the four quadrant arctangent of y and x.
1341 NumberArcTan2,
1342
1343 /// Give the logarithm of a number.
1344 NumberLog,
1345
1346 /// Raise a number to a power.
1347 Pow,
1348
1349 /// Concatenation of strings.
1350 StringConcat,
1351
1352 /// Polymorphic equality.
1353 Eq,
1354
1355 /// Strictly less than comparison operator.
1356 LessThan,
1357
1358 /// Less than or equal comparison operator.
1359 LessOrEq,
1360
1361 /// Strictly greater than comparison operator.
1362 GreaterThan,
1363
1364 /// Greater than or equal comparison operator.
1365 GreaterOrEq,
1366
1367 /// Apply a contract to a label and a value. The value is is stored on the stack unevaluated,
1368 /// while the contract and the label are the strict arguments to this operator. `ApplyContract`
1369 /// also accepts contracts as records, which are translated to a function that merge said
1370 /// contract with its argument. Finally, this operator marks the location of the contract
1371 /// argument on the stack for better error reporting.
1372 ///
1373 /// Either the contract raises a blame error, or the primop evaluates to the value returned by
1374 /// the contract that can be used in place of the original value.
1375 ContractApply,
1376
1377 /// Variant of [Self::ContractApply] which also applies an arbitrary contract to a label and a
1378 /// value, but instead of either blaming or returning the value, it has the same return value
1379 /// as contract built via [UnaryOp::ContractCustom], that is `[| 'Ok Dyn, 'Error {..}|]`. The
1380 /// value returned through `'Ok` can still have lazy blame expressions inside, of course.
1381 ///
1382 /// Put differently, `%contract/check% (%contract/custom% custom) label value` is equivalent to
1383 /// `custom label value`, modulo argument tracking. [Self::ContractCheck] doesn't only work on
1384 /// custom contracts, but on builtin contracts as well.
1385 ///
1386 /// This operation is useful for contract composition, that is when calling a contract from
1387 /// another contract. In theory, one could use [Self::ContractApply], but the caller then needs
1388 /// to wrap the result in `'Ok`, and much more importantly, `%contract/apply%` converts all
1389 /// immediate errors returned as `'Error` into blame errors. This is not desirable, as blame
1390 /// errors can't be caught, which artificially makes the called contract entirely delayed. This
1391 /// typically wouldn't play very well with boolean combinators. On the other hand,
1392 /// [Self::ContractCheck] preserves the immediate/delayed part of the called contract.
1393 ContractCheck,
1394
1395 /// Take a record of type `{message | String | optional, notes | String | optional}`.
1396 LabelWithErrorData,
1397
1398 /// Unseal a sealed term.
1399 ///
1400 /// See [`BinaryOp::Seal`].
1401 Unseal,
1402
1403 /// Go to a specific field in the type path of a label.
1404 ///
1405 /// See `LabelGoDom`.
1406 LabelGoField,
1407
1408 /// Extend a record with a dynamic field.
1409 ///
1410 /// Dynamic means that the field name may be an expression instead of a statically known
1411 /// string. `RecordExtend` tries to evaluate this name to a string, and in case of success, add
1412 /// a field with this name to the given record with the expression on top of the stack as
1413 /// content.
1414 ///
1415 /// The field may have been defined with attached metadata, pending contracts and may or may
1416 /// not have a defined value. We can't store those information as a term argument (metadata
1417 /// aren't first class values, at least at the time of writing), so for now we attach it
1418 /// directly to the extend primop. This isn't ideal, and in the future we may want to have a
1419 /// more principled primop.
1420 RecordInsert {
1421 metadata: SharedMetadata,
1422 pending_contracts: Vec<RuntimeContract>,
1423 ext_kind: RecordExtKind,
1424 op_kind: RecordOpKind,
1425 },
1426
1427 /// Remove a field from a record. The field name is given as an argument.
1428 RecordRemove(RecordOpKind),
1429
1430 /// Dynamically access a field of record. The field name is given as an argument which should
1431 /// evaluate to a string.
1432 RecordGet,
1433
1434 /// Test if a record has a specific field.
1435 RecordHasField(RecordOpKind),
1436
1437 /// Test if the field of a record exists and has a definition.
1438 RecordFieldIsDefined(RecordOpKind),
1439
1440 /// Take a pair of records and split them into four separate records:
1441 ///
1442 /// - `left_only`: fields of the left argument but not in the right
1443 /// - `left_center`: fields of the left argument that happens to also be in the right (but the
1444 /// value and the metadata are taken from the left)
1445 /// - `right_center`: fields of the right argument that happens to also be in the left (but the
1446 /// value and the metadata are taken from the right)
1447 /// - `right_only`: fields of the right argument but not in the left
1448 ///
1449 /// As opposed to an equivalent user-defined implementation, this primop has better performance
1450 /// and is able to preserve field metadata.
1451 ///
1452 /// If `left` (resp. `right`) is open or has a sealed tail, then `left_only` (resp.
1453 /// `right_only`) will inherit the same properties. `left_center` (resp. `right_center`) are
1454 /// always closed and without a sealed tail.
1455 RecordSplitPair,
1456
1457 /// Take a pair of disjoint records (i.e. records with no common field) and combine them into
1458 /// one. It's a form of merging, but based on the assumption that the records are disjoint and
1459 /// thus non-conflicting, it's simpler and more efficient than a general merge.
1460 ///
1461 /// As for merge, this raises a blame error if one of the arguments has a sealed tail.
1462 RecordDisjointMerge,
1463
1464 /// Concatenate two arrays.
1465 ArrayConcat,
1466
1467 /// Access the n-th element of an array.
1468 ArrayAt,
1469
1470 /// The merge operator (see [crate::eval::merge]). `Merge` is parametrized by a
1471 /// [crate::label::MergeLabel], which carries additional information for error-reporting
1472 /// purpose.
1473 Merge(MergeLabel),
1474
1475 /// Hash a string.
1476 Hash,
1477
1478 /// Serialize a value to a string.
1479 Serialize,
1480
1481 /// Deserialize a string to a value.
1482 Deserialize,
1483
1484 /// Split a string into an array.
1485 StringSplit,
1486
1487 /// Determine if a string is a substring of another one.
1488 StringContains,
1489
1490 /// Compare two strings lexicographically.
1491 StringCompare,
1492
1493 /// Base64 encode a string.
1494 StringBase64Encode,
1495
1496 /// Decode a base64 encoded string.
1497 StringBase64Decode,
1498
1499 /// Seal a term with a sealing key (see [`Term::Sealed`]).
1500 Seal,
1501
1502 /// Lazily apply a contract to an Array.
1503 /// This simply inserts a contract into the array attributes.
1504 ContractArrayLazyApp,
1505
1506 /// Lazily map contracts over a record. The arguments are a label and a function which takes
1507 /// the name of the field as a parameter and returns the corresponding contract.
1508 ContractRecordLazyApp,
1509
1510 /// Set the message of the current diagnostic of a label.
1511 LabelWithMessage,
1512
1513 /// Set the notes of the current diagnostic of a label.
1514 LabelWithNotes,
1515
1516 /// Append a note to the current diagnostic of a label.
1517 LabelAppendNote,
1518
1519 /// Look up the [`crate::label::TypeVarData`] associated with a [`SealingKey`] in the type
1520 /// environment of a label.
1521 LabelLookupTypeVar,
1522}
1523
1524impl BinaryOp {
1525 pub fn pos(&self) -> OpPos {
1526 use BinaryOp::*;
1527 match self {
1528 Plus | Sub | Mult | Div | Modulo | StringConcat | Eq | LessThan | LessOrEq
1529 | GreaterThan | GreaterOrEq | ArrayConcat | Merge(_) => OpPos::Infix,
1530 _ => OpPos::Prefix,
1531 }
1532 }
1533}
1534
1535impl fmt::Display for BinaryOp {
1536 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1537 use BinaryOp::*;
1538 match self {
1539 Plus => write!(f, "(+)"),
1540 Sub => write!(f, "(-)"),
1541 Mult => write!(f, "(*)"),
1542 Div => write!(f, "(/)"),
1543 Modulo => write!(f, "(%)"),
1544 NumberArcTan2 => write!(f, "number/arctan2"),
1545 NumberLog => write!(f, "number/log"),
1546 Pow => write!(f, "pow"),
1547 StringConcat => write!(f, "string/concat"),
1548 Eq => write!(f, "(==)"),
1549 LessThan => write!(f, "(<)"),
1550 LessOrEq => write!(f, "(<=)"),
1551 GreaterThan => write!(f, "(>)"),
1552 GreaterOrEq => write!(f, "(>=)"),
1553 ContractApply => write!(f, "contract/apply"),
1554 ContractCheck => write!(f, "contract/check"),
1555 LabelWithErrorData => write!(f, "label/with_error_data"),
1556 Unseal => write!(f, "unseal"),
1557 LabelGoField => write!(f, "label/go_field"),
1558 RecordInsert {
1559 op_kind: RecordOpKind::IgnoreEmptyOpt,
1560 ..
1561 } => write!(f, "record/insert"),
1562 RecordInsert {
1563 op_kind: RecordOpKind::ConsiderAllFields,
1564 ..
1565 } => write!(f, "record/insert_with_opts"),
1566 RecordRemove(RecordOpKind::IgnoreEmptyOpt) => write!(f, "record/remove"),
1567 RecordRemove(RecordOpKind::ConsiderAllFields) => write!(f, "record/remove_with_opts"),
1568 RecordGet => write!(f, "record/get"),
1569 RecordHasField(RecordOpKind::IgnoreEmptyOpt) => write!(f, "record/has_field"),
1570 RecordHasField(RecordOpKind::ConsiderAllFields) => {
1571 write!(f, "record/has_field_with_opts")
1572 }
1573 RecordFieldIsDefined(RecordOpKind::IgnoreEmptyOpt) => {
1574 write!(f, "record/field_is_defined")
1575 }
1576 RecordFieldIsDefined(RecordOpKind::ConsiderAllFields) => {
1577 write!(f, "record/field_is_defined_with_opts")
1578 }
1579 Self::RecordSplitPair => write!(f, "record/split_pair"),
1580 Self::RecordDisjointMerge => write!(f, "record/disjoint_merge"),
1581 ArrayConcat => write!(f, "(@)"),
1582 ArrayAt => write!(f, "array/at"),
1583 Merge(_) => write!(f, "(&)"),
1584 Hash => write!(f, "hash"),
1585 Serialize => write!(f, "serialize"),
1586 Deserialize => write!(f, "deserialize"),
1587 StringSplit => write!(f, "string/split"),
1588 StringContains => write!(f, "string/contains"),
1589 StringCompare => write!(f, "string/compare"),
1590 StringBase64Encode => write!(f, "string/base64_encode"),
1591 StringBase64Decode => write!(f, "string/base64_decode"),
1592 Seal => write!(f, "seal"),
1593 ContractArrayLazyApp => write!(f, "contract/array_lazy_apply"),
1594 ContractRecordLazyApp => write!(f, "contract/record_lazy_apply"),
1595 LabelWithMessage => write!(f, "label/with_message"),
1596 LabelWithNotes => write!(f, "label/with_notes"),
1597 LabelAppendNote => write!(f, "label/append_note"),
1598 LabelLookupTypeVar => write!(f, "label/lookup_type_variable"),
1599 }
1600 }
1601}
1602
1603/// Primitive n-ary operators. Unary and binary operator make up for most of operators and are
1604/// hence special cased. `NAryOp` handles strict operations of arity greater than 2.
1605#[derive(Clone, Debug, PartialEq, Eq)]
1606pub enum NAryOp {
1607 /// Replace a substring by another one in a string.
1608 StringReplace,
1609
1610 /// Same as [`NAryOp::StringReplace`], but the pattern is interpreted as a regular expression.
1611 StringReplaceRegex,
1612
1613 /// Return a substring of an original string.
1614 StringSubstr,
1615
1616 /// The merge operator in contract mode (see [crate::eval::merge]). The arguments are in order
1617 /// the contract's label, the value to check, and the contract as a record.
1618 MergeContract,
1619
1620 /// Seals one record into the tail of another. Used to ensure that functions using polymorphic
1621 /// record contracts do not violate parametricity.
1622 ///
1623 /// Takes four arguments:
1624 /// - a sealing key, which must be provided later to unseal the tail,
1625 /// - a label, which will be used to assign blame correctly tail access is
1626 /// attempted,
1627 /// - a record, which is the record we wish to seal the tail into,
1628 /// - the record that we wish to seal.
1629 RecordSealTail,
1630
1631 /// Unseals a term from the tail of a record and returns it.
1632 ///
1633 /// Takes three arguments:
1634 /// - the sealing key, which was used to seal the tail,
1635 /// - a label which will be used to assign blame correctly if
1636 /// something goes wrong while unsealing,
1637 /// - the record whose tail we wish to unseal.
1638 RecordUnsealTail,
1639
1640 /// Insert type variable data into the `type_environment` of a [`crate::label::Label`]
1641 ///
1642 /// Takes four arguments:
1643 /// - the sealing key assigned to the type variable
1644 /// - the [introduction polarity](crate::label::Polarity) of the type variable
1645 /// - the [kind](crate::typ::VarKind) of the type variable
1646 /// - a label on which to operate
1647 LabelInsertTypeVar,
1648
1649 /// Return a sub-array corresponding to a range. Given that Nickel uses array slices under the
1650 /// hood, as long as the array isn't modified later, this operation is constant in time and
1651 /// memory.
1652 ArraySlice,
1653}
1654
1655impl NAryOp {
1656 pub fn arity(&self) -> usize {
1657 match self {
1658 NAryOp::StringReplace
1659 | NAryOp::StringReplaceRegex
1660 | NAryOp::StringSubstr
1661 | NAryOp::MergeContract
1662 | NAryOp::RecordUnsealTail
1663 | NAryOp::LabelInsertTypeVar
1664 | NAryOp::ArraySlice => 3,
1665 NAryOp::RecordSealTail => 4,
1666 }
1667 }
1668}
1669
1670impl fmt::Display for NAryOp {
1671 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1672 use NAryOp::*;
1673 match self {
1674 StringReplace => write!(f, "string/replace"),
1675 StringReplaceRegex => write!(f, "string/replace_regex"),
1676 StringSubstr => write!(f, "string/substr"),
1677 MergeContract => write!(f, "record/merge_contract"),
1678 RecordSealTail => write!(f, "record/seal_tail"),
1679 RecordUnsealTail => write!(f, "record/unseal_tail"),
1680 LabelInsertTypeVar => write!(f, "label/insert_type_variable"),
1681 ArraySlice => write!(f, "array/slice"),
1682 }
1683 }
1684}
1685
1686impl PrettyPrintCap for NickelValue {}
1687
1688impl Traverse<NickelValue> for Term {
1689 /// Traverse through all expressions in the tree.
1690 ///
1691 /// This also recurses into the terms that are contained in `Type` subtrees.
1692 fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<Term, E>
1693 where
1694 F: FnMut(NickelValue) -> Result<NickelValue, E>,
1695 {
1696 Ok(match self {
1697 Term::Fun(data) => Term::Fun(FunData {
1698 arg: data.arg,
1699 body: data.body.traverse(f, order)?,
1700 }),
1701 Term::Let(mut data) => {
1702 data.bindings = data
1703 .bindings
1704 .into_iter()
1705 .map(|(key, val)| Ok((key, val.traverse(f, order)?)))
1706 .collect::<Result<_, E>>()?;
1707 data.body = data.body.traverse(f, order)?;
1708
1709 Term::Let(data)
1710 }
1711 Term::App(mut data) => {
1712 data.head = data.head.traverse(f, order)?;
1713 data.arg = data.arg.traverse(f, order)?;
1714 Term::App(data)
1715 }
1716 Term::Op1(mut data) => {
1717 data.arg = data.arg.traverse(f, order)?;
1718 Term::Op1(data)
1719 }
1720 Term::Op2(mut data) => {
1721 data.arg1 = data.arg1.traverse(f, order)?;
1722 data.arg2 = data.arg2.traverse(f, order)?;
1723 Term::Op2(data)
1724 }
1725 Term::OpN(mut data) => {
1726 data.args = data
1727 .args
1728 .into_iter()
1729 .map(|t| t.traverse(f, order))
1730 .collect::<Result<Vec<NickelValue>, E>>()?;
1731
1732 Term::OpN(data)
1733 }
1734 Term::Sealed(mut data) => {
1735 data.inner = data.inner.traverse(f, order)?;
1736 Term::Sealed(data)
1737 }
1738 Term::RecRecord(mut data) => {
1739 // The annotation on `map_res` uses Result's corresponding trait to convert from
1740 // Iterator<Result> to a Result<Iterator>
1741 let static_fields_res: Result<IndexMap<LocIdent, Field>, E> = data
1742 .record
1743 .fields
1744 .into_iter()
1745 // For the conversion to work, note that we need a Result<(Ident,Field), E>
1746 .map(|(id, field)| Ok((id, field.traverse(f, order)?)))
1747 .collect();
1748
1749 data.dyn_fields = data
1750 .dyn_fields
1751 .into_iter()
1752 .map(|(id_t, field)| {
1753 let id_t = id_t.traverse(f, order)?;
1754 let field = field.traverse(f, order)?;
1755
1756 Ok((id_t, field))
1757 })
1758 .collect::<Result<Vec<_>, _>>()?;
1759
1760 data.record = RecordData::new_shared_tail(
1761 static_fields_res?,
1762 data.record.attrs,
1763 data.record.sealed_tail,
1764 );
1765
1766 Term::RecRecord(data)
1767 }
1768 Term::StrChunks(chunks) => {
1769 let chunks_res: Result<Vec<StrChunk<NickelValue>>, E> = chunks
1770 .into_iter()
1771 .map(|chunk| match chunk {
1772 chunk @ StrChunk::Literal(_) => Ok(chunk),
1773 StrChunk::Expr(t, indent) => {
1774 Ok(StrChunk::Expr(t.traverse(f, order)?, indent))
1775 }
1776 })
1777 .collect();
1778
1779 Term::StrChunks(chunks_res?)
1780 }
1781 Term::Annotated(mut data) => {
1782 data.annot = Rc::new(Rc::unwrap_or_clone(data.annot).traverse(f, order)?);
1783 data.inner = data.inner.traverse(f, order)?;
1784 Term::Annotated(data)
1785 }
1786 Term::Closurize(value) => {
1787 let value = value.traverse(f, order)?;
1788 Term::Closurize(value)
1789 }
1790 Term::Var(_)
1791 | Term::Import(_)
1792 | Term::ResolvedImport(_)
1793 | Term::ParseError(_)
1794 | Term::RuntimeError(_) => self,
1795 })
1796 }
1797
1798 fn traverse_ref<S, U>(
1799 &self,
1800 f: &mut dyn FnMut(&NickelValue, &S) -> TraverseControl<S, U>,
1801 state: &S,
1802 ) -> Option<U> {
1803 match self {
1804 Term::Var(_)
1805 | Term::Import(_)
1806 | Term::ResolvedImport(_)
1807 | Term::ParseError(_)
1808 | Term::RuntimeError(_) => None,
1809 Term::StrChunks(chunks) => chunks.iter().find_map(|ch| {
1810 if let StrChunk::Expr(term, _) = ch {
1811 term.traverse_ref(f, state)
1812 } else {
1813 None
1814 }
1815 }),
1816 Term::Op1(data) => data.arg.traverse_ref(f, state),
1817 Term::Sealed(data) => data.inner.traverse_ref(f, state),
1818 Term::Fun(FunData { arg: _, body: t }) | Term::Closurize(t) => t.traverse_ref(f, state),
1819 Term::Let(data) => data
1820 .bindings
1821 .iter()
1822 .find_map(|(_id, t)| t.traverse_ref(f, state))
1823 .or_else(|| data.body.traverse_ref(f, state)),
1824 Term::Op2(data) => data
1825 .arg1
1826 .traverse_ref(f, state)
1827 .or_else(|| data.arg2.traverse_ref(f, state)),
1828 Term::App(data) => data
1829 .head
1830 .traverse_ref(f, state)
1831 .or_else(|| data.arg.traverse_ref(f, state)),
1832 Term::RecRecord(data) => data
1833 .record
1834 .fields
1835 .values()
1836 .find_map(|field| field.traverse_ref(f, state))
1837 .or_else(|| {
1838 data.dyn_fields.iter().find_map(|(id, field)| {
1839 id.traverse_ref(f, state)
1840 .or_else(|| field.traverse_ref(f, state))
1841 })
1842 }),
1843 Term::OpN(data) => data.args.iter().find_map(|t| t.traverse_ref(f, state)),
1844 Term::Annotated(data) => data
1845 .inner
1846 .traverse_ref(f, state)
1847 .or_else(|| data.annot.traverse_ref(f, state)),
1848 }
1849 }
1850}
1851
1852/// A helper for some reference counted term data that we expect to be unique (1-referenced
1853/// counted) and we need to map in place. This function make it possible to do it without
1854/// discarding the `Rc` allocation.
1855///
1856/// If the `Rc` isn't unique, this method still works but will allocate (in that case, it's better
1857/// to avoid using this function and rather clone the content and allocate a new `Rc` manually).
1858pub fn unique_map_in_place<T: Default + Clone>(rc: &mut Rc<T>, f: impl FnOnce(T) -> T) {
1859 let data_slot = Rc::make_mut(rc);
1860 let mapped = f(std::mem::take(data_slot));
1861 let _ = std::mem::replace(data_slot, mapped);
1862}
1863
1864/// A fallible version of [unique_map_in_place].
1865pub fn fallible_unique_map_in_place<T: Default + Clone, E>(
1866 rc: &mut Rc<T>,
1867 f: impl FnOnce(T) -> Result<T, E>,
1868) -> Result<(), E> {
1869 let data_slot = Rc::make_mut(rc);
1870 let mapped = f(std::mem::take(data_slot))?;
1871 let _ = std::mem::replace(data_slot, mapped);
1872 Ok(())
1873}
1874
1875impl_display_from_pretty!(Term);
1876
1877#[macro_use]
1878/// Helpers to build [Term] objects as [values][crate::eval::value::NickelValue] from other
1879/// values.
1880pub mod make {
1881 use super::*;
1882
1883 pub mod builder;
1884
1885 /// Multi-ary application for types implementing `Into<NickelValue>`.
1886 #[macro_export]
1887 macro_rules! mk_app {
1888 ( $f:expr, $arg:expr) => {
1889 $crate::eval::value::NickelValue::from(
1890 $crate::term::Term::app(
1891 $crate::eval::value::NickelValue::from($f),
1892 $crate::eval::value::NickelValue::from($arg)
1893 )
1894 )
1895 };
1896 ( $f:expr, $fst:expr , $( $args:expr ),+ ) => {
1897 mk_app!(mk_app!($f, $fst), $( $args ),+)
1898 };
1899 }
1900
1901 /// Multi-ary application for types implementing `Into<NickelValue>`.
1902 #[macro_export]
1903 macro_rules! mk_opn {
1904 ( $op:expr, $( $args:expr ),+) => {
1905 {
1906 let args = vec![$( $crate::eval::value::NickelValue::from($args) ),+];
1907 $crate::eval::value::NickelValue::from($crate::term::Term::OpN($op, args))
1908 }
1909 };
1910 }
1911
1912 /// Multi argument function for types implementing `Into<Ident>` (for the identifiers), and
1913 /// `Into<NickelValue>` for the body.
1914 #[macro_export]
1915 macro_rules! mk_fun {
1916 ( $id:expr, $body:expr ) => {
1917 //MARKER
1918 $crate::eval::value::NickelValue::from(
1919 $crate::term::Term::fun(
1920 $crate::identifier::LocIdent::from($id),
1921 $crate::eval::value::NickelValue::from($body)
1922 )
1923 )
1924 };
1925 ( $id1:expr, $id2:expr , $( $rest:expr ),+ ) => {
1926 mk_fun!($crate::identifier::LocIdent::from($id1), mk_fun!($id2, $( $rest ),+))
1927 };
1928 }
1929
1930 /// Multi field record for types implementing `Into<Ident>` (for the identifiers), and
1931 /// `Into<NickelValue>` for the fields. Identifiers and corresponding content are specified as a
1932 /// tuple: `mk_record!(("field1", t1), ("field2", t2))` corresponds to the record `{ field1 =
1933 /// t1; field2 = t2 }`.
1934 #[macro_export]
1935 macro_rules! mk_record {
1936 ( $( ($id:expr, $body:expr) ),* ) => {
1937 {
1938 let mut fields = indexmap::IndexMap::<$crate::identifier::LocIdent, $crate::eval::value::NickelValue>::new();
1939 $(
1940 fields.insert($id.into(), $body.into());
1941 )*
1942 $crate::eval::value::NickelValue::record_posless(
1943 $crate::term::record::RecordData::with_field_values(fields)
1944 )
1945 }
1946 };
1947 }
1948
1949 /// Array for types implementing `Into<NickelValue>` (for elements). The array's attributes are a
1950 /// trailing (optional) `ArrayAttrs`, separated by a `;`. `mk_array!(Term::Num(42))` corresponds
1951 /// to `\[42\]`. Here the attributes are `ArrayAttrs::default()`, though the evaluated array may
1952 /// have different attributes.
1953 #[macro_export]
1954 macro_rules! mk_array {
1955 ( $( $terms:expr ),* ) => {
1956 {
1957 let ts : $crate::eval::value::Array =
1958 [$( $crate::eval::value::NickelValue::from($terms) ),*]
1959 .into_iter()
1960 .collect();
1961 $crate::eval::value::NickelValue::array_posless(ts, Vec::new())
1962 }
1963 };
1964 }
1965
1966 pub fn var<I>(v: I) -> NickelValue
1967 where
1968 I: Into<LocIdent>,
1969 {
1970 Term::Var(v.into()).into()
1971 }
1972
1973 pub fn let_in<I, T1, T2, Iter>(rec: bool, bindings: Iter, t2: T2) -> NickelValue
1974 where
1975 T1: Into<NickelValue>,
1976 T2: Into<NickelValue>,
1977 I: Into<LocIdent>,
1978 Iter: IntoIterator<Item = (I, T1)>,
1979 {
1980 Term::let_in(
1981 bindings
1982 .into_iter()
1983 .map(|(id, t)| (id.into(), t.into()))
1984 .collect(),
1985 t2.into(),
1986 LetAttrs {
1987 binding_type: BindingType::Normal,
1988 rec,
1989 },
1990 )
1991 .into()
1992 }
1993
1994 pub fn let_one_in<I, T1, T2>(id: I, t1: T1, t2: T2) -> NickelValue
1995 where
1996 T1: Into<NickelValue>,
1997 T2: Into<NickelValue>,
1998 I: Into<LocIdent>,
1999 {
2000 let_in(false, std::iter::once((id, t1)), t2)
2001 }
2002
2003 pub fn let_one_rec_in<I, T1, T2>(id: I, t1: T1, t2: T2) -> NickelValue
2004 where
2005 T1: Into<NickelValue>,
2006 T2: Into<NickelValue>,
2007 I: Into<LocIdent>,
2008 {
2009 let_in(true, std::iter::once((id, t1)), t2)
2010 }
2011
2012 pub fn if_then_else<T1, T2, T3>(cond: T1, t1: T2, t2: T3) -> NickelValue
2013 where
2014 T1: Into<NickelValue>,
2015 T2: Into<NickelValue>,
2016 T3: Into<NickelValue>,
2017 {
2018 mk_app!(
2019 Term::op1(UnaryOp::IfThenElse, cond.into()),
2020 t1.into(),
2021 t2.into()
2022 )
2023 }
2024
2025 pub fn op1<T>(op: UnaryOp, t: T) -> NickelValue
2026 where
2027 T: Into<NickelValue>,
2028 {
2029 Term::op1(op, t.into()).into()
2030 }
2031
2032 pub fn op2<T1, T2>(op: BinaryOp, t1: T1, t2: T2) -> NickelValue
2033 where
2034 T1: Into<NickelValue>,
2035 T2: Into<NickelValue>,
2036 {
2037 Term::op2(op, t1.into(), t2.into()).into()
2038 }
2039
2040 pub fn opn<T>(op: NAryOp, args: Vec<T>) -> NickelValue
2041 where
2042 T: Into<NickelValue>,
2043 {
2044 Term::opn(op, args.into_iter().map(T::into).collect()).into()
2045 }
2046
2047 pub fn apply_contract<T>(
2048 pos_table: &mut PosTable,
2049 typ: Type,
2050 l: Label,
2051 t: T,
2052 ) -> Result<NickelValue, UnboundTypeVariableError>
2053 where
2054 T: Into<NickelValue>,
2055 {
2056 Ok(mk_app!(
2057 op2(
2058 BinaryOp::ContractApply,
2059 typ.contract(pos_table)?,
2060 NickelValue::label_posless(l)
2061 ),
2062 t.into()
2063 ))
2064 }
2065
2066 pub fn id() -> NickelValue {
2067 mk_fun!("x", var("x"))
2068 }
2069
2070 pub fn import<S>(path: S, format: InputFormat) -> NickelValue
2071 where
2072 S: Into<OsString>,
2073 {
2074 Term::Import(Import::Path {
2075 path: path.into(),
2076 format,
2077 })
2078 .into()
2079 }
2080
2081 pub fn integer(n: impl Into<i64>) -> NickelValue {
2082 NickelValue::number_posless(n.into())
2083 }
2084
2085 pub fn static_access<I, S, T>(record: T, fields: I) -> NickelValue
2086 where
2087 I: IntoIterator<Item = S>,
2088 I::IntoIter: DoubleEndedIterator,
2089 S: Into<LocIdent>,
2090 T: Into<NickelValue>,
2091 {
2092 fields.into_iter().fold(record.into(), |value, field| {
2093 make::op1(UnaryOp::RecordAccess(field.into()), value)
2094 })
2095 }
2096
2097 pub fn enum_variant<S, T>(tag: S, arg: T) -> NickelValue
2098 where
2099 S: Into<LocIdent>,
2100 T: Into<NickelValue>,
2101 {
2102 NickelValue::enum_variant_posless(tag.into(), Some(arg.into()))
2103 }
2104
2105 pub fn custom_contract<T>(contract: T) -> NickelValue
2106 where
2107 T: Into<NickelValue>,
2108 {
2109 NickelValue::custom_contract_posless(contract.into())
2110 }
2111}
2112
2113#[cfg(test)]
2114mod tests {
2115 use super::*;
2116
2117 use crate::{
2118 combine::Combine,
2119 label::Label,
2120 typ::{Type, TypeF},
2121 };
2122
2123 #[test]
2124 fn make_static_access() {
2125 let t = make::op1(
2126 UnaryOp::RecordAccess("record".into()),
2127 make::op1(
2128 UnaryOp::RecordAccess("records".into()),
2129 make::var("predicates"),
2130 ),
2131 );
2132 assert_eq!(
2133 make::static_access(make::var("predicates"), ["records", "record"]),
2134 t
2135 );
2136 }
2137
2138 #[test]
2139 fn contract_annotation_order() {
2140 let ty1 = LabeledType {
2141 typ: TypeF::Number.into(),
2142 label: Label::dummy(),
2143 };
2144 let annot1 = TypeAnnotation {
2145 typ: None,
2146 contracts: vec![ty1.clone()],
2147 };
2148
2149 let ty2 = LabeledType {
2150 typ: TypeF::Bool.into(),
2151 label: Label::dummy(),
2152 };
2153 let annot2 = TypeAnnotation {
2154 typ: None,
2155 contracts: vec![ty2.clone()],
2156 };
2157
2158 assert_eq!(Combine::combine(annot1, annot2).contracts, vec![ty1, ty2])
2159 }
2160
2161 /// Regression test for issue [#548](https://github.com/tweag/nickel/issues/548)
2162 #[test]
2163 fn type_annotation_combine() {
2164 let inner = TypeAnnotation {
2165 typ: Some(LabeledType {
2166 typ: Type::from(TypeF::Number),
2167 label: Label::dummy(),
2168 }),
2169 ..Default::default()
2170 };
2171 let outer = TypeAnnotation::default();
2172 let res = TypeAnnotation::combine(outer, inner);
2173 assert_ne!(res.typ, None);
2174 }
2175}