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,
types::{Type, Typed},
},
syntax::ast::Expression,
syntax::ast::{Expression, Positionable},
};
impl SemanticCheck for Expression<'_> {
@ -20,12 +20,13 @@ impl SemanticCheck for Expression<'_> {
Type::Function(parameters, _return_type) => {
// Check parameters length
if parameters.len() != arguments.len() {
let (error_start, error_end) = f.arguments.get_position();
return Err(MistiError::Semantic(SemanticError {
// TODO: fix
error_start: 0,
error_end: 1,
error_start,
error_end,
reason: format!(
"Expected {} arguments, found {}",
"Expected {} arguments, got {}",
parameters.len(),
arguments.len(),
),
@ -54,10 +55,10 @@ impl SemanticCheck for Expression<'_> {
}
}
_ => {
let (error_start, error_end) = fun.get_position();
return Err(MistiError::Semantic(SemanticError {
// TODO: fix
error_start: 0,
error_end: 1,
error_start,
error_end,
reason: format!(
"Expected a function type, got {:?}",
function_datatype
@ -94,7 +95,11 @@ mod tests {
// 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 arguments = ArgumentsList {
arguments: vec![],
paren_open_pos: 5,
paren_close_pos: 7,
};
let expr = Expression::FunctionCall(FunctionCall {
function: Box::new(expr_function),
@ -128,6 +133,8 @@ mod tests {
let arg_1 = Expression::Int(&arg_t);
let arguments = ArgumentsList {
arguments: vec![arg_1],
paren_open_pos: 5,
paren_close_pos: 10,
};
let expr = Expression::FunctionCall(FunctionCall {
@ -145,4 +152,72 @@ mod tests {
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 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 {
function: Box::new(fn_expr),
@ -208,7 +212,11 @@ mod tests {
let id_token = Token::new_identifier("print".into(), 0);
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 {
function: Box::new(fn_expr),
@ -233,7 +241,11 @@ mod tests {
let id_token = Token::new_identifier("print".into(), 0);
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 {
function: Box::new(fn_expr),

View File

@ -1,4 +1,4 @@
use super::Expression;
use super::{Expression, Positionable};
#[derive(Debug)]
pub struct FunctionCall<'a> {
@ -9,4 +9,13 @@ pub struct FunctionCall<'a> {
#[derive(Debug)]
pub struct ArgumentsList<'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 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
#[derive(Debug)]
pub struct ModuleAST<'a> {
@ -70,19 +76,19 @@ pub enum Expression<'a> {
BinaryOperator(Box<Expression<'a>>, Box<Expression<'a>>, &'a String),
}
impl Expression<'_> {
impl Positionable for Expression<'_> {
/// Returns the absolute start and end position
/// of this expression
pub fn get_position(&self) -> (usize, usize) {
fn get_position(&self) -> (usize, usize) {
match self {
Expression::Identifier(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::String(id) => (id.position, id.get_end_position()),
Expression::Boolean(id) => (id.position, id.get_end_position()),
Expression::FunctionCall(_) => todo!(),
Expression::UnaryOperator(_, _) => todo!(),
Expression::BinaryOperator(_, _, _) => todo!(),
Expression::FunctionCall(_) => (0, 1),
Expression::UnaryOperator(_, _) => (0, 1),
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
let (_closing_paren, next_pos) =
let (closing_paren, next_pos) =
match parse_token_type(tokens, current_pos, TokenType::RightParen) {
Ok((t, next)) => (t, next),
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;
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)]