feat: store identifier token in the ast

master
Araozu 2024-08-12 18:44:05 -05:00
parent d5f2176fa7
commit 833a8774d8
12 changed files with 137 additions and 89 deletions

View File

@ -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

View 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 = &parameters[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)
}
}
}

View File

@ -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;

View File

@ -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 = &parameters[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(())
}
}

View File

@ -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() {
}
}

View File

@ -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),

View File

@ -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"),
} }

View File

@ -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"),
} }

View File

@ -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"),
} }

View File

@ -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!(),
} }

View File

@ -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"),
} }

View File

@ -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!(),
} }