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