use crate::{diag::*, eval::*, objecttree::*, parse::*, source_file_cache::*};
use microcad_core::Id;
#[derive(Debug, Clone)]
pub enum StackFrame {
SourceFile(std::rc::Rc<SourceFile>, SymbolTable),
Namespace(SymbolTable),
ModuleCall(SymbolTable, Option<ObjectNode>),
FunctionCall(SymbolTable),
}
impl StackFrame {
pub fn symbol_table(&self) -> &SymbolTable {
match self {
Self::SourceFile(_, table) => table,
Self::Namespace(table) => table,
Self::ModuleCall(table, _) => table,
Self::FunctionCall(table) => table,
}
}
pub fn symbol_table_mut(&mut self) -> &mut SymbolTable {
match self {
Self::SourceFile(_, table) => table,
Self::Namespace(table) => table,
Self::ModuleCall(table, _) => table,
Self::FunctionCall(table) => table,
}
}
}
impl Default for StackFrame {
fn default() -> Self {
Self::Namespace(SymbolTable::default())
}
}
impl Symbols for StackFrame {
fn fetch(&self, id: &Id) -> Option<std::rc::Rc<Symbol>> {
self.symbol_table().fetch(id)
}
fn add(&mut self, symbol: Symbol) -> &mut Self {
self.symbol_table_mut().add(symbol);
self
}
fn add_alias(&mut self, symbol: Symbol, alias: Id) -> &mut Self {
self.symbol_table_mut().add_alias(symbol, alias);
self
}
fn copy<T: Symbols>(&self, into: &mut T) {
self.symbol_table().copy(into);
}
}
pub struct Context {
stack: Vec<StackFrame>,
current_source_file: Option<std::rc::Rc<SourceFile>>,
source_files: SourceFileCache,
diag_handler: DiagHandler,
}
impl Context {
pub fn from_source_file(source_file: SourceFile) -> Self {
let rc_source_file = std::rc::Rc::new(source_file);
let mut ctx = Self {
stack: vec![StackFrame::default()],
current_source_file: Some(rc_source_file.clone()),
..Default::default()
};
ctx.source_files.add(rc_source_file);
ctx
}
pub fn eval(&mut self) -> super::EvalResult<ObjectNode> {
let node = self
.current_source_file()
.expect("No current source file")
.eval(self)?;
Ok(node)
}
pub fn current_source_file(&self) -> Option<std::rc::Rc<SourceFile>> {
self.current_source_file.clone()
}
fn push(&mut self, stack_frame: StackFrame) {
self.stack.push(stack_frame);
}
fn pop(&mut self) {
self.stack.pop();
}
pub fn top(&self) -> &StackFrame {
self.stack.last().expect("Empty stack")
}
pub fn top_mut(&mut self) -> &mut StackFrame {
self.stack.last_mut().expect("Empty stack")
}
pub fn scope(
&mut self,
stack_frame: StackFrame,
f: impl FnOnce(&mut Self) -> crate::eval::EvalResult<()>,
) -> crate::eval::EvalResult<()> {
self.push(stack_frame);
f(self)?;
self.pop();
Ok(())
}
pub fn diag(&self) -> &DiagHandler {
&self.diag_handler
}
pub fn fetch_symbols_by_qualified_name(
&mut self,
name: &QualifiedName,
) -> EvalResult<Vec<Symbol>> {
name.fetch_symbols(self)
}
pub fn add_source_file(&mut self, source_file: SourceFile) {
self.source_files.add(std::rc::Rc::new(source_file))
}
}
impl PushDiag for Context {
fn push_diag(&mut self, diag: Diag) -> crate::eval::EvalResult<()> {
self.diag_handler.push_diag(diag)
}
}
impl Symbols for Context {
fn fetch(&self, id: &Id) -> Option<std::rc::Rc<Symbol>> {
self.stack
.iter()
.rev()
.flat_map(|stack_frame| stack_frame.fetch(id))
.next()
}
fn add(&mut self, symbol: Symbol) -> &mut Self {
self.top_mut().add(symbol);
self
}
fn add_alias(&mut self, symbol: Symbol, alias: Id) -> &mut Self {
self.top_mut().add_alias(symbol, alias);
self
}
fn copy<T: Symbols>(&self, into: &mut T) {
self.top().symbol_table().iter().for_each(|(_, symbol)| {
into.add(symbol.as_ref().clone());
});
}
}
impl GetSourceFileByHash for Context {
fn get_source_file_by_hash(&self, hash: u64) -> Option<&SourceFile> {
self.source_files.get_source_file_by_hash(hash)
}
}
impl Default for Context {
fn default() -> Self {
Self {
stack: vec![StackFrame::default()],
current_source_file: None,
source_files: SourceFileCache::default(),
diag_handler: DiagHandler::default(),
}
}
}
#[test]
fn context_basic() {
use crate::{eval::*, parse::*, parser::*, src_ref::*};
let mut context = Context::default();
context.add_value("a".into(), Value::Integer(Refer::none(1)));
context.add_value("b".into(), Value::Integer(Refer::none(2)));
assert_eq!(
context
.fetch(&"a".into())
.expect("test error")
.id()
.expect("test error"),
"a"
);
assert_eq!(
context
.fetch(&"b".into())
.expect("test error")
.id()
.expect("test error"),
"b"
);
let c = Parser::parse_rule::<Assignment>(Rule::assignment, "c = a + b", 0).expect("test error");
c.eval(&mut context).expect("test error");
}