From 8b4199f49a2f400eea7ef551aa40d859697b01d1 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 19 Nov 2025 22:03:05 +0800 Subject: [PATCH 1/6] Refactor stdlib into directory --- Cargo.lock | 20 +++ Cargo.toml | 1 + crates/rue-compiler/Cargo.toml | 1 + crates/rue-compiler/src/compiler.rs | 18 +-- crates/rue-compiler/src/file.rs | 87 ++++++++++--- .../src/std/conditions/message_flags.rue | 9 ++ .../src/std/conditions/opcodes.rue | 35 ++++++ .../src/{std.rue => std/conditions/types.rue} | 118 +----------------- .../rue-compiler/src/std/curry_tree_hash.rue | 34 +++++ crates/rue-compiler/src/std/main.rue | 22 ++++ crates/rue-compiler/src/std/prelude.rue | 6 + crates/rue-compiler/src/std/tree_hash.rue | 15 +++ crates/rue-diagnostic/src/srcloc.rs | 6 +- 13 files changed, 230 insertions(+), 142 deletions(-) create mode 100644 crates/rue-compiler/src/std/conditions/message_flags.rue create mode 100644 crates/rue-compiler/src/std/conditions/opcodes.rue rename crates/rue-compiler/src/{std.rue => std/conditions/types.rue} (51%) create mode 100644 crates/rue-compiler/src/std/curry_tree_hash.rue create mode 100644 crates/rue-compiler/src/std/main.rue create mode 100644 crates/rue-compiler/src/std/prelude.rue create mode 100644 crates/rue-compiler/src/std/tree_hash.rue diff --git a/Cargo.lock b/Cargo.lock index 941a6816..d38ee34b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1051,6 +1051,25 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "2.12.0" @@ -1725,6 +1744,7 @@ dependencies = [ "expect-test", "hex", "id-arena", + "include_dir", "indexmap", "log", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 162b8116..8566c2d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,3 +84,4 @@ colored = "3.0.0" send_wrapper = "0.6.0" chialisp = "0.4.1" toml = "0.9.8" +include_dir = "0.7.4" diff --git a/crates/rue-compiler/Cargo.toml b/crates/rue-compiler/Cargo.toml index b0c6cef2..c9b4fd51 100644 --- a/crates/rue-compiler/Cargo.toml +++ b/crates/rue-compiler/Cargo.toml @@ -32,6 +32,7 @@ clvmr = { workspace = true } indexmap = { workspace = true } log = { workspace = true } thiserror = { workspace = true } +include_dir = { workspace = true } [dev-dependencies] rue-lexer = { workspace = true } diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 1c245c62..f9404e6e 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -17,7 +17,7 @@ use rue_options::CompilerOptions; use rue_parser::{SyntaxNode, SyntaxToken}; use rue_types::{Check, CheckError, Comparison, Type, TypeId}; -use crate::{File, FileTree, SyntaxItem, SyntaxItemKind, SyntaxMap}; +use crate::{FileTree, SyntaxItem, SyntaxItemKind, SyntaxMap}; #[derive(Debug, Clone)] pub struct Compiler { @@ -56,7 +56,7 @@ impl Compiler { let mut ctx = Self { options, - source: Source::new(Arc::from(""), SourceKind::Std), + source: Source::new(Arc::from(""), SourceKind::Std("".to_string())), diagnostics: Vec::new(), db, syntax_map: SyntaxMap::new(), @@ -68,15 +68,17 @@ impl Compiler { registered_scopes: HashSet::new(), }; - let std = File::std(&mut ctx); - let tree = FileTree::File(std.clone()); + let tree = FileTree::load_std(&mut ctx).unwrap(); tree.compile_impl(&mut ctx, false); - let std_scope = std.module(&ctx).scope; + let prelude_file = tree + .find(&SourceKind::Std("prelude.rue".to_string())) + .unwrap(); + let prelude_scope = ctx.module(prelude_file.module).scope; let prelude = ctx.alloc_child_scope(); for (name, symbol) in ctx - .scope(std_scope) + .scope(prelude_scope) .exported_symbols() .map(|(name, symbol)| (name.to_string(), symbol)) .collect::>() @@ -86,7 +88,7 @@ impl Compiler { } for (name, ty) in ctx - .scope(std_scope) + .scope(prelude_scope) .exported_types() .map(|(name, ty)| (name.to_string(), ty)) .collect::>() @@ -95,7 +97,7 @@ impl Compiler { .insert_type(name.to_string(), ty, false); } - ctx.push_scope(prelude, std.document.syntax().text_range().start()); + ctx.push_scope(prelude, prelude_file.document.syntax().text_range().start()); ctx } diff --git a/crates/rue-compiler/src/file.rs b/crates/rue-compiler/src/file.rs index b4a16718..2eed2b7d 100644 --- a/crates/rue-compiler/src/file.rs +++ b/crates/rue-compiler/src/file.rs @@ -2,11 +2,13 @@ use std::{ collections::{HashMap, HashSet}, fs, io, path::{Path, PathBuf}, + string::FromUtf8Error, sync::Arc, }; use clvmr::{Allocator, NodePtr}; use id_arena::Arena; +use include_dir::{Dir, DirEntry, include_dir}; use indexmap::{IndexMap, IndexSet, indexset}; use rowan::{TextRange, TextSize}; use rue_ast::{AstDocument, AstNode}; @@ -34,6 +36,9 @@ pub enum Error { #[error("Source not found in compilation unit")] SourceNotFound(SourceKind), + + #[error("UTF-8 conversion error: {0}")] + Utf8(#[from] FromUtf8Error), } #[derive(Debug, Clone)] @@ -57,7 +62,71 @@ pub enum FileTree { Directory(Directory), } +static STD_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/src/std"); + impl FileTree { + pub(crate) fn load_std(ctx: &mut Compiler) -> Result { + Self::load_std_dir(ctx, "std".to_string(), STD_DIR.entries()) + } + + fn load_std_dir<'a>( + ctx: &mut Compiler, + name: String, + entries: &'a [DirEntry<'a>], + ) -> Result { + let scope = ctx.alloc_child_scope(); + + let module = ctx.alloc_symbol(Symbol::Module(ModuleSymbol { + name: Some(Name::new(name.as_str(), None)), + scope, + declarations: ModuleDeclarations::default(), + })); + + let mut children = Vec::new(); + + for entry in entries { + children.extend(Self::try_load_std_entry(ctx, entry)?); + } + + Ok(Self::Directory(Directory::new(ctx, name, module, children))) + } + + fn try_load_std_entry<'a>( + ctx: &mut Compiler, + entry: &'a DirEntry<'a>, + ) -> Result, Error> { + let file_name = entry + .path() + .file_name() + .ok_or(io::Error::new( + io::ErrorKind::InvalidFilename, + "Missing file name", + ))? + .to_string_lossy() + .to_string(); + + if let Some(file) = entry.as_file() { + #[allow(clippy::case_sensitive_file_extension_comparisons)] + if !file_name.ends_with(".rue") { + return Ok(None); + } + + let source_kind = SourceKind::Std(entry.path().to_string_lossy().to_string()); + + let text = String::from_utf8(file.contents().to_vec())?; + + let source = Source::new(Arc::from(text), source_kind); + + Ok(Some(Self::File(File::new( + ctx, + file_name.replace(".rue", ""), + source, + )))) + } else { + Ok(Some(Self::load_std_dir(ctx, file_name, entry.children())?)) + } + } + pub fn compile_file(ctx: &mut Compiler, path: &Path) -> Result { let tree = Self::File(File::new( ctx, @@ -253,7 +322,7 @@ impl FileTree { .find(path) .ok_or_else(|| Error::SourceNotFound(path.clone()))?; - let scope = ctx.module(tree.module()).scope; + let scope = ctx.module(tree.module).scope; let Some(main) = ctx.scope(scope).symbol("main") else { return Ok(None); }; @@ -292,7 +361,7 @@ impl FileTree { .find(path) .ok_or_else(|| Error::SourceNotFound(path.clone()))?; - let scope = ctx.module(tree.module()).scope; + let scope = ctx.module(tree.module).scope; let mut exports = Vec::new(); @@ -360,11 +429,11 @@ impl FileTree { Ok(outputs) } - pub fn find(&self, path: &SourceKind) -> Option<&Self> { + pub fn find(&self, path: &SourceKind) -> Option<&File> { match self { Self::File(file) => { if file.source.kind == *path { - Some(self) + Some(file) } else { None } @@ -454,16 +523,6 @@ impl File { } } - pub fn std(ctx: &mut Compiler) -> Self { - let text = include_str!("./std.rue"); - - Self::new( - ctx, - "std".to_string(), - Source::new(Arc::from(text), SourceKind::Std), - ) - } - pub(crate) fn module<'a>(&'a self, ctx: &'a Compiler) -> &'a ModuleSymbol { match ctx.symbol(self.module) { Symbol::Module(module) => module, diff --git a/crates/rue-compiler/src/std/conditions/message_flags.rue b/crates/rue-compiler/src/std/conditions/message_flags.rue new file mode 100644 index 00000000..db5f501f --- /dev/null +++ b/crates/rue-compiler/src/std/conditions/message_flags.rue @@ -0,0 +1,9 @@ +export inline const SENDER_COIN: 0b111_000 = 0b111_000; +export inline const SENDER_PARENT: 0b100_000 = 0b100_000; +export inline const SENDER_PUZZLE: 0b010_000 = 0b010_000; +export inline const SENDER_AMOUNT: 0b001_000 = 0b001_000; + +export inline const RECEIVER_COIN: 0b000_111 = 0b000_111; +export inline const RECEIVER_PARENT: 0b000_100 = 0b000_100; +export inline const RECEIVER_PUZZLE: 0b000_010 = 0b000_010; +export inline const RECEIVER_AMOUNT: 0b000_001 = 0b000_001; diff --git a/crates/rue-compiler/src/std/conditions/opcodes.rue b/crates/rue-compiler/src/std/conditions/opcodes.rue new file mode 100644 index 00000000..309ab18f --- /dev/null +++ b/crates/rue-compiler/src/std/conditions/opcodes.rue @@ -0,0 +1,35 @@ +export inline const REMARK: 1 = 1; +export inline const AGG_SIG_PARENT: 43 = 43; +export inline const AGG_SIG_PUZZLE: 44 = 44; +export inline const AGG_SIG_AMOUNT: 45 = 45; +export inline const AGG_SIG_PUZZLE_AMOUNT: 46 = 46; +export inline const AGG_SIG_PARENT_AMOUNT: 47 = 47; +export inline const AGG_SIG_PARENT_PUZZLE: 48 = 48; +export inline const AGG_SIG_UNSAFE: 49 = 49; +export inline const AGG_SIG_ME: 50 = 50; +export inline const CREATE_COIN: 51 = 51; +export inline const RESERVE_FEE: 52 = 52; +export inline const CREATE_COIN_ANNOUNCEMENT: 60 = 60; +export inline const ASSERT_COIN_ANNOUNCEMENT: 61 = 61; +export inline const CREATE_PUZZLE_ANNOUNCEMENT: 62 = 62; +export inline const ASSERT_PUZZLE_ANNOUNCEMENT: 63 = 63; +export inline const ASSERT_CONCURRENT_SPEND: 64 = 64; +export inline const ASSERT_CONCURRENT_PUZZLE: 65 = 65; +export inline const SEND_MESSAGE: 66 = 66; +export inline const RECEIVE_MESSAGE: 67 = 67; +export inline const ASSERT_MY_COIN_ID: 70 = 70; +export inline const ASSERT_MY_PARENT_ID: 71 = 71; +export inline const ASSERT_MY_PUZZLE_HASH: 72 = 72; +export inline const ASSERT_MY_AMOUNT: 73 = 73; +export inline const ASSERT_MY_BIRTH_SECONDS: 74 = 74; +export inline const ASSERT_MY_BIRTH_HEIGHT: 75 = 75; +export inline const ASSERT_EPHEMERAL: 76 = 76; +export inline const ASSERT_SECONDS_RELATIVE: 80 = 80; +export inline const ASSERT_SECONDS_ABSOLUTE: 81 = 81; +export inline const ASSERT_HEIGHT_RELATIVE: 82 = 82; +export inline const ASSERT_HEIGHT_ABSOLUTE: 83 = 83; +export inline const ASSERT_BEFORE_SECONDS_RELATIVE: 84 = 84; +export inline const ASSERT_BEFORE_SECONDS_ABSOLUTE: 85 = 85; +export inline const ASSERT_BEFORE_HEIGHT_RELATIVE: 86 = 86; +export inline const ASSERT_BEFORE_HEIGHT_ABSOLUTE: 87 = 87; +export inline const SOFTFORK: 90 = 90; diff --git a/crates/rue-compiler/src/std.rue b/crates/rue-compiler/src/std/conditions/types.rue similarity index 51% rename from crates/rue-compiler/src/std.rue rename to crates/rue-compiler/src/std/conditions/types.rue index 90add477..621f247c 100644 --- a/crates/rue-compiler/src/std.rue +++ b/crates/rue-compiler/src/std/conditions/types.rue @@ -1,38 +1,4 @@ -export inline const REMARK: 1 = 1; -export inline const AGG_SIG_PARENT: 43 = 43; -export inline const AGG_SIG_PUZZLE: 44 = 44; -export inline const AGG_SIG_AMOUNT: 45 = 45; -export inline const AGG_SIG_PUZZLE_AMOUNT: 46 = 46; -export inline const AGG_SIG_PARENT_AMOUNT: 47 = 47; -export inline const AGG_SIG_PARENT_PUZZLE: 48 = 48; -export inline const AGG_SIG_UNSAFE: 49 = 49; -export inline const AGG_SIG_ME: 50 = 50; -export inline const CREATE_COIN: 51 = 51; -export inline const RESERVE_FEE: 52 = 52; -export inline const CREATE_COIN_ANNOUNCEMENT: 60 = 60; -export inline const ASSERT_COIN_ANNOUNCEMENT: 61 = 61; -export inline const CREATE_PUZZLE_ANNOUNCEMENT: 62 = 62; -export inline const ASSERT_PUZZLE_ANNOUNCEMENT: 63 = 63; -export inline const ASSERT_CONCURRENT_SPEND: 64 = 64; -export inline const ASSERT_CONCURRENT_PUZZLE: 65 = 65; -export inline const SEND_MESSAGE: 66 = 66; -export inline const RECEIVE_MESSAGE: 67 = 67; -export inline const ASSERT_MY_COIN_ID: 70 = 70; -export inline const ASSERT_MY_PARENT_ID: 71 = 71; -export inline const ASSERT_MY_PUZZLE_HASH: 72 = 72; -export inline const ASSERT_MY_AMOUNT: 73 = 73; -export inline const ASSERT_MY_BIRTH_SECONDS: 74 = 74; -export inline const ASSERT_MY_BIRTH_HEIGHT: 75 = 75; -export inline const ASSERT_EPHEMERAL: 76 = 76; -export inline const ASSERT_SECONDS_RELATIVE: 80 = 80; -export inline const ASSERT_SECONDS_ABSOLUTE: 81 = 81; -export inline const ASSERT_HEIGHT_RELATIVE: 82 = 82; -export inline const ASSERT_HEIGHT_ABSOLUTE: 83 = 83; -export inline const ASSERT_BEFORE_SECONDS_RELATIVE: 84 = 84; -export inline const ASSERT_BEFORE_SECONDS_ABSOLUTE: 85 = 85; -export inline const ASSERT_BEFORE_HEIGHT_RELATIVE: 86 = 86; -export inline const ASSERT_BEFORE_HEIGHT_ABSOLUTE: 87 = 87; -export inline const SOFTFORK: 90 = 90; +import opcodes::*; export struct Remark { opcode = REMARK, @@ -241,85 +207,3 @@ export type Condition = | AssertBeforeSecondsRelative | AssertBeforeSecondsAbsolute | AssertBeforeHeightRelative | AssertBeforeHeightAbsolute | Softfork; - -export inline const SENDER_COIN: 0b111_000 = 0b111_000; -export inline const SENDER_PARENT: 0b100_000 = 0b100_000; -export inline const SENDER_PUZZLE: 0b010_000 = 0b010_000; -export inline const SENDER_AMOUNT: 0b001_000 = 0b001_000; - -export inline const RECEIVER_COIN: 0b000_111 = 0b000_111; -export inline const RECEIVER_PARENT: 0b000_100 = 0b000_100; -export inline const RECEIVER_PUZZLE: 0b000_010 = 0b000_010; -export inline const RECEIVER_AMOUNT: 0b000_001 = 0b000_001; - -export fn tree_hash(value: Any) -> Bytes32 { - if value is Bytes { - tree_hash_atom(value) - } else { - tree_hash_pair(tree_hash(value.first), tree_hash(value.rest)) - } -} - -export inline fn tree_hash_atom(value: Bytes) -> Bytes32 { - sha256(1 as Bytes + value) -} - -export inline fn tree_hash_pair(first: Bytes32, rest: Bytes32) -> Bytes32 { - sha256(2 as Bytes + first + rest) -} - -inline fn quote_hash(value: Bytes32) -> Bytes32 { - tree_hash_pair(tree_hash_atom(1 as Bytes), value) -} - -inline fn two_item_list_hash(first: Bytes32, rest: Bytes32) -> Bytes32 { - tree_hash_pair(first, tree_hash_pair(rest, tree_hash_atom(nil))) -} - -inline fn apply_hash(mod_hash: Bytes32, environment_hash: Bytes32) -> Bytes32 { - sha256(2 as Bytes + tree_hash_atom(2 as Bytes) + two_item_list_hash(quote_hash(mod_hash), environment_hash)) -} - -inline fn update_hash_with_parameter( - parameter_hash: Bytes32, - environment_hash: Bytes32 -) -> Bytes32 { - sha256(2 as Bytes + tree_hash_atom(4 as Bytes) + two_item_list_hash(quote_hash(parameter_hash), environment_hash)) -} - -fn curried_params_hash(parameters: List) -> Bytes32 { - if parameters is nil { - return tree_hash_atom(1 as Bytes); - } - update_hash_with_parameter(parameters.first, curried_params_hash(parameters.rest)) -} - -export inline fn curry_tree_hash( - mod_hash: Bytes32, - parameters: List -) -> Bytes32 { - apply_hash(mod_hash, curried_params_hash(parameters)) -} - -export fn merge_list(a: List, b: List) -> List { - if a is (T, List) { - return [a.first, ...merge_list(a.rest, b)]; - } - b -} - -export fn deep_equal(a: Any, b: Any) -> Bool { - if a is Bytes { - if b is Bytes { - a == b - } else { - false - } - } else { - if b is Bytes { - false - } else { - deep_equal(a.first, b.first) && deep_equal(a.rest, b.rest) - } - } -} diff --git a/crates/rue-compiler/src/std/curry_tree_hash.rue b/crates/rue-compiler/src/std/curry_tree_hash.rue new file mode 100644 index 00000000..006f6a34 --- /dev/null +++ b/crates/rue-compiler/src/std/curry_tree_hash.rue @@ -0,0 +1,34 @@ +import tree_hash::{tree_hash_atom, tree_hash_pair}; + +inline fn quote_hash(value: Bytes32) -> Bytes32 { + tree_hash_pair(tree_hash_atom(1 as Bytes), value) +} + +inline fn two_item_list_hash(first: Bytes32, rest: Bytes32) -> Bytes32 { + tree_hash_pair(first, tree_hash_pair(rest, tree_hash_atom(nil))) +} + +inline fn apply_hash(mod_hash: Bytes32, environment_hash: Bytes32) -> Bytes32 { + sha256(2 as Bytes + tree_hash_atom(2 as Bytes) + two_item_list_hash(quote_hash(mod_hash), environment_hash)) +} + +inline fn update_hash_with_parameter( + parameter_hash: Bytes32, + environment_hash: Bytes32 +) -> Bytes32 { + sha256(2 as Bytes + tree_hash_atom(4 as Bytes) + two_item_list_hash(quote_hash(parameter_hash), environment_hash)) +} + +fn curried_params_hash(parameters: List) -> Bytes32 { + if parameters is nil { + return tree_hash_atom(1 as Bytes); + } + update_hash_with_parameter(parameters.first, curried_params_hash(parameters.rest)) +} + +export inline fn curry_tree_hash( + mod_hash: Bytes32, + parameters: List +) -> Bytes32 { + apply_hash(mod_hash, curried_params_hash(parameters)) +} diff --git a/crates/rue-compiler/src/std/main.rue b/crates/rue-compiler/src/std/main.rue new file mode 100644 index 00000000..67d0cff5 --- /dev/null +++ b/crates/rue-compiler/src/std/main.rue @@ -0,0 +1,22 @@ +export fn merge_list(a: List, b: List) -> List { + if a is (T, List) { + return [a.first, ...merge_list(a.rest, b)]; + } + b +} + +export fn deep_equal(a: Any, b: Any) -> Bool { + if a is Bytes { + if b is Bytes { + a == b + } else { + false + } + } else { + if b is Bytes { + false + } else { + deep_equal(a.first, b.first) && deep_equal(a.rest, b.rest) + } + } +} diff --git a/crates/rue-compiler/src/std/prelude.rue b/crates/rue-compiler/src/std/prelude.rue new file mode 100644 index 00000000..ab99783c --- /dev/null +++ b/crates/rue-compiler/src/std/prelude.rue @@ -0,0 +1,6 @@ +export main::{deep_equal, merge_list}; +export tree_hash::{tree_hash, tree_hash_atom, tree_hash_pair}; +export curry_tree_hash::curry_tree_hash; +export conditions::message_flags::*; +export conditions::opcodes::*; +export conditions::types::*; diff --git a/crates/rue-compiler/src/std/tree_hash.rue b/crates/rue-compiler/src/std/tree_hash.rue new file mode 100644 index 00000000..4d8bfbe9 --- /dev/null +++ b/crates/rue-compiler/src/std/tree_hash.rue @@ -0,0 +1,15 @@ +export fn tree_hash(value: Any) -> Bytes32 { + if value is Bytes { + tree_hash_atom(value) + } else { + tree_hash_pair(tree_hash(value.first), tree_hash(value.rest)) + } +} + +export inline fn tree_hash_atom(value: Bytes) -> Bytes32 { + sha256(1 as Bytes + value) +} + +export inline fn tree_hash_pair(first: Bytes32, rest: Bytes32) -> Bytes32 { + sha256(2 as Bytes + first + rest) +} diff --git a/crates/rue-diagnostic/src/srcloc.rs b/crates/rue-diagnostic/src/srcloc.rs index 46688bcb..80b1303d 100644 --- a/crates/rue-diagnostic/src/srcloc.rs +++ b/crates/rue-diagnostic/src/srcloc.rs @@ -16,21 +16,21 @@ impl Source { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum SourceKind { - Std, + Std(String), File(String), } impl SourceKind { pub fn check_unused(&self) -> bool { match self { - Self::Std => false, + Self::Std(_) => false, Self::File(_) => true, } } pub fn display(&self, relative_to: &Path) -> String { match self { - Self::Std => "std".to_string(), + Self::Std(path) => Path::new("std").join(path).to_string_lossy().to_string(), Self::File(path) => Path::new(path) .strip_prefix(relative_to) .map_or_else(|_| path.clone(), |path| path.to_string_lossy().to_string()), From 0d1b192c9b861e9a19cfd344851d446276ed7811 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 19 Nov 2025 22:13:02 +0800 Subject: [PATCH 2/6] Add no-std option --- crates/rue-compiler/src/Rue.toml | 3 ++ crates/rue-compiler/src/compiler.rs | 58 +++++++++++----------- crates/rue-lsp/src/cache.rs | 24 ++++++--- crates/rue-lsp/src/main.rs | 3 +- crates/rue-options/src/compiler_options.rs | 6 +++ crates/rue-options/src/find.rs | 16 ++++-- crates/rue-options/src/manifest.rs | 3 ++ wasm/src/lib.rs | 13 +++-- 8 files changed, 80 insertions(+), 46 deletions(-) create mode 100644 crates/rue-compiler/src/Rue.toml diff --git a/crates/rue-compiler/src/Rue.toml b/crates/rue-compiler/src/Rue.toml new file mode 100644 index 00000000..633b1d1e --- /dev/null +++ b/crates/rue-compiler/src/Rue.toml @@ -0,0 +1,3 @@ +[compiler] +entrypoint = "std" +std = false diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index f9404e6e..6ce2435e 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -68,36 +68,38 @@ impl Compiler { registered_scopes: HashSet::new(), }; - let tree = FileTree::load_std(&mut ctx).unwrap(); - tree.compile_impl(&mut ctx, false); - let prelude_file = tree - .find(&SourceKind::Std("prelude.rue".to_string())) - .unwrap(); - let prelude_scope = ctx.module(prelude_file.module).scope; - - let prelude = ctx.alloc_child_scope(); - - for (name, symbol) in ctx - .scope(prelude_scope) - .exported_symbols() - .map(|(name, symbol)| (name.to_string(), symbol)) - .collect::>() - { - ctx.scope_mut(prelude) - .insert_symbol(name.to_string(), symbol, false); - } + if options.std { + let tree = FileTree::load_std(&mut ctx).unwrap(); + tree.compile_impl(&mut ctx, false); + let prelude_file = tree + .find(&SourceKind::Std("prelude.rue".to_string())) + .unwrap(); + let prelude_scope = ctx.module(prelude_file.module).scope; + + let prelude = ctx.alloc_child_scope(); + + for (name, symbol) in ctx + .scope(prelude_scope) + .exported_symbols() + .map(|(name, symbol)| (name.to_string(), symbol)) + .collect::>() + { + ctx.scope_mut(prelude) + .insert_symbol(name.to_string(), symbol, false); + } - for (name, ty) in ctx - .scope(prelude_scope) - .exported_types() - .map(|(name, ty)| (name.to_string(), ty)) - .collect::>() - { - ctx.scope_mut(prelude) - .insert_type(name.to_string(), ty, false); - } + for (name, ty) in ctx + .scope(prelude_scope) + .exported_types() + .map(|(name, ty)| (name.to_string(), ty)) + .collect::>() + { + ctx.scope_mut(prelude) + .insert_type(name.to_string(), ty, false); + } - ctx.push_scope(prelude, prelude_file.document.syntax().text_range().start()); + ctx.push_scope(prelude, prelude_file.document.syntax().text_range().start()); + } ctx } diff --git a/crates/rue-lsp/src/cache.rs b/crates/rue-lsp/src/cache.rs index 327eca57..2511b217 100644 --- a/crates/rue-lsp/src/cache.rs +++ b/crates/rue-lsp/src/cache.rs @@ -2,10 +2,11 @@ use std::{collections::HashSet, sync::Arc}; use indexmap::IndexMap; use rowan::{TextRange, TextSize}; -use rue_compiler::{Compiler, CompletionContext, SyntaxItemKind, SyntaxMap}; +use rue_compiler::{Compiler, CompletionContext, FileTree, SyntaxItemKind, SyntaxMap}; use rue_diagnostic::{LineCol, Source, SourceKind}; use rue_hir::{ScopeId, Symbol, SymbolId}; use rue_types::{Type, TypeId, Union}; +use send_wrapper::SendWrapper; use tower_lsp::lsp_types::{ CompletionItem, CompletionItemKind, CompletionItemLabelDetails, Location, Position, Range, Url, }; @@ -54,16 +55,18 @@ pub struct FieldHoverInfo { #[derive(Debug, Clone)] pub struct Cache { ctx: T, + tree: Arc>, source: Source, syntax_map: SyntaxMap, } impl Cache> { - pub fn new(ctx: Arc, source: Source) -> Self { + pub fn new(ctx: Arc, tree: Arc>, source: Source) -> Self { let syntax_map = ctx.syntax_map().clone(); Self { ctx, + tree, source, syntax_map, } @@ -72,6 +75,7 @@ impl Cache> { pub fn to_cloned(&self) -> Cache { Cache { ctx: self.ctx.as_ref().clone(), + tree: self.tree.clone(), source: self.source.clone(), syntax_map: self.syntax_map.clone(), } @@ -492,12 +496,14 @@ impl Cache { .into_iter() .filter(|(_, kind)| matches!(kind, SourceKind::File(_))) .map(|(span, kind)| { - let SourceKind::File(path) = kind else { + let SourceKind::File(path) = &kind else { unreachable!(); }; - let start = LineCol::new(&self.source.text, span.start().into()); - let end = LineCol::new(&self.source.text, span.end().into()); + let text = &self.tree.find(&kind).unwrap().source.text; + + let start = LineCol::new(text, span.start().into()); + let end = LineCol::new(text, span.end().into()); let range = Range::new( Position::new(start.line as u32, start.col as u32), @@ -514,12 +520,14 @@ impl Cache { .into_iter() .filter(|(_, kind)| matches!(kind, SourceKind::File(_))) .map(|(span, kind)| { - let SourceKind::File(path) = kind else { + let SourceKind::File(path) = &kind else { unreachable!(); }; - let start = LineCol::new(&self.source.text, span.start().into()); - let end = LineCol::new(&self.source.text, span.end().into()); + let text = &self.tree.find(&kind).unwrap().source.text; + + let start = LineCol::new(text, span.start().into()); + let end = LineCol::new(text, span.end().into()); let range = Range::new( Position::new(start.line as u32, start.col as u32), diff --git a/crates/rue-lsp/src/main.rs b/crates/rue-lsp/src/main.rs index f3657f4a..21a0ec01 100644 --- a/crates/rue-lsp/src/main.rs +++ b/crates/rue-lsp/src/main.rs @@ -167,6 +167,7 @@ impl Backend { let mut cache = self.cache.lock().unwrap(); let ctx = Arc::new(ctx); + let tree = Arc::new(SendWrapper::new(tree)); for file in tree.all_files() { let SourceKind::File(path) = &file.source.kind else { @@ -177,7 +178,7 @@ impl Backend { cache.insert( uri, - SendWrapper::new(Cache::new(ctx.clone(), file.source.clone())), + SendWrapper::new(Cache::new(ctx.clone(), tree.clone(), file.source.clone())), ); diagnostics.entry(file.source.kind.clone()).or_default(); diff --git a/crates/rue-options/src/compiler_options.rs b/crates/rue-options/src/compiler_options.rs index 27ff7ba0..9e7c4892 100644 --- a/crates/rue-options/src/compiler_options.rs +++ b/crates/rue-options/src/compiler_options.rs @@ -1,5 +1,9 @@ #[derive(Debug, Clone, Copy)] +#[allow(clippy::struct_excessive_bools)] pub struct CompilerOptions { + /// Whether to compile the standard library. + pub std: bool, + /// Whether symbols which are only referenced once (including any parameters they have) /// should be inlined automatically (even if they are not marked as `inline`). pub auto_inline: bool, @@ -14,6 +18,7 @@ pub struct CompilerOptions { impl Default for CompilerOptions { fn default() -> Self { Self { + std: true, auto_inline: true, optimize_lir: true, debug_symbols: false, @@ -24,6 +29,7 @@ impl Default for CompilerOptions { impl CompilerOptions { pub fn debug() -> Self { Self { + std: true, auto_inline: false, optimize_lir: false, debug_symbols: true, diff --git a/crates/rue-options/src/find.rs b/crates/rue-options/src/find.rs index 6d9a0b1f..c8a7a1aa 100644 --- a/crates/rue-options/src/find.rs +++ b/crates/rue-options/src/find.rs @@ -34,13 +34,19 @@ pub fn find_project(path: &Path, debug: bool) -> Result, Error> .ok_or(Error::MissingParent)? .join(&manifest.compiler.entrypoint); + let mut options = if debug { + CompilerOptions::debug() + } else { + CompilerOptions::default() + }; + + if let Some(false) = manifest.compiler.std { + options.std = false; + } + return Ok(Some(Project { manifest: Some(manifest), - options: if debug { - CompilerOptions::debug() - } else { - CompilerOptions::default() - }, + options, entrypoint, })); } diff --git a/crates/rue-options/src/manifest.rs b/crates/rue-options/src/manifest.rs index 4a67ae73..d1734d29 100644 --- a/crates/rue-options/src/manifest.rs +++ b/crates/rue-options/src/manifest.rs @@ -11,6 +11,8 @@ pub struct CompilerSection { #[serde(skip_serializing_if = "Option::is_none")] pub version: Option, pub entrypoint: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub std: Option, } impl Default for CompilerSection { @@ -18,6 +20,7 @@ impl Default for CompilerSection { Self { version: Some(env!("CARGO_PKG_VERSION").to_string()), entrypoint: "puzzles".to_string(), + std: None, } } } diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 6be8555b..e8278ebc 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; +use std::{path::Path, sync::Arc}; use chialisp::classic::clvm_tools::binutils::disassemble; use clvmr::Allocator; use rue_compiler::{Compiler, File, FileTree}; -use rue_diagnostic::{Diagnostic, Source, SourceKind}; +use rue_diagnostic::{Source, SourceKind}; use rue_options::CompilerOptions; use wasm_bindgen::prelude::*; @@ -39,13 +39,18 @@ pub fn compile(source: String) -> Result { tree.compile(&mut ctx); let program = tree - .main(&mut ctx, &mut allocator, &kind)? + .main( + &mut ctx, + &mut allocator, + &kind, + Path::new(".").to_path_buf(), + )? .map(|program| disassemble(&allocator, program, None)); let diagnostics = ctx .take_diagnostics() .iter() - .map(Diagnostic::message) + .map(|diagnostic| diagnostic.message(Path::new("."))) .collect(); Ok(Compilation { From 53d26ee89e132bf093ac1a7d3ffa74e7dd8a30c1 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 19 Nov 2025 22:17:50 +0800 Subject: [PATCH 3/6] Reorg a bit --- crates/rue-compiler/src/std/main.rue | 26 ++++--------------------- crates/rue-compiler/src/std/prelude.rue | 10 +++++++--- crates/rue-compiler/src/std/utils.rue | 22 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 crates/rue-compiler/src/std/utils.rue diff --git a/crates/rue-compiler/src/std/main.rue b/crates/rue-compiler/src/std/main.rue index 67d0cff5..afa8ef2b 100644 --- a/crates/rue-compiler/src/std/main.rue +++ b/crates/rue-compiler/src/std/main.rue @@ -1,22 +1,4 @@ -export fn merge_list(a: List, b: List) -> List { - if a is (T, List) { - return [a.first, ...merge_list(a.rest, b)]; - } - b -} - -export fn deep_equal(a: Any, b: Any) -> Bool { - if a is Bytes { - if b is Bytes { - a == b - } else { - false - } - } else { - if b is Bytes { - false - } else { - deep_equal(a.first, b.first) && deep_equal(a.rest, b.rest) - } - } -} +export conditions; +export curry_tree_hash; +export tree_hash; +export utils; diff --git a/crates/rue-compiler/src/std/prelude.rue b/crates/rue-compiler/src/std/prelude.rue index ab99783c..ce3bde1f 100644 --- a/crates/rue-compiler/src/std/prelude.rue +++ b/crates/rue-compiler/src/std/prelude.rue @@ -1,6 +1,10 @@ -export main::{deep_equal, merge_list}; -export tree_hash::{tree_hash, tree_hash_atom, tree_hash_pair}; -export curry_tree_hash::curry_tree_hash; +export utils::*; +export tree_hash::*; +export curry_tree_hash::*; export conditions::message_flags::*; export conditions::opcodes::*; export conditions::types::*; + +export mod std { + export main::*; +} diff --git a/crates/rue-compiler/src/std/utils.rue b/crates/rue-compiler/src/std/utils.rue new file mode 100644 index 00000000..67d0cff5 --- /dev/null +++ b/crates/rue-compiler/src/std/utils.rue @@ -0,0 +1,22 @@ +export fn merge_list(a: List, b: List) -> List { + if a is (T, List) { + return [a.first, ...merge_list(a.rest, b)]; + } + b +} + +export fn deep_equal(a: Any, b: Any) -> Bool { + if a is Bytes { + if b is Bytes { + a == b + } else { + false + } + } else { + if b is Bytes { + false + } else { + deep_equal(a.first, b.first) && deep_equal(a.rest, b.rest) + } + } +} From 5b81c61bde375cf4134d613039aafa347aa0931b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 19 Nov 2025 22:23:04 +0800 Subject: [PATCH 4/6] Remove unnecessary main.rue --- crates/rue-compiler/src/std/main.rue | 4 ---- crates/rue-compiler/src/std/prelude.rue | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 crates/rue-compiler/src/std/main.rue diff --git a/crates/rue-compiler/src/std/main.rue b/crates/rue-compiler/src/std/main.rue deleted file mode 100644 index afa8ef2b..00000000 --- a/crates/rue-compiler/src/std/main.rue +++ /dev/null @@ -1,4 +0,0 @@ -export conditions; -export curry_tree_hash; -export tree_hash; -export utils; diff --git a/crates/rue-compiler/src/std/prelude.rue b/crates/rue-compiler/src/std/prelude.rue index ce3bde1f..f0d74d5c 100644 --- a/crates/rue-compiler/src/std/prelude.rue +++ b/crates/rue-compiler/src/std/prelude.rue @@ -6,5 +6,8 @@ export conditions::opcodes::*; export conditions::types::*; export mod std { - export main::*; + export conditions; + export curry_tree_hash; + export tree_hash; + export utils; } From 42b4bcd6601cbe83cd3995c9d02e1d61da32d88d Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 20 Nov 2025 09:59:48 +0800 Subject: [PATCH 5/6] Fix race condition --- crates/rue-tests/src/main.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/rue-tests/src/main.rs b/crates/rue-tests/src/main.rs index c2a55bdf..7d88a63c 100644 --- a/crates/rue-tests/src/main.rs +++ b/crates/rue-tests/src/main.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::collections::HashSet; use std::env; use std::fs; use std::path::Path; @@ -98,6 +99,7 @@ fn run_tests(filter_arg: Option<&str>, base_path: &Path, update: bool) -> Result fn walk_dir(path: &Path, filter_arg: Option<&str>, update: bool, failed: &mut bool) -> Result<()> { let mut directories = IndexMap::new(); + let mut directories_to_exclude = HashSet::new(); for entry in fs::read_dir(path)? { let entry = entry?; @@ -137,7 +139,7 @@ fn walk_dir(path: &Path, filter_arg: Option<&str>, update: bool, failed: &mut bo .join(format!("{name}.rue")) .try_exists()? { - directories.shift_remove(name); + directories_to_exclude.insert(name.to_string()); is_dir = true; } @@ -151,7 +153,10 @@ fn walk_dir(path: &Path, filter_arg: Option<&str>, update: bool, failed: &mut bo handle_test_file(name, &entry.path(), failed, update, is_dir)?; } - for (_, path) in directories { + for (name, path) in directories { + if directories_to_exclude.contains(&name) { + continue; + } walk_dir(&path, filter_arg, update, failed)?; } From ca8643dac6383eebf22d7aa8a093af87e78e1ece Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 20 Nov 2025 10:03:39 +0800 Subject: [PATCH 6/6] Display --- crates/rue-hir/Cargo.toml | 2 +- crates/rue-types/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rue-hir/Cargo.toml b/crates/rue-hir/Cargo.toml index 2dd14a82..67ec82ce 100644 --- a/crates/rue-hir/Cargo.toml +++ b/crates/rue-hir/Cargo.toml @@ -23,7 +23,7 @@ id-arena = { workspace = true } indexmap = { workspace = true } num-bigint = { workspace = true } hex = { workspace = true } -derive_more = { workspace = true } +derive_more = { workspace = true, features = ["display"] } log = { workspace = true } [dev-dependencies] diff --git a/crates/rue-types/Cargo.toml b/crates/rue-types/Cargo.toml index b88fcfe2..981c17cc 100644 --- a/crates/rue-types/Cargo.toml +++ b/crates/rue-types/Cargo.toml @@ -22,5 +22,5 @@ clvmr = { workspace = true } hex = { workspace = true } thiserror = { workspace = true } rstest = { workspace = true } -derive_more = { workspace = true } +derive_more = { workspace = true, features = ["display"] } log = { workspace = true }