refactor: impl Parseable for Block
This commit is contained in:
parent
8d5fcd1ce3
commit
6a951434f3
@ -5,10 +5,13 @@ use super::Transpilable;
|
||||
impl Transpilable for Block<'_> {
|
||||
fn transpile(&self) -> String {
|
||||
// TODO: Handle indentation
|
||||
self.statements
|
||||
todo!("transpilation for block");
|
||||
/*
|
||||
self.members
|
||||
.iter()
|
||||
.map(|x| x.transpile())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ mod tests {
|
||||
},
|
||||
};
|
||||
|
||||
/* TODO: reimplement
|
||||
#[test]
|
||||
fn should_transpile() {
|
||||
let tokens = get_tokens(&String::from("fun id() {}")).unwrap();
|
||||
@ -38,5 +39,5 @@ mod tests {
|
||||
}
|
||||
_ => panic!("Expected a function declaration"),
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::{SymbolEntry, SymbolTable},
|
||||
},
|
||||
syntax::ast::{FunctionDeclaration, Statement},
|
||||
syntax::ast::{BlockMember, FunctionDeclaration, Statement},
|
||||
};
|
||||
|
||||
impl SemanticCheck for FunctionDeclaration<'_> {
|
||||
@ -33,16 +33,17 @@ impl SemanticCheck for FunctionDeclaration<'_> {
|
||||
|
||||
// TODO: Check the return type of the function body
|
||||
// This should be the last expression in the block
|
||||
for stmt in self.block.statements.iter() {
|
||||
for stmt in self.block.members.iter() {
|
||||
match stmt {
|
||||
Statement::Binding(b) => {
|
||||
BlockMember::Stmt(Statement::Binding(b)) => {
|
||||
if let Err(err) = b.check_semantics(&function_scope) {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Statement::FnDecl(_) => {
|
||||
BlockMember::Stmt(Statement::FnDecl(_)) => {
|
||||
todo!("Function declaration: semantic check not implemented")
|
||||
}
|
||||
_ => todo!("Expression: semantic check not implemented"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,14 @@ pub struct FunctionDeclaration<'a> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Block<'a> {
|
||||
// TODO: this should be a Vec of Statement|Expression
|
||||
pub statements: Vec<Statement<'a>>,
|
||||
pub members: Vec<BlockMember<'a>>,
|
||||
}
|
||||
|
||||
/// Enum for productions available at the block level
|
||||
#[derive(Debug)]
|
||||
pub enum BlockMember<'a> {
|
||||
Stmt(Statement<'a>),
|
||||
Expr(Expression<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,118 +0,0 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
lexic::token::{Token, TokenType},
|
||||
};
|
||||
|
||||
use super::{ast::Block, utils::parse_token_type, ParsingError, ParsingResult};
|
||||
|
||||
// Assumes that the token at `pos` is a {
|
||||
pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Block> {
|
||||
let mut current_pos = pos;
|
||||
|
||||
let (opening_brace, next_pos) = parse_token_type(tokens, current_pos, TokenType::LeftBrace)?;
|
||||
current_pos = next_pos;
|
||||
|
||||
// Parse block statements
|
||||
let mut statements = Vec::new();
|
||||
|
||||
// First statement
|
||||
match super::statement::try_parse(tokens, current_pos) {
|
||||
Ok((statement, next_pos)) => {
|
||||
current_pos = next_pos;
|
||||
statements.push(statement);
|
||||
}
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// More statements separated by new lines
|
||||
while let Some(t) = tokens.get(current_pos) {
|
||||
if t.token_type != TokenType::NewLine {
|
||||
break;
|
||||
}
|
||||
current_pos += 1;
|
||||
|
||||
match super::statement::try_parse(tokens, current_pos) {
|
||||
Ok((statement, next_pos)) => {
|
||||
current_pos = next_pos;
|
||||
statements.push(statement);
|
||||
}
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse closing brace
|
||||
let (_closing_brace, next_pos) =
|
||||
match parse_token_type(tokens, current_pos, TokenType::RightBrace) {
|
||||
Ok((t, next)) => (t, next),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing brace after the block body."),
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
}));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing brace after the block body."),
|
||||
error_start: opening_brace.position,
|
||||
error_end: opening_brace.get_end_position(),
|
||||
}));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
|
||||
Ok((Block { statements }, current_pos))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::lexic::get_tokens;
|
||||
|
||||
// TODO: rewrite, refactor
|
||||
/*
|
||||
#[test]
|
||||
fn test_parse_block() {
|
||||
let tokens = get_tokens(&String::from("{f()}")).unwrap();
|
||||
let block = parse_block(&tokens, 0);
|
||||
|
||||
let block = match block {
|
||||
ParsingResult::Ok((p, _)) => p,
|
||||
_ => panic!("Expected a block, got: {:?}", block),
|
||||
};
|
||||
|
||||
assert_eq!(block.statements.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_block_2() {
|
||||
let tokens = get_tokens(&String::from("{f()\ng()}")).unwrap();
|
||||
let block = parse_block(&tokens, 0);
|
||||
|
||||
let block = match block {
|
||||
ParsingResult::Ok((p, _)) => p,
|
||||
_ => panic!("Expected a block, got: {:?}", block),
|
||||
};
|
||||
|
||||
assert_eq!(block.statements.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_block_3() {
|
||||
let tokens = get_tokens(&String::from("{\n f()\n}")).unwrap();
|
||||
let block = parse_block(&tokens, 0);
|
||||
|
||||
let block = match block {
|
||||
ParsingResult::Ok((p, _)) => p,
|
||||
_ => {
|
||||
panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens)
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(block.statements.len(), 1);
|
||||
}
|
||||
*/
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
use crate::error_handling::MistiError;
|
||||
|
||||
mod block;
|
||||
mod expression;
|
||||
mod functions;
|
||||
mod parseable;
|
||||
mod parsers;
|
||||
mod statement;
|
||||
mod utils;
|
||||
|
||||
pub mod ast;
|
||||
|
147
src/syntax/parsers/block.rs
Normal file
147
src/syntax/parsers/block.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{Block, BlockMember, Expression, Statement},
|
||||
parseable::{Parseable, ParsingError, ParsingResult},
|
||||
utils::parse_token_type,
|
||||
},
|
||||
};
|
||||
|
||||
impl<'a> Parseable<'a> for Block<'a> {
|
||||
type Item = Block<'a>;
|
||||
|
||||
fn try_parse(tokens: &'a Vec<Token>, current_pos: usize) -> ParsingResult<'a, Self::Item> {
|
||||
let mut current_pos = current_pos;
|
||||
|
||||
let (opening_brace, next_pos) =
|
||||
parse_token_type(tokens, current_pos, TokenType::LeftBrace)?;
|
||||
current_pos = next_pos;
|
||||
|
||||
// Parse BlockMember = Statement | Expression
|
||||
let mut members = Vec::<BlockMember>::new();
|
||||
|
||||
let tokens_len = tokens.len();
|
||||
|
||||
// Minus one because last token is EOF
|
||||
while current_pos < tokens_len - 1 {
|
||||
// Attempt to parse an statement
|
||||
match Statement::try_parse(tokens, current_pos) {
|
||||
Ok((prod, next_pos)) => {
|
||||
members.push(BlockMember::Stmt(prod));
|
||||
current_pos = next_pos;
|
||||
continue;
|
||||
}
|
||||
Err(ParsingError::Err(error)) => {
|
||||
// TODO: Better error handling, write a better error message
|
||||
return Err(ParsingError::Err(error));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
// Attempt to parse an expression
|
||||
match Expression::try_parse(tokens, current_pos) {
|
||||
Ok((prod, next_pos)) => {
|
||||
members.push(BlockMember::Expr(prod));
|
||||
current_pos = next_pos;
|
||||
}
|
||||
Err(ParsingError::Err(error)) => {
|
||||
// TODO: Better error handling, write a better error message
|
||||
return Err(ParsingError::Err(error));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// If we reached this point we didn't match any productions
|
||||
// we get out of the block, the parsing of "}" will deal
|
||||
// with any incorrect tokens
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse closing brace
|
||||
let (_closing_brace, next_pos) =
|
||||
match parse_token_type(tokens, current_pos, TokenType::RightBrace) {
|
||||
Ok((t, next)) => (t, next),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing brace after the block body."),
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
}));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing brace after the block body."),
|
||||
// TODO: use the last token (at pos current_pos) as guide for the error
|
||||
// msg position
|
||||
error_start: opening_brace.position,
|
||||
error_end: opening_brace.get_end_position(),
|
||||
}));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
|
||||
let block = Block { members };
|
||||
Ok((block, current_pos))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::lexic::get_tokens;
|
||||
|
||||
#[test]
|
||||
fn should_parse_empty_block() {
|
||||
let tokens = get_tokens(&String::from("{}")).unwrap();
|
||||
let (block, next_pos) = Block::try_parse(&tokens, 0).unwrap();
|
||||
|
||||
assert_eq!(2, next_pos);
|
||||
assert_eq!(0, block.members.len())
|
||||
}
|
||||
|
||||
// TODO: rewrite, refactor
|
||||
/*
|
||||
#[test]
|
||||
fn test_parse_block() {
|
||||
let tokens = get_tokens(&String::from("{f()}")).unwrap();
|
||||
let block = parse_block(&tokens, 0);
|
||||
|
||||
let block = match block {
|
||||
ParsingResult::Ok((p, _)) => p,
|
||||
_ => panic!("Expected a block, got: {:?}", block),
|
||||
};
|
||||
|
||||
assert_eq!(block.statements.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_block_2() {
|
||||
let tokens = get_tokens(&String::from("{f()\ng()}")).unwrap();
|
||||
let block = parse_block(&tokens, 0);
|
||||
|
||||
let block = match block {
|
||||
ParsingResult::Ok((p, _)) => p,
|
||||
_ => panic!("Expected a block, got: {:?}", block),
|
||||
};
|
||||
|
||||
assert_eq!(block.statements.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_block_3() {
|
||||
let tokens = get_tokens(&String::from("{\n f()\n}")).unwrap();
|
||||
let block = parse_block(&tokens, 0);
|
||||
|
||||
let block = match block {
|
||||
ParsingResult::Ok((p, _)) => p,
|
||||
_ => {
|
||||
panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens)
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(block.statements.len(), 1);
|
||||
}
|
||||
*/
|
||||
}
|
@ -2,8 +2,7 @@ use crate::{
|
||||
error_handling::SyntaxError,
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::FunctionDeclaration,
|
||||
block::parse_block,
|
||||
ast::{Block, FunctionDeclaration},
|
||||
functions::params_list::parse_params_list,
|
||||
parseable::{Parseable, ParsingError, ParsingResult},
|
||||
utils::{parse_token_type, try_operator},
|
||||
@ -72,6 +71,7 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
|
||||
current_pos = next_pos;
|
||||
|
||||
// Try to parse a return type
|
||||
// TODO: abstract parsing of Datatype to parse generics
|
||||
let (return_type, next_pos) = 'return_label: {
|
||||
let (arrow_op, next_pos) = match try_operator(tokens, current_pos, "->".into()) {
|
||||
Ok((op, next)) => (op, next),
|
||||
@ -101,7 +101,8 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
|
||||
current_pos = next_pos;
|
||||
|
||||
// Function body (block)
|
||||
let (block, next_pos) = match parse_block(tokens, current_pos) {
|
||||
// TODO: impl Parseable
|
||||
let (block, next_pos) = match Block::try_parse(tokens, current_pos) {
|
||||
Ok((block, next_pos)) => (block, next_pos),
|
||||
Err(ParsingError::Err(error)) => {
|
||||
return Err(ParsingError::Err(error));
|
||||
@ -316,14 +317,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_not_parse_fun_without_closing_brace() {
|
||||
let tokens = get_tokens(&String::from("fun id() { 20")).unwrap();
|
||||
let tokens = get_tokens(&String::from("fun id() { ")).unwrap();
|
||||
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(err.reason, "Expected a closing brace after the block body.");
|
||||
assert_eq!(err.error_start, 11);
|
||||
assert_eq!(err.error_end, 13);
|
||||
assert_eq!(err.error_start, 9);
|
||||
assert_eq!(err.error_end, 10);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod binding;
|
||||
pub mod block;
|
||||
pub mod expression;
|
||||
pub mod function_declaration;
|
||||
pub mod module;
|
||||
|
@ -22,7 +22,6 @@ impl<'a> Parseable<'a> for ModuleAST<'a> {
|
||||
let mut current_pos = 0;
|
||||
|
||||
// Minus one because last token is EOF
|
||||
// TODO: Does that EOF do anything?
|
||||
while current_pos < tokens_len - 1 {
|
||||
// Attempt to parse an statement
|
||||
match Statement::try_parse(tokens, current_pos) {
|
||||
|
@ -1,51 +0,0 @@
|
||||
use crate::lexic::token::Token;
|
||||
|
||||
use super::{
|
||||
ast::{var_binding::VariableBinding, Statement},
|
||||
parseable::Parseable,
|
||||
ParsingError, ParsingResult,
|
||||
};
|
||||
|
||||
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Statement> {
|
||||
// Try to parse a binding
|
||||
match VariableBinding::try_parse(tokens, pos) {
|
||||
Ok((b, next)) => return Ok((Statement::Binding(b), next)),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// A function call is an expression, not a statement. Remove
|
||||
// Try to parse a function call
|
||||
/*
|
||||
match function_call_expr::try_parse(tokens, pos) {
|
||||
Ok((Expression::FunctionCall(f), next)) => return Ok((Statement::FunctionCall(f), next)),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
_ => {}
|
||||
};
|
||||
*/
|
||||
|
||||
// Return unmatched
|
||||
Err(ParsingError::Unmatched)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_parse_binding() {
|
||||
let input = String::from("val identifier = 20");
|
||||
let tokens = crate::lexic::get_tokens(&input).unwrap();
|
||||
let statement = try_parse(&tokens, 0);
|
||||
|
||||
let statement = match statement {
|
||||
Ok((s, _)) => s,
|
||||
_ => panic!("Expected a statement"),
|
||||
};
|
||||
|
||||
match statement {
|
||||
Statement::Binding(_) => assert!(true),
|
||||
_ => panic!("Expected a binding"),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user