📚 Phase 173 前半完了(Task 1-3)! ✅ Task 1: 名前解決経路調査 - UsingResolverBox / CalleeResolverBox 構造把握 - 3つの重大問題発見: 1. 静的 Box が InstanceBox として扱われる 2. JsonParserBox _parse_number 無限ループ 3. new Alias.BoxName() 構文未サポート ✅ Task 2: 仕様固定(docs) - using.md: +179行(静的 Box 使用例) - LANGUAGE_REFERENCE_2025.md: +54行(static box ライブラリ方針) ✅ Task 3: JsonParserBox バグ修正 - MIR Nested-If-in-Loop Bug 発見・回避 - while → loop() 構文統一(10箇所) - ネスト if-else のフラット化 📋 成果物: - phase173_task1_investigation.md(220行) - phase173_using_static_box_resolution.md - phase173-2_using_resolver_mir_lowering.md(後半指示書) - mir-nested-if-loop-bug.md(バグ分析) - json_parser_min.hako(テストファイル) 🎯 次: Task 4-6(using resolver + MIR lowering 統合) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
21 KiB
using — Imports and Namespaces (Phase 15+)
実装状況: Phase 15.5後に本格実装予定 | 基本ドット記法は実装済み
Status: Accepted (Runner‑side resolution). Using is resolved by the Runner; prelude is merged as text (DFS) before parsing/execution.
Phase 20.36 更新
- 依存の唯一の真実(SSOT):
nyash.tomlの[using](aliases/packages/paths) - 実体の合成は“テキスト統合(merge_prelude_text)”に一本化(AST マージは撤退)
- プロファイル運用:
NYASH_USING_PROFILE={dev|ci|prod}で厳格度を段階的に切替- dev: toml + ファイル内 using を許可(実験/bring‑up)。
- ci: toml 優先、ファイル using は警告または限定許可。
- prod: toml のみ。ファイル using/path はエラー(追記ガイドを提示)。
🎯 設計思想:Everything has Namespace
核心コンセプト
すべてのBox、関数、メンバーが明確な名前空間を持ち、衝突・曖昧性を根本解決。
// ✅ 実装済み:ドット記法
network.HttpClient() // プラグイン修飾名
plugin.network.HttpClient() // フルパス
// 🚧 Phase 15.5後:明示的スコープ演算子
::print("global") // グローバルスコープ
builtin::StringBox("test") // 内蔵版明示
plugin::StringBox("test") // プラグイン版明示
MIR Callee革新との統合
MIR Callee革新設計と完全統合:
// Phase 1: 型安全関数呼び出し(実装済み)
pub enum Callee {
Global(String), // ::print, global::func
Method { box_name, method, receiver }, // obj.method()
Extern(String), // nyash.console.log
Value(ValueId), // 第一級関数
}
// Phase 3: 完全修飾名対応(Phase 15.5後)
pub enum QualifiedCallee {
Qualified { namespace: Vec<String>, name: String },
Scoped { scope: ScopeKind, name: String },
}
📊 実装状況
✅ 現在実装済み
- ドット記法:
plugin.BoxName、namespace.member - using基本構文: ファイルトップでの宣言
- エイリアス:
using long.path as Alias - プラグイン修飾:
network.HttpClient
🚧 Phase 15.5後実装予定
- built-in namespace:
builtin.StringBoxvsplugin.StringBox - 完全修飾名:
nyash.builtin.print、std.console.log - スコープ演算子:
::global_func、Type::static_method - 厳密解決: コンパイル時名前空間検証
Policy(Runner前処理)
- Accept
usinglines at the top of the file to declare module namespaces or file imports. - Resolution is performed by the Rust Runner when
NYASH_ENABLE_USING=1. - 実体の結合はテキスト統合(merge_prelude_text)。AST マージ経路は撤退。
- Runner は
nyash.tomlの[using]を唯一の真実として参照(prod)。dev/ci は段階的に緩和可能。 - Selfhost compiler (Ny→JSON v0) collects using lines and emits
meta.usingswhen present. The bridge currently ignores this meta field. - Prelude の中にさらに
usingが含まれている場合は、Runner が再帰的にusingをストリップしてから AST として取り込みます(入れ子の前処理をサポート)。 - パス解決の順序(dev/ci): 呼び出し元ファイルのディレクトリ →
$NYASH_ROOT→ 実行バイナリからのプロジェクトルート推定(target/release/nyash の 3 階層上)→nyash.tomlの[using.paths]。
Deprecated: include
- 言語仕様としてはサポートしない(VM/コンパイラともに受理しない)。
- 例外は開発支援用の前処理(preinclude)のみ。実行系や言語仕様の責務ではなく、テストハーネスからフラグで明示的に有効化する。
- Flags:
NYASH_PREINCLUDE=1/HAKO_PREINCLUDE=1(既定OFF) - quick プロファイルでは include 依存は既定で SKIP(
SMOKES_INCLUDE_POLICY=skip|warn|error。順次 ERROR へ移行予定)。 - 本番(prod)では using/alias のみを正道に固定。
using "path"は開発限定(NYASH_ALLOW_USING_FILE=1)で運用する。
- Flags:
Namespace Resolution (Runner‑side)
- Goal: keep IR/VM/JIT untouched. All resolution happens in Runner/Registry.
- Default search order (3 stages, deterministic):
- Local/Core Boxes (nyrt)
- Aliases (nyash.toml [imports] /
needs … as …) - Plugins (short name if unique, otherwise qualified
pluginName.BoxName)
- On ambiguity: error with candidates and remediation (qualify or define alias).
- Modes:
- Relaxed (default): short names allowed when unique。
- Strict: plugin短名にprefix必須(env
NYASH_PLUGIN_REQUIRE_PREFIX=1または nyash.toml[plugins] require_prefix=true)。
- Aliases:
- nyash.toml
[imports] HttpClient = "network.HttpClient" - needs sugar:
needs plugin.network.HttpClient as HttpClient(file‑scoped alias)
- nyash.toml
📦 静的 Box の using(Phase 173+)
実装状況: Phase 173 で実装中(2025-12-04)
基本概念
静的 Box(static box)をライブラリとして using で import し、静的メソッドを呼び出す機能。
// 静的 Box の定義(ライブラリ側)
static box JsonParserBox {
method parse(json_str) {
// JSON文字列をパース
return me._parse_value(json_str, 0).get("value")
}
_parse_value(s, pos) {
// 内部メソッド(private的な扱い)
// ...
}
}
// 静的 Box の使用(呼び出し側)
using tools.hako_shared.json_parser as JsonParserBox
static box Main {
main() {
// 静的メソッド呼び出し(インスタンス化不要)
local result = JsonParserBox.parse("{\"x\":1}")
// 結果の利用
if result != null {
local x = result.get("x")
print("x = " + x)
}
return 0
}
}
許容される呼び出しパターン
✅ Phase 173 で実装済み
- 静的メソッド直接呼び出し
using tools.hako_shared.json_parser as JsonParserBox local value = JsonParserBox.parse("{\"x\":1}")JsonParserBoxは静的 Box のエイリアスparse()は静的メソッド- インスタンス化不要
🚧 Phase 174+ で実装予定
- 名前空間的な Box アクセス(未実装)
using lib.containers as Containers local list = new Containers.ListBox() // エラー(Phase 173 時点) local map = new Containers.MapBox() // エラー(Phase 173 時点)new Alias.BoxName()構文は未サポート- パーサーエラー:
Unexpected token DOT, expected LPAREN
静的 Box vs インスタンス Box
| 特徴 | 静的 Box | インスタンス Box |
|---|---|---|
| 定義 | static box BoxName { } |
box BoxName { } |
| インスタンス化 | 不要(シングルトン) | 必要(new BoxName()) |
| メソッド呼び出し | BoxName.method() |
instance.method() |
| 状態保持 | 共有状態(グローバル的) | インスタンスごと |
| using での扱い | 型として登録 | 型として登録 |
| 典型的用途 | ユーティリティ、パーサー | データ構造、オブジェクト |
実装の技術的詳細
名前解決の流れ
-
using statement 解決(
.hako側)UsingResolverBox.resolve_path_alias()でファイルパス解決- エイリアスを「型」として環境に登録(Phase 173 で実装)
-
AST 生成(
.hakoパーサー)Alias.method()を静的Box呼び出しとして認識- フラグ
is_static_box_call: trueを付与(Phase 173 で実装)
-
MIR lowering(Rust 側)
CalleeResolverBox.resolve()で Callee に変換- 静的Box呼び出しは
Callee::Global("BoxName.method/arity")に解決 - VM 実行時に正しく静的メソッドとして呼び出される
Phase 171-2 で発見された問題
症状:
[ERROR] ❌ [rust-vm] VM error: Unknown method '_skip_whitespace' on InstanceBox
原因:
- using で import した静的 Box が「インスタンス」として扱われていた
- 内部メソッド(
_skip_whitespace等)が解決できず
解決: Phase 173 で using system を修正し、静的 Box を型として正しく登録
使用例
JsonParserBox の利用
using tools.hako_shared.json_parser as JsonParserBox
static box HakoAnalysisBuilderBox {
_extract_cfg_from_mir_json(json_text) {
// JsonParserBox を使った JSON パース
local root = JsonParserBox.parse(json_text)
if root == null { return me._empty_cfg() }
// CFG オブジェクトの抽出
local cfg = root.get("cfg")
if cfg == null { return me._empty_cfg() }
local functions = cfg.get("functions")
// ... 以下、CFG 処理続行
}
}
ProgramJSONBox の利用(Phase 172)
using tools.hako_shared.json_parser as JsonParserBox
static box Compiler {
load_program(json_str) {
// Program JSON v0 をパース
local program = JsonParserBox.parse_program(json_str)
if program == null { return null }
// ProgramJSONBox インスタンスから情報取得
local version = program.get_version() // "0"
local kind = program.get_kind() // "program"
local defs = program.get_defs() // ArrayBox
return program
}
}
制限事項(Phase 173 時点)
-
パーサー制限
new Alias.BoxName()構文は未サポート- 静的 Box 内の Box 定義をインスタンス化できない
-
名前空間階層
Alias.Namespace.BoxNameのような多階層は未サポート- エイリアスは1階層のみ
-
型推論
- 静的メソッドの戻り値型は動的(MapBox/ArrayBox 等)
- コンパイル時型チェックなし
Phase 174+ での拡張予定
-
名前空間的 Box アクセス
new Alias.BoxName()構文のサポート- パーサー修正による対応
-
型システム統合
- HIR 層の導入
- 静的型推論の強化
-
明示的スコープ
::演算子のサポートBoxName::static_method()構文
Plugins
- Unified namespace with Boxes. Prefer short names when unique.
- Qualified form:
network.HttpClient - Per‑plugin control (nyash.toml):
prefix,require_prefix,expose_short_names- 現状は設定の読み取りのみ(導線)。挙動への反映は段階的に実施予定。
needs sugar (optional)
- Treated as a synonym to
usingon the Runner side; registers aliases only. - Examples:
needs utils.StringHelper,needs plugin.network.HttpClient as HttpClient,needs plugin.network.*
nyash.toml — Unified Using(唯一の真実 / SSOT)
Using resolution is centralized under the [using] table. Three forms are supported:
[using.paths]— additional search roots for path lookups- Example:
paths = ["apps", "lib", "."]
- Example:
[using.<name>]— named packages (file or directory)- Keys:
path = "lib/math_utils/", optionalmain = "math_utils.hako" - Optional
kind = "dylib"withbid = "MathBox"for plug‑ins (dev only)
- Keys:
[using.aliases]— alias mapping from short name to a package name- Example:
aliases.json = "json_native"
- Example:
Notes
- Aliases are fully resolved:
using jsonfirst rewrites tojson_native, then resolves to a concrete path via[using.json_native]. includeは廃止。代替はusing "./path/to/file.hako" as Name。prod ではnyash.tomlへの登録が必須。
Development toggles
- Resolution is performed by the Runner when
NYASH_ENABLE_USING=1(既定ON)。 - Prelude は常にテキスト統合(DFS/循環検出/キャッシュ)。
NYASH_USING_ASTは後方互換のために残るが AST マージは行わない。 NYASH_RESOLVE_TRACE=1で解決ログ(cache‑hit/候補/未解決)を出力。- 前処理は最小 normalize を適用(CRLF→LF、
}直前の冗長;を除去、EOF 改行付加)。prod のコードスタイルに依存しないこと。
Dylib autoload (dev guard)
- Enable autoload during using resolution: set env
NYASH_USING_DYLIB_AUTOLOAD=1. - Resolution returns a token
dylib:<path>; when autoload is on, Runner calls the plugin host toload_library_direct(lib_name, path, boxes). boxesis taken from[using.<name>].bidif present; otherwise the loader falls back to plugin‑embedded TypeBox metadata.- Safety: keep OFF by default. Prefer configuring libraries under
nyash.tomlfor production.
Index and Cache (Runner)
- BoxIndex(グローバル):プラグインBox一覧とaliasesを集約し、Runner起動時(plugins init後)に構築・更新。
aliases: HashMap<String,String>(nyash.toml[aliases]と envNYASH_ALIASES)plugin_boxes: HashSet<String>(読み取り専用)
- 解決キャッシュ:グローバルの小さなキャッシュで同一キーの再解決を回避(キー:
tgt|base|strict|paths)。 - トレース:
NYASH_RESOLVE_TRACE=1で解決手順やキャッシュヒット、未解決候補を出力。
Syntax
- Namespace:
using core.stdorusing core.std as Std - File path:
using "apps/examples/string_p0.hako" as Strings - Relative path is allowed; absolute paths are discouraged.
Style
- Place all
usinglines at the top of the file, before any code. - One using per line; avoid trailing semicolons. Newline separation is preferred.
- Order: sort alphabetically by target. Group namespaces before file paths.
- Prefer an explicit alias (
as ...) when the target is long. Suggested alias style isPascalCase(e.g.,Std,Json,UI).
Examples
using core.std as Std
using "apps/examples/string_p0.hako" as Strings
static box Main {
main(args) {
local console = new ConsoleBox()
console.println("hello")
return 0
}
}
nyash.toml examples
[using]
paths = ["apps", "lib", "."]
[using.json_native]
path = "apps/lib/json_native/"
main = "parser.hako"
[using.aliases]
json = "json_native"
# Dylib (dev)
[using.math_plugin]
kind = "dylib"
path = "plugins/math/libmath.so"
bid = "MathBox"
Qualified/Plugins/Aliases examples
# nyash.toml
[plugins.network]
path = "plugins/network.so"
prefix = "network"
require_prefix = false
[imports]
HttpClient = "network.HttpClient"
# code
needs plugin.network.HttpClient as HttpClient
static box Main {
main(args) {
let a = new HttpClient() # alias
let b = new network.HttpClient() # qualified
}
}
📌 Note: Type::static_method と宣言構文について
- 呼び出し側では
Type::static_method(...)のように「型にぶら下がった関数」を使うことができるが、 これはあくまで 呼び出し構文 だよ。 - 宣言側の構文としては
static boxの中に通常のメソッドを定義する のが Stage‑3 仕様の正しい形で、static method foo() { ... }のようなトップレベル/Box外の宣言構文は legacy/非推奨 であり、今後の実装では使用しない。
Runner Configuration
- Enable using pre‑processing:
NYASH_ENABLE_USING=1 - CLI from-the-top registration:
--using "ns as Alias"or--using '"apps/foo.hako" as Foo'(repeatable) - Using profiles (phase‑in):
NYASH_USING_PROFILE={dev|ci|prod}- dev: AST マージ 既定ON、legacy前置きは既定で無効(必要時は
NYASH_LEGACY_USING_ALLOW=1で一時許可) - ci: AST マージ 既定ON、legacy前置きは既定で無効(同上の一時許可)
- prod: AST マージ 既定OFF、toml のみ(file using/path はエラー・追記ガイド)
- dev: AST マージ 既定ON、legacy前置きは既定で無効(必要時は
- Strict mode (plugin prefix required):
NYASH_PLUGIN_REQUIRE_PREFIX=1またはnyash.tomlの[plugins] require_prefix=true - Aliases from env:
NYASH_ALIASES="Foo=apps/foo/main.hako,Bar=lib/bar.hako" - Additional search paths:
NYASH_USING_PATH="apps:lib:." - Selfhost pipeline keeps child stdout quiet and extracts JSON only:
NYASH_JSON_ONLY=1(set by Runner automatically for child) - Selfhost emits
meta.usingsautomatically when present; no additional flags required.
Note: Provider/Type 分離(型名は不変で提供者のみを切替)については ADR を参照。
docs/development/adr/adr-001-no-corebox-everything-is-plugin.md
🔬 Quick Smokes(AST + Profiles)
開発・CIで最小コストに確認できるスモークを用意しています。AST プレリュードとプロファイル(dev/prod)の基本動作をカバーします。
- dev:
using "file"許可 + AST マージ - prod:
using "file"禁止(toml へ誘導) / alias・package は許可
実行例(quick プロファイル)
# 1) dev で file using が通る(AST マージ)
./tools/smokes/v2/run.sh --profile quick --filter "using_profiles_ast.sh$"
# 2) 相対パス using(サブディレクトリ)
./tools/smokes/v2/run.sh --profile quick --filter "using_relative_file_ast.sh$"
# 3) 複数プレリュード(toml packages)+ 依存(B→A)
./tools/smokes/v2/run.sh --profile quick --filter "using_multi_prelude_dep_ast.sh$"
テストソース
tools/smokes/v2/profiles/quick/core/using_profiles_ast.shtools/smokes/v2/profiles/quick/core/using_relative_file_ast.shtools/smokes/v2/profiles/quick/core/using_multi_prelude_dep_ast.sh
注意
- ログに
[using] stripped line:が出力されますが、これは AST マージ前の using 行の除去ログです(機能上問題ありません)。 - 実行バイナリは
target/release/nyashを前提とします。未ビルド時はcargo build --releaseを実行してください。
🔗 関連ドキュメント
設計・アーキテクチャ
- MIR Callee革新設計 - 型安全関数呼び出し
- Phase 15.5 Core Box統一 - プラグイン統一計画
- Box Factory設計 - builtin vs plugin優先順位
実装ガイド
- Callee実装ロードマップ
- プラグインシステム - プラグイン開発ガイド
- 完全言語リファレンス - 全構文仕様
📝 実装ノート
Notes
- Phase 15 keeps resolution in the Runner to minimize parser complexity. Future phases may leverage
meta.usingsfor compiler decisions. - レガシー実装の扱い: テキスト前置き/括弧補正などのシムは段階的に削除(prod プロファイルから先に無効化)。
- AST マージは dev/ci/prod の全プロファイルで共通基盤とし、曖昧性(宣言≻式)問題の再発を原理的に回避する。
- Unknown fields in the top‑level JSON (like
meta) are ignored by the current bridge. - 未解決時(非strict)は実行を継続し、
NYASH_RESOLVE_TRACE=1で候補を提示。strict時はエラーで候補を表示。 - Phase 15.5完了により、現代的な名前空間システムを実現予定
Deprecated: Include/Export(廃止)
このセクションは移行期の参考情報です。include は設計上の一貫性と学習コスト低減のため廃止しました。今後はすべて using に一本化してください(ファイル・パッケージ・DLL すべてを using で扱えます)。既存コードの移行は以下の対応例を推奨します。
local M = include "./path/module.hako"→using "./path/module.hako" as Mincludeの探索ルートは[using.paths]に統合(nyash.toml)
注: include は完全に非推奨です。コードは using に書き換えてください(互換シムは提供しません)。
Overview
- One file exports one static box.
include(path)evaluates the file and returns that Box instance.
Syntax
local Math = include "lib/math.hako"
local r = Math.add(1, 2)
Rules
- Single static box per file(0/複数はエラー)
- Expression form:
include(...)は Box インスタンスを返す式 - Caching: 同一パスは一度だけ評価(2回目以降はキャッシュ返却)
- Path resolution(MVP):
- Relative allowed; absolute discouraged
- nyash.toml
[include.roots]でstd=/stdlib等のルート定義を許可 - 省略拡張は
.hako、ディレクトリならindex.hako
Backends
- Interpreter: 実行時に評価し Box を返す
- VM/AOT: MIR Builder が対象ファイルを読み取り、同一 MIR モジュールに static box を降ろす(専用 MIR 命令は追加しない)
Limitations
- 循環 include の検出/診断は未実装(後続で active-load 追跡と経路表示を追加)
Rationale
- MIR 仕様に変更を入れず、実用的なモジュール分割を提供
- Everything‑is‑Box に整合(モジュール=Box、メソッド/フィールド=API)