feat: semantic check simple assignment

This commit is contained in:
Araozu 2024-11-03 11:11:22 -05:00
parent 712936e4d6
commit 469a9eaf5e
8 changed files with 139 additions and 21 deletions

View File

@ -30,6 +30,7 @@
- [ ] Test semantic analysis - [ ] Test semantic analysis
- [ ] Generate php code from current AST - [ ] Generate php code from current AST
- [x] Typecheck and semantic check simple assignment
- [x] Test correct operator precedence - [x] Test correct operator precedence
- [x] Parse assignments - [x] Parse assignments
- [x] Parse dot `.` operator - [x] Parse dot `.` operator

View File

@ -25,6 +25,7 @@ pub const SEMANTIC_MISMATCHED_TYPES: u32 = 21;
pub const SEMANTIC_DUPLICATED_REFERENCE: u32 = 22; pub const SEMANTIC_DUPLICATED_REFERENCE: u32 = 22;
pub const SEMANTIC_MISMATCHED_ARGUMENT_COUNT: u32 = 23; pub const SEMANTIC_MISMATCHED_ARGUMENT_COUNT: u32 = 23;
pub const SYNTAX_INVALID_ARRAY_ACCESS: u32 = 24; pub const SYNTAX_INVALID_ARRAY_ACCESS: u32 = 24;
pub const SEMANTIC_IMMUTABLE_VARIABLE: u32 = 25;
/// Reads the error codes from the error code list /// Reads the error codes from the error code list
pub fn error_code_to_string() -> String { pub fn error_code_to_string() -> String {

View File

@ -38,8 +38,7 @@ pub fn compile_file(input: &String) -> Result<(), ()> {
let out_code = match compile(&contents) { let out_code = match compile(&contents) {
Ok(out_code) => out_code, Ok(out_code) => out_code,
Err(error) => { Err(_) => {
eprintln!("{}", error);
return Err(()); return Err(());
} }
}; };
@ -60,15 +59,15 @@ pub fn compile_file(input: &String) -> Result<(), ()> {
} }
/// Full pipeline from THP source code to PHP output /// Full pipeline from THP source code to PHP output
fn compile(input: &String) -> Result<String, String> { fn compile(input: &String) -> Result<String, ()> {
// //
// Lexical analysis // Lexical analysis
// //
let tokens = match lexic::get_tokens(input) { let tokens = match lexic::get_tokens(input) {
Ok(t) => t, Ok(t) => t,
Err(error) => { Err(error) => {
let chars: Vec<char> = input.chars().collect(); error.print_ariadne(input);
return Err(error.get_error_str(&chars)); return Err(());
} }
}; };
@ -77,9 +76,9 @@ fn compile(input: &String) -> Result<String, String> {
// //
let ast = match syntax::build_ast(&tokens) { let ast = match syntax::build_ast(&tokens) {
Ok(ast) => ast, Ok(ast) => ast,
Err(reason) => { Err(error) => {
let chars: Vec<char> = input.chars().collect(); error.print_ariadne(input);
return Err(reason.get_error_str(&chars)); return Err(());
} }
}; };
@ -89,10 +88,9 @@ fn compile(input: &String) -> Result<String, String> {
let res1 = crate::semantic::check_semantics(&ast); let res1 = crate::semantic::check_semantics(&ast);
match res1 { match res1 {
Ok(_) => {} Ok(_) => {}
Err(reason) => { Err(error) => {
let chars: Vec<char> = input.chars().collect(); error.print_ariadne(input);
let error = format!("{}: {}", "error".on_red(), reason.get_error_str(&chars)); return Err(());
return Err(error);
} }
} }

View File

@ -0,0 +1,86 @@
use crate::{
error_handling::{
self,
error_messages::{SEMANTIC_IMMUTABLE_VARIABLE, SEMANTIC_INVALID_REFERENCE},
ErrorContainer, ErrorLabel,
},
semantic::{self, impls::SemanticCheck, types::Typed},
syntax::ast::{Assignment, Positionable},
};
impl SemanticCheck for Assignment<'_> {
fn check_semantics(
&self,
scope: &semantic::symbol_table::SymbolTable,
) -> Result<(), error_handling::MistiError> {
// for now the assignment can only be to a variable
// get the datatype and mutability status
let datatype = match scope.get_type_and_mut(&self.identifier.value) {
Some((datatype, true)) => datatype,
Some((_, false)) => {
// throw error: variable is immutable
let label = ErrorLabel {
message: String::from(
"This variable is immutable, therefore it cannot be assigned a new value",
),
start: self.identifier.position,
end: self.identifier.get_end_position(),
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_IMMUTABLE_VARIABLE,
error_offset: self.identifier.position,
labels: vec![label],
note: None,
help: None,
};
return Err(econtainer);
}
None => {
// throw error: variable does not exist
let label = ErrorLabel {
message: String::from("This variable does not exist in this scope"),
start: self.identifier.position,
end: self.identifier.get_end_position(),
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_INVALID_REFERENCE,
error_offset: self.identifier.position,
labels: vec![label],
note: None,
help: None,
};
return Err(econtainer);
}
};
// assert the datatype is the same
let expression_type = self.expression.get_type(scope)?;
if !datatype.equals(&expression_type) {
// throw error: variable and expression have different types
let label = ErrorLabel {
message: format!("This variable has type {:?}", datatype),
start: self.identifier.position,
end: self.identifier.get_end_position(),
};
let (expr_start, expr_end) = self.expression.get_position();
let label2 = ErrorLabel {
message: format!("But this expression has type {:?}", expression_type),
start: expr_start,
end: expr_end,
};
let econtainer = ErrorContainer {
error_code: SEMANTIC_INVALID_REFERENCE,
error_offset: self.identifier.position,
labels: vec![label, label2],
note: None,
help: None,
};
return Err(econtainer);
}
// ok
Ok(())
}
}

View File

@ -64,7 +64,7 @@ impl SemanticCheck for VariableBinding<'_> {
return Err(econtainer); return Err(econtainer);
} }
scope.insert(binding_name.clone(), datatype); scope.insert_custom(binding_name.clone(), datatype, self.is_mutable);
Ok(()) Ok(())
} }

View File

@ -1,3 +1,4 @@
pub mod assignment;
pub mod binding; pub mod binding;
pub mod block; pub mod block;
pub mod conditional; pub mod conditional;

View File

@ -25,9 +25,7 @@ impl SemanticCheck for Statement<'_> {
Statement::Conditional(c) => c.check_semantics(scope), Statement::Conditional(c) => c.check_semantics(scope),
Statement::ForLoop(f) => f.check_semantics(scope), Statement::ForLoop(f) => f.check_semantics(scope),
Statement::WhileLoop(w) => w.check_semantics(scope), Statement::WhileLoop(w) => w.check_semantics(scope),
Statement::Assignment(_assignment) => { Statement::Assignment(a) => a.check_semantics(scope),
unimplemented!("Semantic check for an assignment")
}
} }
} }
} }

View File

@ -12,7 +12,7 @@ struct SymbolTableNode {
// the parent scope // the parent scope
parent: Option<Rc<RefCell<SymbolTableNode>>>, parent: Option<Rc<RefCell<SymbolTableNode>>>,
// the current scope // the current scope
scope: HashMap<String, Type>, scope: HashMap<String, (Type, bool)>,
} }
impl SymbolTable { impl SymbolTable {
@ -33,7 +33,17 @@ impl SymbolTable {
/// Inserts a new symbol into the current table scope /// Inserts a new symbol into the current table scope
pub fn insert(&self, key: String, value: Type) { pub fn insert(&self, key: String, value: Type) {
self.node.borrow_mut().insert(key, value); self.node.borrow_mut().insert(key, value, false);
}
/// Inserts a new symbol into the current table scope
pub fn insert_mutable(&self, key: String, value: Type) {
self.node.borrow_mut().insert(key, value, true);
}
/// Inserts a new symbol into the current table scope
pub fn insert_custom(&self, key: String, value: Type, is_mutable: bool) {
self.node.borrow_mut().insert(key, value, is_mutable);
} }
/// Tests if a symbol is declared in the current or parent scopes /// Tests if a symbol is declared in the current or parent scopes
@ -45,6 +55,11 @@ impl SymbolTable {
pub fn get_type<'a>(&'a self, key: &String) -> Option<Type> { pub fn get_type<'a>(&'a self, key: &String) -> Option<Type> {
self.node.borrow_mut().get_type(key) self.node.borrow_mut().get_type(key)
} }
/// Gets the datatype of a symbol, if it exists, and if its mutable
pub fn get_type_and_mut<'a>(&'a self, key: &String) -> Option<(Type, bool)> {
self.node.borrow_mut().get_type_and_mut(key)
}
} }
impl SymbolTableNode { impl SymbolTableNode {
@ -65,8 +80,8 @@ impl SymbolTableNode {
} }
/// Inserts a new symbol into the current scope /// Inserts a new symbol into the current scope
pub fn insert(&mut self, key: String, value: Type) { pub fn insert(&mut self, key: String, value: Type, is_mutable: bool) {
self.scope.insert(key, value); self.scope.insert(key, (value, is_mutable));
} }
/// Tests if a symbol is declared in the current or parent scopes /// Tests if a symbol is declared in the current or parent scopes
@ -87,7 +102,7 @@ impl SymbolTableNode {
/// Returns the symbol's datatype /// Returns the symbol's datatype
pub fn get_type<'a>(&'a mut self, key: &String) -> Option<Type> { pub fn get_type<'a>(&'a mut self, key: &String) -> Option<Type> {
// Try to get the type in the current scope // Try to get the type in the current scope
if let Some(entry) = self.scope.get(key) { if let Some((entry, _)) = self.scope.get(key) {
// TODO: Change to allow other types of datatypes: functions, classes, maps // TODO: Change to allow other types of datatypes: functions, classes, maps
return Some(entry.clone()); return Some(entry.clone());
} }
@ -101,4 +116,22 @@ impl SymbolTableNode {
None => None, None => None,
} }
} }
/// Returns the symbol's datatype and mutability
pub fn get_type_and_mut<'a>(&'a mut self, key: &String) -> Option<(Type, bool)> {
// Try to get the type in the current scope
if let Some((entry, mutable)) = self.scope.get(key) {
// TODO: Change to allow other types of datatypes: functions, classes, maps
return Some((entry.clone(), *mutable));
}
// Try to get the type in the parent scope
match &self.parent {
Some(parent) => {
parent.as_ref().borrow_mut().get_type_and_mut(key)
// parent.get_type(key)
}
None => None,
}
}
} }