Files
hakorune/apps/lib/json_native/lexer/scanner.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

347 lines
9.7 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.

// 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) + "'}"
}
}