feat(repl): Phase 288.1 session persistence + auto-display
実装内容: - AST Rewriter (~430行): 未宣言変数を __repl.get/set に変換 - ExternCall Bridge: VM で __repl.get/set ハンドラー実装 - Rc<RefCell<>> セッション共有: VM と REPL runner 間で永続化 - 式自動表示: pure expression の結果を自動出力 - _ 変数: 最後の表示値を保存(Void は除外) - .reset 実装: セッション変数の完全クリア - Fail-Fast: 未定義変数読み取りで明示的エラー + ヒント 変更ファイル (8ファイル, +592行): - src/runner/repl/ast_rewriter.rs (NEW, +430行) - src/runner/repl/repl_runner.rs (+84/-35行) - src/backend/mir_interpreter/handlers/externals.rs (+54行) - src/mir/builder/calls/build.rs (+41行) - src/backend/mir_interpreter/mod.rs (+12行) - src/runner/repl/repl_session.rs (+11/-9行) - src/runner/repl/mod.rs (+2行) - src/runner/mod.rs (+2/-1行) REPL専用設計(src/mir/builder/calls/build.rs の特別扱い理由): - __repl.get/set は REPL mode 専用の橋渡し機能 - try_build_repl_method_call() で早期検出・ExternCall 変換 - file mode では決して使用されない(VM で "outside REPL mode" エラー) - 将来的にも file mode への影響ゼロを保証 検証済み: - 変数永続化: x = 42; print(x) → 42 ✅ - 式自動表示: 1 + 1 → 2 ✅ - _ 変数: 10 * 2 → 20; _ → 20 ✅ - Fail-Fast: 未定義エラー + ヒント ✅ - 回帰テスト: 154/154 PASS ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -264,6 +264,60 @@ impl MirInterpreter {
|
||||
}
|
||||
return Err(self.err_invalid("hostbridge.extern_invoke unsupported [externals]"));
|
||||
}
|
||||
|
||||
// Phase 288.1: REPL session variable bridge
|
||||
("__repl", "get") => {
|
||||
// args: [name: String]
|
||||
if args.len() != 1 {
|
||||
return Err(self.err_invalid(format!(
|
||||
"__repl.get expects 1 argument, got {}",
|
||||
args.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let name = self.reg_load(args[0])?.to_string();
|
||||
|
||||
// REPL session から取得
|
||||
if let Some(session) = &self.repl_session {
|
||||
// Clone the value before the borrow ends to avoid borrowing conflicts
|
||||
let value_opt = session.borrow().get(&name).cloned();
|
||||
match value_opt {
|
||||
Some(value) => {
|
||||
self.write_result(dst, value);
|
||||
Ok(())
|
||||
}
|
||||
None => Err(self.err_invalid(format!(
|
||||
"Undefined variable: '{}'\nHint: Variable not defined. Assign a value first.",
|
||||
name
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(self.err_invalid("__repl.get called outside REPL mode".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
("__repl", "set") => {
|
||||
// args: [name: String, value: Any]
|
||||
if args.len() != 2 {
|
||||
return Err(self.err_invalid(format!(
|
||||
"__repl.set expects 2 arguments, got {}",
|
||||
args.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let name = self.reg_load(args[0])?.to_string();
|
||||
let value = self.reg_load(args[1])?.clone();
|
||||
|
||||
// REPL session に保存
|
||||
if let Some(session) = &self.repl_session {
|
||||
session.borrow_mut().set(name, value);
|
||||
self.write_void(dst);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(self.err_invalid("__repl.set called outside REPL mode".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
_ => Err(self.err_invalid(format!("ExternCall {}.{} not supported", iface, method))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,9 @@
|
||||
* Print/Debug (best-effort), Barrier/Safepoint (no-op).
|
||||
*/
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
@ -46,6 +48,9 @@ pub struct MirInterpreter {
|
||||
/// Call stack depth (exec_function_inner nesting). Used as a safety valve
|
||||
/// to prevent Rust stack overflow on accidental infinite recursion in MIR.
|
||||
pub(super) call_depth: usize,
|
||||
/// Phase 288.1: REPL session reference (REPL mode only)
|
||||
/// Enables variable persistence across REPL lines via __repl.get/set bridge
|
||||
pub(super) repl_session: Option<Rc<RefCell<crate::runner::repl::ReplSessionBox>>>,
|
||||
}
|
||||
|
||||
impl MirInterpreter {
|
||||
@ -64,6 +69,7 @@ impl MirInterpreter {
|
||||
branch_count: 0,
|
||||
compare_count: 0,
|
||||
call_depth: 0,
|
||||
repl_session: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,6 +101,12 @@ impl MirInterpreter {
|
||||
.detect_from_mir_functions(self.functions.keys());
|
||||
}
|
||||
|
||||
/// Phase 288.1: Set REPL session for variable persistence
|
||||
/// Enables __repl.get/set ExternCall handlers to access session state
|
||||
pub fn set_repl_session(&mut self, session: Rc<RefCell<crate::runner::repl::ReplSessionBox>>) {
|
||||
self.repl_session = Some(session);
|
||||
}
|
||||
|
||||
/// Execute a BoxCall with VM's complete semantics (Phase 27-shortterm S-5.2-improved)
|
||||
///
|
||||
/// This wrapper allows external modules (e.g., JoinIR Runner) to invoke BoxCall
|
||||
|
||||
Reference in New Issue
Block a user