From af62aae5ee593617cc71644932aff6acef3d9857 Mon Sep 17 00:00:00 2001 From: Bryan English Date: Tue, 3 Feb 2026 22:04:05 -0500 Subject: [PATCH] implement stdlib in import system --- rel-lang/rel-interpret/src/lib.rs | 3 - rel-lang/rel-ir/src/lib.rs | 3 +- rel-lang/relc/src/ir.rs | 102 +++++++++++++++++++++--------- rel-lang/stdlib/mem.rel | 2 - rel-lang/stdlib/out.rel | 5 +- rel-lang/stdlib/putn.c | 16 ----- rel-lang/stdlib/test.sh | 16 ----- rel-lang/tests/test.sh | 9 +++ rel-lang/tests/test1.rel | 14 ++++ 9 files changed, 100 insertions(+), 70 deletions(-) delete mode 100644 rel-lang/stdlib/putn.c delete mode 100644 rel-lang/stdlib/test.sh create mode 100644 rel-lang/tests/test.sh create mode 100644 rel-lang/tests/test1.rel diff --git a/rel-lang/rel-interpret/src/lib.rs b/rel-lang/rel-interpret/src/lib.rs index 68fa489..edd6e48 100644 --- a/rel-lang/rel-interpret/src/lib.rs +++ b/rel-lang/rel-interpret/src/lib.rs @@ -102,9 +102,6 @@ impl<'a> Interpreter<'a> { let a = self.ds_pop()?; self.data_stack.push(a - b); }, - IR::PutN => { - println!("{}", self.data_stack.last().ok_or(anyhow!("empty data stack"))?); - }, IR::Dup => { self.data_stack.push(*self.data_stack.last().ok_or(anyhow!("empty data stack"))?); }, diff --git a/rel-lang/rel-ir/src/lib.rs b/rel-lang/rel-ir/src/lib.rs index 699dbba..919a08f 100644 --- a/rel-lang/rel-ir/src/lib.rs +++ b/rel-lang/rel-ir/src/lib.rs @@ -27,13 +27,14 @@ pub enum IR { ModU64, Equals, GreaterThan, + LessThan, BitwiseOr, Dup, Swap, Drop, Over, Rot, - PutN, + StackPointer, If, Else, EndIf, diff --git a/rel-lang/relc/src/ir.rs b/rel-lang/relc/src/ir.rs index aa50b18..55006f6 100644 --- a/rel-lang/relc/src/ir.rs +++ b/rel-lang/relc/src/ir.rs @@ -5,6 +5,7 @@ use rel_ir::*; use std::collections::{HashSet, HashMap}; use std::path::PathBuf; use std::rc::Rc; +use std::include_str; use anyhow::{Result, bail}; @@ -19,7 +20,9 @@ struct IRModule { text: Vec, imports: Vec>, exports: Vec, - source_file: PathBuf, + // TODO these next two should form an enum, not two options + source_file: Option, + std_specifier: Option, number: usize, } @@ -49,46 +52,80 @@ impl IRModule { struct ImportTree { data: Vec, text: Vec, - all_modules: HashMap>, + all_modules: HashMap>, all_exports: HashSet, entrypoint: Rc, module_count: usize, - collapse_seen: HashSet, + collapse_seen: HashSet, +} + +fn std_import(specifier: &str) -> Result<&str> { + match specifier { + "std:mem" => Ok(include_str!("../../stdlib/mem.rel")), + "std:out" => Ok(include_str!("../../stdlib/out.rel")), + _ => bail!("{} is not a standard library module", specifier), + } } impl ImportTree { fn import(&mut self, importer_dir: &PathBuf, specifier: &str, is_entrypoint: bool) -> Result> { - let mut path = PathBuf::from(specifier); - if path.is_relative() { - let mut new_path = importer_dir.clone(); - new_path.push(path); - path = new_path.canonicalize()?; - } - if self.all_modules.contains_key(&path) { - let module = self.all_modules.get(&path).unwrap().clone(); - return Ok(module); - } + Ok(if specifier.starts_with("std:") { + let contents = std_import(specifier)?; + let parsed = &Module::parse(tokenize(&contents)?, is_entrypoint)?; + let module = self.generate_internal(None, Some(specifier.to_string()), parsed); + let module = Rc::new(module); + self.all_modules.insert(specifier.to_string(), module.clone()); + module + } else { + let mut path = PathBuf::from(specifier); + if path.is_relative() { + let mut new_path = importer_dir.clone(); + new_path.push(path); + path = new_path.canonicalize()?; + } + let path_key = path.to_string_lossy().to_string(); + if self.all_modules.contains_key(&path_key) { + let module = self.all_modules.get(&path_key).unwrap().clone(); + return Ok(module); + } - let contents = std::fs::read_to_string(&path)?; + let contents = &std::fs::read_to_string(&path)?; + let module = self.generate_internal(Some(path), None, &Module::parse(tokenize(&contents)?, is_entrypoint)?); + let module = Rc::new(module); + self.all_modules.insert(path_key, module.clone()); + if is_entrypoint { + self.entrypoint = module.clone(); + } + module + }) - let module = self.generate_internal(path, &Module::parse(tokenize(&contents)?, is_entrypoint)?); - let module = Rc::new(module); - self.all_modules.insert(module.source_file.clone(), module.clone()); - if is_entrypoint { - self.entrypoint = module.clone(); - } - Ok(module) } - fn generate_internal(&mut self, path: PathBuf, module: &Module) -> IRModule { + fn generate_internal(&mut self, path: Option, std_specifier: Option, module: &Module) -> IRModule { // Eventually these will end up being sections in assembly let mut text = vec![]; let mut data = vec![]; let mut imports = vec![]; - module.imports.iter().for_each(|imported| { - if let Some(parent_path) = path.parent() { - match self.import(&parent_path.to_path_buf(), imported, false) { + if let Some(ref path) = path { + module.imports.iter().for_each(|imported| { + if let Some(parent_path) = path.parent() { + match self.import(&parent_path.to_path_buf(), imported, false) { + Ok(module) => { + imports.push(module); + }, + Err(msg) => { + eprintln!("{}", msg); + } + } + } else { + } + }); + } else { + // We're in a stdlib module, which can only import other stdlib + // modules. + module.imports.iter().for_each(|imported| { + match self.import(&PathBuf::new(), imported, false) { Ok(module) => { imports.push(module); }, @@ -96,9 +133,8 @@ impl ImportTree { eprintln!("{}", msg); } } - } else { - } - }); + }); + } let exports: Vec<_> = module.exports.iter().map(|s| { self.all_exports.insert(s.to_string()); @@ -181,12 +217,18 @@ impl ImportTree { imports, exports, source_file: path, + std_specifier, number, } } fn collapse(&mut self, module: Rc) -> Result<()> { - if self.collapse_seen.contains(&module.source_file) { + let seen_key = if let Some(source_file) = &module.source_file { + source_file.to_string_lossy().to_string() + } else { + module.std_specifier.clone().unwrap() + }; + if self.collapse_seen.contains(&seen_key) { return Ok(()) } @@ -228,6 +270,8 @@ impl ImportTree { self.text.push(new_instruction); } + self.collapse_seen.insert(seen_key); + Ok(()) } } diff --git a/rel-lang/stdlib/mem.rel b/rel-lang/stdlib/mem.rel index 49d14db..da5bfca 100644 --- a/rel-lang/stdlib/mem.rel +++ b/rel-lang/stdlib/mem.rel @@ -42,5 +42,3 @@ export free dealloc ; -1024 alloc -free diff --git a/rel-lang/stdlib/out.rel b/rel-lang/stdlib/out.rel index 48dfd94..000b320 100644 --- a/rel-lang/stdlib/out.rel +++ b/rel-lang/stdlib/out.rel @@ -17,6 +17,8 @@ - \ ( len ) ; +export puts + : puts ( addr -- addr) dup dup \ ( addr addr addr ) strlen \ ( addr addr len ) @@ -27,8 +29,6 @@ drop \ ( addr ) ; -"Hello, World! \n" puts - : ZERO_CHAR 48 ; : NEWLINE_CHAR 10 ; @@ -67,4 +67,3 @@ export putn drop \ ( num ) ; -28 putn diff --git a/rel-lang/stdlib/putn.c b/rel-lang/stdlib/putn.c deleted file mode 100644 index 201050c..0000000 --- a/rel-lang/stdlib/putn.c +++ /dev/null @@ -1,16 +0,0 @@ -#include - -extern unsigned long data_stack_end; -register unsigned long * stack_pointer asm("s2"); - -void putn() { - unsigned long * stack_index = &data_stack_end; - printf("stack: "); - while (stack_index != stack_pointer) { - printf("%ld ", *stack_index); - stack_index -= 1; - } - printf("%ld\n", *stack_pointer); -} - - diff --git a/rel-lang/stdlib/test.sh b/rel-lang/stdlib/test.sh deleted file mode 100644 index 0bef584..0000000 --- a/rel-lang/stdlib/test.sh +++ /dev/null @@ -1,16 +0,0 @@ -UNAME=$(uname -m) -CMD_PREFIX=$([ "$UNAME" = "riscv64" ] && echo "" || echo "riscv64-unknown-linux-gnu-") -AS="${CMD_PREFIX}as" -CC="${CMD_PREFIX}cc" - - -# ../target/debug/relc mem.rel -# $AS -o mem.o mem.asm -# $CC -O1 -no-pie -o test-mem.out putn.c mem.o -nostartfiles -# ./test-mem.out - - -../target/debug/relc out.rel -$AS -o out.o out.asm -$CC -O1 -no-pie -o test-out.out out.o putn.c -nostartfiles -./test-out.out diff --git a/rel-lang/tests/test.sh b/rel-lang/tests/test.sh new file mode 100644 index 0000000..328e1e6 --- /dev/null +++ b/rel-lang/tests/test.sh @@ -0,0 +1,9 @@ +UNAME=$(uname -m) +CMD_PREFIX=$([ "$UNAME" = "riscv64" ] && echo "" || echo "riscv64-unknown-linux-gnu-") +AS="${CMD_PREFIX}as" +LD="${CMD_PREFIX}ld" + +../target/debug/relc test1.rel +$AS -o test1.o test1.asm +$LD -o test1.out test1.o +./test1.out diff --git a/rel-lang/tests/test1.rel b/rel-lang/tests/test1.rel new file mode 100644 index 0000000..0adf96a --- /dev/null +++ b/rel-lang/tests/test1.rel @@ -0,0 +1,14 @@ +\ vim: filetype=forth + +import "std:out" + +"Hello, World! \n" puts +drop + +42 putn +drop + +import "std:mem" + +64 alloc +free -- 2.43.0