Files
hakorune/apps/lib/json_native/parser/parser.nyash
Selfhosting Dev 7ab1e59450 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>
2025-09-23 04:51:17 +09:00

413 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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()
}
}