122 lines
2.4 KiB
Rust
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)
|
|
}
|