feat(repl): Phase 288 P2 - Implicit local binding (VMValue persistence)
Box-First architecture: - ReplSessionBox: VMValue-based session state (not ValueId) - repl_mode flag: File/REPL mode isolation - MirCompiler.set_repl_mode(): Public API for REPL Files modified: - src/mir/builder/repl_session.rs: NEW (+55 lines) - Session state - src/mir/builder.rs: repl_mode field (+4 lines) - src/mir/mod.rs: set_repl_mode() method (+4 lines) - src/mir/builder/vars/assignment_resolver.rs: REPL skip (+3 lines) - src/runner/mod.rs: repl_session + eval implementation (+45 lines) REPL behavior: - x = 1: Implicit local creation (暗黙 local) - Compilation + execution pipeline complete - Variable persistence: TODO for P3 (as planned) Test results: ✅ x = 1 compiles and executes ✅ File mode regression: 154/154 tests pass File mode unchanged (assignment_resolver.rs: minimal 3-line check). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -88,6 +88,7 @@ pub(crate) mod type_registry;
|
||||
mod types; // types::annotation / inference(型注釈/推論の箱: 推論は後段)
|
||||
mod utils;
|
||||
mod vars; // variables/scope helpers // small loop helpers (header/exit context) // TypeRegistryBox(型情報管理の一元化)
|
||||
pub(crate) mod repl_session; // Phase 288 P2: REPL session state (VMValue-based persistence)
|
||||
|
||||
// Unified member property kinds for computed/once/birth_once
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@ -207,6 +208,11 @@ pub struct MirBuilder {
|
||||
/// Used when HAKO_MIR_BUILDER_METHODIZE=1 to convert Global("BoxName.method/arity")
|
||||
/// to Method{receiver=singleton} calls
|
||||
pub(super) static_box_singletons: HashMap<String, ValueId>,
|
||||
|
||||
/// Phase 288 P2: REPL mode flag - enables implicit local declarations
|
||||
/// File mode: false (explicit local required)
|
||||
/// REPL mode: true (暗黙 local 許可)
|
||||
pub(crate) repl_mode: bool,
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
@ -256,6 +262,7 @@ impl MirBuilder {
|
||||
recursion_depth: 0,
|
||||
root_is_app_mode: None,
|
||||
static_box_singletons: HashMap::new(), // Phase 21.7: methodization support
|
||||
repl_mode: false, // Phase 288 P2: REPL mode (default: file mode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
55
src/mir/builder/repl_session.rs
Normal file
55
src/mir/builder/repl_session.rs
Normal file
@ -0,0 +1,55 @@
|
||||
//! ReplSessionBox - Session state for REPL mode
|
||||
//!
|
||||
//! Box-First Design: Encapsulates REPL-specific state
|
||||
//! Phase 288 P2
|
||||
//!
|
||||
//! **重要**: ValueId は MIR コンパイル単位でのみ有効なので、
|
||||
//! 実行時の値(VMValue)を保持する。
|
||||
|
||||
use crate::backend::VMValue;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// REPL session context - isolated from file mode
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ReplSessionBox {
|
||||
/// Session-level variables (runtime values, persists across evaluations)
|
||||
pub variables: BTreeMap<String, VMValue>,
|
||||
|
||||
/// Last expression value (for `_` variable)
|
||||
pub last_value: Option<VMValue>,
|
||||
|
||||
/// Evaluation counter
|
||||
pub eval_count: usize,
|
||||
}
|
||||
|
||||
impl ReplSessionBox {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// REPL set: 変数に実行時の値を保存
|
||||
pub fn set(&mut self, name: String, value: VMValue) {
|
||||
self.variables.insert(name, value);
|
||||
}
|
||||
|
||||
/// REPL get: 変数の実行時の値を取得(未定義は None)
|
||||
pub fn get(&self, name: &str) -> Option<&VMValue> {
|
||||
self.variables.get(name)
|
||||
}
|
||||
|
||||
/// セッションに変数が存在するか確認
|
||||
pub fn has(&self, name: &str) -> bool {
|
||||
self.variables.contains_key(name)
|
||||
}
|
||||
|
||||
pub fn set_last_value(&mut self, value: VMValue) {
|
||||
self.last_value = Some(value.clone());
|
||||
self.variables.insert("_".to_string(), value);
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.variables.clear();
|
||||
self.last_value = None;
|
||||
self.eval_count = 0;
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,11 @@ impl AssignmentResolverBox {
|
||||
builder: &MirBuilder,
|
||||
var_name: &str,
|
||||
) -> Result<(), String> {
|
||||
// Phase 288 P2: REPL mode allows implicit local declarations
|
||||
if builder.repl_mode {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Compiler-generated temporaries are not part of the user variable namespace.
|
||||
if var_name.starts_with("__pin$") {
|
||||
return Ok(());
|
||||
|
||||
@ -126,6 +126,11 @@ impl MirCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 288 P2: Set REPL mode flag
|
||||
pub fn set_repl_mode(&mut self, repl_mode: bool) {
|
||||
self.builder.repl_mode = repl_mode;
|
||||
}
|
||||
|
||||
/// Compile AST to MIR module with verification
|
||||
pub fn compile_with_source(
|
||||
&mut self,
|
||||
|
||||
@ -51,6 +51,8 @@ use nyash_rust::runtime;
|
||||
/// Main execution coordinator
|
||||
pub struct NyashRunner {
|
||||
config: CliConfig,
|
||||
/// Phase 288 P2: REPL session - stores runtime values across evaluations
|
||||
repl_session: std::cell::RefCell<Option<crate::mir::builder::repl_session::ReplSessionBox>>,
|
||||
}
|
||||
|
||||
/// Minimal task runner: read hako.toml (preferred) or nyash.toml [env]/[tasks], run the named task via shell
|
||||
@ -68,7 +70,10 @@ impl NyashRunner {
|
||||
std::sync::Arc::new(runtime::ring0::Ring0Registry::build(profile))
|
||||
});
|
||||
|
||||
Self { config }
|
||||
Self {
|
||||
config,
|
||||
repl_session: std::cell::RefCell::new(None), // Phase 288 P2
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Nyash based on the configuration
|
||||
@ -148,9 +153,51 @@ impl NyashRunner {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
/// Phase 288 P1: Evaluate REPL line (stub)
|
||||
fn eval_repl_line(&self, _line: &str) -> Result<String, String> {
|
||||
Ok("(evaluation Phase 288 P2)".to_string())
|
||||
/// Phase 288 P2: Evaluate REPL line (VMValue persistence)
|
||||
fn eval_repl_line(&self, line: &str) -> Result<String, String> {
|
||||
use crate::parser::NyashParser;
|
||||
use crate::mir::MirCompiler;
|
||||
use crate::backend::mir_interpreter::MirInterpreter;
|
||||
use crate::mir::builder::repl_session::ReplSessionBox;
|
||||
|
||||
// Initialize session on first use
|
||||
{
|
||||
let mut session_ref = self.repl_session.borrow_mut();
|
||||
if session_ref.is_none() {
|
||||
*session_ref = Some(ReplSessionBox::new());
|
||||
}
|
||||
}
|
||||
|
||||
// Parse (minimal wrapper for REPL context)
|
||||
let code = format!("static box __Repl {{ main() {{ {} }} }}", line);
|
||||
let ast = NyashParser::parse_from_string(&code)
|
||||
.map_err(|e| format!("Parse error: {}", e))?;
|
||||
|
||||
// Compile with REPL mode flag (暗黙 local 許可)
|
||||
let mut compiler = MirCompiler::new();
|
||||
compiler.set_repl_mode(true);
|
||||
|
||||
let mir_result = compiler.compile_with_source(ast, Some("<repl>"))
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
|
||||
// Execute
|
||||
let mut vm = MirInterpreter::new();
|
||||
let result_box = vm.execute_module(&mir_result.module)
|
||||
.map_err(|e| format!("Runtime error: {}", e))?;
|
||||
|
||||
// Phase 288 P2: Convert to VMValue and store in session
|
||||
use crate::backend::VMValue;
|
||||
let vm_value = VMValue::from_nyash_box(result_box);
|
||||
|
||||
{
|
||||
let mut session_ref = self.repl_session.borrow_mut();
|
||||
if let Some(ref mut session) = *session_ref {
|
||||
session.set_last_value(vm_value);
|
||||
session.eval_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok("(execution Phase 288 P3)".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user