Files
hakorune/docs/reference/language/repl.md
tomoaki 2b86b658d8 docs(repl): Phase 288 documentation cleanup
Updated documentation to reflect actual implementation status:
- docs/reference/language/repl.md: Corrected examples (variable persistence TODO)
- docs/development/current/main/10-Now.md: Phase 288 completion noted
- docs/development/current/main/30-Backlog.md: Updated with Phase 288.1 tasks
- docs/development/current/main/phases/phase-288/: Added phase documentation

Accurate REPL behavior examples:
- x = 1 works (implicit local)
- print(x) errors (persistence in Phase 288.1)
- Expression auto-display deferred

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 14:01:32 +09:00

358 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# REPL Mode (ReadEvalPrint Loop) — Specification (SSOT)
**Status**: Phase 288 MVP Implementation (2025-12-25) — partial (dev tool)
## Philosophy: Two Execution Contexts, One Language
Nyash provides **two execution contexts** for the same language:
1. **File Mode** - Strict, box-oriented, production-ready
- Forces explicit structure (`static box Main { main() { ... } }`)
- Prevents mutable globals at top-level
- Optimized for large, maintainable codebases
2. **REPL Mode** - Interactive, exploratory, development-friendly
- Implicit local variables for rapid iteration
- Persistent session scope across inputs
- Optimized for learning, debugging, and experimentation
**Key Principle**: Same parser, same language semantics. The difference is **binding rules** and **execution context**.
This document defines Nyash REPL mode semantics. The primary design goal is:
- **File mode** stays strict and box-oriented (no mutable globals at top-level).
- **REPL mode** is convenience-oriented (interactive, persistent session scope, implicit locals).
- **Parser stays the same**; the difference is primarily **binding (name resolution)** rules.
## Terms
- **Top-level**: Code that is not inside a function/method body.
- **File mode**: Normal execution of a `.hako`/Nyash file via the runner.
- **REPL mode**: Interactive execution session (future CLI: `--repl`).
- **Global variable (in this project)**: A mutable binding created at file top-level (e.g., `x = 1` or `local x = 1` at top-level).
## 1) File Mode vs REPL Mode (high-level contract)
### Starting REPL Mode
```bash
hakorune --repl # Full flag
hakorune -i # Short alias (interactive)
```
### File Mode (strict) - Code Example
```nyash
// File: program.hako
// ❌ ERROR: Top-level executable statements forbidden
x = 1
print("Hello")
local temp = 42
// ✅ ALLOWED: Declarations only
static box Main {
main() {
local x // ✅ Explicit declaration REQUIRED
x = 1
print(x)
return 0
}
}
```
**File Mode Rules**:
- Top-level allows **declarations only** (`box`, `static box`, `function`, `static function`, `using`)
- Top-level **statements are rejected** (Fail-Fast):
- Assignment (`x = 1`)
- `local` declarations (`local x = 1`)
- Expression statements (`f()`), `print(...)`, control flow, etc.
- **Rationale**: Prevents mutable globals; maintains "state lives in boxes" discipline
### REPL Mode (convenient) - Interactive Example
```nyash
>>> x = 1 // ✅ Implicitly creates session-level local
>>>
>>> print(x) // ⏳ Phase 288.1: session→eval bridge needed
Error: Undefined variable 'x'
>>> y // ❌ ERROR: Undefined variable (Fail-Fast)
Error: Undefined variable 'y'
Hint: Variable not defined. Assign a value first.
>>> local z = 3 // ✅ Explicit declaration also works
>>>
>>> print(z)
3
>>> 1 + 1 // ⏳ Phase 288.1: expression auto-display
>>> _ // ⏳ Phase 288.1: last value binding
>>> .reset // ✅ Clear session
Session reset
>>> print(x) // ❌ ERROR: Session cleared
Error: Undefined variable 'x'
```
**REPL Mode Rules**:
- The REPL has exactly one **persistent session scope**
- Session scope is conceptually a **lexical frame that persists across inputs**
- Assignments can create bindings implicitly (see §2)
- Reads of undefined names are errors (Fail-Fast; no silent `void`)
- **Implementation**: Session stores runtime values (`VMValue`), not MIR IDs
## Implementation Status (MVP vs planned)
Implemented in Phase 288 MVP:
- `hakorune --repl` / `-i`
- `.help`, `.exit/.quit`, `.reset`
- REPL-only session object exists (runtime-value based)
- File mode regression gate remains green
Planned for Phase 288.1:
- Session variables visible to subsequent compiled lines (`x = 1` then `print(x)`)
- Expression auto-display and `_` last-value binding
## 2) Binding Rules in REPL Mode
### 2.1 Implicit local on assignment (key feature)
When executing `name = expr` in REPL mode:
- If `name` already exists in the session scope, update it.
- If `name` does not exist, **create a new session binding** and assign to it.
This applies to compound assignments as well (if supported): `name += expr`, etc.
### 2.2 Reads are strict
When evaluating `name` in REPL mode:
- If `name` exists in the session scope, return its value.
- Otherwise, raise **NameError / undeclared variable** (Fail-Fast).
### 2.3 `local` is accepted but not required
REPL accepts `local name = expr` / `local name` as explicit declarations.
- Semantics: declare/update `name` in the session scope (same end result as implicit assignment).
- Guidance: `local` remains useful for clarity, but REPL users are not forced to write it.
## 3) Output Rules (REPL UX contract)
REPL output distinguishes expressions vs statements:
- If the input is an **expression**, print its value (pretty display) unless it is `void`.
- If the input is a **statement**, do not auto-print.
### 3.1 Convenience binding `_`
- `_` is bound to the **last auto-printed value** (expressions only).
- `_` is not updated when the value is `void`.
### 3.2 Output suppression (planned)
- A trailing `;` may suppress auto-print for expressions (planned; should be implemented without changing the core parser).
## 4) REPL Meta Commands
REPL supports dot-commands (not part of the language grammar):
- `.help` — show help
- `.exit` — exit the REPL
- `.reset` — clear the session scope (remove all bindings and definitions created in the session)
Additional commands may be added for debugging (e.g., `.ast`, `.mir`), but they must remain REPL-only and default-off for CI.
## 5) Implementation Contract (Phase 288 SSOT)
### 5.1 Architecture: Box-First Modularization
```
┌─────────────────────────────────────┐
│ Runner (src/runner/mod.rs) │
│ ┌───────────────────────────────┐ │
│ │ ReplSessionBox │ │ ← REPL専用state隔離
│ │ - variables: BTreeMap< │ │
│ │ String, VMValue> │ │ ← 実行時の値を永続化
│ │ - last_value: Option<VMValue> │ │
│ │ - eval_count: usize │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ MirBuilder (src/mir/builder.rs) │
│ - repl_mode: bool │ ← mode 分岐フラグのみ
│ (assignment_resolver.rs は不変) │ ← file mode 専用に保つ
└─────────────────────────────────────┘
```
### 5.2 Mode Flags
| Flag | File Mode | REPL Mode | Purpose |
|------|-----------|-----------|---------|
| `builder.repl_mode` | `false` | `true` | Implicit local 許可フラグ |
| `top_level_exec_allowed` | `false` | N/A | File mode: top-level 実行禁止 |
### 5.3 Session Persistence Contract
**File Mode**:
- Each file execution is independent
- No state persists across runs
- All variables are scoped to functions/boxes
**REPL Mode**:
- Session persists across line inputs
- Variables stored as `VMValue` (runtime values)
- **Critical**: NOT `ValueId` (MIR-compilation-specific, invalidated per line)
**Why VMValue?**
```rust
// ❌ WRONG: ValueId is per-compilation
session.variables: BTreeMap<String, ValueId> // Invalid across lines!
// ✅ CORRECT: VMValue is runtime value
session.variables: BTreeMap<String, VMValue> // Persists across compilations
```
### 5.4 Evaluation Pipeline (REPL)
```
User Input (line)
Parse → AST
MirCompiler (repl_mode=true)
MIR Module
MirInterpreter::execute_module()
VMValue (return value)
session.set_last_value(vm_value) // Store in session
Output (Phase 288.1: expression auto-display)
```
### 5.5 Fail-Fast Guarantee
**Both Modes**:
- Undefined variable reads → Immediate error
- No silent `void` or fallback values
- Clear error messages with hints
**File Mode Hint**:
```
Error: Undefined variable 'x'
Hint: Nyash requires explicit local declaration. Use `local <name>` before assignment.
```
**REPL Mode Hint**:
```
Error: Undefined variable 'x'
Hint: Variable not defined. Assign a value first.
```
## 6) Compatibility and `strip_local_decl` policy
Historical compatibility code exists that can strip top-level `local` from certain inputs.
SSOT policy:
- **File mode must not "strip and accept" top-level `local`** (would violate the "no globals" rule).
- If compatibility behavior is kept, it must be **REPL-only** and/or explicitly gated (e.g., `--compat`), with a stable warning tag.
## 7) Error Messages (Fail-Fast wording)
Recommended file-mode errors:
- `Error: top-level statements are not allowed in file mode. Put code inside Main.main() or run with --repl.`
- `Error: 'local' is not allowed at top-level in file mode. Use Main.main() or REPL mode.`
## 8) Minimal Conformance Tests (spec lock)
### File mode
1. `x = 1` at top-level → error (top-level statements not allowed)
2. `local x = 1` at top-level → error (local not allowed at top-level)
3. `print("hi")` at top-level → error
4. Declarations at top-level → OK
5. Statements inside `Main.main()` or `main()` → OK
### REPL mode
1. `x = 1` then `x` → prints `1`
2. `y` (undefined) → NameError
3. `x = 1; x = 2; x` → prints `2`
4. `local z = 3; z` → prints `3`
5. `x = 1; .reset; x` → NameError
---
## 9) Phase 288 MVP Implementation Status
### Completed (Phase 288 P0-P3) - 2025-12-25
**CLI Entry** (`--repl` / `-i` flags)
**REPL Loop** (`.help`, `.exit`, `.reset` commands)
**ReplSessionBox** (VMValue-based session state)
**Implicit Local Binding** (`x = 1` compiles without error)
**print() Output** (ExternCall output displays correctly)
**`_` Variable** (last value stored in session)
**Session Reset** (`.reset` command clears state)
**Main Box Entry Point** (VM execution fixed)
### Deferred to Phase 288.1+
**Variable Persistence** (session variables accessible across lines)
**Expression Auto-Display** (`1+1``2` automatic output)
**Complex Expression Detection** (distinguish expression vs statement)
**REPL Variable Injection** (session → compilation context bridge)
### Current Behavior (Phase 288 P3 MVP)
```nyash
>>> .help
Commands:
.exit / .quit - Exit REPL
.reset - Clear session
.help - Show this help
>>> x = 42
>>> # Silent (implicit local creation)
>>> print("Hello REPL!")
Hello REPL! # print() output displays
>>> print(42 + 1)
43 # Arithmetic works
>>> 1 + 1
>>> # Silent (expression auto-display in Phase 288.1)
>>> .reset
Session reset
>>> print("After reset")
After reset # REPL continues after reset
```
**Known Limitations** (Phase 288 MVP):
- Variable persistence not yet implemented (`x = 1` then `print(x)` errors)
- `_` variable stored but not accessible in user code yet
- No expression auto-display (deferred to Phase 288.1)
**Design Philosophy**: Follow 80/20 rule - deliver working REPL first, polish UX later.
---
**Document Version**: Phase 288 P0 (2025-12-25)
**Implementation**: Phase 288 P1-P3 (CLI + Session + Basic UX)
**Next Phase**: Phase 288.1 (Expression Auto-Display)