diff --git a/CHANGELOG.md b/CHANGELOG.md index 74bd57f..862e953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,8 +23,9 @@ - [x] Parse function call parameters - [x] Codegen function call parameters - [x] Parse function declaration arguments (Type id) -- [ ] Begin work on semantic analysis -- [ ] Symbol table +- [x] Begin work on semantic analysis +- [x] Minimal symbol table +- [x] Check duplicate function declarations - [ ] Typecheck bindings - [ ] Typecheck functions - [ ] Transform simple THP expression into PHP statements diff --git a/src/error_handling/syntax_error.rs b/src/error_handling/syntax_error.rs index 197ef5c..e585c25 100644 --- a/src/error_handling/syntax_error.rs +++ b/src/error_handling/syntax_error.rs @@ -116,11 +116,7 @@ fn get_line_number(chars: &Vec, target_pos: usize) -> usize { #[cfg(test)] mod tests { use super::*; - use crate::{ - error_handling::MistiError, - lexic::get_tokens, - syntax::construct_ast, - }; + use crate::{error_handling::MistiError, lexic::get_tokens, syntax::construct_ast}; fn _get_error_data(input: String) -> (Vec, MistiError) { let tokens = get_tokens(&input).unwrap(); diff --git a/src/file/mod.rs b/src/file/mod.rs index 9157466..6ddcae8 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -62,7 +62,16 @@ fn build_ast(input: &String, tokens: Vec) -> String { let ast = syntax::construct_ast(&tokens); match ast { - Ok(ast) => codegen::codegen(&ast), + Ok(ast) => { + match crate::semantic::check_semantics(&ast) { + Ok(_) => {} + Err(reason) => { + panic!("{}", reason) + } + }; + + codegen::codegen(&ast) + } Err(reason) => { let chars: Vec = input.chars().into_iter().collect(); panic!("{}", reason.get_error_str(&chars)) diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 4c9a7e2..c308b59 100755 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -30,6 +30,15 @@ fn build_ast(input: &String, tokens: Vec) { match ast { Ok(ast) => { + let res1 = crate::semantic::check_semantics(&ast); + match res1 { + Ok(_) => {} + Err(reason) => { + eprintln!("{}", reason); + return; + } + } + let js_code = codegen::codegen(&ast); println!("{}", js_code) } diff --git a/src/semantic/impls.rs b/src/semantic/impls.rs new file mode 100644 index 0000000..0440cb7 --- /dev/null +++ b/src/semantic/impls.rs @@ -0,0 +1,39 @@ +use crate::syntax::ast::{ModuleAST, TopLevelDeclaration}; + +use super::symbol_table::SymbolTable; + +pub trait SemanticCheck { + fn check_semantics(&self, scope: &SymbolTable) -> Result<(), String>; +} + +impl SemanticCheck for ModuleAST { + fn check_semantics(&self, scope: &SymbolTable) -> Result<(), String> { + for declaration in &self.declarations { + declaration.check_semantics(scope)?; + } + + Ok(()) + } +} + +impl SemanticCheck for TopLevelDeclaration { + fn check_semantics(&self, scope: &SymbolTable) -> Result<(), String> { + match self { + TopLevelDeclaration::Binding(_) => Err("Binding not implemented".into()), + TopLevelDeclaration::FunctionDeclaration(function) => { + let function_name = function.identifier.as_ref().clone(); + + if scope.test(&function_name) { + return Err(format!("Function {} already defined", function_name)); + } + + scope.insert( + function_name, + super::symbol_table::SymbolEntry::Function(vec![], "Unit".into()), + ); + + Ok(()) + } + } + } +} diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 2084387..4e65c6f 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -1,11 +1,23 @@ +use crate::syntax::ast::ModuleAST; + +mod impls; mod symbol_table; +use impls::SemanticCheck; + // What to do? // 1. Create a mutable symbol table // 2. Walk the AST // 3. Add the symbols declared to the symbol table, annotating them with their type // 4. Check if the symbols used are declared +pub fn check_semantics(ast: &ModuleAST) -> Result<(), String> { + // For now there's only support for a single file + let global_scope = symbol_table::SymbolTable::new(); + + ast.check_semantics(&global_scope) +} + #[cfg(test)] mod tests { use super::symbol_table::{SymbolEntry, SymbolTable};