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 { op: String, operand: String },
94
95    #[serde(rename = "call")]
96    Call {
97        func: String,
98        args: Vec<String>,
99    },
100
101    #[serde(rename = "method_call")]
102    MethodCall {
103        object: String,
104        method: String,
105        args: Vec<String>,
106    },
107
108    #[serde(rename = "if")]
109    If {
110        cond: String,
111        then: Vec<ANFBinding>,
112        #[serde(rename = "else")]
113        else_branch: Vec<ANFBinding>,
114    },
115
116    #[serde(rename = "loop")]
117    Loop {
118        count: usize,
119        body: Vec<ANFBinding>,
120        #[serde(rename = "iterVar")]
121        iter_var: String,
122    },
123
124    #[serde(rename = "assert")]
125    Assert { value: String },
126
127    #[serde(rename = "update_prop")]
128    UpdateProp { name: String, value: String },
129
130    #[serde(rename = "get_state_script")]
131    GetStateScript {},
132
133    #[serde(rename = "check_preimage")]
134    CheckPreimage { preimage: String },
135
136    #[serde(rename = "deserialize_state")]
137    DeserializeState { preimage: String },
138
139    #[serde(rename = "add_output")]
140    AddOutput {
141        satoshis: String,
142        #[serde(rename = "stateValues")]
143        state_values: Vec<String>,
144        #[serde(default)]
145        preimage: String,
146    },
147}
148
149// ---------------------------------------------------------------------------
150// Constant value helpers
151// ---------------------------------------------------------------------------
152
153/// Typed constant value extracted from a `serde_json::Value`.
154#[derive(Debug, Clone)]
155pub enum ConstValue {
156    Bool(bool),
157    Int(i128),
158    Str(String),
159}
160
161impl ANFValue {
162    /// Extract the typed constant from a `LoadConst` value.
163    pub fn const_value(&self) -> Option<ConstValue> {
164        match self {
165            ANFValue::LoadConst { value } => parse_const_value(value),
166            _ => None,
167        }
168    }
169}
170
171/// Parse a `serde_json::Value` into a `ConstValue`.
172pub fn parse_const_value(v: &serde_json::Value) -> Option<ConstValue> {
173    match v {
174        serde_json::Value::Bool(b) => Some(ConstValue::Bool(*b)),
175        serde_json::Value::Number(n) => {
176            // Try i64 first (covers most values), then fall back to f64 for larger numbers
177            if let Some(i) = n.as_i64() {
178                Some(ConstValue::Int(i as i128))
179            } else if let Some(f) = n.as_f64() {
180                Some(ConstValue::Int(f as i128))
181            } else {
182                None
183            }
184        }
185        serde_json::Value::String(s) => Some(ConstValue::Str(s.clone())),
186        _ => None,
187    }
188}