Files
nyash-codex d5b065e5c4 docs(phase173): Phase 173 Task 1-3 complete - investigation, spec, and JsonParserBox bugfix
📚 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>
2025-12-04 17:47:19 +09:00

21 KiB
Raw Permalink Blame History

using — Imports and Namespaces (Phase 15+)

実装状況: Phase 15.5後に本格実装予定 | 基本ドット記法は実装済み

Status: Accepted (Runnerside 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 を許可(実験/bringup
    • 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.BoxNamenamespace.member
  • using基本構文: ファイルトップでの宣言
  • エイリアス: using long.path as Alias
  • プラグイン修飾: network.HttpClient

🚧 Phase 15.5後実装予定

  • built-in namespace: builtin.StringBox vs plugin.StringBox
  • 完全修飾名: nyash.builtin.printstd.console.log
  • スコープ演算子: ::global_funcType::static_method
  • 厳密解決: コンパイル時名前空間検証

PolicyRunner前処理

  • Accept using lines 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.usings when 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 依存は既定で SKIPSMOKES_INCLUDE_POLICY=skip|warn|error。順次 ERROR へ移行予定)。
    • 本番prodでは using/alias のみを正道に固定。using "path" は開発限定(NYASH_ALLOW_USING_FILE=1)で運用する。

Namespace Resolution (Runnerside)

  • Goal: keep IR/VM/JIT untouched. All resolution happens in Runner/Registry.
  • Default search order (3 stages, deterministic):
    1. Local/Core Boxes (nyrt)
    2. Aliases (nyash.toml [imports] / needs … as …)
    3. 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 (filescoped alias)

📦 静的 Box の usingPhase 173+

実装状況: Phase 173 で実装中2025-12-04

基本概念

静的 Boxstatic 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 で実装済み

  1. 静的メソッド直接呼び出し
    using tools.hako_shared.json_parser as JsonParserBox
    
    local value = JsonParserBox.parse("{\"x\":1}")
    
    • JsonParserBox は静的 Box のエイリアス
    • parse() は静的メソッド
    • インスタンス化不要

🚧 Phase 174+ で実装予定

  1. 名前空間的な 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 での扱い 型として登録 型として登録
典型的用途 ユーティリティ、パーサー データ構造、オブジェクト

実装の技術的詳細

名前解決の流れ

  1. using statement 解決.hako 側)

    • UsingResolverBox.resolve_path_alias() でファイルパス解決
    • エイリアスを「型」として環境に登録Phase 173 で実装)
  2. AST 生成.hako パーサー)

    • Alias.method() を静的Box呼び出しとして認識
    • フラグ is_static_box_call: true を付与Phase 173 で実装)
  3. MIR loweringRust 側)

    • 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 時点)

  1. パーサー制限

    • new Alias.BoxName() 構文は未サポート
    • 静的 Box 内の Box 定義をインスタンス化できない
  2. 名前空間階層

    • Alias.Namespace.BoxName のような多階層は未サポート
    • エイリアスは1階層のみ
  3. 型推論

    • 静的メソッドの戻り値型は動的MapBox/ArrayBox 等)
    • コンパイル時型チェックなし

Phase 174+ での拡張予定

  1. 名前空間的 Box アクセス

    • new Alias.BoxName() 構文のサポート
    • パーサー修正による対応
  2. 型システム統合

    • HIR 層の導入
    • 静的型推論の強化
  3. 明示的スコープ

    • :: 演算子のサポート
    • BoxName::static_method() 構文

Plugins

  • Unified namespace with Boxes. Prefer short names when unique.
  • Qualified form: network.HttpClient
  • Perplugin control (nyash.toml): prefix, require_prefix, expose_short_names
    • 現状は設定の読み取りのみ(導線)。挙動への反映は段階的に実施予定。

needs sugar (optional)

  • Treated as a synonym to using on 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", "."]
  • [using.<name>] — named packages (file or directory)
    • Keys: path = "lib/math_utils/", optional main = "math_utils.hako"
    • Optional kind = "dylib" with bid = "MathBox" for plugins (dev only)
  • [using.aliases] — alias mapping from short name to a package name
    • Example: aliases.json = "json_native"

Notes

  • Aliases are fully resolved: using json first rewrites to json_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 で解決ログcachehit/候補/未解決)を出力。
  • 前処理は最小 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 to load_library_direct(lib_name, path, boxes).
  • boxes is taken from [using.<name>].bid if present; otherwise the loader falls back to pluginembedded TypeBox metadata.
  • Safety: keep OFF by default. Prefer configuring libraries under nyash.toml for production.

Index and Cache (Runner)

  • BoxIndexグローバルプラグインBox一覧とaliasesを集約し、Runner起動時plugins init後に構築・更新。
    • aliases: HashMap<String,String>nyash.toml [aliases] と env NYASH_ALIASES
    • plugin_boxes: HashSet<String>(読み取り専用)
  • 解決キャッシュ:グローバルの小さなキャッシュで同一キーの再解決を回避(キー: tgt|base|strict|paths)。
  • トレース:NYASH_RESOLVE_TRACE=1 で解決手順やキャッシュヒット、未解決候補を出力。

Syntax

  • Namespace: using core.std or using 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 using lines 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 is PascalCase (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 の中に通常のメソッドを定義する のが Stage3 仕様の正しい形で、 static method foo() { ... } のようなトップレベルBox外の宣言構文は legacy/非推奨 であり、今後の実装では使用しない。

Runner Configuration

  • Enable using preprocessing: NYASH_ENABLE_USING=1
  • CLI from-the-top registration: --using "ns as Alias" or --using '"apps/foo.hako" as Foo' (repeatable)
  • Using profiles (phasein): 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 はエラー・追記ガイド)
  • 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.usings automatically when present; no additional flags required.

Note: Provider/Type 分離(型名は不変で提供者のみを切替)については ADR を参照。
docs/development/adr/adr-001-no-corebox-everything-is-plugin.md

🔬 Quick SmokesAST + 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.sh
  • tools/smokes/v2/profiles/quick/core/using_relative_file_ast.sh
  • tools/smokes/v2/profiles/quick/core/using_multi_prelude_dep_ast.sh

注意

  • ログに [using] stripped line: が出力されますが、これは AST マージ前の using 行の除去ログです(機能上問題ありません)。
  • 実行バイナリは target/release/nyash を前提とします。未ビルド時は cargo build --release を実行してください。

🔗 関連ドキュメント

設計・アーキテクチャ

実装ガイド

📝 実装ノート

Notes

  • Phase 15 keeps resolution in the Runner to minimize parser complexity. Future phases may leverage meta.usings for compiler decisions.
  • レガシー実装の扱い: テキスト前置き/括弧補正などのシムは段階的に削除prod プロファイルから先に無効化)。
  • AST マージは dev/ci/prod の全プロファイルで共通基盤とし、曖昧性(宣言≻式)問題の再発を原理的に回避する。
  • Unknown fields in the toplevel 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 M
  • include の探索ルートは [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 file0/複数はエラー)
  • Expression form: include(...) は Box インスタンスを返す式
  • Caching: 同一パスは一度だけ評価2回目以降はキャッシュ返却
  • Path resolutionMVP:
    • 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 仕様に変更を入れず、実用的なモジュール分割を提供
  • EverythingisBox に整合(モジュール=Box、メソッド/フィールド=API