Files
hakorune/src/runner/modes/common_util/resolve/prelude_manager.rs

252 lines
8.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Prelude Manager Box - 綺麗綺麗なプレリュード統合専門家!📦
//!
//! テキストマージとASTマージを分離して、
//! 保守性とテスト容易性を向上させるにゃ!
use crate::runner::NyashRunner;
use crate::runner::modes::common_util::resolve::using_resolution::UsingResolutionBox;
/// 📦 PreludeManagerBox - プレリュード統合の専門家!
///
/// テキストベースとASTベースの両方の統合を
/// 統一インターフェースで提供する箱にゃ!
pub struct PreludeManagerBox<'a> {
runner: &'a NyashRunner,
}
/// 🎯 MergeStrategy - 統合戦略!
#[derive(Debug, Clone)]
pub enum MergeStrategy {
/// 🚀 テキストベース統合(高速)
Text,
/// 🧠 ASTベース統合高機能
Ast,
}
/// 📊 MergeResult - 統合結果!
#[derive(Debug)]
pub struct MergeResult {
pub merged_content: String,
pub strategy: MergeStrategy,
pub prelude_count: usize,
pub total_bytes: usize,
}
impl<'a> PreludeManagerBox<'a> {
/// 🌟 新しいPreludeManagerBoxを作るにゃ
pub fn new(runner: &'a NyashRunner) -> Self {
Self { runner }
}
/// 🚀 テキストベース統合を実行するにゃ!
pub fn merge_text(
&self,
source: &str,
filename: &str,
prelude_paths: &[String],
) -> Result<MergeResult, String> {
let trace = std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1");
if prelude_paths.is_empty() {
return Ok(MergeResult {
merged_content: source.to_string(),
strategy: MergeStrategy::Text,
prelude_count: 0,
total_bytes: source.len(),
});
}
if trace {
crate::runner::trace::log(format!(
"[prelude/text] {} prelude files for '{}'",
prelude_paths.len(),
filename
));
}
// テキスト統合ロジック
let merged = self.build_text_merged(source, filename, prelude_paths, trace)?;
let total_bytes = merged.len();
Ok(MergeResult {
merged_content: merged,
strategy: MergeStrategy::Text,
prelude_count: prelude_paths.len(),
total_bytes,
})
}
/// 🧠 ASTベース統合を実行するにゃ
pub fn merge_ast(
&self,
source: &str,
filename: &str,
prelude_paths: &[String],
) -> Result<MergeResult, String> {
let trace = std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1");
if prelude_paths.is_empty() {
return Ok(MergeResult {
merged_content: source.to_string(),
strategy: MergeStrategy::Ast,
prelude_count: 0,
total_bytes: source.len(),
});
}
if trace {
crate::runner::trace::log(format!(
"[prelude/ast] {} prelude files for '{}'",
prelude_paths.len(),
filename
));
}
// TODO: AST統合ロジックをここに実装
// 今はテキスト統合にフォールバック
self.merge_text(source, filename, prelude_paths)
}
/// 🏗️ テキスト統合を組み立てるにゃ!
fn build_text_merged(
&self,
source: &str,
filename: &str,
prelude_paths: &[String],
trace: bool,
) -> Result<String, String> {
let mut merged = String::new();
// プレリュードをDFS順に追加
for (idx, path) in prelude_paths.iter().enumerate() {
let content = std::fs::read_to_string(path)
.map_err(|e| format!("using: failed to read '{}': {}", path, e))?;
// using行を除去して正規化
let using_resolver = UsingResolutionBox::new(&self.runner, path)?;
let (cleaned_raw, _nested) = self.collect_using_and_strip_internal(&content, path)?;
let cleaned = self.normalize_text_for_inline(&cleaned_raw);
if trace {
crate::runner::trace::log(format!(
"[prelude/text] [{}] '{}' ({} bytes)",
idx + 1,
path,
cleaned.len()
));
}
merged.push_str(&cleaned);
merged.push('\n');
}
// デバッグモードなら境界マーカーを追加
if std::env::var("NYASH_RESOLVE_SEAM_DEBUG").ok().as_deref() == Some("1") {
merged.push_str("\n/* --- using prelude/main boundary --- */\n\n");
}
// メインソースを正規化して追加
let cleaned_main = self.normalize_text_for_inline(source);
merged.push_str(&cleaned_main);
if trace {
crate::runner::trace::log(format!(
"[prelude/text] final merged: {} bytes ({} prelude + {} main)",
merged.len(),
merged.len() - cleaned_main.len(),
cleaned_main.len()
));
}
Ok(self.normalize_text_for_inline(&merged))
}
/// 🧹 using行を収集して除去するにゃ内部実装
fn collect_using_and_strip_internal(
&self,
code: &str,
filename: &str,
) -> Result<(String, Vec<String>), String> {
// 既存のcollect_using_and_strip関数を呼び出す
// TODO: 将来的にはUsingResolutionBox経由に置き換える
crate::runner::modes::common_util::resolve::strip::collect_using_and_strip(
&self.runner,
code,
filename,
)
}
/// 🔧 テキストを正規化するにゃ!
fn normalize_text_for_inline(&self, s: &str) -> String {
let mut out = s.replace("\r\n", "\n").replace("\r", "\n");
// `}` の前の `;` を除去(複数回パス)
for _ in 0..2 {
let mut tmp = String::with_capacity(out.len());
let bytes = out.as_bytes();
let mut i = 0usize;
while i < bytes.len() {
if bytes[i] == b';' {
// 先読みしてスペース/改行をスキップ
let mut j = i + 1;
while j < bytes.len() {
let c = bytes[j];
if c == b' ' || c == b'\t' || c == b'\n' {
j += 1;
} else {
break;
}
}
if j < bytes.len() && bytes[j] == b'}' {
// `;` をドロップ
i += 1;
continue;
}
}
tmp.push(bytes[i] as char);
i += 1;
}
out = tmp;
}
// ファイル末尾に改行を追加
if !out.ends_with('\n') {
out.push('\n');
}
out
}
/// 📊 最適な統合戦略を選択するにゃ!
pub fn select_strategy(&self, prelude_count: usize) -> MergeStrategy {
// 環境変数でAST統合が強制されている場合はASTを選択
if crate::config::env::using_ast_enabled() {
return MergeStrategy::Ast;
}
// プレリュード数が多い場合はテキスト統合を選択(高速)
if prelude_count > 5 {
return MergeStrategy::Text;
}
// デフォルトはテキスト統合
MergeStrategy::Text
}
/// 🚀 自動戦略選択で統合を実行するにゃ!
pub fn merge_auto(
&self,
source: &str,
filename: &str,
prelude_paths: &[String],
) -> Result<MergeResult, String> {
let strategy = self.select_strategy(prelude_paths.len());
match strategy {
MergeStrategy::Text => self.merge_text(source, filename, prelude_paths),
MergeStrategy::Ast => self.merge_ast(source, filename, prelude_paths),
}
}
}