Parse val/var binding & implicit val binding

This commit is contained in:
Araozu 2024-03-18 09:51:17 -05:00
parent f60992c303
commit 2d32f1a0bc
5 changed files with 121 additions and 52 deletions

View File

@ -32,13 +32,13 @@
## v0.0.11 ## v0.0.11
- [ ] Parse binding of form `val Type variable = value` - [x] Parse binding of form `val Type variable = value`
- [ ] Parse binding of form `Type variable = value` - [x] Parse binding of form `Type variable = value`
- [ ] Infer datatype of `value` in the above for a simple expression - [ ] Infer datatype of `value` in the above for a simple expression
- [ ] Ensure that the anotated datatype matches the datatype of `value` in the above - [ ] Ensure that the anotated datatype matches the datatype of `value` in the above
- [ ] Infer datatype of a `val variable = value` in the AST: Use the infered datatype - [ ] Infer datatype of a `val variable = value` in the AST: Use the infered datatype
- [ ] Formally define the top level constructs - [x] Formally define the top level constructs
- [ ] Parse bindings and function declarations as top level constructs - [x] Parse bindings and function declarations as top level constructs
- [ ] Parse function declaration arguments (`Type id`) - [ ] Parse function declaration arguments (`Type id`)
- [x] Parse function return datatype (`fun f() -> Type`) - [x] Parse function return datatype (`fun f() -> Type`)
- [x] Return parsing to variables to var/val - [x] Return parsing to variables to var/val

View File

@ -4,29 +4,51 @@ use super::{expression, ParsingError, ParsingResult};
use crate::error_handling::SyntaxError; use crate::error_handling::SyntaxError;
use crate::lexic::token::{Token, TokenType}; use crate::lexic::token::{Token, TokenType};
/*
binding = val binding | var binding
val binding = "val", datatype?, binding remainder
| datatype, binding remainder
var binding = "var", datatype?, binding remainder
binding remainder = identifier, "=", expression
*/
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Binding> { pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Binding> {
let mut current_pos = pos; let mut current_pos = pos;
// TODO: Detect if the binding starts with a datatype
// TODO: Revert to val/var
/* /*
* let keyword * val/var keyword
*/ */
let (is_mutable, binding_token, next_pos) = { let (is_var, binding_token, next_pos) = 'token: {
match parse_token_type(tokens, current_pos, TokenType::VAL) { // check for VAL
Ok((val_token, next_pos)) => (false, val_token, next_pos), if let Ok((val_token, next_pos)) = parse_token_type(tokens, current_pos, TokenType::VAL) {
_ => { break 'token (false, Some(val_token), next_pos);
// If VAL is not found, search for VAR };
match parse_token_type(tokens, current_pos, TokenType::VAR) {
Ok((var_token, next_pos)) => (true, var_token, next_pos), // check for VAR
_ => return Err(ParsingError::Unmatched), match parse_token_type(tokens, current_pos, TokenType::VAR) {
} Ok((var_token, next_pos)) => (true, Some(var_token), next_pos),
} // If a VAR is not found it is still possible that the binding is an implicit VAL
_ => (false, None, current_pos),
} }
}; };
current_pos = next_pos; current_pos = next_pos;
/*
* datatype
*/
let (datatype, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Datatype) {
Ok((t, next)) => (Some(t), next),
_ => (None, current_pos),
};
current_pos = next_pos;
// Here:
// If the binding is None and the datatype is None, then we didn't match a binding
if binding_token.is_none() && datatype.is_none() {
return Err(ParsingError::Unmatched);
}
/* /*
* identifier * identifier
*/ */
@ -45,15 +67,29 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Bindin
return Err(ParsingError::Err(error)); return Err(ParsingError::Err(error));
} }
_ => { _ => {
// The parser didn't find an Identifier after VAL/VAR // The parser didn't find an Identifier after VAL/VAR or the Datatype
return Err(ParsingError::Err(SyntaxError { match (binding_token, datatype) {
reason: format!( (Some(binding_token), _) => {
"There should be an identifier after a `{}` token", return Err(ParsingError::Err(SyntaxError {
if is_mutable { "val" } else { "var" } reason: format!(
), "There should be an identifier after a `{}` token",
error_start: binding_token.position, if is_var { "val" } else { "var" }
error_end: binding_token.get_end_position(), ),
})); error_start: binding_token.position,
error_end: binding_token.get_end_position(),
}));
}
(None, Some(datatype_token)) => {
return Err(ParsingError::Err(SyntaxError {
reason: "There should be an identifier after the datatype".into(),
error_start: datatype_token.position,
error_end: datatype_token.get_end_position(),
}));
}
_ => {
panic!("Illegal parser state: binding_token and datatype are both None")
}
};
} }
}; };
current_pos = next_pos; current_pos = next_pos;
@ -82,6 +118,9 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Bindin
}; };
current_pos += 1; current_pos += 1;
/*
* Expression of the binding
*/
let (expression, next_pos) = match expression::try_parse(tokens, current_pos) { let (expression, next_pos) = match expression::try_parse(tokens, current_pos) {
Ok((exp, next)) => (exp, next), Ok((exp, next)) => (exp, next),
_ => { _ => {
@ -95,10 +134,10 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Bindin
current_pos = next_pos; current_pos = next_pos;
let binding = Binding { let binding = Binding {
datatype: None, datatype,
identifier: &identifier, identifier: &identifier,
expression, expression,
is_mutable, is_mutable: is_var,
}; };
Ok((binding, current_pos)) Ok((binding, current_pos))
@ -144,26 +183,51 @@ mod tests {
assert_eq!("=", token.value); assert_eq!("=", token.value);
} }
/*
#[test] #[test]
fn should_parse_binding_with_datatype() { fn should_parse_val_binding_with_datatype() {
let tokens = get_tokens(&String::from("Num val identifier = 20")).unwrap(); let tokens = get_tokens(&String::from("val Int identifier = 20")).unwrap();
let ParseResult::Ok(Binding::Val(binding), _) = try_parse(&tokens, 0) else { let (binding, _) = try_parse(&tokens, 0).unwrap();
panic!()
};
assert_eq!(Some(String::from("Num")), binding.datatype); assert!(!binding.is_mutable);
assert_eq!("identifier", format!("{}", binding.identifier)); assert_eq!("Int", binding.datatype.unwrap().value);
assert_eq!("identifier", binding.identifier.value);
let tokens = get_tokens(&String::from("Bool var identifier = 20")).unwrap(); }
let ParseResult::Ok(Binding::Var(binding), _) = try_parse(&tokens, 0) else {
panic!() #[test]
}; fn should_parse_var_binding_with_datatype() {
let tokens = get_tokens(&String::from("var Int identifier = 20")).unwrap();
assert_eq!(Some(String::from("Bool")), binding.datatype); let (binding, _) = try_parse(&tokens, 0).unwrap();
assert_eq!("identifier", format!("{}", binding.identifier));
assert!(binding.is_mutable);
assert!(binding.datatype.is_some());
assert_eq!("Int", binding.datatype.unwrap().value);
assert_eq!("identifier", binding.identifier.value);
}
#[test]
fn should_parse_implicit_val_binding() {
let tokens = get_tokens(&String::from("Int identifier = 20")).unwrap();
let (binding, _) = try_parse(&tokens, 0).unwrap();
assert!(!binding.is_mutable);
assert!(binding.datatype.is_some());
assert_eq!("Int", binding.datatype.unwrap().value);
assert_eq!("identifier", binding.identifier.value);
}
#[test]
fn should_return_error_on_implicit_val_binding() {
let tokens = get_tokens(&String::from("Int => 20")).unwrap();
let binding = try_parse(&tokens, 0);
match binding {
Err(ParsingError::Err(error)) => {
assert_eq!(4, error.error_start);
assert_eq!(6, error.error_end);
}
_ => panic!("Error expected"),
}
} }
*/
#[test] #[test]
fn should_return_correct_error() { fn should_return_correct_error() {

View File

@ -69,7 +69,6 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Functi
}; };
current_pos = next_pos; current_pos = next_pos;
// Try to parse a return type // Try to parse a return type
let (return_type, next_pos) = 'return_label: { let (return_type, next_pos) = 'return_label: {
let (arrow_op, next_pos) = match try_operator(tokens, current_pos, "->".into()) { let (arrow_op, next_pos) = match try_operator(tokens, current_pos, "->".into()) {
@ -134,7 +133,6 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Functi
)) ))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::lexic::get_tokens; use crate::lexic::get_tokens;
@ -355,7 +353,10 @@ mod tests {
let (function_declaration, _) = try_parse(&tokens, 0).unwrap(); let (function_declaration, _) = try_parse(&tokens, 0).unwrap();
assert_eq!(function_declaration.identifier.value, String::from("id")); assert_eq!(function_declaration.identifier.value, String::from("id"));
assert_eq!(function_declaration.return_type.unwrap().value, String::from("String")); assert_eq!(
function_declaration.return_type.unwrap().value,
String::from("String")
);
} }
#[test] #[test]

View File

@ -58,7 +58,13 @@ arguments list = "(", ")"
## Binding ## Binding
```ebnf ```ebnf
binding = ("val" | "var"), identifier, "=", expression binding = val binding | var binding
val binding = "val", datatype?, binding remainder
| datatype, binding remainder
var binding = "var", datatype?, binding remainder
binding remainder = identifier, "=", expression
``` ```

View File

@ -75,5 +75,3 @@ pub fn parse_token_type(
None => Err(ParsingError::Unmatched), None => Err(ParsingError::Unmatched),
} }
} }