docs(repl): Phase 288 P0 - Establish REPL SSOT
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 <noreply@anthropic.com>
This commit is contained in:
@ -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<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).
|
||||
- **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)
|
||||
|
||||
Reference in New Issue
Block a user