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