diff --git a/scripts/print_symbol_table.py b/scripts/print_symbol_table.py new file mode 100755 index 00000000..6955813e --- /dev/null +++ b/scripts/print_symbol_table.py @@ -0,0 +1,52 @@ +import symtable + +def print_symbol_table_info(symbol_table: symtable.SymbolTable, indent=0): + # Print the information of the current symbol table + print(" " * indent + f"Symbol Table: {symbol_table.get_name()}") + print(" " * (indent + 2) + f"Type: {symbol_table.get_type()}") + print(" " * (indent + 2) + f"Identifier: {symbol_table.get_id()}") + print(" " * (indent + 2) + f"First Line Number: {symbol_table.get_lineno()}") + + # Print information specific to Function symbol table + if symbol_table.get_type() == 'function': + function_info = symbol_table.get_parameters(), symbol_table.get_locals(), \ + symbol_table.get_globals(), symbol_table.get_nonlocals(), \ + symbol_table.get_frees() + print(" " * (indent + 2) + f"Function Info: {function_info}") + + # Print information specific to Class symbol table + if symbol_table.get_type() == 'class': + class_methods = symbol_table.get_methods() + print(" " * (indent + 2) + f"Class Methods: {class_methods}") + + # Print information about each symbol in the symbol table + for symbol in symbol_table.get_symbols(): + print(" " * (indent + 2) + f"Symbol: {symbol.get_name()}") + print(" " * (indent + 4) + f"Referenced: {symbol.is_referenced()}") + print(" " * (indent + 4) + f"Imported: {symbol.is_imported()}") + print(" " * (indent + 4) + f"Parameter: {symbol.is_parameter()}") + print(" " * (indent + 4) + f"Global: {symbol.is_global()}") + print(" " * (indent + 4) + f"Nonlocal: {symbol.is_nonlocal()}") + print(" " * (indent + 4) + f"Declared Global: {symbol.is_declared_global()}") + print(" " * (indent + 4) + f"Local: {symbol.is_local()}") + print(" " * (indent + 4) + f"Annotated: {symbol.is_annotated()}") + print(" " * (indent + 4) + f"Free: {symbol.is_free()}") + print(" " * (indent + 4) + f"Assigned: {symbol.is_assigned()}") + print(" " * (indent + 4) + f"Namespace: {symbol.is_namespace()}") + print(" " * (indent + 4) + f"Namespaces: {symbol.get_namespaces()}") + # print namespaces (redundant because repeated in children) + # for namespace in symbol.get_namespaces(): + # print(" " * (indent + 6) + f"Namespace: {namespace}") + # print_symbol_table_info(namespace, indent + 8) + + # Recursively print information of child symbol tables + for child_table in symbol_table.get_children(): + print_symbol_table_info(child_table, indent + 2) + +# Example usage: +code = """ +a = 1 +""" +symbol_table = symtable.symtable(code, "example", "exec") +print_symbol_table_info(symbol_table) + diff --git a/typechecker/src/build.rs b/typechecker/src/build.rs index 7ad5f3d7..46ae53cb 100644 --- a/typechecker/src/build.rs +++ b/typechecker/src/build.rs @@ -114,6 +114,36 @@ mod tests { "def f(): a = 1 return +", + ]; + for source in sources { + let path = write_temp_source(source); + let mut manager = BuildManager::new( + vec![BuildSource { + path, + module: String::from("test"), + source: source.to_string(), + followed: false, + }], + Settings::test_settings(), + ); + manager.build(); + let module = manager.modules.values().last().unwrap(); + insta::with_settings!({ + description => source, // the template source code + omit_expression => true // do not include the default expression + }, { + assert_debug_snapshot!(module.symbol_table); + }); + } + } + + #[test] + fn test_class_def() { + let sources = vec![ + "class c: + def __init__(self): + b = 1 ", ]; for source in sources { diff --git a/typechecker/src/nodes.rs b/typechecker/src/nodes.rs index 9e5db330..53bcc24d 100755 --- a/typechecker/src/nodes.rs +++ b/typechecker/src/nodes.rs @@ -67,4 +67,9 @@ impl<'a> TraversalVisitor for EnderpyFile { let func = f.clone(); self.defs.push(Statement::FunctionDef(func)); } + + fn visit_class_def(&mut self, c: &parser::ast::ClassDef) { + let class = c.clone(); + self.defs.push(Statement::ClassDef(class)); + } } diff --git a/typechecker/src/semantic_analyzer.rs b/typechecker/src/semantic_analyzer.rs index 448ccd5b..5e5f58a5 100644 --- a/typechecker/src/semantic_analyzer.rs +++ b/typechecker/src/semantic_analyzer.rs @@ -6,7 +6,7 @@ use crate::{ ast_visitor::TraversalVisitor, nodes::EnderpyFile, symbol_table::{ - Declaration, DeclarationPath, Function, SymbolScope, SymbolTable, SymbolTableNode, + Class, Declaration, DeclarationPath, Function, SymbolScope, SymbolTable, SymbolTableNode, SymbolTableScope, SymbolTableType, Variable, }, }; @@ -36,9 +36,6 @@ impl SemanticAnalyzer { let symbol_node = SymbolTableNode { name, declarations: vec![decl], - module_public: false, - module_hidden: false, - implicit: false, }; self.globals.add_symbol(symbol_node) } @@ -85,6 +82,9 @@ impl SemanticAnalyzer { ) } } + Expression::Attribute(_) => print!( + "Ignoring attribute assingment. See https://github.com/Glyphack/enderpy/issues/157" + ), _ => panic!("cannot assign to {:?} is not supported", target), } } @@ -237,6 +237,7 @@ impl TraversalVisitor for SemanticAnalyzer { }; self.globals.enter_scope(SymbolTableScope::new( crate::symbol_table::SymbolTableType::Function, + f.name.clone(), )); let mut return_statements = vec![]; let mut yeild_statements = vec![]; @@ -267,9 +268,31 @@ impl TraversalVisitor for SemanticAnalyzer { } fn visit_class_def(&mut self, c: &parser::ast::ClassDef) { + let declaration_path = DeclarationPath { + module_name: self.file.module_name.clone(), + node: c.node, + }; + self.globals.enter_scope(SymbolTableScope::new( + SymbolTableType::Class, + c.name.clone(), + )); + let mut methods = vec![]; for stmt in &c.body { + match stmt { + parser::ast::Statement::FunctionDef(f) => { + methods.push(f.name.clone()); + } + _ => (), + } self.visit_stmt(&stmt); } + self.globals.exit_scope(); + + let class_declaration = Declaration::Class(Box::new(Class { + declaration_path, + methods, + })); + self.create_symbol(c.name.clone(), class_declaration); } fn visit_match(&mut self, m: &parser::ast::Match) { diff --git a/typechecker/src/settings.rs b/typechecker/src/settings.rs index c0714dd0..07818c80 100644 --- a/typechecker/src/settings.rs +++ b/typechecker/src/settings.rs @@ -18,15 +18,11 @@ pub struct Settings { impl Settings { pub fn new() -> Result { let run_mode = env::var("RUN_MODE").unwrap_or_else(|_| "development".into()); - let s = Config::builder() // Start off by merging in the "default" configuration file .add_source(File::with_name("examples/hierarchical-env/config/default")) .build()?; - // Now that we're done, let's access our configuration - println!("debug: {:?}", s.get_bool("debug")); - s.try_deserialize() } diff --git a/typechecker/src/snapshots/typechecker__build__tests__assign_stmt-3.snap b/typechecker/src/snapshots/typechecker__build__tests__assign_stmt-3.snap index 1d0e505d..3a918a3b 100644 --- a/typechecker/src/snapshots/typechecker__build__tests__assign_stmt-3.snap +++ b/typechecker/src/snapshots/typechecker__build__tests__assign_stmt-3.snap @@ -7,8 +7,8 @@ SymbolTable { SymbolTableScope { symbol_table_type: Module, symbols: { - "d": SymbolTableNode { - name: "d", + "c": SymbolTableNode { + name: "c", declarations: [ Variable( Variable { @@ -63,8 +63,8 @@ SymbolTable { module_hidden: false, implicit: false, }, - "c": SymbolTableNode { - name: "c", + "d": SymbolTableNode { + name: "d", declarations: [ Variable( Variable { diff --git a/typechecker/src/snapshots/typechecker__build__tests__class_def.snap b/typechecker/src/snapshots/typechecker__build__tests__class_def.snap new file mode 100644 index 00000000..9cd84038 --- /dev/null +++ b/typechecker/src/snapshots/typechecker__build__tests__class_def.snap @@ -0,0 +1,100 @@ +--- +source: typechecker/src/build.rs +description: "class c:\n def __init__(self):\n b = 1\n" +--- +SymbolTable { + scopes: [ + SymbolTableScope { + symbol_table_type: Module, + name: "global", + symbols: { + "c": SymbolTableNode { + name: "c", + declarations: [ + Class( + Class { + declaration_path: DeclarationPath { + module_name: "test", + node: Node { + start: 0, + end: 46, + }, + }, + methods: [ + "__init__", + ], + }, + ), + ], + }, + }, + }, + ], + all_scopes: [ + SymbolTableScope { + symbol_table_type: Function, + name: "__init__", + symbols: { + "b": SymbolTableNode { + name: "b", + declarations: [ + Variable( + Variable { + declaration_path: DeclarationPath { + module_name: "test", + node: Node { + start: 40, + end: 45, + }, + }, + scope: Global, + type_annotation: None, + inferred_type_source: Some( + Constant( + Constant { + node: Node { + start: 44, + end: 45, + }, + value: Int( + "1", + ), + }, + ), + ), + is_constant: false, + }, + ), + ], + }, + }, + }, + SymbolTableScope { + symbol_table_type: Class, + name: "c", + symbols: { + "__init__": SymbolTableNode { + name: "__init__", + declarations: [ + Function( + Function { + declaration_path: DeclarationPath { + module_name: "test", + node: Node { + start: 12, + end: 46, + }, + }, + is_method: true, + is_generator: false, + return_statements: [], + yeild_statements: [], + raise_statements: [], + }, + ), + ], + }, + }, + }, + ], +} diff --git a/typechecker/src/symbol_table.rs b/typechecker/src/symbol_table.rs index af4bd473..d233dccd 100644 --- a/typechecker/src/symbol_table.rs +++ b/typechecker/src/symbol_table.rs @@ -12,13 +12,15 @@ pub struct SymbolTable { #[derive(Debug)] pub struct SymbolTableScope { pub symbol_table_type: SymbolTableType, + pub name: String, symbols: HashMap, } impl SymbolTableScope { - pub fn new(symbol_table_type: SymbolTableType) -> Self { + pub fn new(symbol_table_type: SymbolTableType, name: String) -> Self { SymbolTableScope { symbol_table_type, + name, symbols: HashMap::new(), } } @@ -35,9 +37,6 @@ pub enum SymbolTableType { pub struct SymbolTableNode { pub name: String, pub declarations: Vec, - pub module_public: bool, - pub module_hidden: bool, - pub implicit: bool, } #[derive(Debug, Clone)] @@ -50,6 +49,7 @@ pub struct DeclarationPath { pub enum Declaration { Variable(Box), Function(Box), + Class(Box), } #[derive(Debug)] @@ -72,6 +72,14 @@ pub struct Function { pub raise_statements: Vec, } +#[derive(Debug)] +pub struct Class { + pub declaration_path: DeclarationPath, + // Method names, can be used to look up the function in the symbol table + // of the class + pub methods: Vec, +} + #[derive(Debug, Clone, Copy)] pub enum SymbolScope { Global, @@ -85,6 +93,7 @@ impl SymbolTable { let global_scope = SymbolTableScope { symbol_table_type, symbols: HashMap::new(), + name: String::from("global"), }; SymbolTable { scopes: vec![global_scope], @@ -119,6 +128,7 @@ impl SymbolTable { Some(scope) => self.all_scopes.push(scope), None => panic!("tried to exit non-existent scope"), } + } pub fn add_symbol(&mut self, symbol_node: SymbolTableNode) {