json_native: Import JSON native implementation from feature branch

- Added apps/lib/json_native/ directory with complete JSON parser implementation
- Updated CLAUDE.md with JSON native import status and collect_prints investigation
- Added debug traces to mini_vm_core.nyash for collect_prints abnormal termination
- Note: JSON native uses match expressions incompatible with current parser
- Investigation ongoing with Codex for collect_prints method issues

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Selfhosting Dev
2025-09-23 04:51:17 +09:00
parent 39f27a348a
commit 7ab1e59450
20 changed files with 4390 additions and 8 deletions

View File

@ -277,16 +277,19 @@ NYASH_LLVM_USE_HARNESS=1 ./target/release/nyash program.nyash
- 🎯 **AI協働デバッグ**: Claude+ChatGPT修正+系統的トレースの完璧な連携実現
- 📋 詳細: JITアーカイブは `archive/jit-cranelift/` に完全移動、復活手順も完備
## 📝 Update (2025-09-22) 🎯 Phase 15 using system完全解決
## 📝 Update (2025-09-22) 🎯 Phase 15 JSON Native実装導入調査継続中
-**using systemパーサー問題完全解決** `NYASH_RESOLVE_FIX_BRACES=1`でブレースバランス自動修正
- 🆕 **JSON Native実装を導入** 別Claude Code君の`feature/phase15-nyash-json-native`から`apps/lib/json_native/`取り込み完了
- 🔧 **ChatGPTの統合実装承認** JSON読み込み処理統合は正しい方向性、技術的に高度
- 🔍 **根本原因解明**: using systemのファイル統合時にブレースバランス問題、自動修正で対応
- 📊 **現在の状況**:
- using systemパーサーエラー: ✅ 完全解決
- collect_prints()処理: ✅ echo/itoa正常動作
- 🔍 **調査中**: collect_prints()戻り値の異常終了問題メソッド実行は正常、returnトレースのみ欠落
- 🎯 **技術成果**: Task先生の調査により、using system統合の複雑な技術課題を解決
- 🚀 **Phase 15セルフホスティング**: 主要なusing system障害を克服、安定性大幅向上
- JSON Native: 📦 取り込み済みmatch式互換性の課題あり
- 🔍 **調査中**: collect_prints()戻り値の異常終了問題Codex/Task並行調査中
- 🎯 **技術成果**:
- Task先生の調査により、using system統合の複雑な技術課題を解決
- JSON Native実装yyjson置き換えの基盤完成度90%
- 🚀 **Phase 15セルフホスティング**: 主要障害を克服、JSON Native統合準備中
## 📝 Update (2025-09-18) 🌟 Property System革命達成
-**Property System革命完了** ChatGPT5×Claude×Codexの協働により、stored/computed/once/birth_once統一構文完成

View File

@ -0,0 +1,213 @@
# Nyash JSON Native Architecture
> 美しさ重視のモジュラー設計 - yyjsonの最適化と対極のアプローチ
## 🎨 設計哲学
### yyjson vs Nyash JSON
| 項目 | yyjson | Nyash JSON |
|------|--------|------------|
| **構造** | 単一巨大ファイル | モジュラー分離 |
| **最適化** | 最高速追求 | 理解しやすさ優先 |
| **保守性** | 専門家向け | チーム開発向け |
| **拡張性** | 困難 | 容易 |
### 80/20ルール適用
- **80%**: 美しく理解しやすい構造で動くものを作る
- **20%**: 後で必要に応じて最適化(ホットパス統合等)
## 📂 モジュラー構造
```
apps/lib/json_native/
├── README.md # プロジェクト概要
├── ARCHITECTURE.md # この設計ドキュメント
├── core/ # 🌟 核心データ構造
│ ├── node.nyash # JsonNode - JSON値表現
│ ├── value.nyash # JsonValue - 型安全ラッパー
│ └── error.nyash # JsonError - エラーハンドリング
├── lexer/ # 🔍 字句解析層
│ ├── tokenizer.nyash # トークナイザー本体
│ ├── token.nyash # トークン定義
│ └── scanner.nyash # 文字スキャナー
├── parser/ # 🏗️ 構文解析層
│ ├── parser.nyash # メインパーサー
│ ├── recursive.nyash # 再帰下降パーサー
│ └── validator.nyash # JSON妥当性検証
├── utils/ # 🛠️ ユーティリティ
│ ├── string.nyash # 文字列処理ヘルパー
│ ├── escape.nyash # エスケープ処理
│ └── pretty.nyash # 整形出力
├── tests/ # 🧪 テストスイート
│ ├── unit/ # 単体テスト
│ ├── integration/ # 統合テスト
│ └── performance/ # 性能テスト
└── examples/ # 📖 使用例
├── basic.nyash # 基本的な使用例
├── advanced.nyash # 高度な使用例
└── benchmark.nyash # ベンチマーク例
```
## 🎯 各モジュールの責務
### Core層 - データ構造の基盤
```nyash
// core/node.nyash - JSON値の抽象表現
box JsonNode {
kind: StringBox // "null"|"bool"|"int"|"string"|"array"|"object"
value: Box // 実際の値
meta: Box // メタデータ(位置情報等)
}
// core/value.nyash - 型安全なアクセス
box JsonValue {
node: JsonNode // 内部ノード
// as_string(), as_int(), as_bool() 等の型安全メソッド
}
// core/error.nyash - エラー情報
box JsonError {
code: StringBox // エラーコード
message: StringBox // エラーメッセージ
position: IntegerBox // エラー位置
}
```
### Lexer層 - 文字列をトークンに分解
```nyash
// lexer/token.nyash - トークン定義
box JsonToken {
type: StringBox // "STRING"|"NUMBER"|"LBRACE"|"RBRACE"等
value: StringBox // トークンの値
start: IntegerBox // 開始位置
end: IntegerBox // 終了位置
}
// lexer/tokenizer.nyash - メイントークナイザー
box JsonTokenizer {
scanner: JsonScanner // 文字スキャナー
tokens: ArrayBox // 生成されたトークン配列
}
```
### Parser層 - トークンをASTに変換
```nyash
// parser/parser.nyash - メインパーサー
box JsonParser {
tokenizer: JsonTokenizer // 字句解析器
current: IntegerBox // 現在のトークン位置
// parse() -> JsonNode
}
// parser/recursive.nyash - 再帰下降実装
static box RecursiveParser {
parse_value(tokens, pos) // 値をパース
parse_object(tokens, pos) // オブジェクトをパース
parse_array(tokens, pos) // 配列をパース
}
```
### Utils層 - 共通ユーティリティ
```nyash
// utils/string.nyash - 文字列処理
static box StringUtils {
trim(s) // 空白トリム
is_whitespace(ch) // 空白文字判定
is_digit(ch) // 数字判定
}
// utils/escape.nyash - エスケープ処理
static box EscapeUtils {
escape_string(s) // JSON文字列エスケープ
unescape_string(s) // JSONエスケープ解除
validate_string(s) // 文字列妥当性検証
}
```
## 🔄 モジュール間の依存関係
```
Core ←── Lexer ←── Parser ←── API
↑ ↑ ↑
Utils ────┴─────────┴─ (共通ユーティリティ)
```
### 依存性の最小化
- **Core**: 他モジュールに依存しない独立基盤
- **Lexer**: CoreとUtilsのみに依存
- **Parser**: Core, Lexer, Utilsに依存
- **Utils**: 完全独立(どこからでも使用可能)
## 🚀 段階的実装戦略
### Phase 1: Core基盤 (完了)
- [x] JsonNode基本実装
- [x] 基本的なJSON生成機能
### Phase 2: Utils基盤
- [ ] StringUtils実装
- [ ] EscapeUtils実装
- [ ] PrettyPrint実装
### Phase 3: Lexer実装
- [ ] JsonToken定義
- [ ] JsonScanner実装
- [ ] JsonTokenizer実装
### Phase 4: Parser実装
- [ ] RecursiveParser実装
- [ ] JsonParser統合
- [ ] エラーハンドリング
### Phase 5: 統合&テスト
- [ ] 全モジュール統合
- [ ] 包括的テストスイート
- [ ] 性能測定&調整
## 💡 美しさの利点
### 1. **理解しやすさ**
- モジュール境界が明確
- 各ファイルが単一責任
- 新規開発者でもすぐ理解
### 2. **保守性**
- バグ修正が局所化
- 機能追加が容易
- リファクタリングが安全
### 3. **テスト性**
- 各モジュール独立テスト可能
- モックによる分離テスト
- デバッグが簡単
### 4. **拡張性**
- 新機能を独立モジュールで追加
- 既存コードを破壊せずに拡張
- プラグイン機構の基盤
## 🎯 美しいコードの指針
### ファイルサイズ制限
- 各ファイル: **200行以下**を目標
- 複雑な機能は複数ファイルに分割
- 読みやすさを最優先
### 命名規則
- **Box名**: PascalCase (JsonNode, JsonParser)
- **メソッド名**: snake_case (parse_value, as_string)
- **ファイル名**: snake_case (tokenizer.nyash, recursive.nyash)
### コメント戦略
- **なぜ**: 設計の意図を説明
- **何**: 複雑なアルゴリズムを説明
- **注意**: エッジケースや制限事項
この美しいアーキテクチャで、yyjsonとは対極のアプローチを実践し、
保守しやすく理解しやすいJSON実装を作り上げます

View File

@ -0,0 +1,172 @@
# Nyash JSON Native
> yyjsonC依存→ 完全Nyash実装で外部依存完全排除
## 🎯 プロジェクト目標
- **C依存ゼロ**: yyjsonからの完全脱却
- **Everything is Box**: 全てをNyash Boxで実装
- **80/20ルール**: 動作優先、最適化は後
- **段階切り替え**: `NYASH_JSON_PROVIDER=nyash|yyjson|serde`
## 📦 アーキテクチャ設計
### 🔍 yyjson分析結果
```c
// yyjson核心設計パターン
struct yyjson_val {
uint64_t tag; // 型+サブタイプ+長さ
yyjson_val_uni uni; // ペイロードunion
};
```
### 🎨 Nyash版設計 (Everything is Box)
```nyash
// 🌟 JSON値を表現するBox
box JsonNode {
kind: StringBox // "null"|"bool"|"int"|"string"|"array"|"object"
value: Box // 実際の値(種類に応じて)
children: ArrayBox // 配列・オブジェクト用(オプション)
keys: ArrayBox // オブジェクトのキー配列(オプション)
}
// 🔧 JSON字句解析器
box JsonLexer {
text: StringBox // 入力文字列
pos: IntegerBox // 現在位置
tokens: ArrayBox // トークン配列
}
// 🏗️ JSON構文解析器
box JsonParser {
lexer: JsonLexer // 字句解析器
current: IntegerBox // 現在のトークン位置
}
```
## 📂 ファイル構造
```
apps/lib/json_native/
├── README.md # この設計ドキュメント
├── lexer.nyash # JSON字句解析器状態機械ベース
├── parser.nyash # JSON構文解析器再帰下降
├── node.nyash # JsonNode実装Everything is Box
├── tests/ # テストケース
│ ├── lexer_test.nyash
│ ├── parser_test.nyash
│ └── integration_test.nyash
└── examples/ # 使用例
├── simple_parse.nyash
└── complex_object.nyash
```
## 🎯 実装戦略
### Phase 1: JsonNode基盤1週目
- [x] JsonNode Box実装
- [x] 基本的な値型サポートnull, bool, int, string
- [x] 配列・オブジェクト構造サポート
### Phase 2: JsonLexer実装2週目
- [ ] トークナイザー実装(状態機械ベース)
- [ ] エラーハンドリング
- [ ] 位置情報追跡
### Phase 3: JsonParser実装3週目
- [ ] 再帰下降パーサー実装
- [ ] JsonNode構築
- [ ] ネストされた構造サポート
### Phase 4: 統合最適化4週目
- [ ] C ABI Bridge最小実装
- [ ] 既存JSONBoxとの互換性
- [ ] 性能測定・最適化
## 🔄 既存システムとの統合
### 段階的移行戦略
```bash
# 環境変数で切り替え
export NYASH_JSON_PROVIDER=nyash # 新実装
export NYASH_JSON_PROVIDER=yyjson # 既存C実装
export NYASH_JSON_PROVIDER=serde # Rust実装
```
### 既存APIとの互換性
```nyash
// 既存JSONBox APIを維持
local json = new JSONBox()
json.parse("{\"key\": \"value\"}") // 内部でNyash実装を使用
```
## 🎨 設計思想
### Everything is Box原則
- **JsonNode**: 完全なBox実装
- **境界明確化**: 各コンポーネントをBox化
- **差し替え可能**: プロバイダー切り替え対応
### 80/20ルール適用
- **80%**: まず動く実装(文字列操作ベース)
- **20%**: 後で最適化バイナリ処理、SIMD等
### フォールバック戦略
- **エラー時**: 既存実装に安全復帰
- **性能不足時**: yyjsonに切り替え可能
- **互換性**: 既存APIを100%維持
## 🧪 テスト戦略
### 基本テストケース
```json
{"null": null}
{"bool": true}
{"int": 42}
{"string": "hello"}
{"array": [1,2,3]}
{"object": {"nested": "value"}}
```
### エラーケース
```
{invalid} // 不正なJSON
{"unclosed": "str // 閉じられていない文字列
[1,2, // 不完全な配列
```
### 性能テストケース
```
大きなJSONファイル10MB+
深くネストされた構造100レベル+
多数の小さなオブジェクト10万個+
```
## 🚀 実行例
```nyash
// 基本的な使用例
local JsonNative = include "apps/lib/json_native/node.nyash"
// JSON文字列をパース
local text = "{\"name\": \"Nyash\", \"version\": 1}"
local node = JsonNative.parse(text)
// 値にアクセス
print(node.get("name").str()) // "Nyash"
print(node.get("version").int()) // 1
// JSON文字列に戻す
print(node.stringify()) // {"name":"Nyash","version":1}
```
## 📊 進捗追跡
- [ ] Week 1: JsonNode基盤
- [ ] Week 2: JsonLexer実装
- [ ] Week 3: JsonParser実装
- [ ] Week 4: 統合&最適化
**開始日**: 2025-09-22
**目標完了**: 2025-10-20
**実装者**: Claude × User協働

View File

@ -0,0 +1,276 @@
// 簡単なNyashスクリプトJSON解析の「ずれ」問題分析
// yyjsonが必要になった理由と最小限の解決要件
local JsonNode = include "apps/lib/json_native/core/node.nyash"
static box ParsingErrorAnalysis {
// ===== 典型的な「ずれ」パターンテスト =====
test_parsing_errors() {
print("🔍 JSON解析「ずれ」問題の分析開始")
// 1. エスケープ処理の問題
print("\n1⃣ エスケープ処理の「ずれ」")
this.test_escape_issues()
// 2. ネスト構造の問題
print("\n2⃣ ネスト構造の「ずれ」")
this.test_nesting_issues()
// 3. 数値解析の問題
print("\n3⃣ 数値解析の「ずれ」")
this.test_number_issues()
// 4. 境界判定の問題
print("\n4⃣ 境界判定の「ずれ」")
this.test_boundary_issues()
// 5. 空白処理の問題
print("\n5⃣ 空白処理の「ずれ」")
this.test_whitespace_issues()
}
// エスケープ処理問題
test_escape_issues() {
local problematic_cases = new ArrayBox()
// ❌ 簡単なsubstring解析だと失敗するケース
problematic_cases.push("{\"message\": \"say \\\"hello\\\"\"}") // 内部クォート
problematic_cases.push("{\"path\": \"C:\\\\Users\\\\name\"}") // バックスラッシュ
problematic_cases.push("{\"newline\": \"line1\\nline2\"}") // 改行エスケープ
problematic_cases.push("{\"unicode\": \"\\u0041\\u0042\"}") // Unicode
local i = 0
loop(i < problematic_cases.length()) {
local json_text = problematic_cases.get(i)
print("Test case: " + json_text)
// 簡単な解析(ずれやすい)
local simple_result = this.simple_parse(json_text)
print("Simple parse: " + simple_result)
// Nyash Native解析
local native_result = JsonNode.parse(json_text)
print("Native parse: " + native_result.stringify())
print("---")
i = i + 1
}
}
// ネスト構造問題
test_nesting_issues() {
local complex_cases = new ArrayBox()
// ❌ ネスト深度で混乱するケース
complex_cases.push("{\"a\": {\"b\": {\"c\": \"deep\"}}}") // 深いネスト
complex_cases.push("{\"arr\": [{\"x\": 1}, {\"y\": 2}]}") // 配列内オブジェクト
complex_cases.push("[{\"type\": \"A\"}, {\"type\": \"B\"}]") // オブジェクト配列
complex_cases.push("{\"mixed\": [1, \"str\", {\"nested\": true}]}") // 混合配列
local i = 0
loop(i < complex_cases.length()) {
local json_text = complex_cases.get(i)
print("Complex case: " + json_text)
// 簡単な解析の限界
local bracket_count = this.count_brackets(json_text)
print("Bracket analysis: " + bracket_count.open + " open, " + bracket_count.close + " close")
// TODO: 本格的なパーサーで正確に解析
print("✅ Requires proper parser for accuracy")
print("---")
i = i + 1
}
}
// 数値解析問題
test_number_issues() {
local number_cases = new ArrayBox()
// ❌ 数値の種類で混乱するケース
number_cases.push("{\"int\": 42}") // 整数
number_cases.push("{\"negative\": -123}") // 負数
number_cases.push("{\"float\": 3.14159}") // 小数
number_cases.push("{\"scientific\": 1.23e-4}") // 指数表記
number_cases.push("{\"zero\": 0}") // ゼロ
number_cases.push("{\"big\": 9223372036854775807}") // 大きな数
local i = 0
loop(i < number_cases.length()) {
local json_text = number_cases.get(i)
print("Number case: " + json_text)
// 現在のNyash Nativeでテスト
local result = JsonNode.parse(json_text)
print("Parse result: " + result.stringify())
print("---")
i = i + 1
}
}
// 境界判定問題
test_boundary_issues() {
local boundary_cases = new ArrayBox()
// ❌ 文字列境界で混乱するケース
boundary_cases.push("{\"key with spaces\": \"value\"}") // スペース付きキー
boundary_cases.push("{\"comma,inside\": \"value,with,commas\"}") // 内部カンマ
boundary_cases.push("{\"colon:inside\": \"value:with:colons\"}") // 内部コロン
boundary_cases.push("{\"}bracket{\": \"value}with}brackets\"}") // 内部ブラケット
local i = 0
loop(i < boundary_cases.length()) {
local json_text = boundary_cases.get(i)
print("Boundary case: " + json_text)
print("⚠️ Simple substring parsing would fail here")
print("✅ Needs proper tokenization")
print("---")
i = i + 1
}
}
// 空白処理問題
test_whitespace_issues() {
local whitespace_cases = new ArrayBox()
// ❌ 空白の扱いで混乱するケース
whitespace_cases.push("{ \"key\" : \"value\" }") // 通常の空白
whitespace_cases.push("{\n \"multiline\": \"value\"\n}") // 改行・インデント
whitespace_cases.push("{\"no\":\"spaces\"}") // 空白なし
whitespace_cases.push("{\t\"tab\":\t\"separated\"\t}") // タブ区切り
whitespace_cases.push("{ \"mixed\":\n\t \"whitespace\" }") // 混合空白
local i = 0
loop(i < whitespace_cases.length()) {
local json_text = whitespace_cases.get(i)
print("Whitespace case: '" + json_text + "'")
// 現在のNyash Nativeでテスト
local result = JsonNode.parse(json_text)
print("Parse result: " + result.stringify())
print("---")
i = i + 1
}
}
// ===== 簡単な解析の実装例(問題のあるパターン) =====
// ❌ 問題のある簡単な解析(参考用)
simple_parse(json_text) {
// これが「ずれ」の原因パターン
if json_text.substring(0, 1) == "{" and json_text.substring(json_text.length() - 1, json_text.length()) == "}" {
return "object(simple)"
} else {
if json_text.substring(0, 1) == "[" and json_text.substring(json_text.length() - 1, json_text.length()) == "]" {
return "array(simple)"
} else {
return "unknown(simple)"
}
}
}
// 括弧の数を数える(不正確な方法の例)
count_brackets(s) {
local open = 0
local close = 0
local i = 0
loop(i < s.length()) {
local ch = s.substring(i, i + 1)
if ch == "{" or ch == "[" {
open = open + 1
} else {
if ch == "}" or ch == "]" {
close = close + 1
}
}
i = i + 1
}
return { open: open, close: close }
}
}
// ===== yyjson相当の精度要件定義 =====
static box AccuracyRequirements {
// 最小限で確実な要件
get_minimum_requirements() {
local req = new MapBox()
// 必須機能yyjson相当の精度
req.set("essential", new ArrayBox())
req.get("essential").push("正確なエスケープ処理(\\n, \\\", \\\\, \\u0000")
req.get("essential").push("ネスト構造の正確な解析(無制限深度)")
req.get("essential").push("文字列境界の正確な判定(クォート内外)")
req.get("essential").push("基本数値の正確な解析int, float")
req.get("essential").push("空白の正確な処理(有意/無意の区別)")
req.get("essential").push("構文エラーの確実な検出")
// 推奨機能(必要に応じて)
req.set("recommended", new ArrayBox())
req.get("recommended").push("指数表記の数値サポート1.23e-4")
req.get("recommended").push("Unicode エスケープ(\\u0041")
req.get("recommended").push("詳細なエラー位置情報")
req.get("recommended").push("ストリーミング解析大きなJSON")
// 不要機能yyjsonにあるが省略可能
req.set("optional", new ArrayBox())
req.get("optional").push("JSON PointerRFC 6901")
req.get("optional").push("JSON PatchRFC 6902")
req.get("optional").push("超高速浮動小数点変換")
req.get("optional").push("インクリメンタル読み込み")
req.get("optional").push("カスタムアロケーター")
return req
}
// 最小実装スコープ決定
get_minimal_scope() {
print("🎯 Nyash JSON Native最小実装スコープ")
print("目標: yyjsonの確実性、でも機能は最小限")
local scope = new MapBox()
// Phase 1: 基本精度現在80%完成)
scope.set("phase1", new ArrayBox())
scope.get("phase1").push("✅ 基本JSON型null, bool, int, string, array, object")
scope.get("phase1").push("✅ JSON文字列生成エスケープ処理")
scope.get("phase1").push("✅ 美しいモジュラー設計")
scope.get("phase1").push("🔄 基本的なエスケープ解析(現在部分実装)")
// Phase 2: yyjson相当の精度残り20%
scope.set("phase2", new ArrayBox())
scope.get("phase2").push("🎯 正確なLexerトークン分割")
scope.get("phase2").push("🎯 正確なParser構文解析")
scope.get("phase2").push("🎯 エラー検出・報告")
scope.get("phase2").push("🎯 複雑なネスト構造対応")
return scope
}
}
// テスト実行用
static box Main {
main() {
ParsingErrorAnalysis.test_parsing_errors()
print("\n📋 精度要件分析")
local requirements = AccuracyRequirements.get_minimum_requirements()
local scope = AccuracyRequirements.get_minimal_scope()
print("\n💡 結論:「どこまで作ればいいか」")
print("✅ Phase 1完了基本機能・美しい設計")
print("🎯 Phase 2必要yyjson相当の精度達成")
print("❌ Phase 3不要JSON Pointer等の高度機能")
print("\n🚀 次のステップ正確なLexer実装で精度向上")
return 0
}
}

View File

@ -0,0 +1,301 @@
// JsonNode — Everything is Box JSON実装の核心
// 80/20ルール適用: まず動くもの → 後で最適化
// 美しいモジュラー設計: Utilsを活用してDRY原則を実践
local StringUtils = include "apps/lib/json_native/utils/string.nyash"
local EscapeUtils = include "apps/lib/json_native/utils/escape.nyash"
// 🌟 JSON値を表現するBoxEverything is Box原則
static box JsonNode {
// ===== ファクトリーメソッド =====
// null値を作成
create_null() {
local node = new JsonNode()
node.kind = "null"
node.value = null
return node
}
// bool値を作成
create_bool(b) {
local node = new JsonNode()
node.kind = "bool"
node.value = b
return node
}
// int値を作成
create_int(i) {
local node = new JsonNode()
node.kind = "int"
node.value = i
return node
}
// string値を作成
create_string(s) {
local node = new JsonNode()
node.kind = "string"
node.value = s
return node
}
// array値を作成
create_array() {
local node = new JsonNode()
node.kind = "array"
node.value = new ArrayBox()
return node
}
// object値を作成
create_object() {
local node = new JsonNode()
node.kind = "object"
node.value = new MapBox()
return node
}
// ===== メインAPI =====
// JSON文字列を解析簡易版
parse(json_text) {
// TODO: 本格的なレクサー・パーサーは後で実装
// まずは超シンプルな実装で動作確認
// 空白をトリム(美しいモジュラー設計)
local text = StringUtils.trim(json_text)
// null
if text == "null" {
return this.create_null()
}
// boolean
if text == "true" {
return this.create_bool(true)
}
if text == "false" {
return this.create_bool(false)
}
// 数値Utils活用で美しく
if StringUtils.is_integer(text) {
local num = StringUtils.parse_integer(text)
return this.create_int(num)
}
// 文字列(エスケープ処理対応)
if StringUtils.starts_with(text, "\"") and StringUtils.ends_with(text, "\"") {
local content = EscapeUtils.unquote_string(text)
return this.create_string(content)
}
// 配列(超シンプル版)
if StringUtils.starts_with(text, "[") and StringUtils.ends_with(text, "]") {
local array_node = this.create_array()
// TODO: 要素のパース処理は後で実装
return array_node
}
// オブジェクト(超シンプル版)
if StringUtils.starts_with(text, "{") and StringUtils.ends_with(text, "}") {
local object_node = this.create_object()
// TODO: キー・バリューのパース処理は後で実装
return object_node
}
// パースエラー
return this.create_null()
}
// ===== アクセッサーメソッド =====
// 値の種類を取得
get_kind() {
return me.kind
}
// bool値として取得
as_bool() {
if me.kind == "bool" {
return me.value
}
return false
}
// int値として取得
as_int() {
if me.kind == "int" {
return me.value
}
return 0
}
// string値として取得
as_string() {
if me.kind == "string" {
return me.value
}
return ""
}
// 配列のサイズを取得
array_size() {
if me.kind == "array" {
return me.value.length()
}
return 0
}
// 配列の要素を取得
array_get(index) {
if me.kind == "array" {
return me.value.get(index)
}
return null
}
// 配列に要素を追加
array_push(node) {
if me.kind == "array" {
me.value.push(node)
}
}
// オブジェクトのキーを取得
object_get(key) {
if me.kind == "object" {
return me.value.get(key)
}
return null
}
// オブジェクトに値を設定
object_set(key, node) {
if me.kind == "object" {
me.value.set(key, node)
}
}
// オブジェクトのキー一覧を取得
object_keys() {
if me.kind == "object" {
return me.value.keys()
}
return new ArrayBox()
}
// ===== JSON文字列化 =====
// JSON文字列に変換
stringify() {
if me.kind == "null" {
return "null"
} else {
if me.kind == "bool" {
if me.value {
return "true"
} else {
return "false"
}
} else {
if me.kind == "int" {
return "" + me.value
} else {
if me.kind == "string" {
return EscapeUtils.quote_string(me.value)
} else {
if me.kind == "array" {
local parts = new ArrayBox()
local i = 0
loop(i < me.value.length()) {
local elem = me.value.get(i)
parts.push(elem.stringify())
i = i + 1
}
return "[" + StringUtils.join(parts, ",") + "]"
} else {
if me.kind == "object" {
local parts = new ArrayBox()
local keys = me.value.keys()
local i = 0
loop(i < keys.length()) {
local key = keys.get(i)
local val = me.value.get(key)
local pair = EscapeUtils.quote_string(key) + ":" + val.stringify()
parts.push(pair)
i = i + 1
}
return "{" + StringUtils.join(parts, ",") + "}"
} else {
return "null"
}
}
}
}
}
}
}
// ===== ヘルパーメソッド =====
// 美しいモジュラー設計により、重複コードを削除
// 全ての文字列処理はStringUtils、エスケープ処理はEscapeUtilsに委譲
}
// 🎯 動作確認用のインスタンス生成
box JsonNodeInstance {
kind: StringBox
value: Box
birth() {
me.kind = "null"
me.value = null
}
// インスタンスメソッドとして静的メソッドを呼び出し
get_kind() { return me.kind }
as_bool() {
if me.kind == "bool" {
return me.value
}
return false
}
as_int() {
if me.kind == "int" {
return me.value
}
return 0
}
as_string() {
if me.kind == "string" {
return me.value
}
return ""
}
stringify() {
if me.kind == "null" {
return "null"
} else {
if me.kind == "bool" {
if me.value {
return "true"
} else {
return "false"
}
} else {
if me.kind == "int" {
return "" + me.value
} else {
if me.kind == "string" {
return "\"" + me.value + "\""
} else {
return "null"
}
}
}
}
}
}

View File

@ -0,0 +1,347 @@
// JsonScanner — 文字レベルのスキャン(美しいモジュラー設計)
// 責務: 文字単位での読み取り・位置管理・先読み機能
// StringUtils dependency removed - using internal methods
// 🔍 JSON文字スキャナーEverything is Box
static box JsonScannerModule {
create_scanner(input_text) {
return new JsonScanner(input_text)
}
}
box JsonScanner {
text: StringBox // 入力文字列
position: IntegerBox // 現在位置
length: IntegerBox // 文字列長
line: IntegerBox // 現在行番号
column: IntegerBox // 現在列番号
birth(input_text) {
me.text = input_text
me.position = 0
me.length = input_text.length()
me.line = 1
me.column = 1
}
// ===== 基本読み取りメソッド =====
// 現在文字を取得EOF時は空文字列
current() {
if me.position >= me.length {
return "" // EOF
}
return me.text.substring(me.position, me.position + 1)
}
// 次の文字を先読み(位置は移動しない)
peek() {
if me.position + 1 >= me.length {
return "" // EOF
}
return me.text.substring(me.position + 1, me.position + 2)
}
// n文字先を先読み
peek_at(offset) {
local pos = me.position + offset
if pos >= me.length {
return "" // EOF
}
return me.text.substring(pos, pos + 1)
}
// 現在位置から指定長の文字列を取得
substring(len) {
local end_pos = me.position + len
if end_pos > me.length {
end_pos = me.length
}
return me.text.substring(me.position, end_pos)
}
// ===== 位置制御メソッド =====
// 1文字進む
advance() {
if me.position < me.length {
local ch = me.current()
me.position = me.position + 1
// 行番号・列番号の更新
if ch == "\n" {
me.line = me.line + 1
me.column = 1
} else {
me.column = me.column + 1
}
return ch
}
return "" // EOF
}
// n文字進む
advance_by(n) {
local i = 0
loop(i < n) {
if me.is_eof() {
break
}
me.advance()
i = i + 1
}
}
// 指定位置にジャンプ(行番号は再計算)
seek(pos) {
if pos < 0 {
pos = 0
}
if pos > me.length {
pos = me.length
}
me.position = pos
me.recalculate_line_column()
}
// ===== 状態チェックメソッド =====
// EOFかどうか
is_eof() {
return me.position >= me.length
}
// 残り文字数
remaining() {
return me.length - me.position
}
// 現在位置を取得
get_position() {
return me.position
}
get_line() {
return me.line
}
get_column() {
return me.column
}
// 空白文字判定(内蔵)
is_whitespace_char(ch) {
return ch == " " or ch == "\t" or ch == "\n" or ch == "\r"
}
// 数字文字判定(内蔵)
is_digit_char(ch) {
return ch == "0" or ch == "1" or ch == "2" or ch == "3" or ch == "4" or ch == "5" or ch == "6" or ch == "7" or ch == "8" or ch == "9"
}
// 16進数字判定内蔵
is_hex_digit_char(ch) {
return me.is_digit_char(ch) or ch == "a" or ch == "b" or ch == "c" or ch == "d" or ch == "e" or ch == "f" or ch == "A" or ch == "B" or ch == "C" or ch == "D" or ch == "E" or ch == "F"
}
// ===== 高レベル読み取りメソッド =====
// 空白をスキップ
skip_whitespace() {
loop(not me.is_eof()) {
local ch = me.current()
if not me.is_whitespace_char(ch) {
break
}
me.advance()
}
}
// 指定文字列にマッチするかチェック(マッチしたら消費)
match_string(expected) {
if me.remaining() < expected.length() {
return false
}
local actual = me.text.substring(me.position, me.position + expected.length())
if actual == expected {
me.advance_by(expected.length())
return true
}
return false
}
// 指定文字列の先読みチェック(位置は移動しない)
starts_with(prefix) {
if me.remaining() < prefix.length() {
return false
}
local actual = me.text.substring(me.position, me.position + prefix.length())
return actual == prefix
}
// 条件を満たす間読み取り続ける
read_while(condition_fn) {
local start_pos = me.position
loop(not me.is_eof()) {
local ch = me.current()
if not condition_fn(ch) {
break
}
me.advance()
}
return me.text.substring(start_pos, me.position)
}
// 数値文字列を読み取り
read_number() {
local start_pos = me.position
// マイナス符号
if me.current() == "-" {
me.advance()
}
// 整数部分
if me.current() == "0" {
me.advance()
} else {
if me.is_digit_char(me.current()) {
loop(not me.is_eof() and me.is_digit_char(me.current())) {
me.advance()
}
} else {
return null // 無効な数値
}
}
// 小数部分(オプション)
if me.current() == "." {
me.advance()
if not me.is_digit_char(me.current()) {
return null // 無効な小数
}
loop(not me.is_eof() and me.is_digit_char(me.current())) {
me.advance()
}
}
// 指数部分(オプション)
local ch = me.current()
if ch == "e" or ch == "E" {
me.advance()
ch = me.current()
if ch == "+" or ch == "-" {
me.advance()
}
if not me.is_digit_char(me.current()) {
return null // 無効な指数
}
loop(not me.is_eof() and me.is_digit_char(me.current())) {
me.advance()
}
}
return me.text.substring(start_pos, me.position)
}
// 文字列リテラルを読み取り(クォート含む)
read_string_literal() {
local start_pos = me.position
// 開始クォート
if me.current() != "\"" {
return null
}
me.advance()
// 文字列内容
loop(not me.is_eof()) {
local ch = me.current()
if ch == "\"" {
// 終了クォート
me.advance()
return me.text.substring(start_pos, me.position)
} else {
if ch == "\\" {
// エスケープシーケンス
me.advance()
if me.is_eof() {
return null // 不完全なエスケープ
}
local escaped = me.current()
if escaped == "u" {
// Unicode エスケープ \uXXXX
me.advance()
local i = 0
loop(i < 4) {
if me.is_eof() or not me.is_hex_digit_char(me.current()) {
return null // 無効なUnicodeエスケープ
}
me.advance()
i = i + 1
}
} else {
// 通常のエスケープ
me.advance()
}
} else {
if ch == "\n" or ch == "\r" {
return null // 文字列内の生の改行は無効
}
me.advance()
}
}
}
return null // 終了クォートが見つからない
}
// ===== プライベートメソッド =====
// 行番号・列番号を再計算seek後に使用
recalculate_line_column() {
me.line = 1
me.column = 1
local i = 0
loop(i < me.position) {
local ch = me.text.substring(i, i + 1)
if ch == "\n" {
me.line = me.line + 1
me.column = 1
} else {
me.column = me.column + 1
}
i = i + 1
}
}
// ===== デバッグメソッド =====
get_context(radius) {
local start = me.position - radius
if start < 0 { start = 0 }
local end = me.position + radius
if end > me.length { end = me.length }
local before = me.text.substring(start, me.position)
local current = me.current()
local after = me.text.substring(me.position + 1, end)
return before + "【" + current + "】" + after
}
to_debug_string() {
return "Scanner{pos:" + me.position + "/" + me.length + ", line:" + me.line + ", col:" + me.column + ", context:'" + me.get_context(5) + "'}"
}
}

View File

@ -0,0 +1,111 @@
// JsonScanner — 簡易版現在のNyash制限内で動作
// 責務: 文字単位での読み取り・位置管理
// 🔍 JSON文字スキャナーEverything is Box
static box JsonScannerModule {
create_scanner(input_text) {
return new JsonScanner(input_text)
}
}
box JsonScanner {
text: StringBox // 入力文字列
position: IntegerBox // 現在位置
length: IntegerBox // 文字列長
birth(input_text) {
me.text = input_text
me.position = 0
me.length = input_text.length()
}
// ===== 基本読み取りメソッド =====
// 現在文字を取得EOF時は空文字列
current() {
if me.position >= me.length {
return "" // EOF
}
return me.text.substring(me.position, me.position + 1)
}
// 次の文字を先読み(位置は移動しない)
peek() {
if me.position + 1 >= me.length {
return "" // EOF
}
return me.text.substring(me.position + 1, me.position + 2)
}
// 1文字進む
advance() {
if me.position < me.length {
local ch = me.current()
me.position = me.position + 1
return ch
}
return "" // EOF
}
// ===== 状態チェックメソッド =====
// EOFかどうか
is_eof() {
return me.position >= me.length
}
// 現在位置を取得
get_position() {
return me.position
}
// ===== 簡易空白スキップ =====
// 空白をスキップ(内蔵判定)
skip_whitespace() {
loop(not me.is_eof()) {
local ch = me.current()
if not me.is_whitespace_char(ch) {
break
}
me.advance()
}
}
// 空白文字判定(内蔵)
is_whitespace_char(ch) {
return ch == " " or ch == "\t" or ch == "\n" or ch == "\r"
}
// 指定文字列にマッチするかチェック
match_string(expected) {
if expected.length() > me.remaining() {
return false
}
local i = 0
loop(i < expected.length()) {
local pos = me.position + i
if pos >= me.length {
return false
}
local actual = me.text.substring(pos, pos + 1)
local expect = expected.substring(i, i + 1)
if actual != expect {
return false
}
i = i + 1
}
return true
}
// 残り文字数
remaining() {
return me.length - me.position
}
// デバッグ情報
to_string() {
return "Scanner(pos:" + me.position + "/" + me.length + ")"
}
}

View File

@ -0,0 +1,250 @@
// JsonToken — JSON字句解析の基本単位美しいモジュラー設計
// 責務: JSONトークンの定義と操作
// 🎯 JSONトークンの種類定義
static box TokenType {
// リテラル値
NULL() { return "NULL" }
TRUE() { return "TRUE" }
FALSE() { return "FALSE" }
NUMBER() { return "NUMBER" }
STRING() { return "STRING" }
// 構造文字
LBRACE() { return "LBRACE" } // {
RBRACE() { return "RBRACE" } // }
LBRACKET() { return "LBRACKET" } // [
RBRACKET() { return "RBRACKET" } // ]
COMMA() { return "COMMA" } // ,
COLON() { return "COLON" } // :
// 制御トークン
EOF() { return "EOF" } // 終端
ERROR() { return "ERROR" } // エラー
// 空白(通常は無視、デバッグ用)
WHITESPACE() { return "WHITESPACE" }
}
// 🌟 JSONトークンEverything is Box
box JsonToken {
type: StringBox // トークンタイプ
value: StringBox // トークンの値
start: IntegerBox // 開始位置
end: IntegerBox // 終了位置
line: IntegerBox // 行番号(エラー報告用)
column: IntegerBox // 列番号(エラー報告用)
birth(token_type, token_value, start_pos, end_pos) {
me.type = token_type
me.value = token_value
me.start = start_pos
me.end = end_pos
me.line = 1
me.column = start_pos + 1
}
// ===== アクセッサーメソッド =====
get_type() { return me.type }
get_value() { return me.value }
get_start() { return me.start }
get_end() { return me.end }
get_line() { return me.line }
get_column() { return me.column }
// ===== 判定メソッド =====
is_literal() {
return me.type == "NULL" or me.type == "TRUE" or me.type == "FALSE" or me.type == "NUMBER" or me.type == "STRING"
}
is_structural() {
return me.type == "LBRACE" or me.type == "RBRACE" or me.type == "LBRACKET" or me.type == "RBRACKET" or me.type == "COMMA" or me.type == "COLON"
}
is_value_start() {
return me.is_literal() or me.type == "LBRACE" or me.type == "LBRACKET"
}
is_error() {
return me.type == "ERROR"
}
is_eof() {
return me.type == "EOF"
}
// ===== デバッグ用メソッド =====
to_string() {
return me.type + "(" + me.value + ") at " + me.start + "-" + me.end
}
to_debug_string() {
return me.type + "{value: \"" + me.value + "\", pos: " + me.start + "-" + me.end + ", line: " + me.line + ", col: " + me.column + "}"
}
}
// 🏭 トークンファクトリー(便利メソッド集)
static box TokenFactory {
// リテラル値トークン
create_null(start, end) {
return new JsonToken("NULL", "null", start, end)
}
create_true(start, end) {
return new JsonToken("TRUE", "true", start, end)
}
create_false(start, end) {
return new JsonToken("FALSE", "false", start, end)
}
create_number(value, start, end) {
return new JsonToken("NUMBER", value, start, end)
}
create_string(value, start, end) {
return new JsonToken("STRING", value, start, end)
}
// 構造文字トークン
create_lbrace(start) {
return new JsonToken("LBRACE", "{", start, start + 1)
}
create_rbrace(start) {
return new JsonToken("RBRACE", "}", start, start + 1)
}
create_lbracket(start) {
return new JsonToken("LBRACKET", "[", start, start + 1)
}
create_rbracket(start) {
return new JsonToken("RBRACKET", "]", start, start + 1)
}
create_comma(start) {
return new JsonToken("COMMA", ",", start, start + 1)
}
create_colon(start) {
return new JsonToken("COLON", ":", start, start + 1)
}
// 制御トークン
create_eof(pos) {
return new JsonToken("EOF", "", pos, pos)
}
create_error(message, start, end) {
return new JsonToken("ERROR", message, start, end)
}
create_whitespace(value, start, end) {
return new JsonToken("WHITESPACE", value, start, end)
}
// ===== 文字からトークンタイプを判定 =====
char_to_token_type(ch) {
return match ch {
"{" => "LBRACE",
"}" => "RBRACE",
"[" => "LBRACKET",
"]" => "RBRACKET",
"," => "COMMA",
":" => "COLON",
_ => null
}
}
// 文字が構造文字かどうか判定
is_structural_char(ch) {
return ch == "{" or ch == "}" or ch == "[" or ch == "]" or ch == "," or ch == ":"
}
// 文字が空白かどうか判定
is_whitespace_char(ch) {
return ch == " " or ch == "\t" or ch == "\n" or ch == "\r"
}
// 文字が数値の開始文字かどうか判定
is_number_start_char(ch) {
return ch == "-" or (ch >= "0" and ch <= "9")
}
// キーワードからトークンタイプを判定
keyword_to_token_type(keyword) {
return match keyword {
"null" => "NULL",
"true" => "TRUE",
"false" => "FALSE",
_ => null
}
}
}
// 📊 トークン統計(デバッグ・分析用)
static box TokenStats {
analyze_tokens(tokens) {
local stats = new MapBox()
// トークンタイプ別カウント
local type_counts = new MapBox()
local i = 0
loop(i < tokens.length()) {
local token = tokens.get(i)
local type = token.get_type()
if type_counts.has(type) {
local count = type_counts.get(type) + 1
type_counts.set(type, count)
} else {
type_counts.set(type, 1)
}
i = i + 1
}
stats.set("type_counts", type_counts)
stats.set("total_tokens", tokens.length())
// エラートークンの存在チェック
local has_errors = type_counts.has("ERROR")
stats.set("has_errors", has_errors)
if has_errors {
stats.set("error_count", type_counts.get("ERROR"))
}
return stats
}
print_stats(stats) {
print("📊 Token Analysis Results:")
print("Total tokens: " + stats.get("total_tokens"))
print("Has errors: " + stats.get("has_errors"))
if stats.get("has_errors") {
print("Error count: " + stats.get("error_count"))
}
print("\nToken type breakdown:")
local type_counts = stats.get("type_counts")
local keys = type_counts.keys()
local i = 0
loop(i < keys.length()) {
local type = keys.get(i)
local count = type_counts.get(type)
print(" " + type + ": " + count)
i = i + 1
}
}
}

View File

@ -0,0 +1,142 @@
// JsonToken — 簡易版現在のNyash制限内で動作
// 責務: JSONトークンの基本構造
// 🌟 JSONトークンEverything is Box
box JsonToken {
type: StringBox // トークンタイプ
value: StringBox // トークンの値
start: IntegerBox // 開始位置
end: IntegerBox // 終了位置
birth(token_type, token_value, start_pos, end_pos) {
me.type = token_type
me.value = token_value
me.start = start_pos
me.end = end_pos
}
// ===== アクセッサーメソッド =====
get_type() { return me.type }
get_value() { return me.value }
get_start() { return me.start }
get_end() { return me.end }
// ===== 判定メソッド(簡易版) =====
is_literal() {
return me.type == "NULL" or me.type == "TRUE" or me.type == "FALSE" or me.type == "NUMBER" or me.type == "STRING"
}
is_structural() {
return me.type == "LBRACE" or me.type == "RBRACE" or me.type == "LBRACKET" or me.type == "RBRACKET" or me.type == "COMMA" or me.type == "COLON"
}
is_error() {
return me.type == "ERROR"
}
is_eof() {
return me.type == "EOF"
}
// ===== デバッグ用メソッド =====
to_string() {
return me.type + "(" + me.value + ") at " + me.start + "-" + me.end
}
}
// 🏭 トークンファクトリー(簡易版)
static box TokenFactory {
// リテラル値トークン
create_null(start, end) {
return new JsonToken("NULL", "null", start, end)
}
create_true(start, end) {
return new JsonToken("TRUE", "true", start, end)
}
create_false(start, end) {
return new JsonToken("FALSE", "false", start, end)
}
create_number(value, start, end) {
return new JsonToken("NUMBER", value, start, end)
}
create_string(value, start, end) {
return new JsonToken("STRING", value, start, end)
}
// 構造文字トークン
create_lbrace(start) {
return new JsonToken("LBRACE", "{", start, start + 1)
}
create_rbrace(start) {
return new JsonToken("RBRACE", "}", start, start + 1)
}
create_lbracket(start) {
return new JsonToken("LBRACKET", "[", start, start + 1)
}
create_rbracket(start) {
return new JsonToken("RBRACKET", "]", start, start + 1)
}
create_comma(start) {
return new JsonToken("COMMA", ",", start, start + 1)
}
create_colon(start) {
return new JsonToken("COLON", ":", start, start + 1)
}
// 制御トークン
create_eof(pos) {
return new JsonToken("EOF", "", pos, pos)
}
create_error(message, start, end) {
return new JsonToken("ERROR", message, start, end)
}
// ===== 文字からトークンタイプを判定 =====
char_to_token_type(ch) {
if ch == "{" { return "LBRACE" }
if ch == "}" { return "RBRACE" }
if ch == "[" { return "LBRACKET" }
if ch == "]" { return "RBRACKET" }
if ch == "," { return "COMMA" }
if ch == ":" { return "COLON" }
return null
}
// 文字が構造文字かどうか判定
is_structural_char(ch) {
return ch == "{" or ch == "}" or ch == "[" or ch == "]" or ch == "," or ch == ":"
}
// 文字が空白かどうか判定
is_whitespace_char(ch) {
return ch == " " or ch == "\t" or ch == "\n" or ch == "\r"
}
// 文字が数値の開始文字かどうか判定
is_number_start_char(ch) {
return ch == "-" or (ch >= "0" and ch <= "9")
}
// キーワードからトークンタイプを判定
keyword_to_token_type(keyword) {
if keyword == "null" { return "NULL" }
if keyword == "true" { return "TRUE" }
if keyword == "false" { return "FALSE" }
return null
}
}

View File

@ -0,0 +1,379 @@
// JsonTokenizer — 精度重視の字句解析器yyjson相当精度
// 責務: 文字列をトークン列に変換、エラー検出、位置情報管理
local JsonScanner = include "apps/lib/json_native/lexer/scanner.nyash"
local JsonToken = include "apps/lib/json_native/lexer/token.nyash"
// Removed other dependencies - using self-contained methods
// 🎯 高精度JSONトークナイザーEverything is Box
box JsonTokenizer {
scanner: JsonScanner // 文字スキャナー
tokens: ArrayBox // 生成されたトークン配列
errors: ArrayBox // エラー情報配列
birth(input_text) {
me.scanner = new JsonScanner(input_text)
me.tokens = new ArrayBox()
me.errors = new ArrayBox()
}
// ===== メイン解析メソッド =====
// 全文字列をトークン化
tokenize() {
// 初期化
me.tokens = new ArrayBox()
me.errors = new ArrayBox()
// メインループ
loop(not me.scanner.is_eof()) {
local token = me.next_token()
if token != null {
me.tokens.push(token)
// エラートークンがあれば記録
if token.is_error() {
me.errors.push(token)
}
// EOFに到達したら終了
if token.is_eof() {
break
}
} else {
// トークン生成失敗(内部エラー)
local error_token = new JsonToken("ERROR", "Internal tokenizer error", me.scanner.get_position(), me.scanner.get_position() + 1)
me.tokens.push(error_token)
me.errors.push(error_token)
break
}
}
// 最終的にEOFトークンを追加まだ追加されていない場合
if me.tokens.length() == 0 or not me.tokens.get(me.tokens.length() - 1).is_eof() {
me.tokens.push(new JsonToken("EOF", "", me.scanner.get_position(), me.scanner.get_position()))
}
return me.tokens
}
// 次のトークンを1つ取得
next_token() {
// 空白をスキップ
me.scanner.skip_whitespace()
// EOF チェック
if me.scanner.is_eof() {
return new JsonToken("EOF", "", me.scanner.get_position(), me.scanner.get_position())
}
local start_pos = me.scanner.get_position()
local ch = me.scanner.current()
// 構造文字(単一文字)
local structural_type = me.char_to_token_type(ch)
if structural_type != null {
me.scanner.advance()
return this.create_structural_token(structural_type, start_pos)
}
// 文字列リテラル
if ch == "\"" {
return me.tokenize_string()
}
// 数値リテラル
if me.is_number_start_char(ch) {
return me.tokenize_number()
}
// キーワードnull, true, false
if me.is_alpha_char(ch) {
return me.tokenize_keyword()
}
// 不明な文字(エラー)
me.scanner.advance()
return new JsonToken("ERROR", "Unexpected character: '" + ch + "'", start_pos, me.scanner.get_position())
}
// ===== 専用トークナイザーメソッド =====
// 文字列トークン化
tokenize_string() {
local start_pos = me.scanner.get_position()
local literal = me.scanner.read_string_literal()
if literal == null {
return new JsonToken("ERROR", "Unterminated string literal", start_pos, me.scanner.get_position())
}
// エスケープ解除して値を取得
local unescaped = me.unquote_string(literal)
// 文字列妥当性検証
if not me.validate_string(unescaped) {
return new JsonToken("ERROR", "Invalid string content", start_pos, me.scanner.get_position())
}
return new JsonToken("STRING", unescaped, start_pos, me.scanner.get_position())
}
// 数値トークン化
tokenize_number() {
local start_pos = me.scanner.get_position()
local number_str = me.scanner.read_number()
if number_str == null {
return new JsonToken("ERROR", "Invalid number format", start_pos, me.scanner.get_position())
}
// 数値の妥当性を再チェック
if not me.validate_number_format(number_str) {
return new JsonToken("ERROR", "Malformed number: " + number_str, start_pos, me.scanner.get_position())
}
return new JsonToken("NUMBER", number_str, start_pos, me.scanner.get_position())
}
// キーワードトークン化
tokenize_keyword() {
local start_pos = me.scanner.get_position()
// アルファベット文字を読み取り
local keyword = me.scanner.read_while(this.is_identifier_char)
// キーワード判定
local token_type = me.keyword_to_token_type(keyword)
if token_type != null {
return new JsonToken(token_type, keyword, start_pos, me.scanner.get_position())
}
// 不明なキーワード(エラー)
return new JsonToken("ERROR", "Unknown keyword: " + keyword, start_pos, me.scanner.get_position())
}
// ===== ヘルパーメソッド =====
// 構造トークン作成
create_structural_token(token_type, start_pos) {
return new JsonToken(token_type, this.token_type_to_char(token_type), start_pos, start_pos + 1)
}
// トークンタイプから文字を取得
token_type_to_char(token_type) {
if token_type == "LBRACE" {
return "{"
} else {
if token_type == "RBRACE" {
return "}"
} else {
if token_type == "LBRACKET" {
return "["
} else {
if token_type == "RBRACKET" {
return "]"
} else {
if token_type == "COMMA" {
return ","
} else {
if token_type == "COLON" {
return ":"
} else {
return ""
}
}
}
}
}
}
}
// 識別子文字かどうか判定
is_identifier_char(ch) {
return me.is_alphanumeric_char(ch) or ch == "_"
}
// 数値形式の妥当性検証
validate_number_format(num_str) {
// 基本的な数値パターンチェック
if num_str.length() == 0 {
return false
}
// JSON数値の厳密な検証
// 先頭ゼロの禁止("0"以外で"0"で始まる整数は無効)
if num_str.length() > 1 and num_str.substring(0, 1) == "0" {
local second_char = num_str.substring(1, 2)
if me.is_digit_char(second_char) {
return false // "01", "02" などは無効
}
}
// マイナス符号の後に数字があるかチェック
if me.starts_with(num_str, "-") {
if num_str.length() == 1 {
return false // "-" だけは無効
}
local after_minus = num_str.substring(1, 2)
if not me.is_digit_char(after_minus) {
return false
}
}
return true
}
// ===== 結果取得メソッド =====
get_tokens() {
return me.tokens
}
get_errors() {
return me.errors
}
has_errors() {
return me.errors.length() > 0
}
get_error_count() {
return me.errors.length()
}
// ===== デバッグ・分析メソッド =====
print_tokens() {
print("🔍 Tokenization Results:")
print("Total tokens: " + me.tokens.length())
print("Errors: " + me.errors.length())
if me.has_errors() {
print("\n❌ Errors found:")
local i = 0
loop(i < me.errors.length()) {
local error = me.errors.get(i)
print(" " + error.to_debug_string())
i = i + 1
}
}
print("\n📋 Token list:")
local i = 0
loop(i < me.tokens.length()) {
local token = me.tokens.get(i)
local prefix = " "
if token.is_error() {
prefix = "❌ "
}
print(prefix + token.to_string())
i = i + 1
}
}
get_statistics() {
local stats = new MapBox()
// 基本統計
stats.set("total_tokens", me.tokens.length())
stats.set("error_count", me.errors.length())
stats.set("success_rate", (me.tokens.length() - me.errors.length()) / me.tokens.length())
// トークンタイプ別統計
local type_counts = new MapBox()
local i = 0
loop(i < me.tokens.length()) {
local token = me.tokens.get(i)
local type = token.get_type()
if type_counts.has(type) {
type_counts.set(type, type_counts.get(type) + 1)
} else {
type_counts.set(type, 1)
}
i = i + 1
}
stats.set("type_distribution", type_counts)
return stats
}
// ===== 内蔵ユーティリティメソッド =====
// アルファベット判定
is_alpha_char(ch) {
return (ch >= "a" and ch <= "z") or (ch >= "A" and ch <= "Z")
}
// 数字文字判定
is_digit_char(ch) {
return ch >= "0" and ch <= "9"
}
// 英数字判定
is_alphanumeric_char(ch) {
return me.is_alpha_char(ch) or me.is_digit_char(ch)
}
// 文字列先頭判定
starts_with(str, prefix) {
if prefix.length() > str.length() {
return false
}
return str.substring(0, prefix.length()) == prefix
}
// 簡易文字列アンクオート
unquote_string(quoted_str) {
if quoted_str.length() < 2 {
return quoted_str
}
if quoted_str.substring(0, 1) == "\"" and quoted_str.substring(quoted_str.length() - 1, quoted_str.length()) == "\"" {
return quoted_str.substring(1, quoted_str.length() - 1)
}
return quoted_str
}
// 簡易文字列検証
validate_string(str) {
// 簡易実装 - 実際のJSONエスケープ検証は複雑
return str.length() >= 0 // 基本的な存在チェックのみ
}
// 文字からトークンタイプを判定
char_to_token_type(ch) {
return match ch {
"{" => "LBRACE",
"}" => "RBRACE",
"[" => "LBRACKET",
"]" => "RBRACKET",
"," => "COMMA",
":" => "COLON",
_ => null
}
}
// 数値開始文字判定
is_number_start_char(ch) {
return ch == "-" or me.is_digit_char(ch)
}
// キーワードからトークンタイプを判定
keyword_to_token_type(keyword) {
return match keyword {
"null" => "NULL",
"true" => "TRUE",
"false" => "FALSE",
_ => null
}
}
}
// 🎯 Static Box - Nyashインクルードシステム要件
static box JsonTokenizerModule {
create_tokenizer(input_text) {
return new JsonTokenizer(input_text)
}
}

View File

@ -0,0 +1,413 @@
// JsonParser — 精度重視の構文解析器yyjson相当精度
// 責務: トークン列をJsonNodeに変換、構文エラー検出、ネスト構造処理
local JsonTokenizer = include "apps/lib/json_native/lexer/tokenizer.nyash"
local JsonToken = include "apps/lib/json_native/lexer/token.nyash"
local TokenType = include "apps/lib/json_native/lexer/token.nyash"
local JsonNode = include "apps/lib/json_native/core/node.nyash"
// 🎯 高精度JSON構文解析器Everything is Box
static box JsonParserModule {
create_parser() {
return new JsonParser()
}
}
box JsonParser {
tokens: ArrayBox // トークン配列
position: IntegerBox // 現在のトークン位置
errors: ArrayBox // 構文エラー配列
birth() {
me.tokens = new ArrayBox()
me.position = 0
me.errors = new ArrayBox()
}
// ===== メイン解析メソッド =====
// JSON文字列を完全解析
parse(json_text) {
// 初期化
me.position = 0
me.errors = new ArrayBox()
// Step 1: 字句解析
local tokenizer = new JsonTokenizer(json_text)
me.tokens = tokenizer.tokenize()
// 字句解析エラーをチェック
if tokenizer.has_errors() {
local lexer_errors = tokenizer.get_errors()
local i = 0
loop(i < lexer_errors.length()) {
me.errors.push(lexer_errors.get(i))
i = i + 1
}
return null // 字句解析エラーがあれば構文解析は実行しない
}
// Step 2: 構文解析
local result = me.parse_value()
// Step 3: 余剰トークンチェック
if result != null and not me.is_at_end() {
me.add_error("Unexpected tokens after JSON value")
return null
}
return result
}
// JSON値を解析再帰下降の開始点
parse_value() {
local token = me.current_token()
if token == null or token.is_eof() {
me.add_error("Unexpected end of input")
return null
}
if token.is_error() {
me.add_error("Lexer error: " + token.get_value())
return null
}
local token_type = token.get_type()
// リテラル値
if token_type == "NULL" {
me.advance()
return JsonNode.create_null()
} else {
if token_type == "TRUE" {
me.advance()
return JsonNode.create_bool(true)
} else {
if token_type == "FALSE" {
me.advance()
return JsonNode.create_bool(false)
} else {
if token_type == "NUMBER" {
return me.parse_number()
} else {
if token_type == "STRING" {
return me.parse_string()
} else {
if token_type == "LBRACE" {
return me.parse_object()
} else {
if token_type == "LBRACKET" {
return me.parse_array()
} else {
me.add_error("Expected JSON value, got: " + token_type)
return null
}
}
}
}
}
}
}
}
// ===== 専用パーサーメソッド =====
// 数値解析
parse_number() {
local token = me.current_token()
if token == null or token.get_type() != TokenType.NUMBER() {
me.add_error("Expected number token")
return null
}
local number_str = token.get_value()
me.advance()
// 数値変換(簡易版)
local number_value = me.convert_number(number_str)
return JsonNode.create_int(number_value)
}
// 文字列解析
parse_string() {
local token = me.current_token()
if token == null or token.get_type() != TokenType.STRING() {
me.add_error("Expected string token")
return null
}
local string_value = token.get_value()
me.advance()
return JsonNode.create_string(string_value)
}
// オブジェクト解析
parse_object() {
local start_token = me.current_token()
if start_token == null or start_token.get_type() != TokenType.LBRACE() {
me.add_error("Expected '{' to start object")
return null
}
me.advance() // '{'を消費
local object_node = JsonNode.create_object()
// 空オブジェクトチェック
if me.match_token(TokenType.RBRACE()) {
return object_node
}
// キー・値ペアの解析
loop(true) {
// キー解析
local key_token = me.current_token()
if key_token == null or key_token.get_type() != TokenType.STRING() {
me.add_error("Expected string key in object")
return null
}
local key = key_token.get_value()
me.advance()
// コロン
if not me.match_token(TokenType.COLON()) {
me.add_error("Expected ':' after object key")
return null
}
// 値解析
local value = me.parse_value()
if value == null {
return null // エラーは既に記録済み
}
// オブジェクトに追加
object_node.object_set(key, value)
// 継続判定
if me.match_token(TokenType.COMMA()) {
// 次のキー・値ペアに続く
continue
} else {
if me.match_token(TokenType.RBRACE()) {
break // オブジェクト終了
} else {
me.add_error("Expected ',' or '}' in object")
return null
}
}
}
return object_node
}
// 配列解析
parse_array() {
local start_token = me.current_token()
if start_token == null or start_token.get_type() != TokenType.LBRACKET() {
me.add_error("Expected '[' to start array")
return null
}
me.advance() // '['を消費
local array_node = JsonNode.create_array()
// 空配列チェック
if me.match_token(TokenType.RBRACKET()) {
return array_node
}
// 要素の解析
loop(true) {
// 値解析
local value = me.parse_value()
if value == null {
return null // エラーは既に記録済み
}
// 配列に追加
array_node.array_push(value)
// 継続判定
if me.match_token(TokenType.COMMA()) {
// 次の要素に続く
continue
} else {
if me.match_token(TokenType.RBRACKET()) {
break // 配列終了
} else {
me.add_error("Expected ',' or ']' in array")
return null
}
}
}
return array_node
}
// ===== トークン操作メソッド =====
// 現在のトークンを取得
current_token() {
if me.position >= me.tokens.length() {
return null
}
return me.tokens.get(me.position)
}
// 次のトークンを先読み
peek_token() {
if me.position + 1 >= me.tokens.length() {
return null
}
return me.tokens.get(me.position + 1)
}
// 位置を1つ進める
advance() {
if me.position < me.tokens.length() {
me.position = me.position + 1
}
}
// 指定されたトークンタイプにマッチするかチェック(マッチしたら消費)
match_token(expected_type) {
local token = me.current_token()
if token != null and token.get_type() == expected_type {
me.advance()
return true
}
return false
}
// 終端に到達したかチェック
is_at_end() {
local token = me.current_token()
return token == null or token.is_eof()
}
// ===== エラー処理メソッド =====
// エラーを追加
add_error(message) {
local token = me.current_token()
local error_info = new MapBox()
error_info.set("message", message)
if token != null {
error_info.set("position", token.get_start())
error_info.set("line", token.get_line())
error_info.set("column", token.get_column())
error_info.set("token", token.get_type() + "(" + token.get_value() + ")")
} else {
error_info.set("position", me.position)
error_info.set("line", 0)
error_info.set("column", 0)
error_info.set("token", "EOF")
}
me.errors.push(error_info)
}
// エラーがあるかチェック
has_errors() {
return me.errors.length() > 0
}
// エラー情報を取得
get_errors() {
return me.errors
}
// エラーを文字列として取得
get_error_messages() {
local messages = new ArrayBox()
local i = 0
loop(i < me.errors.length()) {
local error = me.errors.get(i)
local message = "Error at line " + error.get("line") + ", column " + error.get("column") + ": " + error.get("message")
messages.push(message)
i = i + 1
}
return messages
}
// ===== ユーティリティメソッド =====
// 数値文字列を数値に変換(簡易版)
convert_number(number_str) {
// TODO: より完全な数値変換実装
// 現在は簡易的にStringUtilsを使用
local StringUtils = include "apps/lib/json_native/utils/string.nyash"
if StringUtils.is_integer(number_str) {
return StringUtils.parse_integer(number_str)
} else {
// 浮動小数点数の場合は後で実装
// とりあえず0を返す
return 0
}
}
// ===== デバッグメソッド =====
print_errors() {
if not me.has_errors() {
print("✅ No parsing errors")
return
}
print("❌ Parsing errors (" + me.errors.length() + "):")
local messages = me.get_error_messages()
local i = 0
loop(i < messages.length()) {
print(" " + messages.get(i))
i = i + 1
}
}
get_parse_statistics() {
local stats = new MapBox()
stats.set("tokens_processed", me.position)
stats.set("total_tokens", me.tokens.length())
stats.set("error_count", me.errors.length())
stats.set("success", not me.has_errors())
if me.tokens.length() > 0 {
stats.set("completion_rate", me.position / me.tokens.length())
} else {
stats.set("completion_rate", 0)
}
return stats
}
}
// 🚀 便利な関数(ワンライナー使用)
static box JsonParserUtils {
// 文字列を直接パース(エラー処理込み)
parse_json(json_text) {
local parser = new JsonParser()
local result = parser.parse(json_text)
if parser.has_errors() {
print("JSON Parse Errors:")
parser.print_errors()
return null
}
return result
}
// パースしてJSONに戻すラウンドトリップテスト用
roundtrip_test(json_text) {
local parsed = this.parse_json(json_text)
if parsed == null {
return null
}
return parsed.stringify()
}
}

View File

@ -0,0 +1,287 @@
// 最終統合テスト - Nyash JSON Native完全版
local JsonParserUtils = include "apps/lib/json_native/parser/parser.nyash"
static box FinalIntegrationTest {
main() {
print("🎉 Nyash JSON Native 最終統合テスト")
print("Phase 1 (80%) + Phase 2 (20%) = 100% 完成確認")
// 実行前準備
print("\n📋 テスト準備:")
print("✅ Phase 1: 美しいモジュラー設計")
print("✅ Phase 2: yyjson相当精度")
print("🎯 目標: 「ずれ」問題の完全解決")
// 1. 完全機能テスト
print("\n1⃣ 完全機能テスト")
local function_result = this.test_complete_functionality()
// 2. 「ずれ」解決確認テスト
print("\n2⃣ 「ずれ」解決確認テスト")
local accuracy_result = this.test_parsing_accuracy_resolution()
// 3. 美しさ vs 性能テスト
print("\n3⃣ 美しさ vs 性能テスト")
local beauty_result = this.test_beauty_vs_performance()
// 4. yyjson置き換え最終確認
print("\n4⃣ yyjson置き換え最終確認")
local replacement_result = this.test_yyjson_replacement_final()
// 5. 総合判定
print("\n5⃣ 総合判定")
local overall_success = function_result and accuracy_result and beauty_result and replacement_result
if overall_success {
print("🏆 Nyash JSON Native 完全成功!")
print("✅ yyjsonC依存→ Nyash実装 完全置き換え可能")
print("✅ 美しいモジュラー設計 vs 10000行巨大ファイル")
print("✅ 「ずれ」問題の完全解決")
print("\n🚀 次のステップ: 実際のプラグインシステム統合")
} else {
print("⚠️ 一部改善が必要")
this.print_improvement_suggestions()
}
return if overall_success { 0 } else { 1 }
}
// 完全機能テスト
test_complete_functionality() {
print("Complete functionality verification:")
local test_suite = new ArrayBox()
// Phase 1機能基本
test_suite.push({name: "null values", input: "null"})
test_suite.push({name: "boolean values", input: "true"})
test_suite.push({name: "integers", input: "42"})
test_suite.push({name: "strings", input: "\"hello\""})
test_suite.push({name: "empty arrays", input: "[]"})
test_suite.push({name: "empty objects", input: "{}"})
// Phase 2機能高精度
test_suite.push({name: "escaped strings", input: "\"say \\\"hello\\\"\""})
test_suite.push({name: "nested objects", input: "{\"a\": {\"b\": \"c\"}}"})
test_suite.push({name: "complex arrays", input: "[1, \"two\", {\"three\": true}]"})
test_suite.push({name: "mixed whitespace", input: " { \"key\" : \"value\" } "})
local passed = 0
local total = test_suite.length()
local i = 0
loop(i < total) {
local test = test_suite.get(i)
local result = JsonParserUtils.roundtrip_test(test.input)
if result != null {
print(" ✅ " + test.name)
passed = passed + 1
} else {
print(" ❌ " + test.name + " failed")
}
i = i + 1
}
local success_rate = (passed * 100) / total
print("Functionality: " + passed + "/" + total + " (" + success_rate + "%)")
return success_rate >= 90
}
// 「ずれ」解決確認テスト
test_parsing_accuracy_resolution() {
print("Parsing accuracy resolution verification:")
// 以前問題となっていた「ずれ」パターンをテスト
local problematic_cases = new ArrayBox()
// エスケープ問題
problematic_cases.push({
name: "Quote escaping",
input: "{\"message\": \"say \\\"hello\\\"\"}"
})
// ネスト問題
problematic_cases.push({
name: "Deep nesting",
input: "{\"a\": {\"b\": {\"c\": \"deep\"}}}"
})
// 境界問題
problematic_cases.push({
name: "Comma in strings",
input: "{\"comma,inside\": \"value,with,commas\"}"
})
// 空白問題
problematic_cases.push({
name: "Mixed whitespace",
input: "{\n \"multiline\": \"value\"\n}"
})
local resolved = 0
local i = 0
loop(i < problematic_cases.length()) {
local test = problematic_cases.get(i)
local parsed = JsonParserUtils.parse_json(test.input)
if parsed != null {
local output = parsed.stringify()
print(" ✅ " + test.name + " resolved")
print(" Input: " + test.input)
print(" Output: " + output)
resolved = resolved + 1
} else {
print(" ❌ " + test.name + " still problematic")
}
i = i + 1
}
print("Accuracy resolution: " + resolved + "/" + problematic_cases.length())
return resolved == problematic_cases.length()
}
// 美しさ vs 性能テスト
test_beauty_vs_performance() {
print("Beauty vs Performance verification:")
// 美しさ指標
local beauty_score = this.calculate_beauty_score()
print(" Beauty score: " + beauty_score + "/100")
print(" ✅ Modular design (vs yyjson monolith)")
print(" ✅ 200-line modules (vs 10000-line file)")
print(" ✅ Everything is Box consistency")
print(" ✅ DRY principle adherence")
// 性能指標(簡易)
local performance_score = this.calculate_performance_score()
print(" Performance score: " + performance_score + "/100")
print(" ✅ Accurate parsing (no 'ずれ')")
print(" ✅ Complete error detection")
print(" ⚠️ Speed optimization pending (acceptable for Phase 2)")
// バランス判定
local balance_good = beauty_score >= 80 and performance_score >= 60
if balance_good {
print(" ✅ Excellent beauty-performance balance")
} else {
print(" ⚠️ Balance needs adjustment")
}
return balance_good
}
// yyjson置き換え最終確認
test_yyjson_replacement_final() {
print("yyjson replacement final verification:")
// 実際の使用パターンテスト
local real_usage = "{\"kind\":\"Program\",\"statements\":[]}"
local parsed = JsonParserUtils.parse_json(real_usage)
if parsed != null and parsed.get_kind() == "object" {
local kind_node = parsed.object_get("kind")
if kind_node != null and kind_node.as_string() == "Program" {
print(" ✅ Real usage pattern works")
} else {
print(" ❌ Real usage pattern broken")
return false
}
} else {
print(" ❌ Basic parsing failed")
return false
}
// API互換性確認
print(" ✅ JsonDocBox API compatibility maintained")
print(" ✅ Error handling equivalent")
print(" ✅ Result format consistent")
return true
}
// 美しさスコア計算
calculate_beauty_score() {
// 美しさの指標(簡易版)
local score = 0
// モジュラー設計
score = score + 25 // vs monolithic yyjson
// ファイルサイズ適正
score = score + 25 // 200行 vs 10000行
// 一貫性
score = score + 25 // Everything is Box
// 可読性
score = score + 25 // 理解しやすさ
return score
}
// 性能スコア計算(簡易版)
calculate_performance_score() {
local score = 0
// 精度
score = score + 40 // yyjson相当精度
// エラー検出
score = score + 30 // 完全なエラー検出
// 速度(現時点では最適化前)
score = score + 30 // 許容範囲内
return score
}
// 改善提案
print_improvement_suggestions() {
print("\n💡 改善提案:")
print("1. 数値解析の強化(浮動小数点・指数表記)")
print("2. Unicodeエスケープの完全対応")
print("3. ストリーミング解析対応大容量JSON")
print("4. 性能最適化(必要に応じて)")
print("5. 詳細なエラー位置情報")
}
}
// 🎯 完成記念テスト
static box CompletionCelebration {
celebrate_completion() {
print("\n🎉🎉🎉 Nyash JSON Native 完成記念 🎉🎉🎉")
print("")
print("🏆 達成事項:")
print(" ✅ yyjsonC依存からの完全脱却")
print(" ✅ 「ずれ」問題の根本解決")
print(" ✅ 美しいモジュラー設計の実現")
print(" ✅ Everything is Box一貫性")
print(" ✅ 80/20ルール実践成功")
print("")
print("📊 設計思想の勝利:")
print(" 🆚 yyjson: 10000行巨大ファイル")
print(" ✨ Nyash: 200行×美しいモジュール")
print("")
print("🚀 革命的な成果:")
print(" 🔄 substring()危険解析 → 🎯 精密トークナイザー")
print(" 🔄 エスケープ無視 → 🎯 完全エスケープ対応")
print(" 🔄 ネスト深度無視 → 🎯 無制限ネスト処理")
print(" 🔄 境界判定失敗 → 🎯 構文解析による確実性")
print("")
print("🎯 次の冒険:")
print(" 📦 プラグインシステム統合")
print(" 🔄 実際のyyjson置き換え")
print(" ⚡ 性能最適化(必要に応じて)")
print(" 🌍 Nyash生態系への統合")
print("")
print("✨ 美しさが機能性を兼ね備えた奇跡の実装!")
print("🎊 おめでとうございます!")
}
}

View File

@ -0,0 +1,182 @@
// 完全統合テスト - 美しいモジュラー設計の動作確認
local JsonNode = include "apps/lib/json_native/core/node.nyash"
print("🎨 Nyash JSON Native 統合テスト開始")
print("美しいモジュラー設計 vs yyjson巨大ファイル")
// ===== 基本JSON生成・パーステスト =====
print("\n📊 基本JSON操作テスト")
// 複雑なJSONオブジェクト生成
local user = JsonNode.create_object()
user.object_set("id", JsonNode.create_int(42))
user.object_set("name", JsonNode.create_string("Alice \"Wonder\" Smith"))
user.object_set("active", JsonNode.create_bool(true))
user.object_set("profile", JsonNode.create_null())
// 配列生成
local tags = JsonNode.create_array()
tags.array_push(JsonNode.create_string("developer"))
tags.array_push(JsonNode.create_string("nyash-lover"))
tags.array_push(JsonNode.create_string("json-native"))
user.object_set("tags", tags)
// ネストしたオブジェクト
local settings = JsonNode.create_object()
settings.object_set("theme", JsonNode.create_string("dark"))
settings.object_set("notifications", JsonNode.create_bool(false))
user.object_set("settings", settings)
// JSON文字列生成
local json_output = user.stringify()
print("Generated JSON:")
print(json_output)
// ===== パース・ラウンドトリップテスト =====
print("\n🔄 パース・ラウンドトリップテスト")
// 基本値のパースとラウンドトリップ
local test_cases = new ArrayBox()
test_cases.push("null")
test_cases.push("true")
test_cases.push("false")
test_cases.push("42")
test_cases.push("\"hello world\"")
test_cases.push("\"say \\\"hello\\\"\"")
test_cases.push("[]")
test_cases.push("{}")
local i = 0
loop(i < test_cases.length()) {
local input = test_cases.get(i)
local parsed = JsonNode.parse(input)
local output = parsed.stringify()
print("Input: " + input)
print("Output: " + output)
if input == output {
print("✅ Perfect roundtrip!")
} else {
print("⚠️ Roundtrip difference (expected for complex cases)")
}
print("")
i = i + 1
}
// ===== 型安全アクセステスト =====
print("\n🔒 型安全アクセステスト")
// 数値アクセス
local num_node = JsonNode.create_int(123)
print("Integer node as int: " + num_node.as_int()) // 123
print("Integer node as string: " + num_node.as_string()) // ""
print("Integer node as bool: " + num_node.as_bool()) // false
// 文字列アクセス
local str_node = JsonNode.create_string("test")
print("String node as string: " + str_node.as_string()) // "test"
print("String node as int: " + str_node.as_int()) // 0
// bool アクセス
local bool_node = JsonNode.create_bool(true)
print("Bool node as bool: " + bool_node.as_bool()) // true
print("Bool node as string: " + bool_node.as_string()) // ""
// ===== 配列・オブジェクト操作テスト =====
print("\n📦 コレクション操作テスト")
// 配列操作
local arr = JsonNode.create_array()
arr.array_push(JsonNode.create_string("first"))
arr.array_push(JsonNode.create_int(2))
arr.array_push(JsonNode.create_bool(true))
print("Array size: " + arr.array_size())
print("Array[0]: " + arr.array_get(0).stringify())
print("Array[1]: " + arr.array_get(1).stringify())
print("Array[2]: " + arr.array_get(2).stringify())
print("Array JSON: " + arr.stringify())
// オブジェクト操作
local obj = JsonNode.create_object()
obj.object_set("key1", JsonNode.create_string("value1"))
obj.object_set("key2", JsonNode.create_int(999))
print("Object['key1']: " + obj.object_get("key1").stringify())
print("Object['key2']: " + obj.object_get("key2").stringify())
print("Object['missing']: " + obj.object_get("missing")) // null
print("Object JSON: " + obj.stringify())
// オブジェクトキー一覧
local keys = obj.object_keys()
print("Object keys count: " + keys.length())
local j = 0
loop(j < keys.length()) {
print("Key[" + j + "]: " + keys.get(j))
j = j + 1
}
// ===== エッジケーステスト =====
print("\n⚠ エッジケーステスト")
// 空の構造
local empty_arr = JsonNode.create_array()
local empty_obj = JsonNode.create_object()
print("Empty array: " + empty_arr.stringify())
print("Empty object: " + empty_obj.stringify())
// エスケープが必要な文字列
local special_str = JsonNode.create_string("Line1\nLine2\tTabbed")
print("Special string: " + special_str.stringify())
// null値の処理
local null_node = JsonNode.create_null()
print("Null node: " + null_node.stringify())
print("Null as string: " + null_node.as_string())
print("Null as int: " + null_node.as_int())
print("Null as bool: " + null_node.as_bool())
print("\n🎉 統合テスト完了!")
print("🏆 美しいモジュラー設計の勝利 - 理解しやすく保守しやすいJSON実装")
print("💡 yyjsonの10000行に対し、Nyashは美しい200行×モジュール設計")
// ===== 性能予備測定 =====
print("\n⏱ 簡易性能テスト")
local start_time = 0 // TODO: 時間測定機能が必要
local iterations = 100
// 生成テスト
local k = 0
loop(k < iterations) {
local temp_obj = JsonNode.create_object()
temp_obj.object_set("id", JsonNode.create_int(k))
temp_obj.object_set("name", JsonNode.create_string("User" + k))
temp_obj.stringify() // JSON生成
k = k + 1
}
print("生成テスト完了: " + iterations + " iterations")
// パーステスト
k = 0
loop(k < iterations) {
JsonNode.parse("42")
JsonNode.parse("\"test\"")
JsonNode.parse("true")
k = k + 1
}
print("パーステスト完了: " + iterations + " iterations")
print("🚀 次のステップ: Lexer・Parserで複雑なJSON対応")

View File

@ -0,0 +1,243 @@
// Phase 2 精度テスト - yyjson相当精度の検証
local JsonParser = include "apps/lib/json_native/parser/parser.nyash"
local JsonParserUtils = include "apps/lib/json_native/parser/parser.nyash"
static box Phase2AccuracyTest {
main() {
print("🎯 Phase 2 完成テスト - yyjson相当精度検証")
print("美しいモジュラー設計 vs 「ずれ」問題の完全解決")
// 1. 基本精度テスト
print("\n1⃣ 基本JSON精度テスト")
this.test_basic_accuracy()
// 2. エスケープ処理精度テスト
print("\n2⃣ エスケープ処理精度テスト")
this.test_escape_accuracy()
// 3. ネスト構造精度テスト
print("\n3⃣ ネスト構造精度テスト")
this.test_nesting_accuracy()
// 4. 境界条件精度テスト
print("\n4⃣ 境界条件精度テスト")
this.test_boundary_accuracy()
// 5. エラー検出精度テスト
print("\n5⃣ エラー検出精度テスト")
this.test_error_detection()
// 6. ラウンドトリップテスト
print("\n6⃣ ラウンドトリップテスト")
this.test_roundtrip_accuracy()
print("\n🏆 Phase 2 精度テスト完了!")
return 0
}
// 基本JSON精度テスト
test_basic_accuracy() {
local test_cases = new ArrayBox()
// 基本値テスト
test_cases.push({input: "null", expected: "null"})
test_cases.push({input: "true", expected: "true"})
test_cases.push({input: "false", expected: "false"})
test_cases.push({input: "42", expected: "42"})
test_cases.push({input: "-123", expected: "-123"})
test_cases.push({input: "\"hello\"", expected: "\"hello\""})
// 空構造テスト
test_cases.push({input: "[]", expected: "[]"})
test_cases.push({input: "{}", expected: "{}"})
local passed = 0
local total = test_cases.length()
local i = 0
loop(i < total) {
local test_case = test_cases.get(i)
local input = test_case.input
local expected = test_case.expected
local result = JsonParserUtils.roundtrip_test(input)
if result == expected {
print("✅ " + input + " → " + result)
passed = passed + 1
} else {
print("❌ " + input + " → " + result + " (expected: " + expected + ")")
}
i = i + 1
}
print("Basic accuracy: " + passed + "/" + total + " passed")
}
// エスケープ処理精度テスト
test_escape_accuracy() {
local escape_cases = new ArrayBox()
// 基本エスケープ
escape_cases.push({input: "\"say \\\"hello\\\"\"", desc: "Quote escaping"})
escape_cases.push({input: "\"path\\\\to\\\\file\"", desc: "Backslash escaping"})
escape_cases.push({input: "\"line1\\nline2\"", desc: "Newline escaping"})
escape_cases.push({input: "\"tab\\there\"", desc: "Tab escaping"})
// Unicode エスケープ(簡易版)
// escape_cases.push({input: "\"\\u0041\\u0042\"", desc: "Unicode escaping"})
local i = 0
loop(i < escape_cases.length()) {
local test_case = escape_cases.get(i)
local input = test_case.input
local desc = test_case.desc
print("Testing: " + desc)
print(" Input: " + input)
local parsed = JsonParserUtils.parse_json(input)
if parsed != null {
local output = parsed.stringify()
print(" Output: " + output)
print(" ✅ Success")
} else {
print(" ❌ Parse failed")
}
i = i + 1
}
}
// ネスト構造精度テスト
test_nesting_accuracy() {
local nesting_cases = new ArrayBox()
// 浅いネスト
nesting_cases.push("{\"a\": {\"b\": \"value\"}}")
nesting_cases.push("[1, [2, 3], 4]")
nesting_cases.push("{\"array\": [1, 2, 3], \"object\": {\"key\": \"value\"}}")
// 複雑なネスト
nesting_cases.push("{\"users\": [{\"name\": \"Alice\", \"age\": 30}, {\"name\": \"Bob\", \"age\": 25}]}")
nesting_cases.push("[{\"type\": \"A\", \"data\": {\"x\": 1}}, {\"type\": \"B\", \"data\": {\"y\": 2}}]")
local i = 0
loop(i < nesting_cases.length()) {
local input = nesting_cases.get(i)
print("Testing complex nesting:")
print(" Input: " + input)
local parsed = JsonParserUtils.parse_json(input)
if parsed != null {
local output = parsed.stringify()
print(" Output: " + output)
print(" ✅ Nesting handled correctly")
} else {
print(" ❌ Nesting parse failed")
}
print("")
i = i + 1
}
}
// 境界条件精度テスト
test_boundary_accuracy() {
local boundary_cases = new ArrayBox()
// 空白処理
boundary_cases.push({input: " { \"key\" : \"value\" } ", desc: "Whitespace handling"})
boundary_cases.push({input: "{\n \"key\": \"value\"\n}", desc: "Newline handling"})
boundary_cases.push({input: "{\"key\":\"value\"}", desc: "No spaces"})
// 特殊文字
boundary_cases.push({input: "{\"key with spaces\": \"value\"}", desc: "Key with spaces"})
boundary_cases.push({input: "{\"comma,inside\": \"value,with,commas\"}", desc: "Commas in strings"})
local i = 0
loop(i < boundary_cases.length()) {
local test_case = boundary_cases.get(i)
local input = test_case.input
local desc = test_case.desc
print("Testing: " + desc)
print(" Input: '" + input + "'")
local parsed = JsonParserUtils.parse_json(input)
if parsed != null {
print(" ✅ Boundary condition handled")
} else {
print(" ❌ Boundary condition failed")
}
i = i + 1
}
}
// エラー検出精度テスト
test_error_detection() {
local error_cases = new ArrayBox()
// 構文エラー
error_cases.push({input: "{\"key\": }", desc: "Missing value"})
error_cases.push({input: "{\"key\" \"value\"}", desc: "Missing colon"})
error_cases.push({input: "{\"key\": \"value\",}", desc: "Trailing comma"})
error_cases.push({input: "[1, 2, 3,]", desc: "Trailing comma in array"})
// 不正な文字列
error_cases.push({input: "{\"key\": \"unclosed string}", desc: "Unclosed string"})
error_cases.push({input: "{\"key\": \"invalid\\x escape\"}", desc: "Invalid escape"})
// 不正な数値
error_cases.push({input: "{\"key\": 01}", desc: "Leading zero"})
error_cases.push({input: "{\"key\": -}", desc: "Incomplete number"})
local i = 0
loop(i < error_cases.length()) {
local test_case = error_cases.get(i)
local input = test_case.input
local desc = test_case.desc
print("Testing error detection: " + desc)
print(" Input: " + input)
local parsed = JsonParserUtils.parse_json(input)
if parsed == null {
print(" ✅ Error correctly detected")
} else {
print(" ❌ Error not detected (should have failed)")
}
i = i + 1
}
}
// ラウンドトリップテスト
test_roundtrip_accuracy() {
local complex_json = "{\"project\": \"Nyash JSON Native\", \"version\": 1, \"features\": [\"modular\", \"beautiful\", \"accurate\"], \"config\": {\"provider\": \"nyash\", \"fallback\": true}, \"status\": \"phase2_complete\"}"
print("Complex JSON roundtrip test:")
print("Input: " + complex_json)
local result = JsonParserUtils.roundtrip_test(complex_json)
if result != null {
print("Output: " + result)
print("✅ Complex roundtrip successful")
// 2回目のラウンドトリップテスト安定性確認
local second_result = JsonParserUtils.roundtrip_test(result)
if second_result == result {
print("✅ Stable roundtrip confirmed")
} else {
print("⚠️ Roundtrip not stable")
}
} else {
print("❌ Complex roundtrip failed")
}
}
}

View File

@ -0,0 +1,80 @@
// JsonNode基本動作テスト - 80%の動く基盤を確認
local JsonNode = include "apps/lib/json_native/core/node.nyash"
// ===== 基本値テスト =====
print("🧪 JsonNode基本動作テスト開始")
// null値テスト
local null_node = JsonNode.create_null()
print("null test: " + null_node.stringify()) // 期待値: "null"
// bool値テスト
local true_node = JsonNode.create_bool(true)
local false_node = JsonNode.create_bool(false)
print("bool test: " + true_node.stringify()) // 期待値: "true"
print("bool test: " + false_node.stringify()) // 期待値: "false"
// int値テスト
local int_node = JsonNode.create_int(42)
print("int test: " + int_node.stringify()) // 期待値: "42"
// string値テスト
local str_node = JsonNode.create_string("hello")
print("string test: " + str_node.stringify()) // 期待値: "\"hello\""
// エスケープテスト
local escape_node = JsonNode.create_string("say \"hello\"")
print("escape test: " + escape_node.stringify()) // 期待値: "\"say \\\"hello\\\"\""
// ===== 配列テスト =====
local array_node = JsonNode.create_array()
array_node.array_push(JsonNode.create_int(1))
array_node.array_push(JsonNode.create_int(2))
array_node.array_push(JsonNode.create_string("three"))
print("array test: " + array_node.stringify()) // 期待値: "[1,2,\"three\"]"
// ===== オブジェクトテスト =====
local object_node = JsonNode.create_object()
object_node.object_set("name", JsonNode.create_string("Nyash"))
object_node.object_set("version", JsonNode.create_int(1))
object_node.object_set("active", JsonNode.create_bool(true))
print("object test: " + object_node.stringify()) // 期待値: {"name":"Nyash","version":1,"active":true}
// ===== ネストテスト =====
local nested_object = JsonNode.create_object()
nested_object.object_set("user", object_node)
nested_object.object_set("items", array_node)
print("nested test: " + nested_object.stringify())
// ===== パーステスト(簡易版) =====
print("\n🔍 簡易パーステスト")
local parsed_null = JsonNode.parse("null")
print("parse null: " + parsed_null.stringify())
local parsed_true = JsonNode.parse("true")
print("parse true: " + parsed_true.stringify())
local parsed_false = JsonNode.parse("false")
print("parse false: " + parsed_false.stringify())
local parsed_int = JsonNode.parse("42")
print("parse int: " + parsed_int.stringify())
local parsed_string = JsonNode.parse("\"hello\"")
print("parse string: " + parsed_string.stringify())
local parsed_array = JsonNode.parse("[]")
print("parse empty array: " + parsed_array.stringify())
local parsed_object = JsonNode.parse("{}")
print("parse empty object: " + parsed_object.stringify())
print("\n✅ JsonNode基本動作テスト完了")
print("🎯 次のステップ: レクサー・パーサー実装で複雑なJSONに対応")

View File

@ -0,0 +1,71 @@
// Utils層テスト - StringUtils & EscapeUtilsの動作確認
local StringUtils = include "apps/lib/json_native/utils/string.nyash"
local EscapeUtils = include "apps/lib/json_native/utils/escape.nyash"
print("🧪 Utils層テスト開始")
// ===== StringUtilsテスト =====
print("\n📝 StringUtilsテスト")
// トリム機能テスト
local trimmed = StringUtils.trim(" hello world ")
print("trim test: '" + trimmed + "'") // 期待値: 'hello world'
// 文字判定テスト
print("is_digit('5'): " + StringUtils.is_digit("5")) // true
print("is_digit('a'): " + StringUtils.is_digit("a")) // false
print("is_alpha('A'): " + StringUtils.is_alpha("A")) // true
print("is_alpha('1'): " + StringUtils.is_alpha("1")) // false
// 文字列検索テスト
print("index_of('hello', 'l'): " + StringUtils.index_of("hello", "l")) // 2
print("contains('hello', 'ell'): " + StringUtils.contains("hello", "ell")) // true
// 大文字小文字変換テスト
print("to_upper('hello'): " + StringUtils.to_upper("hello")) // HELLO
print("to_lower('WORLD'): " + StringUtils.to_lower("WORLD")) // world
// 配列結合テスト
local arr = new ArrayBox()
arr.push("a")
arr.push("b")
arr.push("c")
print("join test: " + StringUtils.join(arr, ",")) // a,b,c
// 数値判定・変換テスト
print("is_integer('42'): " + StringUtils.is_integer("42")) // true
print("is_integer('abc'): " + StringUtils.is_integer("abc")) // false
print("parse_integer('42'): " + StringUtils.parse_integer("42")) // 42
// ===== EscapeUtilsテスト =====
print("\n🔒 EscapeUtilsテスト")
// 基本エスケープテスト
print("escape_string('hello'): " + EscapeUtils.escape_string("hello"))
print("escape_string('say \"hi\"'): " + EscapeUtils.escape_string("say \"hi\""))
// クォート機能テスト
print("quote_string('hello'): " + EscapeUtils.quote_string("hello"))
// アンエスケープテスト
local escaped = "say \\\"hi\\\""
local unescaped = EscapeUtils.unescape_string(escaped)
print("unescape test: " + unescaped)
// クォート除去テスト
local quoted = "\"hello world\""
local unquoted = EscapeUtils.unquote_string(quoted)
print("unquote test: " + unquoted)
// 妥当性検証テスト
print("validate_string('hello'): " + EscapeUtils.validate_string("hello"))
print("validate_string('hello\\nworld'): " + EscapeUtils.validate_string("hello\\nworld"))
// 安全表示テスト
print("safe_display('hello\\tworld'): " + EscapeUtils.safe_display("hello\tworld"))
print("\n✅ Utils層テスト完了")
print("🎯 美しいモジュラー設計の威力を確認")

View File

@ -0,0 +1,294 @@
// yyjson置き換えテスト - 既存APIとの互換性確認
local JsonParserUtils = include "apps/lib/json_native/parser/parser.nyash"
// 🔄 既存JsonDocBox API互換テスト
static box JsonDocBoxCompatTest {
main() {
print("🔄 yyjson置き換えテスト - API互換性確認")
print("目標: 既存のJsonDocBox使用コードが同じ結果を返すこと")
// 1. 基本API互換テスト
print("\n1⃣ 基本API互換テスト")
this.test_basic_api_compat()
// 2. 実際の使用例テスト
print("\n2⃣ 実際の使用例テスト")
this.test_real_usage_examples()
// 3. エラー処理互換テスト
print("\n3⃣ エラー処理互換テスト")
this.test_error_handling_compat()
// 4. 性能比較テスト
print("\n4⃣ 性能比較テスト")
this.test_performance_comparison()
print("\n✅ yyjson置き換えテスト完了")
print("🎯 次のステップ: プラグインシステムでの実際の置き換え")
return 0
}
// 基本API互換テスト
test_basic_api_compat() {
print("JsonDocBox API互換性テスト:")
// 既存の使用パターンをシミュレート
local test_json = "{\"kind\":\"Program\",\"statements\":[]}"
print("Original JSON: " + test_json)
// Nyash Native版で解析
local native_result = this.simulate_jsondocbox_api(test_json)
if native_result != null {
print("Native result: " + native_result)
print("✅ API compatibility maintained")
} else {
print("❌ API compatibility broken")
}
// より複雑なケース
local complex_json = "{\"user\": {\"name\": \"Alice\", \"id\": 123}, \"active\": true}"
print("\nComplex JSON: " + complex_json)
local complex_result = this.simulate_jsondocbox_api(complex_json)
if complex_result != null {
print("Complex result: " + complex_result)
print("✅ Complex JSON handled correctly")
} else {
print("❌ Complex JSON handling failed")
}
}
// JsonDocBox APIをシミュレート
simulate_jsondocbox_api(json_text) {
// 既存コードパターン:
// local doc = new JsonDocBox()
// doc.parse(json)
// local root = doc.root()
// print(root.get("kind").str())
local parsed = JsonParserUtils.parse_json(json_text)
if parsed == null {
return null
}
// root.get("kind").str() をシミュレート
if parsed.get_kind() == "object" {
local kind_node = parsed.object_get("kind")
if kind_node != null and kind_node.get_kind() == "string" {
return kind_node.as_string()
}
}
return "non_object_or_no_kind"
}
// 実際の使用例テスト
test_real_usage_examples() {
// apps/tests/jsonbox_parse_ok.nyash の内容をシミュレート
print("Real usage example simulation:")
local examples = new ArrayBox()
examples.push("{\"kind\":\"Program\",\"statements\":[]}")
examples.push("{\"type\":\"function\",\"name\":\"main\",\"body\":[]}")
examples.push("{\"operation\":\"add\",\"left\":1,\"right\":2}")
local i = 0
loop(i < examples.length()) {
local json = examples.get(i)
print("Testing: " + json)
// エラーチェックdoc.error()に相当)
local parsed = JsonParserUtils.parse_json(json)
if parsed == null {
print(" Error detected (as expected for invalid JSON)")
} else {
print(" Parse success: " + parsed.stringify())
}
i = i + 1
}
}
// エラー処理互換テスト
test_error_handling_compat() {
print("Error handling compatibility:")
// 既存のエラーケースをテスト
local error_cases = new ArrayBox()
error_cases.push("{\"kind\": }") // apps/tests/jsonbox_parse_err.nyash
error_cases.push("{invalid json}")
error_cases.push("[1, 2, 3") // 不完全な配列
error_cases.push("{\"key\": \"value\",}") // 末尾カンマ
local i = 0
loop(i < error_cases.length()) {
local invalid_json = error_cases.get(i)
print("Testing error case: " + invalid_json)
local result = JsonParserUtils.parse_json(invalid_json)
if result == null {
print(" ✅ Error correctly detected and handled")
} else {
print(" ❌ Error not detected (should have failed)")
}
i = i + 1
}
}
// 性能比較テスト
test_performance_comparison() {
print("Performance comparison test:")
// 小さなJSON
local small_json = "{\"key\": \"value\"}"
local small_iterations = 100
print("Small JSON (" + small_iterations + " iterations):")
print(" JSON: " + small_json)
local start_time = 0 // TODO: 時間測定機能が必要
local i = 0
loop(i < small_iterations) {
JsonParserUtils.parse_json(small_json)
i = i + 1
}
print(" ✅ Small JSON performance test completed")
// 大きなJSON
local large_json = this.generate_large_json()
print("\nLarge JSON test:")
print(" Size: " + large_json.length() + " characters")
local large_result = JsonParserUtils.parse_json(large_json)
if large_result != null {
print(" ✅ Large JSON parsed successfully")
print(" Result size: " + large_result.stringify().length() + " characters")
} else {
print(" ❌ Large JSON parsing failed")
}
}
// 大きなJSONを生成
generate_large_json() {
local json = "{\"users\": ["
local i = 0
loop(i < 10) {
if i > 0 {
json = json + ", "
}
local active_val = "true"
if i % 2 != 0 {
active_val = "false"
}
local user_json = "{\"id\": " + i + ", \"name\": \"User" + i + "\", \"active\": " + active_val + ", \"tags\": [\"tag1\", \"tag2\", \"tag3\"], \"metadata\": {\"created\": \"2025-01-01\", \"updated\": \"2025-01-02\"}}"
json = json + user_json
i = i + 1
}
json = json + "], \"total\": 10, \"status\": \"active\"}"
return json
}
}
// 🎯 置き換え完了検証
static box ReplacementVerification {
verify_replacement_ready() {
print("🎯 yyjson置き換え準備状況の検証")
// 1. 機能完全性チェック
local functionality_score = this.check_functionality()
print("Functionality: " + functionality_score + "/100")
// 2. 精度チェック
local accuracy_score = this.check_accuracy()
print("Accuracy: " + accuracy_score + "/100")
// 3. API互換性チェック
local compatibility_score = this.check_compatibility()
print("Compatibility: " + compatibility_score + "/100")
// 4. 総合判定
local total_score = (functionality_score + accuracy_score + compatibility_score) / 3
print("Total score: " + total_score + "/100")
if total_score >= 80 {
print("🎉 置き換え準備完了!")
print("✅ yyjson → Nyash JSON Native 置き換え可能")
return true
} else {
print("⚠️ 置き換えにはさらなる改善が必要")
print("❌ 追加開発が必要な領域あり")
return false
}
}
check_functionality() {
// 基本機能のチェック(簡易版)
local tests = new ArrayBox()
tests.push("null")
tests.push("true")
tests.push("42")
tests.push("\"string\"")
tests.push("[]")
tests.push("{}")
local passed = 0
local i = 0
loop(i < tests.length()) {
local result = JsonParserUtils.parse_json(tests.get(i))
if result != null {
passed = passed + 1
}
i = i + 1
}
return (passed * 100) / tests.length()
}
check_accuracy() {
// 精度のチェック(ラウンドトリップ)
local tests = new ArrayBox()
tests.push("\"escaped\\\"quote\"")
tests.push("{\"nested\": {\"deep\": \"value\"}}")
tests.push("[1, 2, {\"mixed\": true}]")
local passed = 0
local i = 0
loop(i < tests.length()) {
local result = JsonParserUtils.roundtrip_test(tests.get(i))
if result != null {
passed = passed + 1
}
i = i + 1
}
return (passed * 100) / tests.length()
}
check_compatibility() {
// API互換性のチェック既存使用パターン
local json = "{\"kind\":\"Program\",\"statements\":[]}"
local docbox_compat = JsonDocBoxCompatTest()
local result = docbox_compat.simulate_jsondocbox_api(json)
if result == "Program" {
return 100 // 完全互換
} else {
if result != null {
return 70 // 部分互換
} else {
return 0 // 非互換
}
}
}
}

View File

@ -0,0 +1,306 @@
// EscapeUtils — JSON文字列エスケープ処理美しいモジュラー設計
// 責務: JSON文字列のエスケープ・アンエスケープ・妥当性検証
static box EscapeUtils {
// ===== JSON文字列エスケープ =====
// 文字列をJSON用にエスケープ
escape_string(s) {
local result = ""
local i = 0
loop(i < s.length()) {
local ch = s.substring(i, i + 1)
result = result + this.escape_char(ch)
i = i + 1
}
return result
}
// 1文字をエスケープ
escape_char(ch) {
if ch == "\"" {
return "\\\""
} else {
if ch == "\\" {
return "\\\\"
} else {
if ch == "/" {
return "\\/"
} else {
if ch == "\b" {
return "\\b"
} else {
if ch == "\f" {
return "\\f"
} else {
if ch == "\n" {
return "\\n"
} else {
if ch == "\r" {
return "\\r"
} else {
if ch == "\t" {
return "\\t"
} else {
if this.is_control_char(ch) {
return this.escape_unicode(ch)
} else {
return ch
}
}
}
}
}
}
}
}
}
}
// 制御文字かどうか判定
is_control_char(ch) {
// ASCII制御文字0x00-0x1Fの簡易判定
// TODO: より完全な制御文字判定
local code = this.char_code(ch)
return code >= 0 and code <= 31
}
// 文字のASCIIコードを取得簡易版
char_code(ch) {
// 主要な文字のASCIIコード簡易実装
if ch == "\0" { return 0 } else { if ch == "\t" { return 9 } else { if ch == "\n" { return 10 } else { if ch == "\r" { return 13 } else {
if ch == " " { return 32 } else { if ch == "!" { return 33 } else { if ch == "\"" { return 34 } else { if ch == "#" { return 35 } else {
if ch == "$" { return 36 } else { if ch == "%" { return 37 } else { if ch == "&" { return 38 } else { if ch == "'" { return 39 } else {
if ch == "(" { return 40 } else { if ch == ")" { return 41 } else { if ch == "*" { return 42 } else { if ch == "+" { return 43 } else {
if ch == "," { return 44 } else { if ch == "-" { return 45 } else { if ch == "." { return 46 } else { if ch == "/" { return 47 } else {
if ch == "0" { return 48 } else { if ch == "1" { return 49 } else { if ch == "2" { return 50 } else { if ch == "3" { return 51 } else {
if ch == "4" { return 52 } else { if ch == "5" { return 53 } else { if ch == "6" { return 54 } else { if ch == "7" { return 55 } else {
if ch == "8" { return 56 } else { if ch == "9" { return 57 } else {
return 0 // その他の文字は0
} } } } } } } } } } } } } } } } } } } } } } } } } } } } } }
}
// Unicodeエスケープ形式に変換簡易版
escape_unicode(ch) {
local code = this.char_code(ch)
return "\\u" + this.int_to_hex4(code)
}
// 整数を4桁の16進数文字列に変換
int_to_hex4(n) {
// 簡易実装: 0-127のASCII文字のみ対応
if n >= 0 and n <= 15 {
return "000" + this.int_to_hex_digit(n)
} else {
if n >= 16 and n <= 255 {
local high = n / 16
local low = n % 16
return "00" + this.int_to_hex_digit(high) + this.int_to_hex_digit(low)
} else {
// より大きな値は後で実装
return "0000"
}
}
}
// 整数0-15を16進数文字に変換
int_to_hex_digit(n) {
return match n {
0 => "0", 1 => "1", 2 => "2", 3 => "3",
4 => "4", 5 => "5", 6 => "6", 7 => "7",
8 => "8", 9 => "9", 10 => "a", 11 => "b",
12 => "c", 13 => "d", 14 => "e", 15 => "f",
_ => "0"
}
}
// ===== JSON文字列アンエスケープ =====
// エスケープされたJSON文字列を元に戻す
unescape_string(s) {
local result = ""
local i = 0
loop(i < s.length()) {
local ch = s.substring(i, i + 1)
if ch == "\\" and i + 1 < s.length() {
local next_ch = s.substring(i + 1, i + 2)
local unescaped = this.unescape_sequence(next_ch, s, i)
result = result + unescaped.value
i = i + unescaped.advance
} else {
result = result + ch
i = i + 1
}
}
return result
}
// エスケープシーケンスを解釈
unescape_sequence(next_ch, full_string, pos) {
return match next_ch {
"\"" => { value: "\"", advance: 2 },
"\\" => { value: "\\", advance: 2 },
"/" => { value: "/", advance: 2 },
"b" => { value: "\b", advance: 2 },
"f" => { value: "\f", advance: 2 },
"n" => { value: "\n", advance: 2 },
"r" => { value: "\r", advance: 2 },
"t" => { value: "\t", advance: 2 },
"u" => {
// Unicodeエスケープ \\uXXXX
if pos + 5 < full_string.length() {
local hex = full_string.substring(pos + 2, pos + 6)
if this.is_valid_hex4(hex) {
{ value: this.hex_to_char(hex), advance: 6 }
} else {
{ value: "\\u", advance: 2 } // 無効な場合はそのまま
}
} else {
{ value: "\\u", advance: 2 }
}
},
_ => {
// 不明なエスケープはそのまま残す
{ value: "\\" + next_ch, advance: 2 }
}
}
}
// 4桁の16進数文字列が有効かどうか判定
is_valid_hex4(s) {
if s.length() != 4 {
return false
}
local i = 0
loop(i < 4) {
local ch = s.substring(i, i + 1)
if not this.is_hex_digit(ch) {
return false
}
i = i + 1
}
return true
}
// 16進数文字かどうか判定
is_hex_digit(ch) {
return (ch >= "0" and ch <= "9") or
(ch >= "a" and ch <= "f") or
(ch >= "A" and ch <= "F")
}
// 4桁の16進数文字列を文字に変換簡易版
hex_to_char(hex) {
// 簡易実装: 基本的なASCII文字のみ対応
return match hex {
"0020" => " ", // スペース
"0021" => "!", // 感嘆符
"0022" => "\"", // ダブルクォート
"005C" => "\\", // バックスラッシュ
"0041" => "A", // A
"0061" => "a", // a
_ => "?" // 不明な文字は?で代替
}
}
// ===== 妥当性検証 =====
// JSON文字列が妥当かどうか検証
validate_string(s) {
local i = 0
loop(i < s.length()) {
local ch = s.substring(i, i + 1)
// 制御文字のチェック
if this.is_control_char(ch) and ch != "\t" and ch != "\n" and ch != "\r" {
return false // エスケープされていない制御文字
}
// エスケープシーケンスのチェック
if ch == "\\" {
if i + 1 >= s.length() {
return false // 不完全なエスケープ
}
local next_ch = s.substring(i + 1, i + 2)
if not this.is_valid_escape_char(next_ch) {
return false // 無効なエスケープ文字
}
// Unicodeエスケープの特別処理
if next_ch == "u" {
if i + 5 >= s.length() {
return false // 不完全なUnicodeエスケープ
}
local hex = s.substring(i + 2, i + 6)
if not this.is_valid_hex4(hex) {
return false // 無効な16進数
}
i = i + 6 // Unicodeエスケープをスキップ
} else {
i = i + 2 // 通常のエスケープをスキップ
}
} else {
i = i + 1
}
}
return true
}
// 有効なエスケープ文字かどうか判定
is_valid_escape_char(ch) {
return ch == "\"" or ch == "\\" or ch == "/" or
ch == "b" or ch == "f" or ch == "n" or
ch == "r" or ch == "t" or ch == "u"
}
// ===== 便利メソッド =====
// 文字列をJSON文字列リテラルとしてクォート
quote_string(s) {
return "\"" + this.escape_string(s) + "\""
}
// JSON文字列リテラルからクォートを除去してアンエスケープ
unquote_string(s) {
if s.length() >= 2 and s.substring(0, 1) == "\"" and s.substring(s.length() - 1, s.length()) == "\"" {
local content = s.substring(1, s.length() - 1)
return this.unescape_string(content)
} else {
return s // クォートされていない場合はそのまま
}
}
// 安全な文字列表示(デバッグ用)
safe_display(s) {
local result = "\""
local i = 0
loop(i < s.length()) {
local ch = s.substring(i, i + 1)
if this.is_printable(ch) {
result = result + ch
} else {
result = result + this.escape_char(ch)
}
i = i + 1
}
return result + "\""
}
// 印刷可能文字かどうか判定
is_printable(ch) {
local code = this.char_code(ch)
return code >= 32 and code <= 126 // 基本的な印刷可能ASCII文字
}
}

View File

@ -0,0 +1,303 @@
// StringUtils — 文字列処理ユーティリティ(美しいモジュラー設計)
// 責務: 文字列の基本操作・判定・変換
static box StringUtils {
// ===== 空白処理 =====
// 文字列の前後空白をトリム
trim(s) {
return this.trim_end(this.trim_start(s))
}
// 先頭空白をトリム
trim_start(s) {
local i = 0
loop(i < s.length()) {
if not this.is_whitespace(s.substring(i, i + 1)) {
break
}
i = i + 1
}
return s.substring(i, s.length())
}
// 末尾空白をトリム
trim_end(s) {
local i = s.length() - 1
loop(i >= 0) {
if not this.is_whitespace(s.substring(i, i + 1)) {
break
}
i = i - 1
}
return s.substring(0, i + 1)
}
// ===== 文字判定 =====
// 空白文字かどうか判定
is_whitespace(ch) {
return ch == " " or ch == "\t" or ch == "\n" or ch == "\r"
}
// 数字文字かどうか判定
is_digit(ch) {
return ch == "0" or ch == "1" or ch == "2" or ch == "3" or ch == "4" or ch == "5" or ch == "6" or ch == "7" or ch == "8" or ch == "9"
}
// 16進数字かどうか判定
is_hex_digit(ch) {
return this.is_digit(ch) or ch == "a" or ch == "b" or ch == "c" or ch == "d" or ch == "e" or ch == "f" or ch == "A" or ch == "B" or ch == "C" or ch == "D" or ch == "E" or ch == "F"
}
// アルファベットかどうか判定
is_alpha(ch) {
return (ch >= "a" and ch <= "z") or (ch >= "A" and ch <= "Z")
}
// 英数字かどうか判定
is_alphanumeric(ch) {
return this.is_alpha(ch) or this.is_digit(ch)
}
// ===== 文字列検索 =====
// 文字が最初に現れる位置を取得(見つからない場合は-1
index_of(s, ch) {
local i = 0
loop(i < s.length()) {
if s.substring(i, i + 1) == ch {
return i
}
i = i + 1
}
return -1
}
// 文字が最後に現れる位置を取得(見つからない場合は-1
last_index_of(s, ch) {
local i = s.length() - 1
loop(i >= 0) {
if s.substring(i, i + 1) == ch {
return i
}
i = i - 1
}
return -1
}
// 部分文字列が含まれているか判定
contains(s, substr) {
return this.index_of_string(s, substr) != -1
}
// 部分文字列の位置を取得(見つからない場合は-1
index_of_string(s, substr) {
if substr.length() == 0 {
return 0
}
if substr.length() > s.length() {
return -1
}
local i = 0
loop(i <= s.length() - substr.length()) {
if s.substring(i, i + substr.length()) == substr {
return i
}
i = i + 1
}
return -1
}
// ===== 文字列変換 =====
// 文字列を大文字に変換(簡易版)
to_upper(s) {
local result = ""
local i = 0
loop(i < s.length()) {
local ch = s.substring(i, i + 1)
result = result + this.char_to_upper(ch)
i = i + 1
}
return result
}
// 文字列を小文字に変換(簡易版)
to_lower(s) {
local result = ""
local i = 0
loop(i < s.length()) {
local ch = s.substring(i, i + 1)
result = result + this.char_to_lower(ch)
i = i + 1
}
return result
}
// 1文字を大文字に変換
char_to_upper(ch) {
if ch >= "a" and ch <= "z" {
// 簡易実装: 主要な小文字のみ対応
// 全アルファベット対応JSON処理で必要
if ch == "a" { return "A" } if ch == "b" { return "B" } if ch == "c" { return "C" }
if ch == "d" { return "D" } if ch == "e" { return "E" } if ch == "f" { return "F" }
if ch == "g" { return "G" } if ch == "h" { return "H" } if ch == "i" { return "I" }
if ch == "j" { return "J" } if ch == "k" { return "K" } if ch == "l" { return "L" }
if ch == "m" { return "M" } if ch == "n" { return "N" } if ch == "o" { return "O" }
if ch == "p" { return "P" } if ch == "q" { return "Q" } if ch == "r" { return "R" }
if ch == "s" { return "S" } if ch == "t" { return "T" } if ch == "u" { return "U" }
if ch == "v" { return "V" } if ch == "w" { return "W" } if ch == "x" { return "X" }
if ch == "y" { return "Y" } if ch == "z" { return "Z" }
return ch
} else {
return ch
}
}
// 1文字を小文字に変換
char_to_lower(ch) {
if ch >= "A" and ch <= "Z" {
// 簡易実装: 主要な大文字のみ対応
// 全アルファベット対応JSON処理で必要
if ch == "A" { return "a" } if ch == "B" { return "b" } if ch == "C" { return "c" }
if ch == "D" { return "d" } if ch == "E" { return "e" } if ch == "F" { return "f" }
if ch == "G" { return "g" } if ch == "H" { return "h" } if ch == "I" { return "i" }
if ch == "J" { return "j" } if ch == "K" { return "k" } if ch == "L" { return "l" }
if ch == "M" { return "m" } if ch == "N" { return "n" } if ch == "O" { return "o" }
if ch == "P" { return "p" } if ch == "Q" { return "q" } if ch == "R" { return "r" }
if ch == "S" { return "s" } if ch == "T" { return "t" } if ch == "U" { return "u" }
if ch == "V" { return "v" } if ch == "W" { return "w" } if ch == "X" { return "x" }
if ch == "Y" { return "y" } if ch == "Z" { return "z" }
return ch
} else {
return ch
}
}
// ===== 文字列結合 =====
// 配列を指定された区切り文字で結合
join(arr, separator) {
local result = ""
local i = 0
loop(i < arr.length()) {
if i > 0 {
result = result + separator
}
result = result + arr.get(i)
i = i + 1
}
return result
}
// 文字列を指定された区切り文字で分割(簡易版)
split(s, separator) {
local result = new ArrayBox()
if separator.length() == 0 {
result.push(s)
return result
}
local start = 0
local i = 0
loop(i <= s.length() - separator.length()) {
if s.substring(i, i + separator.length()) == separator {
result.push(s.substring(start, i))
start = i + separator.length()
i = start
} else {
i = i + 1
}
}
// 最後の部分を追加
if start <= s.length() {
result.push(s.substring(start, s.length()))
}
return result
}
// ===== 数値変換 =====
// 文字列が数値表現かどうか判定(整数のみ、簡易版)
is_integer(s) {
if s.length() == 0 {
return false
}
local start = 0
if s.substring(0, 1) == "-" {
if s.length() == 1 {
return false
}
start = 1
}
local i = start
loop(i < s.length()) {
if not this.is_digit(s.substring(i, i + 1)) {
return false
}
i = i + 1
}
return true
}
// 文字列を整数に変換(簡易版)
parse_integer(s) {
if not this.is_integer(s) {
return 0
}
// 超簡易実装: よく使われる数値のみ対応
// JSON処理に必要な数値パース実装
// 基本数字 0-9
if s == "0" { return 0 } if s == "1" { return 1 } if s == "2" { return 2 }
if s == "3" { return 3 } if s == "4" { return 4 } if s == "5" { return 5 }
if s == "6" { return 6 } if s == "7" { return 7 } if s == "8" { return 8 } if s == "9" { return 9 }
// よく使われる2桁数値 10-20
if s == "10" { return 10 } if s == "11" { return 11 } if s == "12" { return 12 }
if s == "13" { return 13 } if s == "14" { return 14 } if s == "15" { return 15 }
if s == "16" { return 16 } if s == "17" { return 17 } if s == "18" { return 18 }
if s == "19" { return 19 } if s == "20" { return 20 }
// JSON頻出数値
if s == "42" { return 42 } if s == "100" { return 100 } if s == "200" { return 200 }
if s == "404" { return 404 } if s == "500" { return 500 } if s == "1000" { return 1000 }
// 負数
if s == "-1" { return -1 } if s == "-2" { return -2 } if s == "-10" { return -10 }
// TODO: より完全な数値パース実装が必要(算術演算による動的パース)
// 現在はJSON処理によく使われる数値のみ対応
return 0
}
// ===== ユーティリティ =====
// 文字列が空または空白のみかどうか判定
is_empty_or_whitespace(s) {
return this.trim(s).length() == 0
}
// 文字列の先頭が指定された文字列で始まるか判定
starts_with(s, prefix) {
if prefix.length() > s.length() {
return false
}
return s.substring(0, prefix.length()) == prefix
}
// 文字列の末尾が指定された文字列で終わるか判定
ends_with(s, suffix) {
if suffix.length() > s.length() {
return false
}
return s.substring(s.length() - suffix.length(), s.length()) == suffix
}
}

View File

@ -544,7 +544,11 @@ static box MiniVm {
if guard > 200 { if trace == 1 { print("[collect][guard_break] guard="+guard) } break }
local p = index_of_from(json, k_print, pos)
if trace == 1 { print("[collect][loop] pos="+pos+" p="+p+" guard="+guard) }
if p < 0 { if trace == 1 { print("[collect][p_break] p="+p) } break }
if p < 0 {
if trace == 1 { print("[collect][p_break] p="+p) }
if trace == 1 { print("[collect][pre_break] about to break, out.size="+out.size()) }
break
}
// bound current Print slice to [this, next)
local obj_start = p
local next_p = index_of_from(json, k_print, p + k_print.length())
@ -757,8 +761,13 @@ static box MiniVm {
pos = obj_end + 1
if pos <= p { pos = p + k_print.length() }
}
if trace == 1 { print("[collect][loop_exit] guard="+guard+" out.size="+out.size()) }
if trace == 1 { print("[collect][return] out.size="+out.size()) }
if trace == 1 { print("[collect][loop_exit] starting cleanup") }
if trace == 1 { print("[collect][guard_val] "+guard) }
if trace == 1 { print("[collect][out_ref_check] checking out reference") }
if trace == 1 { print("[collect][calling_size] about to call out.size()") }
local outsize = out.size()
if trace == 1 { print("[collect][size_result] size="+outsize) }
if trace == 1 { print("[collect][return] returning out with size="+outsize) }
return out
}
}