2023-03-14 21:10:43 +00:00
|
|
|
use super::ast_types::{Binding, ValBinding, VarBinding};
|
|
|
|
use super::{expression, SyntaxResult};
|
2023-03-15 21:33:00 +00:00
|
|
|
use crate::error_handling::SyntaxError;
|
2023-01-08 23:09:06 +00:00
|
|
|
use crate::token::{Token, TokenType};
|
|
|
|
|
2023-02-15 21:17:50 +00:00
|
|
|
// TODO: Should return a 3 state value:
|
2023-01-08 23:09:06 +00:00
|
|
|
// - Success: binding parsed successfully
|
|
|
|
// - NotFound: the first token (var | val) was not found, so the parser should try other options
|
|
|
|
// - Error: token (var | val) was found, but then other expected tokens were not found
|
2023-03-14 21:10:43 +00:00
|
|
|
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult> {
|
2023-02-15 21:17:50 +00:00
|
|
|
let mut pos = pos;
|
2023-03-14 21:10:43 +00:00
|
|
|
|
2023-02-15 21:17:50 +00:00
|
|
|
// Optional datatype annotation
|
|
|
|
let datatype_annotation = {
|
|
|
|
match tokens.get(pos) {
|
|
|
|
Some(t) if t.token_type == TokenType::Datatype => {
|
|
|
|
pos += 1;
|
|
|
|
Some(String::from(&t.value))
|
|
|
|
}
|
2023-03-15 21:33:00 +00:00
|
|
|
// If the first token is anything else, ignore
|
2023-02-15 21:17:50 +00:00
|
|
|
Some(_) => None,
|
2023-03-15 21:33:00 +00:00
|
|
|
// This should never match, as there should always be at least a
|
|
|
|
// TokenType::Semicolon or TokenType::EOF
|
|
|
|
None => panic!(
|
|
|
|
"Internal compiler error: Illegal token stream at src/syntax/binding.rs#try_parse"
|
|
|
|
),
|
2023-02-15 21:17:50 +00:00
|
|
|
}
|
|
|
|
};
|
2023-03-14 21:10:43 +00:00
|
|
|
|
2023-02-15 21:17:50 +00:00
|
|
|
// var/val keyword
|
2023-03-15 21:33:00 +00:00
|
|
|
let (is_val, binding_token) = {
|
2023-02-09 23:44:31 +00:00
|
|
|
let res1 = try_token_type(tokens, pos, TokenType::VAL);
|
|
|
|
match res1 {
|
2023-03-15 21:33:00 +00:00
|
|
|
Some(val_token) => (true, val_token),
|
2023-02-09 23:44:31 +00:00
|
|
|
None => {
|
|
|
|
let res2 = try_token_type(tokens, pos, TokenType::VAR);
|
|
|
|
match res2 {
|
2023-03-15 21:33:00 +00:00
|
|
|
Some(var_token) => (false, var_token),
|
|
|
|
// Neither VAL nor VAR were matched, the parser should try
|
|
|
|
// other constructs
|
2023-03-14 21:10:43 +00:00
|
|
|
None => return None,
|
2023-02-09 23:44:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2023-01-08 23:09:06 +00:00
|
|
|
|
|
|
|
let identifier = try_token_type(tokens, pos + 1, TokenType::Identifier);
|
2023-03-14 21:10:43 +00:00
|
|
|
if identifier.is_none() {
|
2023-03-15 21:41:04 +00:00
|
|
|
// TODO: Differentiate between no token found and incorrect token found.
|
|
|
|
// TODO:
|
2023-03-15 21:33:00 +00:00
|
|
|
// The parser didn't find an Identifier after VAL/VAR
|
|
|
|
return Some(SyntaxResult::Err(SyntaxError {
|
2023-03-15 21:41:04 +00:00
|
|
|
reason: format!(
|
|
|
|
"There should be an identifier after a `{}` token",
|
|
|
|
if is_val {"val"} else {"var"}
|
|
|
|
),
|
2023-03-15 21:33:00 +00:00
|
|
|
error_start: binding_token.position,
|
|
|
|
error_end: binding_token.position + binding_token.value.len(),
|
|
|
|
}));
|
2023-03-14 21:10:43 +00:00
|
|
|
}
|
2023-01-08 23:09:06 +00:00
|
|
|
let identifier = identifier.unwrap();
|
|
|
|
|
|
|
|
let equal_operator = try_operator(tokens, pos + 2, String::from("="));
|
2023-03-14 21:10:43 +00:00
|
|
|
if equal_operator.is_none() {
|
|
|
|
// TODO: return Error
|
|
|
|
return None;
|
|
|
|
}
|
2023-01-08 23:09:06 +00:00
|
|
|
|
|
|
|
let expression = expression::try_parse(tokens, pos + 3);
|
2023-03-14 21:10:43 +00:00
|
|
|
if expression.is_none() {
|
|
|
|
// TODO: return Error
|
|
|
|
return None;
|
|
|
|
}
|
2023-01-08 23:09:06 +00:00
|
|
|
let expression = expression.unwrap();
|
|
|
|
|
2023-03-14 21:10:43 +00:00
|
|
|
let binding = if is_val {
|
|
|
|
Binding::Val(ValBinding {
|
2023-02-15 21:17:50 +00:00
|
|
|
datatype: datatype_annotation,
|
2023-02-09 23:44:31 +00:00
|
|
|
identifier: &identifier.value,
|
|
|
|
expression,
|
2023-03-14 21:10:43 +00:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Binding::Var(VarBinding {
|
2023-02-15 21:17:50 +00:00
|
|
|
datatype: datatype_annotation,
|
2023-02-09 23:44:31 +00:00
|
|
|
identifier: &identifier.value,
|
|
|
|
expression,
|
2023-03-14 21:10:43 +00:00
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(SyntaxResult::Ok(binding))
|
2023-01-08 23:09:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Option<&Token> {
|
|
|
|
tokens
|
|
|
|
.get(pos)
|
2023-03-14 21:10:43 +00:00
|
|
|
.and_then(|token| (token.token_type == token_type).then(|| token))
|
2023-01-08 23:09:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn try_operator(tokens: &Vec<Token>, pos: usize, operator: String) -> Option<&Token> {
|
2023-03-14 21:10:43 +00:00
|
|
|
tokens.get(pos).and_then(|token| {
|
|
|
|
(token.token_type == TokenType::Operator && token.value == operator).then(|| token)
|
|
|
|
})
|
2023-01-08 23:09:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::lexic::get_tokens;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_parse_val_binding() {
|
|
|
|
let tokens = get_tokens(&String::from("val identifier = 20")).unwrap();
|
|
|
|
let binding = try_parse(&tokens, 0).unwrap();
|
|
|
|
|
|
|
|
match binding {
|
2023-03-14 21:10:43 +00:00
|
|
|
SyntaxResult::Ok(Binding::Val(binding)) => {
|
2023-01-08 23:09:06 +00:00
|
|
|
assert_eq!("identifier", binding.identifier);
|
|
|
|
}
|
2023-03-14 21:10:43 +00:00
|
|
|
_ => panic!(),
|
2023-01-08 23:09:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_parse_val() {
|
|
|
|
let tokens = get_tokens(&String::from("val")).unwrap();
|
|
|
|
let token = try_token_type(&tokens, 0, TokenType::VAL).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(TokenType::VAL, token.token_type);
|
|
|
|
assert_eq!("val", token.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_parse_identifier() {
|
|
|
|
let tokens = get_tokens(&String::from("identifier")).unwrap();
|
|
|
|
let token = try_token_type(&tokens, 0, TokenType::Identifier).unwrap();
|
|
|
|
|
|
|
|
assert_eq!("identifier", token.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_parse_operator() {
|
|
|
|
let tokens = get_tokens(&String::from("=")).unwrap();
|
|
|
|
let token = try_operator(&tokens, 0, String::from("=")).unwrap();
|
|
|
|
|
|
|
|
assert_eq!("=", token.value);
|
|
|
|
}
|
2023-03-14 21:10:43 +00:00
|
|
|
|
2023-02-15 21:17:50 +00:00
|
|
|
#[test]
|
|
|
|
fn should_parse_binding_with_datatype() {
|
|
|
|
let tokens = get_tokens(&String::from("Num val identifier = 20")).unwrap();
|
|
|
|
let binding = try_parse(&tokens, 0).unwrap();
|
|
|
|
|
|
|
|
match binding {
|
2023-03-14 21:10:43 +00:00
|
|
|
SyntaxResult::Ok(Binding::Val(binding)) => {
|
2023-02-15 21:17:50 +00:00
|
|
|
assert_eq!(Some(String::from("Num")), binding.datatype);
|
|
|
|
assert_eq!("identifier", binding.identifier);
|
|
|
|
}
|
2023-03-14 21:10:43 +00:00
|
|
|
_ => panic!(),
|
2023-02-15 21:17:50 +00:00
|
|
|
}
|
2023-03-14 21:10:43 +00:00
|
|
|
|
2023-02-15 21:17:50 +00:00
|
|
|
let tokens = get_tokens(&String::from("Bool var identifier = true")).unwrap();
|
|
|
|
let binding = try_parse(&tokens, 0).unwrap();
|
|
|
|
|
|
|
|
match binding {
|
2023-03-14 21:10:43 +00:00
|
|
|
SyntaxResult::Ok(Binding::Var(binding)) => {
|
2023-02-15 21:17:50 +00:00
|
|
|
assert_eq!(Some(String::from("Bool")), binding.datatype);
|
|
|
|
assert_eq!("identifier", binding.identifier);
|
|
|
|
}
|
2023-03-14 21:10:43 +00:00
|
|
|
_ => panic!(),
|
2023-02-15 21:17:50 +00:00
|
|
|
}
|
|
|
|
}
|
2023-03-15 21:33:00 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_return_correct_error() {
|
|
|
|
let tokens = get_tokens(&String::from("val")).unwrap();
|
|
|
|
assert_eq!(TokenType::VAL, tokens[0].token_type);
|
|
|
|
assert_eq!(0, tokens[0].position);
|
|
|
|
let binding = try_parse(&tokens, 0).unwrap();
|
|
|
|
|
|
|
|
match binding {
|
|
|
|
SyntaxResult::Err(error) => {
|
|
|
|
assert_eq!(0, error.error_start);
|
|
|
|
assert_eq!(3, error.error_end);
|
|
|
|
}
|
|
|
|
_ => panic!(),
|
|
|
|
}
|
|
|
|
}
|
2023-01-08 23:09:06 +00:00
|
|
|
}
|