refactor(stage1-bridge): モジュール分割 - 275→386行(4ファイル)
**分割構成**: - modules.rs (73行): collect_modules_list - nyash.toml解析 - args.rs (106行): build_stage1_args - mode別args構築 - env.rs (82行): configure_stage1_env - 環境変数設定 - mod.rs (125行): maybe_run_stage1_cli_stub - エントリポイント **効果**: - 単一責任原則: 各ファイルが明確な責務 - テスタビリティ: 個別関数を単体テスト可能 - 可読性: 275行単一ファイル → 4ファイル(73-125行) - 保守性: 変更影響範囲が明確 **永続性**: - static instance化と無関係(削除されない) - Phase 25.x方針適合(新規モジュールの分割は自然) Phase: 25.x
This commit is contained in:
@ -1,275 +0,0 @@
|
||||
/*!
|
||||
* Stage‑1 CLI bridge — delegate to Hako Stage1 stub when explicitly enabled.
|
||||
*
|
||||
* - Entry: NYASH_USE_STAGE1_CLI=1 (default OFF).
|
||||
* - Toggle guard for child recursion: NYASH_STAGE1_CLI_CHILD=1 (set by bridge).
|
||||
* - Entry path override: STAGE1_CLI_ENTRY or HAKORUNE_STAGE1_ENTRY (optional).
|
||||
* - Mode toggles:
|
||||
* - STAGE1_EMIT_PROGRAM_JSON=1 : emit program-json <source.hako>
|
||||
* - STAGE1_EMIT_MIR_JSON=1 : emit mir-json (<source.hako> or STAGE1_PROGRAM_JSON)
|
||||
* - STAGE1_BACKEND={vm|llvm|pyvm} hint for run path (default: CLI backend)
|
||||
*
|
||||
* Notes
|
||||
* - This bridge aims to keep Rust Stage0 thin: it only invokes the Stage1 stub
|
||||
* (lang/src/runner/stage1_cli.hako) with script args and exits with the stub's code.
|
||||
* - When toggles are unset or this is a child invocation, the bridge is a no-op.
|
||||
*/
|
||||
|
||||
use super::NyashRunner;
|
||||
use crate::cli::CliGroups;
|
||||
use serde_json;
|
||||
use std::{path::Path, process};
|
||||
|
||||
impl NyashRunner {
|
||||
fn collect_modules_list() -> Option<String> {
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let path = PathBuf::from("nyash.toml");
|
||||
if !path.exists() {
|
||||
return None;
|
||||
}
|
||||
let content = fs::read_to_string(&path).ok()?;
|
||||
let mut entries: Vec<String> = Vec::new();
|
||||
let mut seen: HashSet<String> = HashSet::new();
|
||||
let mut in_modules = false;
|
||||
for raw in content.lines() {
|
||||
let line = raw.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
if line.starts_with('[') {
|
||||
in_modules = line == "[modules]";
|
||||
continue;
|
||||
}
|
||||
if !in_modules {
|
||||
continue;
|
||||
}
|
||||
if let Some((k, v_raw)) = line.split_once('=') {
|
||||
let key = k.trim().trim_matches('"');
|
||||
let mut v = v_raw.trim();
|
||||
if let Some((head, _comment)) = v.split_once('#') {
|
||||
v = head.trim();
|
||||
}
|
||||
if v.starts_with('"') && v.ends_with('"') && v.len() >= 2 {
|
||||
v = &v[1..v.len().saturating_sub(1)];
|
||||
}
|
||||
if !key.is_empty() && !v.is_empty() {
|
||||
if seen.insert(key.to_string()) {
|
||||
entries.push(format!("{key}={v}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a few well-known aliases required by Stage‑1 CLI if they are absent in nyash.toml.
|
||||
for (k, v) in [
|
||||
(
|
||||
"lang.compiler.entry.using_resolver_box",
|
||||
"lang/src/compiler/entry/using_resolver_box.hako",
|
||||
),
|
||||
(
|
||||
"selfhost.shared.host_bridge.codegen_bridge",
|
||||
"lang/src/shared/host_bridge/codegen_bridge_box.hako",
|
||||
),
|
||||
] {
|
||||
if seen.insert(k.to_string()) {
|
||||
entries.push(format!("{k}={v}"));
|
||||
}
|
||||
}
|
||||
if entries.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(entries.join("|||"))
|
||||
}
|
||||
}
|
||||
|
||||
/// If enabled, run the Stage‑1 CLI stub as a child process and return its exit code.
|
||||
/// Returns None when the bridge is not engaged.
|
||||
pub(crate) fn maybe_run_stage1_cli_stub(&self, groups: &CliGroups) -> Option<i32> {
|
||||
if std::env::var("NYASH_STAGE1_CLI_CHILD").ok().as_deref() == Some("1") {
|
||||
return None;
|
||||
}
|
||||
if std::env::var("NYASH_USE_STAGE1_CLI").ok().as_deref() != Some("1") {
|
||||
return None;
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("2")
|
||||
{
|
||||
eprintln!("[stage1-bridge/debug] NYASH_USE_STAGE1_CLI=1 detected");
|
||||
}
|
||||
|
||||
let entry = std::env::var("STAGE1_CLI_ENTRY")
|
||||
.or_else(|_| std::env::var("HAKORUNE_STAGE1_ENTRY"))
|
||||
.unwrap_or_else(|_| "lang/src/runner/stage1_cli.hako".to_string());
|
||||
if !Path::new(&entry).exists() {
|
||||
eprintln!("[stage1-cli] entry not found: {}", entry);
|
||||
return Some(97);
|
||||
}
|
||||
|
||||
let source = groups
|
||||
.input
|
||||
.file
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("STAGE1_SOURCE").ok())
|
||||
.or_else(|| std::env::var("STAGE1_INPUT").ok());
|
||||
|
||||
let emit_program = std::env::var("STAGE1_EMIT_PROGRAM_JSON").ok().as_deref() == Some("1");
|
||||
let emit_mir = std::env::var("STAGE1_EMIT_MIR_JSON").ok().as_deref() == Some("1");
|
||||
|
||||
let mut stage1_args: Vec<String> = Vec::new();
|
||||
let mut stage1_env_script_args: Option<String> = None;
|
||||
let mut stage1_source_env: Option<String> = None;
|
||||
let mut stage1_progjson_env: Option<String> = None;
|
||||
if emit_program {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] STAGE1_EMIT_PROGRAM_JSON=1 but no input file provided");
|
||||
process::exit(97);
|
||||
});
|
||||
stage1_args.push("emit".into());
|
||||
stage1_args.push("program-json".into());
|
||||
stage1_args.push(src);
|
||||
stage1_source_env = stage1_args.last().cloned();
|
||||
} else if emit_mir {
|
||||
if let Ok(pjson) = std::env::var("STAGE1_PROGRAM_JSON") {
|
||||
stage1_args.push("emit".into());
|
||||
stage1_args.push("mir-json".into());
|
||||
stage1_args.push("--from-program-json".into());
|
||||
stage1_args.push(pjson);
|
||||
stage1_progjson_env = stage1_args.last().cloned();
|
||||
} else {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] STAGE1_EMIT_MIR_JSON=1 but no input file provided");
|
||||
process::exit(97);
|
||||
});
|
||||
stage1_args.push("emit".into());
|
||||
stage1_args.push("mir-json".into());
|
||||
stage1_args.push(src);
|
||||
stage1_source_env = stage1_args.last().cloned();
|
||||
}
|
||||
} else {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] NYASH_USE_STAGE1_CLI=1 requires an input file to run");
|
||||
process::exit(97);
|
||||
});
|
||||
stage1_args.push("run".into());
|
||||
let backend = std::env::var("STAGE1_BACKEND")
|
||||
.ok()
|
||||
.unwrap_or_else(|| groups.backend.backend.clone());
|
||||
stage1_args.push("--backend".into());
|
||||
stage1_args.push(backend);
|
||||
stage1_args.push(src);
|
||||
stage1_source_env = stage1_args.last().cloned();
|
||||
}
|
||||
|
||||
// Forward script args provided to the parent process (via -- arg1 arg2 ...)
|
||||
if let Ok(json) = std::env::var("NYASH_SCRIPT_ARGS_JSON") {
|
||||
if let Ok(mut extras) = serde_json::from_str::<Vec<String>>(&json) {
|
||||
stage1_args.append(&mut extras);
|
||||
}
|
||||
}
|
||||
// Also pass args via env to guarantee argv is well-defined in the stub.
|
||||
if std::env::var("NYASH_SCRIPT_ARGS_JSON").is_err() {
|
||||
if let Ok(json) = serde_json::to_string(&stage1_args) {
|
||||
stage1_env_script_args = Some(json);
|
||||
}
|
||||
}
|
||||
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| {
|
||||
// Fallback to release binary path when current_exe is unavailable
|
||||
std::path::PathBuf::from("target/release/nyash")
|
||||
});
|
||||
let mut cmd = std::process::Command::new(exe);
|
||||
let entry_fn =
|
||||
std::env::var("NYASH_ENTRY").unwrap_or_else(|_| "Stage1CliMain.main/1".to_string());
|
||||
cmd.arg(&entry).arg("--");
|
||||
for a in &stage1_args {
|
||||
cmd.arg(a);
|
||||
}
|
||||
if let Some(json) = stage1_env_script_args.as_ref() {
|
||||
cmd.env("NYASH_SCRIPT_ARGS_JSON", json);
|
||||
}
|
||||
if let Some(src) = stage1_source_env.as_ref() {
|
||||
cmd.env("STAGE1_SOURCE", src);
|
||||
}
|
||||
if let Some(pjson) = stage1_progjson_env.as_ref() {
|
||||
cmd.env("STAGE1_PROGRAM_JSON", pjson);
|
||||
}
|
||||
// Pass source text inline to avoid FileBox dependency when possible.
|
||||
if stage1_source_env.is_none() {
|
||||
if let Some(src_path) = source.as_ref() {
|
||||
if let Ok(text) = std::fs::read_to_string(&src_path) {
|
||||
cmd.env("STAGE1_SOURCE_TEXT", text);
|
||||
}
|
||||
}
|
||||
} else if let Some(src_path) = stage1_source_env.as_ref() {
|
||||
if let Ok(text) = std::fs::read_to_string(src_path) {
|
||||
cmd.env("STAGE1_SOURCE_TEXT", text);
|
||||
}
|
||||
}
|
||||
cmd.env("NYASH_STAGE1_CLI_CHILD", "1");
|
||||
if std::env::var("NYASH_NYRT_SILENT_RESULT").is_err() {
|
||||
cmd.env("NYASH_NYRT_SILENT_RESULT", "1");
|
||||
}
|
||||
if std::env::var("NYASH_DISABLE_PLUGINS").is_err() {
|
||||
cmd.env("NYASH_DISABLE_PLUGINS", "0");
|
||||
}
|
||||
if std::env::var("NYASH_FILEBOX_MODE").is_err() {
|
||||
cmd.env("NYASH_FILEBOX_MODE", "auto");
|
||||
}
|
||||
if std::env::var("NYASH_BOX_FACTORY_POLICY").is_err() {
|
||||
cmd.env("NYASH_BOX_FACTORY_POLICY", "builtin_first");
|
||||
}
|
||||
// Stage‑1 CLI 経路では既定で using 適用を無効化し、
|
||||
// prefix は空(HAKO_STAGEB_APPLY_USINGS=0)とする。
|
||||
// UsingResolver/UsingCollector の検証は専用テストで行い、
|
||||
// CLI 本線はシンプルな Program(JSON) 生成に集中させる。
|
||||
if std::env::var("HAKO_STAGEB_APPLY_USINGS").is_err() {
|
||||
cmd.env("HAKO_STAGEB_APPLY_USINGS", "0");
|
||||
}
|
||||
if std::env::var("NYASH_ENABLE_USING").is_err() {
|
||||
cmd.env("NYASH_ENABLE_USING", "1");
|
||||
}
|
||||
if std::env::var("HAKO_ENABLE_USING").is_err() {
|
||||
cmd.env("HAKO_ENABLE_USING", "1");
|
||||
}
|
||||
if std::env::var("NYASH_PARSER_STAGE3").is_err() {
|
||||
cmd.env("NYASH_PARSER_STAGE3", "1");
|
||||
}
|
||||
if std::env::var("HAKO_PARSER_STAGE3").is_err() {
|
||||
cmd.env("HAKO_PARSER_STAGE3", "1");
|
||||
}
|
||||
if std::env::var("HAKO_STAGEB_MODULES_LIST").is_err() {
|
||||
if let Some(mods) = Self::collect_modules_list() {
|
||||
cmd.env("HAKO_STAGEB_MODULES_LIST", mods);
|
||||
}
|
||||
}
|
||||
if std::env::var("NYASH_ENTRY").is_err() {
|
||||
cmd.env("NYASH_ENTRY", &entry_fn);
|
||||
}
|
||||
if std::env::var("STAGE1_BACKEND").is_err() {
|
||||
if let Some(be) = stage1_args
|
||||
.windows(2)
|
||||
.find(|w| w[0] == "--backend")
|
||||
.map(|w| w[1].clone())
|
||||
{
|
||||
cmd.env("STAGE1_BACKEND", be);
|
||||
}
|
||||
}
|
||||
|
||||
crate::cli_v!(
|
||||
"[stage1-cli] delegating to stub: {} -- {}",
|
||||
entry,
|
||||
stage1_args.join(" ")
|
||||
);
|
||||
let status = match cmd.status() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("[stage1-cli] failed to spawn stub: {}", e);
|
||||
return Some(97);
|
||||
}
|
||||
};
|
||||
Some(status.code().unwrap_or(1))
|
||||
}
|
||||
}
|
||||
106
src/runner/stage1_bridge/args.rs
Normal file
106
src/runner/stage1_bridge/args.rs
Normal file
@ -0,0 +1,106 @@
|
||||
/*!
|
||||
* Stage-1 CLI bridge - args builder
|
||||
*
|
||||
* Constructs stage1_args based on execution mode (emit_program / emit_mir / run).
|
||||
*/
|
||||
|
||||
use crate::cli::CliGroups;
|
||||
use serde_json;
|
||||
use std::process;
|
||||
|
||||
/// Stage-1 args construction result
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Stage1Args {
|
||||
pub args: Vec<String>,
|
||||
pub env_script_args: Option<String>,
|
||||
pub source_env: Option<String>,
|
||||
pub progjson_env: Option<String>,
|
||||
}
|
||||
|
||||
/// Build stage1_args based on execution mode
|
||||
///
|
||||
/// # Modes
|
||||
/// - emit_program: emit program-json <source.hako>
|
||||
/// - emit_mir: emit mir-json (<source.hako> or STAGE1_PROGRAM_JSON)
|
||||
/// - run: run --backend <backend> <source.hako>
|
||||
pub(super) fn build_stage1_args(groups: &CliGroups) -> Stage1Args {
|
||||
let source = groups
|
||||
.input
|
||||
.file
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("STAGE1_SOURCE").ok())
|
||||
.or_else(|| std::env::var("STAGE1_INPUT").ok());
|
||||
|
||||
let emit_program = std::env::var("STAGE1_EMIT_PROGRAM_JSON")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1");
|
||||
let emit_mir = std::env::var("STAGE1_EMIT_MIR_JSON").ok().as_deref() == Some("1");
|
||||
|
||||
let mut args: Vec<String> = Vec::new();
|
||||
let mut source_env: Option<String> = None;
|
||||
let mut progjson_env: Option<String> = None;
|
||||
|
||||
if emit_program {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] STAGE1_EMIT_PROGRAM_JSON=1 but no input file provided");
|
||||
process::exit(97);
|
||||
});
|
||||
args.push("emit".into());
|
||||
args.push("program-json".into());
|
||||
args.push(src);
|
||||
source_env = args.last().cloned();
|
||||
} else if emit_mir {
|
||||
if let Ok(pjson) = std::env::var("STAGE1_PROGRAM_JSON") {
|
||||
args.push("emit".into());
|
||||
args.push("mir-json".into());
|
||||
args.push("--from-program-json".into());
|
||||
args.push(pjson);
|
||||
progjson_env = args.last().cloned();
|
||||
} else {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] STAGE1_EMIT_MIR_JSON=1 but no input file provided");
|
||||
process::exit(97);
|
||||
});
|
||||
args.push("emit".into());
|
||||
args.push("mir-json".into());
|
||||
args.push(src);
|
||||
source_env = args.last().cloned();
|
||||
}
|
||||
} else {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] NYASH_USE_STAGE1_CLI=1 requires an input file to run");
|
||||
process::exit(97);
|
||||
});
|
||||
args.push("run".into());
|
||||
let backend = std::env::var("STAGE1_BACKEND")
|
||||
.ok()
|
||||
.unwrap_or_else(|| groups.backend.backend.clone());
|
||||
args.push("--backend".into());
|
||||
args.push(backend);
|
||||
args.push(src);
|
||||
source_env = args.last().cloned();
|
||||
}
|
||||
|
||||
// Forward script args provided to the parent process (via -- arg1 arg2 ...)
|
||||
if let Ok(json) = std::env::var("NYASH_SCRIPT_ARGS_JSON") {
|
||||
if let Ok(mut extras) = serde_json::from_str::<Vec<String>>(&json) {
|
||||
args.append(&mut extras);
|
||||
}
|
||||
}
|
||||
|
||||
// Also pass args via env to guarantee argv is well-defined in the stub.
|
||||
let env_script_args = if std::env::var("NYASH_SCRIPT_ARGS_JSON").is_err() {
|
||||
serde_json::to_string(&args).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Stage1Args {
|
||||
args,
|
||||
env_script_args,
|
||||
source_env,
|
||||
progjson_env,
|
||||
}
|
||||
}
|
||||
82
src/runner/stage1_bridge/env.rs
Normal file
82
src/runner/stage1_bridge/env.rs
Normal file
@ -0,0 +1,82 @@
|
||||
/*!
|
||||
* Stage-1 CLI bridge - environment variable configurator
|
||||
*
|
||||
* Sets default environment variables for Stage-1 CLI child process.
|
||||
*/
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
/// Configure environment variables for Stage-1 CLI child process
|
||||
///
|
||||
/// Sets defaults for:
|
||||
/// - Runtime behavior (NYASH_NYRT_SILENT_RESULT, NYASH_DISABLE_PLUGINS, etc.)
|
||||
/// - Parser toggles (NYASH_PARSER_STAGE3, NYASH_ENABLE_USING, etc.)
|
||||
/// - Stage-B configuration (HAKO_STAGEB_APPLY_USINGS, HAKO_STAGEB_MODULES_LIST, etc.)
|
||||
pub(super) fn configure_stage1_env(
|
||||
cmd: &mut Command,
|
||||
entry_fn: &str,
|
||||
stage1_args: &[String],
|
||||
modules_list: Option<String>,
|
||||
) {
|
||||
// Child recursion guard
|
||||
cmd.env("NYASH_STAGE1_CLI_CHILD", "1");
|
||||
|
||||
// Runtime defaults
|
||||
if std::env::var("NYASH_NYRT_SILENT_RESULT").is_err() {
|
||||
cmd.env("NYASH_NYRT_SILENT_RESULT", "1");
|
||||
}
|
||||
if std::env::var("NYASH_DISABLE_PLUGINS").is_err() {
|
||||
cmd.env("NYASH_DISABLE_PLUGINS", "0");
|
||||
}
|
||||
if std::env::var("NYASH_FILEBOX_MODE").is_err() {
|
||||
cmd.env("NYASH_FILEBOX_MODE", "auto");
|
||||
}
|
||||
if std::env::var("NYASH_BOX_FACTORY_POLICY").is_err() {
|
||||
cmd.env("NYASH_BOX_FACTORY_POLICY", "builtin_first");
|
||||
}
|
||||
|
||||
// Stage-1 CLI 経路では既定で using 適用を無効化し、
|
||||
// prefix は空(HAKO_STAGEB_APPLY_USINGS=0)とする。
|
||||
// UsingResolver/UsingCollector の検証は専用テストで行い、
|
||||
// CLI 本線はシンプルな Program(JSON) 生成に集中させる。
|
||||
if std::env::var("HAKO_STAGEB_APPLY_USINGS").is_err() {
|
||||
cmd.env("HAKO_STAGEB_APPLY_USINGS", "0");
|
||||
}
|
||||
|
||||
// Parser toggles
|
||||
if std::env::var("NYASH_ENABLE_USING").is_err() {
|
||||
cmd.env("NYASH_ENABLE_USING", "1");
|
||||
}
|
||||
if std::env::var("HAKO_ENABLE_USING").is_err() {
|
||||
cmd.env("HAKO_ENABLE_USING", "1");
|
||||
}
|
||||
if std::env::var("NYASH_PARSER_STAGE3").is_err() {
|
||||
cmd.env("NYASH_PARSER_STAGE3", "1");
|
||||
}
|
||||
if std::env::var("HAKO_PARSER_STAGE3").is_err() {
|
||||
cmd.env("HAKO_PARSER_STAGE3", "1");
|
||||
}
|
||||
|
||||
// Modules list
|
||||
if std::env::var("HAKO_STAGEB_MODULES_LIST").is_err() {
|
||||
if let Some(mods) = modules_list {
|
||||
cmd.env("HAKO_STAGEB_MODULES_LIST", mods);
|
||||
}
|
||||
}
|
||||
|
||||
// Entry function
|
||||
if std::env::var("NYASH_ENTRY").is_err() {
|
||||
cmd.env("NYASH_ENTRY", entry_fn);
|
||||
}
|
||||
|
||||
// Backend hint
|
||||
if std::env::var("STAGE1_BACKEND").is_err() {
|
||||
if let Some(be) = stage1_args
|
||||
.windows(2)
|
||||
.find(|w| w[0] == "--backend")
|
||||
.map(|w| w[1].clone())
|
||||
{
|
||||
cmd.env("STAGE1_BACKEND", be);
|
||||
}
|
||||
}
|
||||
}
|
||||
125
src/runner/stage1_bridge/mod.rs
Normal file
125
src/runner/stage1_bridge/mod.rs
Normal file
@ -0,0 +1,125 @@
|
||||
/*!
|
||||
* Stage-1 CLI bridge — delegate to Hako Stage1 stub when explicitly enabled.
|
||||
*
|
||||
* - Entry: NYASH_USE_STAGE1_CLI=1 (default OFF).
|
||||
* - Toggle guard for child recursion: NYASH_STAGE1_CLI_CHILD=1 (set by bridge).
|
||||
* - Entry path override: STAGE1_CLI_ENTRY or HAKORUNE_STAGE1_ENTRY (optional).
|
||||
* - Mode toggles:
|
||||
* - STAGE1_EMIT_PROGRAM_JSON=1 : emit program-json <source.hako>
|
||||
* - STAGE1_EMIT_MIR_JSON=1 : emit mir-json (<source.hako> or STAGE1_PROGRAM_JSON)
|
||||
* - STAGE1_BACKEND={vm|llvm|pyvm} hint for run path (default: CLI backend)
|
||||
*
|
||||
* Notes
|
||||
* - This bridge aims to keep Rust Stage0 thin: it only invokes the Stage1 stub
|
||||
* (lang/src/runner/stage1_cli.hako) with script args and exits with the stub's code.
|
||||
* - When toggles are unset or this is a child invocation, the bridge is a no-op.
|
||||
*/
|
||||
|
||||
mod args;
|
||||
mod env;
|
||||
mod modules;
|
||||
|
||||
use super::NyashRunner;
|
||||
use crate::cli::CliGroups;
|
||||
use std::{path::Path, process};
|
||||
|
||||
impl NyashRunner {
|
||||
/// If enabled, run the Stage-1 CLI stub as a child process and return its exit code.
|
||||
/// Returns None when the bridge is not engaged.
|
||||
pub(crate) fn maybe_run_stage1_cli_stub(&self, groups: &CliGroups) -> Option<i32> {
|
||||
// Guard: skip if child invocation
|
||||
if std::env::var("NYASH_STAGE1_CLI_CHILD")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// Guard: skip if not enabled
|
||||
if std::env::var("NYASH_USE_STAGE1_CLI")
|
||||
.ok()
|
||||
.as_deref()
|
||||
!= Some("1")
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("2")
|
||||
{
|
||||
eprintln!("[stage1-bridge/debug] NYASH_USE_STAGE1_CLI=1 detected");
|
||||
}
|
||||
|
||||
// Locate Stage-1 CLI entry
|
||||
let entry = std::env::var("STAGE1_CLI_ENTRY")
|
||||
.or_else(|_| std::env::var("HAKORUNE_STAGE1_ENTRY"))
|
||||
.unwrap_or_else(|_| "lang/src/runner/stage1_cli.hako".to_string());
|
||||
if !Path::new(&entry).exists() {
|
||||
eprintln!("[stage1-cli] entry not found: {}", entry);
|
||||
return Some(97);
|
||||
}
|
||||
|
||||
// Build args
|
||||
let args_result = args::build_stage1_args(groups);
|
||||
|
||||
// Collect modules list
|
||||
let modules_list = modules::collect_modules_list();
|
||||
|
||||
// Prepare command
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| {
|
||||
// Fallback to release binary path when current_exe is unavailable
|
||||
std::path::PathBuf::from("target/release/nyash")
|
||||
});
|
||||
let mut cmd = std::process::Command::new(exe);
|
||||
let entry_fn = std::env::var("NYASH_ENTRY")
|
||||
.unwrap_or_else(|_| "Stage1CliMain.main/1".to_string());
|
||||
cmd.arg(&entry).arg("--");
|
||||
for a in &args_result.args {
|
||||
cmd.arg(a);
|
||||
}
|
||||
|
||||
// Set environment variables for args
|
||||
if let Some(json) = args_result.env_script_args.as_ref() {
|
||||
cmd.env("NYASH_SCRIPT_ARGS_JSON", json);
|
||||
}
|
||||
if let Some(src) = args_result.source_env.as_ref() {
|
||||
cmd.env("STAGE1_SOURCE", src);
|
||||
}
|
||||
if let Some(pjson) = args_result.progjson_env.as_ref() {
|
||||
cmd.env("STAGE1_PROGRAM_JSON", pjson);
|
||||
}
|
||||
|
||||
// Pass source text inline to avoid FileBox dependency when possible.
|
||||
if args_result.source_env.is_none() {
|
||||
if let Some(src_path) = groups.input.file.as_ref() {
|
||||
if let Ok(text) = std::fs::read_to_string(&src_path) {
|
||||
cmd.env("STAGE1_SOURCE_TEXT", text);
|
||||
}
|
||||
}
|
||||
} else if let Some(src_path) = args_result.source_env.as_ref() {
|
||||
if let Ok(text) = std::fs::read_to_string(src_path) {
|
||||
cmd.env("STAGE1_SOURCE_TEXT", text);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure environment
|
||||
env::configure_stage1_env(&mut cmd, &entry_fn, &args_result.args, modules_list);
|
||||
|
||||
crate::cli_v!(
|
||||
"[stage1-cli] delegating to stub: {} -- {}",
|
||||
entry,
|
||||
args_result.args.join(" ")
|
||||
);
|
||||
|
||||
// Execute
|
||||
let status = match cmd.status() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("[stage1-cli] failed to spawn stub: {}", e);
|
||||
return Some(97);
|
||||
}
|
||||
};
|
||||
Some(status.code().unwrap_or(1))
|
||||
}
|
||||
}
|
||||
73
src/runner/stage1_bridge/modules.rs
Normal file
73
src/runner/stage1_bridge/modules.rs
Normal file
@ -0,0 +1,73 @@
|
||||
/*!
|
||||
* Stage-1 CLI bridge - modules list collector
|
||||
*
|
||||
* Parses nyash.toml [modules] section and formats module mappings
|
||||
* for Stage-1 CLI environment variables.
|
||||
*/
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Collect modules list from nyash.toml [modules] section
|
||||
///
|
||||
/// Returns a "|||"-separated list of "key=value" entries for HAKO_STAGEB_MODULES_LIST.
|
||||
/// Includes well-known aliases required by Stage-1 CLI if absent in nyash.toml.
|
||||
pub(super) fn collect_modules_list() -> Option<String> {
|
||||
let path = PathBuf::from("nyash.toml");
|
||||
if !path.exists() {
|
||||
return None;
|
||||
}
|
||||
let content = fs::read_to_string(&path).ok()?;
|
||||
let mut entries: Vec<String> = Vec::new();
|
||||
let mut seen: HashSet<String> = HashSet::new();
|
||||
let mut in_modules = false;
|
||||
for raw in content.lines() {
|
||||
let line = raw.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
if line.starts_with('[') {
|
||||
in_modules = line == "[modules]";
|
||||
continue;
|
||||
}
|
||||
if !in_modules {
|
||||
continue;
|
||||
}
|
||||
if let Some((k, v_raw)) = line.split_once('=') {
|
||||
let key = k.trim().trim_matches('"');
|
||||
let mut v = v_raw.trim();
|
||||
if let Some((head, _comment)) = v.split_once('#') {
|
||||
v = head.trim();
|
||||
}
|
||||
if v.starts_with('"') && v.ends_with('"') && v.len() >= 2 {
|
||||
v = &v[1..v.len().saturating_sub(1)];
|
||||
}
|
||||
if !key.is_empty() && !v.is_empty() {
|
||||
if seen.insert(key.to_string()) {
|
||||
entries.push(format!("{key}={v}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a few well-known aliases required by Stage-1 CLI if they are absent in nyash.toml.
|
||||
for (k, v) in [
|
||||
(
|
||||
"lang.compiler.entry.using_resolver_box",
|
||||
"lang/src/compiler/entry/using_resolver_box.hako",
|
||||
),
|
||||
(
|
||||
"selfhost.shared.host_bridge.codegen_bridge",
|
||||
"lang/src/shared/host_bridge/codegen_bridge_box.hako",
|
||||
),
|
||||
] {
|
||||
if seen.insert(k.to_string()) {
|
||||
entries.push(format!("{k}={v}"));
|
||||
}
|
||||
}
|
||||
if entries.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(entries.join("|||"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user