114 lines
3.0 KiB
Rust
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()?)
|
|
}
|