1use super::{primop::PrimOp, *};
8use crate::{
9 combine::Combine,
10 eval::value::{
11 ArrayData, Container, EnumVariantData, NickelValue, TypeData, ValueContentRef,
12 ValueContentRefMut,
13 },
14 label,
15 position::{PosIdx, PosTable},
16 term::{self, pattern::compile::Compile as _},
17 typ as mline_type,
18};
19
20use nickel_lang_parser::{
21 ast::{
22 self,
23 pattern::{Pattern, PatternData},
24 record::Record,
25 typ::{
26 EnumRow, EnumRows, EnumRowsUnr, RecordRow, RecordRows, RecordRowsUnr, Type, TypeUnr,
27 },
28 },
29 error::ParseError,
30 identifier::LocIdent,
31 typ::{EnumRowF, RecordRowF},
32};
33
34use indexmap::IndexMap;
35use smallvec::SmallVec;
36use std::rc::Rc;
37
38pub trait FromMainline<'ast, T> {
48 fn from_mainline(alloc: &'ast AstAlloc, pos_table: &PosTable, mainline: &T) -> Self;
49}
50
51impl<'ast> FromMainline<'ast, term::TypeAnnotation> for Annotation<'ast> {
52 fn from_mainline(
53 alloc: &'ast AstAlloc,
54 pos_table: &PosTable,
55 annot: &term::TypeAnnotation,
56 ) -> Self {
57 let typ = annot
58 .typ
59 .as_ref()
60 .map(|typ| typ.typ.to_ast(alloc, pos_table));
61
62 let contracts = alloc.alloc_many(
63 annot
64 .contracts
65 .iter()
66 .map(|contract| contract.typ.to_ast(alloc, pos_table)),
67 );
68
69 Annotation { typ, contracts }
70 }
71}
72
73impl<'ast> FromMainline<'ast, (LocIdent, term::record::Field)> for record::FieldDef<'ast> {
74 fn from_mainline(
75 alloc: &'ast AstAlloc,
76 pos_table: &PosTable,
77 (id, content): &(LocIdent, term::record::Field),
78 ) -> Self {
79 let pos = content
80 .value
81 .as_ref()
82 .map(|value| id.pos.fuse(pos_table.get(value.pos_idx())))
83 .unwrap_or(id.pos);
84
85 record::FieldDef {
86 path: alloc.alloc_singleton(record::FieldPathElem::Ident(*id)),
87 value: content
88 .value
89 .as_ref()
90 .map(|term| term.to_ast(alloc, pos_table)),
91 metadata: content.metadata.to_ast(alloc, pos_table),
92 pos,
93 }
94 }
95}
96
97impl<'ast> FromMainline<'ast, term::record::FieldMetadata> for record::FieldMetadata<'ast> {
98 fn from_mainline(
99 alloc: &'ast AstAlloc,
100 pos_table: &PosTable,
101 metadata: &term::record::FieldMetadata,
102 ) -> Self {
103 let doc = metadata.doc.as_ref().map(|doc| alloc.alloc_str(doc));
104
105 record::FieldMetadata {
106 doc,
107 annotation: metadata.annotation.to_ast(alloc, pos_table),
108 opt: metadata.opt,
109 not_exported: metadata.not_exported,
110 priority: metadata.priority.clone(),
111 }
112 }
113}
114
115impl<'ast> FromMainline<'ast, term::record::SharedMetadata> for record::FieldMetadata<'ast> {
116 fn from_mainline(
117 alloc: &'ast AstAlloc,
118 pos_table: &PosTable,
119 metadata: &term::record::SharedMetadata,
120 ) -> Self {
121 metadata
122 .0
123 .as_ref()
124 .map(|metadata| (**metadata).to_ast(alloc, pos_table))
125 .unwrap_or_default()
126 }
127}
128
129impl<'ast> FromMainline<'ast, term::record::Include> for record::Include<'ast> {
130 fn from_mainline(
131 alloc: &'ast AstAlloc,
132 pos_table: &PosTable,
133 mainline: &term::record::Include,
134 ) -> Self {
135 record::Include {
136 ident: mainline.ident,
137 metadata: mainline.metadata.to_ast(alloc, pos_table),
138 }
139 }
140}
141
142impl<'ast> FromMainline<'ast, mline_type::Type> for Type<'ast> {
143 fn from_mainline(
144 alloc: &'ast AstAlloc,
145 pos_table: &PosTable,
146 mainline: &mline_type::Type,
147 ) -> Self {
148 Type {
149 typ: mainline.typ.to_ast(alloc, pos_table),
150 pos: mainline.pos,
151 }
152 }
153}
154
155type MainlineTypeUnr = mline_type::TypeF<
156 Box<mline_type::Type>,
157 mline_type::RecordRows,
158 mline_type::EnumRows,
159 NickelValue,
160>;
161
162impl<'ast> FromMainline<'ast, MainlineTypeUnr> for TypeUnr<'ast> {
163 fn from_mainline(alloc: &'ast AstAlloc, pos_table: &PosTable, typ: &MainlineTypeUnr) -> Self {
164 typ.clone().map(
165 |typ| alloc.alloc((*typ).to_ast(alloc, pos_table)),
166 |rrows| rrows.to_ast(alloc, pos_table),
167 |erows| erows.to_ast(alloc, pos_table),
168 |ctr| ctr.to_ast(alloc, pos_table),
169 )
170 }
171}
172
173impl<'ast> FromMainline<'ast, mline_type::RecordRows> for RecordRows<'ast> {
174 fn from_mainline(
175 alloc: &'ast AstAlloc,
176 pos_table: &PosTable,
177 rrows: &mline_type::RecordRows,
178 ) -> Self {
179 RecordRows(rrows.0.to_ast(alloc, pos_table))
180 }
181}
182
183impl<'ast> FromMainline<'ast, mline_type::EnumRows> for EnumRows<'ast> {
184 fn from_mainline(
185 alloc: &'ast AstAlloc,
186 pos_table: &PosTable,
187 erows: &mline_type::EnumRows,
188 ) -> Self {
189 EnumRows(erows.0.to_ast(alloc, pos_table))
190 }
191}
192
193type MainlineEnumRowsUnr = mline_type::EnumRowsF<Box<mline_type::Type>, Box<mline_type::EnumRows>>;
194
195impl<'ast> FromMainline<'ast, MainlineEnumRowsUnr> for EnumRowsUnr<'ast> {
196 fn from_mainline(
197 alloc: &'ast AstAlloc,
198 pos_table: &PosTable,
199 erows: &MainlineEnumRowsUnr,
200 ) -> Self {
201 erows.clone().map(
202 |typ| alloc.alloc((*typ).to_ast(alloc, pos_table)),
203 |erows| alloc.alloc((*erows).to_ast(alloc, pos_table)),
204 )
205 }
206}
207
208type MainlineRecordRowsUnr =
209 mline_type::RecordRowsF<Box<mline_type::Type>, Box<mline_type::RecordRows>>;
210
211impl<'ast> FromMainline<'ast, MainlineRecordRowsUnr> for RecordRowsUnr<'ast> {
212 fn from_mainline(
213 alloc: &'ast AstAlloc,
214 pos_table: &PosTable,
215 rrows: &MainlineRecordRowsUnr,
216 ) -> Self {
217 rrows.clone().map(
218 |typ| alloc.alloc((*typ).to_ast(alloc, pos_table)),
219 |rrows| alloc.alloc((*rrows).to_ast(alloc, pos_table)),
220 )
221 }
222}
223
224impl<'ast> FromMainline<'ast, term::Term> for Node<'ast> {
225 fn from_mainline(alloc: &'ast AstAlloc, pos_table: &PosTable, term: &term::Term) -> Self {
226 use term::Term;
227
228 match term {
229 Term::StrChunks(chunks) => {
230 alloc.string_chunks(chunks.iter().rev().map(|chunk| match chunk {
231 term::StrChunk::Literal(s) => StringChunk::Literal(s.clone()),
232 term::StrChunk::Expr(expr, indent) => {
233 StringChunk::Expr(expr.to_ast(alloc, pos_table), *indent)
234 }
235 }))
236 }
237 Term::Fun(data) => {
238 let fst_arg = Pattern::any(data.arg);
239 let body = &data.body;
240
241 let mut args = vec![fst_arg];
242 let mut maybe_next_fun = body;
243
244 let final_body = loop {
245 match maybe_next_fun.as_term() {
246 Some(Term::Fun(data)) => {
247 args.push(Pattern::any(data.arg));
248 maybe_next_fun = &data.body;
249 }
250 _ => break maybe_next_fun,
251 }
252 };
253
254 alloc.fun(args, final_body.to_ast(alloc, pos_table))
255 }
256 Term::Let(data) => alloc.let_block(
257 data.bindings.iter().map(|(id, value)| LetBinding {
258 pattern: Pattern::any(*id),
259 value: value.to_ast(alloc, pos_table),
260 metadata: Default::default(),
261 }),
262 data.body.to_ast(alloc, pos_table),
263 data.attrs.rec,
264 ),
265 Term::App(data) => {
266 if let Some(Term::App(data_inner)) = data.head.as_term()
270 && let Some(Term::Op1(data)) = data_inner.head.as_term()
271 && data.op == term::UnaryOp::IfThenElse
272 {
273 return alloc.if_then_else(
274 data.arg.to_ast(alloc, pos_table),
275 data_inner.arg.to_ast(alloc, pos_table),
276 data.arg.to_ast(alloc, pos_table),
277 );
278 }
279
280 let mut args = vec![data.arg.to_ast(alloc, pos_table)];
281 let mut maybe_next_app = data.head.as_term();
282
283 while let Some(Term::App(data_next)) = maybe_next_app {
284 args.push(data_next.arg.to_ast(alloc, pos_table));
285 maybe_next_app = data_next.head.as_term();
286 }
287
288 args.reverse();
292 alloc.app(data.head.to_ast(alloc, pos_table), args)
293 }
294 Term::Var(id) => Node::Var(*id),
295 Term::RecRecord(data) => {
296 let mut field_defs = Vec::new();
297
298 field_defs.extend(data.record.fields.iter().map(|(id, field)| {
299 let pos = field
300 .value
301 .as_ref()
302 .map(|value| id.pos.fuse(value.pos(pos_table)))
303 .unwrap_or(id.pos);
304
305 record::FieldDef {
306 path: record::FieldPathElem::single_ident_path(alloc, *id),
307 value: field
308 .value
309 .as_ref()
310 .map(|term| term.to_ast(alloc, pos_table)),
311 metadata: field.metadata.to_ast(alloc, pos_table),
312 pos,
313 }
314 }));
315
316 field_defs.extend(data.dyn_fields.iter().map(|(expr, field)| {
317 let pos_field_name = expr.pos(pos_table);
318 let pos = field
319 .value
320 .as_ref()
321 .map(|v| pos_field_name.fuse(v.pos(pos_table)))
322 .unwrap_or(pos_field_name);
323
324 record::FieldDef {
325 path: record::FieldPathElem::single_expr_path(
326 alloc,
327 expr.to_ast(alloc, pos_table),
328 ),
329 metadata: field.metadata.to_ast(alloc, pos_table),
330 value: field
331 .value
332 .as_ref()
333 .map(|term| term.to_ast(alloc, pos_table)),
334 pos,
335 }
336 }));
337
338 alloc.record(Record {
339 field_defs: alloc.alloc_many(field_defs),
340 includes: alloc.alloc_many(
341 data.includes
342 .iter()
343 .map(|incl| incl.to_ast(alloc, pos_table)),
344 ),
345 open: data.record.attrs.open,
346 })
347 }
348 Term::Op1(data) => alloc.prim_op(
349 PrimOp::from(&data.op),
350 std::iter::once(data.arg.to_ast(alloc, pos_table)),
351 ),
352 Term::Op2(data) => {
353 let op = PrimOp::from(&data.op);
358 let mut args = [
359 data.arg1.to_ast(alloc, pos_table),
360 data.arg2.to_ast(alloc, pos_table),
361 ];
362
363 if matches!(op, PrimOp::ArrayAt | PrimOp::StringContains) {
364 args.swap(0, 1);
365 }
366
367 alloc.prim_op(op, args)
368 }
369 Term::OpN(data) => {
370 let op = PrimOp::from(&data.op);
372 let mut args: Vec<_> = data
373 .args
374 .iter()
375 .map(|arg| arg.to_ast(alloc, pos_table))
376 .collect();
377
378 if let PrimOp::StringSubstr = op {
379 debug_assert_eq!(args.len(), 3);
380 args.swap(0, 1);
383 args.swap(1, 2);
384 }
385
386 alloc.prim_op(op, args)
387 }
388 Term::Sealed(..) => panic!("didn't expect a sealed term at the first stage"),
389 Term::Annotated(data) => alloc.annotated(
390 (*data.annot).to_ast(alloc, pos_table),
391 data.inner.to_ast(alloc, pos_table),
392 ),
393 Term::Import(term::Import::Path { path, format }) => {
394 alloc.import_path(path.clone(), *format)
395 }
396 Term::Import(term::Import::Package { id }) => alloc.import_package(*id),
397 Term::ResolvedImport(_) => panic!("didn't expect a resolved import at parsing stage"),
398 Term::ParseError(error) => alloc.parse_error((**error).clone()),
399 Term::RuntimeError(_) => panic!("didn't expect a runtime error at parsing stage"),
400 Term::Closurize(value) => Ast::from_mainline(alloc, pos_table, value).node,
401 }
402 }
403}
404
405impl<'ast> FromMainline<'ast, NickelValue> for Ast<'ast> {
406 fn from_mainline(alloc: &'ast AstAlloc, pos_table: &PosTable, rterm: &NickelValue) -> Self {
407 let node = match rterm.content_ref() {
408 ValueContentRef::Null => Node::Null,
409 ValueContentRef::Bool(b) => Node::Bool(b),
410 ValueContentRef::Number(n) => alloc.number(n.clone()),
411 ValueContentRef::Array(Container::Empty) => Node::Array(&[]),
412 ValueContentRef::Array(Container::Alloc(ArrayData {
415 array,
416 pending_contracts: _,
417 })) => {
418 let elts = array
422 .iter()
423 .map(|elt| elt.to_ast(alloc, pos_table))
424 .collect::<Vec<_>>();
425 alloc.array(elts)
426 }
427 ValueContentRef::Record(Container::Empty) => alloc.record(Record::empty()),
428 ValueContentRef::Record(Container::Alloc(data)) => {
429 let field_defs = alloc.alloc_many(data.fields.iter().map(|(id, field)| {
430 let pos = field
431 .value
432 .as_ref()
433 .map(|value| id.pos.fuse(value.pos(pos_table)))
434 .unwrap_or(id.pos);
435
436 record::FieldDef {
437 path: record::FieldPathElem::single_ident_path(alloc, *id),
438 value: field
439 .value
440 .as_ref()
441 .map(|term| term.to_ast(alloc, pos_table)),
442 metadata: field.metadata.to_ast(alloc, pos_table),
443 pos,
444 }
445 }));
446
447 alloc.record(Record {
448 field_defs,
449 includes: &[],
450 open: data.attrs.open,
451 })
452 }
453 ValueContentRef::String(s) => alloc.string(s),
454 ValueContentRef::Term(term) => term.to_ast(alloc, pos_table),
455 ValueContentRef::EnumVariant(EnumVariantData { tag, arg }) => {
456 alloc.enum_variant(*tag, arg.as_ref().map(|arg| arg.to_ast(alloc, pos_table)))
457 }
458 ValueContentRef::Type(TypeData { typ, contract: _ }) => {
460 alloc.typ(typ.to_ast(alloc, pos_table))
461 }
462 ValueContentRef::Label(_)
466 | ValueContentRef::CustomContract(_)
467 | ValueContentRef::ForeignId(_)
468 | ValueContentRef::SealingKey(_)
469 | ValueContentRef::Thunk(_) => unimplemented!(),
470 };
471
472 Ast {
473 node,
474 pos: rterm.pos(pos_table),
475 }
476 }
477}
478
479impl<'ast> FromMainline<'ast, NickelValue> for &'ast Ast<'ast> {
480 fn from_mainline(alloc: &'ast AstAlloc, pos_table: &PosTable, rterm: &NickelValue) -> Self {
481 alloc.alloc(rterm.to_ast(alloc, pos_table))
482 }
483}
484
485pub trait ToAst<'ast, T> {
487 fn to_ast(&self, alloc: &'ast AstAlloc, pos_table: &PosTable) -> T;
488}
489
490impl<'ast, S, T> ToAst<'ast, T> for S
491where
492 T: FromMainline<'ast, S>,
493{
494 fn to_ast(&self, alloc: &'ast AstAlloc, pos_table: &PosTable) -> T {
495 T::from_mainline(alloc, pos_table, self)
496 }
497}
498
499impl From<&term::UnaryOp> for PrimOp {
501 fn from(op: &term::UnaryOp) -> Self {
502 match op {
503 term::UnaryOp::IfThenElse => {
504 panic!("if-then-else should have been handed separately by special casing")
505 }
506 term::UnaryOp::Typeof => PrimOp::Typeof,
507 term::UnaryOp::Cast => PrimOp::Cast,
508 term::UnaryOp::BoolAnd => PrimOp::BoolAnd,
509 term::UnaryOp::BoolOr => PrimOp::BoolOr,
510 term::UnaryOp::BoolNot => PrimOp::BoolNot,
511 term::UnaryOp::Blame => PrimOp::Blame,
512 term::UnaryOp::EnumEmbed(loc_ident) => PrimOp::EnumEmbed(*loc_ident),
513 term::UnaryOp::RecordAccess(loc_ident) => PrimOp::RecordStatAccess(*loc_ident),
514 term::UnaryOp::ArrayMap => PrimOp::ArrayMap,
515 term::UnaryOp::RecordMap => PrimOp::RecordMap,
516 term::UnaryOp::LabelFlipPol => PrimOp::LabelFlipPol,
517 term::UnaryOp::LabelPol => PrimOp::LabelPol,
518 term::UnaryOp::LabelGoDom => PrimOp::LabelGoDom,
519 term::UnaryOp::LabelGoCodom => PrimOp::LabelGoCodom,
520 term::UnaryOp::LabelGoArray => PrimOp::LabelGoArray,
521 term::UnaryOp::LabelGoDict => PrimOp::LabelGoDict,
522 term::UnaryOp::Seq => PrimOp::Seq,
523 term::UnaryOp::DeepSeq => PrimOp::DeepSeq,
524 term::UnaryOp::ArrayLength => PrimOp::ArrayLength,
525 term::UnaryOp::ArrayGen => PrimOp::ArrayGen,
526 term::UnaryOp::RecordFields(record_op_kind) => PrimOp::RecordFields(*record_op_kind),
527 term::UnaryOp::RecordValues => PrimOp::RecordValues,
528 term::UnaryOp::StringTrim => PrimOp::StringTrim,
529 term::UnaryOp::StringChars => PrimOp::StringChars,
530 term::UnaryOp::StringUppercase => PrimOp::StringUppercase,
531 term::UnaryOp::StringLowercase => PrimOp::StringLowercase,
532 term::UnaryOp::StringLength => PrimOp::StringLength,
533 term::UnaryOp::ToString => PrimOp::ToString,
534 term::UnaryOp::NumberFromString => PrimOp::NumberFromString,
535 term::UnaryOp::EnumFromString => PrimOp::EnumFromString,
536 term::UnaryOp::StringIsMatch => PrimOp::StringIsMatch,
537 term::UnaryOp::StringFind => PrimOp::StringFind,
538 term::UnaryOp::StringFindAll => PrimOp::StringFindAll,
539 term::UnaryOp::Force {
540 ignore_not_exported,
541 } => PrimOp::Force {
542 ignore_not_exported: *ignore_not_exported,
543 },
544 term::UnaryOp::RecordEmptyWithTail => PrimOp::RecordEmptyWithTail,
545 term::UnaryOp::RecordFreeze => PrimOp::RecordFreeze,
546 term::UnaryOp::Trace => PrimOp::Trace,
547 term::UnaryOp::LabelPushDiag => PrimOp::LabelPushDiag,
548 #[cfg(feature = "nix-experimental")]
549 term::UnaryOp::EvalNix => PrimOp::EvalNix,
550 term::UnaryOp::EnumGetArg => PrimOp::EnumGetArg,
551 term::UnaryOp::EnumMakeVariant => PrimOp::EnumMakeVariant,
552 term::UnaryOp::EnumIsVariant => PrimOp::EnumIsVariant,
553 term::UnaryOp::EnumGetTag => PrimOp::EnumGetTag,
554 term::UnaryOp::ContractCustom => PrimOp::ContractCustom,
555 term::UnaryOp::NumberArcCos => PrimOp::NumberArcCos,
556 term::UnaryOp::NumberArcSin => PrimOp::NumberArcSin,
557 term::UnaryOp::NumberArcTan => PrimOp::NumberArcTan,
558 term::UnaryOp::NumberCos => PrimOp::NumberCos,
559 term::UnaryOp::NumberSin => PrimOp::NumberSin,
560 term::UnaryOp::NumberTan => PrimOp::NumberTan,
561
562 op @ (term::UnaryOp::TagsOnlyMatch { .. }
563 | term::UnaryOp::ChunksConcat
564 | term::UnaryOp::StringIsMatchCompiled(_)
565 | term::UnaryOp::StringFindCompiled(_)
566 | term::UnaryOp::StringFindAllCompiled(_)
567 | term::UnaryOp::RecDefault
568 | term::UnaryOp::RecForce
569 | term::UnaryOp::ContractPostprocessResult
570 | term::UnaryOp::ContractAttachDefaultLabel) => {
571 panic!("didn't expect {op} at the parsing stage")
572 }
573 }
574 }
575}
576
577impl From<&term::BinaryOp> for PrimOp {
578 fn from(op: &term::BinaryOp) -> Self {
579 match op {
580 term::BinaryOp::Plus => PrimOp::Plus,
581 term::BinaryOp::Sub => PrimOp::Sub,
582 term::BinaryOp::Mult => PrimOp::Mult,
583 term::BinaryOp::Div => PrimOp::Div,
584 term::BinaryOp::Modulo => PrimOp::Modulo,
585 term::BinaryOp::NumberArcTan2 => PrimOp::NumberArcTan2,
586 term::BinaryOp::NumberLog => PrimOp::NumberLog,
587 term::BinaryOp::Pow => PrimOp::Pow,
588 term::BinaryOp::StringConcat => PrimOp::StringConcat,
589 term::BinaryOp::StringBase64Encode => PrimOp::StringBase64Encode,
590 term::BinaryOp::StringBase64Decode => PrimOp::StringBase64Decode,
591 term::BinaryOp::Eq => PrimOp::Eq,
592 term::BinaryOp::LessThan => PrimOp::LessThan,
593 term::BinaryOp::LessOrEq => PrimOp::LessOrEq,
594 term::BinaryOp::GreaterThan => PrimOp::GreaterThan,
595 term::BinaryOp::GreaterOrEq => PrimOp::GreaterOrEq,
596 term::BinaryOp::ContractApply => PrimOp::ContractApply,
597 term::BinaryOp::ContractCheck => PrimOp::ContractCheck,
598 term::BinaryOp::LabelWithErrorData => PrimOp::LabelWithErrorData,
599 term::BinaryOp::LabelGoField => PrimOp::LabelGoField,
600 term::BinaryOp::RecordInsert {
604 metadata,
605 pending_contracts,
606 ext_kind,
607 op_kind,
608 } if metadata.is_empty()
609 && pending_contracts.is_empty()
610 && *ext_kind == term::RecordExtKind::WithValue =>
611 {
612 PrimOp::RecordInsert(*op_kind)
613 }
614 term::BinaryOp::RecordRemove(record_op_kind) => PrimOp::RecordRemove(*record_op_kind),
615 term::BinaryOp::RecordGet => PrimOp::RecordGet,
616 term::BinaryOp::RecordHasField(record_op_kind) => {
617 PrimOp::RecordHasField(*record_op_kind)
618 }
619 term::BinaryOp::RecordFieldIsDefined(record_op_kind) => {
620 PrimOp::RecordFieldIsDefined(*record_op_kind)
621 }
622 term::BinaryOp::RecordSplitPair => PrimOp::RecordSplitPair,
623 term::BinaryOp::RecordDisjointMerge => PrimOp::RecordDisjointMerge,
624 term::BinaryOp::ArrayConcat => PrimOp::ArrayConcat,
625 term::BinaryOp::ArrayAt => PrimOp::ArrayAt,
626 term::BinaryOp::Merge(merge_label) => PrimOp::Merge(merge_label.kind),
627 term::BinaryOp::Hash => PrimOp::Hash,
628 term::BinaryOp::Serialize => PrimOp::Serialize,
629 term::BinaryOp::Deserialize => PrimOp::Deserialize,
630 term::BinaryOp::StringSplit => PrimOp::StringSplit,
631 term::BinaryOp::StringContains => PrimOp::StringContains,
632 term::BinaryOp::StringCompare => PrimOp::StringCompare,
633 term::BinaryOp::ContractArrayLazyApp => PrimOp::ContractArrayLazyApp,
634 term::BinaryOp::ContractRecordLazyApp => PrimOp::ContractRecordLazyApp,
635 term::BinaryOp::LabelWithMessage => PrimOp::LabelWithMessage,
636 term::BinaryOp::LabelWithNotes => PrimOp::LabelWithNotes,
637 term::BinaryOp::LabelAppendNote => PrimOp::LabelAppendNote,
638 term::BinaryOp::LabelLookupTypeVar => PrimOp::LabelLookupTypeVar,
639
640 term::BinaryOp::Seal => PrimOp::Seal,
641 term::BinaryOp::Unseal => PrimOp::Unseal,
642
643 term::BinaryOp::RecordInsert { .. } => {
644 panic!("didn't expect %record/insert% at the parsing stage")
645 }
646 }
647 }
648}
649
650impl From<&term::NAryOp> for PrimOp {
651 fn from(op: &term::NAryOp) -> Self {
652 match op {
653 term::NAryOp::StringReplace => PrimOp::StringReplace,
654 term::NAryOp::StringReplaceRegex => PrimOp::StringReplaceRegex,
655 term::NAryOp::StringSubstr => PrimOp::StringSubstr,
656 term::NAryOp::MergeContract => PrimOp::MergeContract,
657 term::NAryOp::RecordSealTail => PrimOp::RecordSealTail,
658 term::NAryOp::RecordUnsealTail => PrimOp::RecordUnsealTail,
659 term::NAryOp::LabelInsertTypeVar => PrimOp::LabelInsertTypeVar,
660 term::NAryOp::ArraySlice => PrimOp::ArraySlice,
661 }
662 }
663}
664
665pub trait FromAst<T> {
673 fn from_ast(ast: &T, pos_table: &mut PosTable) -> Self;
674}
675
676pub trait ToMainline<T> {
677 fn to_mainline(&self, pos_table: &mut PosTable) -> T;
678}
679
680impl<S, T> ToMainline<T> for S
681where
682 T: FromAst<S>,
683{
684 fn to_mainline(&self, pos_table: &mut PosTable) -> T {
685 T::from_ast(self, pos_table)
686 }
687}
688
689impl<'ast> FromAst<Annotation<'ast>> for term::TypeAnnotation {
690 fn from_ast(annot: &Annotation<'ast>, pos_table: &mut PosTable) -> Self {
691 let typ = annot.typ.as_ref().map(|typ| typ.to_mainline(pos_table));
692
693 let contracts = annot
694 .contracts
695 .iter()
696 .map(|ctr| ctr.to_mainline(pos_table))
697 .collect();
698
699 term::TypeAnnotation { typ, contracts }
700 }
701}
702
703impl<'ast> FromAst<StringChunk<Ast<'ast>>> for term::StrChunk<NickelValue> {
704 fn from_ast(chunk: &StringChunk<Ast<'ast>>, pos_table: &mut PosTable) -> Self {
705 match chunk {
706 StringChunk::Literal(s) => term::StrChunk::Literal(s.clone()),
707 StringChunk::Expr(expr, indent) => {
708 term::StrChunk::Expr(expr.to_mainline(pos_table), *indent)
709 }
710 }
711 }
712}
713
714pub enum FieldName {
717 Ident(LocIdent),
718 Expr(NickelValue),
719}
720
721impl FromAst<record::FieldPathElem<'_>> for FieldName {
722 fn from_ast(elem: &record::FieldPathElem<'_>, pos_table: &mut PosTable) -> Self {
723 match elem {
724 record::FieldPathElem::Ident(id) => FieldName::Ident(*id),
725 record::FieldPathElem::Expr(node) => FieldName::Expr(node.to_mainline(pos_table)),
726 }
727 }
728}
729
730impl<'ast> FromAst<record::FieldDef<'ast>> for (FieldName, term::record::Field) {
731 fn from_ast(field: &record::FieldDef<'ast>, pos_table: &mut PosTable) -> Self {
732 use super::record::FieldPathElem;
738
739 let name_innermost = field.path.last().unwrap().try_as_ident();
741
742 let initial = term::record::Field {
743 value: field.value.as_ref().map(|v| v.to_mainline(pos_table)),
744 metadata: term::record::FieldMetadata::from_ast(&field.metadata, pos_table)
745 .with_field_name(name_innermost)
746 .into(),
747 pending_contracts: Vec::new(),
748 };
749
750 let mut it = field.path.iter();
751 let fst = it.next().unwrap();
752
753 let content = it.rev().fold(initial, |acc, path_elem| {
754 let acc_pos = acc.value.as_ref().map(|value| value.pos(pos_table));
758
759 let pos = acc_pos
760 .map(|p| p.fuse(path_elem.pos()))
761 .unwrap_or_else(|| path_elem.pos());
762
763 match path_elem {
764 FieldPathElem::Ident(id) => {
765 let mut fields = IndexMap::new();
766 fields.insert(*id, acc);
767 let pos_idx = pos_table.push(pos);
768 term::record::Field::from(
769 closurize(NickelValue::record(
771 term::record::RecordData {
772 fields,
773 ..Default::default()
774 },
775 pos_idx,
776 )),
777 )
778 }
779 FieldPathElem::Expr(expr) => {
780 let pos = expr.pos;
781 let expr = NickelValue::from_ast(expr, pos_table);
782 let static_access = expr
783 .as_term()
784 .and_then(term::Term::try_str_chunk_as_static_str);
785
786 if let Some(static_access) = static_access {
787 let id = LocIdent::new_with_pos(static_access, pos);
788 let mut fields = IndexMap::new();
789 fields.insert(id, acc);
790
791 term::record::Field::from(
792 closurize(NickelValue::record(
794 term::record::RecordData {
795 fields,
796 ..Default::default()
797 },
798 pos_table.push(pos),
799 )),
800 )
801 } else {
802 term::record::Field::from(NickelValue::term(
807 term::Term::rec_record(
808 term::record::RecordData::empty(),
809 Vec::new(),
810 vec![(expr, acc)],
811 None,
812 false,
813 ),
814 pos_table.push(pos),
815 ))
816 }
817 }
818 }
819 });
820
821 (fst.to_mainline(pos_table), content)
822 }
823}
824
825impl<'ast> FromAst<record::FieldMetadata<'ast>> for term::record::FieldMetadata {
826 fn from_ast(metadata: &record::FieldMetadata<'ast>, pos_table: &mut PosTable) -> Self {
827 term::record::FieldMetadata {
828 doc: metadata.doc.as_ref().map(|doc| Rc::from(*doc)),
829 annotation: metadata.annotation.to_mainline(pos_table),
830 opt: metadata.opt,
831 not_exported: metadata.not_exported,
832 priority: metadata.priority.clone(),
833 }
834 }
835}
836
837impl<'ast> FromAst<record::Include<'ast>> for term::record::Include {
838 fn from_ast(incl: &record::Include<'ast>, pos_table: &mut PosTable) -> Self {
839 term::record::Include {
840 ident: incl.ident,
841 metadata: incl.metadata.to_mainline(pos_table),
842 }
843 }
844}
845
846impl<'ast> FromAst<Type<'ast>> for mline_type::Type {
847 fn from_ast(typ: &Type<'ast>, pos_table: &mut PosTable) -> Self {
848 mline_type::Type {
849 typ: typ.typ.to_mainline(pos_table),
850 pos: typ.pos,
851 }
852 }
853}
854
855impl<'ast> FromAst<TypeUnr<'ast>> for MainlineTypeUnr {
856 fn from_ast(typ: &TypeUnr<'ast>, pos_table: &mut PosTable) -> Self {
857 typ.clone().map_state(
858 |typ, pos_table| Box::new(typ.to_mainline(pos_table)),
859 |rrows, pos_table| rrows.to_mainline(pos_table),
860 |erows, pos_table| erows.to_mainline(pos_table),
861 |ctr, pos_table| ctr.to_mainline(pos_table),
862 pos_table,
863 )
864 }
865}
866
867impl<'ast> FromAst<RecordRows<'ast>> for mline_type::RecordRows {
868 fn from_ast(rrows: &RecordRows<'ast>, pos_table: &mut PosTable) -> Self {
869 mline_type::RecordRows(rrows.0.to_mainline(pos_table))
870 }
871}
872
873impl<'ast> FromAst<EnumRows<'ast>> for mline_type::EnumRows {
874 fn from_ast(erows: &EnumRows<'ast>, pos_table: &mut PosTable) -> Self {
875 mline_type::EnumRows(erows.0.to_mainline(pos_table))
876 }
877}
878
879impl<'ast> FromAst<EnumRowsUnr<'ast>> for MainlineEnumRowsUnr {
880 fn from_ast(erows: &EnumRowsUnr<'ast>, pos_table: &mut PosTable) -> Self {
881 erows.clone().map_state(
882 |typ, pos_table| Box::new(typ.to_mainline(pos_table)),
883 |erows, pos_table| Box::new(erows.to_mainline(pos_table)),
884 pos_table,
885 )
886 }
887}
888
889impl<'ast> FromAst<EnumRow<'ast>> for mline_type::EnumRow {
890 fn from_ast(erow: &EnumRow<'ast>, pos_table: &mut PosTable) -> Self {
891 mline_type::EnumRow(EnumRowF {
892 id: erow.id,
893 typ: erow
894 .typ
895 .as_ref()
896 .map(|ty| Box::new((*ty).to_mainline(pos_table))),
897 })
898 }
899}
900
901impl<'ast> FromAst<RecordRowsUnr<'ast>> for MainlineRecordRowsUnr {
902 fn from_ast(rrows: &RecordRowsUnr<'ast>, pos_table: &mut PosTable) -> Self {
903 rrows.clone().map_state(
904 |typ, pos_table| Box::new(typ.to_mainline(pos_table)),
905 |rrows, pos_table| Box::new(rrows.to_mainline(pos_table)),
906 pos_table,
907 )
908 }
909}
910
911impl<'ast> FromAst<RecordRow<'ast>> for mline_type::RecordRow {
912 fn from_ast(rrow: &RecordRow<'ast>, pos_table: &mut PosTable) -> Self {
913 mline_type::RecordRow(RecordRowF {
914 id: rrow.id,
915 typ: Box::new(rrow.typ.to_mainline(pos_table)),
916 })
917 }
918}
919
920impl<'ast> FromAst<Type<'ast>> for term::LabeledType {
921 fn from_ast(typ: &Type<'ast>, pos_table: &mut PosTable) -> Self {
922 let typ: mline_type::Type = typ.to_mainline(pos_table);
923 if typ.pos.into_opt().is_none() {
925 panic!("Expected a position to be set for the type {typ:?}");
926 }
927 let span = typ.pos.unwrap();
931
932 term::LabeledType {
933 typ: typ.clone(),
934 label: label::Label {
935 typ: std::rc::Rc::new(typ),
936 span: pos_table.push(span.into()),
937 ..Default::default()
938 },
939 }
940 }
941}
942
943enum TermPrimOp {
946 Unary(term::UnaryOp),
947 Binary(term::BinaryOp),
948 NAry(term::NAryOp),
949}
950
951impl FromAst<PrimOp> for TermPrimOp {
952 fn from_ast(op: &PrimOp, _pos_table: &mut PosTable) -> Self {
953 match op {
954 PrimOp::Typeof => TermPrimOp::Unary(term::UnaryOp::Typeof),
955 PrimOp::Cast => TermPrimOp::Unary(term::UnaryOp::Cast),
956 PrimOp::BoolAnd => TermPrimOp::Unary(term::UnaryOp::BoolAnd),
957 PrimOp::BoolOr => TermPrimOp::Unary(term::UnaryOp::BoolOr),
958 PrimOp::BoolNot => TermPrimOp::Unary(term::UnaryOp::BoolNot),
959 PrimOp::Blame => TermPrimOp::Unary(term::UnaryOp::Blame),
960 PrimOp::EnumEmbed(loc_ident) => TermPrimOp::Unary(term::UnaryOp::EnumEmbed(*loc_ident)),
961 PrimOp::RecordStatAccess(loc_ident) => {
962 TermPrimOp::Unary(term::UnaryOp::RecordAccess(*loc_ident))
963 }
964 PrimOp::ArrayMap => TermPrimOp::Unary(term::UnaryOp::ArrayMap),
965 PrimOp::RecordMap => TermPrimOp::Unary(term::UnaryOp::RecordMap),
966 PrimOp::LabelFlipPol => TermPrimOp::Unary(term::UnaryOp::LabelFlipPol),
967 PrimOp::LabelPol => TermPrimOp::Unary(term::UnaryOp::LabelPol),
968 PrimOp::LabelGoDom => TermPrimOp::Unary(term::UnaryOp::LabelGoDom),
969 PrimOp::LabelGoCodom => TermPrimOp::Unary(term::UnaryOp::LabelGoCodom),
970 PrimOp::LabelGoArray => TermPrimOp::Unary(term::UnaryOp::LabelGoArray),
971 PrimOp::LabelGoDict => TermPrimOp::Unary(term::UnaryOp::LabelGoDict),
972 PrimOp::Seq => TermPrimOp::Unary(term::UnaryOp::Seq),
973 PrimOp::DeepSeq => TermPrimOp::Unary(term::UnaryOp::DeepSeq),
974 PrimOp::ArrayLength => TermPrimOp::Unary(term::UnaryOp::ArrayLength),
975 PrimOp::ArrayGen => TermPrimOp::Unary(term::UnaryOp::ArrayGen),
976 PrimOp::RecordFields(record_op_kind) => {
977 TermPrimOp::Unary(term::UnaryOp::RecordFields(*record_op_kind))
978 }
979 PrimOp::RecordValues => TermPrimOp::Unary(term::UnaryOp::RecordValues),
980 PrimOp::StringTrim => TermPrimOp::Unary(term::UnaryOp::StringTrim),
981 PrimOp::StringChars => TermPrimOp::Unary(term::UnaryOp::StringChars),
982 PrimOp::StringUppercase => TermPrimOp::Unary(term::UnaryOp::StringUppercase),
983 PrimOp::StringLowercase => TermPrimOp::Unary(term::UnaryOp::StringLowercase),
984 PrimOp::StringLength => TermPrimOp::Unary(term::UnaryOp::StringLength),
985 PrimOp::ToString => TermPrimOp::Unary(term::UnaryOp::ToString),
986 PrimOp::NumberFromString => TermPrimOp::Unary(term::UnaryOp::NumberFromString),
987 PrimOp::EnumFromString => TermPrimOp::Unary(term::UnaryOp::EnumFromString),
988 PrimOp::StringIsMatch => TermPrimOp::Unary(term::UnaryOp::StringIsMatch),
989 PrimOp::StringFind => TermPrimOp::Unary(term::UnaryOp::StringFind),
990 PrimOp::StringFindAll => TermPrimOp::Unary(term::UnaryOp::StringFindAll),
991 PrimOp::Force {
992 ignore_not_exported,
993 } => TermPrimOp::Unary(term::UnaryOp::Force {
994 ignore_not_exported: *ignore_not_exported,
995 }),
996 PrimOp::RecordEmptyWithTail => TermPrimOp::Unary(term::UnaryOp::RecordEmptyWithTail),
997 PrimOp::RecordFreeze => TermPrimOp::Unary(term::UnaryOp::RecordFreeze),
998 PrimOp::Trace => TermPrimOp::Unary(term::UnaryOp::Trace),
999 PrimOp::LabelPushDiag => TermPrimOp::Unary(term::UnaryOp::LabelPushDiag),
1000 PrimOp::EnumGetArg => TermPrimOp::Unary(term::UnaryOp::EnumGetArg),
1001 PrimOp::EnumMakeVariant => TermPrimOp::Unary(term::UnaryOp::EnumMakeVariant),
1002 PrimOp::EnumIsVariant => TermPrimOp::Unary(term::UnaryOp::EnumIsVariant),
1003 PrimOp::EnumGetTag => TermPrimOp::Unary(term::UnaryOp::EnumGetTag),
1004 PrimOp::ContractCustom => TermPrimOp::Unary(term::UnaryOp::ContractCustom),
1005 PrimOp::NumberArcCos => TermPrimOp::Unary(term::UnaryOp::NumberArcCos),
1006 PrimOp::NumberArcSin => TermPrimOp::Unary(term::UnaryOp::NumberArcSin),
1007 PrimOp::NumberArcTan => TermPrimOp::Unary(term::UnaryOp::NumberArcTan),
1008 PrimOp::NumberCos => TermPrimOp::Unary(term::UnaryOp::NumberCos),
1009 PrimOp::NumberSin => TermPrimOp::Unary(term::UnaryOp::NumberSin),
1010 PrimOp::NumberTan => TermPrimOp::Unary(term::UnaryOp::NumberTan),
1011 #[cfg(feature = "nix-experimental")]
1012 PrimOp::EvalNix => TermPrimOp::Unary(term::UnaryOp::EvalNix),
1013
1014 PrimOp::Plus => TermPrimOp::Binary(term::BinaryOp::Plus),
1016 PrimOp::Sub => TermPrimOp::Binary(term::BinaryOp::Sub),
1017 PrimOp::Mult => TermPrimOp::Binary(term::BinaryOp::Mult),
1018 PrimOp::Div => TermPrimOp::Binary(term::BinaryOp::Div),
1019 PrimOp::Modulo => TermPrimOp::Binary(term::BinaryOp::Modulo),
1020 PrimOp::NumberArcTan2 => TermPrimOp::Binary(term::BinaryOp::NumberArcTan2),
1021 PrimOp::NumberLog => TermPrimOp::Binary(term::BinaryOp::NumberLog),
1022 PrimOp::Pow => TermPrimOp::Binary(term::BinaryOp::Pow),
1023 PrimOp::StringConcat => TermPrimOp::Binary(term::BinaryOp::StringConcat),
1024 PrimOp::Eq => TermPrimOp::Binary(term::BinaryOp::Eq),
1025 PrimOp::LessThan => TermPrimOp::Binary(term::BinaryOp::LessThan),
1026 PrimOp::LessOrEq => TermPrimOp::Binary(term::BinaryOp::LessOrEq),
1027 PrimOp::GreaterThan => TermPrimOp::Binary(term::BinaryOp::GreaterThan),
1028 PrimOp::GreaterOrEq => TermPrimOp::Binary(term::BinaryOp::GreaterOrEq),
1029 PrimOp::ContractApply => TermPrimOp::Binary(term::BinaryOp::ContractApply),
1030 PrimOp::ContractCheck => TermPrimOp::Binary(term::BinaryOp::ContractCheck),
1031 PrimOp::LabelWithErrorData => TermPrimOp::Binary(term::BinaryOp::LabelWithErrorData),
1032 PrimOp::LabelGoField => TermPrimOp::Binary(term::BinaryOp::LabelGoField),
1033 PrimOp::RecordInsert(record_op_kind) => {
1034 TermPrimOp::Binary(term::BinaryOp::RecordInsert {
1035 metadata: Default::default(),
1036 pending_contracts: Vec::new(),
1037 ext_kind: term::RecordExtKind::WithValue,
1038 op_kind: *record_op_kind,
1039 })
1040 }
1041 PrimOp::RecordRemove(record_op_kind) => {
1042 TermPrimOp::Binary(term::BinaryOp::RecordRemove(*record_op_kind))
1043 }
1044 PrimOp::RecordGet => TermPrimOp::Binary(term::BinaryOp::RecordGet),
1045 PrimOp::RecordHasField(record_op_kind) => {
1046 TermPrimOp::Binary(term::BinaryOp::RecordHasField(*record_op_kind))
1047 }
1048 PrimOp::RecordFieldIsDefined(record_op_kind) => {
1049 TermPrimOp::Binary(term::BinaryOp::RecordFieldIsDefined(*record_op_kind))
1050 }
1051 PrimOp::RecordSplitPair => TermPrimOp::Binary(term::BinaryOp::RecordSplitPair),
1052 PrimOp::RecordDisjointMerge => TermPrimOp::Binary(term::BinaryOp::RecordDisjointMerge),
1053 PrimOp::ArrayConcat => TermPrimOp::Binary(term::BinaryOp::ArrayConcat),
1054 PrimOp::ArrayAt => TermPrimOp::Binary(term::BinaryOp::ArrayAt),
1055 PrimOp::Merge(merge_kind) => {
1056 let dummy_label: label::MergeLabel = label::Label::dummy().into();
1062
1063 TermPrimOp::Binary(term::BinaryOp::Merge(label::MergeLabel {
1064 kind: *merge_kind,
1065 ..dummy_label
1066 }))
1067 }
1068 PrimOp::Hash => TermPrimOp::Binary(term::BinaryOp::Hash),
1069 PrimOp::Serialize => TermPrimOp::Binary(term::BinaryOp::Serialize),
1070 PrimOp::Deserialize => TermPrimOp::Binary(term::BinaryOp::Deserialize),
1071 PrimOp::StringSplit => TermPrimOp::Binary(term::BinaryOp::StringSplit),
1072 PrimOp::StringContains => TermPrimOp::Binary(term::BinaryOp::StringContains),
1073 PrimOp::StringCompare => TermPrimOp::Binary(term::BinaryOp::StringCompare),
1074 PrimOp::Seal => TermPrimOp::Binary(term::BinaryOp::Seal),
1075 PrimOp::Unseal => TermPrimOp::Binary(term::BinaryOp::Unseal),
1076 PrimOp::ContractArrayLazyApp => {
1077 TermPrimOp::Binary(term::BinaryOp::ContractArrayLazyApp)
1078 }
1079 PrimOp::ContractRecordLazyApp => {
1080 TermPrimOp::Binary(term::BinaryOp::ContractRecordLazyApp)
1081 }
1082 PrimOp::LabelWithMessage => TermPrimOp::Binary(term::BinaryOp::LabelWithMessage),
1083 PrimOp::LabelWithNotes => TermPrimOp::Binary(term::BinaryOp::LabelWithNotes),
1084 PrimOp::LabelAppendNote => TermPrimOp::Binary(term::BinaryOp::LabelAppendNote),
1085 PrimOp::LabelLookupTypeVar => TermPrimOp::Binary(term::BinaryOp::LabelLookupTypeVar),
1086
1087 PrimOp::StringReplace => TermPrimOp::NAry(term::NAryOp::StringReplace),
1089 PrimOp::StringReplaceRegex => TermPrimOp::NAry(term::NAryOp::StringReplaceRegex),
1090 PrimOp::StringSubstr => TermPrimOp::NAry(term::NAryOp::StringSubstr),
1091 PrimOp::StringBase64Encode => TermPrimOp::Binary(term::BinaryOp::StringBase64Encode),
1092 PrimOp::StringBase64Decode => TermPrimOp::Binary(term::BinaryOp::StringBase64Decode),
1093 PrimOp::MergeContract => TermPrimOp::NAry(term::NAryOp::MergeContract),
1094 PrimOp::RecordSealTail => TermPrimOp::NAry(term::NAryOp::RecordSealTail),
1095 PrimOp::RecordUnsealTail => TermPrimOp::NAry(term::NAryOp::RecordUnsealTail),
1096 PrimOp::LabelInsertTypeVar => TermPrimOp::NAry(term::NAryOp::LabelInsertTypeVar),
1097 PrimOp::ArraySlice => TermPrimOp::NAry(term::NAryOp::ArraySlice),
1098 }
1099 }
1100}
1101
1102impl<'ast> FromAst<Ast<'ast>> for NickelValue {
1103 fn from_ast(ast: &Ast<'ast>, pos_table: &mut PosTable) -> Self {
1104 use term::Term;
1105
1106 let mut result = match &ast.node {
1107 Node::Null => NickelValue::null().with_pos_idx(pos_table.push(ast.pos)),
1108 Node::Bool(b) => NickelValue::bool_value(*b, pos_table.push(ast.pos)),
1109 Node::Number(n) => NickelValue::number((**n).clone(), pos_table.push(ast.pos)),
1110 Node::String(s) => NickelValue::string(*s, pos_table.push(ast.pos)),
1111 Node::StringChunks(chunks) => {
1112 let chunks = chunks
1113 .iter()
1114 .rev()
1115 .map(|chunk| match chunk {
1116 StringChunk::Literal(s) => term::StrChunk::Literal(s.clone()),
1117 StringChunk::Expr(expr, indent) => {
1118 term::StrChunk::Expr(expr.to_mainline(pos_table), *indent)
1119 }
1120 })
1121 .collect();
1122
1123 NickelValue::term(Term::StrChunks(chunks), pos_table.push(ast.pos))
1124 }
1125 Node::Fun { args, body } => {
1126 let body_pos = body.pos;
1127
1128 args.iter()
1131 .rev()
1132 .fold(NickelValue::from_ast(body, pos_table), |acc, arg| {
1133 let term = match arg.data {
1134 PatternData::Any(id) => Term::fun(id, acc),
1135 _ => term::pattern::compile::compile_fun_pattern(pos_table, arg, acc),
1136 };
1137
1138 NickelValue::term(term, pos_table.push(arg.pos.fuse(body_pos)))
1146 })
1147 .with_pos_idx(pos_table.push(ast.pos))
1148 }
1149 Node::Let {
1150 bindings,
1151 body,
1152 rec,
1153 } => {
1154 fn with_metadata(
1158 pos_table: &mut PosTable,
1159 metadata: &LetMetadata<'_>,
1160 value: &Ast<'_>,
1161 ) -> NickelValue {
1162 let value: NickelValue = value.to_mainline(pos_table);
1163 let pos = value.pos(pos_table);
1164
1165 if metadata.annotation.is_empty() {
1166 return value;
1167 }
1168
1169 NickelValue::term(
1170 term::Term::annotated(metadata.annotation.to_mainline(pos_table), value),
1171 pos_table.push(pos),
1172 )
1173 }
1174
1175 let try_bindings = bindings
1178 .iter()
1179 .map(
1180 |LetBinding {
1181 pattern,
1182 metadata,
1183 value,
1184 }| match pattern.data {
1185 PatternData::Any(id) => {
1186 Some((id, with_metadata(pos_table, metadata, value)))
1187 }
1188 _ => None,
1189 },
1190 )
1191 .collect::<Option<SmallVec<_>>>();
1192
1193 let body = body.to_mainline(pos_table);
1194 let attrs = term::LetAttrs {
1195 rec: *rec,
1196 ..Default::default()
1197 };
1198
1199 let term = if let Some(bindings) = try_bindings {
1200 Term::let_in(bindings, body, attrs)
1201 } else {
1202 let bindings = bindings
1203 .iter()
1204 .map(
1205 |LetBinding {
1206 pattern,
1207 value,
1208 metadata,
1209 }| {
1210 (pattern, with_metadata(pos_table, metadata, value))
1211 },
1212 )
1213 .collect::<Vec<_>>();
1214
1215 term::pattern::compile::compile_let_pattern(pos_table, &bindings, body, attrs)
1216 };
1217
1218 NickelValue::term(term, pos_table.push(ast.pos))
1219 }
1220 Node::App { head, args } => {
1221 let head_pos = head.pos;
1222
1223 args.iter()
1226 .fold(head.to_mainline(pos_table), |result, arg| {
1227 let arg_pos = arg.pos;
1229 NickelValue::term(
1230 Term::app(result, arg.to_mainline(pos_table)),
1231 pos_table.push(head_pos.fuse(arg_pos)),
1232 )
1233 })
1234 }
1235 Node::Var(loc_ident) => {
1236 NickelValue::term(Term::Var(*loc_ident), pos_table.push(ast.pos))
1237 }
1238 Node::EnumVariant { tag, arg: None } => {
1239 NickelValue::enum_tag(*tag, pos_table.push(ast.pos))
1240 }
1241 Node::EnumVariant {
1242 tag,
1243 arg: Some(arg),
1244 } => closurize(NickelValue::enum_variant(
1245 *tag,
1246 Some(arg.to_mainline(pos_table)),
1247 pos_table.push(ast.pos),
1248 )),
1249 Node::Record(record) => {
1250 let (data, dyn_fields) = (*record).to_mainline(pos_table);
1251 NickelValue::term(
1252 Term::rec_record(
1253 data,
1254 record
1255 .includes
1256 .iter()
1257 .map(|incl| incl.to_mainline(pos_table))
1258 .collect(),
1259 dyn_fields,
1260 None,
1261 false,
1262 ),
1263 pos_table.push(ast.pos),
1264 )
1265 }
1266 Node::IfThenElse {
1267 cond,
1268 then_branch,
1269 else_branch,
1270 } => term::make::if_then_else(
1271 NickelValue::from_ast(cond, pos_table),
1272 NickelValue::from_ast(then_branch, pos_table),
1273 NickelValue::from_ast(else_branch, pos_table),
1274 ),
1275 Node::Match(data) => {
1276 let branches = data
1277 .branches
1278 .iter()
1279 .map(|branch| term::pattern::compile::MatchBranch {
1280 pattern: branch.pattern.clone(),
1281 guard: branch.guard.clone().map(|g| g.to_mainline(pos_table)),
1282 body: branch.body.to_mainline(pos_table),
1283 })
1284 .collect();
1285 let match_data = term::pattern::compile::MatchData { branches };
1286 let arg = LocIdent::fresh();
1287 let pos_idx = pos_table.push(ast.pos);
1288 NickelValue::term(
1289 Term::Fun(term::FunData {
1290 arg,
1291 body: match_data.compile(pos_table, Term::Var(arg).into(), pos_idx),
1292 }),
1293 pos_idx,
1294 )
1295 }
1296 Node::Array(array) => {
1297 let pos_idx = pos_table.push(ast.pos);
1298 let array = array.iter().map(|elt| elt.to_mainline(pos_table)).collect();
1299
1300 closurize(NickelValue::array(array, Vec::new(), pos_idx))
1305 }
1306 Node::PrimOpApp { op, args } => {
1307 let term = match (*op).to_mainline(pos_table) {
1308 TermPrimOp::Unary(op) => Term::op1(op, args[0].to_mainline(pos_table)),
1309 TermPrimOp::Binary(op) => Term::op2(
1314 op,
1315 args[0].to_mainline(pos_table),
1316 args[1].to_mainline(pos_table),
1317 ),
1318 TermPrimOp::NAry(op) => Term::opn(
1319 op,
1320 args.iter()
1321 .map(|arg| (*arg).to_mainline(pos_table))
1322 .collect(),
1323 ),
1324 };
1325
1326 NickelValue::term(term, pos_table.push(ast.pos))
1327 }
1328 Node::Annotated { annot, inner } => NickelValue::term(
1329 Term::annotated(
1330 (*annot).to_mainline(pos_table),
1331 inner.to_mainline(pos_table),
1332 ),
1333 pos_table.push(ast.pos),
1334 ),
1335 Node::Import(Import::Path { path, format }) => NickelValue::term(
1336 Term::Import(term::Import::Path {
1337 path: (*path).to_owned(),
1338 format: *format,
1339 }),
1340 pos_table.push(ast.pos),
1341 ),
1342 Node::Import(Import::Package { id }) => NickelValue::term(
1343 Term::Import(term::Import::Package { id: *id }),
1344 pos_table.push(ast.pos),
1345 ),
1346 Node::Type(typ) => {
1347 let typ: mline_type::Type = (*typ).to_mainline(pos_table);
1348
1349 let contract = typ
1350 .contract(pos_table)
1351 .unwrap_or_else(|err| {
1356 Term::parse_error(ParseError::UnboundTypeVariables(vec![err.0])).into()
1357 });
1358
1359 NickelValue::typ(typ, contract, pos_table.push(ast.pos))
1360 }
1361 Node::ParseError(error) => {
1362 NickelValue::term(Term::parse_error((*error).clone()), pos_table.push(ast.pos))
1363 }
1364 };
1365
1366 if let ValueContentRefMut::Term(term::Term::Op2(data)) = result.content_make_mut()
1368 && let term::BinaryOp::Merge(label) = &mut data.op
1369 {
1370 label.span = pos_table.push(ast.pos);
1371 }
1372
1373 result
1374 }
1375}
1376
1377impl<'ast> FromAst<&'ast Ast<'ast>> for NickelValue {
1378 fn from_ast(ast: &&'ast Ast<'ast>, pos_table: &mut PosTable) -> Self {
1379 FromAst::from_ast(*ast, pos_table)
1380 }
1381}
1382
1383impl<'ast> FromAst<Record<'ast>>
1387 for (
1388 term::record::RecordData,
1389 Vec<(NickelValue, term::record::Field)>,
1390 )
1391{
1392 fn from_ast(record: &Record<'ast>, pos_table: &mut PosTable) -> Self {
1393 use indexmap::map::Entry;
1394
1395 fn insert_static_field(
1396 pos_table: &mut PosTable,
1397 static_fields: &mut IndexMap<LocIdent, term::record::Field>,
1398 id: LocIdent,
1399 field: term::record::Field,
1400 ) {
1401 match static_fields.entry(id) {
1402 Entry::Occupied(mut occpd) => {
1403 let prev = occpd.insert(term::record::Field::default());
1405
1406 occpd.insert(merge_fields(pos_table.push(id.pos), prev, field));
1408 }
1409 Entry::Vacant(vac) => {
1410 vac.insert(field);
1411 }
1412 }
1413 }
1414
1415 let mut static_fields = IndexMap::new();
1416 let mut dynamic_fields = Vec::new();
1417
1418 for def in record.field_defs {
1419 match def.to_mainline(pos_table) {
1420 (FieldName::Ident(id), field) => {
1421 insert_static_field(pos_table, &mut static_fields, id, field)
1422 }
1423 (FieldName::Expr(expr), field) => {
1424 let pos = expr.pos(pos_table);
1425 let static_access = expr
1437 .as_term()
1438 .and_then(term::Term::try_str_chunk_as_static_str);
1439
1440 if let Some(static_access) = static_access {
1441 insert_static_field(
1442 pos_table,
1443 &mut static_fields,
1444 LocIdent::new_with_pos(static_access, pos),
1445 field,
1446 )
1447 } else {
1448 dynamic_fields.push((expr, field));
1449 }
1450 }
1451 }
1452 }
1453
1454 (
1455 term::record::RecordData::new(
1456 static_fields,
1457 term::record::RecordAttrs {
1458 open: record.open,
1459 ..Default::default()
1460 },
1461 None,
1462 ),
1463 dynamic_fields,
1464 )
1465 }
1466}
1467
1468fn merge_fields(
1478 id_span: PosIdx,
1479 field1: term::record::Field,
1480 field2: term::record::Field,
1481) -> term::record::Field {
1482 use crate::eval::{
1483 merge::{merge_doc, split},
1484 value::ValueContent,
1485 };
1486 use term::{BinaryOp, make as mk_term, record::RecordData};
1487
1488 fn merge_values(id_span: PosIdx, v1: NickelValue, v2: NickelValue) -> NickelValue {
1491 match (v1.content(), v2.content()) {
1492 (ValueContent::Record(lens1), ValueContent::Record(lens2)) => {
1493 let rd1 = lens1.take().unwrap_or_alloc();
1494 let rd2 = lens2.take().unwrap_or_alloc();
1495
1496 let split::SplitResult {
1497 left,
1498 center,
1499 right,
1500 } = split::split(rd1.fields, rd2.fields);
1501 let mut fields = IndexMap::with_capacity(left.len() + center.len() + right.len());
1502 fields.extend(left);
1503 fields.extend(right);
1504 for (id, (field1, field2)) in center.into_iter() {
1505 fields.insert(id, merge_fields(id_span, field1, field2));
1506 }
1507
1508 closurize(NickelValue::record_posless(RecordData::new(
1510 fields,
1511 Combine::combine(rd1.attrs, rd2.attrs),
1512 None,
1513 )))
1514 }
1515 (lens1, lens2) => mk_term::op2(
1516 BinaryOp::Merge(label::MergeLabel {
1517 span: id_span,
1518 kind: ast::MergeKind::PiecewiseDef,
1519 }),
1520 lens1.restore(),
1521 lens2.restore(),
1522 ),
1523 }
1524 }
1525
1526 let metadata1 = field1.metadata.into_inner();
1527 let metadata2 = field2.metadata.into_inner();
1528
1529 let (value, priority) = match (field1.value, field2.value) {
1530 (Some(t1), Some(t2)) if metadata1.priority == metadata2.priority => {
1531 (Some(merge_values(id_span, t1, t2)), metadata1.priority)
1532 }
1533 (Some(t), _) if metadata1.priority > metadata2.priority => (Some(t), metadata1.priority),
1534 (_, Some(t)) if metadata1.priority < metadata2.priority => (Some(t), metadata2.priority),
1535 (Some(t), None) => (Some(t), metadata1.priority),
1536 (None, Some(t)) => (Some(t), metadata2.priority),
1537 (None, None) => (None, Default::default()),
1538 _ => unreachable!(),
1539 };
1540
1541 debug_assert!(field1.pending_contracts.is_empty() && field2.pending_contracts.is_empty());
1543
1544 term::record::Field {
1545 value,
1546 metadata: term::record::FieldMetadata {
1549 doc: merge_doc(metadata1.doc, metadata2.doc),
1550 annotation: Combine::combine(metadata1.annotation, metadata2.annotation),
1551 opt: metadata1.opt && metadata2.opt,
1552 not_exported: metadata1.not_exported || metadata2.not_exported,
1553 priority,
1554 }
1555 .into(),
1556 pending_contracts: Vec::new(),
1557 }
1558}
1559
1560#[inline]
1562pub(crate) fn closurize(value: NickelValue) -> NickelValue {
1563 let pos_idx = value.pos_idx();
1564 NickelValue::term(term::Term::Closurize(value), pos_idx)
1565}