use std::collections::HashMap;
use crate::{
ast::{ASTType, LhsAssign, LhsAssignType, AST},
ast_error, ast_from_ast,
error::{ErrorKind, KabelError},
extension::Extension,
out_of_scope_var,
};
pub struct Resolver {
text: Vec<String>,
symbol_table: Vec<HashMap<String, (Symbol, usize)>>, // (Symbol, reference to locals)
pub locals: Vec<Vec<usize>>, // scope
pub scope: usize,
pub errors: Vec<KabelError>,
}
impl Resolver {
pub fn new(text: String) -> Self {
Self {
text: text
.lines()
.collect::<Vec<&str>>()
.iter()
.map(|s| s.to_string())
.collect(),
symbol_table: vec![HashMap::new()],
locals: vec![Vec::new()],
scope: 0,
errors: Vec::new(),
}
}
pub fn visit(&mut self, ast: AST) -> AST {
use ASTType::*;
match ast.kind {
Program(asts) => {
let mut program = Vec::new();
for ast in asts {
let ast = self.visit(ast.clone());
program.push(ast)
}
AST {
kind: ASTType::Program(program),
extensions: Vec::new(),
start_line: 0,
end_line: 0,
start_column: 0,
end_column: 0,
}
}
Function(name, args, block) => {
if self.resolve_var(&name.name).0 {
self.errors.push(out_of_scope_var!(self,
ErrorKind::FunctionAlreadyDeclaredVariable, ast,
"Function \"{}\" already declared", name ;
"hint: has variable \"{}\" already been declared?", name.name));
} else {
let resolution = self.resolve_function(&name.name, 0);
if resolution.is_ok() {
self.errors.push(out_of_scope_var!(self,
ErrorKind::FunctionAlreadyDeclaredFunction, ast,
"Function \"{}\" already declared", name ;
"hint: has function \"{}\" already been declared?", name.name));
}
if let Err((kind, _, _)) = resolution {
if kind == ErrorKind::IncorrectArity {
self.errors.push(out_of_scope_var!(self,
ErrorKind::FunctionAlreadyDeclaredFunction, ast,
"Function \"{}\" already declared", name ;
"hint: has function \"{}\" already been declared?", name.name));
}
}
}
self.locals
.last_mut()
.expect("locals last in function push")
.push(self.scope);
self.symbol_table.last_mut().unwrap().insert(
name.name.clone(),
(
Symbol::Function(args.len()),
self.locals
.last()
.expect("locals last in function symbol len")
.len()
- 1,
),
);
self.locals.push(Vec::new());
self.symbol_table.push(HashMap::new());
self.locals
.last_mut()
.expect("locals last in function self-reference push")
.push(self.scope + 1);
self.symbol_table.last_mut().unwrap().insert(
name.name.clone(),
(
Symbol::Var,
self.locals
.last()
.expect("locals last in function self-reference len")
.len()
- 1,
),
);
for arg in args.clone() {
self.locals
.last_mut()
.expect("locals last in function arg push")
.push(self.scope + 1);
self.symbol_table.last_mut().unwrap().insert(
arg.name,
(
Symbol::Var,
self.locals
.last()
.expect("locals last in function arg len")
.len()
- 1,
),
);
}
let block = self.visit(*block);
self.symbol_table.pop();
self.locals.pop();
AST {
kind: Function(name, args, Box::new(block)),
extensions: vec![Extension::Resolution(
self.scope,
self.locals
.last()
.expect("locals last in function ast len")
.len()
- 1,
)],
start_line: ast.start_line,
end_line: ast.end_line,
start_column: ast.start_column,
end_column: ast.end_column,
}
}
Return(expr) => {
if let Some(expr) = *expr {
let expr = self.visit(expr);
return ast_from_ast!(AST, Return(Box::new(Some(expr))), ast, ast);
}
ast_from_ast!(AST, Return(Box::new(None)), ast, ast)
}
Loop(block) => {
let block = self.visit(*block);
ast_from_ast!(AST, Loop(Box::new(block)), ast, ast)
}
While(condition, block) => {
let condition = self.visit(*condition);
let block = self.visit(*block);
ast_from_ast!(AST, While(Box::new(condition), Box::new(block)), ast, ast)
}
Break => {
ast_from_ast!(AST, Break, ast, ast)
}
Continue => {
ast_from_ast!(AST, Continue, ast, ast)
}
For(expr1, expr2, expr3, block) => {
self.symbol_table.push(HashMap::new());
self.scope += 1;
let mut n_expr1 = None;
let mut n_expr2 = None;
let mut n_expr3 = None;
if let Some(expr) = *expr1 {
n_expr1 = Some(self.visit(expr));
}
if let Some(expr) = *expr2 {
n_expr2 = Some(self.visit(expr));
}
if let Some(expr) = *expr3 {
n_expr3 = Some(self.visit(expr));
}
let block = self.visit(*block);
while let Some(scope) = self.locals.last().expect("locals failed in For").last() {
if self.scope == *scope {
self.locals
.last_mut()
.expect("locals failed in For pop")
.pop();
} else {
break;
}
}
self.scope -= 1;
self.symbol_table.pop();
ast_from_ast!(
AST,
For(
Box::new(n_expr1),
Box::new(n_expr2),
Box::new(n_expr3),
Box::new(block)
),
ast,
ast
)
}
If(condition, block, else_expr) => {
let condition = self.visit(*condition);
let block = self.visit(*block);
let mut n_else_expr = None;
if let Some(else_expr) = *else_expr {
n_else_expr = Some(self.visit(else_expr));
}
ast_from_ast!(
AST,
If(Box::new(condition), Box::new(block), Box::new(n_else_expr)),
ast,
ast
)
}
Block(stmts) => {
self.symbol_table.push(HashMap::new());
self.scope += 1;
let mut n_stmts = Vec::new();
for stmt in stmts {
n_stmts.push(self.visit(stmt));
}
/*for (index, scope) in self.locals.clone().iter().enumerate() {
if self.scope == *scope {
self.locals.remove(index);
}
}*/
while let Some(scope) = self.locals.last().expect("locals last in block").last() {
if self.scope == *scope {
self.locals
.last_mut()
.expect("locals last in block pop")
.pop();
} else {
break;
}
}
self.scope -= 1;
self.symbol_table.pop();
ast_from_ast!(AST, Block(n_stmts), ast, ast)
}
Decl(name, expr) => {
let expr = self.visit(*expr);
self.locals
.last_mut()
.expect("locals last in decl push")
.push(self.scope);
let resolution = self.resolve_var(&name.name);
if resolution.0 && resolution.2 == self.scope {
self.errors.push(out_of_scope_var!(self,
ErrorKind::VariableAlreadyDeclaredVariable, ast,
"Variable \"{}\" already declared", name ;
"hint: has variable \"{}\" already been declared?", name.name));
} else {
let resolution = self.resolve_function(&name.name, 0);
if resolution.is_ok() {
self.errors.push(out_of_scope_var!(self,
ErrorKind::VariableAlreadyDeclaredFunction, ast,
"Variable \"{}\" already declared", name ;
"hint: has function \"{}\" already been declared?", name.name));
}
if let Err((kind, _, _)) = resolution {
if kind == ErrorKind::IncorrectArity {
self.errors.push(out_of_scope_var!(self,
ErrorKind::VariableAlreadyDeclaredFunction, ast,
"Variable \"{}\" already declared", name ;
"hint: has function \"{}\" already been declared?", name.name));
}
}
}
self.symbol_table.last_mut().unwrap().insert(
name.name.clone(),
(
Symbol::Var,
self.locals
.last()
.expect("locals last in decl symbol")
.len()
- 1,
),
);
AST {
kind: Decl(name, Box::new(expr)),
extensions: vec![Extension::Resolution(
self.scope,
self.locals.last().expect("locals last in decl ast").len() - 1,
)],
start_line: ast.start_line,
end_line: ast.end_line,
start_column: ast.start_column,
end_column: ast.end_column,
}
}
Expr(expr) => {
let expr = self.visit(*expr);
ast_from_ast!(AST, Expr(Box::new(expr)), ast, ast)
}
// REMOVE LATER
Print(expr) => {
let expr = self.visit(*expr);
ast_from_ast!(AST, Print(Box::new(expr)), ast, ast)
}
Assign(lhs, expr) => {
let expr = self.visit(*expr);
match lhs.kind.clone() {
LhsAssignType::Ident(name) => {
let resolution = self.resolve_var(&name);
if !resolution.0 {
self.errors.push(out_of_scope_var!(
self,
ErrorKind::OutOfScope,
lhs,
"Variable \"{}\" not in scope",
name
));
}
AST {
kind: Assign(lhs, Box::new(expr)),
extensions: vec![Extension::Resolution(self.scope, resolution.1)],
start_line: ast.start_line,
end_line: ast.end_line,
start_column: ast.start_column,
end_column: ast.end_column,
}
}
LhsAssignType::Subscript(name, index) => {
let name = self.visit(*name);
let index = self.visit(*index);
AST {
kind: Assign(
ast_from_ast!(
LhsAssign,
LhsAssignType::Subscript(Box::new(name), Box::new(index)),
lhs,
lhs
),
Box::new(expr),
),
extensions: Vec::new(),
start_line: ast.start_line,
end_line: ast.end_line,
start_column: ast.start_column,
end_column: ast.end_column,
}
}
}
}
Anonymous(params, block) => {
self.locals.push(Vec::new());
self.symbol_table.push(HashMap::new());
self.locals
.last_mut()
.expect("locals last in anonymous self-reference push")
.push(self.scope);
for param in params.clone() {
self.locals
.last_mut()
.expect("locals last in anonymous param push")
.push(self.scope + 1);
self.symbol_table.last_mut().unwrap().insert(
param.name,
(
Symbol::Var,
self.locals
.last()
.expect("locals last in anonymous param len")
.len()
- 1,
),
);
}
let block = self.visit(*block);
self.symbol_table.pop();
self.locals.pop();
ast_from_ast!(AST, Anonymous(params, Box::new(block)), ast, ast)
}
Ternary(condition, true_expr, false_expr) => {
let condition = self.visit(*condition);
let true_expr = self.visit(*true_expr);
let false_expr = self.visit(*false_expr);
ast_from_ast!(
AST,
Ternary(
Box::new(condition),
Box::new(true_expr),
Box::new(false_expr)
),
ast,
ast
)
}
Subscript(array, index) => {
let array = self.visit(*array);
let index = self.visit(*index);
ast_from_ast!(AST, Subscript(Box::new(array), Box::new(index)), ast, ast)
}
Binary(left, oper, right) => {
let left = self.visit(*left);
let right = self.visit(*right);
ast_from_ast!(AST, Binary(Box::new(left), oper, Box::new(right)), ast, ast)
}
Unary(oper, right) => {
let right = self.visit(*right);
ast_from_ast!(AST, Unary(oper, Box::new(right)), ast, ast)
}
Lit(ref lit) => {
let lit = lit.clone();
if let crate::ast::Lit::Ident(ref name) = lit {
let resolution = self.resolve_var(name);
if !resolution.0 {
self.errors.push(ast_error!(
self,
ErrorKind::OutOfScope,
ast,
"Variable \"{}\" not in scope",
name
))
} else {
return AST {
kind: Lit(lit),
extensions: vec![Extension::Resolution(self.scope, resolution.1)],
start_line: ast.start_line,
end_line: ast.end_line,
start_column: ast.start_column,
end_column: ast.end_column,
};
}
}
ast_from_ast!(AST, Lit(lit), ast, ast)
}
Call(ident, args) => {
let ident = self.visit(*ident.clone());
let mut n_args = Vec::new();
for arg in args {
n_args.push(self.visit(arg));
}
AST {
kind: Call(Box::new(ident), n_args),
extensions: Vec::new(),
start_line: ast.start_line,
end_line: ast.end_line,
start_column: ast.start_column,
end_column: ast.end_column,
}
}
/*Member(left, right) => {
self.visit_member(*left, *right);
}*/
_ => {
panic!("not implemented")
} // not implemented
}
}
// TODO: make visit_member not throw out of scope errors
/*pub fn visit_member(&mut self, left: AST, right: AST) {
self.visit(left);
self.visit(right);
}*/
fn resolve_var(&self, name: &String) -> (bool, usize, usize) {
for (scope_num, scope) in self.symbol_table.iter().enumerate().rev() {
if let Some((Symbol::Var, place)) | Some((Symbol::Function(_), place)) = scope.get(name)
{
return (true, *place, scope_num);
}
}
(false, 0, 0)
}
fn resolve_function(
&mut self,
name: &String,
arity: usize,
) -> Result<usize, (ErrorKind, Option<usize>, Option<usize>)> {
for scope in self.symbol_table.iter().rev() {
if let Some((Symbol::Function(f_arity), place)) = scope.get(name) {
if *f_arity == arity {
return Ok(*place);
} else {
return Err((ErrorKind::IncorrectArity, Some(*f_arity), Some(arity)));
}
}
}
Err((ErrorKind::OutOfScope, None, None))
}
}
#[derive(Debug, Clone, Copy)]
pub enum Symbol {
Var,
Function(usize),
Anonymous(usize),
}