# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build & Run ```bash # Build cargo build # Run REPL (interactive) cargo run # Execute a script file cargo run -- examples/script.ast # Run tests (none yet; the project has no test/ directory) cargo test ``` This is a pure-Rust project with no external dependencies (`edition = "2024"`). ## Architecture **Aster** is a tree-walking interpreter for a custom dynamically-typed scripting language. The pipeline follows the classic compiler frontend pattern: ``` Source text → Lexer → Tokens → Parser → AST → Interpreter → Output ``` ### Module map | Module | Responsibility | |---|---| | `lexer/` | Tokenizer. `Lexer::tokenize()` takes `&str` and returns `(Vec, Vec)`. Tokens carry `line`/`column` for error reporting. Supports `//` single-line comments. | | `parser/` | Recursive-descent parser (Pratt-style for expressions). `Parser::parse()` returns `(Vec, Vec)`. Uses `synchronize()` for error recovery — skips to the next statement boundary on parse failure. | | `ast/` | AST node definitions. `Expr` (in `expr.rs`) covers literals, variables, assignment, property access/indexing, unary/binary/logical ops, calls, lambdas, and object/array literals. `Stmt` (in `stmt.rs`) covers let, expression stmts, blocks, if/while/for, functions, return/break/continue. | | `interpreter/` | Tree-walking evaluator. `Interpreter` holds an `env: Rc>`. `Env` is a linked-list of scopes for lexical nesting. The `Signal` enum propagates `Return`/`Break`/`Continue` through the call stack. `builtins/` registers native functions (`print`, `input`, `clock`) organized as `io` and `os` modules (also available as global functions). | | `error/` | Three error variants: `LexError` (line+column), `ParseError` (token), `RuntimeError` (optional token). All implement `Display` and `std::error::Error`. | | `main.rs` | Two modes: `run_file` (parse-then-execute, exits with code 65 on lex/parse errors, 70 on runtime errors) and `run_repl` (per-line evaluation with `:exit`/`:reset` commands). | ### Key design decisions - **No external crate dependencies.** Everything is built on Rust's stdlib. - **Rc> for shared ownership.** `Env` chains, `Value::Object`, `Value::Array`, and `Value::Function` all use `Rc>` for interior mutability — matching the dynamic, garbage-collected feel without a GC. - **Error-tolerant parsing.** Both lexer and parser collect errors into a `Vec` rather than failing on the first error, so multiple diagnostics can be reported per run. - **Closures capture by environment.** `Function` structs store the `Env` at definition time; lambda expressions (`fn(params) { body }`) work the same way. - **Compound assignment operators** (`+=`, `-=`, `*=`, `/=`, `%=`) are parsed into `Expr::Assign`/`Set`/`IndexSet` with an `AssignOp` variant and evaluated by `apply_assign_op`. ### Language syntax overview Variables: `let x = 42;` Functions: `fn add(a, b) { return a + b; }` Closures: `fn(x, y) { return x + y; }` Control flow: `if`/`while`/`for` (C-style), `break`/`continue`, `return` Data: objects `{ key: val }`, arrays `[1, 2, 3]`, indexing `arr[i]`, property access `obj.prop` Operators: `+ - * / %`, `== != < > <= >=`, `&& || !`, `= += -= *= /= %=` Builtins: `print(...)`, `input(prompt?)`, `os.clock()` (also `clock()` directly)