Compare commits

...

7 Commits

19 changed files with 562 additions and 240 deletions

View File

@ -2,6 +2,7 @@
## TODO ## TODO
- Test correct operator precedence
- Implement functions as first class citizens - Implement functions as first class citizens
- Implement AST transformation before codegen: - Implement AST transformation before codegen:
Create a new AST to represent PHP source code Create a new AST to represent PHP source code
@ -12,7 +13,7 @@
- Store tokens for the semantic analysis phase, to have actual error reporting - Store tokens for the semantic analysis phase, to have actual error reporting
- Parse more complex bindings - Parse more complex bindings
- Watch mode - Watch mode
- Improve error messages - Rework error messages
- Parse other language constructions - Parse other language constructions
- Type checking - Type checking
- Check for conflicting identifiers - Check for conflicting identifiers
@ -28,12 +29,12 @@
## v0.0.13 ## v0.0.13
- [ ] Begin work on a formal grammar - [x] Begin work on a formal grammar
- [ ] Simplify/rewrite AST - [x] Simplify/rewrite AST
- [ ] Define the top level constructs - [x] Define the top level constructs
- [ ] Include the original tokens in the AST - [ ] Include the original tokens in the AST
- [ ] Finish the workflow for a hello world - [ ] Finish the workflow for a hello world
- [ ] Refactor code - [x] Refactor code
- [x] Remove `PARSER couldn't parse any construction` error & replace with an actual error message - [x] Remove `PARSER couldn't parse any construction` error & replace with an actual error message

View File

@ -1,86 +0,0 @@
use crate::{
lexic::token::Token,
syntax::{ast::Expression, ParsingError, ParsingResult},
};
/// Parses a factor expression.
///
/// ```ebnf
/// equality = comparison, (("==" | "!="), comparison )*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (comparison, next_pos) = match super::comparison::try_parse(tokens, pos) {
Ok((expr, next_pos)) => (expr, next_pos),
_ => return Err(ParsingError::Unmatched),
};
parse_many(tokens, next_pos, comparison)
}
fn parse_many<'a>(
tokens: &'a Vec<Token>,
pos: usize,
prev_expr: Expression<'a>,
) -> ParsingResult<'a, Expression<'a>> {
// equality = comparison, (("==" | "!="), comparison )*;
match tokens.get(pos) {
Some(token) if token.value == "==" || token.value == "!=" => {
match super::comparison::try_parse(tokens, pos + 1) {
Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
&token.value,
);
parse_many(tokens, next_pos, expr)
}
_ => Err(ParsingError::Unmatched),
}
}
_ => Ok((prev_expr, pos)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexic::get_tokens;
#[test]
fn should_parse_comparison() {
let tokens = get_tokens(&String::from("a == b")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Ok((expr, _)) => match expr {
Expression::BinaryOperator(exp1, exp2, op) => {
match (*exp1, *exp2) {
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
assert_eq!("a", id1);
assert_eq!("b", id2);
}
_ => panic!("Expected 2 identifiers"),
}
assert_eq!("==", op)
}
_ => panic!("Expected a binary expression with 2 identifiers"),
},
Err(err) => {
panic!("{:?}", err)
}
}
}
#[test]
fn should_not_parse_unfinished_comparison() {
let tokens = get_tokens(&String::from("a ==")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Err(ParsingError::Unmatched) => assert!(true),
_ => panic!("Expected an Unmatched error"),
}
}
}

View File

@ -1,86 +0,0 @@
use crate::{
lexic::token::Token,
syntax::{ast::Expression, ParsingError, ParsingResult},
};
/// Parses a factor expression.
///
/// ```ebnf
/// factor = unary, (("/" | "*"), unary)*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (unary, next_pos) = match super::unary::try_parse(tokens, pos) {
Ok((expr, next_pos)) => (expr, next_pos),
_ => return Err(ParsingError::Unmatched),
};
parse_many(tokens, next_pos, unary)
}
fn parse_many<'a>(
tokens: &'a Vec<Token>,
pos: usize,
prev_expr: Expression<'a>,
) -> ParsingResult<'a, Expression<'a>> {
// (("/" | "*"), unary)*
match tokens.get(pos) {
Some(token) if token.value == "/" || token.value == "*" => {
match super::unary::try_parse(tokens, pos + 1) {
Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
&token.value,
);
parse_many(tokens, next_pos, expr)
}
_ => Err(ParsingError::Unmatched),
}
}
_ => Ok((prev_expr, pos)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexic::get_tokens;
#[test]
fn should_parse_comparison() {
let tokens = get_tokens(&String::from("a * b")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Ok((expr, _)) => match expr {
Expression::BinaryOperator(exp1, exp2, op) => {
match (*exp1, *exp2) {
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
assert_eq!("a", id1);
assert_eq!("b", id2);
}
_ => panic!("Expected 2 identifiers"),
}
assert_eq!("*", op)
}
_ => panic!("Expected a binary expression with 2 identifiers"),
},
Err(err) => {
panic!("{:?}", err)
}
}
}
#[test]
fn should_not_parse_unfinished_comparison() {
let tokens = get_tokens(&String::from("a /")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Err(ParsingError::Unmatched) => assert!(true),
_ => panic!("Expected an Unmatched error"),
}
}
}

View File

@ -1,19 +0,0 @@
use super::{ast::Expression, ParsingResult};
use crate::lexic::token::Token;
mod comparison;
mod equality;
mod factor;
pub mod function_call_expr;
mod primary;
mod term;
mod unary;
/// Expression is defined in the grammar.
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
// TODO: This must be newline/indentation aware
equality::try_parse(tokens, pos)
}
#[cfg(test)]
mod tests {}

View File

@ -3,6 +3,7 @@ use crate::{
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
syntax::{ syntax::{
ast::{functions::ArgumentsList, Expression}, ast::{functions::ArgumentsList, Expression},
parseable::Parseable,
utils::parse_token_type, utils::parse_token_type,
ParsingError, ParsingResult, ParsingError, ParsingResult,
}, },
@ -22,15 +23,14 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Argume
let mut arguments = Vec::<Expression>::new(); let mut arguments = Vec::<Expression>::new();
loop { loop {
let (next_expression, next_pos) = let (next_expression, next_pos) = match Expression::try_parse(tokens, current_pos) {
match super::super::expression::try_parse(tokens, current_pos) { Ok((expression, next_pos)) => (expression, next_pos),
Ok((expression, next_pos)) => (expression, next_pos), Err(ParsingError::Err(error)) => {
Err(ParsingError::Err(error)) => { // TODO: Write a more detailed error
// TODO: Write a more detailed error return Err(ParsingError::Err(error));
return Err(ParsingError::Err(error)); }
} _ => break,
_ => break, };
};
current_pos = next_pos; current_pos = next_pos;
arguments.push(next_expression); arguments.push(next_expression);

View File

@ -1,6 +1,5 @@
use crate::error_handling::MistiError; use crate::error_handling::MistiError;
mod expression;
mod functions; mod functions;
mod parseable; mod parseable;
mod parsers; mod parsers;

View File

@ -101,21 +101,58 @@ mod tests {
assert_eq!(0, block.members.len()) assert_eq!(0, block.members.len())
} }
// TODO: rewrite, refactor
/*
#[test] #[test]
fn test_parse_block() { fn should_parse_block_with_fn() {
let tokens = get_tokens(&String::from("{f()}")).unwrap(); let tokens = get_tokens(&String::from("{\n fun f(){}\n}")).unwrap();
let block = parse_block(&tokens, 0); let (block, next_pos) = Block::try_parse(&tokens, 0).unwrap();
let block = match block { assert_eq!(12, next_pos);
ParsingResult::Ok((p, _)) => p, assert_eq!(1, block.members.len());
_ => panic!("Expected a block, got: {:?}", block),
};
assert_eq!(block.statements.len(), 1); let member = &block.members[0];
match member {
BlockMember::Stmt(Statement::FnDecl(f)) => {
assert_eq!(f.identifier.value, "f");
}
_ => panic!("Expected a function declaration, got {:?}", member),
}
} }
#[test]
fn should_parse_block_with_fn_2() {
let tokens = get_tokens(&String::from("{\n fun f(){}\nfun g(){}\n}")).unwrap();
let (block, next_pos) = Block::try_parse(&tokens, 0).unwrap();
assert_eq!(19, next_pos);
assert_eq!(2, block.members.len());
let member = &block.members[0];
match member {
BlockMember::Stmt(Statement::FnDecl(f)) => {
assert_eq!(f.identifier.value, "f");
}
_ => panic!("Expected a function declaration, got {:?}", member),
}
let member = &block.members[1];
match member {
BlockMember::Stmt(Statement::FnDecl(f)) => {
assert_eq!(f.identifier.value, "g");
}
_ => panic!("Expected a function declaration, got {:?}", member),
}
}
// TODO: rewrite, refactor
#[test]
fn should_parse_simple_expression() {
let tokens = get_tokens(&String::from("{f()}")).unwrap();
let (block, _) = Block::try_parse(&tokens, 0).unwrap();
assert_eq!(block.members.len(), 1);
}
/*
#[test] #[test]
fn test_parse_block_2() { fn test_parse_block_2() {
let tokens = get_tokens(&String::from("{f()\ng()}")).unwrap(); let tokens = get_tokens(&String::from("{f()\ng()}")).unwrap();

View File

@ -1,16 +0,0 @@
use crate::{
lexic::token::Token,
syntax::{
ast::Expression,
expression,
parseable::{Parseable, ParsingResult},
},
};
impl<'a> Parseable<'a> for Expression<'a> {
type Item = Expression<'a>;
fn try_parse(tokens: &'a Vec<Token>, current_pos: usize) -> ParsingResult<'a, Self::Item> {
expression::try_parse(tokens, current_pos)
}
}

View File

@ -0,0 +1,185 @@
use crate::{
handle_dedentation, handle_indentation, lexic::token::{Token, TokenType}, syntax::{ast::Expression, ParsingError, ParsingResult}
};
/// Parses a factor expression.
///
/// ```ebnf
/// equality = comparison, (("==" | "!="), comparison )*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (comparison, next_pos) = match super::comparison::try_parse(tokens, pos) {
Ok((expr, next_pos)) => (expr, next_pos),
_ => return Err(ParsingError::Unmatched),
};
parse_many(tokens, next_pos, comparison, 0)
}
fn parse_many<'a>(
tokens: &'a Vec<Token>,
pos: usize,
prev_expr: Expression<'a>,
indentation_level: u32,
) -> ParsingResult<'a, Expression<'a>> {
// equality = comparison, (("==" | "!="), comparison )*;
let mut indent_count: u32 = 0;
let mut next_pos = pos;
// Handle possible indentation before binary operator
handle_indentation!(tokens, next_pos, indent_count, indentation_level);
let result = match tokens.get(next_pos) {
Some(token) if token.value == "==" || token.value == "!=" => {
next_pos += 1;
// Handle possible indentation after binary operator
handle_indentation!(tokens, next_pos, indent_count, indentation_level);
match super::comparison::try_parse(tokens, next_pos) {
Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
&token.value,
);
parse_many(tokens, next_pos, expr, indentation_level + indent_count)
}
_ => return Err(ParsingError::Unmatched),
}
}
_ => return Ok((prev_expr, pos)),
};
let (new_expr, mut next_pos) = match result {
Ok((e, n)) => (e, n),
_ => return result,
};
handle_dedentation!(tokens, next_pos, indent_count);
Ok((new_expr, next_pos))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexic::get_tokens;
#[test]
fn should_parse_comparison() {
let tokens = get_tokens(&String::from("a == b")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Ok((expr, _)) => match expr {
Expression::BinaryOperator(exp1, exp2, op) => {
match (*exp1, *exp2) {
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
assert_eq!("a", id1);
assert_eq!("b", id2);
}
_ => panic!("Expected 2 identifiers"),
}
assert_eq!("==", op)
}
_ => panic!("Expected a binary expression with 2 identifiers"),
},
Err(err) => {
panic!("{:?}", err)
}
}
}
#[test]
fn should_not_parse_unfinished_comparison() {
let tokens = get_tokens(&String::from("a ==")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Err(ParsingError::Unmatched) => assert!(true),
_ => panic!("Expected an Unmatched error"),
}
}
#[test]
fn should_parse_indented_1() {
let tokens = get_tokens(&String::from("a\n == b")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(tokens[5].token_type, TokenType::DEDENT);
assert_eq!(next, 6);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "==")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_2() {
let tokens = get_tokens(&String::from("a\n == b\n == c")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(tokens[9].token_type, TokenType::DEDENT);
assert_eq!(tokens[10].token_type, TokenType::DEDENT);
assert_eq!(next, 11);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "==")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_3() {
let tokens = get_tokens(&String::from("a\n == b == c")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(tokens[7].token_type, TokenType::DEDENT);
assert_eq!(next, 8);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "==")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_4() {
let tokens = get_tokens(&String::from("a\n == b\n == c")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(next, 9);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "==")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_5() {
let tokens = get_tokens(&String::from("a ==\n b")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(next, 6);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "==")
}
_ => panic!("Expected a binary operator"),
}
}
}

View File

@ -0,0 +1,200 @@
use crate::{
handle_dedentation, handle_indentation, lexic::token::{Token, TokenType}, syntax::{ast::Expression, ParsingError, ParsingResult}
};
/// Parses a factor expression.
///
/// ```ebnf
/// factor = unary, (("/" | "*"), unary)*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (unary, next_pos) = match super::unary::try_parse(tokens, pos) {
Ok((expr, next_pos)) => (expr, next_pos),
_ => return Err(ParsingError::Unmatched),
};
parse_many(tokens, next_pos, unary, 0)
}
fn parse_many<'a>(
tokens: &'a Vec<Token>,
pos: usize,
prev_expr: Expression<'a>,
indentation_level: u32,
) -> ParsingResult<'a, Expression<'a>> {
// (("/" | "*"), unary)*
let mut indent_count: u32 = 0;
let mut next_pos = pos;
// Handle possible indentation before binary operator
handle_indentation!(tokens, next_pos, indent_count, indentation_level);
let result = match tokens.get(next_pos) {
Some(token) if token.value == "/" || token.value == "*" => {
next_pos += 1;
// Handle possible indentation after binary operator
handle_indentation!(tokens, next_pos, indent_count, indentation_level);
match super::unary::try_parse(tokens, next_pos) {
Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator(
Box::new(prev_expr),
Box::new(expr),
&token.value,
);
parse_many(tokens, next_pos, expr, indentation_level + indent_count)
}
_ => return Err(ParsingError::Unmatched),
}
}
_ => return Ok((prev_expr, pos)),
};
let (new_expr, mut next_pos) = match result {
Ok((e, n)) => (e, n),
_ => return result,
};
handle_dedentation!(tokens, next_pos, indent_count);
Ok((new_expr, next_pos))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexic::get_tokens;
#[test]
fn should_parse_comparison() {
let tokens = get_tokens(&String::from("a * b")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Ok((expr, _)) => match expr {
Expression::BinaryOperator(exp1, exp2, op) => {
match (*exp1, *exp2) {
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
assert_eq!("a", id1);
assert_eq!("b", id2);
}
_ => panic!("Expected 2 identifiers"),
}
assert_eq!("*", op)
}
_ => panic!("Expected a binary expression with 2 identifiers"),
},
Err(err) => {
panic!("{:?}", err)
}
}
}
#[test]
fn should_not_parse_unfinished_comparison() {
let tokens = get_tokens(&String::from("a /")).unwrap();
let result = try_parse(&tokens, 0);
match result {
Err(ParsingError::Unmatched) => assert!(true),
_ => panic!("Expected an Unmatched error"),
}
}
#[test]
fn should_parse_indented_1() {
let tokens = get_tokens(&String::from("a\n * b")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(tokens[5].token_type, TokenType::DEDENT);
assert_eq!(next, 6);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "*")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_2() {
let tokens = get_tokens(&String::from("a\n * b\n * c")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(tokens[9].token_type, TokenType::DEDENT);
assert_eq!(tokens[10].token_type, TokenType::DEDENT);
assert_eq!(next, 11);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "*")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_3() {
let tokens = get_tokens(&String::from("a\n * b * c")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(tokens[7].token_type, TokenType::DEDENT);
assert_eq!(next, 8);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "*")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_4() {
let tokens = get_tokens(&String::from("a\n * b\n * c")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(next, 9);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "*")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_5() {
let tokens = get_tokens(&String::from("a /\n b")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(next, 6);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "/")
}
_ => panic!("Expected a binary operator"),
}
}
#[test]
fn should_parse_indented_6() {
let tokens = get_tokens(&String::from("a\n /\n b")).unwrap();
let (result, next) = try_parse(&tokens, 0).unwrap();
assert_eq!(next, 9);
match result {
Expression::BinaryOperator(_, _, op) => {
assert_eq!(op, "/")
}
_ => panic!("Expected a binary operator"),
}
}
}

View File

@ -0,0 +1,37 @@
use super::super::{ast::Expression, ParsingResult};
use crate::{lexic::token::Token, syntax::parseable::Parseable};
mod comparison;
mod equality;
mod factor;
pub mod function_call_expr;
mod primary;
mod term;
mod unary;
mod utils;
impl<'a> Parseable<'a> for Expression<'a> {
type Item = Expression<'a>;
fn try_parse(tokens: &'a Vec<Token>, current_pos: usize) -> ParsingResult<'a, Self::Item> {
// TODO: This must be newline/indentation aware
equality::try_parse(tokens, current_pos)
}
}
#[cfg(test)]
mod tests {
use crate::lexic::get_tokens;
use super::*;
#[test]
fn should_parse_expression_w_indentation_1() {
let tokens = get_tokens(&String::from("a\n == b")).unwrap();
let (expr, _) = Expression::try_parse(&tokens, 0).unwrap();
match expr {
Expression::BinaryOperator(_e1, _e2, _op) => {}
_ => panic!("Expected a binary operation"),
}
}
}

View File

@ -1,7 +1,8 @@
use super::super::utils::Tokenizer;
use crate::{ use crate::{
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
syntax::{ast::Expression, ParsingError, ParsingResult}, syntax::{
ast::Expression, parseable::Parseable, utils::Tokenizer, ParsingError, ParsingResult,
},
}; };
/// This grammar may not be up to date. Refer to the spec for the latest grammar. /// This grammar may not be up to date. Refer to the spec for the latest grammar.
@ -27,7 +28,7 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
} }
fn parse_parenthesized_expression(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> { fn parse_parenthesized_expression(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let expression = super::try_parse(tokens, pos + 1); let expression = Expression::try_parse(tokens, pos + 1);
match expression { match expression {
Ok((expression, next_pos)) => match tokens.get(next_pos) { Ok((expression, next_pos)) => match tokens.get(next_pos) {
Some(token) => match token.token_type { Some(token) => match token.token_type {

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ast::Expression, ParsingError, ParsingResult}, syntax::{ast::Expression, parseable::Parseable, ParsingError, ParsingResult},
}; };
use super::function_call_expr; use super::function_call_expr;
@ -14,7 +14,7 @@ use super::function_call_expr;
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
match tokens.get(pos) { match tokens.get(pos) {
Some(token) if token.value == "!" || token.value == "-" => { Some(token) if token.value == "!" || token.value == "-" => {
match super::try_parse(tokens, pos + 1) { match Expression::try_parse(tokens, pos + 1) {
Ok((expression, next_pos)) => Ok(( Ok((expression, next_pos)) => Ok((
Expression::UnaryOperator(&token.value, Box::new(expression)), Expression::UnaryOperator(&token.value, Box::new(expression)),
next_pos, next_pos,

View File

@ -0,0 +1,43 @@
/// macro for handling indentation in expressions
#[macro_export]
macro_rules! handle_indentation {
($tokens: ident, $next_pos: ident, $indent_count: ident, $indentation_level: ident) => {
match ($tokens.get($next_pos), $tokens.get($next_pos + 1)) {
// New indentation level
(Some(t1), Some(t2))
if t1.token_type == TokenType::NewLine && t2.token_type == TokenType::INDENT =>
{
// set indentation
$next_pos += 2;
$indent_count += 1;
}
// we are indented, ignore newlines
(Some(t), _) if t.token_type == TokenType::NewLine && $indentation_level > 0 => {
$next_pos += 1;
}
// let other handlers handle this
_ => {}
};
};
}
/// macro for handling dedentation in expressions
#[macro_export]
macro_rules! handle_dedentation {
($tokens: ident, $next_pos: ident, $indent_count: ident) => {
for _ in 0..$indent_count {
// Expect a DEDENT for each indentation matched
match $tokens.get($next_pos) {
// continue
Some(t) if t.token_type == TokenType::DEDENT => {}
// This should be unreachable, as the lexer always emits a DEDENT for each INDENT
_ => unreachable!(
"Illegal parser state: Expected DEDENT (count: {})",
$indent_count
),
};
$next_pos += 1;
}
};
}

View File

@ -101,7 +101,6 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
current_pos = next_pos; current_pos = next_pos;
// Function body (block) // Function body (block)
// TODO: impl Parseable
let (block, next_pos) = match Block::try_parse(tokens, current_pos) { let (block, next_pos) = match Block::try_parse(tokens, current_pos) {
Ok((block, next_pos)) => (block, next_pos), Ok((block, next_pos)) => (block, next_pos),
Err(ParsingError::Err(error)) => { Err(ParsingError::Err(error)) => {

View File

@ -4,6 +4,8 @@ use super::{ParsingError, ParsingResult};
pub trait Tokenizer { pub trait Tokenizer {
fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)>; fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)>;
fn get_indented<'a>(&'a self, index: usize, indented: bool) -> (Option<&'a Token>, usize);
} }
impl Tokenizer for Vec<Token> { impl Tokenizer for Vec<Token> {
@ -28,6 +30,31 @@ impl Tokenizer for Vec<Token> {
} }
} }
} }
fn get_indented<'a>(&'a self, index: usize, indented: bool) -> (Option<&'a Token>, usize) {
if !indented {
return (self.get(index), index + 1);
}
let mut current_pos = index;
// Ignore all whitespace and newlines
loop {
match self.get(current_pos) {
Some(token) => {
if token.token_type == TokenType::INDENT
|| token.token_type == TokenType::DEDENT
|| token.token_type == TokenType::NewLine
{
current_pos += 1;
} else {
return (Some(token), current_pos);
}
}
None => return (None, index + 1),
}
}
}
} }
/// Expects the token at `pos` to be an operator of value `operator`. Doesn't ignore whitespace or newlines /// Expects the token at `pos` to be an operator of value `operator`. Doesn't ignore whitespace or newlines