From: Bryan English Date: Sun, 23 Aug 2026 05:54:06 +0000 (-0400) Subject: cargo fmt and a big parser cleanup X-Git-Url: https://rethought.computer/gitweb//gitweb//git?a=commitdiff_plain;h=c2dbe1df49c6daa54dbe03f00e0650454472b268;p=sorel-lang.git cargo fmt and a big parser cleanup --- diff --git a/sorel-codegen/src/riscv64_asm.rs b/sorel-codegen/src/riscv64_asm.rs index b276366..bc8fde6 100644 --- a/sorel-codegen/src/riscv64_asm.rs +++ b/sorel-codegen/src/riscv64_asm.rs @@ -11,13 +11,12 @@ pub struct CodeGen<'a> { lines: Vec, } - // Some inspiration // ================ // // * https://github.com/aw/fiveforths/blob/master/docs/REFERENCE.md#registers-list // * Except using sp as a more C ABI style stack pointer, and s2 for the data stack -// +// // Implementation Choices // ====================== @@ -27,7 +26,6 @@ pub struct CodeGen<'a> { // Use t0, t1, t2 for temporary values in words // Data stack grows down - macro_rules! asm_macro { ($name:ident, $src:expr) => { fn $name(&mut self) { @@ -64,7 +62,6 @@ impl<'a> CodeGen<'a> { fn line(&mut self, line: S) { self.lines.push(format!(" {}", line)); - } fn label(&mut self, line: S) { @@ -104,7 +101,7 @@ impl<'a> CodeGen<'a> { self.push_from(reg); } - pub fn assembly(&mut self) -> Result{ + pub fn assembly(&mut self) -> Result { let mut string_table = HashMap::new(); // Static strings @@ -117,8 +114,8 @@ impl<'a> CodeGen<'a> { self.label(format!("{}:", string_label)); self.line(format!(".asciz \"{}\"", some_string)); // should this be .asciz? self.label(""); - }, - _ => bail!("Currently only string definitions are supported in the data section.") + } + _ => bail!("Currently only string definitions are supported in the data section."), } } @@ -153,24 +150,24 @@ impl<'a> CodeGen<'a> { } self.line("addi sp, sp, -16 # allocate 16 bytes on stack"); // allocate 16 bytes on stack - self.line("sd ra, 8(sp) # store return address on stack"); // store return address on stack - }, + self.line("sd ra, 8(sp) # store return address on stack"); // store return address on stack + } IR::Call(name) => { let mangled = mangle(name); self.label(format!("# call {}", mangled)); - self.line(format!("call {}", mangled)); - }, + self.line(format!("call {}", mangled)); + } IR::WordPointer(name) => { let mangled = mangle(name); self.label(format!("# '{} (word pointer)", mangled)); self.line(format!("la t0, {}", mangled)); self.push_from("t0"); - }, + } IR::CallPtr => { self.label("# callptr"); self.pop_to("t0"); self.line("jalr t0"); - }, + } IR::Ret => { if last_label == "main" { self.label("# exit 0 syscall"); @@ -178,104 +175,108 @@ impl<'a> CodeGen<'a> { self.line("mv a0, x0"); self.line("ecall"); } else { - self.line("ld ra, 8(sp)"); // load return address from stack + self.line("ld ra, 8(sp)"); // load return address from stack self.line("addi sp, sp, 16"); // restore stack pointer self.line("ret"); } - }, + } IR::VarDecl(name) => { var_decls.push(name); - }, + } IR::StackPushVar(name) => { self.label(format!("# stackpushvar {}", name)); self.line(format!("la t0, {}", name)); self.push_from("t0"); - }, + } IR::Load8 => { self.label("# load 8"); self.copy_top_stack_value_to("t0"); self.line("lbu t0, 0(t0)"); // deref pointer in t0 to t0 self.copy_to_top_of_stack("t0"); - }, + } IR::Load16 => { self.label("# load 16"); self.copy_top_stack_value_to("t0"); self.line("lhu t0, 0(t0)"); // deref pointer in t0 to t0 self.copy_to_top_of_stack("t0"); - }, + } IR::Load32 => { self.label("# load 32"); self.copy_top_stack_value_to("t0"); self.line("lwu t0, 0(t0)"); // deref pointer in t0 to t0 self.copy_to_top_of_stack("t0"); - }, + } IR::Load => { self.label("# load 64"); self.copy_top_stack_value_to("t0"); self.line("ld t0, 0(t0)"); // deref pointer in t0 to t0 self.copy_to_top_of_stack("t0"); - }, - IR::Store8 => { // ( x addr -- ) + } + IR::Store8 => { + // ( x addr -- ) self.label("# store 8"); self.pop_some_to("t0 t1"); self.line("sb t0, 0(t1)"); // store x at addr - }, - IR::Store16 => { // ( x addr -- ) + } + IR::Store16 => { + // ( x addr -- ) self.label("# store 16"); self.pop_some_to("t0 t1"); self.line("sh t0, 0(t1)"); // store x at addr - }, - IR::Store32 => { // ( x addr -- ) + } + IR::Store32 => { + // ( x addr -- ) self.label("# store 32"); self.pop_some_to("t0 t1"); self.line("sw t0, 0(t1)"); // store x at addr - }, - IR::Store => { // ( x addr -- ) + } + IR::Store => { + // ( x addr -- ) self.label("# store 64"); self.pop_some_to("t0 t1"); self.line("sd t0, 0(t1)"); // store x at addr - }, + } IR::StackPush(num) => { self.label(format!("# stackpush {}", num)); self.line(format!("li t0, {}", num)); self.push_from("t0"); - }, + } IR::StackPushString(name) => { self.label(format!("# stackpushstring {}", name)); self.line(format!("la t0, {}", name)); self.push_from("t0"); - }, + } IR::AddU64 => { self.label("# add"); - self.pop_call_push("t0 t1", "add t0, t0, t1", "t0"); - }, + self.pop_call_push("t0 t1", "add t0, t0, t1", "t0"); + } IR::SubtractU64 => { self.label("# sub"); - self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); - }, + self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); + } IR::MultiplyU64 => { self.label("# multiply"); - self.pop_call_push("t0 t1", "mul t0, t0, t1", "t0"); - }, + self.pop_call_push("t0 t1", "mul t0, t0, t1", "t0"); + } IR::DivideU64 => { self.label("# divide"); - self.pop_call_push("t0 t1", "div t0, t0, t1", "t0"); - }, + self.pop_call_push("t0 t1", "div t0, t0, t1", "t0"); + } IR::ModU64 => { self.label("# mod"); - self.pop_call_push("t0 t1", "rem t0, t0, t1", "t0"); - }, + self.pop_call_push("t0 t1", "rem t0, t0, t1", "t0"); + } IR::Dup => { self.label("# dup"); self.copy_top_stack_value_to("t0"); self.push_from("t0"); - }, + } IR::Swap => { self.label("# swap"); self.pop_some_to("t1 t0"); self.push_from("t0"); self.push_from("t1"); - }, + } IR::Over => { // TODO this is super inefficient. There's no need to pop anything. Just read // from the second stack position and push it. @@ -284,19 +285,19 @@ impl<'a> CodeGen<'a> { self.push_from("t0"); self.push_from("t1"); self.push_from("t0"); - }, + } IR::Rot => { self.label("# rot"); self.pop_some_to("t0 t1 t2"); self.push_from("t1"); self.push_from("t2"); self.push_from("t0"); - }, + } IR::StackPointer => { self.label("# sp"); self.line("addi t0, s2, 0"); self.push_from("t0"); - }, + } IR::StackBottom => { self.label("# stackbottom"); self.line("la t0, data_stack_end"); @@ -305,59 +306,59 @@ impl<'a> CodeGen<'a> { IR::Drop => { self.label("# drop"); self.move_stack_ptr_by_cells(1); - }, + } IR::Equals => { self.label("# equals"); // Yes, this is the same as subtract, since we're treating 0 as true, and // others as false. - self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); - }, + self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); + } IR::GreaterThan => { self.label("# >"); self.pop_some_to("t0 t1"); self.line("sgt t0, t0, t1"); self.line("seqz t0, t0"); // remember, 0 is true, others are false self.push_from("t0"); - }, + } IR::LessThan => { self.label("# <"); self.pop_some_to("t0 t1"); self.line("slt t0, t0, t1"); self.line("seqz t0, t0"); // remember, 0 is true, others are false self.push_from("t0"); - }, + } IR::BitwiseOr => { self.label("# |"); - self.pop_call_push("t0 t1", "or t0, t0, t1", "t0"); - }, + self.pop_call_push("t0 t1", "or t0, t0, t1", "t0"); + } IR::Sys0 => { self.label("# syscall 0 args"); self.pop_call_push("a7", "ecall", "a0"); - }, + } IR::Sys1 => { self.label("# syscall 1 arg"); self.pop_call_push("a0 a7", "ecall", "a0"); - }, + } IR::Sys2 => { self.label("# syscall 2 args"); self.pop_call_push("a0 a1 a7", "ecall", "a0"); - }, + } IR::Sys3 => { self.label("# syscall 3 args"); self.pop_call_push("a0 a1 a2 a7", "ecall", "a0"); - }, + } IR::Sys4 => { self.label("# syscall 4 args"); self.pop_call_push("a0 a1 a2 a3 a7", "ecall", "a0"); - }, + } IR::Sys5 => { self.label("# syscall 5 args"); self.pop_call_push("a0 a1 a2 a3 a4 a7", "ecall", "a0"); - }, + } IR::Sys6 => { self.label("# syscall 6 args"); self.pop_call_push("a0 a1 a2 a3 a4 a5 a7", "ecall", "a0"); - }, + } // https://cmput229.github.io/229-labs-RISCV/RISC-V-Examples_Public/03-Conditionals/03b-If_Else.html IR::If => { self.label("# if"); @@ -365,14 +366,14 @@ impl<'a> CodeGen<'a> { self.line(format!("bnez t0, _else_{}", if_block_count)); if_stack.push(if_block_count); if_block_count += 1; - }, + } IR::Else => { self.label("# else"); let if_counter = *if_stack.last().unwrap(); self.line(format!("j _endif_{}", if_counter)); self.label(format!("_else_{}:", if_counter)); seen_else.insert(if_counter); - }, + } IR::EndIf => { self.label("# endif"); let stack = &mut if_stack; @@ -384,21 +385,22 @@ impl<'a> CodeGen<'a> { seen_else.remove(&if_counter); } stack.pop(); - }, - IR::Loop => { // keep looping until is true/0 + } + IR::Loop => { + // keep looping until is true/0 self.label(format!("_loop_{}:", loop_count)); self.pop_to("t0"); self.line(format!("beqz t0, _endloop_{}", loop_count)); loop_stack.push(loop_count); loop_count += 1; - }, + } IR::EndLoop => { let stack = &mut loop_stack; let loop_counter = *stack.last().unwrap(); self.line(format!("j _loop_{}", loop_counter)); self.label(format!("_endloop_{}:", loop_counter)); stack.pop(); - }, + } _ => bail!("not implemented yet: {:?}", ir), } } @@ -417,4 +419,3 @@ impl<'a> CodeGen<'a> { Ok(self.lines.join("\n")) } } - diff --git a/sorel-ir/src/ir.rs b/sorel-ir/src/ir.rs index aa2dde0..3396df3 100644 --- a/sorel-ir/src/ir.rs +++ b/sorel-ir/src/ir.rs @@ -59,8 +59,12 @@ pub enum IR { } macro_rules! push_num { - ($num:ident) => { IR::StackPush(*$num as u64) }; - ($num:ident, $num_typ:ty) => { IR::StackPush(*$num as $num_typ as u64) }; + ($num:ident) => { + IR::StackPush(*$num as u64) + }; + ($num:ident, $num_typ:ty) => { + IR::StackPush(*$num as $num_typ as u64) + }; } impl IR { @@ -106,7 +110,7 @@ impl IR { "sys5" => IR::Sys5, "sys6" => IR::Sys6, // TODO num type specfic math like `+:i32`, etc. - _ => { + _ => { if let Some(actual_word) = word.strip_prefix("'") { IR::WordPointer(String::from(actual_word)) } else { @@ -114,12 +118,12 @@ impl IR { } } } - }, + } Token::String(text) => { let string_label = format!("string_{}", data.len()); data.push(IR::StringDef(string_label.clone(), String::from(*text))); IR::StackPushString(string_label) - }, + } Token::NumU8(num) => push_num!(num), Token::NumI8(num) => push_num!(num, u8), Token::NumU16(num) => push_num!(num), @@ -131,5 +135,5 @@ impl IR { Token::NumF32(num) => push_num!(num), Token::NumF64(num) => push_num!(num), } - } + } } diff --git a/sorel-ir/src/lib.rs b/sorel-ir/src/lib.rs index f27c139..c15f58d 100644 --- a/sorel-ir/src/lib.rs +++ b/sorel-ir/src/lib.rs @@ -6,4 +6,3 @@ pub use object::*; mod module; pub use module::*; - diff --git a/sorel-ir/src/module.rs b/sorel-ir/src/module.rs index f559177..f455599 100644 --- a/sorel-ir/src/module.rs +++ b/sorel-ir/src/module.rs @@ -1,6 +1,6 @@ -use std::rc::Rc; use std::cell::RefCell; use std::path::PathBuf; +use std::rc::Rc; use crate::ir::IR; @@ -9,7 +9,7 @@ pub type WrappedIRModule = Rc>; #[derive(Debug, PartialEq, Clone)] pub enum ModuleID { SourceFile(PathBuf), - StdSpecifier(String) + StdSpecifier(String), } impl Default for ModuleID { @@ -22,7 +22,7 @@ impl ModuleID { pub fn to_string(&self) -> String { match self { ModuleID::SourceFile(f) => f.to_string_lossy().to_string(), - ModuleID::StdSpecifier(s) => s.clone() + ModuleID::StdSpecifier(s) => s.clone(), } } } @@ -50,7 +50,7 @@ impl IRModule { if imported.exports.contains(name) { found = Some(imported.number); // Don't break here, since the last one should win. - } + } } if let Some(found) = found { format!("_m{}_{}", found, name) diff --git a/sorel-parser/src/lib.rs b/sorel-parser/src/lib.rs index 120243f..e269e06 100644 --- a/sorel-parser/src/lib.rs +++ b/sorel-parser/src/lib.rs @@ -1,5 +1,5 @@ +use anyhow::{Result, anyhow, bail}; use sorel_tokenizer::Token; -use anyhow::{Result, bail}; #[derive(Debug)] pub struct WordDefinition<'a> { @@ -7,7 +7,7 @@ pub struct WordDefinition<'a> { pub instructions: Vec>, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Module<'a> { pub words: Vec>, pub imports: Vec<&'a str>, @@ -15,97 +15,102 @@ pub struct Module<'a> { pub externs: Vec<&'a str>, } +enum LastMeta { + Import, + Export, + Extern, + None, +} + impl<'a> Module<'a> { pub fn parse(input: Vec>, is_entrypoint: bool) -> Result { - let mut result = vec![]; + let mut module = Module::default(); let mut main = vec![]; - let mut exports = vec![]; - let mut imports = vec![]; - let mut externs = vec![]; let mut current_word: Option = None; - let mut about_to_start_word_def = false; - let mut last_was_import = false; - let mut last_was_export = false; - let mut last_was_extern = false; + let mut last_was_colon = false; + let mut last_meta = LastMeta::None; for token in input { - if about_to_start_word_def { - if let Token::Word(name) = token { - current_word = Some(WordDefinition { - name, - instructions: vec![], - }); - about_to_start_word_def = false; - continue; - } else { - bail!("{:?} is not a valid word name!", token); - } - } else if let Token::Word(word) = token { + if last_was_colon { + // We're about to start defining a word, and the current token + // is the word's name. + current_word = Some(WordDefinition { + name: token.as_word()?, + instructions: vec![], + }); + last_was_colon = false; + continue; + } + + if let Token::Word(word) = token { if word == ":" { if current_word.is_some() { bail!("can't define words inside word definitions!"); } - about_to_start_word_def = true; + last_was_colon = true; continue; } if word == ";" { - let word = current_word.take(); - if let Some(word) = word { - result.push(word); - continue; - } else { - bail!("`;` must be at the end of a word definition"); - } + let word = current_word + .take() + .expect("`;` must be at the end of a word definition"); + module.words.push(word); + continue; } } + if let Some(ref mut current_word) = current_word { current_word.instructions.push(token); - } else { - match token { - Token::Word(word) => { - if word == "import" { - last_was_import = true; - } else if word == "export" { - last_was_export = true; - } else if word == "extern" { - last_was_extern = true; - } else if last_was_export { - exports.push(word); - last_was_export = false; - } else if last_was_extern { - externs.push(word); - last_was_extern = false; - } else { - main.push(token.clone()); - } - }, - Token::String(string) => { - if last_was_import { - imports.push(string); - last_was_import = false; - } else { - main.push(token.clone()); - } - }, - _ => { - main.push(token.clone()); - } - }; + continue; + } + + // From here on out, we're at the top level in the module, which + // means either a "meta" (import, export, or extern) or raw code + // to be added to the `main` word if we're in the entrypoint, or + // ignored otherwise. This macro makes the code a little tighter. + + macro_rules! push_token { + ($as_type:ident, $coll:ident, $bail1:expr, $bail2:expr) => {{ + let token = token + .$as_type() + .map_err(|_| anyhow!("`${}` must be followed by a ${}", $bail1, $bail2))?; + module.$coll.push(token); + LastMeta::None + }}; + () => {{ + main.push(token); + LastMeta::None + }}; } + + last_meta = match last_meta { + LastMeta::Import => push_token!(as_string, imports, "import", "string"), + LastMeta::Export => push_token!(as_word, exports, "export", "word"), + LastMeta::Extern => push_token!(as_word, externs, "extern", "word"), + LastMeta::None => match token { + Token::Word(word) => match word { + "import" => LastMeta::Import, + "export" => LastMeta::Export, + "extern" => LastMeta::Extern, + _ => push_token!(), + }, + _ => push_token!(), + }, + }; } - if about_to_start_word_def || current_word.is_some() { + if last_was_colon || current_word.is_some() { bail!("unfinished word definition!"); } if is_entrypoint { - result.push(WordDefinition { + module.words.push(WordDefinition { name: "main", instructions: main, }); } - - Ok(Module { words: result, imports, exports, externs }) + + Ok(module) } #[cfg(test)] @@ -119,20 +124,25 @@ impl<'a> Module<'a> { } } - - #[cfg(test)] mod tests { use super::*; #[test] fn try_some_parsing() { - let result = Module::parse(sorel_tokenizer::tokenize(" + let result = Module::parse( + sorel_tokenizer::tokenize( + " : hello world 16 \"planet\" ; : soup chicken 4.5 hello ; hello soup -").unwrap(), true).unwrap(); +", + ) + .unwrap(), + true, + ) + .unwrap(); result.debug_print(); } } diff --git a/sorel-tokenizer/src/lib.rs b/sorel-tokenizer/src/lib.rs index 86c2b42..417fce1 100644 --- a/sorel-tokenizer/src/lib.rs +++ b/sorel-tokenizer/src/lib.rs @@ -1,4 +1,4 @@ -use anyhow::{Result, anyhow}; +use anyhow::{Result, anyhow, bail}; use std::str::FromStr; #[derive(Debug, Clone)] @@ -18,7 +18,9 @@ pub enum Token<'a> { } trait IntFromStrRadix { - fn from_radix(src: &str, radix: u32) -> Result where Self: Sized; + fn from_radix(src: &str, radix: u32) -> Result + where + Self: Sized; } macro_rules! radix_impl { @@ -49,18 +51,41 @@ fn parse_int(num: &str) -> Result { } fn parse_float(num: &str) -> Result { - num.parse().map_err(|_| anyhow!("parse error for number: {}", num)) + num.parse() + .map_err(|_| anyhow!("parse error for number: {}", num)) } -impl<'a> Token<'a>{ +impl<'a> Token<'a> { + pub fn as_word(&self) -> Result<&'a str> { + if let Token::Word(string) = self { + Ok(string) + } else { + bail!("{:?} is not a valid word name!", self); + } + } + + pub fn as_string(&self) -> Result<&'a str> { + if let Token::String(string) = self { + Ok(string) + } else { + bail!("{:?} is not a valid string!", self); + } + } + fn parse_word_or_num(input: &'a str) -> Result> { if input == "-" { - return Ok(Token::Word(input)) + return Ok(Token::Word(input)); } - + // we're assuming any token starting with `-` with length greater than one // is a negative number - if input.starts_with('-') || input.chars().nth(0).map(|x| x.is_numeric()).unwrap_or(false) { + if input.starts_with('-') + || input + .chars() + .nth(0) + .map(|x| x.is_numeric()) + .unwrap_or(false) + { if input.contains(':') { let mut splat = input.split(':'); let num = splat.next().ok_or(anyhow!("no number found"))?; @@ -76,7 +101,7 @@ impl<'a> Token<'a>{ "i64" => Ok(Token::NumI64(parse_int(num)?)), "f32" => Ok(Token::NumF32(parse_float(num)?)), "f64" => Ok(Token::NumF64(parse_float(num)?)), - _ => panic!("unknown number type") + _ => panic!("unknown number type"), } } else if input.contains('.') { Ok(Token::NumF64(parse_float(input)?)) @@ -103,14 +128,13 @@ pub fn tokenize<'a>(input: &'a str) -> Result>> { let mut index = 0; let mut first_char = true; - for char in input.chars() { if first_char { first_char = false; } else { index += 1; } - + if in_doc_comment { if char == ')' { in_doc_comment = false; @@ -135,14 +159,13 @@ pub fn tokenize<'a>(input: &'a str) -> Result>> { string_start = None; } } else { - string_start = Some(index + 1) + string_start = Some(index + 1) } last_is_backslash = false; last_is_whitespace = false; continue; } - if string_start.is_some() { last_is_backslash = char == '\\'; continue; @@ -150,7 +173,7 @@ pub fn tokenize<'a>(input: &'a str) -> Result>> { if char.is_whitespace() { if last_is_backslash { - in_line_comment = true; + in_line_comment = true; } else if !last_is_whitespace && let Some(start) = word_or_num_start { let token = &input[start..index]; if token == "(" { @@ -174,7 +197,8 @@ pub fn tokenize<'a>(input: &'a str) -> Result>> { continue; } - if last_is_whitespace { // start of word or num (we already handled strings) + if last_is_whitespace { + // start of word or num (we already handled strings) word_or_num_start = Some(index); last_is_whitespace = false; } @@ -188,17 +212,20 @@ mod tests { #[test] fn try_some_tokenizing() { - let result = tokenize(" + let result = tokenize( + " \\ soup 2 0x10 3.4 - -88 bacon \"hello\" 43:f32 2345:u32 -57:i8 soup -"); +", + ); println!("result: {:?}", result); } #[test] fn comments() { - let result = tokenize(" + let result = tokenize( + " ( foo bar @@ -207,16 +234,19 @@ mod tests { chicken soup ; - "); + ", + ); println!("result: {:?}", result); } #[test] fn strings() { - let result = tokenize(" + let result = tokenize( + " dup \\ ( stuff ) \"hello!\" -"); +", + ); println!("result: {:?}", result); } } diff --git a/sorelc/src/import_tree.rs b/sorelc/src/import_tree.rs index 6ac87d1..2d88abf 100644 --- a/sorelc/src/import_tree.rs +++ b/sorelc/src/import_tree.rs @@ -1,14 +1,14 @@ -use sorel_parser::Module; use sorel_ir::*; +use sorel_parser::Module; use sorel_tokenizer::tokenize; -use std::collections::{HashSet, HashMap}; -use std::path::PathBuf; -use std::rc::Rc; use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; use std::include_str; +use std::path::PathBuf; +use std::rc::Rc; -use anyhow::{Result, bail, anyhow}; +use anyhow::{Result, anyhow, bail}; #[derive(Default)] pub(crate) struct ImportTree { @@ -32,14 +32,22 @@ fn std_import(specifier: &str) -> Result<&str> { } impl ImportTree { - fn import(&mut self, importer_dir: &PathBuf, specifier: &str, is_entrypoint: bool) -> Result { + fn import( + &mut self, + importer_dir: &PathBuf, + specifier: &str, + is_entrypoint: bool, + ) -> Result { let (contents, module_id) = if specifier.starts_with("std:") { if self.all_modules.contains_key(specifier) { let module = self.all_modules.get(specifier).unwrap().clone(); return Ok(module); } let contents = std_import(specifier)?; - (contents.to_string(), ModuleID::StdSpecifier(specifier.to_string())) + ( + contents.to_string(), + ModuleID::StdSpecifier(specifier.to_string()), + ) } else { let mut path = PathBuf::from(specifier); if path.is_relative() { @@ -60,7 +68,8 @@ impl ImportTree { let parsed = &Module::parse(tokens, is_entrypoint)?; let module = self.ir_mod_from_parsed(module_id.clone(), parsed)?; let module = Rc::new(RefCell::new(module)); - self.all_modules.insert(module_id.to_string(), module.clone()); + self.all_modules + .insert(module_id.to_string(), module.clone()); if is_entrypoint { self.entrypoint = module.clone(); } @@ -75,65 +84,76 @@ impl ImportTree { let mut imports = vec![]; let parent_path = match module_id { - ModuleID::SourceFile(ref path) => { - path.parent().ok_or(anyhow!("no parent for path: {:?}", path))?.to_path_buf() - }, + ModuleID::SourceFile(ref path) => path + .parent() + .ok_or(anyhow!("no parent for path: {:?}", path))? + .to_path_buf(), // A stdlib module can only import other stdlib // modules, so no need for parent path. ModuleID::StdSpecifier(_) => PathBuf::new(), }; - module.imports.iter().try_for_each(|imported| -> Result<()> { - let new_module = self.import(&parent_path, imported, false)?; - imports.push(new_module); - Ok(()) - })?; + module + .imports + .iter() + .try_for_each(|imported| -> Result<()> { + let new_module = self.import(&parent_path, imported, false)?; + imports.push(new_module); + Ok(()) + })?; - let exports: Vec<_> = module.exports.iter().map(|s| { - self.all_exports.insert(s.to_string()); - s.to_string() - }).collect(); + let exports: Vec<_> = module + .exports + .iter() + .map(|s| { + self.all_exports.insert(s.to_string()); + s.to_string() + }) + .collect(); let externs = module.externs.iter().map(|s| s.to_string()).collect(); - module.words.iter().try_for_each(|def| -> Result<(), anyhow::Error> { - let mut body = vec![]; - let mut last_call_was_var = false; - let mut vars = vec![]; - def.instructions.iter().try_for_each(|inst| { - let new_token = IR::from_token(inst, &mut data); - if let IR::Call(thing) = new_token { - if thing == "var" { - last_call_was_var = true; - } else { - if last_call_was_var { - body.push(IR::VarDecl(thing.clone())); - vars.push(thing); - last_call_was_var = false; + module + .words + .iter() + .try_for_each(|def| -> Result<(), anyhow::Error> { + let mut body = vec![]; + let mut last_call_was_var = false; + let mut vars = vec![]; + def.instructions.iter().try_for_each(|inst| { + let new_token = IR::from_token(inst, &mut data); + if let IR::Call(thing) = new_token { + if thing == "var" { + last_call_was_var = true; } else { - if vars.contains(&thing) { - body.push(IR::StackPushVar(thing)); + if last_call_was_var { + body.push(IR::VarDecl(thing.clone())); + vars.push(thing); + last_call_was_var = false; } else { - body.push(IR::Call(thing)); + if vars.contains(&thing) { + body.push(IR::StackPushVar(thing)); + } else { + body.push(IR::Call(thing)); + } } } - } - } else { - if last_call_was_var { - bail!("word must come after var!") } else { - body.push(new_token); + if last_call_was_var { + bail!("word must come after var!") + } else { + body.push(new_token); + } } - } + Ok(()) + })?; + + let mut result = vec![IR::Label(def.name.to_string())]; + result.append(&mut body); + result.push(IR::Ret); + text.push(result); Ok(()) })?; - let mut result = vec![IR::Label(def.name.to_string())]; - result.append(&mut body); - result.push(IR::Ret); - text.push(result); - Ok(()) - })?; - let number = self.module_count; self.module_count += 1; @@ -152,7 +172,7 @@ impl ImportTree { let module = module.borrow_mut(); let seen_key = module.module_id.to_string(); if self.collapse_seen.contains(&seen_key) { - return Ok(()) + return Ok(()); } for imported in module.imports.clone() { @@ -176,7 +196,7 @@ impl ImportTree { IR::StackPushString(name) => { let new_name = format!("{}_{}", name, module_number); IR::StackPushString(new_name) - }, + } IR::Label(name) => { if is_entrypoint && name == "main" { last_label_name = String::from("main"); @@ -186,20 +206,14 @@ impl ImportTree { last_label_name = label_name.clone(); IR::Label(module.get_label(name)) } - }, - IR::VarDecl(name) => { - IR::VarDecl(format!("_var_{}_{}", last_label_name, name)) - }, + } + IR::VarDecl(name) => IR::VarDecl(format!("_var_{}_{}", last_label_name, name)), IR::StackPushVar(name) => { IR::StackPushVar(format!("_var_{}_{}", last_label_name, name)) - }, - IR::Call(name) => { - IR::Call(module.get_label_for_call(name)) - }, - IR::WordPointer(name) => { - IR::WordPointer(module.get_label_for_call(name)) - }, - _ => instruction.clone() + } + IR::Call(name) => IR::Call(module.get_label_for_call(name)), + IR::WordPointer(name) => IR::WordPointer(module.get_label_for_call(name)), + _ => instruction.clone(), }; self.text.push(new_instruction); } @@ -212,10 +226,10 @@ impl ImportTree { pub fn build_and_collapse(path: &str) -> Result { let dir = std::env::current_dir()?; - let mut tree: ImportTree = Default::default(); + let mut tree: ImportTree = Default::default(); let module = tree.import(&dir, path, true)?; tree.collapse(module, true)?; - // TODO remove unused words + // TODO remove unused words Ok(IRObject { data: tree.data, text: tree.text, diff --git a/sorelc/src/main.rs b/sorelc/src/main.rs index 18c93a2..4c78dea 100644 --- a/sorelc/src/main.rs +++ b/sorelc/src/main.rs @@ -8,7 +8,9 @@ use std::io::Write; use std::path::PathBuf; fn main() -> Result<()> { - let filename = std::env::args().nth(1).expect("must provide a file to compile"); + let filename = std::env::args() + .nth(1) + .expect("must provide a file to compile"); let module = import_tree::build_and_collapse(&filename)?; let mut generator = CodeGen::new(&module, 4096); let mut asm_path = PathBuf::from(filename);