1509 lines
56 KiB
Plaintext
1509 lines
56 KiB
Plaintext
|
|
// Mini-VM: function-based entry using library
|
|||
|
|
|
|||
|
|
// Minimal JSON cursor helpers (developer preview)
|
|||
|
|
// Note: naive and partial; sufficient for Mini-VM MVP patterns.
|
|||
|
|
static box MiniJsonCur {
|
|||
|
|
_is_digit(ch) { if ch == "0" { return 1 } if ch == "1" { return 1 } if ch == "2" { return 1 } if ch == "3" { return 1 } if ch == "4" { return 1 } if ch == "5" { return 1 } if ch == "6" { return 1 } if ch == "7" { return 1 } if ch == "8" { return 1 } if ch == "9" { return 1 } return 0 }
|
|||
|
|
// Skip whitespace from pos; return first non-ws index or -1
|
|||
|
|
next_non_ws(s, pos) {
|
|||
|
|
local i = pos
|
|||
|
|
local n = s.length()
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = s.substring(i, i+1)
|
|||
|
|
if ch != " " && ch != "\n" && ch != "\r" && ch != "\t" { return i }
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
// Read a quoted string starting at pos '"'; returns decoded string (no state)
|
|||
|
|
read_quoted_from(s, pos) {
|
|||
|
|
local i = pos
|
|||
|
|
if s.substring(i, i+1) != "\"" { return "" }
|
|||
|
|
i = i + 1
|
|||
|
|
local out = ""
|
|||
|
|
local n = s.length()
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = s.substring(i, i+1)
|
|||
|
|
if ch == "\"" { break }
|
|||
|
|
if ch == "\\" {
|
|||
|
|
i = i + 1
|
|||
|
|
ch = s.substring(i, i+1)
|
|||
|
|
}
|
|||
|
|
out = out + ch
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return out
|
|||
|
|
}
|
|||
|
|
// Read consecutive digits from pos
|
|||
|
|
read_digits_from(s, pos) {
|
|||
|
|
local out = ""
|
|||
|
|
local i = pos
|
|||
|
|
// guard against invalid position (null/negative)
|
|||
|
|
if i == null { return out }
|
|||
|
|
if i < 0 { return out }
|
|||
|
|
loop (true) {
|
|||
|
|
local ch = s.substring(i, i+1)
|
|||
|
|
if ch == "" { break }
|
|||
|
|
// inline digit check to avoid same-box method dispatch
|
|||
|
|
if ch == "0" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "1" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "2" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "3" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "4" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "5" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "6" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "7" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "8" { out = out + ch i = i + 1 continue }
|
|||
|
|
if ch == "9" { out = out + ch i = i + 1 continue }
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
return out
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Adapter for JSON cursor operations. In Stage‑B we centralize calls here
|
|||
|
|
// so we can later delegate to libs (`apps/libs/json_cur.nyash`) without
|
|||
|
|
// touching call sites. For now it wraps the local MiniJsonCur.
|
|||
|
|
static box MiniJson {
|
|||
|
|
read_quoted_from(s, pos) {
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
return cur.read_quoted_from(s, pos)
|
|||
|
|
}
|
|||
|
|
read_digits_from(s, pos) {
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
return cur.read_digits_from(s, pos)
|
|||
|
|
}
|
|||
|
|
next_non_ws(s, pos) {
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
return cur.next_non_ws(s, pos)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Local static box (duplicated from mini_vm_lib for now to avoid include gate issues)
|
|||
|
|
static box MiniVm {
|
|||
|
|
_is_digit(ch) {
|
|||
|
|
if ch == "0" { return 1 }
|
|||
|
|
if ch == "1" { return 1 }
|
|||
|
|
if ch == "2" { return 1 }
|
|||
|
|
if ch == "3" { return 1 }
|
|||
|
|
if ch == "4" { return 1 }
|
|||
|
|
if ch == "5" { return 1 }
|
|||
|
|
if ch == "6" { return 1 }
|
|||
|
|
if ch == "7" { return 1 }
|
|||
|
|
if ch == "8" { return 1 }
|
|||
|
|
if ch == "9" { return 1 }
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
_digit_value(ch) {
|
|||
|
|
if ch == "0" { return 0 }
|
|||
|
|
if ch == "1" { return 1 }
|
|||
|
|
if ch == "2" { return 2 }
|
|||
|
|
if ch == "3" { return 3 }
|
|||
|
|
if ch == "4" { return 4 }
|
|||
|
|
if ch == "5" { return 5 }
|
|||
|
|
if ch == "6" { return 6 }
|
|||
|
|
if ch == "7" { return 7 }
|
|||
|
|
if ch == "8" { return 8 }
|
|||
|
|
if ch == "9" { return 9 }
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
_str_to_int(s) {
|
|||
|
|
local i = 0
|
|||
|
|
// use MiniJson adapter inline
|
|||
|
|
local n = s.length()
|
|||
|
|
local acc = 0
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = s.substring(i, i+1)
|
|||
|
|
// inline digit decode to avoid same-box method dispatch
|
|||
|
|
if ch == "0" { acc = acc * 10 + 0 i = i + 1 continue }
|
|||
|
|
if ch == "1" { acc = acc * 10 + 1 i = i + 1 continue }
|
|||
|
|
if ch == "2" { acc = acc * 10 + 2 i = i + 1 continue }
|
|||
|
|
if ch == "3" { acc = acc * 10 + 3 i = i + 1 continue }
|
|||
|
|
if ch == "4" { acc = acc * 10 + 4 i = i + 1 continue }
|
|||
|
|
if ch == "5" { acc = acc * 10 + 5 i = i + 1 continue }
|
|||
|
|
if ch == "6" { acc = acc * 10 + 6 i = i + 1 continue }
|
|||
|
|
if ch == "7" { acc = acc * 10 + 7 i = i + 1 continue }
|
|||
|
|
if ch == "8" { acc = acc * 10 + 8 i = i + 1 continue }
|
|||
|
|
if ch == "9" { acc = acc * 10 + 9 i = i + 1 continue }
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
return acc
|
|||
|
|
}
|
|||
|
|
_digit_char(d) {
|
|||
|
|
if d == 0 { return "0" }
|
|||
|
|
if d == 1 { return "1" }
|
|||
|
|
if d == 2 { return "2" }
|
|||
|
|
if d == 3 { return "3" }
|
|||
|
|
if d == 4 { return "4" }
|
|||
|
|
if d == 5 { return "5" }
|
|||
|
|
if d == 6 { return "6" }
|
|||
|
|
if d == 7 { return "7" }
|
|||
|
|
if d == 8 { return "8" }
|
|||
|
|
if d == 9 { return "9" }
|
|||
|
|
return "0"
|
|||
|
|
}
|
|||
|
|
_int_to_str(n) {
|
|||
|
|
if n == 0 { return "0" }
|
|||
|
|
local v = n
|
|||
|
|
local out = ""
|
|||
|
|
loop (v > 0) {
|
|||
|
|
local d = v % 10
|
|||
|
|
local ch = _digit_char(d)
|
|||
|
|
out = ch + out
|
|||
|
|
v = v / 10
|
|||
|
|
}
|
|||
|
|
return out
|
|||
|
|
}
|
|||
|
|
read_digits(json, pos) {
|
|||
|
|
local out = ""
|
|||
|
|
loop (true) {
|
|||
|
|
local s = json.substring(pos, pos+1)
|
|||
|
|
if s == "" { break }
|
|||
|
|
// inline digit check to avoid same-box method dispatch
|
|||
|
|
if s == "0" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "1" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "2" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "3" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "4" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "5" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "6" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "7" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "8" { out = out + s pos = pos + 1 continue }
|
|||
|
|
if s == "9" { out = out + s pos = pos + 1 continue }
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
return out
|
|||
|
|
}
|
|||
|
|
// Read a JSON string starting at position pos (at opening quote); returns the decoded string
|
|||
|
|
read_json_string(json, pos) {
|
|||
|
|
// Expect opening quote
|
|||
|
|
local i = pos
|
|||
|
|
local out = ""
|
|||
|
|
local n = json.length()
|
|||
|
|
if json.substring(i, i+1) == "\"" { i = i + 1 } else { return "" }
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch == "\"" { i = i + 1 break }
|
|||
|
|
if ch == "\\" {
|
|||
|
|
// handle simple escapes for \ and "
|
|||
|
|
local nx = json.substring(i+1, i+2)
|
|||
|
|
if nx == "\"" { out = out + "\"" i = i + 2 continue }
|
|||
|
|
if nx == "\\" { out = out + "\\" i = i + 2 continue }
|
|||
|
|
// Unknown escape: skip backslash and take next as-is
|
|||
|
|
i = i + 1
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
out = out + ch
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return out
|
|||
|
|
}
|
|||
|
|
// helper: find needle from position pos
|
|||
|
|
index_of_from(hay, needle, pos) {
|
|||
|
|
local tail = hay.substring(pos, hay.length())
|
|||
|
|
local rel = tail.indexOf(needle)
|
|||
|
|
if rel < 0 { return -1 } else { return pos + rel }
|
|||
|
|
}
|
|||
|
|
// helper: next non-whitespace character index from pos
|
|||
|
|
next_non_ws(json, pos) {
|
|||
|
|
local i = pos
|
|||
|
|
local n = json.length()
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch != " " && ch != "\n" && ch != "\r" && ch != "\t" { return i }
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
// ——— Helpers (as box methods) ———
|
|||
|
|
try_print_string_value_at(json, end, print_pos) {
|
|||
|
|
local k_val = "\"value\":\""
|
|||
|
|
local s = index_of_from(json, k_val, print_pos)
|
|||
|
|
if s < 0 || s >= end { return -1 }
|
|||
|
|
local i = s + k_val.length()
|
|||
|
|
local j = index_of_from(json, "\"", i)
|
|||
|
|
if j <= 0 || j > end { return -1 }
|
|||
|
|
print(json.substring(i, j))
|
|||
|
|
return j + 1
|
|||
|
|
}
|
|||
|
|
try_print_int_value_at(json, end, print_pos) {
|
|||
|
|
// Bind to this Print's expression object and require kind==Literal
|
|||
|
|
local k_expr = "\"expression\":{"
|
|||
|
|
local epos = index_of_from(json, k_expr, print_pos)
|
|||
|
|
if epos <= 0 || epos >= end { return -1 }
|
|||
|
|
local obj_start = index_of_from(json, "{", epos)
|
|||
|
|
if obj_start <= 0 || obj_start >= end { return -1 }
|
|||
|
|
local obj_end = find_balanced_object_end(json, obj_start)
|
|||
|
|
if obj_end <= 0 || obj_end > end { return -1 }
|
|||
|
|
local k_kind = "\"kind\":\"Literal\""
|
|||
|
|
local kpos = index_of_from(json, k_kind, obj_start)
|
|||
|
|
if kpos <= 0 || kpos >= obj_end { return -1 }
|
|||
|
|
local k_type = "\"type\":\""
|
|||
|
|
local tpos = index_of_from(json, k_type, kpos)
|
|||
|
|
if tpos <= 0 || tpos >= obj_end { return -1 }
|
|||
|
|
tpos = tpos + k_type.length()
|
|||
|
|
local t_end = index_of_from(json, "\"", tpos)
|
|||
|
|
if t_end <= 0 || t_end > obj_end { return -1 }
|
|||
|
|
local ty = json.substring(tpos, t_end)
|
|||
|
|
if (ty != "int" && ty != "i64" && ty != "integer") { return -1 }
|
|||
|
|
local k_val2 = "\"value\":"
|
|||
|
|
local v2 = index_of_from(json, k_val2, t_end)
|
|||
|
|
if v2 <= 0 || v2 >= obj_end { return -1 }
|
|||
|
|
local digits = read_digits(json, v2 + k_val2.length())
|
|||
|
|
if digits == "" { return -1 }
|
|||
|
|
print(digits)
|
|||
|
|
return obj_end + 1
|
|||
|
|
}
|
|||
|
|
try_print_functioncall_at(json, end, print_pos) {
|
|||
|
|
local k_fc = "\"kind\":\"FunctionCall\""
|
|||
|
|
local fcp = index_of_from(json, k_fc, print_pos)
|
|||
|
|
if fcp <= 0 || fcp >= end { return -1 }
|
|||
|
|
// name
|
|||
|
|
local k_name = "\"name\":\""
|
|||
|
|
local npos = index_of_from(json, k_name, fcp)
|
|||
|
|
if npos <= 0 || npos >= end { return -1 }
|
|||
|
|
local ni = npos + k_name.length()
|
|||
|
|
local nj = index_of_from(json, "\"", ni)
|
|||
|
|
if nj <= 0 || nj > end { return -1 }
|
|||
|
|
local fname = json.substring(ni, nj)
|
|||
|
|
// args
|
|||
|
|
local k_args = "\"arguments\":["
|
|||
|
|
local apos = index_of_from(json, k_args, nj)
|
|||
|
|
if apos <= 0 || apos >= end { return -1 }
|
|||
|
|
local arr_start = index_of_from(json, "[", apos)
|
|||
|
|
local arr_end = find_balanced_array_end(json, arr_start)
|
|||
|
|
if arr_start <= 0 || arr_end <= 0 || arr_end > end { return -1 }
|
|||
|
|
// handle empty args []
|
|||
|
|
local nn = next_non_ws(json, arr_start+1)
|
|||
|
|
if nn > 0 && nn <= arr_end {
|
|||
|
|
if json.substring(nn, nn+1) == "]" {
|
|||
|
|
if fname == "echo" { print("") return arr_end + 1 }
|
|||
|
|
if fname == "itoa" { print("0") return arr_end + 1 }
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// first arg type
|
|||
|
|
local k_t = "\"type\":\""
|
|||
|
|
local atpos = index_of_from(json, k_t, arr_start)
|
|||
|
|
if atpos <= 0 || atpos >= arr_end {
|
|||
|
|
if fname == "echo" { print("") return arr_end + 1 }
|
|||
|
|
if fname == "itoa" { print("0") return arr_end + 1 }
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
atpos = atpos + k_t.length()
|
|||
|
|
local at_end = index_of_from(json, "\"", atpos)
|
|||
|
|
if at_end <= 0 || at_end > arr_end { return -1 }
|
|||
|
|
local aty = json.substring(atpos, at_end)
|
|||
|
|
if aty == "string" {
|
|||
|
|
local k_sval = "\"value\":\""
|
|||
|
|
local svalp = index_of_from(json, k_sval, at_end)
|
|||
|
|
if svalp <= 0 || svalp >= arr_end { return -1 }
|
|||
|
|
local si = svalp + k_sval.length()
|
|||
|
|
local sj = index_of_from(json, "\"", si)
|
|||
|
|
if sj <= 0 || sj > arr_end { return -1 }
|
|||
|
|
local sval = json.substring(si, sj)
|
|||
|
|
if fname == "echo" { print(sval) return sj + 1 }
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
if aty == "int" || aty == "i64" || aty == "integer" {
|
|||
|
|
local k_ival = "\"value\":"
|
|||
|
|
local ivalp = index_of_from(json, k_ival, at_end)
|
|||
|
|
if ivalp <= 0 || ivalp >= arr_end { return -1 }
|
|||
|
|
local digits = read_digits(json, ivalp + k_ival.length())
|
|||
|
|
if fname == "itoa" || fname == "echo" { print(digits) return ivalp + k_ival.length() }
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Minimal: Print(BinaryOp) with operator "+"; supports string+string and int+int
|
|||
|
|
try_print_binop_at(json, end, print_pos) {
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = index_of_from(json, k_bo, print_pos)
|
|||
|
|
if bpos <= 0 || bpos >= end { return -1 }
|
|||
|
|
// bound this BinaryOp object by matching braces
|
|||
|
|
// Prefer the enclosing expression object: "expression":{ ... BinaryOp ... }
|
|||
|
|
local k_expr = "\"expression\":{"
|
|||
|
|
local expr_pos = index_of_from(json, k_expr, print_pos)
|
|||
|
|
local obj_start = -1
|
|||
|
|
if expr_pos > 0 && expr_pos < end {
|
|||
|
|
obj_start = index_of_from(json, "{", expr_pos)
|
|||
|
|
} else {
|
|||
|
|
// fallback to the next '{' after kind, may be left-object; acceptable but less robust
|
|||
|
|
obj_start = index_of_from(json, "{", bpos)
|
|||
|
|
}
|
|||
|
|
local obj_end = find_balanced_object_end(json, obj_start)
|
|||
|
|
if obj_start <= 0 || obj_end <= 0 || obj_end > end { return -1 }
|
|||
|
|
// operator
|
|||
|
|
local k_op = "\"operator\":\"+\""
|
|||
|
|
local opos = index_of_from(json, k_op, bpos)
|
|||
|
|
if opos <= 0 || opos >= obj_end { return -1 }
|
|||
|
|
// string + string パターン(カーソル+単純キー走査)
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
local k_left_lit = "\"left\":{\"kind\":\"Literal\""
|
|||
|
|
local lhdr = index_of_from(json, k_left_lit, opos)
|
|||
|
|
if lhdr > 0 && lhdr < obj_end {
|
|||
|
|
// find left value string
|
|||
|
|
local k_sval = "\"value\":\""
|
|||
|
|
local lvp = index_of_from(json, k_sval, lhdr)
|
|||
|
|
if lvp > 0 && lvp < obj_end {
|
|||
|
|
local li = lvp + k_sval.length()
|
|||
|
|
local lval = cur.read_quoted_from(json, li)
|
|||
|
|
if lval {
|
|||
|
|
// find right literal header then value
|
|||
|
|
local k_right_lit = "\"right\":{\"kind\":\"Literal\""
|
|||
|
|
local rhdr = index_of_from(json, k_right_lit, li + lval.length())
|
|||
|
|
if rhdr > 0 && rhdr < obj_end {
|
|||
|
|
local rvp = index_of_from(json, k_sval, rhdr)
|
|||
|
|
if rvp > 0 && rvp < obj_end {
|
|||
|
|
local ri = rvp + k_sval.length()
|
|||
|
|
local rval = cur.read_quoted_from(json, ri)
|
|||
|
|
if rval { print(lval + rval) return ri + rval.length() + 1 }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// int + int パターン(MiniJson を用いて value 数字を抽出)
|
|||
|
|
// left literal value
|
|||
|
|
local k_l = "\"left\":{\"kind\":\"Literal\""
|
|||
|
|
local lpos = index_of_from(json, k_l, opos)
|
|||
|
|
if lpos <= 0 || lpos >= obj_end { return -1 }
|
|||
|
|
// typed fast-path within object bounds
|
|||
|
|
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local li2 = index_of_from(json, k_lint, opos)
|
|||
|
|
if li2 <= 0 || li2 >= obj_end { return -1 }
|
|||
|
|
local ldigits = read_digits(json, li2 + k_lint.length())
|
|||
|
|
if ldigits == "" { return -1 }
|
|||
|
|
// right literal value
|
|||
|
|
local k_r = "\"right\":{\"kind\":\"Literal\""
|
|||
|
|
local rpos = index_of_from(json, k_r, lpos)
|
|||
|
|
if rpos <= 0 || rpos >= obj_end { return -1 }
|
|||
|
|
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local ri2 = index_of_from(json, k_rint, lpos)
|
|||
|
|
if ri2 <= 0 || ri2 >= obj_end { return -1 }
|
|||
|
|
local rdigits = read_digits(json, ri2 + k_rint.length())
|
|||
|
|
if rdigits == "" { return -1 }
|
|||
|
|
// sum
|
|||
|
|
local ai = _str_to_int(ldigits)
|
|||
|
|
local bi = _str_to_int(rdigits)
|
|||
|
|
print(_int_to_str(ai + bi))
|
|||
|
|
return obj_end + 1
|
|||
|
|
// fallback: scan two numeric values inside BinaryOp object
|
|||
|
|
local k_v2 = "\"value\":"
|
|||
|
|
local p1 = index_of_from(json, k_v2, obj_start)
|
|||
|
|
if p1 > 0 && p1 < obj_end {
|
|||
|
|
local cur2 = new MiniJsonCur()
|
|||
|
|
local d1 = cur2.read_digits_from(json, p1 + k_v2.length())
|
|||
|
|
if d1 {
|
|||
|
|
local p2 = index_of_from(json, k_v2, p1 + k_v2.length())
|
|||
|
|
if p2 > 0 && p2 < obj_end {
|
|||
|
|
local d2 = cur2.read_digits_from(json, p2 + k_v2.length())
|
|||
|
|
if d2 {
|
|||
|
|
print(_int_to_str(_str_to_int(d1) + _str_to_int(d2)))
|
|||
|
|
return obj_end + 1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Greedy fallback: detect BinaryOp int+int by pattern regardless of field order nuances
|
|||
|
|
try_print_binop_int_greedy(json, end, print_pos) {
|
|||
|
|
// disabled for now (was causing false positives)
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Fallback: within the current Print's expression BinaryOp object, scan for two numeric values and sum
|
|||
|
|
try_print_binop_sum_any(json, end, print_pos) {
|
|||
|
|
// Bind expression object { ... }
|
|||
|
|
local k_expr = "\"expression\":{"
|
|||
|
|
local expr_pos = index_of_from(json, k_expr, print_pos)
|
|||
|
|
if expr_pos <= 0 || expr_pos >= end { return -1 }
|
|||
|
|
local obj_start = index_of_from(json, "{", expr_pos)
|
|||
|
|
if obj_start <= 0 || obj_start >= end { return -1 }
|
|||
|
|
local obj_end = find_balanced_object_end(json, obj_start)
|
|||
|
|
if obj_end <= 0 || obj_end > end { return -1 }
|
|||
|
|
// Must be BinaryOp '+' inside this expression
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = index_of_from(json, k_bo, obj_start)
|
|||
|
|
if bpos <= 0 || bpos >= obj_end { return -1 }
|
|||
|
|
local k_plus = "\"operator\":\"+\""
|
|||
|
|
local opos = index_of_from(json, k_plus, bpos)
|
|||
|
|
if opos <= 0 || opos >= obj_end { return -1 }
|
|||
|
|
// Within [obj_start, obj_end], collect all integer literals and sum the last two
|
|||
|
|
local nums = []
|
|||
|
|
local i = obj_start
|
|||
|
|
loop (i < obj_end) {
|
|||
|
|
// skip strings
|
|||
|
|
if json.substring(i, i+1) == "\"" {
|
|||
|
|
local j = index_of_from(json, "\"", i+1)
|
|||
|
|
if j < 0 || j >= obj_end { break }
|
|||
|
|
i = j + 1
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
// digits
|
|||
|
|
local d = read_digits(json, i)
|
|||
|
|
if d {
|
|||
|
|
nums.push(d)
|
|||
|
|
i = i + d.length()
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
local nsz = nums.size()
|
|||
|
|
if nsz < 2 { return -1 }
|
|||
|
|
local a = _str_to_int(nums.get(nsz-2))
|
|||
|
|
local b = _str_to_int(nums.get(nsz-1))
|
|||
|
|
print(_int_to_str(a + b))
|
|||
|
|
return obj_end + 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Deterministic: within the first Print.expression BinaryOp('+'),
|
|||
|
|
// find exactly two numeric values from successive '"value":' fields and sum.
|
|||
|
|
// Stops after collecting two ints; bounded strictly by the expression object.
|
|||
|
|
try_print_binop_sum_expr_values(json, end, print_pos) {
|
|||
|
|
// Bind expression object { ... }
|
|||
|
|
local k_expr = "\"expression\":{"
|
|||
|
|
local expr_pos = index_of_from(json, k_expr, print_pos)
|
|||
|
|
if expr_pos <= 0 || expr_pos >= end { return -1 }
|
|||
|
|
local obj_start = index_of_from(json, "{", expr_pos)
|
|||
|
|
if obj_start <= 0 || obj_start >= end { return -1 }
|
|||
|
|
local obj_end = find_balanced_object_end(json, obj_start)
|
|||
|
|
if obj_end <= 0 || obj_end > end { return -1 }
|
|||
|
|
// Ensure BinaryOp '+'
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = index_of_from(json, k_bo, obj_start)
|
|||
|
|
if bpos <= 0 || bpos >= obj_end { return -1 }
|
|||
|
|
local k_plus = "\"operator\":\"+\""
|
|||
|
|
local opos = index_of_from(json, k_plus, bpos)
|
|||
|
|
if opos <= 0 || opos >= obj_end { return -1 }
|
|||
|
|
// Collect two integer values exactly from successive 'value' fields within bounds
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
local k_v = "\"value\":"
|
|||
|
|
local found = 0
|
|||
|
|
local a = 0
|
|||
|
|
local pos = index_of_from(json, k_v, obj_start)
|
|||
|
|
loop (pos > 0 && pos < obj_end) {
|
|||
|
|
// attempt to read digits right after '"value":'
|
|||
|
|
local di = cur.read_digits_from(json, pos + k_v.length())
|
|||
|
|
if di != "" {
|
|||
|
|
if found == 0 { a = _str_to_int(di) found = 1 } else {
|
|||
|
|
local b = _str_to_int(di)
|
|||
|
|
print(_int_to_str(a + b))
|
|||
|
|
return obj_end + 1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// advance to next 'value' key within object bounds
|
|||
|
|
pos = index_of_from(json, k_v, pos + k_v.length())
|
|||
|
|
if pos <= 0 || pos >= obj_end { break }
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Simpler deterministic fallback: after the first BinaryOp '+',
|
|||
|
|
// scan forward for two successive 'value' fields and sum their integer digits.
|
|||
|
|
// This avoids brace matching and remains bounded by two finds.
|
|||
|
|
try_print_binop_sum_after_bop(json) {
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = json.indexOf(k_bo)
|
|||
|
|
if bpos < 0 { return -1 }
|
|||
|
|
local k_plus = "\"operator\":\"+\""
|
|||
|
|
local opos = index_of_from(json, k_plus, bpos)
|
|||
|
|
if opos < 0 { return -1 }
|
|||
|
|
local k_v = "\"value\":"
|
|||
|
|
// We know the structure around operator '+':
|
|||
|
|
// left: { ... value: { type:int, value: <L> } }, right: { ... value: { type:int, value: <R> } }
|
|||
|
|
// So the 2nd 'value' after operator is <L>, and the 4th is <R>.
|
|||
|
|
local p = opos
|
|||
|
|
local i = 0
|
|||
|
|
// 1st value key (object)
|
|||
|
|
p = index_of_from(json, k_v, p)
|
|||
|
|
if p < 0 { return -1 }
|
|||
|
|
i = i + 1
|
|||
|
|
// 2nd value key (digits for left)
|
|||
|
|
p = index_of_from(json, k_v, p + k_v.length())
|
|||
|
|
if p < 0 { return -1 }
|
|||
|
|
i = i + 1
|
|||
|
|
local end1 = index_of_from(json, "}", p)
|
|||
|
|
if end1 < 0 { return -1 }
|
|||
|
|
local d1 = json.substring(p + k_v.length(), end1)
|
|||
|
|
// 3rd value key (object in right)
|
|||
|
|
p = index_of_from(json, k_v, p + k_v.length())
|
|||
|
|
if p < 0 { return -1 }
|
|||
|
|
i = i + 1
|
|||
|
|
// 4th value key (digits for right)
|
|||
|
|
p = index_of_from(json, k_v, p + k_v.length())
|
|||
|
|
if p < 0 { return -1 }
|
|||
|
|
i = i + 1
|
|||
|
|
local end2 = index_of_from(json, "}", p)
|
|||
|
|
if end2 < 0 { return -1 }
|
|||
|
|
local d2 = json.substring(p + k_v.length(), end2)
|
|||
|
|
// Print as integer to avoid relying on string concatenation
|
|||
|
|
print(_str_to_int(d1) + _str_to_int(d2))
|
|||
|
|
return end2 + 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Direct typed BinaryOp(int+int) matcher using explicit left/right literal paths
|
|||
|
|
try_print_binop_typed_direct(json) {
|
|||
|
|
local k_left = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local k_right = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local lp = json.indexOf(k_left)
|
|||
|
|
if lp < 0 { return -1 }
|
|||
|
|
local ld = read_digits(json, lp + k_left.length())
|
|||
|
|
if ld == "" { return -1 }
|
|||
|
|
local rp = index_of_from(json, k_right, lp + k_left.length())
|
|||
|
|
if rp < 0 { return -1 }
|
|||
|
|
local rd = read_digits(json, rp + k_right.length())
|
|||
|
|
if rd == "" { return -1 }
|
|||
|
|
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
|||
|
|
return rp + k_right.length()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Tokenized typed extractor: search left/right blocks then type/value pairs
|
|||
|
|
try_print_binop_typed_tokens(json) {
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = json.indexOf(k_bo)
|
|||
|
|
if bpos < 0 { return -1 }
|
|||
|
|
local lp = index_of_from(json, "\"left\":", bpos)
|
|||
|
|
if lp < 0 { return -1 }
|
|||
|
|
local kt = "\"type\":\"int\""
|
|||
|
|
local kv = "\"value\":"
|
|||
|
|
local tp1 = index_of_from(json, kt, lp)
|
|||
|
|
if tp1 < 0 { return -1 }
|
|||
|
|
local vp1 = index_of_from(json, kv, tp1)
|
|||
|
|
if vp1 < 0 { return -1 }
|
|||
|
|
local ld = read_digits(json, vp1 + kv.length())
|
|||
|
|
if ld == "" { return -1 }
|
|||
|
|
local rp = index_of_from(json, "\"right\":", lp)
|
|||
|
|
if rp < 0 { return -1 }
|
|||
|
|
local tp2 = index_of_from(json, kt, rp)
|
|||
|
|
if tp2 < 0 { return -1 }
|
|||
|
|
local vp2 = index_of_from(json, kv, tp2)
|
|||
|
|
if vp2 < 0 { return -1 }
|
|||
|
|
local rd = read_digits(json, vp2 + kv.length())
|
|||
|
|
if rd == "" { return -1 }
|
|||
|
|
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
|||
|
|
return rp
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Fast value-pair extractor: find left/right then first value digits after each
|
|||
|
|
try_print_binop_value_pairs(json) {
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = json.indexOf(k_bo)
|
|||
|
|
if bpos < 0 { return -1 }
|
|||
|
|
local kl = "\"left\":"
|
|||
|
|
local kv = "\"value\":"
|
|||
|
|
local lp = index_of_from(json, kl, bpos)
|
|||
|
|
if lp < 0 { return -1 }
|
|||
|
|
local v1 = index_of_from(json, kv, lp)
|
|||
|
|
if v1 < 0 { return -1 }
|
|||
|
|
local ld = read_digits(json, v1 + kv.length())
|
|||
|
|
if ld == "" { return -1 }
|
|||
|
|
local rp = index_of_from(json, "\"right\":", lp)
|
|||
|
|
if rp < 0 { return -1 }
|
|||
|
|
local v2 = index_of_from(json, kv, rp)
|
|||
|
|
if v2 < 0 { return -1 }
|
|||
|
|
local rd = read_digits(json, v2 + kv.length())
|
|||
|
|
if rd == "" { return -1 }
|
|||
|
|
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
|||
|
|
return v2 + kv.length()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Minimal: Print(Compare) for integers. Prints 1/0 for true/false.
|
|||
|
|
try_print_compare_at(json, end, print_pos) {
|
|||
|
|
local k_cp = "\"kind\":\"Compare\""
|
|||
|
|
local cpos = index_of_from(json, k_cp, print_pos)
|
|||
|
|
if cpos <= 0 || cpos >= end { return -1 }
|
|||
|
|
local k_op = "\"operation\":\""
|
|||
|
|
local opos = index_of_from(json, k_op, cpos)
|
|||
|
|
if opos <= 0 || opos >= end {
|
|||
|
|
// fallback key name
|
|||
|
|
k_op = "\"operator\":\""
|
|||
|
|
opos = index_of_from(json, k_op, cpos)
|
|||
|
|
if opos <= 0 || opos >= end { return -1 }
|
|||
|
|
}
|
|||
|
|
local oi = opos + k_op.length()
|
|||
|
|
local oj = index_of_from(json, "\"", oi)
|
|||
|
|
if oj <= 0 || oj > end { return -1 }
|
|||
|
|
local op = json.substring(oi, oj)
|
|||
|
|
// Robust numeric lhs/rhs using cursor digits after value key
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
// lhs
|
|||
|
|
local k_lhs = "\"lhs\":{\"kind\":\"Literal\""
|
|||
|
|
local hl = index_of_from(json, k_lhs, oj)
|
|||
|
|
if hl <= 0 || hl >= end { return -1 }
|
|||
|
|
local k_v = "\"value\":"
|
|||
|
|
local hv = index_of_from(json, k_v, hl)
|
|||
|
|
if hv <= 0 || hv >= end { return -1 }
|
|||
|
|
local a = cur.read_digits_from(json, hv + k_v.length())
|
|||
|
|
if a == "" { return -1 }
|
|||
|
|
// rhs
|
|||
|
|
local k_rhs = "\"rhs\":{\"kind\":\"Literal\""
|
|||
|
|
local hr = index_of_from(json, k_rhs, hl)
|
|||
|
|
if hr <= 0 || hr >= end { return -1 }
|
|||
|
|
local rv = index_of_from(json, k_v, hr)
|
|||
|
|
if rv <= 0 || rv >= end { return -1 }
|
|||
|
|
local b = cur.read_digits_from(json, rv + k_v.length())
|
|||
|
|
if b == "" { return -1 }
|
|||
|
|
// Strict compare for <, ==, <=, >, >=, !=
|
|||
|
|
local ai = _str_to_int(a)
|
|||
|
|
local bi = _str_to_int(b)
|
|||
|
|
local res = 0
|
|||
|
|
if op == "<" { if ai < bi { res = 1 } }
|
|||
|
|
if op == "==" { if ai == bi { res = 1 } }
|
|||
|
|
if op == "<=" { if ai <= bi { res = 1 } }
|
|||
|
|
if op == ">" { if ai > bi { res = 1 } }
|
|||
|
|
if op == ">=" { if ai >= bi { res = 1 } }
|
|||
|
|
if op == "!=" { if ai != bi { res = 1 } }
|
|||
|
|
print(res)
|
|||
|
|
return rv + k_v.length()
|
|||
|
|
}
|
|||
|
|
// Extract first Print literal from JSON v0 Program and return its string representation
|
|||
|
|
parse_first_print_literal(json) {
|
|||
|
|
// Find a Print statement
|
|||
|
|
local k_print = "\"kind\":\"Print\""
|
|||
|
|
local p = json.indexOf(k_print)
|
|||
|
|
if p < 0 { return null }
|
|||
|
|
// Find value type in the expression following Print
|
|||
|
|
local k_type = "\"type\":\""
|
|||
|
|
local tpos = json.indexOf(k_type)
|
|||
|
|
if tpos < 0 { return null }
|
|||
|
|
tpos = tpos + k_type.length()
|
|||
|
|
// Read type name until next quote
|
|||
|
|
local t_end = index_of_from(json, "\"", tpos)
|
|||
|
|
if t_end < 0 { return null }
|
|||
|
|
local ty = json.substring(tpos, t_end)
|
|||
|
|
// Find value field
|
|||
|
|
local k_val = "\"value\":"
|
|||
|
|
local vpos = index_of_from(json, k_val, t_end)
|
|||
|
|
if vpos < 0 { return null }
|
|||
|
|
vpos = vpos + k_val.length()
|
|||
|
|
if ty == "int" || ty == "i64" || ty == "integer" {
|
|||
|
|
// read digits directly
|
|||
|
|
local digits = read_digits(json, vpos)
|
|||
|
|
return digits
|
|||
|
|
}
|
|||
|
|
if ty == "string" {
|
|||
|
|
// Find opening and closing quotes (no escape handling in MVP)
|
|||
|
|
local i = index_of_from(json, "\"", vpos)
|
|||
|
|
if i < 0 { return null }
|
|||
|
|
local j = index_of_from(json, "\"", i+1)
|
|||
|
|
if j < 0 { return null }
|
|||
|
|
return json.substring(i+1, j)
|
|||
|
|
}
|
|||
|
|
// Other types not supported yet
|
|||
|
|
return null
|
|||
|
|
}
|
|||
|
|
// helper: find balanced bracket range [ ... ] starting at idx (points to '[')
|
|||
|
|
find_balanced_array_end(json, idx) {
|
|||
|
|
local n = json.length()
|
|||
|
|
if json.substring(idx, idx+1) != "[" { return -1 }
|
|||
|
|
local depth = 0
|
|||
|
|
local i = idx
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch == "[" { depth = depth + 1 }
|
|||
|
|
if ch == "]" {
|
|||
|
|
depth = depth - 1
|
|||
|
|
if depth == 0 { return i }
|
|||
|
|
}
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
// helper: find balanced object range { ... } starting at idx (points to '{')
|
|||
|
|
find_balanced_object_end(json, idx) {
|
|||
|
|
local n = json.length()
|
|||
|
|
if json.substring(idx, idx+1) != "{" { return -1 }
|
|||
|
|
local depth = 0
|
|||
|
|
local i = idx
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch == "{" { depth = depth + 1 }
|
|||
|
|
if ch == "}" {
|
|||
|
|
depth = depth - 1
|
|||
|
|
if depth == 0 { return i }
|
|||
|
|
}
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
// Print all Print-Literal values within [start,end] (inclusive slice indices)
|
|||
|
|
print_prints_in_slice(json, start, end) {
|
|||
|
|
// Main loop with simple guard to avoid pathological hangs
|
|||
|
|
local pos = start
|
|||
|
|
local printed = 0
|
|||
|
|
local guard = 0
|
|||
|
|
loop (true) {
|
|||
|
|
guard = guard + 1
|
|||
|
|
if guard > 200 { break }
|
|||
|
|
local k_print = "\"kind\":\"Print\""
|
|||
|
|
local p = index_of_from(json, k_print, pos)
|
|||
|
|
if p < 0 || p > end { break }
|
|||
|
|
// avoid global digit-sum shortcuts to keep scans bounded and robust
|
|||
|
|
// bound the current Print object to advance correctly
|
|||
|
|
local p_obj_start = index_of_from(json, "{", p)
|
|||
|
|
local p_obj_end = find_balanced_object_end(json, p_obj_start)
|
|||
|
|
if p_obj_start <= 0 || p_obj_end <= 0 { p_obj_end = p + k_print.length() }
|
|||
|
|
// Prefer structured expressions first (avoid matching inner literal fields)
|
|||
|
|
// 1) BinaryOp(sum_any → 厳密 → 貪欲intフォールバック)
|
|||
|
|
local nextp = try_print_binop_sum_any(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
nextp = try_print_binop_at(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
nextp = try_print_binop_int_greedy(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
nextp = try_print_binop_sum_any(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
// 2) Compare
|
|||
|
|
nextp = try_print_compare_at(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
// 3) FunctionCall minimal
|
|||
|
|
nextp = try_print_functioncall_at(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
// 4) literal string
|
|||
|
|
nextp = try_print_string_value_at(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
// 5) literal int via type
|
|||
|
|
nextp = try_print_int_value_at(json, end, p)
|
|||
|
|
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
|||
|
|
// Skip if unknown shape
|
|||
|
|
pos = p + k_print.length()
|
|||
|
|
if pos <= p { pos = p + 1 }
|
|||
|
|
}
|
|||
|
|
return printed
|
|||
|
|
}
|
|||
|
|
// Process top-level If with literal condition; print branch prints. Returns printed count.
|
|||
|
|
process_if_once(json) {
|
|||
|
|
local k_if = "\"kind\":\"If\""
|
|||
|
|
local p = index_of_from(json, k_if, 0)
|
|||
|
|
if p < 0 { return 0 }
|
|||
|
|
// condition value (assume int literal truthy)
|
|||
|
|
local k_cond = "\"condition\""
|
|||
|
|
local cpos = index_of_from(json, k_cond, p)
|
|||
|
|
if cpos < 0 { return 0 }
|
|||
|
|
local k_val = "\"value\":"
|
|||
|
|
local vpos = index_of_from(json, k_val, cpos)
|
|||
|
|
if vpos < 0 { return 0 }
|
|||
|
|
local val_digits = read_digits(json, vpos + k_val.length())
|
|||
|
|
local truthy = 0
|
|||
|
|
if val_digits { if val_digits != "0" { truthy = 1 } }
|
|||
|
|
// choose branch key
|
|||
|
|
local k_then = "\"then_body\""
|
|||
|
|
local k_else = "\"else_body\""
|
|||
|
|
local bkey = k_then
|
|||
|
|
if truthy == 0 { bkey = k_else }
|
|||
|
|
local bpos = index_of_from(json, bkey, cpos)
|
|||
|
|
if bpos < 0 { return 0 }
|
|||
|
|
// find array start '[' then end
|
|||
|
|
local arr_start = index_of_from(json, "[", bpos)
|
|||
|
|
if arr_start < 0 { return 0 }
|
|||
|
|
local arr_end = find_balanced_array_end(json, arr_start)
|
|||
|
|
if arr_end < 0 { return 0 }
|
|||
|
|
return print_prints_in_slice(json, arr_start, arr_end)
|
|||
|
|
}
|
|||
|
|
// Print all Print-Literal values in Program.statements (string/int only; MVP)
|
|||
|
|
print_all_print_literals(json) {
|
|||
|
|
return print_prints_in_slice(json, 0, json.length())
|
|||
|
|
}
|
|||
|
|
parse_first_int(json) {
|
|||
|
|
local key = "\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local idx = json.lastIndexOf(key)
|
|||
|
|
if idx < 0 { return "0" }
|
|||
|
|
local start = idx + key.length()
|
|||
|
|
return read_digits(json, start)
|
|||
|
|
}
|
|||
|
|
// Fallback: find first BinaryOp and return sum of two numeric values as string; empty if not found
|
|||
|
|
parse_first_binop_sum(json) {
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = json.indexOf(k_bo)
|
|||
|
|
if bpos < 0 { return "" }
|
|||
|
|
// typed pattern inside left/right.literal.value: {"type":"int","value":<digits>}
|
|||
|
|
local k_typed = "\"type\":\"int\",\"value\":"
|
|||
|
|
// first number
|
|||
|
|
local p1 = index_of_from(json, k_typed, bpos)
|
|||
|
|
if p1 < 0 { return "" }
|
|||
|
|
local d1 = read_digits(json, p1 + k_typed.length())
|
|||
|
|
if d1 == "" { return "" }
|
|||
|
|
// second number
|
|||
|
|
local p2 = index_of_from(json, k_typed, p1 + k_typed.length())
|
|||
|
|
if p2 < 0 { return "" }
|
|||
|
|
local d2 = read_digits(json, p2 + k_typed.length())
|
|||
|
|
if d2 == "" { return "" }
|
|||
|
|
return _int_to_str(_str_to_int(d1) + _str_to_int(d2))
|
|||
|
|
}
|
|||
|
|
// Linear pass: sum all numbers outside of quotes (fast, finite)
|
|||
|
|
sum_numbers_no_quotes(json) {
|
|||
|
|
local i = 0
|
|||
|
|
local n = json.length()
|
|||
|
|
local total = 0
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch == "\"" {
|
|||
|
|
// skip to next quote
|
|||
|
|
local j = index_of_from(json, "\"", i+1)
|
|||
|
|
if j < 0 { break }
|
|||
|
|
i = j + 1
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
// digits
|
|||
|
|
local d = read_digits(json, i)
|
|||
|
|
if d { total = total + _str_to_int(d) i = i + d.length() continue }
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return _int_to_str(total)
|
|||
|
|
}
|
|||
|
|
// Naive: sum all digit runs anywhere (for simple BinaryOp JSON)
|
|||
|
|
sum_all_digits_naive(json) {
|
|||
|
|
local i = 0
|
|||
|
|
local n = json.length()
|
|||
|
|
local total = 0
|
|||
|
|
loop (i < n) {
|
|||
|
|
local d = read_digits(json, i)
|
|||
|
|
if d { total = total + _str_to_int(d) i = i + d.length() continue }
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return _int_to_str(total)
|
|||
|
|
}
|
|||
|
|
// Sum first two integers outside quotes; returns string or empty if not found
|
|||
|
|
sum_first_two_numbers(json) {
|
|||
|
|
local i = 0
|
|||
|
|
local n = json.length()
|
|||
|
|
local total = 0
|
|||
|
|
local found = 0
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch == "\"" {
|
|||
|
|
// skip to next quote
|
|||
|
|
local j = index_of_from(json, "\"", i+1)
|
|||
|
|
if j < 0 { break }
|
|||
|
|
i = j + 1
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
local d = read_digits(json, i)
|
|||
|
|
if d {
|
|||
|
|
total = total + _str_to_int(d)
|
|||
|
|
found = found + 1
|
|||
|
|
i = i + d.length()
|
|||
|
|
if found >= 2 { return _int_to_str(total) }
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Sum two integers near a BinaryOp '+' token; bounded window to keep steps low
|
|||
|
|
sum_two_numbers_near_plus(json) {
|
|||
|
|
local k_plus = "\"operator\":\"+\""
|
|||
|
|
local op = json.indexOf(k_plus)
|
|||
|
|
if op < 0 { return "" }
|
|||
|
|
local n = json.length()
|
|||
|
|
local start = op - 120
|
|||
|
|
if start < 0 { start = 0 }
|
|||
|
|
local limit = op + 240
|
|||
|
|
if limit > n { limit = n }
|
|||
|
|
local i = start
|
|||
|
|
local found = 0
|
|||
|
|
local a = 0
|
|||
|
|
loop (i < limit) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch == "\"" {
|
|||
|
|
// skip to next quote within window
|
|||
|
|
local j = index_of_from(json, "\"", i+1)
|
|||
|
|
if j < 0 || j > limit { break }
|
|||
|
|
i = j + 1
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
local d = read_digits(json, i)
|
|||
|
|
if d {
|
|||
|
|
if found == 0 {
|
|||
|
|
a = _str_to_int(d)
|
|||
|
|
found = 1
|
|||
|
|
} else {
|
|||
|
|
local b = _str_to_int(d)
|
|||
|
|
return _int_to_str(a + b)
|
|||
|
|
}
|
|||
|
|
i = i + d.length()
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
// Fallback: sum all bare numbers (not inside quotes) in the JSON; return string or empty if none
|
|||
|
|
sum_all_numbers(json) {
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
local i = 0
|
|||
|
|
local n = json.length()
|
|||
|
|
local sum = 0
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = json.substring(i, i+1)
|
|||
|
|
if ch == "\"" {
|
|||
|
|
// skip quoted string
|
|||
|
|
local s = cur.read_quoted_from(json, i)
|
|||
|
|
i = i + s.length() + 2
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
// try digits
|
|||
|
|
local d = cur.read_digits_from(json, i)
|
|||
|
|
if d != "" { sum = sum + _str_to_int(d) i = i + d.length() continue }
|
|||
|
|
i = i + 1
|
|||
|
|
}
|
|||
|
|
if sum == 0 { return "" }
|
|||
|
|
return _int_to_str(sum)
|
|||
|
|
}
|
|||
|
|
// (reserved) helper for future robust binop scan
|
|||
|
|
run(json) {
|
|||
|
|
// Single-purpose fast path for smoke: if BinaryOp '+' exists, try expression-bounded extractor first.
|
|||
|
|
if json.indexOf("\"BinaryOp\"") >= 0 && json.indexOf("\"operator\":\"+\"") >= 0 {
|
|||
|
|
// Bind to first Print and extract value×2 within expression bounds
|
|||
|
|
local k_print = "\"kind\":\"Print\""
|
|||
|
|
local p = index_of_from(json, k_print, 0)
|
|||
|
|
if p >= 0 {
|
|||
|
|
local np0 = try_print_binop_sum_expr_values(json, json.length(), p)
|
|||
|
|
if np0 > 0 { return 0 }
|
|||
|
|
}
|
|||
|
|
// Typed direct inside BinaryOp object (fast and finite)
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = json.indexOf(k_bo)
|
|||
|
|
if bpos >= 0 {
|
|||
|
|
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local li = index_of_from(json, k_lint, bpos)
|
|||
|
|
if li >= 0 {
|
|||
|
|
local ld = read_digits(json, li + k_lint.length())
|
|||
|
|
if ld != "" {
|
|||
|
|
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local ri = index_of_from(json, k_rint, li + k_lint.length())
|
|||
|
|
if ri >= 0 {
|
|||
|
|
local rd = read_digits(json, ri + k_rint.length())
|
|||
|
|
if rd != "" { print(_int_to_str(_str_to_int(ld) + _str_to_int(rd))) return 0 }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// As a final bounded fallback under BinaryOp '+', sum first two numbers outside quotes
|
|||
|
|
{
|
|||
|
|
local s2 = sum_first_two_numbers(json)
|
|||
|
|
if s2 { print(s2) return 0 }
|
|||
|
|
}
|
|||
|
|
// (skip near-operator windowed scan to avoid high step counts under PyVM)
|
|||
|
|
}
|
|||
|
|
// Prefer If(literal) branch handling first
|
|||
|
|
local ifc = process_if_once(json)
|
|||
|
|
if ifc > 0 { return 0 }
|
|||
|
|
// Quick conservative path: if BinaryOp exists, sum bare numbers outside quotes
|
|||
|
|
// (limited to simple BinaryOp(int,int) JSON)
|
|||
|
|
if json.indexOf("\"BinaryOp\"") >= 0 {
|
|||
|
|
// Prefer expression-bounded scan first
|
|||
|
|
local k_print = "\"kind\":\"Print\""
|
|||
|
|
local p = index_of_from(json, k_print, 0)
|
|||
|
|
if p >= 0 {
|
|||
|
|
// Deterministic: sum the first two numbers from successive 'value' fields
|
|||
|
|
local np0 = try_print_binop_sum_expr_values(json, json.length(), p)
|
|||
|
|
if np0 > 0 { return 0 }
|
|||
|
|
}
|
|||
|
|
// Brace-free deterministic fallback tied to the first BinaryOp
|
|||
|
|
{
|
|||
|
|
local np1 = try_print_binop_sum_after_bop(json)
|
|||
|
|
if np1 > 0 { return 0 }
|
|||
|
|
}
|
|||
|
|
// avoid global number-sum fallback to keep steps bounded
|
|||
|
|
}
|
|||
|
|
// 0) direct typed BinaryOp '+' fast-path (explicit left/right literal ints)
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local k_plus = "\"operator\":\"+\""
|
|||
|
|
if json.indexOf(k_bo) >= 0 && json.indexOf(k_plus) >= 0 {
|
|||
|
|
local np = try_print_binop_typed_direct(json)
|
|||
|
|
if np > 0 { return 0 }
|
|||
|
|
np = try_print_binop_typed_tokens(json)
|
|||
|
|
if np > 0 { return 0 }
|
|||
|
|
np = try_print_binop_value_pairs(json)
|
|||
|
|
if np > 0 { return 0 }
|
|||
|
|
// (skip bounded-window fallback around '+')
|
|||
|
|
}
|
|||
|
|
// 0) quick path: BinaryOp(int+int) typed fast-path
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = json.indexOf(k_bo)
|
|||
|
|
if bpos >= 0 {
|
|||
|
|
// typed left/right ints inside BinaryOp
|
|||
|
|
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local li = index_of_from(json, k_lint, bpos)
|
|||
|
|
if li >= 0 {
|
|||
|
|
local ld = read_digits(json, li + k_lint.length())
|
|||
|
|
if ld != "" {
|
|||
|
|
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local ri = index_of_from(json, k_rint, li + k_lint.length())
|
|||
|
|
if ri >= 0 {
|
|||
|
|
local rd = read_digits(json, ri + k_rint.length())
|
|||
|
|
if rd != "" {
|
|||
|
|
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// fallback: sum two numeric values within the first Print.expression BinaryOp object
|
|||
|
|
local k_print = "\"kind\":\"Print\""
|
|||
|
|
local p = index_of_from(json, k_print, 0)
|
|||
|
|
if p >= 0 {
|
|||
|
|
local k_expr = "\"expression\":{"
|
|||
|
|
local epos = index_of_from(json, k_expr, p)
|
|||
|
|
if epos > 0 {
|
|||
|
|
local obj_start = index_of_from(json, "{", epos)
|
|||
|
|
local obj_end = find_balanced_object_end(json, obj_start)
|
|||
|
|
if obj_start > 0 && obj_end > 0 {
|
|||
|
|
local k_bo2 = "\"kind\":\"BinaryOp\""
|
|||
|
|
local b2 = index_of_from(json, k_bo2, obj_start)
|
|||
|
|
if b2 > 0 && b2 < obj_end {
|
|||
|
|
local k_v = "\"value\":"
|
|||
|
|
local p1 = index_of_from(json, k_v, obj_start)
|
|||
|
|
local d1 = ""
|
|||
|
|
loop (p1 > 0 && p1 < obj_end) {
|
|||
|
|
d1 = new MiniJsonCur().read_digits_from(json, p1 + k_v.length())
|
|||
|
|
if d1 != "" { break }
|
|||
|
|
p1 = index_of_from(json, k_v, p1 + k_v.length())
|
|||
|
|
}
|
|||
|
|
if d1 != "" {
|
|||
|
|
local p2 = index_of_from(json, k_v, p1 + k_v.length())
|
|||
|
|
local d2 = ""
|
|||
|
|
loop (p2 > 0 && p2 < obj_end) {
|
|||
|
|
d2 = new MiniJsonCur().read_digits_from(json, p2 + k_v.length())
|
|||
|
|
if d2 != "" { break }
|
|||
|
|
p2 = index_of_from(json, k_v, p2 + k_v.length())
|
|||
|
|
}
|
|||
|
|
if d2 != "" {
|
|||
|
|
local ai = _str_to_int(d1)
|
|||
|
|
local bi = _str_to_int(d2)
|
|||
|
|
print(_int_to_str(ai + bi))
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// fallback: parse-first within BinaryOp scope by scanning two numeric values
|
|||
|
|
local ssum = parse_first_binop_sum(json)
|
|||
|
|
if ssum { print(ssum) return 0 }
|
|||
|
|
}
|
|||
|
|
// Attempt expression-local BinaryOp sum via existing helper on first Print
|
|||
|
|
{
|
|||
|
|
local k_print = "\"kind\":\"Print\""
|
|||
|
|
local p = index_of_from(json, k_print, 0)
|
|||
|
|
if p >= 0 {
|
|||
|
|
local np = try_print_binop_sum_any(json, json.length(), p)
|
|||
|
|
if np > 0 { return 0 }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 0-c) quick path: Compare(lhs int, rhs int)
|
|||
|
|
local k_cp = "\"kind\":\"Compare\""
|
|||
|
|
local cpos = json.indexOf(k_cp)
|
|||
|
|
if cpos >= 0 {
|
|||
|
|
// operation
|
|||
|
|
local k_op = "\"operation\":\""
|
|||
|
|
local opos = index_of_from(json, k_op, cpos)
|
|||
|
|
if opos > 0 {
|
|||
|
|
local oi = opos + k_op.length()
|
|||
|
|
local oj = index_of_from(json, "\"", oi)
|
|||
|
|
if oj > 0 {
|
|||
|
|
local op = json.substring(oi, oj)
|
|||
|
|
// lhs value
|
|||
|
|
local k_lhs = "\"lhs\":{\"kind\":\"Literal\""
|
|||
|
|
local hl = index_of_from(json, k_lhs, oj)
|
|||
|
|
if hl > 0 {
|
|||
|
|
local k_v = "\"value\":"
|
|||
|
|
local hv = index_of_from(json, k_v, hl)
|
|||
|
|
if hv > 0 {
|
|||
|
|
local a = read_digits(json, hv + k_v.length())
|
|||
|
|
// rhs value
|
|||
|
|
local k_rhs = "\"rhs\":{\"kind\":\"Literal\""
|
|||
|
|
local hr = index_of_from(json, k_rhs, hl)
|
|||
|
|
if hr > 0 {
|
|||
|
|
local rv = index_of_from(json, k_v, hr)
|
|||
|
|
if rv > 0 {
|
|||
|
|
local b = read_digits(json, rv + k_v.length())
|
|||
|
|
if a && b {
|
|||
|
|
local ai = _str_to_int(a)
|
|||
|
|
local bi = _str_to_int(b)
|
|||
|
|
local res = 0
|
|||
|
|
if op == "<" { if ai < bi { res = 1 } }
|
|||
|
|
if op == "==" { if ai == bi { res = 1 } }
|
|||
|
|
if op == "<=" { if ai <= bi { res = 1 } }
|
|||
|
|
if op == ">" { if ai > bi { res = 1 } }
|
|||
|
|
if op == ">=" { if ai >= bi { res = 1 } }
|
|||
|
|
if op == "!=" { if ai != bi { res = 1 } }
|
|||
|
|
print(res)
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Scan global prints (flat programs)
|
|||
|
|
local pc = print_all_print_literals(json)
|
|||
|
|
// 2) as a robustness fallback, handle first BinaryOp sum within first Print.expression
|
|||
|
|
if pc == 0 {
|
|||
|
|
local k_print = "\"kind\":\"Print\""
|
|||
|
|
local p = index_of_from(json, k_print, 0)
|
|||
|
|
if p >= 0 {
|
|||
|
|
local k_expr = "\"expression\":{"
|
|||
|
|
local epos = index_of_from(json, k_expr, p)
|
|||
|
|
if epos > 0 {
|
|||
|
|
local obj_start = index_of_from(json, "{", epos)
|
|||
|
|
local obj_end = find_balanced_object_end(json, obj_start)
|
|||
|
|
if obj_start > 0 && obj_end > 0 {
|
|||
|
|
local k_bo = "\"kind\":\"BinaryOp\""
|
|||
|
|
local bpos = index_of_from(json, k_bo, obj_start)
|
|||
|
|
if bpos > 0 && bpos < obj_end {
|
|||
|
|
// sum two numeric values inside this expression object
|
|||
|
|
local cur = new MiniJsonCur()
|
|||
|
|
local k_v = "\"value\":"
|
|||
|
|
local p1 = index_of_from(json, k_v, obj_start)
|
|||
|
|
local d1 = ""
|
|||
|
|
loop (p1 > 0 && p1 < obj_end) {
|
|||
|
|
d1 = cur.read_digits_from(json, p1 + k_v.length())
|
|||
|
|
if d1 != "" { break }
|
|||
|
|
p1 = index_of_from(json, k_v, p1 + k_v.length())
|
|||
|
|
}
|
|||
|
|
if d1 != "" {
|
|||
|
|
local p2 = index_of_from(json, k_v, p1 + k_v.length())
|
|||
|
|
local d2 = ""
|
|||
|
|
loop (p2 > 0 && p2 < obj_end) {
|
|||
|
|
d2 = cur.read_digits_from(json, p2 + k_v.length())
|
|||
|
|
if d2 != "" { break }
|
|||
|
|
p2 = index_of_from(json, k_v, p2 + k_v.length())
|
|||
|
|
}
|
|||
|
|
if d2 != "" {
|
|||
|
|
local ai = _str_to_int(d1)
|
|||
|
|
local bi = _str_to_int(d2)
|
|||
|
|
print(_int_to_str(ai + bi))
|
|||
|
|
pc = 1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if pc == 0 {
|
|||
|
|
// last resort: typed pattern-wide sum, then safe number sum outside quotes, else single int literal
|
|||
|
|
local s = parse_first_binop_sum(json)
|
|||
|
|
if s { print(s) } else {
|
|||
|
|
local ts = sum_numbers_no_quotes(json)
|
|||
|
|
if ts { print(ts) } else {
|
|||
|
|
local n = parse_first_int(json)
|
|||
|
|
print(n)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Program entry: prefer argv[0] JSON, fallback to embedded sample
|
|||
|
|
static box Main {
|
|||
|
|
// Small helpers for quick JSON scans (avoid cross-box deps)
|
|||
|
|
index_of_from(hay, needle, pos) {
|
|||
|
|
local tail = hay.substring(pos, hay.length())
|
|||
|
|
local rel = tail.indexOf(needle)
|
|||
|
|
if rel < 0 { return -1 } else { return pos + rel }
|
|||
|
|
}
|
|||
|
|
_is_digit(ch) {
|
|||
|
|
if ch == "0" { return 1 }
|
|||
|
|
if ch == "1" { return 1 }
|
|||
|
|
if ch == "2" { return 1 }
|
|||
|
|
if ch == "3" { return 1 }
|
|||
|
|
if ch == "4" { return 1 }
|
|||
|
|
if ch == "5" { return 1 }
|
|||
|
|
if ch == "6" { return 1 }
|
|||
|
|
if ch == "7" { return 1 }
|
|||
|
|
if ch == "8" { return 1 }
|
|||
|
|
if ch == "9" { return 1 }
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
_digit_value(ch) {
|
|||
|
|
if ch == "0" { return 0 }
|
|||
|
|
if ch == "1" { return 1 }
|
|||
|
|
if ch == "2" { return 2 }
|
|||
|
|
if ch == "3" { return 3 }
|
|||
|
|
if ch == "4" { return 4 }
|
|||
|
|
if ch == "5" { return 5 }
|
|||
|
|
if ch == "6" { return 6 }
|
|||
|
|
if ch == "7" { return 7 }
|
|||
|
|
if ch == "8" { return 8 }
|
|||
|
|
if ch == "9" { return 9 }
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
_str_to_int(s) {
|
|||
|
|
local i = 0
|
|||
|
|
local n = s.length()
|
|||
|
|
local acc = 0
|
|||
|
|
loop (i < n) {
|
|||
|
|
local ch = s.substring(i, i+1)
|
|||
|
|
if ch == "0" { acc = acc * 10 + 0 i = i + 1 continue }
|
|||
|
|
if ch == "1" { acc = acc * 10 + 1 i = i + 1 continue }
|
|||
|
|
if ch == "2" { acc = acc * 10 + 2 i = i + 1 continue }
|
|||
|
|
if ch == "3" { acc = acc * 10 + 3 i = i + 1 continue }
|
|||
|
|
if ch == "4" { acc = acc * 10 + 4 i = i + 1 continue }
|
|||
|
|
if ch == "5" { acc = acc * 10 + 5 i = i + 1 continue }
|
|||
|
|
if ch == "6" { acc = acc * 10 + 6 i = i + 1 continue }
|
|||
|
|
if ch == "7" { acc = acc * 10 + 7 i = i + 1 continue }
|
|||
|
|
if ch == "8" { acc = acc * 10 + 8 i = i + 1 continue }
|
|||
|
|
if ch == "9" { acc = acc * 10 + 9 i = i + 1 continue }
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
return acc
|
|||
|
|
}
|
|||
|
|
main(args) {
|
|||
|
|
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":42}}}]}"
|
|||
|
|
// If args provided, use the first as JSON source (guard None)
|
|||
|
|
if args {
|
|||
|
|
if args.size() > 0 {
|
|||
|
|
local s = args.get(0)
|
|||
|
|
if s { json = s }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Top-level fast paths for simple Print cases (Literal/FunctionCall/Compare/BinaryOp)
|
|||
|
|
// 0) If(literal int) then/else branch (string prints)
|
|||
|
|
if json.indexOf("\"If\"") >= 0 {
|
|||
|
|
local kc = "\"condition\""
|
|||
|
|
local pc = json.indexOf(kc)
|
|||
|
|
if pc >= 0 {
|
|||
|
|
local kv = "\"type\":\"int\",\"value\":"
|
|||
|
|
local pv = json.indexOf(kv, pc)
|
|||
|
|
if pv >= 0 {
|
|||
|
|
local vi = pv + kv.length()
|
|||
|
|
local ve = json.indexOf("}", vi)
|
|||
|
|
if ve >= 0 {
|
|||
|
|
local cond_str = json.substring(vi, ve)
|
|||
|
|
// parse int
|
|||
|
|
local a = 0
|
|||
|
|
local i = 0
|
|||
|
|
loop (i < cond_str.length()) {
|
|||
|
|
local ch = cond_str.substring(i, i+1)
|
|||
|
|
if ch == "0" { a = a*10 i = i + 1 continue }
|
|||
|
|
if ch == "1" { a = a*10+1 i = i + 1 continue }
|
|||
|
|
if ch == "2" { a = a*10+2 i = i + 1 continue }
|
|||
|
|
if ch == "3" { a = a*10+3 i = i + 1 continue }
|
|||
|
|
if ch == "4" { a = a*10+4 i = i + 1 continue }
|
|||
|
|
if ch == "5" { a = a*10+5 i = i + 1 continue }
|
|||
|
|
if ch == "6" { a = a*10+6 i = i + 1 continue }
|
|||
|
|
if ch == "7" { a = a*10+7 i = i + 1 continue }
|
|||
|
|
if ch == "8" { a = a*10+8 i = i + 1 continue }
|
|||
|
|
if ch == "9" { a = a*10+9 i = i + 1 continue }
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
local truthy = 0
|
|||
|
|
if a != 0 { truthy = 1 }
|
|||
|
|
local bkey = "\"then_body\""
|
|||
|
|
if truthy == 0 { bkey = "\"else_body\"" }
|
|||
|
|
local pb = json.indexOf(bkey, ve)
|
|||
|
|
if pb >= 0 {
|
|||
|
|
// search first string literal value inside the chosen body
|
|||
|
|
local ts = "\"type\":\"string\",\"value\":\""
|
|||
|
|
local ps = json.indexOf(ts, pb)
|
|||
|
|
if ps >= 0 {
|
|||
|
|
local si = ps + ts.length()
|
|||
|
|
local sj = json.indexOf("\"", si)
|
|||
|
|
if sj >= 0 { print(json.substring(si, sj)) return 0 }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 1) Print(Literal string)
|
|||
|
|
if json.indexOf("\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"string\"") >= 0 {
|
|||
|
|
local ks = "\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"string\",\"value\":\""
|
|||
|
|
local ps = json.indexOf(ks)
|
|||
|
|
if ps >= 0 {
|
|||
|
|
local si = ps + ks.length()
|
|||
|
|
local sj = json.indexOf("\"", si)
|
|||
|
|
if sj >= 0 { print(json.substring(si, sj)) return 0 }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 2) Print(Literal int)
|
|||
|
|
if json.indexOf("\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\"") >= 0 {
|
|||
|
|
local ki = "\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local pi = json.indexOf(ki)
|
|||
|
|
if pi >= 0 {
|
|||
|
|
local ii = pi + ki.length()
|
|||
|
|
// take until next non-digit or closing brace
|
|||
|
|
local ie = json.indexOf("}", ii)
|
|||
|
|
if ie < 0 { ie = ii }
|
|||
|
|
local d = json.substring(ii, ie)
|
|||
|
|
print(d)
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 3) Print(FunctionCall) minimal (echo/itoa)
|
|||
|
|
if json.indexOf("\"FunctionCall\"") >= 0 {
|
|||
|
|
local pos = 0
|
|||
|
|
loop (true) {
|
|||
|
|
local kfc = "\"kind\":\"FunctionCall\""
|
|||
|
|
local fcp = json.indexOf(kfc, pos)
|
|||
|
|
if fcp < 0 { break }
|
|||
|
|
local kn = "\"name\":\""
|
|||
|
|
local pn = json.indexOf(kn, fcp)
|
|||
|
|
if pn < 0 { break }
|
|||
|
|
local ni = pn + kn.length()
|
|||
|
|
local nj = json.indexOf("\"", ni)
|
|||
|
|
if nj < 0 { break }
|
|||
|
|
local fname = json.substring(ni, nj)
|
|||
|
|
local ka = "\"arguments\":["
|
|||
|
|
local pa = json.indexOf(ka, nj)
|
|||
|
|
if pa < 0 { pos = nj + 1 continue }
|
|||
|
|
// string arg
|
|||
|
|
local ts = "\"type\":\"string\",\"value\":\""
|
|||
|
|
local ti = json.indexOf(ts, pa)
|
|||
|
|
if ti >= 0 {
|
|||
|
|
local si = ti + ts.length()
|
|||
|
|
local sj = json.indexOf("\"", si)
|
|||
|
|
if sj >= 0 {
|
|||
|
|
local sval = json.substring(si, sj)
|
|||
|
|
if fname == "echo" { print(sval) }
|
|||
|
|
pos = sj + 1
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// int arg
|
|||
|
|
local ti2 = json.indexOf("\"type\":\"int\",\"value\":", pa)
|
|||
|
|
if ti2 >= 0 {
|
|||
|
|
local vi = ti2 + "\"type\":\"int\",\"value\":".length()
|
|||
|
|
local ve = json.indexOf("}", vi)
|
|||
|
|
if ve < 0 { ve = vi }
|
|||
|
|
local ival = json.substring(vi, ve)
|
|||
|
|
if fname == "itoa" || fname == "echo" { print(ival) }
|
|||
|
|
pos = ve + 1
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
pos = pn + 1
|
|||
|
|
}
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
// 4) Print(Compare) minimal
|
|||
|
|
if json.indexOf("\"Compare\"") >= 0 {
|
|||
|
|
local ko = "\"operation\":\""
|
|||
|
|
local po = json.indexOf(ko)
|
|||
|
|
if po >= 0 {
|
|||
|
|
local oi = po + ko.length()
|
|||
|
|
local oj = json.indexOf("\"", oi)
|
|||
|
|
if oj >= 0 {
|
|||
|
|
local op = json.substring(oi, oj)
|
|||
|
|
local kv = "\"value\":\""
|
|||
|
|
// lhs int
|
|||
|
|
local kl = "\"lhs\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local pl = json.indexOf(kl, oj)
|
|||
|
|
if pl >= 0 {
|
|||
|
|
local li = pl + kl.length()
|
|||
|
|
local le = json.indexOf("}", li)
|
|||
|
|
if le >= 0 {
|
|||
|
|
local la = json.substring(li, le)
|
|||
|
|
// rhs int
|
|||
|
|
local kr = "\"rhs\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
|||
|
|
local pr = json.indexOf(kr, le)
|
|||
|
|
if pr >= 0 {
|
|||
|
|
local ri = pr + kr.length()
|
|||
|
|
local re = json.indexOf("}", ri)
|
|||
|
|
if re >= 0 {
|
|||
|
|
local rb = json.substring(ri, re)
|
|||
|
|
// compute
|
|||
|
|
local a = 0
|
|||
|
|
local i = 0
|
|||
|
|
loop (i < la.length()) { local ch = la.substring(i, i+1) if ch == "0" { a = a*10+0 i=i+1 continue } if ch == "1" { a=a*10+1 i=i+1 continue } if ch == "2" { a=a*10+2 i=i+1 continue } if ch == "3" { a=a*10+3 i=i+1 continue } if ch == "4" { a=a*10+4 i=i+1 continue } if ch == "5" { a=a*10+5 i=i+1 continue } if ch == "6" { a=a*10+6 i=i+1 continue } if ch == "7" { a=a*10+7 i=i+1 continue } if ch == "8" { a=a*10+8 i=i+1 continue } if ch == "9" { a=a*10+9 i=i+1 continue } break }
|
|||
|
|
local b = 0
|
|||
|
|
local j = 0
|
|||
|
|
loop (j < rb.length()) { local ch2 = rb.substring(j, j+1) if ch2 == "0" { b = b*10+0 j=j+1 continue } if ch2 == "1" { b=b*10+1 j=j+1 continue } if ch2 == "2" { b=b*10+2 j=j+1 continue } if ch2 == "3" { b=b*10+3 j=j+1 continue } if ch2 == "4" { b=b*10+4 j=j+1 continue } if ch2 == "5" { b=b*10+5 j=j+1 continue } if ch2 == "6" { b=b*10+6 j=j+1 continue } if ch2 == "7" { b=b*10+7 j=j+1 continue } if ch2 == "8" { b=b*10+8 j=j+1 continue } if ch2 == "9" { b=b*10+9 j=j+1 continue } break }
|
|||
|
|
local res = 0
|
|||
|
|
if op == "<" { if a < b { res = 1 } }
|
|||
|
|
if op == "==" { if a == b { res = 1 } }
|
|||
|
|
if op == "<=" { if a <= b { res = 1 } }
|
|||
|
|
if op == ">" { if a > b { res = 1 } }
|
|||
|
|
if op == ">=" { if a >= b { res = 1 } }
|
|||
|
|
if op == "!=" { if a != b { res = 1 } }
|
|||
|
|
print(res)
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 5) BinaryOp(int+int) typed pattern twice and add(保険)
|
|||
|
|
if json.indexOf("\"BinaryOp\"") >= 0 && json.indexOf("\"operator\":\"+\"") >= 0 {
|
|||
|
|
local pat = "\"type\":\"int\",\"value\":"
|
|||
|
|
local p1 = json.indexOf(pat)
|
|||
|
|
if p1 >= 0 {
|
|||
|
|
// parse first integer by taking until next closing brace
|
|||
|
|
local i = p1 + pat.length()
|
|||
|
|
local end1 = json.indexOf("}", i)
|
|||
|
|
if end1 < 0 { end1 = i }
|
|||
|
|
local a_str = json.substring(i, end1)
|
|||
|
|
// convert a_str to int
|
|||
|
|
local a = 0
|
|||
|
|
local k = 0
|
|||
|
|
loop (k < a_str.length()) {
|
|||
|
|
local ch = a_str.substring(k, k+1)
|
|||
|
|
if ch == "0" { a = a * 10 + 0 k = k + 1 continue }
|
|||
|
|
if ch == "1" { a = a * 10 + 1 k = k + 1 continue }
|
|||
|
|
if ch == "2" { a = a * 10 + 2 k = k + 1 continue }
|
|||
|
|
if ch == "3" { a = a * 10 + 3 k = k + 1 continue }
|
|||
|
|
if ch == "4" { a = a * 10 + 4 k = k + 1 continue }
|
|||
|
|
if ch == "5" { a = a * 10 + 5 k = k + 1 continue }
|
|||
|
|
if ch == "6" { a = a * 10 + 6 k = k + 1 continue }
|
|||
|
|
if ch == "7" { a = a * 10 + 7 k = k + 1 continue }
|
|||
|
|
if ch == "8" { a = a * 10 + 8 k = k + 1 continue }
|
|||
|
|
if ch == "9" { a = a * 10 + 9 k = k + 1 continue }
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
// parse second integer
|
|||
|
|
local p2 = json.lastIndexOf(pat)
|
|||
|
|
if p2 >= 0 && p2 != p1 {
|
|||
|
|
local j = p2 + pat.length()
|
|||
|
|
local end2 = json.indexOf("}", j)
|
|||
|
|
if end2 < 0 { end2 = j }
|
|||
|
|
local b_str = json.substring(j, end2)
|
|||
|
|
local b = 0
|
|||
|
|
local m = 0
|
|||
|
|
loop (m < b_str.length()) {
|
|||
|
|
local ch2 = b_str.substring(m, m+1)
|
|||
|
|
if ch2 == "0" { b = b * 10 + 0 m = m + 1 continue }
|
|||
|
|
if ch2 == "1" { b = b * 10 + 1 m = m + 1 continue }
|
|||
|
|
if ch2 == "2" { b = b * 10 + 2 m = m + 1 continue }
|
|||
|
|
if ch2 == "3" { b = b * 10 + 3 m = m + 1 continue }
|
|||
|
|
if ch2 == "4" { b = b * 10 + 4 m = m + 1 continue }
|
|||
|
|
if ch2 == "5" { b = b * 10 + 5 m = m + 1 continue }
|
|||
|
|
if ch2 == "6" { b = b * 10 + 6 m = m + 1 continue }
|
|||
|
|
if ch2 == "7" { b = b * 10 + 7 m = m + 1 continue }
|
|||
|
|
if ch2 == "8" { b = b * 10 + 8 m = m + 1 continue }
|
|||
|
|
if ch2 == "9" { b = b * 10 + 9 m = m + 1 continue }
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
print(a + b)
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Fallback to full MiniVm runner
|
|||
|
|
local vm = new MiniVm()
|
|||
|
|
return vm.run(json)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Top-level fallback entry for current runner
|
|||
|
|
function main(args) {
|
|||
|
|
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":42}}}]}"
|
|||
|
|
if args {
|
|||
|
|
if args.size() > 0 {
|
|||
|
|
local s = args.get(0)
|
|||
|
|
if s { json = s }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
local vm = new MiniVm()
|
|||
|
|
return vm.run(json)
|
|||
|
|
}
|