Add type system and refator for having only epression
This commit is contained in:
Giulio Agostini
2026-07-06 10:43:17 +02:00
parent 9f15a00b98
commit 842216729b
23 changed files with 940 additions and 979 deletions
+27 -29
View File
@@ -4,14 +4,14 @@
//! assert on the resulting AST, complementing the per-module unit tests inside
//! `src/frontend/{lexer,parser}.rs`.
use rlox::common::ast::{AstNode, Expr, Stmt};
use rlox::common::ast::{AstNode, Expr};
use rlox::common::base_value::{BaseValue, Number};
use rlox::frontend::lexer::Lexer;
use rlox::frontend::parser::Parser;
use rlox::frontend::tokens::TokenType;
/// Run the full frontend pipeline on `src`.
fn parse(src: &str) -> Result<Vec<AstNode<Stmt>>, String> {
fn parse(src: &str) -> Result<Vec<AstNode>, String> {
let tokens = Lexer::new(src.to_string(), 0)
.scans_tokens()
.map_err(|e| format!("lex error: {e}"))?;
@@ -21,23 +21,26 @@ fn parse(src: &str) -> Result<Vec<AstNode<Stmt>>, String> {
}
/// Parse `src`, panicking if the frontend reports any error.
fn parse_ok(src: &str) -> Vec<AstNode<Stmt>> {
fn parse_ok(src: &str) -> Vec<AstNode> {
parse(src).expect("expected source to lex and parse")
}
/// Parse `src` and return the single statement it should produce.
fn single_stmt(src: &str) -> Stmt {
/// Parse `src` and return the single node it should produce.
///
/// Statements and expressions share the unified `Expr`/`AstNode` type, so this
/// simply returns the node of the sole top-level statement.
fn single_stmt(src: &str) -> Expr {
let mut stmts = parse_ok(src);
assert_eq!(stmts.len(), 1, "expected exactly one statement for {src:?}");
stmts.remove(0).node
}
/// Parse `src` and return the expression of its single expression-statement.
/// Parse `src` and return its single expression-statement's expression.
///
/// Expression statements aren't wrapped in a dedicated variant anymore; the
/// node itself is the expression.
fn single_expr(src: &str) -> Expr {
match single_stmt(src) {
Stmt::Expression { expression, .. } => expression.node,
other => panic!("expected expression statement, got {other:?}"),
}
single_stmt(src)
}
// ---------------------------------------------------------------------------
@@ -47,7 +50,7 @@ fn single_expr(src: &str) -> Expr {
#[test]
fn lexes_and_parses_number_literal() {
match single_expr("42;") {
Expr::Literal { value } => assert_eq!(value, BaseValue::Number(Number::I32(42))),
Expr::Literal { value } => assert_eq!(*value, BaseValue::Number(Number::I32(42))),
other => panic!("expected literal, got {other:?}"),
}
}
@@ -194,7 +197,7 @@ fn assignment_to_non_identifier_is_an_error() {
#[test]
fn parses_var_declaration() {
match single_stmt("x := 5;") {
Stmt::VarDeclaration {
Expr::VarDeclaration {
name, initializer, ..
} => {
assert_eq!(name, "x");
@@ -206,13 +209,13 @@ fn parses_var_declaration() {
#[test]
fn parses_print_statement() {
assert!(matches!(single_stmt("print 1;"), Stmt::Print { .. }));
assert!(matches!(single_stmt("print 1;"), Expr::Print { .. }));
}
#[test]
fn parses_block_with_multiple_statements() {
match single_stmt("do print 1; print 2; end") {
Stmt::Block { statements, .. } => assert_eq!(statements.len(), 2),
Expr::Block { statements, .. } => assert_eq!(statements.len(), 2),
other => panic!("expected block, got {other:?}"),
}
}
@@ -220,7 +223,7 @@ fn parses_block_with_multiple_statements() {
#[test]
fn parses_if_else() {
match single_stmt("if true then print 1; else print 2;") {
Stmt::If {
Expr::If {
else_branch: Some(_),
..
} => {}
@@ -232,37 +235,32 @@ fn parses_if_else() {
fn parses_while_loop() {
assert!(matches!(
single_stmt("while true do print 1; end"),
Stmt::While { .. }
Expr::While { .. }
));
}
#[test]
fn parses_for_loop_with_assignment_increment() {
match single_stmt("for i := 0; i <= 2; i = i + 1; do print i; end") {
Stmt::For { increment, .. } => match increment.node {
// The increment desugars to an expression statement holding an assignment.
Stmt::Expression { expression, .. } => {
assert!(matches!(expression.node, Expr::Assign { .. }))
}
other => panic!("expected expression-statement increment, got {other:?}"),
},
Expr::For { increment, .. } => {
// The increment is an assignment expression.
assert!(matches!(increment.node, Expr::Assign { .. }))
}
other => panic!("expected for loop, got {other:?}"),
}
}
#[test]
fn parses_function_declaration() {
match single_stmt("add :: fn (a, b) do return a + b; end") {
Stmt::VarDeclaration {
match single_stmt("add :: fn (a, b): Number do return a + b; end;") {
Expr::VarDeclaration {
name, initializer, ..
} => {
assert_eq!(name, "add");
match initializer {
Some(init) => assert!(matches!(
init.node,
Expr::Literal {
value: BaseValue::Function(_)
}
&init.node,
Expr::Literal { value } if matches!(&**value, BaseValue::Function(_))
)),
None => panic!("expected a function initializer"),
}