feat(runtime): Phase 87 CoreBoxId/CoreMethodId 箱化完了
ハードコード文字列から型安全な enum への箱化により、 Box名・メソッド名管理を完全にコンパイル時検証可能に。 主な実装: - CoreBoxId enum 定義(19個) - core_required: 6個(String, Integer, Bool, Array, Map, Console) - core_optional: 9個(Float, Null, File, Path, Regex, Math, Time, Json, Toml) - 特殊型: 4個(Function, Result, Method, Missing) - CoreMethodId enum 定義(30個) - 各 Box のメソッドを型安全に管理 - 引数数、戻り値型情報を統合 - is_reserved_type() を CoreBoxId ベースにリファクタリング - infer_boxcall_return_type() を CoreMethodId ベースに改良(75行 → 25行、67%削減) 検証結果: - テスト: ✅ 11/11 passed(新規追加) - ビルド: ✅ 成功(0エラー) - 型安全性: ✅ タイポ不可能 効果: - SSOT 確立(src/runtime/core_box_ids.rs に一元化) - コンパイル時検証(実行時エラー → コンパイルエラー) - 保守性向上(変更箇所の一元化) - IDE 支援(enum 補完可能) ドキュメント: - core_boxes_design.md 作成(Phase 87 完全仕様) - Phase 85 README 更新(Phase 87 セクション追加) Phase 15.5「Everything is Plugin」アーキテクチャ基盤完成 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
417
src/runtime/core_box_ids.rs
Normal file
417
src/runtime/core_box_ids.rs
Normal file
@ -0,0 +1,417 @@
|
||||
//! Phase 87: Core Box ID 定義
|
||||
//!
|
||||
//! Nyash の core Box を型安全な enum で管理する。
|
||||
//! ハードコード文字列からの脱却により、コンパイル時検証を実現。
|
||||
|
||||
/// Phase 85 調査結果に基づく Core Box ID
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum CoreBoxId {
|
||||
// ===== Phase 85: core_required (6個) =====
|
||||
/// StringBox - 文字列基本型
|
||||
String,
|
||||
/// IntegerBox - 整数基本型
|
||||
Integer,
|
||||
/// BoolBox - 真偽値基本型
|
||||
Bool,
|
||||
/// ArrayBox - 配列基本型
|
||||
Array,
|
||||
/// MapBox - マップ基本型
|
||||
Map,
|
||||
/// ConsoleBox - コンソール入出力
|
||||
Console,
|
||||
|
||||
// ===== Phase 85: core_optional (9個) =====
|
||||
/// FloatBox - 浮動小数点数
|
||||
Float,
|
||||
/// NullBox - null値
|
||||
Null,
|
||||
/// FileBox - ファイル操作
|
||||
File,
|
||||
/// PathBox - パス操作
|
||||
Path,
|
||||
/// RegexBox - 正規表現
|
||||
Regex,
|
||||
/// MathBox - 数学関数
|
||||
Math,
|
||||
/// TimeBox - 時刻操作
|
||||
Time,
|
||||
/// JsonBox - JSON操作
|
||||
Json,
|
||||
/// TomlBox - TOML操作
|
||||
Toml,
|
||||
|
||||
// ===== 特殊型 =====
|
||||
/// FunctionBox - 第一級関数
|
||||
Function,
|
||||
/// ResultBox - Result型(QMark対応)
|
||||
Result,
|
||||
/// MethodBox - メソッド
|
||||
Method,
|
||||
/// MissingBox - 欠損値
|
||||
Missing,
|
||||
}
|
||||
|
||||
/// Phase 87: Core Box カテゴリ
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CoreBoxCategory {
|
||||
/// Phase 85: 必須(予約名保護)
|
||||
CoreRequired,
|
||||
/// Phase 85: 推奨(デフォルトロード)
|
||||
CoreOptional,
|
||||
/// 特殊型
|
||||
Special,
|
||||
}
|
||||
|
||||
impl CoreBoxId {
|
||||
/// Box名を返す(例: "StringBox")
|
||||
pub fn name(&self) -> &'static str {
|
||||
use CoreBoxId::*;
|
||||
match self {
|
||||
String => "StringBox",
|
||||
Integer => "IntegerBox",
|
||||
Bool => "BoolBox",
|
||||
Array => "ArrayBox",
|
||||
Map => "MapBox",
|
||||
Console => "ConsoleBox",
|
||||
Float => "FloatBox",
|
||||
Null => "NullBox",
|
||||
File => "FileBox",
|
||||
Path => "PathBox",
|
||||
Regex => "RegexBox",
|
||||
Math => "MathBox",
|
||||
Time => "TimeBox",
|
||||
Json => "JsonBox",
|
||||
Toml => "TomlBox",
|
||||
Function => "FunctionBox",
|
||||
Result => "ResultBox",
|
||||
Method => "MethodBox",
|
||||
Missing => "MissingBox",
|
||||
}
|
||||
}
|
||||
|
||||
/// 全CoreBoxIdを反復
|
||||
pub fn iter() -> impl Iterator<Item = CoreBoxId> {
|
||||
use CoreBoxId::*;
|
||||
[
|
||||
String, Integer, Bool, Array, Map, Console,
|
||||
Float, Null, File, Path, Regex, Math, Time, Json, Toml,
|
||||
Function, Result, Method, Missing,
|
||||
].into_iter()
|
||||
}
|
||||
|
||||
/// 名前からCoreBoxIdを取得
|
||||
pub fn from_name(name: &str) -> Option<CoreBoxId> {
|
||||
Self::iter().find(|id| id.name() == name)
|
||||
}
|
||||
|
||||
/// Phase 86: core_required チェック
|
||||
pub fn is_core_required(&self) -> bool {
|
||||
use CoreBoxId::*;
|
||||
matches!(self, String | Integer | Bool | Array | Map | Console)
|
||||
}
|
||||
|
||||
/// Phase 87: カテゴリ分類
|
||||
pub fn category(&self) -> CoreBoxCategory {
|
||||
use CoreBoxId::*;
|
||||
match self {
|
||||
String | Integer | Bool | Array | Map | Console => CoreBoxCategory::CoreRequired,
|
||||
Float | Null | File | Path | Regex | Math | Time | Json | Toml => CoreBoxCategory::CoreOptional,
|
||||
Function | Result | Method | Missing => CoreBoxCategory::Special,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 87: Core Method ID 定義
|
||||
///
|
||||
/// Box のメソッドを型安全に管理。
|
||||
/// Phase 84-4-B のハードコード型情報を統合。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum CoreMethodId {
|
||||
// ===== StringBox methods =====
|
||||
StringLength,
|
||||
StringUpper,
|
||||
StringLower,
|
||||
StringConcat,
|
||||
StringSubstring,
|
||||
StringReplace,
|
||||
StringTrim,
|
||||
StringSplit,
|
||||
|
||||
// ===== IntegerBox methods =====
|
||||
IntegerAbs,
|
||||
IntegerMin,
|
||||
IntegerMax,
|
||||
|
||||
// ===== BoolBox methods =====
|
||||
BoolNot,
|
||||
BoolAnd,
|
||||
BoolOr,
|
||||
|
||||
// ===== ArrayBox methods =====
|
||||
ArrayLength,
|
||||
ArrayPush,
|
||||
ArrayPop,
|
||||
ArrayGet,
|
||||
|
||||
// ===== MapBox methods =====
|
||||
MapGet,
|
||||
MapSet,
|
||||
MapHas,
|
||||
MapKeys,
|
||||
|
||||
// ===== ConsoleBox methods =====
|
||||
ConsolePrintln,
|
||||
ConsoleLog,
|
||||
ConsoleError,
|
||||
|
||||
// ===== FileBox methods =====
|
||||
FileRead,
|
||||
FileWrite,
|
||||
FileOpen,
|
||||
|
||||
// ===== ResultBox methods (QMark 対応) =====
|
||||
ResultIsOk,
|
||||
ResultGetValue,
|
||||
}
|
||||
|
||||
impl CoreMethodId {
|
||||
/// メソッドが属する Box ID
|
||||
pub fn box_id(&self) -> CoreBoxId {
|
||||
use CoreMethodId::*;
|
||||
match self {
|
||||
StringLength | StringUpper | StringLower |
|
||||
StringConcat | StringSubstring | StringReplace |
|
||||
StringTrim | StringSplit => CoreBoxId::String,
|
||||
|
||||
IntegerAbs | IntegerMin | IntegerMax => CoreBoxId::Integer,
|
||||
|
||||
BoolNot | BoolAnd | BoolOr => CoreBoxId::Bool,
|
||||
|
||||
ArrayLength | ArrayPush | ArrayPop | ArrayGet => CoreBoxId::Array,
|
||||
|
||||
MapGet | MapSet | MapHas | MapKeys => CoreBoxId::Map,
|
||||
|
||||
ConsolePrintln | ConsoleLog | ConsoleError => CoreBoxId::Console,
|
||||
|
||||
FileRead | FileWrite | FileOpen => CoreBoxId::File,
|
||||
|
||||
ResultIsOk | ResultGetValue => CoreBoxId::Result,
|
||||
}
|
||||
}
|
||||
|
||||
/// メソッド名(例: "length")
|
||||
pub fn name(&self) -> &'static str {
|
||||
use CoreMethodId::*;
|
||||
match self {
|
||||
StringLength => "length",
|
||||
StringUpper => "upper",
|
||||
StringLower => "lower",
|
||||
StringConcat => "concat",
|
||||
StringSubstring => "substring",
|
||||
StringReplace => "replace",
|
||||
StringTrim => "trim",
|
||||
StringSplit => "split",
|
||||
|
||||
IntegerAbs => "abs",
|
||||
IntegerMin => "min",
|
||||
IntegerMax => "max",
|
||||
|
||||
BoolNot => "not",
|
||||
BoolAnd => "and",
|
||||
BoolOr => "or",
|
||||
|
||||
ArrayLength => "length",
|
||||
ArrayPush => "push",
|
||||
ArrayPop => "pop",
|
||||
ArrayGet => "get",
|
||||
|
||||
MapGet => "get",
|
||||
MapSet => "set",
|
||||
MapHas => "has",
|
||||
MapKeys => "keys",
|
||||
|
||||
ConsolePrintln => "println",
|
||||
ConsoleLog => "log",
|
||||
ConsoleError => "error",
|
||||
|
||||
FileRead => "read",
|
||||
FileWrite => "write",
|
||||
FileOpen => "open",
|
||||
|
||||
ResultIsOk => "isOk",
|
||||
ResultGetValue => "getValue",
|
||||
}
|
||||
}
|
||||
|
||||
/// 引数の数
|
||||
pub fn arity(&self) -> usize {
|
||||
use CoreMethodId::*;
|
||||
match self {
|
||||
StringLength | StringUpper | StringLower | StringTrim |
|
||||
IntegerAbs |
|
||||
BoolNot |
|
||||
ArrayLength | ArrayPop |
|
||||
MapKeys |
|
||||
ResultIsOk | ResultGetValue => 0,
|
||||
|
||||
StringConcat | StringSubstring | StringReplace | StringSplit |
|
||||
IntegerMin | IntegerMax |
|
||||
BoolAnd | BoolOr |
|
||||
ArrayGet | ArrayPush |
|
||||
MapGet | MapHas |
|
||||
ConsolePrintln | ConsoleLog | ConsoleError |
|
||||
FileRead | FileWrite | FileOpen => 1,
|
||||
|
||||
MapSet => 2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 84-4-B: 戻り値型(型推論用)
|
||||
pub fn return_type_name(&self) -> &'static str {
|
||||
use CoreMethodId::*;
|
||||
match self {
|
||||
StringLength | ArrayLength => "IntegerBox",
|
||||
|
||||
StringUpper | StringLower | StringConcat | StringSubstring |
|
||||
StringReplace | StringTrim => "StringBox",
|
||||
|
||||
IntegerAbs | IntegerMin | IntegerMax => "IntegerBox",
|
||||
|
||||
BoolNot | BoolAnd | BoolOr | MapHas | ResultIsOk => "BoolBox",
|
||||
|
||||
ArrayPush | ArrayPop | MapSet |
|
||||
ConsolePrintln | ConsoleLog | ConsoleError |
|
||||
FileWrite => "Void",
|
||||
|
||||
ArrayGet | MapGet | MapKeys | StringSplit => "Unknown",
|
||||
|
||||
FileRead => "StringBox",
|
||||
FileOpen => "FileBox",
|
||||
ResultGetValue => "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
/// 全CoreMethodIdを反復
|
||||
pub fn iter() -> impl Iterator<Item = CoreMethodId> {
|
||||
use CoreMethodId::*;
|
||||
[
|
||||
StringLength, StringUpper, StringLower, StringConcat, StringSubstring,
|
||||
StringReplace, StringTrim, StringSplit,
|
||||
IntegerAbs, IntegerMin, IntegerMax,
|
||||
BoolNot, BoolAnd, BoolOr,
|
||||
ArrayLength, ArrayPush, ArrayPop, ArrayGet,
|
||||
MapGet, MapSet, MapHas, MapKeys,
|
||||
ConsolePrintln, ConsoleLog, ConsoleError,
|
||||
FileRead, FileWrite, FileOpen,
|
||||
ResultIsOk, ResultGetValue,
|
||||
].into_iter()
|
||||
}
|
||||
|
||||
/// Box名とメソッド名から CoreMethodId を取得
|
||||
pub fn from_box_and_method(box_id: CoreBoxId, method: &str) -> Option<CoreMethodId> {
|
||||
Self::iter().find(|m| m.box_id() == box_id && m.name() == method)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// ===== CoreBoxId tests =====
|
||||
|
||||
#[test]
|
||||
fn test_core_box_id_from_name() {
|
||||
assert_eq!(CoreBoxId::from_name("StringBox"), Some(CoreBoxId::String));
|
||||
assert_eq!(CoreBoxId::from_name("IntegerBox"), Some(CoreBoxId::Integer));
|
||||
assert_eq!(CoreBoxId::from_name("BoolBox"), Some(CoreBoxId::Bool));
|
||||
assert_eq!(CoreBoxId::from_name("UnknownBox"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_box_id_name() {
|
||||
assert_eq!(CoreBoxId::String.name(), "StringBox");
|
||||
assert_eq!(CoreBoxId::Integer.name(), "IntegerBox");
|
||||
assert_eq!(CoreBoxId::Console.name(), "ConsoleBox");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_box_id_iter() {
|
||||
let count = CoreBoxId::iter().count();
|
||||
assert_eq!(count, 19); // Phase 87: 19個の CoreBox
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_box_id_is_core_required() {
|
||||
// Phase 85: core_required (6個)
|
||||
assert!(CoreBoxId::String.is_core_required());
|
||||
assert!(CoreBoxId::Integer.is_core_required());
|
||||
assert!(CoreBoxId::Bool.is_core_required());
|
||||
assert!(CoreBoxId::Array.is_core_required());
|
||||
assert!(CoreBoxId::Map.is_core_required());
|
||||
assert!(CoreBoxId::Console.is_core_required());
|
||||
|
||||
// Phase 85: core_optional
|
||||
assert!(!CoreBoxId::File.is_core_required());
|
||||
assert!(!CoreBoxId::Float.is_core_required());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_box_id_category() {
|
||||
assert_eq!(CoreBoxId::String.category(), CoreBoxCategory::CoreRequired);
|
||||
assert_eq!(CoreBoxId::File.category(), CoreBoxCategory::CoreOptional);
|
||||
assert_eq!(CoreBoxId::Function.category(), CoreBoxCategory::Special);
|
||||
}
|
||||
|
||||
// ===== CoreMethodId tests =====
|
||||
|
||||
#[test]
|
||||
fn test_core_method_id_box_id() {
|
||||
assert_eq!(CoreMethodId::StringLength.box_id(), CoreBoxId::String);
|
||||
assert_eq!(CoreMethodId::ArrayPush.box_id(), CoreBoxId::Array);
|
||||
assert_eq!(CoreMethodId::ConsolePrintln.box_id(), CoreBoxId::Console);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_method_id_name() {
|
||||
assert_eq!(CoreMethodId::StringLength.name(), "length");
|
||||
assert_eq!(CoreMethodId::ArrayPush.name(), "push");
|
||||
assert_eq!(CoreMethodId::ConsolePrintln.name(), "println");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_method_id_arity() {
|
||||
assert_eq!(CoreMethodId::StringLength.arity(), 0);
|
||||
assert_eq!(CoreMethodId::StringConcat.arity(), 1);
|
||||
assert_eq!(CoreMethodId::MapSet.arity(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_method_id_return_type() {
|
||||
assert_eq!(CoreMethodId::StringLength.return_type_name(), "IntegerBox");
|
||||
assert_eq!(CoreMethodId::StringUpper.return_type_name(), "StringBox");
|
||||
assert_eq!(CoreMethodId::BoolNot.return_type_name(), "BoolBox");
|
||||
assert_eq!(CoreMethodId::ArrayPush.return_type_name(), "Void");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_method_id_from_box_and_method() {
|
||||
assert_eq!(
|
||||
CoreMethodId::from_box_and_method(CoreBoxId::String, "length"),
|
||||
Some(CoreMethodId::StringLength)
|
||||
);
|
||||
assert_eq!(
|
||||
CoreMethodId::from_box_and_method(CoreBoxId::Array, "push"),
|
||||
Some(CoreMethodId::ArrayPush)
|
||||
);
|
||||
assert_eq!(
|
||||
CoreMethodId::from_box_and_method(CoreBoxId::String, "unknown_method"),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_method_id_iter() {
|
||||
let count = CoreMethodId::iter().count();
|
||||
assert!(count >= 27); // Phase 87: 27個以上のメソッド
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
//! プラグインシステムとBox管理の中核
|
||||
|
||||
pub mod box_registry;
|
||||
pub mod core_box_ids; // Phase 87: CoreBoxId/CoreMethodId 型安全enum
|
||||
pub mod deprecations;
|
||||
pub mod gc;
|
||||
pub mod gc_controller;
|
||||
@ -35,6 +36,7 @@ pub mod type_registry; // Phase 12: TypeId→TypeBox 解決(雛形) // env.m
|
||||
mod tests;
|
||||
|
||||
pub use box_registry::{get_global_registry, BoxFactoryRegistry, BoxProvider};
|
||||
pub use core_box_ids::{CoreBoxCategory, CoreBoxId, CoreMethodId}; // Phase 87: 型安全enum
|
||||
pub use plugin_config::PluginConfig;
|
||||
pub use plugin_loader_unified::{
|
||||
get_global_plugin_host, init_global_plugin_host, MethodHandle, PluginBoxType, PluginHost,
|
||||
|
||||
Reference in New Issue
Block a user