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::collections::{HashMap, HashSet};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -11,417 +13,15 @@ use common_macros::hash_map;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
use crate::compiler::visitors::*;
|
||||||
use crate::obj::prelude::*;
|
use crate::obj::prelude::*;
|
||||||
use crate::obj::Ptr;
|
use crate::obj::{Ptr, BUILTINS};
|
||||||
use crate::obj::BUILTINS;
|
|
||||||
use crate::parser::Parser;
|
use crate::parser::Parser;
|
||||||
use crate::token::TokenKind;
|
use crate::token::TokenKind;
|
||||||
use crate::vm::*;
|
use crate::vm::*;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
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
|
// Misc
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
408
src/compiler/visitors.rs
Normal file
408
src/compiler/visitors.rs
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::ast::*;
|
||||||
|
use crate::compiler::Result;
|
||||||
|
use crate::token::TokenKind;
|
||||||
|
use crate::vm::*;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// LineNumber visitor
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) 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)]
|
||||||
|
pub(super) struct LocalAssignCollector {
|
||||||
|
names: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalAssignCollector {
|
||||||
|
pub 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)]
|
||||||
|
pub(super) struct LocalNameCollector {
|
||||||
|
names: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalNameCollector {
|
||||||
|
pub 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user