refactor: new strategy to handle indentation around binary op
This commit is contained in:
parent
56ea63cf8c
commit
d08019c010
@ -1,4 +1,5 @@
|
|||||||
use crate::lexic::token::TokenType;
|
use crate::lexic::token::TokenType;
|
||||||
|
use crate::syntax::parsers::expression::utils::try_binary_op;
|
||||||
use crate::{
|
use crate::{
|
||||||
handle_dedentation, handle_indentation,
|
handle_dedentation, handle_indentation,
|
||||||
lexic::token::Token,
|
lexic::token::Token,
|
||||||
@ -27,33 +28,21 @@ fn parse_many<'a>(
|
|||||||
) -> ParsingResult<'a, Expression<'a>> {
|
) -> ParsingResult<'a, Expression<'a>> {
|
||||||
// term = factor, (("-" | "+"), factor)*;
|
// term = factor, (("-" | "+"), factor)*;
|
||||||
|
|
||||||
let mut indent_count: u32 = 0;
|
let (token, next_pos, indent_count) =
|
||||||
let mut next_pos = pos;
|
match try_binary_op(tokens, pos, vec!["+", "-"], indentation_level) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => return Ok((prev_expr, pos)),
|
||||||
|
};
|
||||||
|
|
||||||
// Handle possible indentation before binary operator
|
// Parse the next factor
|
||||||
handle_indentation!(tokens, next_pos, indent_count, indentation_level);
|
let result = match super::factor::try_parse(tokens, next_pos) {
|
||||||
|
Ok((expr, next_pos)) => {
|
||||||
|
let expr =
|
||||||
|
Expression::BinaryOperator(Box::new(prev_expr), Box::new(expr), &token.value);
|
||||||
|
|
||||||
let result = match tokens.get(next_pos) {
|
parse_many(tokens, next_pos, expr, indentation_level + indent_count)
|
||||||
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::factor::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)),
|
_ => return Err(ParsingError::Unmatched),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (new_expr, mut next_pos) = match result {
|
let (new_expr, mut next_pos) = match result {
|
||||||
|
@ -1,3 +1,57 @@
|
|||||||
|
use crate::lexic::token::Token;
|
||||||
|
use crate::lexic::token::TokenType::{NewLine, INDENT};
|
||||||
|
|
||||||
|
/// Attempts to parse a binary operator and handles indentation
|
||||||
|
///
|
||||||
|
/// Binary operators may be in a new line as long as they are indented.
|
||||||
|
/// The new line may be before or after the operator.
|
||||||
|
///
|
||||||
|
/// Once an operator is indented, all following operators completely disregard newline/indentation
|
||||||
|
/// until a matching dedent is found.
|
||||||
|
pub fn try_binary_op<'a>(
|
||||||
|
tokens: &'a Vec<Token>,
|
||||||
|
pos: usize,
|
||||||
|
operators: Vec<&str>,
|
||||||
|
indentation_level: u32,
|
||||||
|
) -> Option<(&'a Token, usize, u32)> {
|
||||||
|
let mut indent_count = 0;
|
||||||
|
|
||||||
|
// handle possible opening indentation
|
||||||
|
let pos = match (tokens.get(pos), tokens.get(pos + 1)) {
|
||||||
|
// New indentation level
|
||||||
|
(Some(t1), Some(t2)) if t1.token_type == NewLine && t2.token_type == INDENT => {
|
||||||
|
indent_count += 1;
|
||||||
|
pos + 2
|
||||||
|
}
|
||||||
|
// when indented, ignore newlines
|
||||||
|
(Some(t), _) if t.token_type == NewLine && indentation_level > 0 => pos + 1,
|
||||||
|
// let other handlers handle this
|
||||||
|
_ => pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
// try to parse binary operator
|
||||||
|
let (matched_token, pos) = match tokens.get(pos) {
|
||||||
|
Some(token) if operators.contains(&token.value.as_str()) => (token, pos + 1),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle possible closing indentation
|
||||||
|
let pos = match (tokens.get(pos), tokens.get(pos + 1)) {
|
||||||
|
// New indentation level
|
||||||
|
(Some(t1), Some(t2)) if t1.token_type == NewLine && t2.token_type == INDENT => {
|
||||||
|
indent_count += 1;
|
||||||
|
pos + 2
|
||||||
|
}
|
||||||
|
// when indented, ignore newlines
|
||||||
|
(Some(t), _) if t.token_type == NewLine && indentation_level > 0 => pos + 1,
|
||||||
|
// let other handlers handle this
|
||||||
|
_ => pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
// return the matched token, next position and new indentation level
|
||||||
|
Some((matched_token, pos, indent_count))
|
||||||
|
}
|
||||||
|
|
||||||
/// macro for handling indentation in expressions
|
/// macro for handling indentation in expressions
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! handle_indentation {
|
macro_rules! handle_indentation {
|
||||||
|
@ -31,6 +31,7 @@ impl Tokenizer for Vec<Token> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unused? remove?
|
||||||
fn get_indented<'a>(&'a self, index: usize, indented: bool) -> (Option<&'a Token>, usize) {
|
fn get_indented<'a>(&'a self, index: usize, indented: bool) -> (Option<&'a Token>, usize) {
|
||||||
if !indented {
|
if !indented {
|
||||||
return (self.get(index), index + 1);
|
return (self.get(index), index + 1);
|
||||||
|
Loading…
Reference in New Issue
Block a user