wip:
Add type system and refator for having only epression
This commit is contained in:
+27
-29
@@ -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"),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user