Files
hakorune/src/boxes/basic/string_box.rs
nyash-codex f9d100ce01 chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
2025-11-21 06:25:17 +09:00

211 lines
6.3 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.

//! StringBox - String values in Nyash
//!
//! Implements the core StringBox type with all string manipulation methods.
use crate::box_trait::{BoxBase, BoxCore, NyashBox};
use crate::boxes::ArrayBox;
use std::any::Any;
use std::fmt::{Debug, Display};
/// String values in Nyash - UTF-8 strings with rich method support
#[derive(Debug, Clone, PartialEq)]
pub struct StringBox {
pub value: String,
base: BoxBase,
}
impl StringBox {
pub fn new(value: impl Into<String>) -> Self {
Self {
value: value.into(),
base: BoxBase::new(),
}
}
pub fn empty() -> Self {
Self::new("")
}
// ===== String Methods for Nyash =====
/// Split string by delimiter and return ArrayBox
pub fn split(&self, delimiter: &str) -> Box<dyn NyashBox> {
let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect();
let array_elements: Vec<Box<dyn NyashBox>> = parts
.into_iter()
.map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
.collect();
Box::new(ArrayBox::new_with_elements(array_elements))
}
/// Find substring and return position (or -1 if not found)
pub fn find(&self, search: &str) -> Box<dyn NyashBox> {
use crate::box_trait::IntegerBox;
match self.value.find(search) {
Some(pos) => Box::new(IntegerBox::new(pos as i64)),
None => Box::new(IntegerBox::new(-1)),
}
}
/// Replace all occurrences of old with new
pub fn replace(&self, old: &str, new: &str) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.replace(old, new)))
}
/// Trim whitespace from both ends
pub fn trim(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.trim()))
}
/// Convert to uppercase
pub fn to_upper(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_uppercase()))
}
/// Convert to lowercase
pub fn to_lower(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_lowercase()))
}
/// Check if string contains substring
pub fn contains(&self, search: &str) -> Box<dyn NyashBox> {
use crate::box_trait::BoolBox;
Box::new(BoolBox::new(self.value.contains(search)))
}
/// Check if string starts with prefix
pub fn starts_with(&self, prefix: &str) -> Box<dyn NyashBox> {
use crate::box_trait::BoolBox;
Box::new(BoolBox::new(self.value.starts_with(prefix)))
}
/// Check if string ends with suffix
pub fn ends_with(&self, suffix: &str) -> Box<dyn NyashBox> {
use crate::box_trait::BoolBox;
Box::new(BoolBox::new(self.value.ends_with(suffix)))
}
/// Join array elements using this string as delimiter
pub fn join(&self, array_box: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
let strings: Vec<String> = array
.items
.read()
.unwrap()
.iter()
.map(|element| element.to_string_box().value)
.collect();
Box::new(StringBox::new(strings.join(&self.value)))
} else {
// If not an ArrayBox, treat as single element
Box::new(StringBox::new(array_box.to_string_box().value))
}
}
/// Get string length
///
/// Env gate: NYASH_STR_CP=1 → count Unicode scalar values (chars),
/// otherwise use UTF-8 byte length (legacy/default).
pub fn length(&self) -> Box<dyn NyashBox> {
use crate::box_trait::IntegerBox;
let use_cp = std::env::var("NYASH_STR_CP").ok().as_deref() == Some("1");
let n = if use_cp {
self.value.chars().count() as i64
} else {
self.value.len() as i64
};
Box::new(IntegerBox::new(n))
}
/// size() エイリアスlength と同義)
pub fn size(&self) -> Box<dyn NyashBox> {
self.length()
}
/// Convert string to integer (parse as i64)
pub fn to_integer(&self) -> Box<dyn NyashBox> {
use crate::box_trait::IntegerBox;
match self.value.trim().parse::<i64>() {
Ok(n) => Box::new(IntegerBox::new(n)),
Err(_) => {
// If parsing fails, return 0 (JavaScript-like behavior)
Box::new(IntegerBox::new(0))
}
}
}
/// Get character at index
pub fn get(&self, index: usize) -> Option<Box<dyn NyashBox>> {
if let Some(ch) = self.value.chars().nth(index) {
Some(Box::new(StringBox::new(ch.to_string())))
} else {
None
}
}
/// Get substring from start to end (exclusive)
pub fn substring(&self, start: usize, end: usize) -> Box<dyn NyashBox> {
let chars: Vec<char> = self.value.chars().collect();
let actual_end = end.min(chars.len());
let actual_start = start.min(actual_end);
let substring: String = chars[actual_start..actual_end].iter().collect();
Box::new(StringBox::new(substring))
}
}
impl BoxCore for StringBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.value)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for StringBox {
fn to_string_box(&self) -> StringBox {
self.clone()
}
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
use crate::box_trait::BoolBox;
if let Some(other_string) = other.as_any().downcast_ref::<StringBox>() {
BoolBox::new(self.value == other_string.value)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"StringBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
impl Display for StringBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}