From 807c46314b6794b307b7004d32f326e2376b8d68 Mon Sep 17 00:00:00 2001 From: Araozu Date: Tue, 19 Sep 2023 20:06:38 -0500 Subject: [PATCH] [syntax] Alternative function to expect tokens --- src/error_handling/syntax_error.rs | 50 ++++++------ src/syntax/binding.rs | 24 +----- src/syntax/function_declaration.rs | 122 +++++++++++++++++------------ src/syntax/utils.rs | 40 ++++++++++ 4 files changed, 136 insertions(+), 100 deletions(-) diff --git a/src/error_handling/syntax_error.rs b/src/error_handling/syntax_error.rs index 7a57b60..2ea6040 100644 --- a/src/error_handling/syntax_error.rs +++ b/src/error_handling/syntax_error.rs @@ -16,31 +16,31 @@ impl PrintableError for SyntaxError { } } -// Extracts a line of code -// -// - `chars`: Input where to extract the line from -// - `start_position`: Position where the erroneous code starts -// - `end_position`: Position where the erroneous code ends -// -// Returns a tuple of: -// -// - `String`: The faulty line -// - `usize`: The amount of chars *before* the faulty code -// - `usize`: The lenght of the faulty code -// -// ## Example -// -// ``` -// let input = String::from("\n\nval number == 50\n\n").chars().into_iter().collect(); -// let start_position = 13; -// let end_position = 15; -// -// let (line, before, length) = get_line(&input, start_position, end_position); -// -// assert_eq!("val number == 50", line); -// assert_eq!(11, before); -// assert_eq!(2, length); -// ``` +/// Extracts a lin e of code +/// +/// - `chars`: Input where to extract the line from +/// - `start_position`: Position where the erroneous code starts +/// - `end_position`: Position where the erroneous code ends +/// +/// Returns a tuple of: +/// +/// - `String`: The faulty line +/// - `usize`: The amount of chars *before* the faulty code +/// - `usize`: The lenght of the faulty code +/// +/// ## Example +/// +/// ``` +/// let input = String::from("\n\nval number == 50\n\n").chars().into_iter().collect(); +/// let start_position = 13; +/// let end_position = 15; +/// +/// let (line, before, length) = get_line(&input, start_position, end_position); +/// +/// assert_eq!("val number == 50", line); +/// assert_eq!(11, before); +/// assert_eq!(2, length); +/// ``` fn get_line( chars: &Vec, start_position: usize, diff --git a/src/syntax/binding.rs b/src/syntax/binding.rs index 0becd91..e8e0086 100644 --- a/src/syntax/binding.rs +++ b/src/syntax/binding.rs @@ -1,4 +1,5 @@ use super::ast::{Binding, ValBinding, VarBinding}; +use super::utils::{try_operator, try_token_type}; use super::{expression, SyntaxResult}; use crate::error_handling::SyntaxError; use crate::lexic::token::{Token, TokenType}; @@ -125,29 +126,6 @@ pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> Option )) } -/// Expects the token at `pos` to be of type `token_type` -fn try_token_type(tokens: &Vec, pos: usize, token_type: TokenType) -> Result3<&Token> { - match tokens.get(pos) { - Some(t) if t.token_type == token_type => Result3::Ok(t), - Some(t) if t.token_type == TokenType::EOF => { - Result3::None - } - Some(t) => Result3::Err(t), - None => Result3::None, - } -} - -fn try_operator(tokens: &Vec, pos: usize, operator: String) -> Result3<&Token> { - match tokens.get(pos) { - Some(t) if t.token_type == TokenType::Operator && t.value == operator => Result3::Ok(t), - Some(t) if t.token_type == TokenType::NewLine || t.token_type == TokenType::EOF => { - Result3::None - } - Some(t) => Result3::Err(t), - None => Result3::None, - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/syntax/function_declaration.rs b/src/syntax/function_declaration.rs index d11f589..e74234b 100644 --- a/src/syntax/function_declaration.rs +++ b/src/syntax/function_declaration.rs @@ -6,7 +6,7 @@ use crate::{ use super::{ ast::{FunctionDeclaration, TopLevelDeclaration}, - utils::try_token_type, + utils::{expect_token_w, try_token_type}, SyntaxResult, }; @@ -21,60 +21,44 @@ pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> Option }; current_pos += 1; + /* + + try_token_type( + tokens, + current_pos, + TokenType::Identifier, + ignore_whitespace, + "There should be an identifier after a `fun` token, but found `{}`", + ) -> token, usize? + + */ + // Parse identifier - let identifier = match try_token_type(tokens, current_pos, TokenType::Identifier) { - Result3::Ok(t) => t, - Result3::Err(t) => { - // The parser found a token, but it's not an identifier - return Some(SyntaxResult::Err(SyntaxError { - reason: format!( - "There should be an identifier after a `fun` token, but found `{}`", - t.value - ), - error_start: t.position, - error_end: t.get_end_position(), - })); - } - Result3::None => { - // The parser didn't find any token - return Some(SyntaxResult::Err(SyntaxError { - reason: format!( - "There should be an identifier after a `fun` token, but found nothing" - ), - error_start: fun_keyword.position, - error_end: fun_keyword.get_end_position(), - })); - } + let identifier = match expect_token_w( + tokens, + current_pos, + TokenType::Identifier, + "Expected an identifier after the `fun` keyword.".into(), + fun_keyword, + ) { + Ok(t) => t, + Err(err) => return err, }; current_pos += 1; - // Parse an opening paren - let opening_paren = match try_token_type(tokens, current_pos, TokenType::LeftParen) { - Result3::Ok(t) => t, - Result3::Err(t) => { - // The parser found a token, but it's not an opening paren - return Some(SyntaxResult::Err(SyntaxError { - reason: format!( - "There should be an opening paren after the identifier, but found `{}`", - t.value - ), - error_start: t.position, - error_end: t.get_end_position(), - })); - } - Result3::None => { - // The parser didn't find any token - return Some(SyntaxResult::Err(SyntaxError { - reason: format!( - "There should be an opening paren after the identifier, but found nothing" - ), - error_start: identifier.position, - error_end: identifier.get_end_position(), - })); - } + let opening_paren = match expect_token_w( + tokens, + current_pos, + TokenType::LeftParen, + "Expected an opening paren afted the function identifier.".into(), + identifier, + ) { + Ok(t) => t, + Err(err) => return err, }; current_pos += 1; + // Parse a closing paren let closing_paren = match try_token_type(tokens, current_pos, TokenType::RightParen) { Result3::Ok(t) => t, @@ -188,7 +172,7 @@ mod tests { Some(SyntaxResult::Err(err)) => { assert_eq!( err.reason, - "There should be an identifier after a `fun` token, but found `=`" + "Expected an identifier after the `fun` keyword." ); assert_eq!(err.error_start, 4); assert_eq!(err.error_end, 5); @@ -202,7 +186,7 @@ mod tests { Some(SyntaxResult::Err(err)) => { assert_eq!( err.reason, - "There should be an identifier after a `fun` token, but found nothing" + "Expected an identifier after the `fun` keyword." ); assert_eq!(err.error_start, 0); assert_eq!(err.error_end, 3); @@ -220,7 +204,7 @@ mod tests { Some(SyntaxResult::Err(err)) => { assert_eq!( err.reason, - "There should be an opening paren after the identifier, but found `=`" + "Expected an opening paren afted the function identifier." ); assert_eq!(err.error_start, 7); assert_eq!(err.error_end, 8); @@ -234,7 +218,7 @@ mod tests { Some(SyntaxResult::Err(err)) => { assert_eq!( err.reason, - "There should be an opening paren after the identifier, but found nothing" + "Expected an opening paren afted the function identifier." ); assert_eq!(err.error_start, 4); assert_eq!(err.error_end, 6); @@ -275,6 +259,40 @@ mod tests { } } + #[test] + fn should_not_parse_fun_when_missing_id() { + let tokens = get_tokens(&String::from("fun")).unwrap(); + let fun_decl = try_parse(&tokens, 0); + + match fun_decl { + Some(SyntaxResult::Err(err)) => { + assert_eq!( + err.reason, + "Expected an identifier after the `fun` keyword." + ); + assert_eq!(err.error_start, 0); + assert_eq!(err.error_end, 3); + } + _ => panic!("Expected an error: {:?}", fun_decl), + } + + let tokens = get_tokens(&String::from("fun\n")).unwrap(); + println!("{:?}", tokens); + let fun_decl = try_parse(&tokens, 0); + + match fun_decl { + Some(SyntaxResult::Err(err)) => { + assert_eq!( + err.reason, + "Expected an identifier after the `fun` keyword." + ); + assert_eq!(err.error_start, 0); + assert_eq!(err.error_end, 3); + } + _ => panic!("Expected an error: {:?}", fun_decl), + } + } + #[test] fn should_not_parse_fun_without_opening_brace() { let tokens = get_tokens(&String::from("fun id() =")).unwrap(); diff --git a/src/syntax/utils.rs b/src/syntax/utils.rs index ecbe58d..847f92c 100644 --- a/src/syntax/utils.rs +++ b/src/syntax/utils.rs @@ -1,11 +1,24 @@ use crate::{ + error_handling::SyntaxError, lexic::token::{Token, TokenType}, utils::Result3, }; +use super::SyntaxResult; + +/// Expects the token at `pos` to be of type `token_type` pub fn try_token_type(tokens: &Vec, pos: usize, token_type: TokenType) -> Result3<&Token> { match tokens.get(pos) { Some(t) if t.token_type == token_type => Result3::Ok(t), + Some(t) if t.token_type == TokenType::EOF || t.token_type == TokenType::NewLine => Result3::None, + Some(t) => Result3::Err(t), + None => Result3::None, + } +} + +pub fn try_operator(tokens: &Vec, pos: usize, operator: String) -> Result3<&Token> { + match tokens.get(pos) { + Some(t) if t.token_type == TokenType::Operator && t.value == operator => Result3::Ok(t), Some(t) if t.token_type == TokenType::NewLine || t.token_type == TokenType::EOF => { Result3::None } @@ -13,3 +26,30 @@ pub fn try_token_type(tokens: &Vec, pos: usize, token_type: TokenType) -> None => Result3::None, } } + +pub fn expect_token_w<'a>( + tokens: &'a Vec, + pos: usize, + token_type: TokenType, + error_message: String, + prev_token: &Token, +) -> Result<&'a Token, Option> { + match tokens.get(pos) { + Some(t) if t.token_type == token_type => Ok(t), + Some(t) if t.token_type == TokenType::EOF || t.token_type == TokenType::NewLine => Err(Some(SyntaxResult::Err(SyntaxError { + reason: error_message, + error_start: prev_token.position, + error_end: prev_token.get_end_position(), + }))), + Some(t) => Err(Some(SyntaxResult::Err(SyntaxError { + reason: error_message, + error_start: t.position, + error_end: t.get_end_position(), + }))), + None => Err(Some(SyntaxResult::Err(SyntaxError { + reason: error_message, + error_start: prev_token.position, + error_end: prev_token.get_end_position(), + }))), + } +}