hence/henceforth/src/lib/compiler.rs

114 lines
3.0 KiB
Rust

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<Self> {
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<Instruction>,
}
#[derive(Debug)]
pub struct Data {
instructions: Vec<Instruction>,
words: HashMap<String, Word>,
}
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<String> {
let mut data = Data::new();
data.generate_instructions(ast.body)?;
dbg!(&data);
Ok(Template { data }.render_once()?)
}