Files
hakorune/docs/reference/language/repl.md
tomoaki dd1b2a22e1 docs(repl): Phase 288 final documentation polish
Updated documentation for Phase 288 completion:
- docs/reference/language/repl.md: Status clarified (P0–P3 complete)
- docs/development/current/main/phases/phase-288/README.md: Added P0-P3 summaries

Changes:
- repl.md: "partial (dev tool)" → "P0–P3 complete / Phase 288.1 next"
- README.md: Each phase now has detailed description with key deliverables
- Added Box化モジュール化 (3445ef7a7) to completion record

Documentation improvements:
-  P0-P3 completion clearly marked
-  VMValue persistence approach documented
-  Box-First modularization recorded
-  Consistent with 10-Now.md, 30-Backlog.md, P1-INSTRUCTIONS.md

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

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

12 KiB
Raw Blame History

REPL Mode (ReadEvalPrint Loop) — Specification (SSOT)

Status: Phase 288 P0P3 complete (2025-12-25, MVP) / Phase 288.1 next (session persistence + auto-display)

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

hakorune --repl   # Full flag
hakorune -i       # Short alias (interactive)

File Mode (strict) - Code Example

// 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

>>> 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?

// ❌ 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+12 automatic output) Complex Expression Detection (distinguish expression vs statement) REPL Variable Injection (session → compilation context bridge)

Current Behavior (Phase 288 P3 MVP)

>>> .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)