lines: Vec<String>,
}
-
// 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
// ======================
// 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) {
fn line<S: Display>(&mut self, line: S) {
self.lines.push(format!(" {}", line));
-
}
fn label<S: Display>(&mut self, line: S) {
self.push_from(reg);
}
- pub fn assembly(&mut self) -> Result<String>{
+ pub fn assembly(&mut self) -> Result<String> {
let mut string_table = HashMap::new();
// Static strings
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."),
}
}
}
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");
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.
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");
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");
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;
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),
}
}
Ok(self.lines.join("\n"))
}
}
-
}
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 {
"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 {
}
}
}
- },
+ }
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),
Token::NumF32(num) => push_num!(num),
Token::NumF64(num) => push_num!(num),
}
- }
+ }
}
mod module;
pub use module::*;
-
-use std::rc::Rc;
use std::cell::RefCell;
use std::path::PathBuf;
+use std::rc::Rc;
use crate::ir::IR;
#[derive(Debug, PartialEq, Clone)]
pub enum ModuleID {
SourceFile(PathBuf),
- StdSpecifier(String)
+ StdSpecifier(String),
}
impl Default for 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(),
}
}
}
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)
+use anyhow::{Result, anyhow, bail};
use sorel_tokenizer::Token;
-use anyhow::{Result, bail};
#[derive(Debug)]
pub struct WordDefinition<'a> {
pub instructions: Vec<Token<'a>>,
}
-#[derive(Debug)]
+#[derive(Debug, Default)]
pub struct Module<'a> {
pub words: Vec<WordDefinition<'a>>,
pub imports: Vec<&'a str>,
pub externs: Vec<&'a str>,
}
+enum LastMeta {
+ Import,
+ Export,
+ Extern,
+ None,
+}
+
impl<'a> Module<'a> {
pub fn parse(input: Vec<Token<'a>>, is_entrypoint: bool) -> Result<Self> {
- 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<WordDefinition> = 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)]
}
}
-
-
#[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();
}
}
-use anyhow::{Result, anyhow};
+use anyhow::{Result, anyhow, bail};
use std::str::FromStr;
#[derive(Debug, Clone)]
}
trait IntFromStrRadix {
- fn from_radix(src: &str, radix: u32) -> Result<Self> where Self: Sized;
+ fn from_radix(src: &str, radix: u32) -> Result<Self>
+ where
+ Self: Sized;
}
macro_rules! radix_impl {
}
fn parse_float<T: FromStr>(num: &str) -> Result<T> {
- 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<Token<'a>> {
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"))?;
"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)?))
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;
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;
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 == "(" {
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;
}
#[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
chicken
soup
;
- ");
+ ",
+ );
println!("result: {:?}", result);
}
#[test]
fn strings() {
- let result = tokenize("
+ let result = tokenize(
+ "
dup \\ ( stuff )
\"hello!\"
-");
+",
+ );
println!("result: {:?}", result);
}
}
-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 {
}
impl ImportTree {
- fn import(&mut self, importer_dir: &PathBuf, specifier: &str, is_entrypoint: bool) -> Result<WrappedIRModule> {
+ fn import(
+ &mut self,
+ importer_dir: &PathBuf,
+ specifier: &str,
+ is_entrypoint: bool,
+ ) -> Result<WrappedIRModule> {
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() {
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();
}
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;
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() {
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");
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);
}
pub fn build_and_collapse(path: &str) -> Result<IRObject> {
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,
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);