feat: store identifier token in the ast
This commit is contained in:
parent
d5f2176fa7
commit
833a8774d8
@ -25,7 +25,7 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
|
|||||||
// TODO: This definitely needs refactoring
|
// TODO: This definitely needs refactoring
|
||||||
let function_expr: &Expression = &*fc.function;
|
let function_expr: &Expression = &*fc.function;
|
||||||
match function_expr {
|
match function_expr {
|
||||||
Expression::Identifier(id) if *id == "print" => {
|
Expression::Identifier(id) if id.value == "print" => {
|
||||||
// transform to print() expression
|
// transform to print() expression
|
||||||
// no parameters supported
|
// no parameters supported
|
||||||
|
|
||||||
|
106
src/semantic/checks/expression.rs
Normal file
106
src/semantic/checks/expression.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
use crate::{
|
||||||
|
error_handling::{semantic_error::SemanticError, MistiError},
|
||||||
|
semantic::{impls::SemanticCheck, symbol_table::SymbolTable, types::{Type, Typed}},
|
||||||
|
syntax::ast::Expression,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl SemanticCheck for Expression<'_> {
|
||||||
|
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> {
|
||||||
|
match self {
|
||||||
|
Expression::FunctionCall(f) => {
|
||||||
|
let fun = &*f.function;
|
||||||
|
let arguments = &*f.arguments.arguments;
|
||||||
|
|
||||||
|
let function_datatype = fun.get_type(scope)?;
|
||||||
|
match function_datatype {
|
||||||
|
Type::Function(parameters, _return_type) => {
|
||||||
|
// Check parameters length
|
||||||
|
if parameters.len() != arguments.len() {
|
||||||
|
return Err(MistiError::Semantic(SemanticError {
|
||||||
|
// TODO: fix
|
||||||
|
error_start: 0,
|
||||||
|
error_end: 1,
|
||||||
|
reason: format!(
|
||||||
|
"Expected {} arguments, found {}",
|
||||||
|
parameters.len(),
|
||||||
|
arguments.len(),
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that each argument matches the required datatype
|
||||||
|
for i in 0..parameters.len() {
|
||||||
|
let parameter = ¶meters[i];
|
||||||
|
let argument = &arguments[i];
|
||||||
|
|
||||||
|
let argument_datatype = argument.get_type(scope)?;
|
||||||
|
if !argument_datatype.is_value(parameter) {
|
||||||
|
// The argument and the parameter have diferent types
|
||||||
|
return Err(MistiError::Semantic(SemanticError {
|
||||||
|
// TODO: fix
|
||||||
|
error_start: 0,
|
||||||
|
error_end: 1,
|
||||||
|
reason: format!(
|
||||||
|
"Expected datatype {}, got {:?}",
|
||||||
|
parameter, argument
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(MistiError::Semantic(SemanticError {
|
||||||
|
// TODO: fix
|
||||||
|
error_start: 0,
|
||||||
|
error_end: 1,
|
||||||
|
reason: format!(
|
||||||
|
"Expected a function type, got {:?}",
|
||||||
|
function_datatype
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::Int(_) => {}
|
||||||
|
Expression::Float(_) => {}
|
||||||
|
Expression::String(_) => {}
|
||||||
|
Expression::Boolean(_) => {}
|
||||||
|
_ => todo!("Check semantics for expression other than function call and primitive"),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{error_handling::MistiError, lexic::token::Token, semantic::{impls::SemanticCheck, symbol_table::SymbolTable}, syntax::ast::{functions::{ArgumentsList, FunctionCall}, Expression}};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_error() {
|
||||||
|
// source code: `print()`
|
||||||
|
let expr_token = Token::new_identifier("print".into(), 0);
|
||||||
|
let expr_function = Expression::Identifier(&expr_token);
|
||||||
|
let arguments = ArgumentsList {
|
||||||
|
arguments: vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
let expr = Expression::FunctionCall(FunctionCall {
|
||||||
|
function: Box::new(expr_function),
|
||||||
|
arguments: Box::new(arguments),
|
||||||
|
});
|
||||||
|
|
||||||
|
let scope = SymbolTable::new();
|
||||||
|
|
||||||
|
let output = expr.check_semantics(&scope);
|
||||||
|
match output {
|
||||||
|
Ok(_) => panic!("Expected an error"),
|
||||||
|
Err(MistiError::Semantic(err)) => {
|
||||||
|
assert_eq!(err.reason, "Cannot find `print` in this scope.");
|
||||||
|
assert_eq!(err.error_start, 0);
|
||||||
|
assert_eq!(err.error_end, 5);
|
||||||
|
},
|
||||||
|
Err(e) => panic!("Expected a Semantic error, got {:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
pub mod binding;
|
pub mod binding;
|
||||||
pub mod function_declaration;
|
pub mod function_declaration;
|
||||||
pub mod top_level_declaration;
|
pub mod top_level_declaration;
|
||||||
|
pub mod expression;
|
||||||
|
|
||||||
|
@ -29,72 +29,3 @@ impl SemanticCheck for Statement<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move to its own file when it grows
|
|
||||||
impl SemanticCheck for Expression<'_> {
|
|
||||||
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> {
|
|
||||||
match self {
|
|
||||||
Expression::FunctionCall(f) => {
|
|
||||||
let fun = &*f.function;
|
|
||||||
let arguments = &*f.arguments.arguments;
|
|
||||||
|
|
||||||
let function_datatype = fun.get_type(scope)?;
|
|
||||||
match function_datatype {
|
|
||||||
Type::Function(parameters, _return_type) => {
|
|
||||||
// Check parameters length
|
|
||||||
if parameters.len() != arguments.len() {
|
|
||||||
return Err(MistiError::Semantic(SemanticError {
|
|
||||||
// TODO: fix
|
|
||||||
error_start: 0,
|
|
||||||
error_end: 1,
|
|
||||||
reason: format!(
|
|
||||||
"Expected {} arguments, found {}",
|
|
||||||
parameters.len(),
|
|
||||||
arguments.len(),
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that each argument matches the required datatype
|
|
||||||
for i in 0..parameters.len() {
|
|
||||||
let parameter = ¶meters[i];
|
|
||||||
let argument = &arguments[i];
|
|
||||||
|
|
||||||
let argument_datatype = argument.get_type(scope)?;
|
|
||||||
if !argument_datatype.is_value(parameter) {
|
|
||||||
// The argument and the parameter have diferent types
|
|
||||||
return Err(MistiError::Semantic(SemanticError {
|
|
||||||
// TODO: fix
|
|
||||||
error_start: 0,
|
|
||||||
error_end: 1,
|
|
||||||
reason: format!(
|
|
||||||
"Expected datatype {}, got {:?}",
|
|
||||||
parameter, argument
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(MistiError::Semantic(SemanticError {
|
|
||||||
// TODO: fix
|
|
||||||
error_start: 0,
|
|
||||||
error_end: 1,
|
|
||||||
reason: format!(
|
|
||||||
"Expected a function type, got {:?}",
|
|
||||||
function_datatype
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::Int(_) => {}
|
|
||||||
Expression::Float(_) => {}
|
|
||||||
Expression::String(_) => {}
|
|
||||||
Expression::Boolean(_) => {}
|
|
||||||
_ => todo!("Check semantics for expression other than function call and primitive"),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,13 +16,13 @@ impl Typed for Expression<'_> {
|
|||||||
Expression::Boolean(_) => Ok(Type::Value("Bool".into())),
|
Expression::Boolean(_) => Ok(Type::Value("Bool".into())),
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
// Attempt to get the datatype of the identifier in the current scope
|
// Attempt to get the datatype of the identifier in the current scope
|
||||||
let datatype = match scope.get_type(identifier) {
|
let datatype = match scope.get_type(&identifier.value) {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => {
|
None => {
|
||||||
return Err(MistiError::Semantic(SemanticError {
|
return Err(MistiError::Semantic(SemanticError {
|
||||||
error_start: 0,
|
error_start: identifier.position,
|
||||||
error_end: 1,
|
error_end: identifier.get_end_position(),
|
||||||
reason: format!("The identifier {} does not exist.", identifier),
|
reason: format!("Cannot find `{}` in this scope.", identifier.value),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -37,7 +37,7 @@ impl Typed for Expression<'_> {
|
|||||||
|
|
||||||
match &*f.function {
|
match &*f.function {
|
||||||
Expression::Identifier(id) => {
|
Expression::Identifier(id) => {
|
||||||
match scope.get_type(id) {
|
match scope.get_type(&id.value) {
|
||||||
Some(t) => Ok(t),
|
Some(t) => Ok(t),
|
||||||
None => Err(MistiError::Semantic(SemanticError {
|
None => Err(MistiError::Semantic(SemanticError {
|
||||||
// TODO: Actually find the start and end position
|
// TODO: Actually find the start and end position
|
||||||
@ -45,7 +45,7 @@ impl Typed for Expression<'_> {
|
|||||||
// just the string value
|
// just the string value
|
||||||
error_start: 0,
|
error_start: 0,
|
||||||
error_end: 1,
|
error_end: 1,
|
||||||
reason: format!("Type not found for symbol {}", id),
|
reason: format!("Type not found for symbol {}", id.value),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,3 +114,12 @@ impl Typed for Expression<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn should_error() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -64,7 +64,7 @@ pub enum Expression<'a> {
|
|||||||
Float(&'a String),
|
Float(&'a String),
|
||||||
String(&'a String),
|
String(&'a String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Identifier(&'a String),
|
Identifier(&'a Token),
|
||||||
FunctionCall(FunctionCall<'a>),
|
FunctionCall(FunctionCall<'a>),
|
||||||
UnaryOperator(&'a String, Box<Expression<'a>>),
|
UnaryOperator(&'a String, Box<Expression<'a>>),
|
||||||
BinaryOperator(Box<Expression<'a>>, Box<Expression<'a>>, &'a String),
|
BinaryOperator(Box<Expression<'a>>, Box<Expression<'a>>, &'a String),
|
||||||
|
@ -62,8 +62,8 @@ mod tests {
|
|||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
Expression::BinaryOperator(exp1, exp2, op) => {
|
||||||
match (*exp1, *exp2) {
|
match (*exp1, *exp2) {
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
||||||
assert_eq!("a", id1);
|
assert_eq!("a", id1.value);
|
||||||
assert_eq!("b", id2);
|
assert_eq!("b", id2.value);
|
||||||
}
|
}
|
||||||
_ => panic!("Expected 2 identifiers"),
|
_ => panic!("Expected 2 identifiers"),
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@ mod tests {
|
|||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
Expression::BinaryOperator(exp1, exp2, op) => {
|
||||||
match (*exp1, *exp2) {
|
match (*exp1, *exp2) {
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
||||||
assert_eq!("a", id1);
|
assert_eq!("a", id1.value);
|
||||||
assert_eq!("b", id2);
|
assert_eq!("b", id2.value);
|
||||||
}
|
}
|
||||||
_ => panic!("Expected 2 identifiers"),
|
_ => panic!("Expected 2 identifiers"),
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,8 @@ mod tests {
|
|||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
Expression::BinaryOperator(exp1, exp2, op) => {
|
||||||
match (*exp1, *exp2) {
|
match (*exp1, *exp2) {
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
||||||
assert_eq!("a", id1);
|
assert_eq!("a", id1.value);
|
||||||
assert_eq!("b", id2);
|
assert_eq!("b", id2.value);
|
||||||
}
|
}
|
||||||
_ => panic!("Expected 2 identifiers"),
|
_ => panic!("Expected 2 identifiers"),
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
|
|||||||
TokenType::Identifier if token.value == "true" || token.value == "false" => {
|
TokenType::Identifier if token.value == "true" || token.value == "false" => {
|
||||||
Ok((Expression::Boolean(token.value == "true"), token_pos + 1))
|
Ok((Expression::Boolean(token.value == "true"), token_pos + 1))
|
||||||
}
|
}
|
||||||
TokenType::Identifier => Ok((Expression::Identifier(&token.value), token_pos + 1)),
|
TokenType::Identifier => Ok((Expression::Identifier(&token), token_pos + 1)),
|
||||||
TokenType::LeftParen => parse_parenthesized_expression(tokens, token_pos),
|
TokenType::LeftParen => parse_parenthesized_expression(tokens, token_pos),
|
||||||
_ => Err(ParsingError::Unmatched),
|
_ => Err(ParsingError::Unmatched),
|
||||||
},
|
},
|
||||||
@ -90,7 +90,7 @@ mod tests {
|
|||||||
|
|
||||||
match expression {
|
match expression {
|
||||||
Ok((Expression::Identifier(value), _)) => {
|
Ok((Expression::Identifier(value), _)) => {
|
||||||
assert_eq!("someIdentifier", format!("{}", value))
|
assert_eq!("someIdentifier", format!("{}", value.value))
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ mod tests {
|
|||||||
|
|
||||||
match expression {
|
match expression {
|
||||||
Ok((Expression::Identifier(value), _)) => {
|
Ok((Expression::Identifier(value), _)) => {
|
||||||
assert_eq!("identifier", format!("{}", value))
|
assert_eq!("identifier", format!("{}", value.value))
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,8 @@ mod tests {
|
|||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
Expression::BinaryOperator(exp1, exp2, op) => {
|
||||||
match (*exp1, *exp2) {
|
match (*exp1, *exp2) {
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
||||||
assert_eq!("a", id1);
|
assert_eq!("a", id1.value);
|
||||||
assert_eq!("b", id2);
|
assert_eq!("b", id2.value);
|
||||||
}
|
}
|
||||||
_ => panic!("Expected 2 identifiers"),
|
_ => panic!("Expected 2 identifiers"),
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ mod tests {
|
|||||||
|
|
||||||
match expression {
|
match expression {
|
||||||
Ok((Expression::Identifier(value), _)) => {
|
Ok((Expression::Identifier(value), _)) => {
|
||||||
assert_eq!("identifier", format!("{}", value))
|
assert_eq!("identifier", format!("{}", value.value))
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user