1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
use super::*;
use crate::{
    combine::Combine,
    error::EvalError,
    identifier::{Ident, LocIdent},
    label::Label,
};
use std::{collections::HashSet, rc::Rc};

/// Additional attributes for record.
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone)]
pub struct RecordAttrs {
    /// If the record is an open record, ie ending with `..`. Open records have a different
    /// behavior when used as a record contract: they allow additional fields to be present.
    pub open: bool,
    /// A record is closurized when each element is a [crate::term::Term::Closure] or a constant.
    /// Note that closurization is _required_ for evaluated records that are passed to e.g.
    /// [crate::eval::merge::merge] or other primitive operators. Non-closurized record are mostly
    /// produced by the parser or when building Nickel terms programmatically. When encountered by
    /// the main eval loop, they are closurized and the flag is set accordingly.
    ///
    /// Ideally, we would have a different AST representation for evaluation, where records would
    /// be closurized by construction. In the meantime, while we need to cope with a unique AST
    /// across the whole pipeline, we use this flag.
    pub closurized: bool,
}

impl RecordAttrs {
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the `closurized` flag to true and return the updated attributes.
    pub fn closurized(mut self) -> Self {
        self.closurized = true;
        self
    }
}

impl Combine for RecordAttrs {
    fn combine(left: Self, right: Self) -> Self {
        RecordAttrs {
            open: left.open || right.open,
            closurized: left.closurized && right.closurized,
        }
    }
}

/// Dependencies of a field or a cache element over the other recursive fields of a recursive
/// record.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FieldDeps {
    /// The set of dependencies is fixed and has been computed. When attached to an element, an
    /// empty set of dependency means that the element isn't revertible, but standard.
    Known(Rc<HashSet<Ident>>),

    /// The element is revertible, but the set of dependencies hasn't been computed. In that case,
    /// the interpreter should be conservative and assume that any recursive references can appear
    /// in the content of the corresponding element.
    Unknown,
}

impl FieldDeps {
    /// Compute the union of two cache elements dependencies. [`FieldDeps::Unknown`] can be see as
    /// the top element, meaning that if one of the two set of dependencies is
    /// [`FieldDeps::Unknown`], so is the result.
    pub fn union(self, other: Self) -> Self {
        match (self, other) {
            // If one of the field has unknown dependencies (understand: may depend on all the other
            // fields), then the resulting fields has unknown dependencies as well
            (FieldDeps::Unknown, _) | (_, FieldDeps::Unknown) => FieldDeps::Unknown,
            (FieldDeps::Known(deps1), FieldDeps::Known(deps2)) => {
                let union: HashSet<Ident> = deps1.union(&*deps2).cloned().collect();
                FieldDeps::Known(Rc::new(union))
            }
        }
    }

    /// Return an empty set of dependencies
    pub fn empty() -> Self {
        FieldDeps::Known(Rc::new(HashSet::new()))
    }

    /// Return `true` if the dependencies are known and are empty, or `false` otherwise.
    pub fn is_empty(&self) -> bool {
        matches!(self, FieldDeps::Known(deps) if deps.is_empty())
    }
}

impl From<HashSet<Ident>> for FieldDeps {
    fn from(set: HashSet<Ident>) -> Self {
        FieldDeps::Known(Rc::new(set))
    }
}

/// Store field interdependencies in a recursive record. Map each static and dynamic field to the
/// set of recursive fields that syntactically appears in their definition as free variables.
#[derive(Debug, Default, Eq, PartialEq, Clone)]
pub struct RecordDeps {
    /// Must have exactly the same keys as the static fields map of the recursive record.
    pub stat_fields: IndexMap<Ident, FieldDeps>,
    /// Must have exactly the same length as the dynamic fields list of the recursive record.
    pub dyn_fields: Vec<FieldDeps>,
}

/// The metadata attached to record fields.
#[derive(Debug, PartialEq, Clone, Default)]
pub struct FieldMetadata {
    pub doc: Option<String>,
    pub annotation: TypeAnnotation,
    /// If the field is optional.
    pub opt: bool,
    /// If the field is serialized.
    pub not_exported: bool,
    pub priority: MergePriority,
}

impl FieldMetadata {
    pub fn new() -> Self {
        Default::default()
    }
}

/// A record field with its metadata.
#[derive(Clone, Default, PartialEq, Debug)]
pub struct Field {
    /// The value is optional because record field may not have a definition (e.g. optional fields).
    pub value: Option<RichTerm>,
    pub metadata: FieldMetadata,
    /// List of contracts yet to be applied.
    /// These are only observed when data enter or leave the record.
    pub pending_contracts: Vec<RuntimeContract>,
}

impl From<RichTerm> for Field {
    fn from(rt: RichTerm) -> Self {
        Field {
            value: Some(rt),
            ..Default::default()
        }
    }
}

impl Field {
    /// Map a function over the value of the field, if any.
    pub fn map_value(self, f: impl FnOnce(RichTerm) -> RichTerm) -> Self {
        Field {
            value: self.value.map(f),
            ..self
        }
    }

    /// Map a fallible function over the value of the field, if any.
    pub fn try_map_value<E>(
        self,
        f: impl FnOnce(RichTerm) -> Result<RichTerm, E>,
    ) -> Result<Self, E> {
        Ok(Field {
            value: self.value.map(f).transpose()?,
            ..self
        })
    }

    /// Determine if a field is optional and without a defined value. In that case, it is usually
    /// silently ignored by most record operations (`has_field`, `values`, etc.).
    pub fn is_empty_optional(&self) -> bool {
        self.value.is_none() && self.metadata.opt
    }

    /// Required by the dynamic extension operator to know if the field being treated has a defined
    /// value that must be obtained from the stack or not.
    pub fn extension_kind(&self) -> RecordExtKind {
        if self.value.is_some() {
            RecordExtKind::WithValue
        } else {
            RecordExtKind::WithoutValue
        }
    }

    pub fn with_name(self, field_name: Option<LocIdent>) -> Self {
        Field {
            metadata: FieldMetadata {
                annotation: self.metadata.annotation.with_field_name(field_name),
                ..self.metadata
            },
            ..self
        }
    }
}

impl Traverse<RichTerm> for Field {
    fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<Field, E>
    where
        F: FnMut(RichTerm) -> Result<RichTerm, E>,
    {
        let annotation = self.metadata.annotation.traverse(f, order)?;
        let value = self.value.map(|v| v.traverse(f, order)).transpose()?;

        let metadata = FieldMetadata {
            annotation,
            ..self.metadata
        };

        let pending_contracts = self
            .pending_contracts
            .into_iter()
            .map(|pending_contract| pending_contract.traverse(f, order))
            .collect::<Result<Vec<_>, _>>()?;

        Ok(Field {
            metadata,
            value,
            pending_contracts,
        })
    }

    fn traverse_ref<S, U>(
        &self,
        f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
        state: &S,
    ) -> Option<U> {
        self.metadata
            .annotation
            .traverse_ref(f, state)
            .or_else(|| self.value.as_ref().and_then(|v| v.traverse_ref(f, state)))
            .or_else(|| {
                self.pending_contracts
                    .iter()
                    .find_map(|c| c.traverse_ref(f, state))
            })
    }
}

/// The base structure of a Nickel record.
///
/// Used to group together fields common to both the [super::Term::Record] and
/// [super::Term::RecRecord] terms.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct RecordData {
    /// Fields whose names are known statically.
    pub fields: IndexMap<LocIdent, Field>,
    /// Attributes which may be applied to a record.
    pub attrs: RecordAttrs,
    /// The hidden part of a record under a polymorphic contract.
    pub sealed_tail: Option<SealedTail>,
}

/// Error raised by [RecordData] methods when trying to access a field that doesn't have a
/// definition and isn't optional.
#[derive(Clone, Debug)]
pub struct MissingFieldDefError {
    pub id: LocIdent,
    pub metadata: FieldMetadata,
}

impl MissingFieldDefError {
    pub fn into_eval_err(self, pos_record: TermPos, pos_access: TermPos) -> EvalError {
        EvalError::MissingFieldDef {
            id: self.id,
            metadata: self.metadata,
            pos_record,
            pos_access,
        }
    }
}

impl RecordData {
    pub fn new(
        fields: IndexMap<LocIdent, Field>,
        attrs: RecordAttrs,
        sealed_tail: Option<SealedTail>,
    ) -> Self {
        RecordData {
            fields,
            attrs,
            sealed_tail,
        }
    }

    /// A record with no fields and the default set of attributes.
    pub fn empty() -> Self {
        Default::default()
    }

    /// A record with the provided fields and the default set of attributes.
    pub fn with_field_values(field_values: impl IntoIterator<Item = (LocIdent, RichTerm)>) -> Self {
        let fields = field_values
            .into_iter()
            .map(|(id, value)| {
                (
                    id,
                    Field {
                        value: Some(value),
                        ..Default::default()
                    },
                )
            })
            .collect();

        RecordData {
            fields,
            ..Default::default()
        }
    }

    /// Returns the record resulting from applying the provided function
    /// to each field.
    ///
    /// Note that `f` is taken as `mut` in order to allow it to mutate
    /// external state while iterating.
    pub fn map_values<F>(self, mut f: F) -> Self
    where
        F: FnMut(LocIdent, Option<RichTerm>) -> Option<RichTerm>,
    {
        let fields = self
            .fields
            .into_iter()
            .map(|(id, field)| {
                (
                    id,
                    Field {
                        value: f(id, field.value),
                        ..field
                    },
                )
            })
            .collect();
        RecordData { fields, ..self }
    }

    /// Returns the record resulting from applying the provided function to each field with a
    /// defined value. Fields without a value are left unchanged.
    pub fn map_defined_values<F>(self, mut f: F) -> Self
    where
        F: FnMut(LocIdent, RichTerm) -> RichTerm,
    {
        self.map_values(|id, value| value.map(|v| f(id, v)))
    }

    /// Turn the record into an iterator over the fields' values, ignoring optional fields without
    /// definition.
    ///
    /// The returned iterator applies pending contracts to each value.
    ///
    /// Fields that aren't optional but yet don't have a definition are mapped to the
    /// error `MissingFieldDefError`.
    pub fn into_iter_without_opts(
        self,
    ) -> impl Iterator<Item = Result<(Ident, RichTerm), MissingFieldDefError>> {
        self.fields
            .into_iter()
            .filter_map(|(id, field)| match field.value {
                Some(v) => {
                    let pos = v.pos;
                    Some(Ok((
                        id.ident(),
                        RuntimeContract::apply_all(v, field.pending_contracts.into_iter(), pos),
                    )))
                }
                None if !field.metadata.opt => Some(Err(MissingFieldDefError {
                    id,
                    metadata: field.metadata,
                })),
                None => None,
            })
    }

    /// Return an iterator over the fields' values, ignoring optional fields
    /// without definition and fields marked as not_exported. Fields that
    /// aren't optional but yet don't have a definition are mapped to the error
    /// `MissingFieldDefError`.
    pub fn iter_serializable(
        &self,
    ) -> impl Iterator<Item = Result<(Ident, &RichTerm), MissingFieldDefError>> {
        self.fields.iter().filter_map(|(id, field)| {
            debug_assert!(field.pending_contracts.is_empty());
            match field.value {
                Some(ref v) if !field.metadata.not_exported => Some(Ok((id.ident(), v))),
                None if !field.metadata.opt && !field.metadata.not_exported => {
                    Some(Err(MissingFieldDefError {
                        id: *id,
                        metadata: field.metadata.clone(),
                    }))
                }
                _ => None,
            }
        })
    }

    /// Get the value of a field. Ignore optional fields without value: trying to get their value
    /// returns `None`, as if they weren't present at all. Trying to extract a field without value
    /// which is non optional return an error.
    ///
    /// This method automatically applies the potential pending contracts
    pub fn get_value_with_ctrs(
        &self,
        id: &LocIdent,
    ) -> Result<Option<RichTerm>, MissingFieldDefError> {
        match self.fields.get(id) {
            Some(Field {
                value: None,
                metadata: metadata @ FieldMetadata { opt: false, .. },
                ..
            }) => Err(MissingFieldDefError {
                id: *id,
                metadata: metadata.clone(),
            }),
            Some(Field {
                value: Some(value),
                pending_contracts,
                ..
            }) => {
                let pos = value.pos;
                Ok(Some(RuntimeContract::apply_all(
                    value.clone(),
                    pending_contracts.iter().cloned(),
                    pos,
                )))
            }
            _ => Ok(None),
        }
    }

    /// Return a vector of all the fields' names of this record sorted alphabetically.
    ///
    /// # Parameters
    ///
    /// - `op_kind` controls if we should ignore or include empty optional fields
    pub fn field_names(&self, op_kind: RecordOpKind) -> Vec<LocIdent> {
        let mut fields: Vec<LocIdent> = self
            .fields
            .iter()
            // Ignore optional fields without definitions.
            .filter(|(_, field)| {
                matches!(op_kind, RecordOpKind::ConsiderAllFields) || !field.is_empty_optional()
            })
            .map(|(id, _)| *id)
            .collect();

        fields.sort_by(|id1, id2| id1.label().cmp(id2.label()));
        fields
    }
}

/// The sealed tail of a Nickel record under a polymorphic contract.
///
/// Note that access to the enclosed term must only be allowed when a matching sealing key is
/// provided. If this is not enforced it will lead to parametricity violations.
#[derive(Clone, Debug, PartialEq)]
pub struct SealedTail {
    /// The key with which the tail is sealed.
    sealing_key: SealingKey,
    /// The label to which blame will be attributed if code tries to
    /// interact with the sealed tail in any way.
    pub label: Label,
    /// The term which is sealed.
    term: RichTerm,
    /// The field names of the sealed fields.
    // You may find yourself wondering why this is a `Vec` rather than a
    // `HashSet` given we only ever do containment checks against it.
    // In brief: we'd need to use a `HashSet<String>`, which would mean
    // allocating `fields.len()` `String`s in a fairly hot codepath.
    // Since we only ever check whether the tail contains a specific field
    // when we already know we're going to raise an error, it's not really
    // an issue to have a linear lookup there, so we do that instead.
    fields: Vec<Ident>,
}

impl SealedTail {
    pub fn new(sealing_key: SealingKey, label: Label, term: RichTerm, fields: Vec<Ident>) -> Self {
        Self {
            sealing_key,
            label,
            term,
            fields,
        }
    }

    /// Returns the sealed term if the key matches, otherwise returns None.
    pub fn unseal(&self, key: &SealingKey) -> Option<&RichTerm> {
        if key == &self.sealing_key {
            Some(&self.term)
        } else {
            None
        }
    }

    pub fn has_field(&self, field: &Ident) -> bool {
        self.fields.contains(field)
    }

    pub fn has_dyn_field(&self, field: &str) -> bool {
        self.fields.iter().any(|i| i.label() == field)
    }
}