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 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}