Skip to main content

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}