feat: handle indent/dedent in == parsing
This commit is contained in:
parent
d568114349
commit
4d9c7fae3e
@ -15,17 +15,19 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
|
|||||||
_ => return Err(ParsingError::Unmatched),
|
_ => return Err(ParsingError::Unmatched),
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_many(tokens, next_pos, comparison)
|
parse_many(tokens, next_pos, comparison, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_many<'a>(
|
fn parse_many<'a>(
|
||||||
tokens: &'a Vec<Token>,
|
tokens: &'a Vec<Token>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
prev_expr: Expression<'a>,
|
prev_expr: Expression<'a>,
|
||||||
|
indentation_level: u32,
|
||||||
) -> ParsingResult<'a, Expression<'a>> {
|
) -> ParsingResult<'a, Expression<'a>> {
|
||||||
// equality = comparison, (("==" | "!="), comparison )*;
|
// equality = comparison, (("==" | "!="), comparison )*;
|
||||||
|
|
||||||
match tokens.get(pos) {
|
let mut indented = false;
|
||||||
|
let result = match tokens.get(pos) {
|
||||||
Some(token) if token.value == "==" || token.value == "!=" => {
|
Some(token) if token.value == "==" || token.value == "!=" => {
|
||||||
match super::comparison::try_parse(tokens, pos + 1) {
|
match super::comparison::try_parse(tokens, pos + 1) {
|
||||||
Ok((expr, next_pos)) => {
|
Ok((expr, next_pos)) => {
|
||||||
@ -35,40 +37,47 @@ fn parse_many<'a>(
|
|||||||
&token.value,
|
&token.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
parse_many(tokens, next_pos, expr)
|
parse_many(tokens, next_pos, expr, indentation_level)
|
||||||
}
|
}
|
||||||
_ => Err(ParsingError::Unmatched),
|
_ => return Err(ParsingError::Unmatched),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If token is a newline: check if the following token is INDENT.
|
// Handle indentation
|
||||||
// If so, ignore those 2 and continue parsing
|
|
||||||
// Then, we should find a DEDENT token to finish this expression?
|
|
||||||
Some(token) if token.token_type == TokenType::NewLine => {
|
Some(token) if token.token_type == TokenType::NewLine => {
|
||||||
match tokens.get(pos + 1) {
|
let next_tok = match tokens.get(pos + 1) {
|
||||||
Some(t) if t.token_type == TokenType::INDENT => {
|
Some(t) => t,
|
||||||
// Ignore indentation and continue parsing
|
None => return Ok((prev_expr, pos)),
|
||||||
let result = parse_many(tokens, pos + 2, prev_expr);
|
};
|
||||||
// Expect a DEDENT token
|
let next_is_indent = next_tok.token_type == TokenType::INDENT;
|
||||||
match result {
|
|
||||||
Ok((expr, next)) => {
|
if next_is_indent {
|
||||||
match tokens.get(next) {
|
// increase indentation level and continue parsing
|
||||||
Some(t) if t.token_type == TokenType::DEDENT => {
|
indented = true;
|
||||||
Ok((expr, next + 1))
|
parse_many(tokens, pos + 2, prev_expr, indentation_level + 1)
|
||||||
}
|
} else if indentation_level > 0 {
|
||||||
_ => unreachable!("Invalid parser state: expected a DEDENT after parsing an indented expression")
|
// ignore the newline, as we are indented
|
||||||
}
|
parse_many(tokens, pos + 1, prev_expr, indentation_level)
|
||||||
}
|
} else {
|
||||||
_ => result
|
Ok((prev_expr, pos))
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Return current parsed value
|
|
||||||
return Ok((prev_expr, pos));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Ok((prev_expr, pos)),
|
_ => Ok((prev_expr, pos)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (new_expr, next_pos) = match result {
|
||||||
|
Ok((e, n)) => (e, n),
|
||||||
|
_ => return result,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Here expect dedents if there are any
|
||||||
|
if indented {
|
||||||
|
match tokens.get(next_pos) {
|
||||||
|
Some(t) if t.token_type == TokenType::DEDENT => return Ok((new_expr, next_pos + 1)),
|
||||||
|
_ => panic!("Expected DEDENT"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok((new_expr, next_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -123,8 +132,8 @@ mod tests {
|
|||||||
match result {
|
match result {
|
||||||
Expression::BinaryOperator(_, _, op) => {
|
Expression::BinaryOperator(_, _, op) => {
|
||||||
assert_eq!(op, "==")
|
assert_eq!(op, "==")
|
||||||
},
|
}
|
||||||
_ => panic!("Expected a binary operator")
|
_ => panic!("Expected a binary operator"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,8 +149,8 @@ mod tests {
|
|||||||
match result {
|
match result {
|
||||||
Expression::BinaryOperator(_, _, op) => {
|
Expression::BinaryOperator(_, _, op) => {
|
||||||
assert_eq!(op, "==")
|
assert_eq!(op, "==")
|
||||||
},
|
}
|
||||||
_ => panic!("Expected a binary operator")
|
_ => panic!("Expected a binary operator"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,9 +165,24 @@ mod tests {
|
|||||||
match result {
|
match result {
|
||||||
Expression::BinaryOperator(_, _, op) => {
|
Expression::BinaryOperator(_, _, op) => {
|
||||||
assert_eq!(op, "==")
|
assert_eq!(op, "==")
|
||||||
},
|
}
|
||||||
_ => panic!("Expected a binary operator")
|
_ => 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!(tokens[8].token_type, TokenType::DEDENT);
|
||||||
|
assert_eq!(next, 9);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Expression::BinaryOperator(_, _, op) => {
|
||||||
|
assert_eq!(op, "==")
|
||||||
|
}
|
||||||
|
_ => panic!("Expected a binary operator"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user