From 796089688a08b8840deb754a5f62d735f6f7a0bb Mon Sep 17 00:00:00 2001 From: tomoaki Date: Thu, 25 Dec 2025 13:24:32 +0900 Subject: [PATCH] docs(repl): Phase 288 P0 - Establish REPL SSOT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expanded docs/reference/language/repl.md (123→336 lines): - Philosophy: "Two execution contexts, one language" - File mode vs REPL mode: Concrete code examples - Implementation Contract: VMValue persistence (not ValueId) - Architecture: Box-First modularization (ReplSessionBox) - Evaluation Pipeline: Parse→Compile→Execute→Store VMValue - Phase 288 MVP status: P0-P3 completed, P288.1 deferred Key design decisions documented: - Session stores VMValue (runtime values, persist across lines) - NOT ValueId (MIR-specific, invalidated per compilation) - assignment_resolver.rs unchanged (file mode専用) - 80/20 rule: Working REPL first, expression auto-display later SSOT ensures implementation matches user expectations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- docs/reference/language/repl.md | 247 +++++++++++++++++++++++++++++--- 1 file changed, 230 insertions(+), 17 deletions(-) diff --git a/docs/reference/language/repl.md b/docs/reference/language/repl.md index 90b98127..9709f3c2 100644 --- a/docs/reference/language/repl.md +++ b/docs/reference/language/repl.md @@ -1,6 +1,22 @@ # REPL Mode (Read–Eval–Print Loop) — Specification (SSOT) -Status: Draft (design locked; implementation may lag) +**Status**: Phase 288 MVP Implementation (2025-12-25) + +## 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: @@ -17,24 +33,80 @@ This document defines Nyash REPL mode semantics. The primary design goal is: ## 1) File Mode vs REPL Mode (high-level contract) -### File mode (strict) +### Starting REPL Mode -- Top-level allows **declarations only** (e.g., `box`, `static box`, `function`, `static function`, `using`). +```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`) + - Assignment (`x = 1`) - `local` declarations (`local x = 1`) - - expression statements (`f()`), `print(...)`, control flow statements, etc. -- Rationale: prevents mutable globals; keeps “state lives in boxes” discipline. + - Expression statements (`f()`), `print(...)`, control flow, etc. +- **Rationale**: Prevents mutable globals; maintains "state lives in boxes" discipline -### REPL mode (convenient) +### REPL Mode (convenient) - Interactive Example -- 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`). +```nyash +>>> x = 1 // ✅ Implicitly creates session-level local +>>> -CLI entry (initial policy): -- Start explicitly with `hakorune --repl` (optional short alias: `-i`). +>>> print(x) // ✅ Reads from session scope +1 + +>>> 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 // ✅ Expression auto-display (Phase 288.1+) +2 + +>>> _ // ✅ Last value stored in _ variable +2 + +>>> .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 ## 2) Binding Rules in REPL Mode @@ -87,23 +159,113 @@ REPL supports dot-commands (not part of the language grammar): Additional commands may be added for debugging (e.g., `.ast`, `.mir`), but they must remain REPL-only and default-off for CI. -## 5) Compatibility and `strip_local_decl` policy +## 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 │ │ +│ │ - 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 // Invalid across lines! + +// ✅ CORRECT: VMValue is runtime value +session.variables: BTreeMap // 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 ` 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). +- **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. -## 6) Error Messages (Fail-Fast wording) +## 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.` -## 7) Minimal Conformance Tests (spec lock) +## 8) Minimal Conformance Tests (spec lock) ### File mode @@ -120,3 +282,54 @@ Recommended file-mode errors: 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) + +✅ **CLI Entry** (`--repl` / `-i` flags) +✅ **REPL Loop** (`.help`, `.exit`, `.reset` commands) +✅ **ReplSessionBox** (VMValue-based session state) +✅ **Implicit Local Binding** (`x = 1` creates session variable) +✅ **print() Output** (ExternCall output displays) +✅ **`_` Variable** (last value stored) +✅ **Session Persistence** (VMValue persists across lines) +✅ **Fail-Fast** (undefined reads error immediately) + +### Deferred to Phase 288.1 + +⏳ **Expression Auto-Display** (`1+1` → `2` automatic output) +⏳ **Complex Expression Detection** (distinguish expression vs statement) +⏳ **REPL Variable Injection** (session variables accessible in compilation) + +### Current Behavior (Phase 288 MVP) + +```nyash +>>> x = 42 +>>> # Silent (no auto-display) + +>>> print(x) +42 # print() output only + +>>> print(_) +42 # _ holds last value + +>>> 1 + 1 +>>> # Silent (expression detection in Phase 288.1) + +>>> .reset +Session reset + +>>> print(x) +Error: Undefined variable 'x' +``` + +**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)