feat: store paren positions on argument list

master
Araozu 2024-08-12 20:08:09 -05:00
parent 96d3e11951
commit d999b8ecfd
5 changed files with 129 additions and 20 deletions

View File

@ -5,7 +5,7 @@ use crate::{
symbol_table::SymbolTable, symbol_table::SymbolTable,
types::{Type, Typed}, types::{Type, Typed},
}, },
syntax::ast::Expression, syntax::ast::{Expression, Positionable},
}; };
impl SemanticCheck for Expression<'_> { impl SemanticCheck for Expression<'_> {
@ -20,12 +20,13 @@ impl SemanticCheck for Expression<'_> {
Type::Function(parameters, _return_type) => { Type::Function(parameters, _return_type) => {
// Check parameters length // Check parameters length
if parameters.len() != arguments.len() { if parameters.len() != arguments.len() {
let (error_start, error_end) = f.arguments.get_position();
return Err(MistiError::Semantic(SemanticError { return Err(MistiError::Semantic(SemanticError {
// TODO: fix error_start,
error_start: 0, error_end,
error_end: 1,
reason: format!( reason: format!(
"Expected {} arguments, found {}", "Expected {} arguments, got {}",
parameters.len(), parameters.len(),
arguments.len(), arguments.len(),
), ),
@ -54,10 +55,10 @@ impl SemanticCheck for Expression<'_> {
} }
} }
_ => { _ => {
let (error_start, error_end) = fun.get_position();
return Err(MistiError::Semantic(SemanticError { return Err(MistiError::Semantic(SemanticError {
// TODO: fix error_start,
error_start: 0, error_end,
error_end: 1,
reason: format!( reason: format!(
"Expected a function type, got {:?}", "Expected a function type, got {:?}",
function_datatype function_datatype
@ -94,7 +95,11 @@ mod tests {
// source code: `print()` // source code: `print()`
let expr_token = Token::new_identifier("print".into(), 0); let expr_token = Token::new_identifier("print".into(), 0);
let expr_function = Expression::Identifier(&expr_token); let expr_function = Expression::Identifier(&expr_token);
let arguments = ArgumentsList { arguments: vec![] }; let arguments = ArgumentsList {
arguments: vec![],
paren_open_pos: 5,
paren_close_pos: 7,
};
let expr = Expression::FunctionCall(FunctionCall { let expr = Expression::FunctionCall(FunctionCall {
function: Box::new(expr_function), function: Box::new(expr_function),
@ -128,6 +133,8 @@ mod tests {
let arg_1 = Expression::Int(&arg_t); let arg_1 = Expression::Int(&arg_t);
let arguments = ArgumentsList { let arguments = ArgumentsList {
arguments: vec![arg_1], arguments: vec![arg_1],
paren_open_pos: 5,
paren_close_pos: 10,
}; };
let expr = Expression::FunctionCall(FunctionCall { let expr = Expression::FunctionCall(FunctionCall {
@ -145,4 +152,72 @@ mod tests {
Err(e) => panic!("Expected semantic error, got {:?}", e), Err(e) => panic!("Expected semantic error, got {:?}", e),
} }
} }
#[test]
fn should_error_on_invalid_function_argument_count() {
// source code: `print()`
let mut scope = SymbolTable::new();
populate(&mut scope);
let expr_token = Token::new_identifier("print".into(), 0);
let expr_function = Expression::Identifier(&expr_token);
let arguments = ArgumentsList {
arguments: vec![],
paren_open_pos: 5,
paren_close_pos: 7,
};
let expr = Expression::FunctionCall(FunctionCall {
function: Box::new(expr_function),
arguments: Box::new(arguments),
});
match expr.check_semantics(&scope) {
Ok(_) => panic!("Expected semantic error, got ok"),
Err(MistiError::Semantic(e)) => {
assert_eq!(e.reason, "Expected 1 arguments, got 0");
assert_eq!(e.error_start, 5);
assert_eq!(e.error_end, 7);
}
Err(e) => panic!("Expected semantic error, got {:?}", e),
}
}
#[test]
fn should_error_on_invalid_function_argument_2() {
// source code: `print(322, 644)`
let mut scope = SymbolTable::new();
populate(&mut scope);
let expr_token = Token::new_identifier("print".into(), 0);
let expr_function = Expression::Identifier(&expr_token);
let arg_t = Token::new_int(String::from("322"), 6);
let arg_1 = Expression::Int(&arg_t);
let arg_t_2 = Token::new_int(String::from("644"), 11);
let arg_2 = Expression::Int(&arg_t_2);
let arguments = ArgumentsList {
arguments: vec![arg_1, arg_2],
paren_open_pos: 5,
paren_close_pos: 15,
};
let expr = Expression::FunctionCall(FunctionCall {
function: Box::new(expr_function),
arguments: Box::new(arguments),
});
match expr.check_semantics(&scope) {
Ok(_) => panic!("Expected semantic error, got ok"),
Err(MistiError::Semantic(e)) => {
assert_eq!(e.reason, "Expected 1 arguments, got 2");
assert_eq!(e.error_start, 5);
assert_eq!(e.error_end, 15);
}
Err(e) => panic!("Expected semantic error, got {:?}", e),
}
}
} }

View File

@ -185,7 +185,11 @@ mod tests {
let id_token = Token::new_identifier("print".into(), 0); let id_token = Token::new_identifier("print".into(), 0);
let fn_expr = Expression::Identifier(&id_token); let fn_expr = Expression::Identifier(&id_token);
let args = ArgumentsList { arguments: vec![] }; let args = ArgumentsList {
arguments: vec![],
paren_open_pos: 5,
paren_close_pos: 7,
};
let fn_call = Expression::FunctionCall(FunctionCall { let fn_call = Expression::FunctionCall(FunctionCall {
function: Box::new(fn_expr), function: Box::new(fn_expr),
@ -208,7 +212,11 @@ mod tests {
let id_token = Token::new_identifier("print".into(), 0); let id_token = Token::new_identifier("print".into(), 0);
let fn_expr = Expression::Identifier(&id_token); let fn_expr = Expression::Identifier(&id_token);
let args = ArgumentsList { arguments: vec![] }; let args = ArgumentsList {
arguments: vec![],
paren_open_pos: 5,
paren_close_pos: 7,
};
let fn_call = Expression::FunctionCall(FunctionCall { let fn_call = Expression::FunctionCall(FunctionCall {
function: Box::new(fn_expr), function: Box::new(fn_expr),
@ -233,7 +241,11 @@ mod tests {
let id_token = Token::new_identifier("print".into(), 0); let id_token = Token::new_identifier("print".into(), 0);
let fn_expr = Expression::Identifier(&id_token); let fn_expr = Expression::Identifier(&id_token);
let args = ArgumentsList { arguments: vec![] }; let args = ArgumentsList {
arguments: vec![],
paren_open_pos: 5,
paren_close_pos: 7,
};
let fn_call = Expression::FunctionCall(FunctionCall { let fn_call = Expression::FunctionCall(FunctionCall {
function: Box::new(fn_expr), function: Box::new(fn_expr),

View File

@ -1,4 +1,4 @@
use super::Expression; use super::{Expression, Positionable};
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionCall<'a> { pub struct FunctionCall<'a> {
@ -9,4 +9,13 @@ pub struct FunctionCall<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct ArgumentsList<'a> { pub struct ArgumentsList<'a> {
pub arguments: Vec<Expression<'a>>, pub arguments: Vec<Expression<'a>>,
pub paren_open_pos: usize,
/// This is after the paren is closed
pub paren_close_pos: usize,
}
impl Positionable for ArgumentsList<'_> {
fn get_position(&self) -> (usize, usize) {
(self.paren_open_pos, self.paren_close_pos)
}
} }

View File

@ -6,6 +6,12 @@ use var_binding::VariableBinding;
pub mod functions; pub mod functions;
pub mod var_binding; pub mod var_binding;
/// Trait that allows nodes to inform
/// on where they start and end on the source code
pub trait Positionable {
fn get_position(&self) -> (usize, usize);
}
/// The AST for a whole THP file /// The AST for a whole THP file
#[derive(Debug)] #[derive(Debug)]
pub struct ModuleAST<'a> { pub struct ModuleAST<'a> {
@ -70,19 +76,19 @@ pub enum Expression<'a> {
BinaryOperator(Box<Expression<'a>>, Box<Expression<'a>>, &'a String), BinaryOperator(Box<Expression<'a>>, Box<Expression<'a>>, &'a String),
} }
impl Expression<'_> { impl Positionable for Expression<'_> {
/// Returns the absolute start and end position /// Returns the absolute start and end position
/// of this expression /// of this expression
pub fn get_position(&self) -> (usize, usize) { fn get_position(&self) -> (usize, usize) {
match self { match self {
Expression::Identifier(id) => (id.position, id.get_end_position()), Expression::Identifier(id) => (id.position, id.get_end_position()),
Expression::Int(id) => (id.position, id.get_end_position()), Expression::Int(id) => (id.position, id.get_end_position()),
Expression::Float(id) => (id.position, id.get_end_position()), Expression::Float(id) => (id.position, id.get_end_position()),
Expression::String(id) => (id.position, id.get_end_position()), Expression::String(id) => (id.position, id.get_end_position()),
Expression::Boolean(id) => (id.position, id.get_end_position()), Expression::Boolean(id) => (id.position, id.get_end_position()),
Expression::FunctionCall(_) => todo!(), Expression::FunctionCall(_) => (0, 1),
Expression::UnaryOperator(_, _) => todo!(), Expression::UnaryOperator(_, _) => (0, 1),
Expression::BinaryOperator(_, _, _) => todo!(), Expression::BinaryOperator(_, _, _) => (0, 1),
} }
} }
} }

View File

@ -53,7 +53,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Argume
} }
// Parse closing paren // Parse closing paren
let (_closing_paren, next_pos) = let (closing_paren, next_pos) =
match parse_token_type(tokens, current_pos, TokenType::RightParen) { match parse_token_type(tokens, current_pos, TokenType::RightParen) {
Ok((t, next)) => (t, next), Ok((t, next)) => (t, next),
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
@ -74,7 +74,14 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Argume
}; };
current_pos = next_pos; current_pos = next_pos;
Ok((ArgumentsList { arguments }, current_pos)) Ok((
ArgumentsList {
arguments,
paren_open_pos: opening_paren.position,
paren_close_pos: closing_paren.get_end_position(),
},
current_pos,
))
} }
#[cfg(test)] #[cfg(test)]