fix(parser): Part 5 - eliminate infinite loops in parse_string2 and scan_with_quote

Problem: Nested escape sequence processing in string parsing functions caused
infinite loops due to missing continue statements, allowing fallthrough to the
else block which corrupted the position counter.

Fixed functions:
- parser_expr_box.hako::parse_string2 (lines 22-83)
- parser_string_scan_box.hako::scan_with_quote (lines 11-101)

Changes:
- Flattened nested 'else { if ... }' chains to flat 'if' statements
- Added explicit 'continue' after each escape sequence processing
- Prevents fallthrough to position increment code

Verification:
 NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_PROGRAM_JSON=1 HAKO_VM_MAX_STEPS=2000000
   apps/tests/minimal_ssa_skip_ws.hako
 No budget exceeded
 Program JSON v0 output to stdout
 Exit code: 0

MirBuilder-friendly pattern applied consistently with Parts 1-4.
Stage-1 CLI infinite loop elimination COMPLETE!
This commit is contained in:
nyash-codex
2025-11-25 00:16:29 +09:00
parent b21ee3c854
commit 844e53d96c
2 changed files with 72 additions and 27 deletions

View File

@ -39,18 +39,44 @@ static box ParserExprBox {
if ch == "\\" && j + 1 < n { if ch == "\\" && j + 1 < n {
local nx = src.substring(j+1, j+2) local nx = src.substring(j+1, j+2)
if nx == "\"" { out = out + "\"" j = j + 2 } if nx == "\"" {
else { if nx == "\\" { out = out + "\\" j = j + 2 } out = out + "\""
else { if nx == "n" { out = out + "\n" j = j + 2 } j = j + 2
else { if nx == "r" { out = out + "\r" j = j + 2 } continue
else { if nx == "t" { out = out + "\t" j = j + 2 } }
else { if nx == "u" && j + 5 < n { out = out + src.substring(j, j+6) j = j + 6 } if nx == "\\" {
else { out = out + nx j = j + 2 } } } } } } out = out + "\\"
} else { j = j + 2
continue
}
if nx == "n" {
out = out + "\n"
j = j + 2
continue
}
if nx == "r" {
out = out + "\r"
j = j + 2
continue
}
if nx == "t" {
out = out + "\t"
j = j + 2
continue
}
if nx == "u" && j + 5 < n {
out = out + src.substring(j, j+6)
j = j + 6
continue
}
out = out + nx
j = j + 2
continue
}
out = out + ch out = out + ch
j = j + 1 j = j + 1
} }
}
ctx.gpos_set(j) ctx.gpos_set(j)
return "{\"type\":\"Str\",\"value\":\"" + ctx.esc_json(out) + "\"}" return "{\"type\":\"Str\",\"value\":\"" + ctx.esc_json(out) + "\"}"

View File

@ -35,48 +35,67 @@ static box ParserStringScanBox {
if nx == "\\" { if nx == "\\" {
out = out + "\\" out = out + "\\"
j = j + 2 j = j + 2
} else { if nx == "\"" { continue
}
if nx == "\"" {
out = out + "\"" out = out + "\""
j = j + 2 j = j + 2
} else { if nx == "'" { continue
}
if nx == "'" {
out = out + "'" out = out + "'"
j = j + 2 j = j + 2
} else { if nx == "/" { continue
}
if nx == "/" {
out = out + "/" out = out + "/"
j = j + 2 j = j + 2
} else { if nx == "b" { continue
}
if nx == "b" {
// Backspace (0x08) - for MVP, skip (empty string) // Backspace (0x08) - for MVP, skip (empty string)
out = out + "" out = out + ""
j = j + 2 j = j + 2
} else { if nx == "f" { continue
}
if nx == "f" {
// Form feed (0x0C) - for MVP, skip (empty string) // Form feed (0x0C) - for MVP, skip (empty string)
out = out + "" out = out + ""
j = j + 2 j = j + 2
} else { if nx == "n" { continue
}
if nx == "n" {
out = out + "\n" out = out + "\n"
j = j + 2 j = j + 2
} else { if nx == "r" { continue
}
if nx == "r" {
// FIX: \r should be CR (0x0D), not LF (0x0A) // FIX: \r should be CR (0x0D), not LF (0x0A)
// Keep as "\r" literal for MVP // Keep as "\r" literal for MVP
out = out + "\r" out = out + "\r"
j = j + 2 j = j + 2
} else { if nx == "t" { continue
}
if nx == "t" {
out = out + "\t" out = out + "\t"
j = j + 2 j = j + 2
} else { if nx == "u" && j + 5 < n { continue
}
if nx == "u" && j + 5 < n {
// \uXXXX: MVP - concatenate as-is (6 chars) // \uXXXX: MVP - concatenate as-is (6 chars)
out = out + src.substring(j, j+6) out = out + src.substring(j, j+6)
j = j + 6 j = j + 6
} else { continue
}
// Unknown escape: tolerate (keep backslash + char) // Unknown escape: tolerate (keep backslash + char)
out = out + "\\" + nx out = out + "\\" + nx
j = j + 2 j = j + 2
} } } } } } } } } } continue
} else { }
out = out + ch out = out + ch
j = j + 1 j = j + 1
} }
}
// if unterminated, return what we have and the last pos to avoid infinite loops // if unterminated, return what we have and the last pos to avoid infinite loops
return out + "@" + ParserCommonUtilsBox.i2s(j) return out + "@" + ParserCommonUtilsBox.i2s(j)
} }