From 8769a852d47875e8edb49853baf35f71f1498fa4 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 6 Jul 2024 09:48:20 -0400 Subject: [PATCH 1/3] Optimize LIR --- crates/rue-compiler/src/optimizer.rs | 65 +++++++++++++-- tests.toml | 116 +++++++++++++-------------- 2 files changed, 118 insertions(+), 63 deletions(-) diff --git a/crates/rue-compiler/src/optimizer.rs b/crates/rue-compiler/src/optimizer.rs index 919d4aa..4d3204f 100644 --- a/crates/rue-compiler/src/optimizer.rs +++ b/crates/rue-compiler/src/optimizer.rs @@ -229,7 +229,18 @@ impl<'a> Optimizer<'a> { fn opt_add(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { let lhs = self.opt_mir(env_id, lhs); let rhs = self.opt_mir(env_id, rhs); - self.db.alloc_lir(Lir::Add(vec![lhs, rhs])) + let mut args = Vec::new(); + if let Lir::Add(lhs) = self.db.lir(lhs) { + args.extend(lhs); + } else { + args.push(lhs); + } + if let Lir::Add(rhs) = self.db.lir(rhs) { + args.extend(rhs); + } else { + args.push(rhs); + } + self.db.alloc_lir(Lir::Add(args)) } fn opt_subtract(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { @@ -241,7 +252,18 @@ impl<'a> Optimizer<'a> { fn opt_multiply(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { let lhs = self.opt_mir(env_id, lhs); let rhs = self.opt_mir(env_id, rhs); - self.db.alloc_lir(Lir::Mul(vec![lhs, rhs])) + let mut args = Vec::new(); + if let Lir::Mul(lhs) = self.db.lir(lhs) { + args.extend(lhs); + } else { + args.push(lhs); + } + if let Lir::Mul(rhs) = self.db.lir(rhs) { + args.extend(rhs); + } else { + args.push(rhs); + } + self.db.alloc_lir(Lir::Mul(args)) } fn opt_divide(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { @@ -316,7 +338,18 @@ impl<'a> Optimizer<'a> { fn opt_concat(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { let lhs = self.opt_mir(env_id, lhs); let rhs = self.opt_mir(env_id, rhs); - self.db.alloc_lir(Lir::Concat(vec![lhs, rhs])) + let mut args = Vec::new(); + if let Lir::Concat(lhs) = self.db.lir(lhs) { + args.extend(lhs); + } else { + args.push(lhs); + } + if let Lir::Concat(rhs) = self.db.lir(rhs) { + args.extend(rhs); + } else { + args.push(rhs); + } + self.db.alloc_lir(Lir::Concat(args)) } fn opt_point_add(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { @@ -338,13 +371,35 @@ impl<'a> Optimizer<'a> { fn opt_any(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { let lhs = self.opt_mir(env_id, lhs); let rhs = self.opt_mir(env_id, rhs); - self.db.alloc_lir(Lir::Any(vec![lhs, rhs])) + let mut args = Vec::new(); + if let Lir::Any(lhs) = self.db.lir(lhs) { + args.extend(lhs); + } else { + args.push(lhs); + } + if let Lir::Any(rhs) = self.db.lir(rhs) { + args.extend(rhs); + } else { + args.push(rhs); + } + self.db.alloc_lir(Lir::Any(args)) } fn opt_all(&mut self, env_id: EnvironmentId, lhs: MirId, rhs: MirId) -> LirId { let lhs = self.opt_mir(env_id, lhs); let rhs = self.opt_mir(env_id, rhs); - self.db.alloc_lir(Lir::All(vec![lhs, rhs])) + let mut args = Vec::new(); + if let Lir::All(lhs) = self.db.lir(lhs) { + args.extend(lhs); + } else { + args.push(lhs); + } + if let Lir::All(rhs) = self.db.lir(rhs) { + args.extend(rhs); + } else { + args.push(rhs); + } + self.db.alloc_lir(Lir::All(args)) } fn opt_not(&mut self, env_id: EnvironmentId, value: MirId) -> LirId { diff --git a/tests.toml b/tests.toml index 813da72..f5aa17d 100644 --- a/tests.toml +++ b/tests.toml @@ -10,11 +10,11 @@ output = "120" hash = "e287a4cdf0ee9e003ee4450c1408d162e08db92a141035bf3ff932d46ca87f93" [const_multiple_reference] -bytes = 77 -cost = 2805 +bytes = 65 +cost = 1672 input = "()" output = "\"Hello, Bob!\"" -hash = "ad1d14ebd66b8fad929ee4b816aa51410d12f987bbeabffd571d0701fe061281" +hash = "3e851b5ad6b5c395bd46219991d2c7bb113e91918a065c705cc9068445bcc2a7" [inline_function_cycle] parser_errors = [] @@ -32,11 +32,11 @@ parser_errors = [] compiler_errors = ["Error: Cannot recursively call inline function. (5:27)"] [mixed_consts] -bytes = 43 -cost = 2144 +bytes = 39 +cost = 1711 input = "()" output = "126" -hash = "834c349efb47a4863d393d9266c217f5b2ab0da53d1b4cd03b1df43ac19ca85f" +hash = "14870051d753aa4295466e166782fb5da7f18ad450a0ed0af9d7ccb83a82bed9" [inline_const_function_cycle] parser_errors = [] @@ -98,11 +98,11 @@ output = "0x288b694028" hash = "0f74e7fd57bb9a7e05cf979ee986a8e0120801d2b18019c275a9717fce22f379" [struct_optional_initializer] -bytes = 323 -cost = 50921 +bytes = 319 +cost = 43841 input = "()" output = "()" -hash = "7346d21032e40d7c691f7411e3d115810f167d1985389d65acf581670c230d0e" +hash = "6e01e2f4d94692686fd7696643431390d821103b211ae74e23def8b42031be97" [struct_single_optional] bytes = 83 @@ -119,11 +119,11 @@ output = "()" hash = "9d76d68d33ab457ba1482630892e687c2a062b6081d5a386b344696908bab9e8" [struct_optional] -bytes = 127 -cost = 5030 +bytes = 123 +cost = 4597 input = "()" output = "221" -hash = "cca28c8cd08bf5e84d1b51883b045c1018acc21064c8f82c5d0c223f5e1039a8" +hash = "f74e8a1315435c77b0677225e1e68bd3a92ba7048b1f9946b7cf9a1c723d787b" [struct] bytes = 37 @@ -140,11 +140,11 @@ output = "76" hash = "917b0e1ee2c8ad5755e1f97c4642ea653288acf816eee1b0d537dd8e01106711" [block_const] -bytes = 101 -cost = 4468 +bytes = 97 +cost = 4022 input = "()" output = "1168" -hash = "9417e13c05b85934e8a305f36ccc83bf55fec11978461c6ebb630b739f837c43" +hash = "09949696fa19e25b6dd9e208fd55fd96d22298930ea41668b1276d5280d67b43" [block_nested_raise] bytes = 21 @@ -155,11 +155,11 @@ hash = "6fdd7c92cc7f9e42a53e07e6123bb5267cc5cc08912d5cb8ed26925aa6fd9672" error = "\"Explicit raise\"" [block_let] -bytes = 47 -cost = 3460 +bytes = 39 +cost = 2581 input = "()" output = "1168" -hash = "3070c823a9d006d346744228f842e6f86692158d6afa8705d01a4be322a8e751" +hash = "4d0b3e7905efc3108e842985d16a05f6284e177cd8075c182ab11b99ba716f18" [block_raise] bytes = 21 @@ -195,11 +195,11 @@ output = "84" hash = "1a6d758fe06cbcd5d1911a5419c1308b52fc69c95872483506e28f6348b08a44" [block_inline_const] -bytes = 37 -cost = 3150 +bytes = 25 +cost = 1838 input = "()" output = "1168" -hash = "11efffdca3fc8786b6e51cc51f13a52e02fc6af181c5b7019424251ae785bd00" +hash = "6636275e009c26ccaba0bde1f76d4e1451f57462b1d2528a3a5df0a3202c1f78" [block_nested_return] parser_errors = [] @@ -227,18 +227,18 @@ output = "()" hash = "a904e8177e07f7f7c2454b8cb53017f85181b0b7841ba9275950e4d6151c9b84" [enum_mixed] -bytes = 279 -cost = 33360 +bytes = 275 +cost = 29112 input = "()" output = "()" -hash = "0af9a22b05c1ceb929e756159fb1c9dffbbc725179e6669ab4c00e712e0008ab" +hash = "951c93c0f57cdebc0c526b69556d6414c62d03be39bad27b08f27bddae98744d" [enum_empty] -bytes = 177 -cost = 12614 +bytes = 173 +cost = 11198 input = "()" output = "()" -hash = "b1a579071d1400cffb70369511e553d9ece5c6966bb03c5b19cd48bde773c8d5" +hash = "600c2c11a7ceb22fd3f5d2559d22120f9ad7e8d47e0966ddcfbcce20e02f373f" [function_call] parser_errors = [] @@ -279,11 +279,11 @@ compiler_errors = [ ] [struct_empty] -bytes = 165 +bytes = 161 cost = 4104 input = "()" output = "()" -hash = "bb2f6e4150d64965b692025d779786e459fc00ab5f7a6cf748315c5d470d64e6" +hash = "bf0ebac819c080030caeaaff2dde6b8382e18c39c30949a382368520e7026771" [birecursive_functions] bytes = 195 @@ -293,11 +293,11 @@ output = "()" hash = "770bb2c0b2582924adf403e62374f8424a2ed510eef70b5f450eccab238a4911" [inner_inline_functions] -bytes = 329 -cost = 16899 +bytes = 301 +cost = 15121 input = "()" output = "0x0a37e3fbe2" -hash = "921423faa6da77bee705fcefd72b1a48d260fd74f197a387c39168e2438cdc8b" +hash = "d0518a7a1bb65cb587cd00b19dc487cf8aae4950fdeea6a4e8c163962fd6ad81" [infer_generic_lambda] bytes = 65 @@ -379,25 +379,25 @@ output = "152" hash = "38b1cec180a0bc0f5ec91097cec51971df126e3b18af54ddba4a3e4a36f9c285" [std] -bytes = 832 -cost = 91299 +bytes = 828 +cost = 82803 input = "()" output = "()" -hash = "7eab50734f88cbad6a3794be152d314496d7ae932f39947f34a7884ab1492f67" +hash = "962d75284aa716681f671c5deac36f613c51b117f7625f3ece1310b590b79b34" [literal_bytes] -bytes = 371 -cost = 17954 +bytes = 367 +cost = 15830 input = "()" output = "0xf1fb6ca14066f4236b3cc44a79a78fe90d44f1d45e04f9b9939863db71b99560" -hash = "da23d6cdd53ce5a6807d40aa0a73b8d2d748a0e8684394e94681c00708f3e660" +hash = "14c7dd45e2f1dba24d5941e5b5ca95eb44dcaee423bf45a8edf8f8a1737ae674" [concat_bytes] -bytes = 42 -cost = 2500 +bytes = 30 +cost = 1159 input = "()" output = "\"Hello, world!\"" -hash = "6da79c81642d2949a1ca049098cd148cfb1ec7444a86cd6392fb46faf6e76391" +hash = "d30484c7f6402b6fe4ab2874dd328ff0c5385394e68d9743cc29ea36405c6961" [integer_separator] bytes = 6 @@ -449,11 +449,11 @@ output = "0x064cc0" hash = "96a314da556b577d15a2e0a2db42e3b7f22610ab4336ed86713716c5a029e787" [boolean_arithmetic] -bytes = 55 -cost = 4977 +bytes = 47 +cost = 3975 input = "()" output = "1" -hash = "bc9d13262f5a2ab4c0e75a62ade773d493a624e05248c596a58bd85d8466c68f" +hash = "7c0bf0b36503c7abd7e264b2d63cc829162cd1fdff59d1f34792cc043a24e9a5" [bitwise_shift] bytes = 41 @@ -493,18 +493,18 @@ compiler_errors = [ ] [p2_fusion] -bytes = 722 -cost = 87151 +bytes = 678 +cost = 69048 input = "((0x4696e7a2b7682e2df01ab47e6e002d0dca895f99c6172e4a55a3e033499532b7 0x32ed6e4964102d7093ef350019dfd14c99a3ea9feac1f3502194384b8976f7c1 0x770bb2c0b2582924adf403e62374f8424a2ed510eef70b5f450eccab238a4911) 0x8d496dc7cdbc417db2132eda6894b29a91511f176e93a1ea943d210cd27822b2 0x3878fc2ed6b703c7c3d00f9f5d8f170ec252ca439be6e5b5b516e1dce1adc0d7 0x3fe4d62a7db919b25377cb207f16fa0fb6519e123366eaa23014dd5d7c967ca2 0x837525fb91b88dbf02d9ef5a0b322f6943f93424b6e8fe6d26dd1be6d3311871 1 0xc19110971a0cea01368f0c7599b8984f74e301b7cda20bd7f86eb2296bbf16c5)" output = "((73 1) (72 0x6155f55414a5cd9193bef33744095a78d57852cf24241b25edeffad6e544c499) (63 0x9d6824bfdfb4c726334a210b657a5e4126c3fbb378598bb3a5b7a210bb75cdb8) (g1_negate 0xc19110971a0cea01368f0c7599b8984f74e301b7cda20bd7f86eb2296bbf16c5 1 (0xc19110971a0cea01368f0c7599b8984f74e301b7cda20bd7f86eb2296bbf16c5)) (60 0x81fdd3fbc407906bc0875522e7a2e77409ee5ef17d3eaa611b505b344588f6b6))" -hash = "1f6abfdd1000d9ed87740ca5a0a888a042d64a8a7b6d60f082aab5be1f0915b4" +hash = "2bd56215c0c88b31c1d488c5080ecf4a131a0bf52c99bb222869da2451ad7540" [multisig] -bytes = 489 -cost = 27062 +bytes = 485 +cost = 24230 input = "((0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) 1 (()) ((51 0x0000000000000000000000000000000000000000000000000000000000000000 1000)))" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x502cea601814d49886e0713402860d6054f6947d811b3b93100c6b364626651a) (g1_negate 0x0000000000000000000000000000000000000000000000000000000000000000 1000))" -hash = "173385b87af5d8940767c328026fe5f8e76bc238d2a3aaddf4f55e844f400fca" +hash = "cc57ebae1e4370a56127589fa2808a5be775dc801b056cb60dbc950010cb0da4" [hello_world] bytes = 16 @@ -535,25 +535,25 @@ output = "((60 36) (73 0x0186a0) (g1_negate 0x42840c6aebec47ce2e01629ce381b461c1 hash = "7eb7bdb24d4069d7dc9787e7727ef4f0feef354712b1963667d475065122b9de" [p2_conditions] -bytes = 145 -cost = 18933 +bytes = 141 +cost = 16101 input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000)))" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0xc239bbcfc69fb5abacdd8dc174c6b33170c6d902dec3bc1c87662020cf044313) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" -hash = "e69958bce8b4294e16e9b54f6f6795228fbd6daa47ff27ab58a953c611367be2" +hash = "9bdda9e1aa36d402104334fad3d7d9ce5b7b507b92f985e24c21513a012f60c4" [p2_delegated_or_hidden] -bytes = 253 -cost = 24446 +bytes = 249 +cost = 20906 input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q . ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x3d7e1145b5969c12f4889f4f1f66bde0e4d3ba54b91784cf604294d162b44b69) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" -hash = "9b1c580707ca8282534c02c1a055427e0954818b6195a29f4442ac3e7ea8e8ee" +hash = "21f96d7bb1b15b83ce81dff3525d4c98793f906f6cc7ebba52a76524a7db6943" [singleton] -bytes = 1427 +bytes = 1395 cost = 0 input = "((0x42840c6aebec47ce2e01629ce381b461c19695264281a7b1aab5d4ff54506775 0x4696e7a2b7682e2df01ab47e6e002d0dca895f99c6172e4a55a3e033499532b7 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8) 1 (0x9b1c580707ca8282534c02c1a055427e0954818b6195a29f4442ac3e7ea8e8ee () 1) 1 ((51 0x173385b87af5d8940767c328026fe5f8e76bc238d2a3aaddf4f55e844f400fca 1)))" output = "()" -hash = "ca8bea1d975df6e382a60ef70f92d14de0eca974c548175adc6ee8c79597f91b" +hash = "37a397e01acadf7d7dc4266250055e482b4bcbd8f62469c9794b0e433983c8a0" error = "()" [enum_type_guard] From bf279af62100240938d28e602fe47e10e5500e92 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 6 Jul 2024 12:36:53 -0400 Subject: [PATCH 2/3] Cat example --- crates/rue-compiler/src/codegen.rs | 10 + crates/rue-compiler/src/compiler/builtins.rs | 30 ++ .../src/compiler/expr/guard_expr.rs | 17 ++ crates/rue-compiler/src/database.rs | 6 + crates/rue-compiler/src/dependency_graph.rs | 10 + crates/rue-compiler/src/hir.rs | 1 + crates/rue-compiler/src/lir.rs | 1 + crates/rue-compiler/src/lowerer.rs | 6 + crates/rue-compiler/src/mir.rs | 1 + crates/rue-compiler/src/optimizer.rs | 6 + crates/rue-compiler/stdlib.rue | 5 +- examples/cat.rue | 272 ++++++++++++++++++ tests.toml | 14 +- 13 files changed, 372 insertions(+), 7 deletions(-) create mode 100644 examples/cat.rue diff --git a/crates/rue-compiler/src/codegen.rs b/crates/rue-compiler/src/codegen.rs index e05101c..c3d803d 100644 --- a/crates/rue-compiler/src/codegen.rs +++ b/crates/rue-compiler/src/codegen.rs @@ -24,6 +24,7 @@ struct Ops { eq: NodePtr, gt_bytes: NodePtr, sha256: NodePtr, + substr: NodePtr, strlen: NodePtr, concat: NodePtr, add: NodePtr, @@ -59,6 +60,7 @@ impl<'a> Codegen<'a> { eq: allocator.new_small_number(9).unwrap(), gt_bytes: allocator.new_small_number(10).unwrap(), sha256: allocator.new_small_number(11).unwrap(), + substr: allocator.new_small_number(12).unwrap(), strlen: allocator.new_small_number(13).unwrap(), concat: allocator.new_small_number(14).unwrap(), add: allocator.new_small_number(16).unwrap(), @@ -96,6 +98,7 @@ impl<'a> Codegen<'a> { Lir::Raise(value) => self.gen_raise(value), Lir::Sha256(values) => self.gen_sha256(values), Lir::Listp(value) => self.gen_listp(value), + Lir::Substr(value, start, end) => self.gen_substr(value, start, end), Lir::Strlen(value) => self.gen_strlen(value), Lir::PubkeyForExp(value) => self.gen_pubkey_for_exp(value), Lir::Concat(values) => self.gen_concat(values), @@ -208,6 +211,13 @@ impl<'a> Codegen<'a> { self.list(&[self.ops.l, value]) } + fn gen_substr(&mut self, value: LirId, start: LirId, end: LirId) -> NodePtr { + let value = self.gen_lir(value); + let start = self.gen_lir(start); + let end = self.gen_lir(end); + self.list(&[self.ops.substr, value, start, end]) + } + fn gen_strlen(&mut self, value: LirId) -> NodePtr { let value = self.gen_lir(value); self.list(&[self.ops.strlen, value]) diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index 8805035..46d908b 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -63,6 +63,7 @@ pub fn builtins(db: &mut Database) -> Builtins { let sha256 = sha256(db, &builtins); let pubkey_for_exp = pubkey_for_exp(db, &builtins); let divmod = divmod(db, &builtins); + let substr = substr(db, &builtins); db.scope_mut(builtins.scope_id) .define_symbol("sha256".to_string(), sha256); @@ -73,6 +74,9 @@ pub fn builtins(db: &mut Database) -> Builtins { db.scope_mut(builtins.scope_id) .define_symbol("divmod".to_string(), divmod); + db.scope_mut(builtins.scope_id) + .define_symbol("substr".to_string(), substr); + builtins } @@ -144,3 +148,29 @@ fn divmod(db: &mut Database, builtins: &Builtins) -> SymbolId { }, })) } + +fn substr(db: &mut Database, builtins: &Builtins) -> SymbolId { + let mut scope = Scope::default(); + let value = db.alloc_symbol(Symbol::Parameter(builtins.bytes)); + let start = db.alloc_symbol(Symbol::Parameter(builtins.int)); + let end = db.alloc_symbol(Symbol::Parameter(builtins.int)); + scope.define_symbol("value".to_string(), value); + scope.define_symbol("start".to_string(), start); + scope.define_symbol("end".to_string(), end); + let value_ref = db.alloc_hir(Hir::Reference(value, TextRange::default())); + let start_ref = db.alloc_hir(Hir::Reference(start, TextRange::default())); + let end_ref = db.alloc_hir(Hir::Reference(end, TextRange::default())); + let hir_id = db.alloc_hir(Hir::Substr(value_ref, start_ref, end_ref)); + let scope_id = db.alloc_scope(scope); + + db.alloc_symbol(Symbol::InlineFunction(Function { + scope_id, + hir_id, + ty: FunctionType { + param_types: vec![builtins.bytes, builtins.int, builtins.int], + rest: Rest::Nil, + return_type: builtins.bytes, + generic_types: Vec::new(), + }, + })) +} diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 977e31e..c08a01e 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -195,6 +195,23 @@ impl Compiler<'_> { hir_id, )) } + (Type::Nullable(inner), Type::Nil) => { + let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); + + Some(( + Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), + hir_id, + )) + } + (Type::Nullable(inner), _) if self.db.compare_type(to, inner).is_equal() => { + let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); + let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); + + Some(( + Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), + hir_id, + )) + } _ => { self.db.error( ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), diff --git a/crates/rue-compiler/src/database.rs b/crates/rue-compiler/src/database.rs index 76d891f..6c2f9e4 100644 --- a/crates/rue-compiler/src/database.rs +++ b/crates/rue-compiler/src/database.rs @@ -165,6 +165,12 @@ impl Database { Op::PubkeyForExp => format!("PubkeyForExp({})", self.dbg_hir(*hir_id)), Op::BitwiseNot => format!("BitwiseNot({})", self.dbg_hir(*hir_id)), }, + Hir::Substr(start, end, string) => format!( + "Substr({}, {}, {})", + self.dbg_hir(*start), + self.dbg_hir(*end), + self.dbg_hir(*string) + ), Hir::Raise(hir_id) => format!( "Raise({})", hir_id diff --git a/crates/rue-compiler/src/dependency_graph.rs b/crates/rue-compiler/src/dependency_graph.rs index b53113f..8f4b115 100644 --- a/crates/rue-compiler/src/dependency_graph.rs +++ b/crates/rue-compiler/src/dependency_graph.rs @@ -180,6 +180,11 @@ impl<'a> GraphBuilder<'a> { self.walk_hir(scope_id, lhs); self.walk_hir(scope_id, rhs); } + Hir::Substr(value, start, end) => { + self.walk_hir(scope_id, value); + self.walk_hir(scope_id, start); + self.walk_hir(scope_id, end); + } Hir::Definition(child_scope_id, hir_id) => { self.walk_definition(scope_id, child_scope_id, hir_id); } @@ -383,6 +388,11 @@ impl<'a> GraphBuilder<'a> { self.ref_hir(scope_id, lhs); self.ref_hir(scope_id, rhs); } + Hir::Substr(value, start, end) => { + self.ref_hir(scope_id, value); + self.ref_hir(scope_id, start); + self.ref_hir(scope_id, end); + } Hir::Definition(scope_id, hir_id) => { self.ref_hir(scope_id, hir_id); } diff --git a/crates/rue-compiler/src/hir.rs b/crates/rue-compiler/src/hir.rs index 4f49a6f..9104ae2 100644 --- a/crates/rue-compiler/src/hir.rs +++ b/crates/rue-compiler/src/hir.rs @@ -15,6 +15,7 @@ pub enum Hir { FunctionCall(HirId, Vec, bool), Op(Op, HirId), BinaryOp(BinOp, HirId, HirId), + Substr(HirId, HirId, HirId), Raise(Option), If(HirId, HirId, HirId), } diff --git a/crates/rue-compiler/src/lir.rs b/crates/rue-compiler/src/lir.rs index 31b8acd..c39a4b3 100644 --- a/crates/rue-compiler/src/lir.rs +++ b/crates/rue-compiler/src/lir.rs @@ -17,6 +17,7 @@ pub enum Lir { Sha256(Vec), Listp(LirId), Strlen(LirId), + Substr(LirId, LirId, LirId), PubkeyForExp(LirId), If(LirId, LirId, LirId), Not(LirId), diff --git a/crates/rue-compiler/src/lowerer.rs b/crates/rue-compiler/src/lowerer.rs index 82b2221..0ba3b0f 100644 --- a/crates/rue-compiler/src/lowerer.rs +++ b/crates/rue-compiler/src/lowerer.rs @@ -66,6 +66,12 @@ impl<'a> Lowerer<'a> { let rhs = self.lower_hir(env_id, rhs); self.db.alloc_mir(Mir::BinaryOp(op, lhs, rhs)) } + Hir::Substr(hir_id, start, end) => { + let hir_id = self.lower_hir(env_id, hir_id); + let start = self.lower_hir(env_id, start); + let end = self.lower_hir(env_id, end); + self.db.alloc_mir(Mir::Substr(hir_id, start, end)) + } Hir::Raise(value) => { let value = value.map(|hir_id| self.lower_hir(env_id, hir_id)); self.db.alloc_mir(Mir::Raise(value)) diff --git a/crates/rue-compiler/src/mir.rs b/crates/rue-compiler/src/mir.rs index 597662f..11f2a5a 100644 --- a/crates/rue-compiler/src/mir.rs +++ b/crates/rue-compiler/src/mir.rs @@ -11,6 +11,7 @@ pub enum Mir { Pair(MirId, MirId), Op(Op, MirId), BinaryOp(BinOp, MirId, MirId), + Substr(MirId, MirId, MirId), Raise(Option), If(MirId, MirId, MirId), Run(MirId, MirId), diff --git a/crates/rue-compiler/src/optimizer.rs b/crates/rue-compiler/src/optimizer.rs index 919d4aa..1ca9cf5 100644 --- a/crates/rue-compiler/src/optimizer.rs +++ b/crates/rue-compiler/src/optimizer.rs @@ -66,6 +66,12 @@ impl<'a> Optimizer<'a> { }; handler(self, env_id, lhs, rhs) } + Mir::Substr(value, start, end) => { + let value = self.opt_mir(env_id, value); + let start = self.opt_mir(env_id, start); + let end = self.opt_mir(env_id, end); + self.db.alloc_lir(Lir::Substr(value, start, end)) + } Mir::If(condition, then_block, else_block) => { self.opt_if(env_id, condition, then_block, else_block) } diff --git a/crates/rue-compiler/stdlib.rue b/crates/rue-compiler/stdlib.rue index 89656cf..27b8540 100644 --- a/crates/rue-compiler/stdlib.rue +++ b/crates/rue-compiler/stdlib.rue @@ -113,10 +113,7 @@ export fun concat(a: T[], b: T[]) -> T[] { if a is (T, T[]) { return [a.first, ...concat(a.rest, b)]; } - if b is (T, T[]) { - return [b.first, ...concat(a, b.rest)]; - } - nil + b } inline const ATOM_PREFIX: Bytes = 1 as Bytes; diff --git a/examples/cat.rue b/examples/cat.rue new file mode 100644 index 0000000..651be16 --- /dev/null +++ b/examples/cat.rue @@ -0,0 +1,272 @@ +// This puzzle has not been audited or tested, and is for example purposes only. + +// Information about the CAT, used for currying purposes. +struct CatInfo { + mod_hash: Bytes32, + mod_hash_hash: Bytes32, + asset_id: Bytes32, +} + +// The puzzle section of the CAT's truths. +struct PuzzleTruths { + inner_puzzle_hash: Bytes32, + ...cat_info: CatInfo, +} + +// The coin section of the CAT's truths. +struct CoinTruths { + my_id: Bytes32, + ...my_coin: Coin, +} + +// The full truth struct for the CAT puzzle. +struct Truths { + puzzle_truths: PuzzleTruths, + ...coin_truths: CoinTruths, +} + +// The TAIL puzzle itself. +type Tail = fun( + truths: Truths, + parent_is_cat: Bool, + lineage_proof: LineageProof?, + extra_delta: Int, + conditions: Condition[], + tail_solution: Any[], +) -> Condition[]; + +// Information about the TAIL reveal. +// This is revealed with `RunTailCondition`. +struct TailInfo { + tail_puzzle: Tail, + tail_solution: Any[], +} + +// A custom condition `(51 () -113 tail_puzzle tail_solution)`. +// We can check `Condition::CreateCoin` then cast to this type instead. +struct RunTailCondition { + opcode: Int, + puzzle_hash: Nil, + amount: Int, + tail_puzzle: Tail, + tail_solution: Any[], +} + +// Information about the current coin. +struct Coin { + parent_coin_info: Bytes32, + puzzle_hash: Bytes32, + amount: Int, +} + +// Proof that the coin is a CAT with the same asset id. +struct CoinProof { + parent_coin_info: Bytes32, + inner_puzzle_hash: Bytes32, + amount: Int, +} + +// Proof that the parent coin is a CAT with the same asset id. +struct LineageProof { + parent_parent_coin_info: Bytes32, + parent_inner_puzzle_hash: Bytes32, + parent_amount: Int, +} + +// The byte prepended to announcements. +// Inner puzzle announcements must not start with this byte. +inline const RING_MORPH_BYTE: Bytes = 0xcb; + +// Calculate the full CAT puzzle hash with curried parameters. +inline fun cat_puzzle_hash(cat_info: CatInfo, inner_puzzle_hash: Bytes32) -> Bytes32 { + curry_tree_hash( + cat_info.mod_hash, + cat_info.mod_hash_hash, + tree_hash_atom(cat_info.asset_id), + inner_puzzle_hash + ) +} + +fun main( + mod_hash: Bytes32, + asset_id: Bytes32, + inner_puzzle: fun(...solution: Any) -> Condition[], + inner_solution: Any, + lineage_proof: LineageProof?, + prev_coin_id: Bytes32, + my_coin: Coin, + next_coin_proof: CoinProof, + prev_subtotal: Int, + extra_delta: Int, +) -> Condition[] { + // For simplicity, we'll pack these values into a struct. + let cat_info = CatInfo { + mod_hash: mod_hash, + mod_hash_hash: tree_hash_atom(mod_hash), + asset_id: asset_id, + }; + + // Calculate the inner puzzle hash and conditions. + let morph = morph_conditions(inner_puzzle(...inner_solution), cat_info, nil); + let inner_puzzle_hash = tree_hash(inner_puzzle); + + // Calculate coin ids. + let coin_id = calculate_coin_id(my_coin.parent_coin_info, my_coin.puzzle_hash, my_coin.amount); + let next_coin_id = calculate_coin_id( + next_coin_proof.parent_coin_info, + cat_puzzle_hash(cat_info, next_coin_proof.inner_puzzle_hash), + next_coin_proof.amount, + ); + + // Create the CAT Truth struct. + let truths = Truths { + puzzle_truths: PuzzleTruths { + inner_puzzle_hash: inner_puzzle_hash, + cat_info: cat_info, + }, + coin_truths: CoinTruths { + my_id: coin_id, + my_coin: my_coin, + }, + }; + + // Check whether the parent is a CAT or not. + let parent_is_cat = lineage_proof is LineageProof && { + let parent_coin_id = calculate_coin_id( + lineage_proof.parent_parent_coin_info, + cat_puzzle_hash(cat_info, lineage_proof.parent_inner_puzzle_hash), + lineage_proof.parent_amount, + ); + my_coin.parent_coin_info == parent_coin_id + }; + + // Calculate the new subtotal. + let remainder = my_coin.amount - morph.sum; + let subtotal = prev_subtotal + remainder + extra_delta; + + // Prepend the ring conditions to the morphed conditions. + // This ensures that the previous and next CATs are linked. + // When they form a ring like this, you can be sure the supply isn't changed. + let conditions: Condition[] = [ + Condition::CreateCoinAnnouncement { + message: RING_MORPH_BYTE + tree_hash([prev_coin_id, prev_subtotal] as Any[]), + }, + Condition::AssertCoinAnnouncement { + announcement_id: sha256(next_coin_id + RING_MORPH_BYTE + tree_hash([coin_id, subtotal] as Any[])), + }, + ...morph.conditions, + ]; + + let final_conditions = if morph.tail_info is TailInfo { + // Make sure the asset id matches the revealed TAIL. + assert tree_hash(morph.tail_info.tail_puzzle) == asset_id; + + // Run the TAIL puzzle with its solution. + // It also has access to various information about the CAT spend. + let tail_conditions = morph.tail_info.tail_puzzle( + truths, + parent_is_cat, + lineage_proof, + extra_delta, + conditions, + morph.tail_info.tail_solution + ); + + // Prepend the TAIL's conditions. + concat(tail_conditions, conditions) + } else { + // If there's no TAIL, make sure the extra delta is zero. + // It must have a parent that is also a CAT. + assert parent_is_cat && extra_delta == 0; + + // Output the conditions as they are. + conditions + }; + + [ + // Make sure that the coin is correct. + Condition::AssertMyCoinId { coin_id: coin_id }, + + // Output the other conditions as well. + ...final_conditions, + ] +} + +struct Morph { + // The morphed conditions. + conditions: Condition[], + + // The total amount of coins created. + sum: Int, + + // Information about the TAIL, revealed in the conditions. + tail_info: TailInfo?, +} + +// Morph all of the conditions and extract the TAIL info. +fun morph_conditions( + conditions: Condition[], + cat_info: CatInfo, + tail_info: TailInfo?, +) -> Morph { + // If there are no conditions, return an empty morph. + if conditions is Nil { + return Morph { + conditions: nil, + sum: 0, + tail_info: tail_info, + }; + } + + let condition = conditions.first; + + if condition is Condition::CreateCoin { + // If the amount is -113, it's a TAIL reveal. + if condition.amount == -113 { + let run_tail = condition as Any as RunTailCondition; + + let rest = morph_conditions(conditions.rest, cat_info, TailInfo { + tail_puzzle: run_tail.tail_puzzle, + tail_solution: run_tail.tail_solution, + }); + + return Morph { + conditions: rest.conditions, + sum: rest.sum, + tail_info: rest.tail_info, + }; + } + + // Otherwise, morph the condition by wrapping the puzzle hash in the CAT layer. + let morphed = Condition::CreateCoin { + puzzle_hash: cat_puzzle_hash(cat_info, condition.puzzle_hash), + amount: condition.amount, + memos: condition.memos, + }; + + let rest = morph_conditions(conditions.rest, cat_info, tail_info); + + return Morph { + conditions: [morphed, ...rest.conditions], + sum: condition.amount + rest.sum, + tail_info: rest.tail_info, + }; + } + + // If the condition is a coin announcement, + // make sure it's not pretending to be part of the ring. + if condition is Condition::CreateCoinAnnouncement + && condition.message.length == 33 + && substr(condition.message, 0, 1) == RING_MORPH_BYTE + { + raise; + } + + let rest = morph_conditions(conditions.rest, cat_info, tail_info); + + Morph { + conditions: [condition, ...rest.conditions], + sum: rest.sum, + tail_info: rest.tail_info, + } +} diff --git a/tests.toml b/tests.toml index 813da72..1043594 100644 --- a/tests.toml +++ b/tests.toml @@ -500,11 +500,11 @@ output = "((73 1) (72 0x6155f55414a5cd9193bef33744095a78d57852cf24241b25edeffad6 hash = "1f6abfdd1000d9ed87740ca5a0a888a042d64a8a7b6d60f082aab5be1f0915b4" [multisig] -bytes = 489 -cost = 27062 +bytes = 439 +cost = 25568 input = "((0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) 1 (()) ((51 0x0000000000000000000000000000000000000000000000000000000000000000 1000)))" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x502cea601814d49886e0713402860d6054f6947d811b3b93100c6b364626651a) (g1_negate 0x0000000000000000000000000000000000000000000000000000000000000000 1000))" -hash = "173385b87af5d8940767c328026fe5f8e76bc238d2a3aaddf4f55e844f400fca" +hash = "95cdea46407cfa3b5a7adb07afac1b9c7ba263f33f9938aa9af2e7546063e501" [hello_world] bytes = 16 @@ -562,3 +562,11 @@ cost = 1710 input = "()" output = "()" hash = "e3153c4596c3b27d1f1cea81cf4c475e2d8f617330a82684bd97f6fae2bacf50" + +[cat] +bytes = 2181 +cost = 0 +input = "()" +output = "()" +hash = "a77540538602dcc5ff23620f7ca00b39cc3bb0f67554ec9ac4d21e1c11f6b935" +error = "()" From 8998743dfb8eff4ebfdf56a16fdc2680c468b2c6 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 7 Jul 2024 23:40:06 -0400 Subject: [PATCH 3/3] Final draft of example --- crates/rue-compiler/src/dependency_graph.rs | 13 +++---------- crates/rue-compiler/src/environment.rs | 4 ++++ crates/rue-compiler/src/optimizer.rs | 14 -------------- examples/cat.rue | 13 +++++-------- tests.toml | 11 +++++------ 5 files changed, 17 insertions(+), 38 deletions(-) diff --git a/crates/rue-compiler/src/dependency_graph.rs b/crates/rue-compiler/src/dependency_graph.rs index 8f4b115..5727086 100644 --- a/crates/rue-compiler/src/dependency_graph.rs +++ b/crates/rue-compiler/src/dependency_graph.rs @@ -214,16 +214,9 @@ impl<'a> GraphBuilder<'a> { .or_default() .insert(parent_scope_id); - let parent_environment_id = self.graph.environments[&parent_scope_id]; - - let child_environment_id = self - .db - .alloc_env(Environment::binding(parent_environment_id)); - - self.graph - .environments - .insert(child_scope_id, child_environment_id); - + let parent_env_id = self.graph.environments[&parent_scope_id]; + let child_env_id = self.db.alloc_env(Environment::binding(parent_env_id)); + self.graph.environments.insert(child_scope_id, child_env_id); self.walk_hir(child_scope_id, hir_id); } diff --git a/crates/rue-compiler/src/environment.rs b/crates/rue-compiler/src/environment.rs index 70d2239..505a779 100644 --- a/crates/rue-compiler/src/environment.rs +++ b/crates/rue-compiler/src/environment.rs @@ -66,6 +66,10 @@ impl Environment { self.parent } + pub fn parent_mut(&mut self) -> &mut Option { + &mut self.parent + } + pub fn build(&self) -> IndexSet { let mut symbol_ids = IndexSet::new(); symbol_ids.extend(self.definitions.iter().copied()); diff --git a/crates/rue-compiler/src/optimizer.rs b/crates/rue-compiler/src/optimizer.rs index e9c9e5f..fa7707b 100644 --- a/crates/rue-compiler/src/optimizer.rs +++ b/crates/rue-compiler/src/optimizer.rs @@ -116,21 +116,7 @@ impl<'a> Optimizer<'a> { let mut environment = Vec::new(); let mut rest; - println!("Referencing {}", self.db.dbg_symbol(symbol_id)); - loop { - for symbol_id in self.db.env(env_id).definitions() { - println!(" Defining {}", self.db.dbg_symbol(symbol_id)); - } - - for symbol_id in self.db.env(env_id).captures() { - println!(" Capturing {}", self.db.dbg_symbol(symbol_id)); - } - - for symbol_id in self.db.env(env_id).parameters() { - println!(" Parameter {}", self.db.dbg_symbol(symbol_id)); - } - environment.extend(self.db.env(env_id).build()); rest = self.db.env(env_id).rest(); diff --git a/examples/cat.rue b/examples/cat.rue index eb9129b..b414691 100644 --- a/examples/cat.rue +++ b/examples/cat.rue @@ -131,14 +131,11 @@ fun main( }; // Check whether the parent is a CAT or not. - let parent_is_cat = lineage_proof is LineageProof && { - let parent_coin_id = calculate_coin_id( - lineage_proof.parent_parent_coin_info, - cat_puzzle_hash(cat_info, lineage_proof.parent_inner_puzzle_hash), - lineage_proof.parent_amount, - ); - my_coin.parent_coin_info == parent_coin_id - }; + let parent_is_cat = lineage_proof is LineageProof && my_coin.parent_coin_info == calculate_coin_id( + lineage_proof.parent_parent_coin_info, + cat_puzzle_hash(cat_info, lineage_proof.parent_inner_puzzle_hash), + lineage_proof.parent_amount, + ); // Calculate the new subtotal. let remainder = my_coin.amount - morph.sum; diff --git a/tests.toml b/tests.toml index b7f4732..7a5887f 100644 --- a/tests.toml +++ b/tests.toml @@ -563,12 +563,11 @@ output = "()" hash = "e3153c4596c3b27d1f1cea81cf4c475e2d8f617330a82684bd97f6fae2bacf50" [cat] -bytes = 2117 -cost = 0 -input = "()" -output = "()" -hash = "5a8eefb2374bd0974bb9464f5c421a70d13e2cc2adb54ef610ebd6d3f53968be" -error = "()" +bytes = 2107 +cost = 359978 +input = "(0x00f43ce9fcc63d5019e209c103e6b0aaf56bbe7fc7fafae5af7f5ee6887a8719 0xd622c62a7292ffee5cf2537a90360ca0b7337b76d7014ec042930c0a87592213 (q (g1_negate () -113 (a (q 2 (i 47 (q 8) (q 2 (i (= 45 2) () (q 8)) 1)) 1) (c (q . 0x895eb35a355941ba7f6a8679a73bb9b8b62cae2b04ef5351eda42583c0f2d861) 1)) ()) (g1_negate 0xb8705f94744e7fc30300ac9b12d306b283f5a702937ee99beabf665be6023001 1 (0xb8705f94744e7fc30300ac9b12d306b283f5a702937ee99beabf665be6023001))) () () 0x615236766bed52d7abaa41d270407f3ec852981852334b213bd8515924459a5d (0x895eb35a355941ba7f6a8679a73bb9b8b62cae2b04ef5351eda42583c0f2d861 0x1ecb863db5d2ae6c71e9a8b0741acb3e034e8164b8ca0e564d5fad8b9dc875d5 1) (0x895eb35a355941ba7f6a8679a73bb9b8b62cae2b04ef5351eda42583c0f2d861 0x130deb20b44082a68293974f8cab9c51e21f9a9f3005000168eb77e49e0fc378 1) () ())" +output = "((70 0x615236766bed52d7abaa41d270407f3ec852981852334b213bd8515924459a5d) (60 0xcb7f53b5de05b4afe58ab663b952aa785fd9ad911564709bd8eacbf4c58ba1e589) (61 0x389f1ea7b9fab7a9294104eb3a181d662f2dbe9c43fbe52802ba9b7eef357e77) (g1_negate 0xc9644528436f44cd9b33282684b4964f55d5551cb3a970ab9cf8f536e0d72ad1 1 (0xb8705f94744e7fc30300ac9b12d306b283f5a702937ee99beabf665be6023001)))" +hash = "00f43ce9fcc63d5019e209c103e6b0aaf56bbe7fc7fafae5af7f5ee6887a8719" [external_function] bytes = 7