Check for function & binding duplication in the global scope

master
Araozu 2024-03-09 21:34:05 -05:00
parent ed9ccab5e1
commit ac2ab8d2dc
6 changed files with 61 additions and 23 deletions

View File

@ -2,6 +2,7 @@
## TODO ## TODO
- Formally define the top level constructs
- Implement AST transformation before codegen: - Implement AST transformation before codegen:
Create a new AST to represent PHP source code Create a new AST to represent PHP source code
and a THP ast -> PHP ast process, so that the and a THP ast -> PHP ast process, so that the
@ -32,9 +33,9 @@
- [x] Minimal symbol table - [x] Minimal symbol table
- [x] Check duplicate function declarations - [x] Check duplicate function declarations
- [x] Improve REPL/File compilation code - [x] Improve REPL/File compilation code
- [ ] Typecheck bindings - [x] Check binding duplication in it's scope
- [x] Typecheck functions - [x] Check function duplication in it's scope
- [ ] Transform simple THP expression into PHP statements - [x] Transform simple THP expression into PHP statements
## v0.0.9 ## v0.0.9

View File

@ -6,22 +6,27 @@ impl Transpilable for Binding<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
let expression_str = self.expression.transpile(); let expression_str = self.expression.transpile();
format!("${} = {}", self.identifier, expression_str) format!("${} = {}", self.identifier.value, expression_str)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::syntax::ast::{var_binding::Binding, Expression}; use crate::{lexic::token::{Token, TokenType}, syntax::ast::{var_binding::Binding, Expression}};
#[test] #[test]
fn binding_should_transpile() { fn binding_should_transpile() {
let id = String::from("identifier"); let id = String::from("identifier");
let id_token = Token {
token_type: TokenType::Identifier,
value: id,
position: 0,
};
let value = String::from("322"); let value = String::from("322");
let binding = Binding { let binding = Binding {
datatype: None, datatype: None,
identifier: Box::new(id), identifier: &id_token,
expression: Expression::Number(&value), expression: Expression::Number(&value),
is_mutable: false, is_mutable: false,
}; };

View File

@ -18,15 +18,20 @@ impl Transpilable for ModuleAST<'_> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; 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] #[test]
fn module_ast_should_transpile() { fn module_ast_should_transpile() {
let id = String::from("identifier"); let id = String::from("identifier");
let id_token = Token {
token_type: TokenType::Identifier,
value: id,
position: 0,
};
let value = String::from("322"); let value = String::from("322");
let binding = Binding { let binding = Binding {
datatype: None, datatype: None,
identifier: Box::new(id), identifier: &id_token,
expression: Expression::Number(&value), expression: Expression::Number(&value),
is_mutable: false, is_mutable: false,
}; };

View File

@ -4,7 +4,7 @@ use crate::{
syntax::ast::{ModuleAST, TopLevelDeclaration}, syntax::ast::{ModuleAST, TopLevelDeclaration},
}; };
use super::symbol_table::SymbolTable; use super::symbol_table::{SymbolEntry, SymbolTable};
pub trait SemanticCheck { pub trait SemanticCheck {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError>; fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError>;
@ -24,18 +24,46 @@ impl SemanticCheck for ModuleAST<'_> {
impl SemanticCheck for TopLevelDeclaration<'_> { impl SemanticCheck for TopLevelDeclaration<'_> {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> {
match self { match self {
TopLevelDeclaration::Binding(_) => { TopLevelDeclaration::Binding(binding) => {
let binding_name = &binding.identifier.value;
if scope.test(binding_name) {
let error = SemanticError { let error = SemanticError {
error_start: 0, error_start: binding.identifier.position,
error_end: 0, error_end: binding.identifier.get_end_position(),
reason: "Binding typechecking: Not implemented".into(), reason: format!(
"Duplicated function: A function with name {} was already defined",
binding_name
),
}; };
Err(MistiError::Semantic(error)) 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));
}
};
scope.insert(binding_name.clone(), SymbolEntry::new_variable(datatype.value.clone()));
Ok(())
} }
TopLevelDeclaration::FunctionDeclaration(function) => { TopLevelDeclaration::FunctionDeclaration(function) => {
let function_name = function.identifier.value.clone(); let function_name = function.identifier.value.clone();
// Check that the function is not already defined
if scope.test(&function_name) { if scope.test(&function_name) {
let error = SemanticError { let error = SemanticError {
error_start: function.identifier.position, error_start: function.identifier.position,
@ -49,10 +77,7 @@ impl SemanticCheck for TopLevelDeclaration<'_> {
return Err(MistiError::Semantic(error)); return Err(MistiError::Semantic(error));
} }
scope.insert( scope.insert(function_name, SymbolEntry::new_function(vec![], "Unit".into()));
function_name,
super::symbol_table::SymbolEntry::Function(vec![], "Unit".into()),
);
Ok(()) Ok(())
} }

View File

@ -1,9 +1,11 @@
use crate::lexic::token::Token;
use super::Expression; use super::Expression;
#[derive(Debug)] #[derive(Debug)]
pub struct Binding<'a> { pub struct Binding<'a> {
pub datatype: Option<String>, pub datatype: Option<&'a Token>,
pub identifier: Box<String>, pub identifier: &'a Token,
pub expression: Expression<'a>, pub expression: Expression<'a>,
pub is_mutable: bool, pub is_mutable: bool,
} }

View File

@ -97,7 +97,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
let binding = Binding { let binding = Binding {
datatype: None, datatype: None,
identifier: Box::new(identifier.value.clone()), identifier: &identifier,
expression, expression,
is_mutable, is_mutable,
}; };
@ -117,7 +117,7 @@ mod tests {
panic!() panic!()
}; };
assert_eq!("identifier", format!("{}", binding.identifier)); assert_eq!("identifier", format!("{}", binding.identifier.value));
} }
#[test] #[test]