use anyhow::{bail, Result}; use indexmap::IndexSet; use itertools::Itertools; use lazy_static::lazy_static; use std::collections::HashMap; use crate::parser; mod instruction; pub use crate::compiler::instruction::Instruction; pub trait Compilable { fn compile(&self, data: &T) -> Result; } #[derive(Debug)] pub struct Word { pub id: usize, pub instructions: Vec, pub times_used: usize, } #[derive(Debug, PartialEq, Eq, Hash)] 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).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> { let mut instructions: Vec = vec![]; let mut iter = body.into_iter().peekable(); while let Some(node) = iter.next() { match node { _ if optimize && iter.next_if_eq(&node).is_some() => { let count = iter.by_ref().peeking_take_while(|n| *n == node).count() + 2; instructions.push(Instruction::Multiple { instruction: Box::new( self.generate_instructions(vec![node], optimize)? .into_iter() .next() .unwrap(), ), count, }); } parser::ast::Node::Comment(_) => {} parser::ast::Node::String { mode, string } => { instructions.push(match mode.as_str() { "." => { let id = self.strings.insert_full(string).0; Instruction::StringPrint(id) } "r" => { let id = self.strings.insert_full(string).0; Instruction::StringReference(id) } "asm" => Instruction::AsmQuote(string), _ => bail!("Unknown string mode: {}", mode), }); } parser::ast::Node::Number(x) => { instructions.push(instruction::Instruction::Push(x as u16)); } parser::ast::Node::WordDefinition { name, stack: _, body, } => { if Instruction::from_word(&name).is_some() { bail!("Word already exists as compiler instruction: {}", name); } else if self.words.contains_key(&name) { bail!("Word already exists as user word definition: {}", name); } let ins = self.generate_instructions(body, optimize)?; self.words.insert( name, Word { id: self.words.len(), instructions: ins, times_used: 0, }, ); } parser::ast::Node::Condition { if_body, else_body } => { let if_instructions = self.generate_instructions(if_body, optimize)?; let else_instructions = self.generate_instructions(else_body, optimize)?; let id = self .conditions .insert_full(Condition { if_instructions, else_instructions, }) .0; instructions.push(Instruction::Condition(id)); } parser::ast::Node::Word(x) => { if let Some(ins) = Instruction::from_word(&x) { instructions.push(ins); } else if let Some(w) = self.words.get_mut(&x) { instructions.push(Instruction::Call(x)); w.times_used += 1; } else { bail!("Word does not exist: {}", x); } } } } Ok(instructions) } 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)), hence::parser::ast::Node::MacroCall { name: "bytes".to_string(), args: vec![hence::arg::Arg::String(s.to_string())], }, hence::parser::ast::Node::Label(format!("data_strings_end_{}", id)), ]); } // conditions for (id, c) in self.conditions.iter().enumerate() { x.push(hence::parser::ast::Node::Label(format!("conditions_{}", id))); x.extend( ); } // words for (name, word) in &self .words .iter() .filter(|(_, w)| w.times_used > 1) .sorted_by(|a, b| Ord::cmp(&a.1.id, &b.1.id)) .collect::>() { x.extend(vec![ hence::parser::ast::Node::Label(format!("words_{}", word.id)), hence::parser::ast::Node::Comment(format!("word: \"{}\"", name)), ]); x.extend( word.instructions .iter() .map(|ins| ins.compile(self)) .collect::>>() .unwrap() .into_iter() .flatten(), ); x.push(hence::parser::ast::Node::MacroCall { name: "return_call_stack_jump".to_string(), args: vec![], }); } x.extend([ hence::parser::ast::Node::Label("main".to_string()), hence::parser::ast::Node::MacroCall { name: "main".to_string(), args: vec![hence::arg::Arg::Variable("main".to_string())], }, ]); x.extend(body); x.push(hence::parser::ast::Node::MacroCall { name: "std_stop".to_string(), args: vec![], }); Ok(x) } } pub fn compile(ast: parser::ast::AST, optimize: bool) -> Result { let mut data = Data::default(); let instructions = data.generate_instructions(ast.body, optimize)?; Ok(hence::parser::ast::AST { body: data.embed( instructions .iter() .map(|ins| ins.compile(&data)) .collect::>>() .unwrap() .into_iter() .flatten() .collect(), )?, }) }