minilua/src/twopass.rs
2025-04-19 12:05:17 +03:00

122 lines
2.4 KiB
Rust

use std::{cell::RefCell, collections::HashMap};
use crate::ast;
#[derive(Debug, Clone)]
pub struct Function {
name: Option<ast::Ident>,
args: Vec<ast::Ident>,
locals: Vec<ast::Ident>,
upvalues: Vec<(ast::Ident, Ident)>,
body: Vec<Statement>,
ret: Box<Expression>,
}
#[derive(Debug, Clone, Copy)]
enum Ident {
Upvalue(usize),
Local(usize),
Argument(usize),
}
#[derive(Debug, Clone)]
enum Statement {
Assign {
target: Location,
source: Box<Expression>,
},
Call(Call),
}
#[derive(Debug, Clone)]
enum Location {
Variable {
name: Ident,
},
Field {
table: Box<Location>,
index: Box<Expression>,
},
}
#[derive(Debug, Clone)]
enum Expression {
Constant(ast::Constant),
Variable(Location),
Call(Call),
Function(Function),
}
#[derive(Debug, Clone)]
struct Call {
callee: Location,
args: Vec<Expression>,
}
#[derive(Debug, Clone, Default)]
struct Scope {
upvalues: Vec<(ast::Ident, Ident)>,
scope: HashMap<ast::Ident, Ident>,
}
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<Ident> {
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<Scope>,
}
impl BuildContext<'_> {
fn request(&self, name: &ast::Ident) -> Option<Ident> {
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)
}