Skip to main content

aiken_lang/
expr.rs

1pub(crate) use crate::{
2    ast::{
3        self, Annotation, ArgBy, ArgName, AssignmentKind, AssignmentPattern, BinOp, Bls12_381Point,
4        ByteArrayFormatPreference, CallArg, Curve, DataType, DataTypeKey, DefinitionLocation,
5        Located, LogicalOpChainKind, ParsedCallArg, RecordConstructorArg, RecordUpdateSpread, Span,
6        TraceKind, TypedArg, TypedAssignmentKind, TypedClause, TypedDataType, TypedIfBranch,
7        TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg, UntypedAssignmentKind, UntypedClause,
8        UntypedIfBranch, UntypedRecordUpdateArg,
9    },
10    parser::token::Base,
11    tipo::{
12        ModuleValueConstructor, Type, TypeVar, ValueConstructor, ValueConstructorVariant,
13        check_replaceable_opaque_type, convert_opaque_type, lookup_data_type_by_tipo,
14    },
15};
16use indexmap::IndexMap;
17use pallas_primitives::alonzo::{Constr, PlutusData};
18use std::{fmt::Debug, ops::Deref, rc::Rc};
19use uplc::{
20    KeyValuePairs,
21    ast::Data,
22    machine::{runtime::convert_tag_to_constr, value::from_pallas_bigint},
23};
24use vec1::Vec1;
25
26#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
27pub enum TypedExpr {
28    UInt {
29        location: Span,
30        tipo: Rc<Type>,
31        value: String,
32        base: Base,
33    },
34
35    String {
36        location: Span,
37        tipo: Rc<Type>,
38        value: String,
39    },
40
41    ByteArray {
42        location: Span,
43        tipo: Rc<Type>,
44        bytes: Vec<u8>,
45        preferred_format: ByteArrayFormatPreference,
46    },
47
48    CurvePoint {
49        location: Span,
50        tipo: Rc<Type>,
51        point: Box<Curve>,
52        preferred_format: ByteArrayFormatPreference,
53    },
54
55    Sequence {
56        location: Span,
57        expressions: Vec<Self>,
58    },
59
60    /// A chain of pipe expressions.
61    /// By this point the type checker has expanded it into a series of
62    /// assignments and function calls, but we still have a Pipeline AST node as
63    /// even though it is identical to `Sequence` we want to use different
64    /// locations when showing it in error messages, etc.
65    Pipeline {
66        location: Span,
67        expressions: Vec<Self>,
68    },
69
70    Var {
71        location: Span,
72        constructor: ValueConstructor,
73        name: String,
74    },
75
76    Fn {
77        location: Span,
78        tipo: Rc<Type>,
79        is_capture: bool,
80        args: Vec<TypedArg>,
81        body: Box<Self>,
82        return_annotation: Option<Annotation>,
83    },
84
85    List {
86        location: Span,
87        tipo: Rc<Type>,
88        elements: Vec<Self>,
89        tail: Option<Box<Self>>,
90    },
91
92    Call {
93        location: Span,
94        tipo: Rc<Type>,
95        fun: Box<Self>,
96        args: Vec<CallArg<Self>>,
97    },
98
99    BinOp {
100        location: Span,
101        tipo: Rc<Type>,
102        name: BinOp,
103        left: Box<Self>,
104        right: Box<Self>,
105    },
106
107    Assignment {
108        location: Span,
109        tipo: Rc<Type>,
110        value: Box<Self>,
111        pattern: TypedPattern,
112        kind: TypedAssignmentKind,
113        comment: Option<String>,
114    },
115
116    Trace {
117        location: Span,
118        tipo: Rc<Type>,
119        then: Box<Self>,
120        text: Box<Self>,
121    },
122
123    When {
124        location: Span,
125        tipo: Rc<Type>,
126        subject: Box<Self>,
127        clauses: Vec<TypedClause>,
128    },
129
130    If {
131        location: Span,
132        #[serde(with = "Vec1Ref")]
133        branches: Vec1<TypedIfBranch>,
134        final_else: Box<Self>,
135        tipo: Rc<Type>,
136    },
137
138    RecordAccess {
139        location: Span,
140        tipo: Rc<Type>,
141        label: String,
142        index: u64,
143        record: Box<Self>,
144    },
145
146    ModuleSelect {
147        location: Span,
148        tipo: Rc<Type>,
149        label: String,
150        module_name: String,
151        module_alias: String,
152        constructor: ModuleValueConstructor,
153    },
154
155    Tuple {
156        location: Span,
157        tipo: Rc<Type>,
158        elems: Vec<Self>,
159    },
160
161    Pair {
162        location: Span,
163        tipo: Rc<Type>,
164        fst: Box<Self>,
165        snd: Box<Self>,
166    },
167
168    TupleIndex {
169        location: Span,
170        tipo: Rc<Type>,
171        index: usize,
172        tuple: Box<Self>,
173    },
174
175    ErrorTerm {
176        location: Span,
177        tipo: Rc<Type>,
178    },
179
180    RecordUpdate {
181        location: Span,
182        tipo: Rc<Type>,
183        spread: Box<Self>,
184        args: Vec<TypedRecordUpdateArg>,
185    },
186
187    UnOp {
188        location: Span,
189        value: Box<Self>,
190        tipo: Rc<Type>,
191        op: UnOp,
192    },
193}
194
195#[derive(serde::Serialize, serde::Deserialize)]
196#[serde(remote = "Vec1")]
197struct Vec1Ref<T>(#[serde(getter = "Vec1::as_vec")] Vec<T>);
198
199impl<T> From<Vec1Ref<T>> for Vec1<T> {
200    fn from(v: Vec1Ref<T>) -> Self {
201        Vec1::try_from_vec(v.0).unwrap()
202    }
203}
204
205impl TypedExpr {
206    pub fn is_simple_expr_to_format(&self) -> bool {
207        match self {
208            Self::String { .. } | Self::UInt { .. } | Self::ByteArray { .. } | Self::Var { .. } => {
209                true
210            }
211            Self::Pair { fst, snd, .. } => {
212                fst.is_simple_expr_to_format() && snd.is_simple_expr_to_format()
213            }
214            Self::Tuple { elems, .. } => elems.iter().all(|e| e.is_simple_expr_to_format()),
215            Self::List { elements, .. } if elements.len() <= 3 => {
216                elements.iter().all(|e| e.is_simple_expr_to_format())
217            }
218            Self::UnOp { value, .. } => value.is_simple_expr_to_format(),
219            _ => false,
220        }
221    }
222
223    pub fn and_then(self, next: Self) -> Self {
224        if let TypedExpr::Trace {
225            tipo,
226            location,
227            then,
228            text,
229        } = self
230        {
231            return TypedExpr::Trace {
232                tipo,
233                location,
234                then: Box::new(then.and_then(next)),
235                text,
236            };
237        }
238
239        TypedExpr::Sequence {
240            location: self.location(),
241            expressions: vec![self, next],
242        }
243    }
244
245    pub fn sequence(exprs: &[TypedExpr]) -> Self {
246        TypedExpr::Sequence {
247            location: Span::empty(),
248            expressions: exprs.to_vec(),
249        }
250    }
251
252    pub fn let_(value: Self, pattern: TypedPattern, tipo: Rc<Type>, location: Span) -> Self {
253        TypedExpr::Assignment {
254            tipo: tipo.clone(),
255            value: value.into(),
256            pattern,
257            kind: AssignmentKind::let_(),
258            comment: None,
259            location,
260        }
261    }
262
263    // Create an expect assignment, unless the target type is `Data`; then fallback to a let.
264    pub fn flexible_expect(
265        value: Self,
266        pattern: TypedPattern,
267        tipo: Rc<Type>,
268        location: Span,
269    ) -> Self {
270        TypedExpr::Assignment {
271            tipo: tipo.clone(),
272            value: value.into(),
273            pattern,
274            kind: if tipo.is_data() {
275                AssignmentKind::let_()
276            } else {
277                AssignmentKind::expect()
278            },
279            comment: None,
280            location,
281        }
282    }
283
284    pub fn local_var(name: &str, tipo: Rc<Type>, location: Span) -> Self {
285        TypedExpr::Var {
286            constructor: ValueConstructor {
287                public: true,
288                variant: ValueConstructorVariant::LocalVariable {
289                    location: Span::empty(),
290                },
291                tipo: tipo.clone(),
292            },
293            name: name.to_string(),
294            location,
295        }
296    }
297
298    pub fn tipo(&self) -> Rc<Type> {
299        match self {
300            Self::Var { constructor, .. } => constructor.tipo.clone(),
301            Self::Trace { then, .. } => then.tipo(),
302            Self::Fn { tipo, .. }
303            | Self::UInt { tipo, .. }
304            | Self::ErrorTerm { tipo, .. }
305            | Self::When { tipo, .. }
306            | Self::List { tipo, .. }
307            | Self::Call { tipo, .. }
308            | Self::If { tipo, .. }
309            | Self::UnOp { tipo, .. }
310            | Self::BinOp { tipo, .. }
311            | Self::Tuple { tipo, .. }
312            | Self::Pair { tipo, .. }
313            | Self::String { tipo, .. }
314            | Self::ByteArray { tipo, .. }
315            | Self::TupleIndex { tipo, .. }
316            | Self::Assignment { tipo, .. }
317            | Self::ModuleSelect { tipo, .. }
318            | Self::RecordAccess { tipo, .. }
319            | Self::RecordUpdate { tipo, .. }
320            | Self::CurvePoint { tipo, .. } => tipo.clone(),
321            Self::Pipeline { expressions, .. } | Self::Sequence { expressions, .. } => expressions
322                .last()
323                .map(TypedExpr::tipo)
324                .unwrap_or_else(Type::void),
325        }
326    }
327
328    pub fn replace_type(&mut self, new_type: Rc<Type>) {
329        match self {
330            Self::Var { constructor, .. } => {
331                constructor.tipo = new_type;
332            }
333            Self::Trace { then, .. } => then.replace_type(new_type),
334            Self::Fn { tipo, .. }
335            | Self::UInt { tipo, .. }
336            | Self::ErrorTerm { tipo, .. }
337            | Self::When { tipo, .. }
338            | Self::List { tipo, .. }
339            | Self::Call { tipo, .. }
340            | Self::If { tipo, .. }
341            | Self::UnOp { tipo, .. }
342            | Self::BinOp { tipo, .. }
343            | Self::Tuple { tipo, .. }
344            | Self::Pair { tipo, .. }
345            | Self::String { tipo, .. }
346            | Self::ByteArray { tipo, .. }
347            | Self::TupleIndex { tipo, .. }
348            | Self::Assignment { tipo, .. }
349            | Self::ModuleSelect { tipo, .. }
350            | Self::RecordAccess { tipo, .. }
351            | Self::RecordUpdate { tipo, .. }
352            | Self::CurvePoint { tipo, .. } => *tipo = new_type,
353            Self::Pipeline { expressions, .. } | Self::Sequence { expressions, .. } => {
354                expressions
355                    .last_mut()
356                    .expect("trying to replace type of an empty sequence or pipeline")
357                    .replace_type(new_type);
358            }
359        }
360    }
361
362    pub fn is_literal(&self) -> bool {
363        matches!(
364            self,
365            Self::UInt { .. }
366                | Self::List { .. }
367                | Self::Tuple { .. }
368                | Self::String { .. }
369                | Self::ByteArray { .. }
370        )
371    }
372
373    pub fn is_error_term(&self) -> bool {
374        matches!(self, Self::ErrorTerm { .. })
375    }
376
377    /// Returns `true` if the typed expr is [`Assignment`].
378    pub fn is_assignment(&self) -> bool {
379        matches!(self, Self::Assignment { .. })
380    }
381
382    pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
383        match self {
384            TypedExpr::Fn { .. }
385            | TypedExpr::UInt { .. }
386            | TypedExpr::Trace { .. }
387            | TypedExpr::List { .. }
388            | TypedExpr::Call { .. }
389            | TypedExpr::When { .. }
390            | TypedExpr::ErrorTerm { .. }
391            | TypedExpr::BinOp { .. }
392            | TypedExpr::Tuple { .. }
393            | TypedExpr::Pair { .. }
394            | TypedExpr::UnOp { .. }
395            | TypedExpr::String { .. }
396            | TypedExpr::Sequence { .. }
397            | TypedExpr::Pipeline { .. }
398            | TypedExpr::ByteArray { .. }
399            | TypedExpr::Assignment { .. }
400            | TypedExpr::TupleIndex { .. }
401            | TypedExpr::RecordAccess { .. }
402            | TypedExpr::CurvePoint { .. } => None,
403            TypedExpr::If { .. } => None,
404
405            // TODO: test
406            // TODO: definition
407            TypedExpr::RecordUpdate { .. } => None,
408
409            // TODO: test
410            TypedExpr::ModuleSelect {
411                module_name,
412                constructor,
413                ..
414            } => Some(DefinitionLocation {
415                module: Some(module_name.as_str()),
416                span: constructor.location(),
417            }),
418
419            // TODO: test
420            TypedExpr::Var { constructor, .. } => Some(constructor.definition_location()),
421        }
422    }
423
424    pub fn type_defining_location(&self) -> Span {
425        match self {
426            Self::Fn { location, .. }
427            | Self::UInt { location, .. }
428            | Self::Var { location, .. }
429            | Self::Trace { location, .. }
430            | Self::ErrorTerm { location, .. }
431            | Self::When { location, .. }
432            | Self::Call { location, .. }
433            | Self::List { location, .. }
434            | Self::BinOp { location, .. }
435            | Self::Tuple { location, .. }
436            | Self::Pair { location, .. }
437            | Self::String { location, .. }
438            | Self::UnOp { location, .. }
439            | Self::Pipeline { location, .. }
440            | Self::ByteArray { location, .. }
441            | Self::Assignment { location, .. }
442            | Self::TupleIndex { location, .. }
443            | Self::ModuleSelect { location, .. }
444            | Self::RecordAccess { location, .. }
445            | Self::RecordUpdate { location, .. }
446            | Self::CurvePoint { location, .. } => *location,
447
448            Self::If { branches, .. } => branches.first().body.type_defining_location(),
449
450            Self::Sequence {
451                expressions,
452                location,
453                ..
454            } => expressions
455                .last()
456                .map(TypedExpr::location)
457                .unwrap_or(*location),
458        }
459    }
460
461    pub fn location(&self) -> Span {
462        match self {
463            Self::Fn { location, .. }
464            | Self::UInt { location, .. }
465            | Self::Trace { location, .. }
466            | Self::Var { location, .. }
467            | Self::ErrorTerm { location, .. }
468            | Self::When { location, .. }
469            | Self::Call { location, .. }
470            | Self::If { location, .. }
471            | Self::List { location, .. }
472            | Self::BinOp { location, .. }
473            | Self::Tuple { location, .. }
474            | Self::Pair { location, .. }
475            | Self::String { location, .. }
476            | Self::UnOp { location, .. }
477            | Self::Sequence { location, .. }
478            | Self::Pipeline { location, .. }
479            | Self::ByteArray { location, .. }
480            | Self::Assignment { location, .. }
481            | Self::TupleIndex { location, .. }
482            | Self::ModuleSelect { location, .. }
483            | Self::RecordAccess { location, .. }
484            | Self::RecordUpdate { location, .. }
485            | Self::CurvePoint { location, .. } => *location,
486        }
487    }
488
489    // This could be optimised in places to exit early if the first of a series
490    // of expressions is after the byte index.
491    pub fn find_node(&self, byte_index: usize) -> Option<Located<'_>> {
492        if !self.location().contains(byte_index) {
493            return None;
494        }
495
496        match self {
497            TypedExpr::ErrorTerm { .. }
498            | TypedExpr::Var { .. }
499            | TypedExpr::UInt { .. }
500            | TypedExpr::String { .. }
501            | TypedExpr::ByteArray { .. }
502            | TypedExpr::ModuleSelect { .. }
503            | TypedExpr::CurvePoint { .. } => Some(Located::Expression(self)),
504
505            TypedExpr::Trace { text, then, .. } => text
506                .find_node(byte_index)
507                .or_else(|| then.find_node(byte_index))
508                .or(Some(Located::Expression(self))),
509
510            TypedExpr::Pipeline { expressions, .. } | TypedExpr::Sequence { expressions, .. } => {
511                expressions.iter().find_map(|e| e.find_node(byte_index))
512            }
513
514            TypedExpr::Fn {
515                body,
516                args,
517                return_annotation,
518                ..
519            } => args
520                .iter()
521                .find_map(|arg| arg.find_node(byte_index))
522                .or_else(|| body.find_node(byte_index))
523                .or_else(|| {
524                    return_annotation
525                        .as_ref()
526                        .and_then(|a| a.find_node(byte_index))
527                })
528                .or(Some(Located::Expression(self))),
529
530            TypedExpr::Tuple {
531                elems: elements, ..
532            } => elements
533                .iter()
534                .find_map(|e| e.find_node(byte_index))
535                .or(Some(Located::Expression(self))),
536
537            TypedExpr::Pair { fst, snd, .. } => [fst, snd]
538                .iter()
539                .find_map(|e| e.find_node(byte_index))
540                .or(Some(Located::Expression(self))),
541
542            TypedExpr::List { elements, tail, .. } => elements
543                .iter()
544                .find_map(|e| e.find_node(byte_index))
545                .or_else(|| tail.as_ref().and_then(|t| t.find_node(byte_index)))
546                .or(Some(Located::Expression(self))),
547
548            TypedExpr::Call { fun, args, .. } => args
549                .iter()
550                .find_map(|arg| arg.find_node(byte_index))
551                .or_else(|| fun.find_node(byte_index))
552                .or(Some(Located::Expression(self))),
553
554            TypedExpr::BinOp { left, right, .. } => left
555                .find_node(byte_index)
556                .or_else(|| right.find_node(byte_index))
557                .or(Some(Located::Expression(self))),
558
559            TypedExpr::Assignment { value, pattern, .. } => pattern
560                .find_node(byte_index, &value.tipo())
561                .or_else(|| value.find_node(byte_index)),
562
563            TypedExpr::When {
564                subject, clauses, ..
565            } => subject
566                .find_node(byte_index)
567                .or_else(|| {
568                    clauses
569                        .iter()
570                        .find_map(|clause| clause.find_node(byte_index, &subject.tipo()))
571                })
572                .or(Some(Located::Expression(self))),
573
574            TypedExpr::RecordAccess {
575                record: expression, ..
576            }
577            | TypedExpr::TupleIndex {
578                tuple: expression, ..
579            } => expression
580                .find_node(byte_index)
581                .or(Some(Located::Expression(self))),
582
583            TypedExpr::RecordUpdate { spread, args, .. } => args
584                .iter()
585                .find_map(|arg| arg.find_node(byte_index))
586                .or_else(|| spread.find_node(byte_index))
587                .or(Some(Located::Expression(self))),
588
589            TypedExpr::If {
590                branches,
591                final_else,
592                ..
593            } => branches
594                .iter()
595                .find_map(|branch| {
596                    branch
597                        .condition
598                        .find_node(byte_index)
599                        .or_else(|| branch.body.find_node(byte_index))
600                })
601                .or_else(|| final_else.find_node(byte_index))
602                .or(Some(Located::Expression(self))),
603
604            TypedExpr::UnOp { value, .. } => value
605                .find_node(byte_index)
606                .or(Some(Located::Expression(self))),
607        }
608    }
609
610    pub fn void(location: Span) -> Self {
611        TypedExpr::Var {
612            name: "Void".to_string(),
613            constructor: ValueConstructor {
614                public: true,
615                variant: ValueConstructorVariant::Record {
616                    name: "Void".to_string(),
617                    arity: 0,
618                    field_map: None,
619                    location: Span::empty(),
620                    module: String::new(),
621                    constructors_count: 1,
622                },
623                tipo: Type::void(),
624            },
625            location,
626        }
627    }
628}
629
630// Represent how a function was written so that we can format it back.
631#[derive(Debug, Clone, PartialEq, Copy)]
632pub enum FnStyle {
633    Plain,
634    Capture,
635    BinOp(BinOp),
636}
637
638#[derive(Debug, Clone, PartialEq)]
639pub enum UntypedExpr {
640    UInt {
641        location: Span,
642        value: String,
643        base: Base,
644    },
645
646    String {
647        location: Span,
648        value: String,
649    },
650
651    Sequence {
652        location: Span,
653        expressions: Vec<Self>,
654    },
655
656    Var {
657        location: Span,
658        name: String,
659    },
660
661    Fn {
662        location: Span,
663        fn_style: FnStyle,
664        arguments: Vec<UntypedArg>,
665        body: Box<Self>,
666        return_annotation: Option<Annotation>,
667    },
668
669    List {
670        location: Span,
671        elements: Vec<Self>,
672        tail: Option<Box<Self>>,
673    },
674
675    Call {
676        arguments: Vec<CallArg<Self>>,
677        fun: Box<Self>,
678        location: Span,
679    },
680
681    BinOp {
682        location: Span,
683        name: BinOp,
684        left: Box<Self>,
685        right: Box<Self>,
686    },
687
688    ByteArray {
689        location: Span,
690        bytes: Vec<(u8, Span)>,
691        preferred_format: ByteArrayFormatPreference,
692    },
693
694    CurvePoint {
695        location: Span,
696        point: Box<Curve>,
697        preferred_format: ByteArrayFormatPreference,
698    },
699
700    PipeLine {
701        expressions: Vec1<Self>,
702        one_liner: bool,
703    },
704
705    Assignment {
706        location: Span,
707        value: Box<Self>,
708        patterns: Vec1<AssignmentPattern>,
709        kind: UntypedAssignmentKind,
710        comment: Option<String>,
711    },
712
713    Trace {
714        kind: TraceKind,
715        location: Span,
716        then: Box<Self>,
717        label: Box<Self>,
718        arguments: Vec<Self>,
719    },
720
721    TraceIfFalse {
722        location: Span,
723        value: Box<Self>,
724    },
725
726    When {
727        location: Span,
728        subject: Box<Self>,
729        clauses: Vec<UntypedClause>,
730    },
731
732    If {
733        location: Span,
734        branches: Vec1<UntypedIfBranch>,
735        final_else: Box<Self>,
736    },
737
738    FieldAccess {
739        location: Span,
740        label: String,
741        container: Box<Self>,
742    },
743
744    Tuple {
745        location: Span,
746        elems: Vec<Self>,
747    },
748
749    Pair {
750        location: Span,
751        fst: Box<Self>,
752        snd: Box<Self>,
753    },
754
755    TupleIndex {
756        location: Span,
757        index: usize,
758        tuple: Box<Self>,
759    },
760
761    ErrorTerm {
762        location: Span,
763    },
764
765    RecordUpdate {
766        location: Span,
767        constructor: Box<Self>,
768        spread: RecordUpdateSpread,
769        arguments: Vec<UntypedRecordUpdateArg>,
770    },
771
772    UnOp {
773        op: UnOp,
774        location: Span,
775        value: Box<Self>,
776    },
777
778    LogicalOpChain {
779        kind: LogicalOpChainKind,
780        expressions: Vec<Self>,
781        location: Span,
782    },
783}
784
785pub const DEFAULT_TODO_STR: &str = "aiken::todo";
786
787pub const DEFAULT_ERROR_STR: &str = "aiken::error";
788
789impl UntypedExpr {
790    // Reify some opaque 'Constant' into an 'UntypedExpr', using a Type annotation. We also need
791    // an extra map to lookup record & enum constructor's names as they're completely erased when
792    // in their PlutusData form, and the Type annotation only contains type name.
793    //
794    // The function performs some sanity check to ensure that the type does indeed somewhat
795    // correspond to the data being given.
796    pub fn reify_constant(
797        data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
798        cst: uplc::ast::Constant,
799        tipo: Rc<Type>,
800    ) -> Result<Self, String> {
801        UntypedExpr::do_reify_constant(data_types, cst, tipo)
802    }
803
804    pub fn is_discard(&self) -> bool {
805        matches!(self, UntypedExpr::Var { name, ..} if name.starts_with("_"))
806    }
807
808    // Reify some opaque 'PlutusData' into an 'UntypedExpr', using a Type annotation. We also need
809    // an extra map to lookup record & enum constructor's names as they're completely erased when
810    // in their PlutusData form, and the Type annotation only contains type name.
811    //
812    // The function performs some sanity check to ensure that the type does indeed somewhat
813    // correspond to the data being given.
814    pub fn reify_data(
815        data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
816        data: PlutusData,
817        tipo: Rc<Type>,
818    ) -> Result<Self, String> {
819        UntypedExpr::do_reify_data(data_types, data, tipo)
820    }
821
822    fn reify_with<T, F>(
823        data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
824        t: T,
825        tipo: Rc<Type>,
826        with: F,
827    ) -> Result<Self, String>
828    where
829        T: Debug,
830        F: Fn(&IndexMap<&DataTypeKey, &TypedDataType>, T, Rc<Type>) -> Result<Self, String>,
831    {
832        if let Type::Var { tipo: var_tipo, .. } = tipo.deref() {
833            match &*var_tipo.borrow() {
834                TypeVar::Link { tipo } => {
835                    return Self::reify_with(data_types, t, tipo.clone(), with);
836                }
837                _ => unreachable!("unbound type during reification {tipo:?} -> {t:?}"),
838            }
839        }
840
841        // NOTE: Opaque types are tricky. We can't tell from a type only if it is
842        // opaque or not. We have to lookup its datatype definition.
843        //
844        // Also, we can't -- in theory -- peak into an opaque type. More so, if it
845        // has a single constructor with a single argument, it is an zero-cost
846        // wrapper. That means the underlying PlutusData has no residue of that
847        // wrapper. So we have to manually reconstruct it before crawling further
848        // down the type tree.
849        if check_replaceable_opaque_type(&tipo, data_types) {
850            let DataType { name, .. } = lookup_data_type_by_tipo(data_types, &tipo)
851                .expect("Type just disappeared from known types? {tipo:?}");
852
853            let inner_type = convert_opaque_type(&tipo, data_types, false);
854
855            let value = Self::reify_with(data_types, t, inner_type, with)?;
856
857            return Ok(UntypedExpr::Call {
858                location: Span::empty(),
859                arguments: vec![CallArg {
860                    label: None,
861                    location: Span::empty(),
862                    value,
863                }],
864                fun: Box::new(UntypedExpr::Var {
865                    name,
866                    location: Span::empty(),
867                }),
868            });
869        }
870
871        with(data_types, t, tipo)
872    }
873
874    fn do_reify_constant(
875        data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
876        cst: uplc::ast::Constant,
877        tipo: Rc<Type>,
878    ) -> Result<Self, String> {
879        Self::reify_with(data_types, cst, tipo, |data_types, cst, tipo| match cst {
880            uplc::ast::Constant::Data(data) => UntypedExpr::do_reify_data(data_types, data, tipo),
881
882            uplc::ast::Constant::Integer(i) => {
883                UntypedExpr::do_reify_data(data_types, Data::integer(i), tipo)
884            }
885
886            uplc::ast::Constant::ByteString(bytes) => {
887                UntypedExpr::do_reify_data(data_types, Data::bytestring(bytes), tipo)
888            }
889
890            uplc::ast::Constant::ProtoList(_, args) => match tipo.deref() {
891                Type::App {
892                    module,
893                    name,
894                    args: type_args,
895                    ..
896                } if module.is_empty() && name.as_str() == "List" => {
897                    if let [inner] = &type_args[..] {
898                        Ok(UntypedExpr::List {
899                            location: Span::empty(),
900                            elements: args
901                                .into_iter()
902                                .map(|arg| {
903                                    UntypedExpr::do_reify_constant(data_types, arg, inner.clone())
904                                })
905                                .collect::<Result<Vec<_>, _>>()?,
906                            tail: None,
907                        })
908                    } else {
909                        Err(
910                            "invalid List type annotation: the list has multiple type-parameters."
911                                .to_string(),
912                        )
913                    }
914                }
915                Type::Tuple { elems, .. } => Ok(UntypedExpr::Tuple {
916                    location: Span::empty(),
917                    elems: args
918                        .into_iter()
919                        .zip(elems)
920                        .map(|(arg, arg_type)| {
921                            UntypedExpr::do_reify_constant(data_types, arg, arg_type.clone())
922                        })
923                        .collect::<Result<Vec<_>, _>>()?,
924                }),
925                _ => Err(format!(
926                    "invalid type annotation. expected List but got: {tipo:?}"
927                )),
928            },
929
930            uplc::ast::Constant::ProtoPair(_, _, left, right) => match tipo.deref() {
931                Type::Pair { fst, snd, .. } => {
932                    let elems = [left.as_ref(), right.as_ref()]
933                        .into_iter()
934                        .zip([fst, snd])
935                        .map(|(arg, arg_type)| {
936                            UntypedExpr::do_reify_constant(
937                                data_types,
938                                arg.to_owned(),
939                                arg_type.clone(),
940                            )
941                        })
942                        .collect::<Result<Vec<_>, _>>()?;
943
944                    Ok(UntypedExpr::Pair {
945                        location: Span::empty(),
946                        fst: elems.first().unwrap().to_owned().into(),
947                        snd: elems.last().unwrap().to_owned().into(),
948                    })
949                }
950                _ => Err(format!(
951                    "invalid type annotation. expected Pair but got: {tipo:?}"
952                )),
953            },
954
955            uplc::ast::Constant::Unit => Ok(UntypedExpr::Var {
956                location: Span::empty(),
957                name: "Void".to_string(),
958            }),
959
960            uplc::ast::Constant::Bool(is_true) => Ok(UntypedExpr::Var {
961                location: Span::empty(),
962                name: if is_true { "True" } else { "False" }.to_string(),
963            }),
964
965            uplc::ast::Constant::String(value) => Ok(UntypedExpr::String {
966                location: Span::empty(),
967                value,
968            }),
969
970            uplc::ast::Constant::Bls12_381G1Element(pt) => Ok(UntypedExpr::CurvePoint {
971                location: Span::empty(),
972                point: Curve::Bls12_381(Bls12_381Point::G1(*pt)).into(),
973                preferred_format: ByteArrayFormatPreference::HexadecimalString,
974            }),
975
976            uplc::ast::Constant::Bls12_381G2Element(pt) => Ok(UntypedExpr::CurvePoint {
977                location: Span::empty(),
978                point: Curve::Bls12_381(Bls12_381Point::G2(*pt)).into(),
979                preferred_format: ByteArrayFormatPreference::HexadecimalString,
980            }),
981
982            uplc::ast::Constant::Bls12_381MlResult(ml) => {
983                let mut bytes = Vec::new();
984
985                bytes.extend((*ml).to_bendian());
986
987                // NOTE: We don't actually have a syntax for representing MillerLoop results, so we
988                // just fake it as a constructor with a bytearray. Note also that the bytearray is
989                // *large*.
990                Ok(UntypedExpr::Call {
991                    location: Span::empty(),
992                    arguments: vec![CallArg {
993                        label: None,
994                        location: Span::empty(),
995                        value: UntypedExpr::ByteArray {
996                            location: Span::empty(),
997                            bytes: bytes.into_iter().map(|b| (b, Span::empty())).collect(),
998                            preferred_format: ByteArrayFormatPreference::HexadecimalString,
999                        },
1000                    }],
1001                    fun: Box::new(UntypedExpr::Var {
1002                        name: "MillerLoopResult".to_string(),
1003                        location: Span::empty(),
1004                    }),
1005                })
1006            }
1007        })
1008    }
1009
1010    fn reify_blind(data: PlutusData) -> Self {
1011        match data {
1012            PlutusData::BigInt(ref i) => UntypedExpr::UInt {
1013                location: Span::empty(),
1014                base: Base::Decimal {
1015                    numeric_underscore: false,
1016                },
1017                value: from_pallas_bigint(i).to_string(),
1018            },
1019
1020            PlutusData::BoundedBytes(bytes) => {
1021                let bytes: Vec<u8> = bytes.into();
1022
1023                UntypedExpr::ByteArray {
1024                    location: Span::empty(),
1025                    bytes: bytes.into_iter().map(|b| (b, Span::empty())).collect(),
1026                    preferred_format: ByteArrayFormatPreference::HexadecimalString,
1027                }
1028            }
1029
1030            PlutusData::Array(elems) => UntypedExpr::List {
1031                location: Span::empty(),
1032                elements: elems
1033                    .to_vec()
1034                    .into_iter()
1035                    .map(UntypedExpr::reify_blind)
1036                    .collect::<Vec<_>>(),
1037                tail: None,
1038            },
1039
1040            PlutusData::Map(indef_or_def) => {
1041                let kvs = match indef_or_def {
1042                    KeyValuePairs::Def(kvs) => kvs,
1043                    KeyValuePairs::Indef(kvs) => kvs,
1044                };
1045
1046                UntypedExpr::List {
1047                    location: Span::empty(),
1048                    elements: kvs
1049                        .into_iter()
1050                        .map(|(k, v)| UntypedExpr::Pair {
1051                            location: Span::empty(),
1052                            fst: UntypedExpr::reify_blind(k).into(),
1053                            snd: UntypedExpr::reify_blind(v).into(),
1054                        })
1055                        .collect::<Vec<_>>(),
1056                    tail: None,
1057                }
1058            }
1059
1060            PlutusData::Constr(Constr {
1061                tag,
1062                any_constructor,
1063                fields,
1064            }) => {
1065                let ix = convert_tag_to_constr(tag).or(any_constructor).unwrap() as usize;
1066
1067                let fields = fields
1068                    .to_vec()
1069                    .into_iter()
1070                    .map(|field| CallArg {
1071                        location: Span::empty(),
1072                        label: None,
1073                        value: UntypedExpr::reify_blind(field),
1074                    })
1075                    .collect::<Vec<_>>();
1076
1077                let mut arguments = vec![CallArg {
1078                    location: Span::empty(),
1079                    label: None,
1080                    value: UntypedExpr::UInt {
1081                        location: Span::empty(),
1082                        value: ix.to_string(),
1083                        base: Base::Decimal {
1084                            numeric_underscore: false,
1085                        },
1086                    },
1087                }];
1088                arguments.extend(fields);
1089
1090                UntypedExpr::Call {
1091                    location: Span::empty(),
1092                    arguments,
1093                    fun: UntypedExpr::Var {
1094                        name: "Constr".to_string(),
1095                        location: Span::empty(),
1096                    }
1097                    .into(),
1098                }
1099            }
1100        }
1101    }
1102
1103    fn do_reify_data(
1104        data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
1105        data: PlutusData,
1106        tipo: Rc<Type>,
1107    ) -> Result<Self, String> {
1108        let tipo = Type::collapse_links(tipo);
1109
1110        if let Type::App { name, module, .. } = tipo.deref()
1111            && module.is_empty()
1112            && name == "Data"
1113        {
1114            return Ok(Self::reify_blind(data));
1115        }
1116
1117        Self::reify_with(
1118            data_types,
1119            data,
1120            tipo,
1121            |data_types, data, tipo| match data {
1122                PlutusData::BigInt(ref i) => Ok(UntypedExpr::UInt {
1123                    location: Span::empty(),
1124                    base: Base::Decimal {
1125                        numeric_underscore: false,
1126                    },
1127                    value: from_pallas_bigint(i).to_string(),
1128                }),
1129
1130                PlutusData::BoundedBytes(bytes) => {
1131                    if tipo.is_string() {
1132                        Ok(UntypedExpr::String {
1133                            location: Span::empty(),
1134                            value: String::from_utf8(bytes.to_vec()).expect("invalid UTF-8 string"),
1135                        })
1136                    } else {
1137                        let bytes: Vec<u8> = bytes.into();
1138                        Ok(UntypedExpr::ByteArray {
1139                            location: Span::empty(),
1140                            bytes: bytes.into_iter().map(|b| (b, Span::empty())).collect(),
1141                            preferred_format: ByteArrayFormatPreference::HexadecimalString,
1142                        })
1143                    }
1144                }
1145
1146                PlutusData::Array(args) => match tipo.deref() {
1147                    Type::App {
1148                        module,
1149                        name,
1150                        args: type_args,
1151                        ..
1152                    } if module.is_empty() && name.as_str() == "List" => {
1153                        if let [inner] = &type_args[..] {
1154                            Ok(UntypedExpr::List {
1155                                location: Span::empty(),
1156                                elements: args
1157                                    .to_vec()
1158                                    .into_iter()
1159                                    .map(|arg| {
1160                                        UntypedExpr::do_reify_data(data_types, arg, inner.clone())
1161                                    })
1162                                    .collect::<Result<Vec<_>, _>>()?,
1163                                tail: None,
1164                            })
1165                        } else {
1166                            Err(
1167                                "invalid List type annotation: the list has multiple type-parameters."
1168                                    .to_string(),
1169                            )
1170                        }
1171                    }
1172                    Type::Tuple { elems, .. } => Ok(UntypedExpr::Tuple {
1173                        location: Span::empty(),
1174                        elems: args
1175                            .to_vec()
1176                            .into_iter()
1177                            .zip(elems)
1178                            .map(|(arg, arg_type)| {
1179                                UntypedExpr::do_reify_data(data_types, arg, arg_type.clone())
1180                            })
1181                            .collect::<Result<Vec<_>, _>>()?,
1182                    }),
1183                    Type::Pair { fst, snd, .. } => {
1184                        let mut elems = args
1185                            .to_vec()
1186                            .into_iter()
1187                            .zip([fst, snd])
1188                            .map(|(arg, arg_type)| {
1189                                UntypedExpr::do_reify_data(data_types, arg, arg_type.clone())
1190                            })
1191                            .collect::<Result<Vec<_>, _>>()?;
1192
1193                        Ok(UntypedExpr::Pair {
1194                            location: Span::empty(),
1195                            fst: elems.remove(0).into(),
1196                            snd: elems.remove(0).into(),
1197                        })
1198                    }
1199                    _ => Err(format!(
1200                        "invalid type annotation. expected List but got: {tipo:?}"
1201                    )),
1202                },
1203
1204                PlutusData::Constr(Constr {
1205                    tag,
1206                    any_constructor,
1207                    fields,
1208                }) => {
1209                    let ix = convert_tag_to_constr(tag).or(any_constructor).unwrap() as usize;
1210
1211                    if let Type::App { args, .. } = tipo.deref()
1212                        && let Some(DataType {
1213                            constructors,
1214                            typed_parameters,
1215                            ..
1216                        }) = lookup_data_type_by_tipo(data_types, &tipo)
1217                    {
1218                        if constructors.is_empty() {
1219                            return Ok(UntypedExpr::Var {
1220                                location: Span::empty(),
1221                                name: "Data".to_string(),
1222                            });
1223                        }
1224
1225                        let constructor = &constructors[ix];
1226
1227                        let generics = typed_parameters.iter().zip(args).fold(
1228                            IndexMap::new(),
1229                            |mut generics, (generic, arg)| {
1230                                if let Some(ix) = generic.get_generic_id() {
1231                                    generics.insert(ix, arg.clone());
1232                                }
1233
1234                                generics
1235                            },
1236                        );
1237
1238                        return if fields.is_empty() {
1239                            Ok(UntypedExpr::Var {
1240                                location: Span::empty(),
1241                                name: constructor.name.to_string(),
1242                            })
1243                        } else {
1244                            let arguments = fields
1245                                .to_vec()
1246                                .into_iter()
1247                                .zip(constructor.arguments.iter())
1248                                .map(|(field, RecordConstructorArg { label, tipo, .. })| {
1249                                    let mut tipo = tipo;
1250
1251                                    // Replace any generic with their specialised definitions
1252                                    // for this type instance.
1253                                    if let Type::Var { tipo: var, .. } =
1254                                        Type::collapse_links(tipo.clone()).as_ref()
1255                                        && let TypeVar::Generic { id } = &*var.borrow()
1256                                    {
1257                                        tipo = generics.get(id).expect("unknown generic?");
1258                                    }
1259
1260                                    UntypedExpr::do_reify_data(data_types, field, tipo.clone()).map(
1261                                        |value| CallArg {
1262                                            label: label.clone(),
1263                                            location: Span::empty(),
1264                                            value,
1265                                        },
1266                                    )
1267                                })
1268                                .collect::<Result<Vec<_>, _>>()?;
1269
1270                            Ok(UntypedExpr::Call {
1271                                location: Span::empty(),
1272                                arguments,
1273                                fun: Box::new(UntypedExpr::Var {
1274                                    name: constructor.name.to_string(),
1275                                    location: Span::empty(),
1276                                }),
1277                            })
1278                        };
1279                    }
1280
1281                    Err(format!(
1282                        "invalid type annotation {tipo:?} for {}{} constructor with fields: {fields:?}",
1283                        ix + 1,
1284                        ordinal::Ordinal::<usize>(ix + 1).suffix(),
1285                    ))
1286                }
1287
1288                PlutusData::Map(indef_or_def) => {
1289                    let kvs = match indef_or_def {
1290                        KeyValuePairs::Def(kvs) => kvs,
1291                        KeyValuePairs::Indef(kvs) => kvs,
1292                    };
1293
1294                    UntypedExpr::do_reify_data(
1295                        data_types,
1296                        Data::list(
1297                            kvs.into_iter()
1298                                .map(|(k, v)| Data::list(vec![k, v]))
1299                                .collect(),
1300                        ),
1301                        tipo,
1302                    )
1303                }
1304            },
1305        )
1306    }
1307
1308    pub fn todo(reason: Option<Self>, location: Span) -> Self {
1309        UntypedExpr::Trace {
1310            location,
1311            kind: TraceKind::Todo,
1312            then: Box::new(UntypedExpr::ErrorTerm { location }),
1313            label: Box::new(reason.unwrap_or_else(|| UntypedExpr::String {
1314                location,
1315                value: DEFAULT_TODO_STR.to_string(),
1316            })),
1317            arguments: Vec::new(),
1318        }
1319    }
1320
1321    pub fn fail(reason: Option<Self>, location: Span) -> Self {
1322        if let Some(reason) = reason {
1323            UntypedExpr::Trace {
1324                location,
1325                kind: TraceKind::Error,
1326                then: Box::new(UntypedExpr::ErrorTerm { location }),
1327                label: Box::new(reason),
1328                arguments: Vec::new(),
1329            }
1330        } else {
1331            UntypedExpr::ErrorTerm { location }
1332        }
1333    }
1334
1335    pub fn tuple_index(self, index: usize, location: Span) -> Self {
1336        UntypedExpr::TupleIndex {
1337            location: self.location().union(location),
1338            index,
1339            tuple: Box::new(self),
1340        }
1341    }
1342
1343    pub fn field_access(self, label: String, location: Span) -> Self {
1344        UntypedExpr::FieldAccess {
1345            location: self.location().union(location),
1346            label,
1347            container: Box::new(self),
1348        }
1349    }
1350
1351    pub fn call(self, args: Vec<ParsedCallArg>, location: Span) -> Self {
1352        let mut holes = Vec::new();
1353
1354        let args = args
1355            .into_iter()
1356            .enumerate()
1357            .map(|(index, a)| match a {
1358                CallArg {
1359                    value: Some(value),
1360                    label,
1361                    location,
1362                } if !value.is_discard() => CallArg {
1363                    value,
1364                    label,
1365                    location,
1366                },
1367                CallArg {
1368                    value,
1369                    label,
1370                    location,
1371                } => {
1372                    let name = format!(
1373                        "{}__{index}_{}",
1374                        ast::CAPTURE_VARIABLE,
1375                        match value {
1376                            Some(UntypedExpr::Var { ref name, .. }) => name,
1377                            _ => "_",
1378                        }
1379                    );
1380
1381                    holes.push(ast::UntypedArg {
1382                        location: Span::empty(),
1383                        annotation: None,
1384                        doc: None,
1385                        by: ArgBy::ByName(ast::ArgName::Named {
1386                            label: name.clone(),
1387                            name: name.clone(),
1388                            location: Span::empty(),
1389                        }),
1390                        is_validator_param: false,
1391                    });
1392
1393                    ast::CallArg {
1394                        label,
1395                        location,
1396                        value: UntypedExpr::Var { location, name },
1397                    }
1398                }
1399            })
1400            .collect();
1401
1402        let call = UntypedExpr::Call {
1403            location: self.location().union(location),
1404            fun: Box::new(self),
1405            arguments: args,
1406        };
1407
1408        if holes.is_empty() {
1409            call
1410        } else {
1411            UntypedExpr::Fn {
1412                location: call.location(),
1413                fn_style: FnStyle::Capture,
1414                arguments: holes,
1415                body: Box::new(call),
1416                return_annotation: None,
1417            }
1418        }
1419    }
1420
1421    pub fn append_in_sequence(self, next: Self) -> Self {
1422        let location = Span {
1423            start: self.location().start,
1424            end: next.location().end,
1425        };
1426
1427        match (self.clone(), next.clone()) {
1428            (left @ Self::Sequence { .. }, right @ Self::Sequence { .. }) => Self::Sequence {
1429                location,
1430                expressions: vec![left, right],
1431            },
1432            (
1433                _,
1434                Self::Sequence {
1435                    expressions: mut next_expressions,
1436                    ..
1437                },
1438            ) => {
1439                let mut current_expressions = vec![self];
1440
1441                current_expressions.append(&mut next_expressions);
1442
1443                Self::Sequence {
1444                    location,
1445                    expressions: current_expressions,
1446                }
1447            }
1448
1449            (_, _) => Self::Sequence {
1450                location,
1451                expressions: vec![self, next],
1452            },
1453        }
1454    }
1455
1456    pub fn location(&self) -> Span {
1457        match self {
1458            Self::PipeLine { expressions, .. } => expressions.last().location(),
1459            Self::Trace { then, .. } => then.location(),
1460            Self::TraceIfFalse { location, .. }
1461            | Self::Fn { location, .. }
1462            | Self::Var { location, .. }
1463            | Self::UInt { location, .. }
1464            | Self::ErrorTerm { location, .. }
1465            | Self::When { location, .. }
1466            | Self::Call { location, .. }
1467            | Self::List { location, .. }
1468            | Self::ByteArray { location, .. }
1469            | Self::BinOp { location, .. }
1470            | Self::Tuple { location, .. }
1471            | Self::Pair { location, .. }
1472            | Self::String { location, .. }
1473            | Self::Assignment { location, .. }
1474            | Self::TupleIndex { location, .. }
1475            | Self::FieldAccess { location, .. }
1476            | Self::RecordUpdate { location, .. }
1477            | Self::UnOp { location, .. }
1478            | Self::LogicalOpChain { location, .. }
1479            | Self::If { location, .. }
1480            | Self::CurvePoint { location, .. } => *location,
1481            Self::Sequence {
1482                location,
1483                expressions,
1484                ..
1485            } => expressions.last().map(Self::location).unwrap_or(*location),
1486        }
1487    }
1488
1489    pub fn start_byte_index(&self) -> usize {
1490        match self {
1491            Self::Sequence {
1492                expressions,
1493                location,
1494                ..
1495            } => expressions
1496                .first()
1497                .map(|e| e.start_byte_index())
1498                .unwrap_or(location.start),
1499            Self::PipeLine { expressions, .. } => expressions.first().start_byte_index(),
1500            Self::Trace { location, .. } | Self::Assignment { location, .. } => location.start,
1501            _ => self.location().start,
1502        }
1503    }
1504
1505    pub fn binop_precedence(&self) -> u8 {
1506        match self {
1507            Self::BinOp { name, .. } => name.precedence(),
1508            Self::PipeLine { .. } => 0,
1509            _ => u8::MAX,
1510        }
1511    }
1512
1513    /// Returns true when an UntypedExpr can be displayed in a flex-break manner (i.e. tries to fit as
1514    /// much as possible on a single line). When false, long lines with several of those patterns
1515    /// will be broken down to one expr per line.
1516    pub fn is_simple_expr_to_format(&self) -> bool {
1517        match self {
1518            Self::String { .. } | Self::UInt { .. } | Self::ByteArray { .. } | Self::Var { .. } => {
1519                true
1520            }
1521            Self::Pair { fst, snd, .. } => {
1522                fst.is_simple_expr_to_format() && snd.is_simple_expr_to_format()
1523            }
1524            Self::Tuple { elems, .. } => elems.iter().all(|e| e.is_simple_expr_to_format()),
1525            Self::List { elements, .. } if elements.len() <= 3 => {
1526                elements.iter().all(|e| e.is_simple_expr_to_format())
1527            }
1528            _ => false,
1529        }
1530    }
1531
1532    pub fn lambda(
1533        names: Vec<(ArgName, Span, Option<Annotation>)>,
1534        expressions: Vec<UntypedExpr>,
1535        location: Span,
1536    ) -> Self {
1537        Self::Fn {
1538            location,
1539            fn_style: FnStyle::Plain,
1540            arguments: names
1541                .into_iter()
1542                .map(|(arg_name, location, annotation)| UntypedArg {
1543                    location,
1544                    doc: None,
1545                    annotation,
1546                    is_validator_param: false,
1547                    by: ArgBy::ByName(arg_name),
1548                })
1549                .collect(),
1550            body: Self::Sequence {
1551                location,
1552                expressions,
1553            }
1554            .into(),
1555            return_annotation: None,
1556        }
1557    }
1558}