feat(repl): Phase 288 P1 - Add CLI entry point

Added --repl / -i flag:
- src/cli/args.rs: clap flag definition (+5 lines)
- src/cli/mod.rs: CliConfig.repl field (+3 lines)
- src/runner/mod.rs: run_repl() loop + stub eval (+71 lines)

Minimal REPL: .help / .exit work, eval stub for P2.

Test results:
   hakorune --repl starts REPL
   .help / .exit / .reset commands work
   File mode regression: 154/154 tests pass

File mode unchanged (0 regressions).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-25 13:28:41 +09:00
parent 796089688a
commit 8941e3bb03
3 changed files with 80 additions and 0 deletions

View File

@ -105,6 +105,12 @@ pub fn build_command() -> Command {
.arg(Arg::new("build-aot").long("build-aot").value_name("{cranelift|llvm}").help("AOT backend for --build"))
.arg(Arg::new("build-profile").long("profile").value_name("{release|debug}").help("Cargo profile for --build"))
.arg(Arg::new("build-target").long("target").value_name("TRIPLE").help("Target triple for --build"))
// Phase 288 P1: REPL mode
.arg(Arg::new("repl")
.long("repl")
.short('i')
.help("Start interactive REPL (Read-Eval-Print Loop)")
.action(clap::ArgAction::SetTrue))
}
fn hex_encode_utf8(s: &str) -> String {
@ -206,6 +212,8 @@ pub fn from_matches(matches: &ArgMatches) -> CliConfig {
macro_expand_child: matches.get_one::<String>("macro-expand-child").cloned(),
dump_expanded_ast_json: matches.get_flag("dump-expanded-ast-json"),
macro_ctx_json: matches.get_one::<String>("macro-ctx-json").cloned(),
// Phase 288 P1: REPL mode
repl: matches.get_flag("repl"),
};
if cfg.cli_verbose {

View File

@ -70,6 +70,8 @@ pub struct CliConfig {
pub macro_expand_child: Option<String>,
pub dump_expanded_ast_json: bool,
pub macro_ctx_json: Option<String>,
// Phase 288 P1: REPL mode
pub repl: bool,
}
pub use groups::{
@ -221,6 +223,8 @@ impl Default for CliConfig {
macro_expand_child: None,
dump_expanded_ast_json: false,
macro_ctx_json: None,
// Phase 288 P1: REPL mode
repl: false,
}
}
}

View File

@ -88,6 +88,70 @@ impl NyashRunner {
fn run_build_mvp(&self, cfg_path: &str) -> Result<(), String> {
build::run_build_mvp_impl(self, cfg_path)
}
/// Phase 288 P1: Run REPL mode
fn run_repl(&self) -> ! {
use std::io::{self, Write};
println!("Nyash REPL v1.0 - Phase 288 MVP");
println!("Type .help for commands, .exit to quit");
let stdin = io::stdin();
let mut line_buf = String::new();
loop {
print!(">>> ");
io::stdout().flush().unwrap();
line_buf.clear();
match stdin.read_line(&mut line_buf) {
Ok(0) => break, // EOF
Ok(_) => {
let line = line_buf.trim();
// REPL commands
match line {
".exit" | ".quit" => break,
".help" => {
println!("Commands:");
println!(" .exit / .quit - Exit REPL");
println!(" .reset - Clear session (Phase 288 P2)");
println!(" .help - Show this help");
continue;
}
".reset" => {
println!("Session reset (Phase 288 P2 implementation)");
continue;
}
"" => continue,
_ => {}
}
// Evaluate line
match self.eval_repl_line(line) {
Ok(result) => {
if !result.is_empty() {
println!("{}", result);
}
}
Err(e) => eprintln!("Error: {}", e),
}
}
Err(e) => {
eprintln!("Input error: {}", e);
break;
}
}
}
println!("Goodbye!");
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())
}
}
#[cfg(not(feature = "jit-direct-only"))]
@ -99,6 +163,10 @@ impl NyashRunner {
crate::runner::modes::macro_child::run_macro_child(macro_file);
return;
}
// Phase 288 P1: REPL mode
if self.config.repl {
self.run_repl(); // never returns
}
let groups = self.config.as_groups();
// CLI mode trace: show backend/file/args when emit-mir trace is enabled