diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fd6b56..8915bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ - Begin work on the code formatter - Remove all panic! and todo! +## v0.1.2 + +- [x] Parse conditionals + ## v0.1.1 diff --git a/src/lexic/scanner/identifier.rs b/src/lexic/scanner/identifier.rs index c426d77..371078e 100755 --- a/src/lexic/scanner/identifier.rs +++ b/src/lexic/scanner/identifier.rs @@ -7,6 +7,8 @@ fn str_is_keyword(s: &String) -> Option { "val" => Some(TokenType::VAL), "var" => Some(TokenType::VAR), "fun" => Some(TokenType::FUN), + "if" => Some(TokenType::IF), + "else" => Some(TokenType::ELSE), _ => None, } } diff --git a/src/lexic/token.rs b/src/lexic/token.rs index baac0b1..65939b3 100755 --- a/src/lexic/token.rs +++ b/src/lexic/token.rs @@ -24,6 +24,8 @@ pub enum TokenType { VAR, EOF, FUN, + IF, + ELSE, } #[derive(Serialize, Debug, Clone, PartialEq)] diff --git a/src/semantic/checks/function_declaration.rs b/src/semantic/checks/function_declaration.rs index ccb877b..b1e68fc 100644 --- a/src/semantic/checks/function_declaration.rs +++ b/src/semantic/checks/function_declaration.rs @@ -49,6 +49,7 @@ impl SemanticCheck for FunctionDeclaration<'_> { return Err(MistiError::Semantic(error)); } + BlockMember::Stmt(Statement::Conditional(_)) => unimplemented!("check conditional"), BlockMember::Expr(e) => e.check_semantics(scope)?, } } diff --git a/src/semantic/checks/top_level_declaration.rs b/src/semantic/checks/top_level_declaration.rs index 80d52ca..fa419f7 100644 --- a/src/semantic/checks/top_level_declaration.rs +++ b/src/semantic/checks/top_level_declaration.rs @@ -22,6 +22,7 @@ impl SemanticCheck for Statement<'_> { match self { Statement::Binding(b) => b.check_semantics(scope), Statement::FnDecl(f) => f.check_semantics(scope), + Statement::Conditional(_) => unimplemented!("check conditional"), } } } diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index 82cdc70..93e5cee 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -31,8 +31,24 @@ pub enum ModuleMembers<'a> { pub enum Statement<'a> { Binding(VariableBinding<'a>), FnDecl(FunctionDeclaration<'a>), + // TODO: Implement conditionals as expressions + Conditional(Conditional<'a>), } +#[derive(Debug)] +pub struct Conditional<'a> { + pub if_member: Condition<'a>, + pub else_if_members: Vec>, + pub else_block: Option> +} + +#[derive(Debug)] +pub struct Condition<'a> { + pub condition: Expression<'a>, + pub body: Block<'a>, +} + + #[derive(Debug)] pub struct FunctionDeclaration<'a> { pub identifier: &'a Token, diff --git a/src/syntax/parsers/conditional.rs b/src/syntax/parsers/conditional.rs new file mode 100644 index 0000000..9ff8218 --- /dev/null +++ b/src/syntax/parsers/conditional.rs @@ -0,0 +1,181 @@ +use crate::{ + error_handling::SyntaxError, + lexic::token::{Token, TokenType}, + syntax::{ + ast::{Block, Condition, Conditional, Expression, Positionable}, + parseable::{Parseable, ParsingError, ParsingResult}, + utils::parse_token_type, + }, +}; + +impl<'a> Parseable<'a> for Conditional<'a> { + type Item = Conditional<'a>; + + fn try_parse(tokens: &'a Vec, current_pos: usize) -> ParsingResult<'a, Self::Item> { + // if keyword + let (if_token, next) = match parse_token_type(tokens, current_pos, TokenType::IF) { + Ok(tuple) => tuple, + _ => return Err(ParsingError::Unmatched), + }; + + // if condition + let (if_expression, next) = match Expression::try_parse(tokens, next) { + Ok(tuple) => tuple, + Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), + Err(ParsingError::Mismatch(wrong_token)) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected an expression after the if token"), + error_start: wrong_token.position, + error_end: wrong_token.get_end_position(), + })); + } + Err(ParsingError::Unmatched) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected an expression after the if token"), + error_start: if_token.position, + error_end: if_token.get_end_position(), + })); + } + }; + + // if block + let (if_block, next) = match Block::try_parse(tokens, next) { + Ok(t) => t, + Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), + Err(ParsingError::Mismatch(wrong_token)) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected a block after the condition"), + error_start: wrong_token.position, + error_end: wrong_token.get_end_position(), + })); + } + Err(ParsingError::Unmatched) => { + let (error_start, error_end) = if_expression.get_position(); + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected a block after the condition"), + error_start, + error_end, + })); + } + }; + + let mut else_if_members = Vec::::new(); + let mut current_pos = next; + let tokens_len = tokens.len(); + + // many else if + while current_pos < tokens_len { + // else token + let (_, next) = match parse_token_type(tokens, current_pos, TokenType::ELSE) { + Ok(tuple) => tuple, + _ => { + break; + } + }; + + // if token + let (if_token, next) = match parse_token_type(tokens, next, TokenType::IF) { + Ok(tuple) => tuple, + // This might be a else {}, not a else if {} + _ => { + break; + } + }; + + // condition + let (condition, next) = match Expression::try_parse(tokens, next) { + Ok(tuple) => tuple, + Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), + Err(ParsingError::Mismatch(wrong_token)) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected an expression after the if token"), + error_start: wrong_token.position, + error_end: wrong_token.get_end_position(), + })); + } + Err(ParsingError::Unmatched) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected an expression after the if token"), + error_start: if_token.position, + error_end: if_token.get_end_position(), + })); + } + }; + + // block + let (block, next) = match Block::try_parse(tokens, next) { + Ok(t) => t, + Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), + Err(ParsingError::Mismatch(wrong_token)) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected a block after the condition"), + error_start: wrong_token.position, + error_end: wrong_token.get_end_position(), + })); + } + Err(ParsingError::Unmatched) => { + let (error_start, error_end) = condition.get_position(); + return Err(ParsingError::Err(SyntaxError { + reason: String::from("Expected a block after the condition"), + error_start, + error_end, + })); + } + }; + + else_if_members.push(Condition { + condition, + body: block, + }); + + current_pos = next; + } + + // else + let (else_block, next) = { + match parse_token_type(tokens, current_pos, TokenType::ELSE) { + Ok((else_token, next)) => { + // block + let (block, next) = match Block::try_parse(tokens, next) { + Ok(t) => t, + Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), + Err(ParsingError::Mismatch(wrong_token)) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from( + "Expected a block after the else keyword", + ), + error_start: wrong_token.position, + error_end: wrong_token.get_end_position(), + })); + } + Err(ParsingError::Unmatched) => { + return Err(ParsingError::Err(SyntaxError { + reason: String::from( + "Expected a block after the else keyword", + ), + error_start: else_token.position, + error_end: else_token.get_end_position(), + })); + } + }; + + (Some(block), next) + } + _ => (None, current_pos), + } + }; + + // return + + let result = Conditional { + if_member: Condition { + condition: if_expression, + body: if_block, + }, + else_if_members, + else_block, + }; + + Ok((result, next)) + } +} diff --git a/src/syntax/parsers/mod.rs b/src/syntax/parsers/mod.rs index 2ae4bce..8187861 100644 --- a/src/syntax/parsers/mod.rs +++ b/src/syntax/parsers/mod.rs @@ -4,3 +4,4 @@ pub mod expression; pub mod function_declaration; pub mod module; pub mod statement; +pub mod conditional; diff --git a/src/syntax/parsers/statement.rs b/src/syntax/parsers/statement.rs index a337cc2..1e56db4 100644 --- a/src/syntax/parsers/statement.rs +++ b/src/syntax/parsers/statement.rs @@ -1,15 +1,15 @@ -use crate::syntax::{ - ast::{var_binding::VariableBinding, FunctionDeclaration, Statement}, - parseable::{Parseable, ParsingError}, +use crate::{ + lexic::token::Token, + syntax::{ + ast::{var_binding::VariableBinding, Conditional, FunctionDeclaration, Statement}, + parseable::{Parseable, ParsingError, ParsingResult}, + }, }; impl<'a> Parseable<'a> for Statement<'a> { type Item = Statement<'a>; - fn try_parse( - tokens: &'a Vec, - current_pos: usize, - ) -> crate::syntax::parseable::ParsingResult<'a, Self::Item> { + fn try_parse(tokens: &'a Vec, current_pos: usize) -> ParsingResult<'a, Self::Item> { // Try to parse a variable binding match VariableBinding::try_parse(tokens, current_pos) { Ok((prod, next)) => { @@ -34,6 +34,13 @@ impl<'a> Parseable<'a> for Statement<'a> { _ => {} } + // Try to parse a conditional + match Conditional::try_parse(tokens, current_pos) { + Ok((prod, next)) => return Ok((Statement::Conditional(prod), next)), + Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), + _ => {} + } + // Here nothing was parsed. Err(ParsingError::Unmatched) }