Check for function & binding duplication in the global scope
This commit is contained in:
parent
ed9ccab5e1
commit
ac2ab8d2dc
@ -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
|
||||||
|
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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 error = SemanticError {
|
let binding_name = &binding.identifier.value;
|
||||||
error_start: 0,
|
|
||||||
error_end: 0,
|
if scope.test(binding_name) {
|
||||||
reason: "Binding typechecking: Not implemented".into(),
|
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) => {
|
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(())
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user