From ac2ab8d2dcb413955c3199de7326940732e6acd6 Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 9 Mar 2024 21:34:05 -0500 Subject: [PATCH] Check for function & binding duplication in the global scope --- CHANGELOG.md | 7 +++--- src/codegen/binding.rs | 11 +++++--- src/codegen/module_ast.rs | 9 +++++-- src/semantic/impls.rs | 47 +++++++++++++++++++++++++++-------- src/syntax/ast/var_binding.rs | 6 +++-- src/syntax/binding.rs | 4 +-- 6 files changed, 61 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e72f38..2ba9f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## TODO +- Formally define the top level constructs - Implement AST transformation before codegen: Create a new AST to represent PHP source code and a THP ast -> PHP ast process, so that the @@ -32,9 +33,9 @@ - [x] Minimal symbol table - [x] Check duplicate function declarations - [x] Improve REPL/File compilation code -- [ ] Typecheck bindings -- [x] Typecheck functions -- [ ] Transform simple THP expression into PHP statements +- [x] Check binding duplication in it's scope +- [x] Check function duplication in it's scope +- [x] Transform simple THP expression into PHP statements ## v0.0.9 diff --git a/src/codegen/binding.rs b/src/codegen/binding.rs index d1233a8..e2cc0c6 100644 --- a/src/codegen/binding.rs +++ b/src/codegen/binding.rs @@ -6,22 +6,27 @@ impl Transpilable for Binding<'_> { fn transpile(&self) -> String { let expression_str = self.expression.transpile(); - format!("${} = {}", self.identifier, expression_str) + format!("${} = {}", self.identifier.value, expression_str) } } #[cfg(test)] mod tests { use super::*; - use crate::syntax::ast::{var_binding::Binding, Expression}; + use crate::{lexic::token::{Token, TokenType}, syntax::ast::{var_binding::Binding, Expression}}; #[test] fn binding_should_transpile() { let id = String::from("identifier"); + let id_token = Token { + token_type: TokenType::Identifier, + value: id, + position: 0, + }; let value = String::from("322"); let binding = Binding { datatype: None, - identifier: Box::new(id), + identifier: &id_token, expression: Expression::Number(&value), is_mutable: false, }; diff --git a/src/codegen/module_ast.rs b/src/codegen/module_ast.rs index caba1b9..632ae91 100644 --- a/src/codegen/module_ast.rs +++ b/src/codegen/module_ast.rs @@ -18,15 +18,20 @@ impl Transpilable for ModuleAST<'_> { #[cfg(test)] mod tests { use super::*; - use crate::syntax::ast::{var_binding::Binding, Expression, TopLevelDeclaration}; + use crate::{lexic::token::{Token, TokenType}, syntax::ast::{var_binding::Binding, Expression, TopLevelDeclaration}}; #[test] fn module_ast_should_transpile() { let id = String::from("identifier"); + let id_token = Token { + token_type: TokenType::Identifier, + value: id, + position: 0, + }; let value = String::from("322"); let binding = Binding { datatype: None, - identifier: Box::new(id), + identifier: &id_token, expression: Expression::Number(&value), is_mutable: false, }; diff --git a/src/semantic/impls.rs b/src/semantic/impls.rs index 71e17e7..3b97844 100644 --- a/src/semantic/impls.rs +++ b/src/semantic/impls.rs @@ -4,7 +4,7 @@ use crate::{ syntax::ast::{ModuleAST, TopLevelDeclaration}, }; -use super::symbol_table::SymbolTable; +use super::symbol_table::{SymbolEntry, SymbolTable}; pub trait SemanticCheck { fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError>; @@ -24,18 +24,46 @@ impl SemanticCheck for ModuleAST<'_> { impl SemanticCheck for TopLevelDeclaration<'_> { fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { match self { - TopLevelDeclaration::Binding(_) => { - let error = SemanticError { - error_start: 0, - error_end: 0, - reason: "Binding typechecking: Not implemented".into(), + TopLevelDeclaration::Binding(binding) => { + let binding_name = &binding.identifier.value; + + if scope.test(binding_name) { + let error = SemanticError { + error_start: binding.identifier.position, + error_end: binding.identifier.get_end_position(), + reason: format!( + "Duplicated function: A function with name {} was already defined", + binding_name + ), + }; + + return Err(MistiError::Semantic(error)); + } + + let datatype = match binding.datatype { + Some(t) => t, + None => { + let error = SemanticError { + error_start: binding.identifier.position, + error_end: binding.identifier.get_end_position(), + reason: format!( + "The variable `{}` didn't define a datatype. Datatype inference is not implemented.", + binding_name + ), + }; + + return Err(MistiError::Semantic(error)); + } }; - Err(MistiError::Semantic(error)) + scope.insert(binding_name.clone(), SymbolEntry::new_variable(datatype.value.clone())); + + Ok(()) } TopLevelDeclaration::FunctionDeclaration(function) => { let function_name = function.identifier.value.clone(); + // Check that the function is not already defined if scope.test(&function_name) { let error = SemanticError { error_start: function.identifier.position, @@ -49,10 +77,7 @@ impl SemanticCheck for TopLevelDeclaration<'_> { return Err(MistiError::Semantic(error)); } - scope.insert( - function_name, - super::symbol_table::SymbolEntry::Function(vec![], "Unit".into()), - ); + scope.insert(function_name, SymbolEntry::new_function(vec![], "Unit".into())); Ok(()) } diff --git a/src/syntax/ast/var_binding.rs b/src/syntax/ast/var_binding.rs index 1bf41d5..5ce620c 100644 --- a/src/syntax/ast/var_binding.rs +++ b/src/syntax/ast/var_binding.rs @@ -1,9 +1,11 @@ +use crate::lexic::token::Token; + use super::Expression; #[derive(Debug)] pub struct Binding<'a> { - pub datatype: Option, - pub identifier: Box, + pub datatype: Option<&'a Token>, + pub identifier: &'a Token, pub expression: Expression<'a>, pub is_mutable: bool, } diff --git a/src/syntax/binding.rs b/src/syntax/binding.rs index 5f76c42..7065a4c 100644 --- a/src/syntax/binding.rs +++ b/src/syntax/binding.rs @@ -97,7 +97,7 @@ pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> ParseResult