Skip to content

Commit

Permalink
Add declaration to symbol table
Browse files Browse the repository at this point in the history
  • Loading branch information
Glyphack committed Jul 24, 2023
1 parent 94ded29 commit e5a0230
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 137 deletions.
3 changes: 1 addition & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use parser::{token, Lexer, Parser};
use std::{fs, path::PathBuf};
use typechecker::{
build::{BuildManager, BuildSource},
semantic_analyzer::SemanticAnalyzer,
settings::{ImportDiscovery, Settings},
};

Expand Down Expand Up @@ -51,7 +50,7 @@ fn check(path: &PathBuf) -> Result<()> {
let source = fs::read_to_string(path)?;
let initial_source = BuildSource {
path: path.to_owned(),
module: None,
module: String::from("test"),
source,
followed: false,
};
Expand Down
63 changes: 28 additions & 35 deletions typechecker/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ use crate::symbol_table::SymbolTable;

pub struct BuildSource {
pub path: PathBuf,
pub module: Option<String>,
pub module: String,
pub source: String,
// If this source was found by following an import
pub followed: bool,
}

pub struct BuildManager {
errors: Vec<String>,
sources: Vec<BuildSource>,
pub modules: HashMap<String, State>,
missing_modules: Vec<String>,
options: Settings,
Expand All @@ -29,54 +28,48 @@ impl BuildManager {
panic!("analyzing more than 1 input is not supported");
}

let mut modules = HashMap::new();

for build_source in sources {
let mod_name = Self::get_module_name(&build_source);
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 });
}

BuildManager {
errors: vec![],
sources,
modules: HashMap::new(),
modules,
missing_modules: vec![],
options,
}
}

fn prepare_modules(&mut self) {
for build_source in &self.sources {
let file = self.parse_file(&build_source.source);
let symbol_table = SymbolTable::new(crate::symbol_table::SymbolTableType::Module, 0);
pub fn parse_file(source: &String, module_name: String) -> EnderpyFile {
let mut parser = Parser::new(source.clone());
let tree = parser.parse();
EnderpyFile::from(tree, module_name)
}

self.modules.insert(
self.get_module_name(build_source),
State { file, symbol_table },
);
}
pub fn get_module_name(source: &BuildSource) -> String {
source
.path
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string()
}

fn prepare_modules(&mut self) {}

// Entry point to analyze the program
pub fn build(&mut self) {
self.prepare_modules();
self.pre_analysis();
}

pub fn get_module_name(&self, source: &BuildSource) -> String {
if let Some(module) = &source.module {
module.clone()
} else {
// TODO: fix how module name is determined
source
.path
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string()
}
}

pub fn parse_file(&self, source: &String) -> EnderpyFile {
let mut parser = Parser::new(source.clone());
let tree = parser.parse();
EnderpyFile::from(tree)
}

// Performs pre-analysis on the source files
// Fills up the symbol table for each module
fn pre_analysis(&mut self) {
Expand Down Expand Up @@ -117,7 +110,7 @@ mod tests {
let mut manager = BuildManager::new(
vec![BuildSource {
path,
module: Some(String::from("test")),
module: String::from("test"),
source: source.to_string(),
followed: false,
}],
Expand Down
1 change: 0 additions & 1 deletion typechecker/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
mod ast_visitor;
mod nodes;
mod pre_analysis;
mod semanal_utils;
mod state;
mod symbol_table;
Expand Down
6 changes: 5 additions & 1 deletion typechecker/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,25 @@ use parser::ast::{Import, ImportFrom, Module, Statement};

use crate::ast_visitor::TraversalVisitor;

#[derive(Clone)]
pub enum ImportKinds {
Import(Import),
ImportFrom(ImportFrom),
}

#[derive(Clone)]
pub struct EnderpyFile {
pub module_name: String,
// all the imports inside the file
pub imports: Vec<ImportKinds>,
// high level definitions inside the file
pub defs: Vec<Statement>,
}

impl<'a> EnderpyFile {
pub fn from(ast: Module) -> Self {
pub fn from(ast: Module, module_name: String) -> Self {
let mut file = Self {
module_name,
defs: vec![],
imports: vec![],
};
Expand Down
5 changes: 0 additions & 5 deletions typechecker/src/pre_analysis.rs

This file was deleted.

19 changes: 1 addition & 18 deletions typechecker/src/semanal_utils.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1 @@
use parser::ast::{ConstantValue, Expression};

use crate::symbol_table::NodeType;

pub fn get_target_name(assignment_target: &Expression) -> String {
match assignment_target {
Expression::Name(n) => n.id.clone(),
_ => panic!("cannot defer name of the assignment target"),
}
}


pub fn get_constant_value_type(constant: &ConstantValue) -> NodeType {
match constant {
ConstantValue::Str(_) => NodeType::String,
_ => panic!("not impl"),
}
}
use parser::ast::Expression;
90 changes: 38 additions & 52 deletions typechecker/src/semantic_analyzer.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,54 @@
use parser::ast::{Expression, Node};
use std::ops::Deref;

use parser::ast::Expression;

use crate::{
ast_visitor::TraversalVisitor,
semanal_utils::{get_constant_value_type, get_target_name},
symbol_table::{NodeType, SymbolScope, SymbolTable, SymbolTableNode},
nodes::EnderpyFile,
symbol_table::{
Declaration, DeclarationPath, SymbolScope, SymbolTable, SymbolTableNode, Variable,
},
};

pub struct SemanticAnalyzer {
pub globals: SymbolTable,
// TODO: Replace errors with another type
file: Box<EnderpyFile>,
errors: Vec<String>,
scope: SymbolScope,
}

impl SemanticAnalyzer {
pub fn new() -> Self {
pub fn new(file: Box<EnderpyFile>) -> Self {
let globals = SymbolTable::new(crate::symbol_table::SymbolTableType::Module, 0);
return SemanticAnalyzer {
globals,
file,
errors: vec![],
scope: SymbolScope::Global,
};
}

fn add_expression_to_symbol_table(
&mut self,
name: String,
node_type: NodeType,
node: Node,
public: bool,
) {
fn add_declaration_to_symbol_table(&mut self, name: String, decl: Declaration) {
let symbol_node = SymbolTableNode {
name,
node,
typ: node_type,
module_public: public,
declarations: vec![decl],
module_public: false,
module_hidden: false,
implicit: false,
scope: self.scope,
};
self.globals.add_symbol(symbol_node)
}

// TODO: return unresolvable part
pub fn get_expr_type(&mut self, expr: &Expression) -> NodeType {
match expr {
Expression::Constant(c) => get_constant_value_type(&c.value),
Expression::Name(n) => *self.resolve_name(&n.id),
// TODO: of course wrong, but maybe
Expression::BinOp(b) => self.get_expr_type(&b.left),
_ => panic!("not implemented type"),
}
}

fn resolve_name(&self, name: &str) -> &NodeType {
let matched_scope = match self.globals.lookup_in_scope(name) {
Some(node) => return &node.typ,
None => (),
};

// TODO: maybe check python globals and libs?

&NodeType::Unknown
}

fn report_unresolved_reference(&mut self) {
self.errors
.push(String::from(format!("cannot resolve reference {}", "")))
}

fn current_scope(&self) -> SymbolScope {
SymbolScope::Global
}
}

impl TraversalVisitor for SemanticAnalyzer {
Expand Down Expand Up @@ -231,9 +212,7 @@ impl TraversalVisitor for SemanticAnalyzer {
}
}

fn visit_constant(&mut self, c: &parser::ast::Constant) {
todo!()
}
fn visit_constant(&mut self, c: &parser::ast::Constant) {}

fn visit_list(&mut self, l: &parser::ast::List) {
todo!()
Expand Down Expand Up @@ -263,9 +242,7 @@ impl TraversalVisitor for SemanticAnalyzer {
todo!()
}

fn visit_bin_op(&mut self, b: &parser::ast::BinOp) {
todo!()
}
fn visit_bin_op(&mut self, b: &parser::ast::BinOp) {}

fn visit_named_expr(&mut self, n: &parser::ast::NamedExpression) {
todo!()
Expand Down Expand Up @@ -343,20 +320,29 @@ impl TraversalVisitor for SemanticAnalyzer {
todo!()
}

// TODO: Maybe we need to provide the full ast::Statement instead of the enum value?
// Not sure but it makes it easier to store the node in Symbol Table
// But puts more weight on the traverse code to decode the type
fn visit_assign(&mut self, assign: &parser::ast::Assign) {
if assign.targets.len() > 1 {
panic!("assignment to multiple targets not implemented")
}
let name = get_target_name(&assign.targets.last().unwrap());
// handle when the value is not constant
let nt = self.get_expr_type(&assign.value);
if (matches!(nt, NodeType::Unknown)) {
self.report_unresolved_reference();
}
self.add_expression_to_symbol_table(name, nt, assign.node, false);
let name_node = match assign.targets.last().unwrap() {
Expression::Name(n) => n,
_ => panic!("assignment to other than name node"),
};

let declared_name = name_node.id.clone();
let decl = Declaration::Variable(Box::new(Variable {
declaration_path: DeclarationPath {
module_name: self.file.module_name.clone(),
node: assign.node,
},
scope: self.current_scope(),
type_annotation: None,
inferred_type_source: Some(assign.value.clone()),
is_constant: false,
}));
self.add_declaration_to_symbol_table(declared_name, decl);

self.visit_expr(&assign.value);
}

fn visit_ann_assign(&mut self, a: &parser::ast::AnnAssign) {
Expand Down
Loading

0 comments on commit e5a0230

Please sign in to comment.