[syntax] Alternative function to expect tokens
This commit is contained in:
parent
5dd104bcc9
commit
807c46314b
@ -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,
|
||||||
|
@ -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::*;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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
|
// Parse identifier
|
||||||
let identifier = match try_token_type(tokens, current_pos, TokenType::Identifier) {
|
let identifier = match expect_token_w(
|
||||||
Result3::Ok(t) => t,
|
tokens,
|
||||||
Result3::Err(t) => {
|
current_pos,
|
||||||
// The parser found a token, but it's not an identifier
|
TokenType::Identifier,
|
||||||
return Some(SyntaxResult::Err(SyntaxError {
|
"Expected an identifier after the `fun` keyword.".into(),
|
||||||
reason: format!(
|
fun_keyword,
|
||||||
"There should be an identifier after a `fun` token, but found `{}`",
|
) {
|
||||||
t.value
|
Ok(t) => t,
|
||||||
),
|
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 identifier after a `fun` token, but found nothing"
|
|
||||||
),
|
|
||||||
error_start: fun_keyword.position,
|
|
||||||
error_end: fun_keyword.get_end_position(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
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();
|
||||||
|
@ -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(),
|
||||||
|
}))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user