tx3_lang/
ir.rs

1//! The Tx3 language intermediate representation (IR).
2//!
3//! This module defines the intermediate representation (IR) for the Tx3
4//! language. It provides the structure for representing Tx3 programs in a more
5//! abstract form, suitable for further processing or execution.
6//!
7//! This module is not intended to be used directly by end-users. See
8//! [`lower`](crate::lower) for lowering an AST to the intermediate
9//! representation.
10
11use std::collections::{HashMap, HashSet};
12
13use bincode::{Decode, Encode};
14use serde::{Deserialize, Serialize};
15
16use crate::{Utxo, UtxoRef};
17
18pub const IR_VERSION: &str = "v1alpha7";
19
20#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
21pub struct StructExpr {
22    pub constructor: usize,
23    pub fields: Vec<Expression>,
24}
25
26impl StructExpr {
27    pub fn unit() -> Self {
28        Self {
29            constructor: 0,
30            fields: vec![],
31        }
32    }
33}
34
35#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
36pub enum Coerce {
37    NoOp(Expression),
38    IntoAssets(Expression),
39    IntoDatum(Expression),
40    IntoScript(Expression),
41}
42
43pub type PropertyIndex = usize;
44
45/// Operations that are executed during the "apply" phase.
46///
47/// These are operations that are executed during the "apply" phase, as opposed
48/// to the compiler operations that are executed during the "compile" phase.
49///
50/// These ops can be executed (aka "reduced") very early in the process. As long
51/// as they underlying expressions are "constant" (aka: don't rely on external
52/// data), the will be simplified directly during the "apply" phase.
53#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
54pub enum BuiltInOp {
55    NoOp(Expression),
56    Add(Expression, Expression),
57    Sub(Expression, Expression),
58    Negate(Expression),
59    Property(Expression, PropertyIndex),
60}
61
62/// Operations that are performed by the compiler.
63///
64/// These are operations that are performed by the compiler, as opposed to the
65/// built-in operations that are executed (aka "reduced") during the "apply"
66/// phase.
67///
68/// These ops can't be executed earlier because they are either: chain-specific
69/// or rely on data that is only available to the compiler.
70#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
71pub enum CompilerOp {
72    BuildScriptAddress(Expression),
73}
74
75#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
76pub struct AssetExpr {
77    pub policy: Expression,
78    pub asset_name: Expression,
79    pub amount: Expression,
80}
81
82/// An ad-hoc compile directive.
83///
84/// It's a generic, pass-through structure that the final chain-specific
85/// compiler can use to compile custom structures. Tx3 won't attempt to process
86/// this IR structure for anything other than trying to apply / reduce its
87/// expressions.
88#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
89pub struct AdHocDirective {
90    pub name: String,
91    pub data: HashMap<String, Expression>,
92}
93
94#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
95pub enum ScriptSource {
96    Embedded(Expression),
97    UtxoRef {
98        r#ref: Expression,
99        source: Option<Expression>,
100    },
101}
102
103impl ScriptSource {
104    pub fn new_ref(r#ref: Expression, source: Expression) -> Self {
105        Self::UtxoRef {
106            r#ref,
107            source: Some(source),
108        }
109    }
110
111    pub fn new_embedded(source: Expression) -> Self {
112        Self::Embedded(source)
113    }
114
115    pub fn expect_parameter(policy_name: String) -> Self {
116        Self::Embedded(
117            Param::ExpectValue(
118                format!("{}_script", policy_name.to_lowercase()),
119                Type::Bytes,
120            )
121            .into(),
122        )
123    }
124
125    pub fn expect_ref_input(policy_name: String, r#ref: Expression) -> Self {
126        Self::UtxoRef {
127            r#ref: r#ref.clone(),
128            source: Some(
129                Coerce::IntoScript(
130                    Param::ExpectInput(
131                        format!("{}_script", policy_name.to_lowercase()),
132                        InputQuery {
133                            address: Expression::None,
134                            min_amount: Expression::None,
135                            r#ref,
136                        },
137                    )
138                    .into(),
139                )
140                .into(),
141            ),
142        }
143    }
144
145    pub fn as_utxo_ref(&self) -> Option<Expression> {
146        match self {
147            Self::UtxoRef { r#ref, .. } => Some(r#ref.clone()),
148            Self::Embedded(Expression::UtxoRefs(x)) => Some(Expression::UtxoRefs(x.clone())),
149            _ => None,
150        }
151    }
152}
153
154#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
155pub struct PolicyExpr {
156    pub name: String,
157    pub hash: Expression,
158    pub script: ScriptSource,
159}
160
161#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
162pub enum Type {
163    Undefined,
164    Unit,
165    Int,
166    Bool,
167    Bytes,
168    Address,
169    Utxo,
170    UtxoRef,
171    AnyAsset,
172    List,
173    Custom(String),
174}
175
176#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
177pub enum Param {
178    Set(Expression),
179    ExpectValue(String, Type),
180    ExpectInput(String, InputQuery),
181    ExpectFees,
182}
183
184#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
185pub enum Expression {
186    None,
187    List(Vec<Expression>),
188    Tuple(Box<(Expression, Expression)>),
189    Struct(StructExpr),
190    Bytes(Vec<u8>),
191    Number(i128),
192    Bool(bool),
193    String(String),
194    Address(Vec<u8>),
195    Hash(Vec<u8>),
196    UtxoRefs(Vec<UtxoRef>),
197    UtxoSet(HashSet<Utxo>),
198    Assets(Vec<AssetExpr>),
199
200    EvalParam(Box<Param>),
201    EvalBuiltIn(Box<BuiltInOp>),
202    EvalCompiler(Box<CompilerOp>),
203    EvalCoerce(Box<Coerce>),
204
205    // pass-through
206    AdHocDirective(Box<AdHocDirective>),
207}
208
209impl Default for Expression {
210    fn default() -> Self {
211        Self::None
212    }
213}
214
215impl Expression {
216    pub fn is_none(&self) -> bool {
217        matches!(self, Self::None)
218    }
219
220    pub fn as_option(&self) -> Option<&Self> {
221        match self {
222            Self::None => None,
223            _ => Some(self),
224        }
225    }
226}
227
228impl From<BuiltInOp> for Expression {
229    fn from(op: BuiltInOp) -> Self {
230        Self::EvalBuiltIn(Box::new(op))
231    }
232}
233
234impl From<CompilerOp> for Expression {
235    fn from(op: CompilerOp) -> Self {
236        Self::EvalCompiler(Box::new(op))
237    }
238}
239
240impl From<Coerce> for Expression {
241    fn from(coerce: Coerce) -> Self {
242        Self::EvalCoerce(Box::new(coerce))
243    }
244}
245
246impl From<Param> for Expression {
247    fn from(param: Param) -> Self {
248        Self::EvalParam(Box::new(param))
249    }
250}
251
252#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
253pub struct InputQuery {
254    pub address: Expression,
255    pub min_amount: Expression,
256    pub r#ref: Expression,
257}
258
259#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
260pub struct Input {
261    pub name: String,
262    pub utxos: Expression,
263    pub redeemer: Expression,
264}
265
266#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
267pub struct Output {
268    pub address: Expression,
269    pub datum: Expression,
270    pub amount: Expression,
271}
272
273#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
274pub struct Validity {
275    pub since: Expression,
276    pub until: Expression,
277}
278
279#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
280pub struct Mint {
281    pub amount: Expression,
282    pub redeemer: Expression,
283}
284
285#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
286pub struct Collateral {
287    pub utxos: Expression,
288}
289
290#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
291pub struct Metadata {
292    pub key: Expression,
293    pub value: Expression,
294}
295
296#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
297pub struct Signers {
298    pub signers: Vec<Expression>,
299}
300
301#[derive(Encode, Decode, Serialize, Deserialize, Debug, Clone)]
302pub struct Tx {
303    pub fees: Expression,
304    pub references: Vec<Expression>,
305    pub inputs: Vec<Input>,
306    pub outputs: Vec<Output>,
307    pub validity: Option<Validity>,
308    pub mints: Vec<Mint>,
309    pub adhoc: Vec<AdHocDirective>,
310    pub collateral: Vec<Collateral>,
311    pub signers: Option<Signers>,
312    pub metadata: Vec<Metadata>,
313}