Skip to main content

rue_lir/
codegen.rs

1use clvm_traits::{ToClvm, clvm_list, clvm_quote, clvm_tuple};
2use clvmr::{Allocator, NodePtr};
3use id_arena::Arena;
4use num_bigint::BigUint;
5
6use crate::{ClvmOp, Lir, LirId, Result, path_to_atom};
7
8#[derive(Debug, Default, Clone, Copy)]
9pub struct CodegenOptions {
10    pub optimize_static_pairs: bool,
11}
12
13pub fn codegen(
14    arena: &Arena<Lir>,
15    allocator: &mut Allocator,
16    lir: LirId,
17    options: CodegenOptions,
18) -> Result<NodePtr> {
19    codegen_impl(arena, allocator, lir, options)
20}
21
22fn codegen_impl(
23    arena: &Arena<Lir>,
24    allocator: &mut Allocator,
25    lir: LirId,
26    options: CodegenOptions,
27) -> Result<NodePtr> {
28    let codegen = |arena: &Arena<Lir>, allocator: &mut Allocator, lir: LirId| {
29        codegen_impl(arena, allocator, lir, options)
30    };
31
32    match &arena[lir] {
33        Lir::Atom(atom) => {
34            if atom.is_empty() {
35                return Ok(NodePtr::NIL);
36            }
37            let atom = allocator.new_atom(atom)?;
38            Ok(clvm_quote!(atom).to_clvm(allocator)?)
39        }
40        Lir::Quote(arg) => {
41            let arg = codegen(arena, allocator, *arg)?;
42            Ok(clvm_quote!(arg).to_clvm(allocator)?)
43        }
44        Lir::Path(path) => {
45            let atom = path_to_atom(path);
46            Ok(allocator.new_atom(&atom)?)
47        }
48        Lir::Run(callee, env) => {
49            if options.optimize_static_pairs
50                && matches!(&arena[*env], Lir::Path(path) if path == &BigUint::from(1u8))
51                && is_static_value(arena, *callee)
52            {
53                return codegen_static_value(arena, allocator, *callee);
54            }
55
56            let callee = codegen(arena, allocator, *callee)?;
57            let env = codegen(arena, allocator, *env)?;
58            Ok(clvm_list!(ClvmOp::Apply, callee, env).to_clvm(allocator)?)
59        }
60        Lir::Closure(function, captures, has_parameters) => {
61            let function = codegen(arena, allocator, *function)?;
62
63            let mut args = if *has_parameters {
64                clvm_quote!(1).to_clvm(allocator)?
65            } else {
66                NodePtr::NIL
67            };
68
69            for capture in captures.iter().rev() {
70                let capture = codegen(arena, allocator, *capture)?;
71                args = clvm_list!(
72                    ClvmOp::Cons,
73                    clvm_quote!(ClvmOp::Cons),
74                    clvm_list!(
75                        ClvmOp::Cons,
76                        clvm_list!(ClvmOp::Cons, clvm_quote!(ClvmOp::Quote), capture),
77                        clvm_list!(ClvmOp::Cons, args, ())
78                    )
79                )
80                .to_clvm(allocator)?;
81            }
82
83            Ok(clvm_list!(
84                ClvmOp::Cons,
85                clvm_quote!(ClvmOp::Apply),
86                clvm_list!(
87                    ClvmOp::Cons,
88                    clvm_list!(ClvmOp::Cons, clvm_quote!(ClvmOp::Quote), function),
89                    clvm_list!(ClvmOp::Cons, args, ())
90                )
91            )
92            .to_clvm(allocator)?)
93        }
94        Lir::First(arg) => {
95            let arg = codegen(arena, allocator, *arg)?;
96            Ok(clvm_list!(ClvmOp::First, arg).to_clvm(allocator)?)
97        }
98        Lir::Rest(arg) => {
99            let arg = codegen(arena, allocator, *arg)?;
100            Ok(clvm_list!(ClvmOp::Rest, arg).to_clvm(allocator)?)
101        }
102        Lir::Cons(first, rest) => {
103            if options.optimize_static_pairs
104                && is_static_value(arena, *first)
105                && is_static_value(arena, *rest)
106            {
107                let first = codegen_static_value(arena, allocator, *first)?;
108                let rest = codegen_static_value(arena, allocator, *rest)?;
109                let pair = allocator.new_pair(first, rest)?;
110                return Ok(clvm_quote!(pair).to_clvm(allocator)?);
111            }
112
113            let first = codegen(arena, allocator, *first)?;
114            let rest = codegen(arena, allocator, *rest)?;
115            Ok(clvm_list!(ClvmOp::Cons, first, rest).to_clvm(allocator)?)
116        }
117        Lir::Listp(arg, _) => {
118            let arg = codegen(arena, allocator, *arg)?;
119            Ok(clvm_list!(ClvmOp::Listp, arg).to_clvm(allocator)?)
120        }
121        Lir::Add(args) => {
122            let args = args
123                .iter()
124                .map(|arg| codegen(arena, allocator, *arg))
125                .collect::<Result<Vec<_>>>()?;
126            Ok((ClvmOp::Add, args).to_clvm(allocator)?)
127        }
128        Lir::Sub(args) => {
129            let args = args
130                .iter()
131                .map(|arg| codegen(arena, allocator, *arg))
132                .collect::<Result<Vec<_>>>()?;
133            Ok((ClvmOp::Sub, args).to_clvm(allocator)?)
134        }
135        Lir::Mul(args) => {
136            let args = args
137                .iter()
138                .map(|arg| codegen(arena, allocator, *arg))
139                .collect::<Result<Vec<_>>>()?;
140            Ok((ClvmOp::Mul, args).to_clvm(allocator)?)
141        }
142        Lir::Div(first, second) => {
143            let first = codegen(arena, allocator, *first)?;
144            let second = codegen(arena, allocator, *second)?;
145            Ok(clvm_list!(ClvmOp::Div, first, second).to_clvm(allocator)?)
146        }
147        Lir::Divmod(first, second) => {
148            let first = codegen(arena, allocator, *first)?;
149            let second = codegen(arena, allocator, *second)?;
150            Ok(clvm_list!(ClvmOp::Divmod, first, second).to_clvm(allocator)?)
151        }
152        Lir::Mod(first, second) => {
153            let first = codegen(arena, allocator, *first)?;
154            let second = codegen(arena, allocator, *second)?;
155            Ok(clvm_list!(ClvmOp::Mod, first, second).to_clvm(allocator)?)
156        }
157        Lir::Modpow(base, exponent, modulus) => {
158            let base = codegen(arena, allocator, *base)?;
159            let exponent = codegen(arena, allocator, *exponent)?;
160            let modulus = codegen(arena, allocator, *modulus)?;
161            Ok(clvm_list!(ClvmOp::Modpow, base, exponent, modulus).to_clvm(allocator)?)
162        }
163        Lir::Eq(first, second) => {
164            let first = codegen(arena, allocator, *first)?;
165            let second = codegen(arena, allocator, *second)?;
166            Ok(clvm_list!(ClvmOp::Eq, first, second).to_clvm(allocator)?)
167        }
168        Lir::Gt(first, second) => {
169            let first = codegen(arena, allocator, *first)?;
170            let second = codegen(arena, allocator, *second)?;
171            Ok(clvm_list!(ClvmOp::Gt, first, second).to_clvm(allocator)?)
172        }
173        Lir::GtBytes(first, second) => {
174            let first = codegen(arena, allocator, *first)?;
175            let second = codegen(arena, allocator, *second)?;
176            Ok(clvm_list!(ClvmOp::GtBytes, first, second).to_clvm(allocator)?)
177        }
178        Lir::Not(arg) => {
179            let arg = codegen(arena, allocator, *arg)?;
180            Ok(clvm_list!(ClvmOp::Not, arg).to_clvm(allocator)?)
181        }
182        Lir::All(args) => {
183            let args = args
184                .iter()
185                .map(|arg| codegen(arena, allocator, *arg))
186                .collect::<Result<Vec<_>>>()?;
187            Ok((ClvmOp::All, args).to_clvm(allocator)?)
188        }
189        Lir::Any(args) => {
190            let args = args
191                .iter()
192                .map(|arg| codegen(arena, allocator, *arg))
193                .collect::<Result<Vec<_>>>()?;
194            Ok((ClvmOp::Any, args).to_clvm(allocator)?)
195        }
196        Lir::If(cond, then_lir, else_lir, inline) => {
197            let cond = codegen(arena, allocator, *cond)?;
198            let then_ptr = codegen(arena, allocator, *then_lir)?;
199            let else_ptr = codegen(arena, allocator, *else_lir)?;
200
201            if *inline {
202                Ok(clvm_list!(ClvmOp::If, cond, then_ptr, else_ptr).to_clvm(allocator)?)
203            } else {
204                Ok(clvm_list!(
205                    ClvmOp::Apply,
206                    clvm_list!(
207                        ClvmOp::If,
208                        cond,
209                        clvm_quote!(then_ptr),
210                        clvm_quote!(else_ptr)
211                    ),
212                    1
213                )
214                .to_clvm(allocator)?)
215            }
216        }
217        Lir::Raise(args) => {
218            let args = args
219                .iter()
220                .map(|arg| codegen(arena, allocator, *arg))
221                .collect::<Result<Vec<_>>>()?;
222            Ok((ClvmOp::Raise, args).to_clvm(allocator)?)
223        }
224        Lir::Concat(args) => {
225            let args = args
226                .iter()
227                .map(|arg| codegen(arena, allocator, *arg))
228                .collect::<Result<Vec<_>>>()?;
229            Ok((ClvmOp::Concat, args).to_clvm(allocator)?)
230        }
231        Lir::Strlen(arg) => {
232            let arg = codegen(arena, allocator, *arg)?;
233            Ok(clvm_list!(ClvmOp::Strlen, arg).to_clvm(allocator)?)
234        }
235        Lir::Substr(arg, start, end) => {
236            let arg = codegen(arena, allocator, *arg)?;
237            let start = codegen(arena, allocator, *start)?;
238            let end = end.map(|end| codegen(arena, allocator, end)).transpose()?;
239            if let Some(end) = end {
240                Ok(clvm_list!(ClvmOp::Substr, arg, start, end).to_clvm(allocator)?)
241            } else {
242                Ok(clvm_list!(ClvmOp::Substr, arg, start).to_clvm(allocator)?)
243            }
244        }
245        Lir::Logand(args) => {
246            let args = args
247                .iter()
248                .map(|arg| codegen(arena, allocator, *arg))
249                .collect::<Result<Vec<_>>>()?;
250            Ok((ClvmOp::Logand, args).to_clvm(allocator)?)
251        }
252        Lir::Logior(args) => {
253            let args = args
254                .iter()
255                .map(|arg| codegen(arena, allocator, *arg))
256                .collect::<Result<Vec<_>>>()?;
257            Ok((ClvmOp::Logior, args).to_clvm(allocator)?)
258        }
259        Lir::Logxor(args) => {
260            let args = args
261                .iter()
262                .map(|arg| codegen(arena, allocator, *arg))
263                .collect::<Result<Vec<_>>>()?;
264            Ok((ClvmOp::Logxor, args).to_clvm(allocator)?)
265        }
266        Lir::Lognot(arg) => {
267            let arg = codegen(arena, allocator, *arg)?;
268            Ok(clvm_list!(ClvmOp::Lognot, arg).to_clvm(allocator)?)
269        }
270        Lir::Ash(arg, shift) => {
271            let arg = codegen(arena, allocator, *arg)?;
272            let shift = codegen(arena, allocator, *shift)?;
273            Ok(clvm_list!(ClvmOp::Ash, arg, shift).to_clvm(allocator)?)
274        }
275        Lir::Lsh(arg, shift) => {
276            let arg = codegen(arena, allocator, *arg)?;
277            let shift = codegen(arena, allocator, *shift)?;
278            Ok(clvm_list!(ClvmOp::Lsh, arg, shift).to_clvm(allocator)?)
279        }
280        Lir::PubkeyForExp(arg) => {
281            let arg = codegen(arena, allocator, *arg)?;
282            Ok(clvm_list!(ClvmOp::PubkeyForExp, arg).to_clvm(allocator)?)
283        }
284        Lir::G1Add(args) => {
285            let args = args
286                .iter()
287                .map(|arg| codegen(arena, allocator, *arg))
288                .collect::<Result<Vec<_>>>()?;
289            Ok((ClvmOp::G1Add, args).to_clvm(allocator)?)
290        }
291        Lir::G1Subtract(args) => {
292            let args = args
293                .iter()
294                .map(|arg| codegen(arena, allocator, *arg))
295                .collect::<Result<Vec<_>>>()?;
296            Ok((ClvmOp::G1Subtract, args).to_clvm(allocator)?)
297        }
298        Lir::G1Multiply(first, second) => {
299            let first = codegen(arena, allocator, *first)?;
300            let second = codegen(arena, allocator, *second)?;
301            Ok(clvm_list!(ClvmOp::G1Multiply, first, second).to_clvm(allocator)?)
302        }
303        Lir::G1Negate(arg) => {
304            let arg = codegen(arena, allocator, *arg)?;
305            Ok(clvm_list!(ClvmOp::G1Negate, arg).to_clvm(allocator)?)
306        }
307        Lir::G1Map(value, dst) => {
308            let value = codegen(arena, allocator, *value)?;
309            let dst = dst.map(|dst| codegen(arena, allocator, dst)).transpose()?;
310            if let Some(dst) = dst {
311                Ok(clvm_list!(ClvmOp::G1Map, value, dst).to_clvm(allocator)?)
312            } else {
313                Ok(clvm_list!(ClvmOp::G1Map, value).to_clvm(allocator)?)
314            }
315        }
316        Lir::G2Add(args) => {
317            let args = args
318                .iter()
319                .map(|arg| codegen(arena, allocator, *arg))
320                .collect::<Result<Vec<_>>>()?;
321            Ok((ClvmOp::G2Add, args).to_clvm(allocator)?)
322        }
323        Lir::G2Subtract(args) => {
324            let args = args
325                .iter()
326                .map(|arg| codegen(arena, allocator, *arg))
327                .collect::<Result<Vec<_>>>()?;
328            Ok((ClvmOp::G2Subtract, args).to_clvm(allocator)?)
329        }
330        Lir::G2Multiply(first, second) => {
331            let first = codegen(arena, allocator, *first)?;
332            let second = codegen(arena, allocator, *second)?;
333            Ok(clvm_list!(ClvmOp::G2Multiply, first, second).to_clvm(allocator)?)
334        }
335        Lir::G2Negate(arg) => {
336            let arg = codegen(arena, allocator, *arg)?;
337            Ok(clvm_list!(ClvmOp::G2Negate, arg).to_clvm(allocator)?)
338        }
339        Lir::G2Map(value, dst) => {
340            let value = codegen(arena, allocator, *value)?;
341            let dst = dst.map(|dst| codegen(arena, allocator, dst)).transpose()?;
342            if let Some(dst) = dst {
343                Ok(clvm_list!(ClvmOp::G2Map, value, dst).to_clvm(allocator)?)
344            } else {
345                Ok(clvm_list!(ClvmOp::G2Map, value).to_clvm(allocator)?)
346            }
347        }
348        Lir::BlsPairingIdentity(args) => {
349            let args = args
350                .iter()
351                .map(|arg| codegen(arena, allocator, *arg))
352                .collect::<Result<Vec<_>>>()?;
353            Ok((ClvmOp::BlsPairingIdentity, args).to_clvm(allocator)?)
354        }
355        Lir::BlsVerify(arg, args) => {
356            let arg = codegen(arena, allocator, *arg)?;
357            let args = args
358                .iter()
359                .map(|arg| codegen(arena, allocator, *arg))
360                .collect::<Result<Vec<_>>>()?;
361            Ok(clvm_tuple!(ClvmOp::BlsVerify, arg, args).to_clvm(allocator)?)
362        }
363        Lir::Sha256(args) | Lir::Sha256Inline(args) => {
364            let args = args
365                .iter()
366                .map(|arg| codegen(arena, allocator, *arg))
367                .collect::<Result<Vec<_>>>()?;
368            Ok((ClvmOp::Sha256, args).to_clvm(allocator)?)
369        }
370        Lir::Keccak256(args) | Lir::Keccak256Inline(args) => {
371            let args = args
372                .iter()
373                .map(|arg| codegen(arena, allocator, *arg))
374                .collect::<Result<Vec<_>>>()?;
375            Ok((ClvmOp::Keccak256, args).to_clvm(allocator)?)
376        }
377        Lir::CoinId(parent, puzzle, amount) => {
378            let parent = codegen(arena, allocator, *parent)?;
379            let puzzle = codegen(arena, allocator, *puzzle)?;
380            let amount = codegen(arena, allocator, *amount)?;
381            Ok(clvm_list!(ClvmOp::CoinId, parent, puzzle, amount).to_clvm(allocator)?)
382        }
383        Lir::K1Verify(pubkey, message, signature) => {
384            let pubkey = codegen(arena, allocator, *pubkey)?;
385            let message = codegen(arena, allocator, *message)?;
386            let signature = codegen(arena, allocator, *signature)?;
387            Ok(
388                clvm_list!(ClvmOp::Secp256K1Verify, pubkey, message, signature)
389                    .to_clvm(allocator)?,
390            )
391        }
392        Lir::R1Verify(pubkey, message, signature) => {
393            let pubkey = codegen(arena, allocator, *pubkey)?;
394            let message = codegen(arena, allocator, *message)?;
395            let signature = codegen(arena, allocator, *signature)?;
396            Ok(
397                clvm_list!(ClvmOp::Secp256R1Verify, pubkey, message, signature)
398                    .to_clvm(allocator)?,
399            )
400        }
401        Lir::Op(op, args) => {
402            let args = codegen(arena, allocator, *args)?;
403            Ok(clvm_list!(
404                ClvmOp::Apply,
405                clvm_list!(
406                    ClvmOp::Cons,
407                    clvm_list!(ClvmOp::Cons, clvm_quote!(op), ()),
408                    args
409                ),
410                ()
411            )
412            .to_clvm(allocator)?)
413        }
414        Lir::DebugPrint(srcloc, value) => {
415            let value = codegen(arena, allocator, *value)?;
416            Ok(clvm_list!(ClvmOp::DebugPrint, clvm_quote!(srcloc), value).to_clvm(allocator)?)
417        }
418    }
419}
420
421fn is_static_value(arena: &Arena<Lir>, lir: LirId) -> bool {
422    match &arena[lir] {
423        Lir::Atom(_) => true,
424        Lir::Cons(first, rest) => is_static_value(arena, *first) && is_static_value(arena, *rest),
425        _ => false,
426    }
427}
428
429fn codegen_static_value(
430    arena: &Arena<Lir>,
431    allocator: &mut Allocator,
432    lir: LirId,
433) -> Result<NodePtr> {
434    match &arena[lir] {
435        Lir::Atom(atom) => {
436            if atom.is_empty() {
437                Ok(NodePtr::NIL)
438            } else {
439                Ok(allocator.new_atom(atom)?)
440            }
441        }
442        Lir::Cons(first, rest) => {
443            let first = codegen_static_value(arena, allocator, *first)?;
444            let rest = codegen_static_value(arena, allocator, *rest)?;
445            Ok(allocator.new_pair(first, rest)?)
446        }
447        _ => unreachable!("static LIR value contained a dynamic expression"),
448    }
449}
450
451#[cfg(test)]
452mod tests {
453    use chialisp::classic::clvm_tools::binutils::disassemble;
454    use expect_test::{Expect, expect};
455
456    use super::*;
457
458    #[allow(clippy::needless_pass_by_value)]
459    fn check(arena: &Arena<Lir>, lir: LirId, expect: Expect) {
460        let mut allocator = Allocator::new();
461        let ptr = codegen(arena, &mut allocator, lir, CodegenOptions::default()).unwrap();
462        let result = disassemble(&allocator, ptr, None);
463        expect.assert_eq(&result);
464    }
465
466    #[allow(clippy::needless_pass_by_value)]
467    fn check_with_options(
468        arena: &Arena<Lir>,
469        lir: LirId,
470        optimize_static_pairs: bool,
471        expect: Expect,
472    ) {
473        let mut allocator = Allocator::new();
474        let ptr = codegen(
475            arena,
476            &mut allocator,
477            lir,
478            CodegenOptions {
479                optimize_static_pairs,
480            },
481        )
482        .unwrap();
483        let result = disassemble(&allocator, ptr, None);
484        expect.assert_eq(&result);
485    }
486
487    #[test]
488    fn test_atom() {
489        let mut arena = Arena::new();
490        let lir = arena.alloc(Lir::Atom(b"hello".to_vec()));
491        check(&arena, lir, expect![[r#"(q . "hello")"#]]);
492    }
493
494    #[test]
495    fn test_nil() {
496        let mut arena = Arena::new();
497        let lir = arena.alloc(Lir::Atom(Vec::new()));
498        check(&arena, lir, expect!["()"]);
499    }
500
501    #[test]
502    fn test_path() {
503        let mut arena = Arena::new();
504        let lir = arena.alloc(Lir::Path(1u8.into()));
505        check(&arena, lir, expect!["1"]);
506    }
507
508    #[test]
509    fn test_quote() {
510        let mut arena = Arena::new();
511        let lir = arena.alloc(Lir::Atom(b"hello".to_vec()));
512        let quote = arena.alloc(Lir::Quote(lir));
513        check(&arena, quote, expect![[r#"(q 1 . "hello")"#]]);
514    }
515
516    #[test]
517    fn test_run() {
518        let mut arena = Arena::new();
519        let a = arena.alloc(Lir::Atom(b"a".to_vec()));
520        let b = arena.alloc(Lir::Atom(b"b".to_vec()));
521        let c = arena.alloc(Lir::Atom(b"c".to_vec()));
522        let mut env = arena.alloc(Lir::Atom(Vec::new()));
523        for &arg in [b, c].iter().rev() {
524            env = arena.alloc(Lir::Cons(arg, env));
525        }
526        let lir = arena.alloc(Lir::Run(a, env));
527        check(
528            &arena,
529            lir,
530            expect!["(a (q . 97) (c (q . 98) (c (q . 99) ())))"],
531        );
532    }
533
534    #[test]
535    fn test_run_static_program_with_entire_env() {
536        let mut arena = Arena::new();
537        let nil = arena.alloc(Lir::Atom(Vec::new()));
538        let five = arena.alloc(Lir::Atom(vec![5]));
539        let args = arena.alloc(Lir::Cons(five, nil));
540        let two = arena.alloc(Lir::Atom(vec![2]));
541        let args = arena.alloc(Lir::Cons(two, args));
542        let add = arena.alloc(Lir::Atom(vec![16]));
543        let program = arena.alloc(Lir::Cons(add, args));
544        let env = arena.alloc(Lir::Path(1u8.into()));
545        let lir = arena.alloc(Lir::Run(program, env));
546
547        check_with_options(
548            &arena,
549            lir,
550            false,
551            expect!["(a (c (q . 16) (c (q . 2) (c (q . 5) ()))) 1)"],
552        );
553        check_with_options(&arena, lir, true, expect!["(+ 2 5)"]);
554    }
555
556    #[test]
557    fn test_closure() {
558        let mut arena = Arena::new();
559        let path = arena.alloc(Lir::Path(2u8.into()));
560        let add = arena.alloc(Lir::Add(vec![path, path]));
561        let function = arena.alloc(Lir::Quote(add));
562        let capture = arena.alloc(Lir::Atom(vec![0x10]));
563        let closure = arena.alloc(Lir::Closure(function, vec![capture], true));
564        check(
565            &arena,
566            closure,
567            expect![
568                "(c (q . 2) (c (c (q . 1) (q 16 2 2)) (c (c (q . 4) (c (c (q . 1) (q . 16)) (c (q . 1) ()))) ())))"
569            ],
570        );
571    }
572
573    #[test]
574    fn test_first() {
575        let mut arena = Arena::new();
576        let first = arena.alloc(Lir::Atom(b"first".to_vec()));
577        let rest = arena.alloc(Lir::Atom(b"rest".to_vec()));
578        let pair = arena.alloc(Lir::Cons(first, rest));
579        let lir = arena.alloc(Lir::First(pair));
580        check(
581            &arena,
582            lir,
583            expect![[r#"(f (c (q . "first") (q . "rest")))"#]],
584        );
585    }
586
587    #[test]
588    fn test_rest() {
589        let mut arena = Arena::new();
590        let first = arena.alloc(Lir::Atom(b"first".to_vec()));
591        let rest = arena.alloc(Lir::Atom(b"rest".to_vec()));
592        let pair = arena.alloc(Lir::Cons(first, rest));
593        let lir = arena.alloc(Lir::Rest(pair));
594        check(
595            &arena,
596            lir,
597            expect![[r#"(r (c (q . "first") (q . "rest")))"#]],
598        );
599    }
600
601    #[test]
602    fn test_cons() {
603        let mut arena = Arena::new();
604        let first = arena.alloc(Lir::Atom(b"first".to_vec()));
605        let rest = arena.alloc(Lir::Atom(b"rest".to_vec()));
606        let lir = arena.alloc(Lir::Cons(first, rest));
607        check(&arena, lir, expect![[r#"(c (q . "first") (q . "rest"))"#]]);
608        check_with_options(&arena, lir, true, expect![[r#"(q "first" . "rest")"#]]);
609
610        let path = arena.alloc(Lir::Path(2u8.into()));
611        let lir = arena.alloc(Lir::Cons(path, lir));
612        check_with_options(
613            &arena,
614            lir,
615            true,
616            expect![[r#"(c 2 (q "first" . "rest"))"#]],
617        );
618    }
619
620    #[test]
621    fn test_listp() {
622        let mut arena = Arena::new();
623        let value = arena.alloc(Lir::Atom(b"value".to_vec()));
624        let lir = arena.alloc(Lir::Listp(value, true));
625        check(&arena, lir, expect![[r#"(l (q . "value"))"#]]);
626    }
627
628    #[test]
629    fn test_add() {
630        let mut arena = Arena::new();
631        let a = arena.alloc(Lir::Atom(vec![0x01]));
632        let b = arena.alloc(Lir::Atom(vec![0x02]));
633        let lir = arena.alloc(Lir::Add(vec![a, b]));
634        check(&arena, lir, expect!["(+ (q . 1) (q . 2))"]);
635    }
636
637    #[test]
638    fn test_sub() {
639        let mut arena = Arena::new();
640        let a = arena.alloc(Lir::Atom(vec![0x01]));
641        let b = arena.alloc(Lir::Atom(vec![0x02]));
642        let lir = arena.alloc(Lir::Sub(vec![a, b]));
643        check(&arena, lir, expect!["(- (q . 1) (q . 2))"]);
644    }
645
646    #[test]
647    fn test_mul() {
648        let mut arena = Arena::new();
649        let a = arena.alloc(Lir::Atom(vec![0x01]));
650        let b = arena.alloc(Lir::Atom(vec![0x02]));
651        let lir = arena.alloc(Lir::Mul(vec![a, b]));
652        check(&arena, lir, expect!["(* (q . 1) (q . 2))"]);
653    }
654
655    #[test]
656    fn test_div() {
657        let mut arena = Arena::new();
658        let a = arena.alloc(Lir::Atom(vec![0x01]));
659        let b = arena.alloc(Lir::Atom(vec![0x02]));
660        let lir = arena.alloc(Lir::Div(a, b));
661        check(&arena, lir, expect!["(/ (q . 1) (q . 2))"]);
662    }
663
664    #[test]
665    fn test_divmod() {
666        let mut arena = Arena::new();
667        let a = arena.alloc(Lir::Atom(vec![0x34]));
668        let b = arena.alloc(Lir::Atom(vec![0x07]));
669        let lir = arena.alloc(Lir::Divmod(a, b));
670        check(&arena, lir, expect!["(divmod (q . 52) (q . 7))"]);
671    }
672
673    #[test]
674    fn test_mod() {
675        let mut arena = Arena::new();
676        let a = arena.alloc(Lir::Atom(vec![0x34]));
677        let b = arena.alloc(Lir::Atom(vec![0x07]));
678        let lir = arena.alloc(Lir::Mod(a, b));
679        check(&arena, lir, expect!["(% (q . 52) (q . 7))"]);
680    }
681
682    #[test]
683    fn test_modpow() {
684        let mut arena = Arena::new();
685        let a = arena.alloc(Lir::Atom(vec![0x34]));
686        let b = arena.alloc(Lir::Atom(vec![0x07]));
687        let c = arena.alloc(Lir::Atom(vec![0x03]));
688        let lir = arena.alloc(Lir::Modpow(a, b, c));
689        check(&arena, lir, expect!["(modpow (q . 52) (q . 7) (q . 3))"]);
690    }
691
692    #[test]
693    fn test_eq() {
694        let mut arena = Arena::new();
695        let a = arena.alloc(Lir::Atom(vec![0x01]));
696        let b = arena.alloc(Lir::Atom(vec![0x02]));
697        let lir = arena.alloc(Lir::Eq(a, b));
698        check(&arena, lir, expect!["(= (q . 1) (q . 2))"]);
699    }
700
701    #[test]
702    fn test_gt() {
703        let mut arena = Arena::new();
704        let a = arena.alloc(Lir::Atom(vec![0x01]));
705        let b = arena.alloc(Lir::Atom(vec![0x02]));
706        let lir = arena.alloc(Lir::Gt(a, b));
707        check(&arena, lir, expect!["(> (q . 1) (q . 2))"]);
708    }
709
710    #[test]
711    fn test_not() {
712        let mut arena = Arena::new();
713        let a = arena.alloc(Lir::Atom(vec![0x01]));
714        let lir = arena.alloc(Lir::Not(a));
715        check(&arena, lir, expect!["(not (q . 1))"]);
716    }
717
718    #[test]
719    fn test_all() {
720        let mut arena = Arena::new();
721        let a = arena.alloc(Lir::Atom(vec![0x01]));
722        let b = arena.alloc(Lir::Atom(vec![0x02]));
723        let lir = arena.alloc(Lir::All(vec![a, b]));
724        check(&arena, lir, expect!["(all (q . 1) (q . 2))"]);
725    }
726
727    #[test]
728    fn test_any() {
729        let mut arena = Arena::new();
730        let a = arena.alloc(Lir::Atom(vec![0x01]));
731        let b = arena.alloc(Lir::Atom(vec![0x02]));
732        let lir = arena.alloc(Lir::Any(vec![a, b]));
733        check(&arena, lir, expect!["(any (q . 1) (q . 2))"]);
734    }
735
736    #[test]
737    fn test_if() {
738        let mut arena = Arena::new();
739        let cond = arena.alloc(Lir::Atom(vec![0x01]));
740        let then = arena.alloc(Lir::Atom(vec![0x02]));
741        let else_ = arena.alloc(Lir::Atom(vec![0x03]));
742        let lir = arena.alloc(Lir::If(cond, then, else_, false));
743        check(
744            &arena,
745            lir,
746            expect!["(a (i (q . 1) (q 1 . 2) (q 1 . 3)) 1)"],
747        );
748    }
749
750    #[test]
751    fn test_raise() {
752        let mut arena = Arena::new();
753        let a = arena.alloc(Lir::Atom(vec![0x01]));
754        let lir = arena.alloc(Lir::Raise(vec![a]));
755        check(&arena, lir, expect!["(x (q . 1))"]);
756    }
757
758    #[test]
759    fn test_gt_bytes() {
760        let mut arena = Arena::new();
761        let a = arena.alloc(Lir::Atom(b"abc".to_vec()));
762        let b = arena.alloc(Lir::Atom(b"def".to_vec()));
763        let lir = arena.alloc(Lir::GtBytes(a, b));
764        check(&arena, lir, expect![[r#"(>s (q . "abc") (q . "def"))"#]]);
765    }
766
767    #[test]
768    fn test_concat() {
769        let mut arena = Arena::new();
770        let a = arena.alloc(Lir::Atom(b"hello ".to_vec()));
771        let b = arena.alloc(Lir::Atom(b"world".to_vec()));
772        let lir = arena.alloc(Lir::Concat(vec![a, b]));
773        check(
774            &arena,
775            lir,
776            expect![[r#"(concat (q . "hello ") (q . "world"))"#]],
777        );
778    }
779
780    #[test]
781    fn test_strlen() {
782        let mut arena = Arena::new();
783        let a = arena.alloc(Lir::Atom(b"hello".to_vec()));
784        let lir = arena.alloc(Lir::Strlen(a));
785        check(&arena, lir, expect![[r#"(strlen (q . "hello"))"#]]);
786    }
787
788    #[test]
789    fn test_substr() {
790        let mut arena = Arena::new();
791        let str = arena.alloc(Lir::Atom(b"hello world".to_vec()));
792        let start = arena.alloc(Lir::Atom(vec![]));
793        let end = arena.alloc(Lir::Atom(vec![0x05]));
794        let lir = arena.alloc(Lir::Substr(str, start, Some(end)));
795        check(
796            &arena,
797            lir,
798            expect![[r#"(substr (q . "hello world") () (q . 5))"#]],
799        );
800
801        // Test without end parameter
802        let lir = arena.alloc(Lir::Substr(str, start, None));
803        check(&arena, lir, expect![[r#"(substr (q . "hello world") ())"#]]);
804    }
805
806    #[test]
807    fn test_bitwise_ops() {
808        let mut arena = Arena::new();
809        let a = arena.alloc(Lir::Atom(vec![0x0F]));
810        let b = arena.alloc(Lir::Atom(vec![0xF0]));
811
812        let logand = arena.alloc(Lir::Logand(vec![a, b]));
813        check(&arena, logand, expect!["(logand (q . 15) (q . -16))"]);
814
815        let logior = arena.alloc(Lir::Logior(vec![a, b]));
816        check(&arena, logior, expect!["(logior (q . 15) (q . -16))"]);
817
818        let logxor = arena.alloc(Lir::Logxor(vec![a, b]));
819        check(&arena, logxor, expect!["(logxor (q . 15) (q . -16))"]);
820
821        let lognot = arena.alloc(Lir::Lognot(a));
822        check(&arena, lognot, expect!["(lognot (q . 15))"]);
823    }
824
825    #[test]
826    fn test_shifts() {
827        let mut arena = Arena::new();
828        let value = arena.alloc(Lir::Atom(vec![0x0F]));
829        let shift = arena.alloc(Lir::Atom(vec![0x02]));
830
831        let ash = arena.alloc(Lir::Ash(value, shift));
832        check(&arena, ash, expect!["(ash (q . 15) (q . 2))"]);
833
834        let lsh = arena.alloc(Lir::Lsh(value, shift));
835        check(&arena, lsh, expect!["(lsh (q . 15) (q . 2))"]);
836    }
837
838    #[test]
839    fn test_pubkey_for_exp() {
840        let mut arena = Arena::new();
841        let exp = arena.alloc(Lir::Atom(vec![0x01]));
842        let lir = arena.alloc(Lir::PubkeyForExp(exp));
843        check(&arena, lir, expect!["(pubkey_for_exp (q . 1))"]);
844    }
845
846    #[test]
847    fn test_g1_ops() {
848        let mut arena = Arena::new();
849        let a = arena.alloc(Lir::Atom(vec![0x01]));
850        let b = arena.alloc(Lir::Atom(vec![0x02]));
851
852        let add = arena.alloc(Lir::G1Add(vec![a, b]));
853        check(&arena, add, expect!["(point_add (q . 1) (q . 2))"]);
854
855        let sub = arena.alloc(Lir::G1Subtract(vec![a, b]));
856        check(&arena, sub, expect!["(g1_subtract (q . 1) (q . 2))"]);
857
858        let mul = arena.alloc(Lir::G1Multiply(a, b));
859        check(&arena, mul, expect!["(g1_multiply (q . 1) (q . 2))"]);
860
861        let neg = arena.alloc(Lir::G1Negate(a));
862        check(&arena, neg, expect!["(g1_negate (q . 1))"]);
863
864        let map = arena.alloc(Lir::G1Map(a, Some(b)));
865        check(&arena, map, expect!["(g1_map (q . 1) (q . 2))"]);
866
867        let map_no_dst = arena.alloc(Lir::G1Map(a, None));
868        check(&arena, map_no_dst, expect!["(g1_map (q . 1))"]);
869    }
870
871    #[test]
872    fn test_g2_ops() {
873        let mut arena = Arena::new();
874        let a = arena.alloc(Lir::Atom(vec![0x01]));
875        let b = arena.alloc(Lir::Atom(vec![0x02]));
876
877        let add = arena.alloc(Lir::G2Add(vec![a, b]));
878        check(&arena, add, expect!["(g2_add (q . 1) (q . 2))"]);
879
880        let sub = arena.alloc(Lir::G2Subtract(vec![a, b]));
881        check(&arena, sub, expect!["(g2_subtract (q . 1) (q . 2))"]);
882
883        let mul = arena.alloc(Lir::G2Multiply(a, b));
884        check(&arena, mul, expect!["(g2_multiply (q . 1) (q . 2))"]);
885
886        let neg = arena.alloc(Lir::G2Negate(a));
887        check(&arena, neg, expect!["(g2_negate (q . 1))"]);
888
889        let map = arena.alloc(Lir::G2Map(a, Some(b)));
890        check(&arena, map, expect!["(g2_map (q . 1) (q . 2))"]);
891
892        let map_no_dst = arena.alloc(Lir::G2Map(a, None));
893        check(&arena, map_no_dst, expect!["(g2_map (q . 1))"]);
894    }
895
896    #[test]
897    fn test_bls_ops() {
898        let mut arena = Arena::new();
899        let a = arena.alloc(Lir::Atom(vec![0x01]));
900        let b = arena.alloc(Lir::Atom(vec![0x02]));
901
902        let identity = arena.alloc(Lir::BlsPairingIdentity(vec![a, b]));
903        check(
904            &arena,
905            identity,
906            expect!["(bls_pairing_identity (q . 1) (q . 2))"],
907        );
908
909        let verify = arena.alloc(Lir::BlsVerify(a, vec![b]));
910        check(&arena, verify, expect!["(bls_verify (q . 1) (q . 2))"]);
911    }
912
913    #[test]
914    fn test_hash_functions() {
915        let mut arena = Arena::new();
916        let a = arena.alloc(Lir::Atom(b"hello".to_vec()));
917        let b = arena.alloc(Lir::Atom(b"world".to_vec()));
918
919        let sha256 = arena.alloc(Lir::Sha256(vec![a, b]));
920        check(
921            &arena,
922            sha256,
923            expect![[r#"(sha256 (q . "hello") (q . "world"))"#]],
924        );
925
926        let keccak256 = arena.alloc(Lir::Keccak256(vec![a, b]));
927        check(
928            &arena,
929            keccak256,
930            expect![[r#"(keccak256 (q . "hello") (q . "world"))"#]],
931        );
932    }
933
934    #[test]
935    fn test_coin_id() {
936        let mut arena = Arena::new();
937        let parent = arena.alloc(Lir::Atom(vec![0x01]));
938        let puzzle = arena.alloc(Lir::Atom(vec![0x02]));
939        let amount = arena.alloc(Lir::Atom(vec![0x03]));
940        let lir = arena.alloc(Lir::CoinId(parent, puzzle, amount));
941        check(&arena, lir, expect!["(coinid (q . 1) (q . 2) (q . 3))"]);
942    }
943
944    #[test]
945    fn test_signature_verification() {
946        let mut arena = Arena::new();
947        let pubkey = arena.alloc(Lir::Atom(vec![0x01]));
948        let message = arena.alloc(Lir::Atom(vec![0x02]));
949        let signature = arena.alloc(Lir::Atom(vec![0x03]));
950
951        let k1_verify = arena.alloc(Lir::K1Verify(pubkey, message, signature));
952        check(
953            &arena,
954            k1_verify,
955            expect!["(0x13d61f00 (q . 1) (q . 2) (q . 3))"],
956        );
957
958        let r1_verify = arena.alloc(Lir::R1Verify(pubkey, message, signature));
959        check(
960            &arena,
961            r1_verify,
962            expect!["(0x1c3a8f00 (q . 1) (q . 2) (q . 3))"],
963        );
964    }
965}