Move utility visitors out of compiler.rs
We are converting a 1200 line file into an 800 and 400 line files. It's actually a lot easier to read now, those visitors rarely ever change and they get in the way of me reading the file (with my eyes, not with a program). Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
408
src/compiler.rs
408
src/compiler.rs
@@ -1,3 +1,5 @@
|
||||
mod visitors;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{self, Display};
|
||||
use std::fs::File;
|
||||
@@ -11,417 +13,15 @@ use common_macros::hash_map;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::compiler::visitors::*;
|
||||
use crate::obj::prelude::*;
|
||||
use crate::obj::Ptr;
|
||||
use crate::obj::BUILTINS;
|
||||
use crate::obj::{Ptr, BUILTINS};
|
||||
use crate::parser::Parser;
|
||||
use crate::token::TokenKind;
|
||||
use crate::vm::*;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// LineNumber visitor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Default)]
|
||||
struct LineNumber {
|
||||
lock_start: bool,
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl LineNumber {
|
||||
fn update_start(&mut self, start: usize) {
|
||||
if !self.lock_start {
|
||||
self.start = start;
|
||||
self.lock_start = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_end(&mut self, end: usize) {
|
||||
self.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
impl StmtVisitor for LineNumber {
|
||||
fn visit_import_stmt(&mut self, stmt: &ImportStmt) -> Result<()> {
|
||||
self.update_start(stmt.import_kw.line);
|
||||
self.update_end(stmt.module.line);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> Result<()> {
|
||||
stmt.expr.accept(self).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_assign_stmt(&mut self, stmt: &AssignStmt) -> Result<()> {
|
||||
self.update_start(stmt.lhs.line);
|
||||
stmt.rhs.accept(self).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> {
|
||||
stmt.expr.accept(self).unwrap();
|
||||
stmt.rhs.accept(self).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_block_stmt(&mut self, stmt: &BlockStmt) -> Result<()> {
|
||||
self.update_start(stmt.lbrace.line);
|
||||
self.update_end(stmt.rbrace.line);
|
||||
Ok(())
|
||||
}
|
||||
fn visit_return_stmt(&mut self, stmt: &ReturnStmt) -> Result<()> {
|
||||
self.update_start(stmt.return_kw.line);
|
||||
self.update_end(stmt.return_kw.line);
|
||||
if let Some(expr) = stmt.expr.as_ref() {
|
||||
expr.accept(self).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn visit_if_stmt(&mut self, stmt: &IfStmt) -> Result<()> {
|
||||
self.update_start(stmt.if_kw.line);
|
||||
stmt.condition.accept(self).unwrap();
|
||||
stmt.then_branch.accept(self).unwrap();
|
||||
for stmt in &stmt.else_branch {
|
||||
stmt.accept(self).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ExprVisitor for LineNumber {
|
||||
fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> {
|
||||
expr.lhs.accept(self).unwrap();
|
||||
expr.rhs.accept(self).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_unary_expr(&mut self, expr: &UnaryExpr) -> Result<()> {
|
||||
self.update_start(expr.op.line);
|
||||
expr.expr.accept(self).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_call_expr(&mut self, expr: &CallExpr) -> Result<()> {
|
||||
expr.expr.accept(self).unwrap();
|
||||
self.update_end(expr.rparen.line);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_get_expr(&mut self, expr: &GetExpr) -> Result<()> {
|
||||
expr.expr.accept(self).unwrap();
|
||||
self.update_end(expr.name.line);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Result<()> {
|
||||
expr.expr.accept(self).unwrap();
|
||||
self.update_end(expr.rbracket.line);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_primary_expr(&mut self, expr: &PrimaryExpr) -> Result<()> {
|
||||
self.update_start(expr.token.line);
|
||||
self.update_end(expr.token.line);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_function_expr(&mut self, expr: &FunctionExpr) -> Result<()> {
|
||||
self.update_start(expr.lparen.line);
|
||||
self.update_end(expr.rbrace.line);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_list_expr(&mut self, expr: &ListExpr) -> Result<()> {
|
||||
self.update_start(expr.lbracket.line);
|
||||
self.update_end(expr.rbracket.line);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_line_number(expr: &dyn Expr) -> LineRange {
|
||||
let mut line_number = LineNumber::default();
|
||||
expr.accept(&mut line_number).unwrap();
|
||||
(line_number.start, line_number.end)
|
||||
}
|
||||
|
||||
fn stmt_line_number(stmt: &dyn Stmt) -> LineRange {
|
||||
let mut line_number = LineNumber::default();
|
||||
stmt.accept(&mut line_number).unwrap();
|
||||
(line_number.start, line_number.end)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// LocalAssignCollector and LocalNameCollector
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO - reduce copy/paste stuff here?
|
||||
|
||||
#[derive(Default)]
|
||||
struct LocalAssignCollector {
|
||||
names: HashSet<String>,
|
||||
}
|
||||
|
||||
impl LocalAssignCollector {
|
||||
fn collect(body: &Vec<StmtP>) -> HashSet<String> {
|
||||
let mut collector = Self::default();
|
||||
for stmt in body {
|
||||
stmt.accept(&mut collector).unwrap();
|
||||
}
|
||||
collector.names
|
||||
}
|
||||
}
|
||||
|
||||
impl StmtVisitor for LocalAssignCollector {
|
||||
fn visit_import_stmt(&mut self, stmt: &ImportStmt) -> Result<()> {
|
||||
if stmt.what.is_empty() {
|
||||
// `import foo`
|
||||
if stmt.module.kind == TokenKind::Name {
|
||||
// do not add `import "my_file.ext"`
|
||||
self.names.insert(stmt.module.text.to_string());
|
||||
}
|
||||
} else {
|
||||
// `import foo, bar from baz`
|
||||
for what in &stmt.what {
|
||||
if what.kind == TokenKind::Name {
|
||||
// do not add `import * from foo`
|
||||
self.names.insert(what.text.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> Result<()> {
|
||||
stmt.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_assign_stmt(&mut self, stmt: &AssignStmt) -> Result<()> {
|
||||
self.names.insert(stmt.lhs.text.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> {
|
||||
stmt.expr.accept(self)?;
|
||||
stmt.rhs.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_block_stmt(&mut self, stmt: &BlockStmt) -> Result<()> {
|
||||
// we visit the block statement because even though it goes below the current "local"
|
||||
// scope, we're ultimately trying to get a list of ALL local names that are assigned to in
|
||||
// this scope.
|
||||
// TODO FIXME BUG this does create some weirdness, for example take this:
|
||||
// outer_function = () {
|
||||
// some_value = 1234
|
||||
// inner_function = () {
|
||||
// {
|
||||
// # this is a local value because we're assigning to it
|
||||
// some_value = 5678
|
||||
// }
|
||||
// # our local named "some_value" has gone out of scope, so hypothetically we
|
||||
// # should be using the "some_value" that was defined in the scope above us.
|
||||
// # however, since we're collecting local assignments in all blocks, this should
|
||||
// # error out as "unknown local 'some_value'"
|
||||
// println(some_value)
|
||||
// }
|
||||
// return inner_function
|
||||
// }
|
||||
//
|
||||
// Ideally, we would be checking nonlocals with every new scope layer, and every new block.
|
||||
// This is a pretty tough bug to solve with how things are set up right now. not sure how
|
||||
// we'll go about solving this one.
|
||||
for stmt in &stmt.stmts {
|
||||
stmt.accept(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_return_stmt(&mut self, stmt: &ReturnStmt) -> Result<()> {
|
||||
if let Some(expr) = stmt.expr.as_ref() {
|
||||
expr.accept(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_if_stmt(&mut self, stmt: &IfStmt) -> Result<()> {
|
||||
stmt.condition.accept(self)?;
|
||||
stmt.then_branch.accept(self)?;
|
||||
for stmt in &stmt.else_branch {
|
||||
stmt.accept(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ExprVisitor for LocalAssignCollector {
|
||||
fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> {
|
||||
expr.lhs.accept(self)?;
|
||||
expr.rhs.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_unary_expr(&mut self, expr: &UnaryExpr) -> Result<()> {
|
||||
expr.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_call_expr(&mut self, expr: &CallExpr) -> Result<()> {
|
||||
expr.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_get_expr(&mut self, expr: &GetExpr) -> Result<()> {
|
||||
expr.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_index_expr(&mut self, _expr: &IndexExpr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_primary_expr(&mut self, _expr: &PrimaryExpr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_function_expr(&mut self, _expr: &FunctionExpr) -> Result<()> {
|
||||
// don't visit function expr, we're only collecting local assigns
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_list_expr(&mut self, _expr: &ListExpr) -> Result<()> {
|
||||
// there shouldn't be any local assignments inside of a list expression
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LocalNameCollector {
|
||||
names: HashSet<String>,
|
||||
}
|
||||
|
||||
impl LocalNameCollector {
|
||||
fn collect(body: &Vec<StmtP>) -> HashSet<String> {
|
||||
let mut collector = Self::default();
|
||||
for stmt in body {
|
||||
stmt.accept(&mut collector).unwrap();
|
||||
}
|
||||
collector.names
|
||||
}
|
||||
}
|
||||
|
||||
impl StmtVisitor for LocalNameCollector {
|
||||
fn visit_import_stmt(&mut self, stmt: &ImportStmt) -> Result<()> {
|
||||
if stmt.what.is_empty() {
|
||||
// `import foo`
|
||||
if stmt.module.kind == TokenKind::Name {
|
||||
// do not add `import "my_file.ext"`
|
||||
self.names.insert(stmt.module.text.to_string());
|
||||
}
|
||||
} else {
|
||||
// `import foo, bar from baz`
|
||||
for what in &stmt.what {
|
||||
if what.kind == TokenKind::Name {
|
||||
// do not add `import * from foo`
|
||||
self.names.insert(what.text.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> Result<()> {
|
||||
stmt.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_assign_stmt(&mut self, stmt: &AssignStmt) -> Result<()> {
|
||||
stmt.rhs.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> {
|
||||
stmt.expr.accept(self)?;
|
||||
stmt.rhs.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_block_stmt(&mut self, stmt: &BlockStmt) -> Result<()> {
|
||||
for stmt in &stmt.stmts {
|
||||
stmt.accept(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_return_stmt(&mut self, stmt: &ReturnStmt) -> Result<()> {
|
||||
if let Some(expr) = stmt.expr.as_ref() {
|
||||
expr.accept(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_if_stmt(&mut self, stmt: &IfStmt) -> Result<()> {
|
||||
stmt.condition.accept(self)?;
|
||||
stmt.then_branch.accept(self)?;
|
||||
for stmt in &stmt.else_branch {
|
||||
stmt.accept(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ExprVisitor for LocalNameCollector {
|
||||
fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> {
|
||||
expr.lhs.accept(self)?;
|
||||
expr.rhs.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_unary_expr(&mut self, expr: &UnaryExpr) -> Result<()> {
|
||||
expr.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_call_expr(&mut self, expr: &CallExpr) -> Result<()> {
|
||||
expr.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_get_expr(&mut self, expr: &GetExpr) -> Result<()> {
|
||||
expr.expr.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Result<()> {
|
||||
expr.expr.accept(self)?;
|
||||
expr.index.accept(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_primary_expr(&mut self, expr: &PrimaryExpr) -> Result<()> {
|
||||
if expr.token.kind == TokenKind::Name {
|
||||
self.names.insert(expr.token.text.to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_function_expr(&mut self, _expr: &FunctionExpr) -> Result<()> {
|
||||
// don't visit function expr, we're only collecting local assigns
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_list_expr(&mut self, expr: &ListExpr) -> Result<()> {
|
||||
for expr in &expr.exprs {
|
||||
expr.accept(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Misc
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user