From 053052284791c27c9fcd30309ea5b1e256bfeae8 Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Sun, 11 Sep 2022 18:02:56 +0200 Subject: [PATCH] [WIP] Add conditions --- Cargo.lock | 33 ++++ henceforth/Cargo.toml | 3 + henceforth/examples/test.fth | 7 +- henceforth/src/lib/compiler.rs | 210 ++++----------------- henceforth/src/lib/compiler/instruction.rs | 129 ++++++++++++- henceforth/src/lib/compiler/template.asm | 64 +++++++ henceforth/src/lib/parser.rs | 57 ++++++ henceforth/src/lib/parser/ast.rs | 15 ++ 8 files changed, 338 insertions(+), 180 deletions(-) create mode 100644 henceforth/src/lib/compiler/template.asm diff --git a/Cargo.lock b/Cargo.lock index 86732a8..19467d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,6 +147,15 @@ dependencies = [ "libc", ] +[[package]] +name = "dependency-graph" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5143247629540606d0888beae9ca0e0b9a81a32151bfecd0b2be4a961155c24d" +dependencies = [ + "petgraph", +] + [[package]] name = "digest" version = "0.9.0" @@ -168,6 +177,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "generic-array" version = "0.14.6" @@ -229,9 +244,11 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "dependency-graph", "hence", "indexmap", "itertools", + "lazy_static", "parse_int", ] @@ -263,6 +280,12 @@ dependencies = [ "either", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.132" @@ -329,6 +352,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "ppv-lite86" version = "0.2.16" diff --git a/henceforth/Cargo.toml b/henceforth/Cargo.toml index 84496ac..3f63dfc 100644 --- a/henceforth/Cargo.toml +++ b/henceforth/Cargo.toml @@ -22,3 +22,6 @@ anyhow = { version = "1.0.62", features = ["backtrace"] } itertools = "0.10.2" parse_int = "0.6.0" indexmap = "1.9.1" +lazy_static = "1.4.0" +dependency-graph = "0.1.5" + diff --git a/henceforth/examples/test.fth b/henceforth/examples/test.fth index 4651478..ad73a56 100644 --- a/henceforth/examples/test.fth +++ b/henceforth/examples/test.fth @@ -1,2 +1,7 @@ -0 @ @ debug +\ 1 1 = debug +\ if 69 else 42 then debug + +: test 42 ; +test debug drop +test test debug diff --git a/henceforth/src/lib/compiler.rs b/henceforth/src/lib/compiler.rs index 3ed16d1..a36a31c 100644 --- a/henceforth/src/lib/compiler.rs +++ b/henceforth/src/lib/compiler.rs @@ -1,6 +1,7 @@ use anyhow::{bail, Result}; use indexmap::IndexSet; use itertools::Itertools; +use lazy_static::lazy_static; use std::collections::HashMap; use crate::parser; @@ -19,21 +20,45 @@ pub struct Word { pub times_used: usize, } +#[derive(Debug)] +pub struct Condition { + pub if_instructions: Vec, + pub else_instructions: Vec, +} + #[derive(Debug)] pub struct Data { pub words: HashMap, + // pub word_graph: + pub conditions: IndexSet, pub strings: IndexSet, } +pub const TEMPLATE_ASM: &str = include_str!("compiler/template.asm"); + +lazy_static! { + #[derive(Debug)] + pub static ref TEMPLATE: hence::parser::ast::Body = hence::parser::parse( + hence::lexer::lex(TEMPLATE_ASM.to_string()).unwrap() + ) + .unwrap() + .body; +} + impl Data { pub fn default() -> Self { Self { words: HashMap::new(), + conditions: IndexSet::new(), strings: IndexSet::new(), } } - pub fn generate_instructions(&mut self, body: parser::ast::Body, optimize: bool) -> Result> { + pub fn generate_instructions( + &mut self, + body: parser::ast::Body, + optimize: bool, + ) -> Result> { let mut instructions: Vec = vec![]; let mut iter = body.into_iter().peekable(); while let Some(node) = iter.next() { @@ -90,6 +115,12 @@ impl Data { }, ); } + parser::ast::Node::Condition { if_body, else_body } => { + instructions.push(Instruction::Condition { + if_instructions: self.generate_instructions(if_body, optimize)?, + else_instructions: self.generate_instructions(else_body, optimize)?, + }); + } parser::ast::Node::Word(x) => { if let Some(ins) = Instruction::from_word(&x) { instructions.push(ins); @@ -106,174 +137,10 @@ impl Data { Ok(instructions) } - pub fn embed(&self, body: hence::parser::ast::Body) -> hence::parser::ast::Body { - let mut x = vec![ - // includes - hence::parser::ast::Node::MacroCall { - name: "include".to_string(), - args: vec![hence::arg::Arg::String("$lib/core.asm".to_string())], - }, - hence::parser::ast::Node::MacroCall { - name: "include".to_string(), - args: vec![hence::arg::Arg::String("$lib/std.asm".to_string())], - }, - hence::parser::ast::Node::MacroCall { - name: "include".to_string(), - args: vec![hence::arg::Arg::String("$lib/main.asm".to_string())], - }, - // constants - hence::parser::ast::Node::MacroCall { - name: "define".to_string(), - args: vec![ - hence::arg::Arg::Variable("MEM_CALL_STACK".to_string()), - hence::arg::Arg::Variable("CORE_MEM_MEM".to_string()), - ], - }, - hence::parser::ast::Node::MacroCall { - name: "define".to_string(), - args: vec![ - hence::arg::Arg::Variable("MEM_LOOP_I".to_string()), - hence::arg::Arg::BinaryExpression { - left: Box::new(hence::arg::Arg::Variable("CORE_MEM_MEM".to_string())), - right: Box::new(hence::arg::Arg::Number(16)), - op: hence::arg::BinaryExpressionOperator::Add, - }, - ], - }, - hence::parser::ast::Node::MacroCall { - name: "define".to_string(), - args: vec![ - hence::arg::Arg::Variable("MEM_LOOP_J".to_string()), - hence::arg::Arg::BinaryExpression { - left: Box::new(hence::arg::Arg::Variable("MEM_LOOP_I".to_string())), - right: Box::new(hence::arg::Arg::Number(1)), - op: hence::arg::BinaryExpressionOperator::Add, - }, - ], - }, - hence::parser::ast::Node::MacroCall { - name: "define".to_string(), - args: vec![ - hence::arg::Arg::Variable("MEM_ALLOC_PTR".to_string()), - hence::arg::Arg::BinaryExpression { - left: Box::new(hence::arg::Arg::Variable("MEM_LOOP_J".to_string())), - right: Box::new(hence::arg::Arg::Number(1)), - op: hence::arg::BinaryExpressionOperator::Add, - }, - ], - }, - // macros - // stack_transfer_alu - hence::parser::ast::Node::MacroCall { - name: "macro".to_string(), - args: vec![hence::arg::Arg::Variable("stack_transfer_alu".to_string())], - }, - hence::parser::ast::Node::MacroCall { - name: "std_ld".to_string(), - args: vec![], - }, - hence::parser::ast::Node::Call { - name: "tlr".to_string(), - arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), - }, - hence::parser::ast::Node::MacroCall { - name: "std_ld".to_string(), - args: vec![], - }, - hence::parser::ast::Node::Call { - name: "tlr".to_string(), - arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), - }, - hence::parser::ast::Node::MacroCall { - name: "endmacro".to_string(), - args: vec![], - }, - // call_word - hence::parser::ast::Node::MacroCall { - name: "macro".to_string(), - args: vec![ - hence::arg::Arg::Variable("call_word".to_string()), - hence::arg::Arg::Variable("call_word_arg_0_label".to_string()), - ], - }, - hence::parser::ast::Node::Call { - name: "ts".to_string(), - arg: Some(hence::arg::Arg::BinaryExpression { - left: Box::new(hence::arg::Arg::Variable("OFFSET".to_string())), - right: Box::new(hence::arg::Arg::Number(3 + 3 + 3 + 1 + 3 + 1)), - op: hence::arg::BinaryExpressionOperator::Add, - }), - }, - hence::parser::ast::Node::Call { - name: "tlr".to_string(), - arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), - }, - hence::parser::ast::Node::Call { - name: "ts".to_string(), - arg: Some(hence::arg::Arg::Variable("MEM_CALL_STACK".to_string())), - }, - hence::parser::ast::Node::Call { - name: "set".to_string(), - arg: None, - }, - hence::parser::ast::Node::Call { - name: "ts".to_string(), - arg: Some(hence::arg::Arg::Variable( - "call_word_arg_0_label".to_string(), - )), - }, - hence::parser::ast::Node::Call { - // tlr CORE_REG_PC -> tlr 0 -> tlr - name: "tlr".to_string(), - arg: Some(hence::arg::Arg::Variable("CORE_REG_PC".to_string())), - }, - hence::parser::ast::Node::MacroCall { - name: "endmacro".to_string(), - args: vec![], - }, - // return_word - hence::parser::ast::Node::MacroCall { - name: "macro".to_string(), - args: vec![hence::arg::Arg::Variable("return_word".to_string())], - }, - hence::parser::ast::Node::MacroCall { - name: "std_get".to_string(), - args: vec![hence::arg::Arg::Variable("MEM_CALL_STACK".to_string())], - }, - hence::parser::ast::Node::Call { - name: "tlr".to_string(), - arg: Some(hence::arg::Arg::Variable("CORE_REG_PC".to_string())), - }, - hence::parser::ast::Node::MacroCall { - name: "endmacro".to_string(), - args: vec![], - }, - // setup - hence::parser::ast::Node::MacroCall { - name: "std_rset".to_string(), - args: vec![ - hence::arg::Arg::Variable("CORE_REG_A".to_string()), - hence::arg::Arg::BinaryExpression { - left: Box::new(hence::arg::Arg::Variable("MEM_ALLOC_PTR".to_string())), - right: Box::new(hence::arg::Arg::Number(1)), - op: hence::arg::BinaryExpressionOperator::Add, - }, - ], - }, - hence::parser::ast::Node::MacroCall { - name: "std_set".to_string(), - args: vec![hence::arg::Arg::Variable("MEM_ALLOC_PTR".to_string())], - }, - // jump_main - hence::parser::ast::Node::MacroCall { - name: "jump_main".to_string(), - args: vec![], - }, - // data - hence::parser::ast::Node::Label("data".to_string()), - hence::parser::ast::Node::Label("data_strings".to_string()), - ]; + pub fn embed(&self, body: hence::parser::ast::Body) -> Result { + let mut x = TEMPLATE.to_vec(); + // strings for (id, s) in self.strings.iter().enumerate() { x.extend([ hence::parser::ast::Node::Label(format!("data_strings_{}", id)), @@ -286,7 +153,6 @@ impl Data { } // words - x.push(hence::parser::ast::Node::Label("words".to_string())); for (name, word) in &self .words .iter() @@ -308,7 +174,7 @@ impl Data { .flatten(), ); x.push(hence::parser::ast::Node::MacroCall { - name: "return_word".to_string(), + name: "return_call_stack_jump".to_string(), args: vec![], }); } @@ -326,7 +192,7 @@ impl Data { args: vec![], }); - x + Ok(x) } } @@ -344,6 +210,6 @@ pub fn compile(ast: parser::ast::AST, optimize: bool) -> Result, + else_instructions: Vec, + }, Multiple { instruction: Box, @@ -926,8 +930,98 @@ impl compiler::Compilable for Instruct arg: None, }, ]), - Instruction::Min => Ok(vec![]), - Instruction::Max => Ok(vec![]), + Instruction::Min => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_GT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlrc".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Max => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_LT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlrc".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), Instruction::Boolean => Ok(vec![ hence::parser::ast::Node::MacroCall { name: "std_ld".to_string(), @@ -1134,10 +1228,15 @@ impl compiler::Compilable for Instruct Instruction::Call(x) => match data.words.get(x) { Some(w) => { if w.times_used > 1 { - Ok(vec![hence::parser::ast::Node::MacroCall { - name: "call_word".to_string(), - args: vec![hence::arg::Arg::Variable(format!("words_{}", w.id))], - }]) + Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "call_stack_jump".to_string(), + args: vec![ + hence::arg::Arg::Variable(format!("words_{}", w.id)), + hence::arg::Arg::Variable("OFFSET".to_string()) + ], + } + ]) } else { Ok(w.instructions .iter() @@ -1153,6 +1252,23 @@ impl compiler::Compilable for Instruct Instruction::AsmQuote(x) => { Ok(hence::parser::parse(hence::lexer::lex(x.to_string())?)?.body) } + Instruction::Condition { + if_instructions, + else_instructions, + } => { + let if_compiled = if_instructions + .iter() + .map(|ins| ins.compile(data)) + .collect::>>()?; + let else_compiled = else_instructions + .iter() + .map(|ins| ins.compile(data)) + .collect::>>()?; + dbg!(if_compiled, else_compiled); + + Ok(vec![]) + } + Instruction::Multiple { instruction, count } => { if *count == 0 { Ok(vec![]) @@ -1418,4 +1534,3 @@ impl Instruction { } } } - diff --git a/henceforth/src/lib/compiler/template.asm b/henceforth/src/lib/compiler/template.asm new file mode 100644 index 0000000..672a8b3 --- /dev/null +++ b/henceforth/src/lib/compiler/template.asm @@ -0,0 +1,64 @@ +.include "$lib/core.asm" +.include "$lib/std.asm" +.include "$lib/main.asm" + +.define MEM_LOOP_I, CORE_MEM_MEM +.define MEM_LOOP_J, (MEM_LOOP_I + 1) +.define MEM_CALL_STACK_PTR, (MEM_LOOP_J + 1) +.define MEM_ALLOC_PTR, (MEM_CALL_STACK_PTR + 16) + +.macro stack_transfer_alu + .std_ld + tlr CORE_REG_B + .std_ld + tlr CORE_REG_A +.endmacro + +.macro call_stack_jump, prep_call_stack_jump_arg_0_label, prep_call_stack_jump_arg_1_offset + .std_rset CORE_REG_C, prep_call_stack_jump_arg_0_label + .std_rset CORE_REG_D, (prep_call_stack_jump_arg_1_offset + 7) + ts call_stack_jump + tlr CORE_REG_PC +.endmacro + +.macro return_call_stack_jump + .std_jump return_call_stack_jump +.endmacro + +.std_rset CORE_REG_A, MEM_CALL_STACK_PTR +.std_set MEM_CALL_STACK_PTR + +.std_rset CORE_REG_A, (MEM_ALLOC_PTR + 1) +.std_set MEM_ALLOC_PTR + +.jump_main + +call_stack_jump: + .std_get MEM_CALL_STACK_PTR + tlr CORE_REG_A + .std_rset CORE_REG_B, 1 + .std_alu CORE_ALU_ADD + tlr CORE_REG_A + tlr CORE_REG_B + .std_set MEM_CALL_STACK_PTR + + tsr CORE_REG_D + tlr CORE_REG_A + tsr CORE_REG_B + set + tsr CORE_REG_C + tlr CORE_REG_PC + +return_call_stack_jump: + .std_get MEM_CALL_STACK_PTR + tlr CORE_REG_A + tlr CORE_REG_C + .std_rset CORE_REG_B, 1 + .std_alu CORE_ALU_SUB + tlr CORE_REG_A + .std_set MEM_CALL_STACK_PTR + + tsr CORE_REG_C + get + tlr CORE_REG_PC + diff --git a/henceforth/src/lib/parser.rs b/henceforth/src/lib/parser.rs index 14a4faa..e29a9ee 100644 --- a/henceforth/src/lib/parser.rs +++ b/henceforth/src/lib/parser.rs @@ -100,6 +100,63 @@ pub fn parse(tokens: Vec) -> Result { body: parse(content.collect())?.body, }); } + "if" => { + let mut depth: usize = 1; + let mut else_used = false; + let if_toks: Vec<_> = iter + .by_ref() + .take_while(|t| match t { + lexer::Token::Word(x) => match x.as_str() { + "if" => { + depth += 1; + true + } + "else" => { + if depth == 1 { + else_used = true; + false + } else { + true + } + } + "then" => { + depth -= 1; + depth != 0 + } + _ => true, + }, + _ => true, + }) + .collect(); + let else_toks: Vec<_> = if else_used { + iter.by_ref() + .take_while(|t| match t { + lexer::Token::Word(x) => match x.as_str() { + "if" => { + depth += 1; + true + } + "then" => { + depth -= 1; + depth != 0 + } + _ => true, + }, + _ => true, + }) + .collect() + } else { + vec![] + }; + if depth != 0 { + bail!("Unbalanced conditions"); + } + + body.push(ast::Node::Condition { + if_body: parse(if_toks)?.body, + else_body: parse(else_toks)?.body, + }); + } _ => { body.push(ast::Node::Word(x)); } diff --git a/henceforth/src/lib/parser/ast.rs b/henceforth/src/lib/parser/ast.rs index 0bec15b..28f3ef8 100644 --- a/henceforth/src/lib/parser/ast.rs +++ b/henceforth/src/lib/parser/ast.rs @@ -38,6 +38,10 @@ pub enum Node { stack: Option, body: Body, }, + Condition { + if_body: Body, + else_body: Body, + }, Word(String), } @@ -56,6 +60,17 @@ impl ToCode for Node { }, body.iter().map(|x| x.to_code()).join(" ") ), + Node::Condition { if_body, else_body } => { + if else_body.is_empty() { + format!("if {} then", if_body.iter().map(|x| x.to_code()).join(" ")) + } else { + format!( + "if {} else {} then", + if_body.iter().map(|x| x.to_code()).join(" "), + else_body.iter().map(|x| x.to_code()).join(" ") + ) + } + } Node::Word(x) => x.clone(), } }