use crate::{asm, Error, Result};
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::fmt::Display;
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Ast {
procs: Vec<Procedure>,
}
impl Ast {
pub(crate) fn new(procs: Vec<Procedure>) -> Self {
Self { procs }
}
pub fn lower(self) -> Result<Vec<asm::Instruct>> {
let mut entry_proc = Procedure::new(Procedure::ENTRY_POINT, Vec::new());
for prc in &self.procs {
if prc.is_entry_point() {
entry_proc = prc.clone();
}
}
entry_proc.lower(&self.procs)
}
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub(crate) enum Exec {
Nop,
Call(String),
Assembly(asm::Instruct),
}
impl Exec {
pub fn call(name: impl Display) -> Self {
Self::Call(name.to_string())
}
pub fn asm(i: asm::Instruct) -> Self {
Self::Assembly(i)
}
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub(crate) struct Procedure {
name: String,
code: Vec<Exec>,
}
impl Procedure {
const ENTRY_POINT: &'static str = "start";
pub fn new(name: impl Display, code: Vec<Exec>) -> Self {
Self {
name: name.to_string(),
code,
}
}
pub fn get_name(&self) -> String {
self.name.clone()
}
pub fn is_entry_point(&self) -> bool {
self.get_name() == Self::ENTRY_POINT
}
pub fn is_proc(&self, name: impl Display) -> bool {
self.get_name() == name.to_string()
}
pub fn lower(&self, procs: &[Self]) -> Result<Vec<asm::Instruct>> {
let mut result = Vec::new();
for expr in &self.code {
match expr {
Exec::Nop => {}
Exec::Call(name) => {
let mut proc_exists = false;
for prc in procs {
if prc.is_proc(&name) {
proc_exists = true;
result.extend(prc.lower(procs)?);
break;
}
}
if !proc_exists {
return Err(Error::ProcedureNotDefined(name.clone()));
}
}
Exec::Assembly(i) => result.push(i.clone()),
}
}
Ok(result)
}
}