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:
2024-10-04 20:26:18 -07:00
parent 17408a8695
commit b21a02f12f
2 changed files with 412 additions and 404 deletions

View File

@@ -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
////////////////////////////////////////////////////////////////////////////////