use std::{cell::RefCell, collections::HashMap}; use crate::ast; #[derive(Debug, Clone)] pub struct Function { name: Option, args: Vec, locals: Vec, upvalues: Vec<(ast::Ident, Ident)>, body: Vec, ret: Box, } #[derive(Debug, Clone, Copy)] enum Ident { Upvalue(usize), Local(usize), Argument(usize), } #[derive(Debug, Clone)] enum Statement { Assign { target: Location, source: Box, }, Call(Call), } #[derive(Debug, Clone)] enum Location { Variable { name: Ident, }, Field { table: Box, index: Box, }, } #[derive(Debug, Clone)] enum Expression { Constant(ast::Constant), Variable(Location), Call(Call), Function(Function), } #[derive(Debug, Clone)] struct Call { callee: Location, args: Vec, } #[derive(Debug, Clone, Default)] struct Scope { upvalues: Vec<(ast::Ident, Ident)>, scope: HashMap, } impl Scope { fn new_toplevel() -> Self { Self { upvalues: vec![("_ENV".to_string(), Ident::Local(0))], scope: [("_ENV".to_string(), Ident::Upvalue(0))] .into_iter() .collect(), } } fn lookup(&self, name: &ast::Ident) -> Option { self.scope.get(name).copied() } fn add_upvalue(&mut self, name: &ast::Ident, up_ident: Ident) -> Ident { let index = self.upvalues.len(); self.upvalues.push((name.clone(), up_ident)); let ident = Ident::Upvalue(index); self.scope.insert(name.clone(), ident); ident } } #[derive(Debug)] struct BuildContext<'a> { parent: Option<&'a BuildContext<'a>>, // mustn't be &mut because of variance scope: RefCell, } impl BuildContext<'_> { fn request(&self, name: &ast::Ident) -> Option { if let Some(ident) = self.scope.borrow().lookup(name) { return Some(ident); } if let Some(ident) = self.parent.as_ref()?.request(name) { return Some(self.scope.borrow_mut().add_upvalue(name, ident)); } None } } fn build_in_scope(parent: &BuildContext<'_>, code: ast::Function) -> Function { let scope = BuildContext { parent: Some(parent), scope: Default::default(), }; if false { build_in_scope(&scope, code.clone()); build_in_scope(&scope, code.clone()); } todo!() } pub fn build(code: ast::Function) -> Function { let mut root = BuildContext { parent: None, scope: RefCell::new(Scope::new_toplevel()), }; build_in_scope(&mut root, code) }