Make environment generic and adapt closures
Added unit test
This commit is contained in:
Regular → Executable
+155
-4
@@ -205,6 +205,10 @@ impl Parser {
|
||||
|
||||
let mut _type_annotation = BaseValue::Nil;
|
||||
self.consume(TokenType::Colon, "Expect column")?;
|
||||
if self.peek().token_type == TokenType::Identifier {
|
||||
// TODO manage type annotation
|
||||
self.consume(TokenType::Identifier, "Expect type name.")?;
|
||||
}
|
||||
|
||||
match self.peek().token_type {
|
||||
TokenType::Equal | TokenType::Identifier => {
|
||||
@@ -374,11 +378,7 @@ impl Parser {
|
||||
|
||||
while self.peek().token_type != TokenType::EndBlock && !self.is_at_end() {
|
||||
let stmt = self.statement(label.clone())?;
|
||||
let should_break = matches!(stmt.node, Stmt::Expression { .. });
|
||||
statements.push(stmt);
|
||||
if should_break {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let end_token = self.consume(TokenType::EndBlock, "Expect 'end' after block.")?;
|
||||
let end_slice = end_token.source_slice.clone();
|
||||
@@ -864,3 +864,154 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::common::base_value::Number;
|
||||
use crate::frontend::lexer::Lexer;
|
||||
|
||||
/// Lex and parse `src`, returning the parser's result.
|
||||
fn parse_source(src: &str) -> LoxResult<Vec<AstNode<Stmt>>> {
|
||||
let tokens = Lexer::new(src.to_string(), 0)
|
||||
.scans_tokens()
|
||||
.expect("source should lex without errors");
|
||||
Parser::new(tokens).parse()
|
||||
}
|
||||
|
||||
/// Parse `src`, panicking if parsing fails.
|
||||
fn parse_ok(src: &str) -> Vec<AstNode<Stmt>> {
|
||||
parse_source(src).expect("expected source to parse")
|
||||
}
|
||||
|
||||
/// Extract the inner expression of an expression statement.
|
||||
fn expression_of(stmt: &AstNode<Stmt>) -> &AstNode<Expr> {
|
||||
match &stmt.node {
|
||||
Stmt::Expression { expression, .. } => expression,
|
||||
other => panic!("expected expression statement, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_number_literal_expression() {
|
||||
let stmts = parse_ok("42;");
|
||||
assert_eq!(stmts.len(), 1);
|
||||
match &expression_of(&stmts[0]).node {
|
||||
Expr::Literal { value } => {
|
||||
assert_eq!(*value, BaseValue::Number(Number::I32(42)));
|
||||
}
|
||||
other => panic!("expected literal, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn respects_multiplication_precedence_over_addition() {
|
||||
// 1 + 2 * 3 should parse as 1 + (2 * 3)
|
||||
let stmts = parse_ok("1 + 2 * 3;");
|
||||
match &expression_of(&stmts[0]).node {
|
||||
Expr::Binary {
|
||||
operator, right, ..
|
||||
} => {
|
||||
assert_eq!(*operator, TokenType::Plus);
|
||||
match &right.node {
|
||||
Expr::Binary { operator, .. } => {
|
||||
assert_eq!(*operator, TokenType::Star)
|
||||
}
|
||||
other => panic!("expected nested binary, got {:?}", other),
|
||||
}
|
||||
}
|
||||
other => panic!("expected binary expression, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_unary_negation() {
|
||||
let stmts = parse_ok("-5;");
|
||||
match &expression_of(&stmts[0]).node {
|
||||
Expr::Unary { operator, .. } => assert_eq!(*operator, TokenType::Minus),
|
||||
other => panic!("expected unary expression, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_grouping_to_change_precedence() {
|
||||
// (1 + 2) * 3 should have a grouping on the left of the multiply.
|
||||
let stmts = parse_ok("(1 + 2) * 3;");
|
||||
match &expression_of(&stmts[0]).node {
|
||||
Expr::Binary { operator, left, .. } => {
|
||||
assert_eq!(*operator, TokenType::Star);
|
||||
assert!(matches!(left.node, Expr::Grouping { .. }));
|
||||
}
|
||||
other => panic!("expected binary expression, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_comparison_expression() {
|
||||
let stmts = parse_ok("1 < 2;");
|
||||
match &expression_of(&stmts[0]).node {
|
||||
Expr::Binary { operator, .. } => assert_eq!(*operator, TokenType::Less),
|
||||
other => panic!("expected binary expression, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_print_statement() {
|
||||
let stmts = parse_ok("print 1;");
|
||||
assert!(matches!(stmts[0].node, Stmt::Print { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_var_declaration_with_initializer() {
|
||||
let stmts = parse_ok("var x: Int = 5;");
|
||||
match &stmts[0].node {
|
||||
Stmt::VarDeclaration {
|
||||
name, initializer, ..
|
||||
} => {
|
||||
assert_eq!(name, "x");
|
||||
assert!(initializer.is_some());
|
||||
}
|
||||
other => panic!("expected var declaration, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_assignment_statement() {
|
||||
let stmts = parse_ok("x = 5;");
|
||||
match &stmts[0].node {
|
||||
Stmt::VarAssigment { name, .. } => assert_eq!(name, "x"),
|
||||
other => panic!("expected assignment statement, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_block_statement() {
|
||||
let stmts = parse_ok("do print 1; print 2; end");
|
||||
match &stmts[0].node {
|
||||
Stmt::Block { statements, .. } => assert_eq!(statements.len(), 2),
|
||||
other => panic!("expected block statement, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_if_statement() {
|
||||
let stmts = parse_ok("if true then print 1;");
|
||||
assert!(matches!(stmts[0].node, Stmt::If { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_while_statement() {
|
||||
let stmts = parse_ok("while true do print 1; end");
|
||||
assert!(matches!(stmts[0].node, Stmt::While { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_on_missing_semicolon_after_print() {
|
||||
assert!(parse_source("print 1").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_on_unclosed_grouping() {
|
||||
assert!(parse_source("(1 + 2;").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user