Make environment generic and adapt closures

Added unit test
This commit is contained in:
Giulio Agostini
2026-06-29 20:50:29 +02:00
parent 4e2645e12d
commit 29c86d278d
30 changed files with 820 additions and 30 deletions
Regular → Executable
+155 -4
View File
@@ -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());
}
}