Files
aster/CLAUDE.md
T
2026-06-16 16:55:43 +08:00

59 lines
3.4 KiB
Markdown

# 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<Token>, Vec<RuntimeError>)`. Tokens carry `line`/`column` for error reporting. Supports `//` single-line comments. |
| `parser/` | Recursive-descent parser (Pratt-style for expressions). `Parser::parse()` returns `(Vec<Stmt>, Vec<RuntimeError>)`. 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<RefCell<Env>>`. `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<RefCell<>> for shared ownership.** `Env` chains, `Value::Object`, `Value::Array`, and `Value::Function` all use `Rc<RefCell<>>` 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)