Skip to main content

runar_compiler_rust/ir/
mod.rs

1//! ANF IR types and loader.
2//!
3//! These types mirror the canonical Rúnar ANF IR JSON schema. Any conformant
4//! Rúnar compiler produces byte-identical ANF IR (when serialised with canonical
5//! JSON), so these types serve as the universal interchange format.
6
7pub mod loader;
8
9use serde::{Deserialize, Serialize};
10
11// ---------------------------------------------------------------------------
12// Program structure
13// ---------------------------------------------------------------------------
14
15/// Top-level ANF IR container.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ANFProgram {
18    #[serde(rename = "contractName")]
19    pub contract_name: String,
20    pub properties: Vec<ANFProperty>,
21    pub methods: Vec<ANFMethod>,
22}
23
24/// A contract-level property (constructor parameter).
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct ANFProperty {
27    pub name: String,
28    #[serde(rename = "type")]
29    pub prop_type: String,
30    pub readonly: bool,
31    #[serde(rename = "initialValue", skip_serializing_if = "Option::is_none")]
32    pub initial_value: Option<serde_json::Value>,
33}
34
35/// A single contract method.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct ANFMethod {
38    pub name: String,
39    pub params: Vec<ANFParam>,
40    pub body: Vec<ANFBinding>,
41    #[serde(rename = "isPublic")]
42    pub is_public: bool,
43}
44
45/// A method parameter.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct ANFParam {
48    pub name: String,
49    #[serde(rename = "type")]
50    pub param_type: String,
51}
52
53// ---------------------------------------------------------------------------
54// Bindings
55// ---------------------------------------------------------------------------
56
57/// A single let-binding: `let <name> = <value>`.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ANFBinding {
60    pub name: String,
61    pub value: ANFValue,
62}
63
64// ---------------------------------------------------------------------------
65// ANF value types (discriminated on `kind`)
66// ---------------------------------------------------------------------------
67
68/// Discriminated union of all ANF value types.
69///
70/// Uses `#[serde(tag = "kind")]` to match the JSON `"kind"` discriminator.
71#[derive(Debug, Clone, Serialize, Deserialize)]
72#[serde(tag = "kind")]
73pub enum ANFValue {
74    #[serde(rename = "load_param")]
75    LoadParam { name: String },
76
77    #[serde(rename = "load_prop")]
78    LoadProp { name: String },
79
80    #[serde(rename = "load_const")]
81    LoadConst { value: serde_json::Value },
82
83    #[serde(rename = "bin_op")]
84    BinOp {
85        op: String,
86        left: String,
87        right: String,
88        #[serde(skip_serializing_if = "Option::is_none")]
89        result_type: Option<String>,
90    },
91
92    #[serde(rename = "unary_op")]
93    UnaryOp {
94        op: String,
95        operand: String,
96        #[serde(skip_serializing_if = "Option::is_none")]
97        result_type: Option<String>,
98    },
99
100    #[serde(rename = "call")]
101    Call {
102        func: String,
103        args: Vec<String>,
104    },
105
106    #[serde(rename = "method_call")]
107    MethodCall {
108        object: String,
109        method: String,
110        args: Vec<String>,
111    },
112
113    #[serde(rename = "if")]
114    If {
115        cond: String,
116        then: Vec<ANFBinding>,
117        #[serde(rename = "else")]
118        else_branch: Vec<ANFBinding>,
119    },
120
121    #[serde(rename = "loop")]
122    Loop {
123        count: usize,
124        body: Vec<ANFBinding>,
125        #[serde(rename = "iterVar")]
126        iter_var: String,
127    },
128
129    #[serde(rename = "assert")]
130    Assert { value: String },
131
132    #[serde(rename = "update_prop")]
133    UpdateProp { name: String, value: String },
134
135    #[serde(rename = "get_state_script")]
136    GetStateScript {},
137
138    #[serde(rename = "check_preimage")]
139    CheckPreimage { preimage: String },
140
141    #[serde(rename = "deserialize_state")]
142    DeserializeState { preimage: String },
143
144    #[serde(rename = "add_output")]
145    AddOutput {
146        satoshis: String,
147        #[serde(rename = "stateValues")]
148        state_values: Vec<String>,
149        #[serde(default)]
150        preimage: String,
151    },
152
153    #[serde(rename = "add_raw_output")]
154    AddRawOutput {
155        satoshis: String,
156        #[serde(rename = "scriptBytes")]
157        script_bytes: String,
158    },
159
160    #[serde(rename = "array_literal")]
161    ArrayLiteral {
162        elements: Vec<String>,
163    },
164}
165
166// ---------------------------------------------------------------------------
167// Constant value helpers
168// ---------------------------------------------------------------------------
169
170/// Typed constant value extracted from a `serde_json::Value`.
171#[derive(Debug, Clone)]
172pub enum ConstValue {
173    Bool(bool),
174    Int(i128),
175    Str(String),
176}
177
178impl ANFValue {
179    /// Extract the typed constant from a `LoadConst` value.
180    pub fn const_value(&self) -> Option<ConstValue> {
181        match self {
182            ANFValue::LoadConst { value } => parse_const_value(value),
183            _ => None,
184        }
185    }
186}
187
188/// Parse a `serde_json::Value` into a `ConstValue`.
189pub fn parse_const_value(v: &serde_json::Value) -> Option<ConstValue> {
190    match v {
191        serde_json::Value::Bool(b) => Some(ConstValue::Bool(*b)),
192        serde_json::Value::Number(n) => {
193            // Try i64 first (covers most values), then fall back to f64 for larger numbers
194            if let Some(i) = n.as_i64() {
195                Some(ConstValue::Int(i as i128))
196            } else if let Some(f) = n.as_f64() {
197                Some(ConstValue::Int(f as i128))
198            } else {
199                None
200            }
201        }
202        serde_json::Value::String(s) => Some(ConstValue::Str(s.clone())),
203        _ => None,
204    }
205}