Files
vulcan/forge_core/src/compiler/state.rs
T

100 lines
3.5 KiB
Rust

use std::collections::BTreeMap;
use crate::ast::Location;
use crate::compiler::compile_error::CompileError;
use crate::compiler::compiled_fn::CompiledFn;
use crate::compiler::text::Text;
use crate::compiler::utils::{Label, Scope, Variable};
/// The compiler state:
#[derive(Clone, PartialEq, Debug, Default)]
pub(crate) struct State {
/// Whether to emit documentation comments in the asm listing
pub comments: bool,
/// Used by gensym to generate unique symbols
pub gensym_index: usize,
/// The globally-defined names
pub global_scope: Scope,
/// The functions
pub functions: BTreeMap<String, CompiledFn>,
/// The string table
pub strings: Vec<(Label, String)>,
/// The statically-allocated buffers (size in bytes)
pub buffers: Vec<(Label, usize)>,
/// The functions that have been prototyped but not yet defined
pub prototypes: Scope,
/// The initialization code for globals
pub init: Text,
}
impl State {
pub(crate) fn with_comments() -> Self {
Self {
comments: true,
..Self::default()
}
}
/// Generate a guaranteed-unique symbolic name
pub(crate) fn gensym(&mut self) -> Label {
self.gensym_index += 1;
format!("_forge_gensym_{}", self.gensym_index)
}
/// Return whether a name exists in the global scope already
fn defined(&self, name: &str) -> bool {
self.global_scope.contains_key(name)
}
/// Add a symbol to the global namespace, catching name collisions
pub(crate) fn add_global<F: Fn(&mut State) -> Variable>(
&mut self,
name: &str,
val: F,
) -> Result<(), CompileError> {
if self.defined(name) {
Err(CompileError(0, 0, format!("name {} already defined", name)))
} else {
let val = val(self);
self.global_scope.insert(name.into(), val);
Ok(())
}
}
pub(crate) fn add_string(&mut self, string: &str) -> Label {
let sym = self.gensym();
self.strings.push((sym.clone(), string.into()));
sym
}
pub(crate) fn add_buffer(&mut self, size: usize) -> Label {
let sym = self.gensym();
self.buffers.push((sym.clone(), size));
sym
}
pub(crate) fn declare_function(&mut self, name: &str, _args: Vec<String>) -> Result<(), CompileError> {
// If it's not already prototyped, gensym a label and put it in the list. If it is, just
// ignore this (we don't check arity so it's not like the arglist being different matters)
// todo: check arity, store it here
if !self.prototypes.contains_key(name) {
let label = self.gensym();
self.add_global(name, |_| Variable::DirectLabel(label.clone()))?;
self.prototypes.insert(name.into(), Variable::DirectLabel(label));
}
Ok(())
}
pub(crate) fn find_or_declare_function(&mut self, name: &str, _loc: Location) -> Result<String, CompileError> {
// If it's in here, remove it and return the label:
// (this will only ever match this way; only thing that puts stuff in prototypes adds directlabels)
if let Some(Variable::DirectLabel(label)) = self.prototypes.remove(name) {
Ok(label)
} else {
// Otherwise, make a new label, add it to globals, and return it
let label = self.gensym();
self.add_global(name, |_| Variable::DirectLabel(label.clone()))?;
Ok(label)
}
}
}