Compare commits
No commits in common. "6f56ab0b4d46151660f51bfbe201d1fe41742f7f" and "712936e4d6cd8712ca642c361ae4dd635598202f" have entirely different histories.
6f56ab0b4d
...
712936e4d6
@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
- [ ] 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
|
||||||
|
@ -25,7 +25,6 @@ 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 {
|
||||||
|
@ -38,7 +38,8 @@ 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(_) => {
|
Err(error) => {
|
||||||
|
eprintln!("{}", error);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -59,15 +60,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, ()> {
|
fn compile(input: &String) -> Result<String, 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) => {
|
||||||
error.print_ariadne(input);
|
let chars: Vec<char> = input.chars().collect();
|
||||||
return Err(());
|
return Err(error.get_error_str(&chars));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,9 +77,9 @@ fn compile(input: &String) -> Result<String, ()> {
|
|||||||
//
|
//
|
||||||
let ast = match syntax::build_ast(&tokens) {
|
let ast = match syntax::build_ast(&tokens) {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(error) => {
|
Err(reason) => {
|
||||||
error.print_ariadne(input);
|
let chars: Vec<char> = input.chars().collect();
|
||||||
return Err(());
|
return Err(reason.get_error_str(&chars));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,9 +89,10 @@ fn compile(input: &String) -> Result<String, ()> {
|
|||||||
let res1 = crate::semantic::check_semantics(&ast);
|
let res1 = crate::semantic::check_semantics(&ast);
|
||||||
match res1 {
|
match res1 {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(error) => {
|
Err(reason) => {
|
||||||
error.print_ariadne(input);
|
let chars: Vec<char> = input.chars().collect();
|
||||||
return Err(());
|
let error = format!("{}: {}", "error".on_red(), reason.get_error_str(&chars));
|
||||||
|
return Err(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
impls::SemanticCheck,
|
impls::SemanticCheck,
|
||||||
types::{Type, Typed},
|
types::{Type, Typed},
|
||||||
},
|
},
|
||||||
syntax::ast::{var_binding::VariableBinding, Positionable},
|
syntax::ast::var_binding::VariableBinding,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl SemanticCheck for VariableBinding<'_> {
|
impl SemanticCheck for VariableBinding<'_> {
|
||||||
@ -46,33 +46,25 @@ impl SemanticCheck for VariableBinding<'_> {
|
|||||||
|
|
||||||
// Both the declared & actual datatypes must be the same
|
// Both the declared & actual datatypes must be the same
|
||||||
if datatype != expression_datatype {
|
if datatype != expression_datatype {
|
||||||
// This can only happen if the binding has an annotated type,
|
let label = ErrorLabel {
|
||||||
// so its safe to unwrap here
|
message: format!(
|
||||||
let datatype_token = self.datatype.unwrap();
|
"The variable `{}` was declared as `{:?}` but its expression has type `{:?}`",
|
||||||
|
binding_name, datatype, expression_datatype
|
||||||
let label1 = ErrorLabel {
|
),
|
||||||
message: format!("The variable is declared as {:?} here", datatype),
|
start: self.identifier.position,
|
||||||
start: datatype_token.position,
|
end: self.identifier.get_end_position(),
|
||||||
end: datatype_token.get_end_position(),
|
|
||||||
};
|
};
|
||||||
let (expr_start, expr_end) = self.expression.get_position();
|
|
||||||
let label2 = ErrorLabel {
|
|
||||||
message: format!("But this expression has type {:?}", expression_datatype),
|
|
||||||
start: expr_start,
|
|
||||||
end: expr_end,
|
|
||||||
};
|
|
||||||
|
|
||||||
let econtainer = ErrorContainer {
|
let econtainer = ErrorContainer {
|
||||||
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
||||||
error_offset: self.identifier.position,
|
error_offset: self.identifier.position,
|
||||||
labels: vec![label1, label2],
|
labels: vec![label],
|
||||||
note: None,
|
note: None,
|
||||||
help: None,
|
help: None,
|
||||||
};
|
};
|
||||||
return Err(econtainer);
|
return Err(econtainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.insert_custom(binding_name.clone(), datatype, self.is_mutable);
|
scope.insert(binding_name.clone(), datatype);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
pub mod assignment;
|
|
||||||
pub mod binding;
|
pub mod binding;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod conditional;
|
pub mod conditional;
|
||||||
|
@ -25,7 +25,9 @@ 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(a) => a.check_semantics(scope),
|
Statement::Assignment(_assignment) => {
|
||||||
|
unimplemented!("Semantic check for an assignment")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, bool)>,
|
scope: HashMap<String, Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolTable {
|
impl SymbolTable {
|
||||||
@ -33,17 +33,7 @@ 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, false);
|
self.node.borrow_mut().insert(key, value);
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
||||||
@ -55,11 +45,6 @@ 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 {
|
||||||
@ -80,8 +65,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, is_mutable: bool) {
|
pub fn insert(&mut self, key: String, value: Type) {
|
||||||
self.scope.insert(key, (value, is_mutable));
|
self.scope.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests if a symbol is declared in the current or parent scopes
|
/// Tests if a symbol is declared in the current or parent scopes
|
||||||
@ -102,7 +87,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());
|
||||||
}
|
}
|
||||||
@ -116,22 +101,4 @@ 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user