[syntax] Alternative function to expect tokens

master
Araozu 2023-09-19 20:06:38 -05:00
parent 5dd104bcc9
commit 807c46314b
4 changed files with 136 additions and 100 deletions

View File

@ -16,31 +16,31 @@ impl PrintableError for SyntaxError {
} }
} }
// Extracts a line of code /// Extracts a lin e of code
// ///
// - `chars`: Input where to extract the line from /// - `chars`: Input where to extract the line from
// - `start_position`: Position where the erroneous code starts /// - `start_position`: Position where the erroneous code starts
// - `end_position`: Position where the erroneous code ends /// - `end_position`: Position where the erroneous code ends
// ///
// Returns a tuple of: /// Returns a tuple of:
// ///
// - `String`: The faulty line /// - `String`: The faulty line
// - `usize`: The amount of chars *before* the faulty code /// - `usize`: The amount of chars *before* the faulty code
// - `usize`: The lenght of the faulty code /// - `usize`: The lenght of the faulty code
// ///
// ## Example /// ## Example
// ///
// ``` /// ```
// let input = String::from("\n\nval number == 50\n\n").chars().into_iter().collect(); /// let input = String::from("\n\nval number == 50\n\n").chars().into_iter().collect();
// let start_position = 13; /// let start_position = 13;
// let end_position = 15; /// let end_position = 15;
// ///
// let (line, before, length) = get_line(&input, start_position, end_position); /// let (line, before, length) = get_line(&input, start_position, end_position);
// ///
// assert_eq!("val number == 50", line); /// assert_eq!("val number == 50", line);
// assert_eq!(11, before); /// assert_eq!(11, before);
// assert_eq!(2, length); /// assert_eq!(2, length);
// ``` /// ```
fn get_line( fn get_line(
chars: &Vec<char>, chars: &Vec<char>,
start_position: usize, start_position: usize,

View File

@ -1,4 +1,5 @@
use super::ast::{Binding, ValBinding, VarBinding}; use super::ast::{Binding, ValBinding, VarBinding};
use super::utils::{try_operator, try_token_type};
use super::{expression, SyntaxResult}; use super::{expression, SyntaxResult};
use crate::error_handling::SyntaxError; use crate::error_handling::SyntaxError;
use crate::lexic::token::{Token, TokenType}; use crate::lexic::token::{Token, TokenType};
@ -125,29 +126,6 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult>
)) ))
} }
/// Expects the token at `pos` to be of type `token_type`
fn try_token_type(tokens: &Vec<Token>, 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<Token>, 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -6,7 +6,7 @@ use crate::{
use super::{ use super::{
ast::{FunctionDeclaration, TopLevelDeclaration}, ast::{FunctionDeclaration, TopLevelDeclaration},
utils::try_token_type, utils::{expect_token_w, try_token_type},
SyntaxResult, SyntaxResult,
}; };
@ -21,60 +21,44 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult>
}; };
current_pos += 1; current_pos += 1;
// Parse identifier /*
let identifier = match try_token_type(tokens, current_pos, TokenType::Identifier) {
Result3::Ok(t) => t, try_token_type(
Result3::Err(t) => { tokens,
// The parser found a token, but it's not an identifier current_pos,
return Some(SyntaxResult::Err(SyntaxError { TokenType::Identifier,
reason: format!( ignore_whitespace,
"There should be an identifier after a `fun` token, but found `{}`", "There should be an identifier after a `fun` token, but found `{}`",
t.value ) -> token, usize?
),
error_start: t.position, */
error_end: t.get_end_position(),
})); // Parse identifier
} let identifier = match expect_token_w(
Result3::None => { tokens,
// The parser didn't find any token current_pos,
return Some(SyntaxResult::Err(SyntaxError { TokenType::Identifier,
reason: format!( "Expected an identifier after the `fun` keyword.".into(),
"There should be an identifier after a `fun` token, but found nothing" fun_keyword,
), ) {
error_start: fun_keyword.position, Ok(t) => t,
error_end: fun_keyword.get_end_position(), Err(err) => return err,
}));
}
}; };
current_pos += 1; current_pos += 1;
// Parse an opening paren let opening_paren = match expect_token_w(
let opening_paren = match try_token_type(tokens, current_pos, TokenType::LeftParen) { tokens,
Result3::Ok(t) => t, current_pos,
Result3::Err(t) => { TokenType::LeftParen,
// The parser found a token, but it's not an opening paren "Expected an opening paren afted the function identifier.".into(),
return Some(SyntaxResult::Err(SyntaxError { identifier,
reason: format!( ) {
"There should be an opening paren after the identifier, but found `{}`", Ok(t) => t,
t.value Err(err) => return err,
),
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(),
}));
}
}; };
current_pos += 1; current_pos += 1;
// Parse a closing paren // Parse a closing paren
let closing_paren = match try_token_type(tokens, current_pos, TokenType::RightParen) { let closing_paren = match try_token_type(tokens, current_pos, TokenType::RightParen) {
Result3::Ok(t) => t, Result3::Ok(t) => t,
@ -188,7 +172,7 @@ mod tests {
Some(SyntaxResult::Err(err)) => { Some(SyntaxResult::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, 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_start, 4);
assert_eq!(err.error_end, 5); assert_eq!(err.error_end, 5);
@ -202,7 +186,7 @@ mod tests {
Some(SyntaxResult::Err(err)) => { Some(SyntaxResult::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, 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_start, 0);
assert_eq!(err.error_end, 3); assert_eq!(err.error_end, 3);
@ -220,7 +204,7 @@ mod tests {
Some(SyntaxResult::Err(err)) => { Some(SyntaxResult::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, 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_start, 7);
assert_eq!(err.error_end, 8); assert_eq!(err.error_end, 8);
@ -234,7 +218,7 @@ mod tests {
Some(SyntaxResult::Err(err)) => { Some(SyntaxResult::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, 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_start, 4);
assert_eq!(err.error_end, 6); 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] #[test]
fn should_not_parse_fun_without_opening_brace() { fn should_not_parse_fun_without_opening_brace() {
let tokens = get_tokens(&String::from("fun id() =")).unwrap(); let tokens = get_tokens(&String::from("fun id() =")).unwrap();

View File

@ -1,11 +1,24 @@
use crate::{ use crate::{
error_handling::SyntaxError,
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
utils::Result3, utils::Result3,
}; };
use super::SyntaxResult;
/// Expects the token at `pos` to be of type `token_type`
pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> { pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> {
match tokens.get(pos) { match tokens.get(pos) {
Some(t) if t.token_type == token_type => Result3::Ok(t), 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<Token>, 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 => { Some(t) if t.token_type == TokenType::NewLine || t.token_type == TokenType::EOF => {
Result3::None Result3::None
} }
@ -13,3 +26,30 @@ pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) ->
None => Result3::None, None => Result3::None,
} }
} }
pub fn expect_token_w<'a>(
tokens: &'a Vec<Token>,
pos: usize,
token_type: TokenType,
error_message: String,
prev_token: &Token,
) -> Result<&'a Token, Option<SyntaxResult>> {
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(),
}))),
}
}