use std::collections::HashMap; use anyhow::{bail, Result}; use sailfish::TemplateOnce; use crate::parser; #[derive(Debug, Clone, PartialEq)] pub enum Instruction { Push(u16), Drop, Add, Sub, Dot, Call(String), } impl Instruction { pub fn from_word(word: &str) -> Option { match word { "drop" => Some(Instruction::Drop), "+" => Some(Instruction::Add), "-" => Some(Instruction::Sub), "." => Some(Instruction::Dot), _ => None, } } } #[derive(Debug)] pub struct Word { id: u16, instructions: Vec, } #[derive(Debug)] pub struct Data { instructions: Vec, words: HashMap, } impl Data { fn new() -> Self { Self { instructions: vec![], words: HashMap::new(), } } pub fn generate_instructions(&mut self, body: parser::ast::Body) -> Result<()> { for node in body { match node { parser::ast::Node::Comment(_) => {} parser::ast::Node::String { mode, string } => {} parser::ast::Node::Number(x) => { self.instructions.push(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 pre_instructions = self.instructions.clone(); self.instructions.clear(); self.generate_instructions(body)?; let instructions = self.instructions.clone(); self.instructions = pre_instructions; self.words.insert( name, Word { id: self.words.len() as u16, instructions, }, ); } parser::ast::Node::Word(x) => { if let Some(ins) = Instruction::from_word(&x) { self.instructions.push(ins); } else if self.words.contains_key(&x) { self.instructions.push(Instruction::Call(x)); } else { bail!("Word does not exist: {}", x); } } } } Ok(()) } } #[derive(TemplateOnce)] #[template(path = "assembly.asm")] pub struct Template { pub data: Data, } pub fn compile(ast: parser::ast::AST) -> Result { let mut data = Data::new(); data.generate_instructions(ast.body)?; dbg!(&data); Ok(Template { data }.render_once()?) }