Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move type_evaluator tests to it's module #166

Merged
merged 1 commit into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn symbols(path: &PathBuf) -> std::result::Result<(), anyhow::Error> {
manager.build();
let module = manager.modules.values().last().unwrap();

println!("{}", module.symbol_table);
println!("{}", module.get_symbol_table());

Ok(())
}
Expand Down
75 changes: 5 additions & 70 deletions typechecker/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl BuildManager {
let file = Box::new(Self::parse_file(&build_source.source, build_source.module));
let symbol_table = SymbolTable::new(crate::symbol_table::SymbolTableType::Module, 0);

modules.insert(mod_name, State { file, symbol_table });
modules.insert(mod_name, State::new(file));
}

BuildManager {
Expand Down Expand Up @@ -75,24 +75,21 @@ impl BuildManager {
}
}

// TODO: separate this can build to be able to test pre analysis and type checking separately
// Performs type checking passes over the code
pub fn type_check(&mut self) {
self.pre_analysis();
for state in self.modules.iter_mut() {
let mut checker = TypeChecker::new(state.1, &self.options);
checker.type_check();
for stmt in &state.1.file.body {
checker.type_check(stmt);
}
self.errors.append(&mut checker.errors);
}
}
}

#[cfg(test)]
mod tests {
use parser::ast::GetNode;

use crate::type_check::type_evaluator::TypeEvaluator;

use super::*;
fn snapshot_symbol_table(source: &str) -> String {
let mut manager = BuildManager::new(
Expand All @@ -108,7 +105,7 @@ mod tests {

let module = manager.modules.values().last().unwrap();

format!("{}", module.symbol_table)
format!("{}", module.get_symbol_table())
}

fn snapshot_type_check(source: &str) -> String {
Expand All @@ -126,50 +123,6 @@ mod tests {
manager.errors.join("\n").to_string()
}

// TODO: refactor and move the test to type check mod
fn snapshot_type_eval(source: &str) -> String {
let mut manager = BuildManager::new(
vec![BuildSource {
path: PathBuf::from("test.py"),
module: String::from("test"),
source: source.to_string(),
followed: false,
}],
Settings::test_settings(),
);

manager.build();

let module = manager.modules.values().last().unwrap();
let symbol_table = module.symbol_table.clone();
let enderpy_file = module.file.clone();

let type_eval = TypeEvaluator::new(symbol_table);

let mut result = HashMap::new();

for stmt in enderpy_file.body {
match stmt {
parser::ast::Statement::ExpressionStatement(e) => {
let t = type_eval.get_type(&e);
match e {
parser::ast::Expression::Name(n) => {
result.insert(n.id, t.to_string());
}
_ => panic!("don't use this test for other expressions"),
}
}
_ => {}
}
}

// sort result by key
let mut result_sorted = result.clone().into_iter().collect::<Vec<_>>();
result_sorted.sort_by(|a, b| a.0.cmp(&b.0));

return format!("{:#?}", result_sorted);
}

macro_rules! snap {
($name:tt, $path:tt) => {
#[test]
Expand Down Expand Up @@ -202,22 +155,6 @@ mod tests {
};
}

macro_rules! snap_type_eval {
($name:tt, $path:tt) => {
#[test]
fn $name() {
let contents = include_str!($path);
let result = snapshot_type_eval(contents);
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../testdata/output/");
settings.set_description(contents);
settings.bind(|| {
insta::assert_snapshot!(result);
});
}
};
}

snap!(
test_simple_var_assignments,
"../testdata/inputs/simple_var_assignment.py"
Expand All @@ -239,6 +176,4 @@ mod tests {
test_type_check_list,
"../testdata/inputs/type_check_list.py"
);

snap_type_eval!(test_type_eval_vars, "../testdata/inputs/type_eval_vars.py");
}
12 changes: 11 additions & 1 deletion typechecker/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ use crate::{

pub struct State {
pub file: Box<EnderpyFile>,
pub symbol_table: SymbolTable,
symbol_table: SymbolTable,
}

impl State {
pub fn new(file: Box<EnderpyFile>) -> Self {
Self {
file,
symbol_table: SymbolTable::new(crate::symbol_table::SymbolTableType::Module, 0),
}
}
/// entry point to fill up the symbol table from the global definitions
pub fn populate_symbol_table(&mut self) {
let mut sem_anal = SemanticAnalyzer::new(self.file.clone());
Expand All @@ -17,4 +23,8 @@ impl State {
}
self.symbol_table = sem_anal.globals
}

pub fn get_symbol_table(&self) -> SymbolTable {
self.symbol_table.clone()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: typechecker/src/build.rs
source: typechecker/src/type_check/type_evaluator.rs
description: "# define variables with various types for testing\na = 1\nb = 2\nc = True\nd = False\ne = \"hello\"\nf = \"world\"\n# g = [1,2,3]\n# h = (1,2,3)\n# i = {1,2,3}\n# j = {\"a\":1,\"b\":2,\"c\":3}\n# k = None\n\na\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\n\n"
expression: result
---
Expand Down
24 changes: 14 additions & 10 deletions typechecker/src/type_check/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@ use ast::{Expression, Statement};
use parser::ast::{self, *};

use crate::{
ast_visitor::TraversalVisitor, settings::Settings, state::State,
ast_visitor::TraversalVisitor, settings::Settings, state::State, symbol_table::SymbolTable,
type_check::rules::is_reassignment_valid,
};

use super::{type_evaluator::TypeEvaluator, type_inference::type_check_bin_op, types::Type};

// TODO: currently only supporting a single file
pub struct TypeChecker<'a> {
pub errors: Vec<String>,
// TODO: currently only supporting a single file
pub module: &'a State,
// The symbol table of the module being type checked
symbol_table: SymbolTable,
// statements to type check
statements: Vec<Statement>,
pub options: &'a Settings,
type_evaluator: TypeEvaluator,
}

#[allow(unused)]
impl<'a> TypeChecker<'a> {
pub fn new(module: &'a State, options: &'a Settings) -> Self {
let symbol_table = module.get_symbol_table();
let statements = module.file.body.clone();
TypeChecker {
errors: vec![],
module,
symbol_table,
statements,
options,
type_evaluator: TypeEvaluator::new(module.symbol_table.clone()),
type_evaluator: TypeEvaluator::new(module.get_symbol_table()),
}
}

pub fn type_check(&mut self) {
for stmt in &self.module.file.body {
self.visit_stmt(stmt);
}
pub fn type_check(&mut self, statement: &Statement) {
self.visit_stmt(statement);
}

fn infer_expr_type(&mut self, expr: &Expression) -> Type {
Expand Down Expand Up @@ -325,7 +329,7 @@ impl<'a> TraversalVisitor for TypeChecker<'a> {
for target in &_a.targets {
match target {
ast::Expression::Name(n) => {
let symbol = self.module.symbol_table.lookup_in_scope(&n.id);
let symbol = self.symbol_table.lookup_in_scope(&n.id);
if let Some(symbol) = symbol {
let prev_target_type = self
.type_evaluator
Expand Down
65 changes: 65 additions & 0 deletions typechecker/src/type_check/type_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,3 +421,68 @@ impl TraversalVisitorImmutGeneric<Type> for TypeEvaluator {
todo!()
}
}

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use super::*;
// TODO: refactor and move the test to type check mod
fn snapshot_type_eval(source: &str) -> String {
use crate::nodes::EnderpyFile;
use crate::state::State;
use parser::Parser;

let mut parser = Parser::new(source.to_string());
let ast_module = parser.parse();

let enderpy_file = EnderpyFile::from(ast_module, "test".to_string());

let mut module = State::new(Box::new(enderpy_file));
module.populate_symbol_table();
let symbol_table = module.get_symbol_table();

let type_eval = TypeEvaluator::new(symbol_table);

let mut result = HashMap::new();

for stmt in module.file.body {
match stmt {
parser::ast::Statement::ExpressionStatement(e) => {
let t = type_eval.get_type(&e);
match e {
parser::ast::Expression::Name(n) => {
result.insert(n.id, t.to_string());
}
_ => panic!("don't use this test for other expressions"),
}
}
_ => {}
}
}

// sort result by key
let mut result_sorted = result.clone().into_iter().collect::<Vec<_>>();
result_sorted.sort_by(|a, b| a.0.cmp(&b.0));

return format!("{:#?}", result_sorted);
}

macro_rules! snap_type_eval {
($name:tt, $path:tt) => {
#[test]
fn $name() {
let contents = include_str!($path);
let result = snapshot_type_eval(contents);
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../testdata/output/");
settings.set_description(contents);
settings.bind(|| {
insta::assert_snapshot!(result);
});
}
};
}

snap_type_eval!(test_type_eval_vars, "./testdata/inputs/type_eval_vars.py");
}