// no external using required for quick json_query (text-slicing) static box Main { main() { // Simple JSON query runner: evaluate a few (json, path) pairs and print results // Path grammar (subset): // := ('.' | '[' ']')* // Examples: .a.b[1], .c, [0] local cases = new ArrayBox() // Case 1 local j1 = "{\"a\":{\"b\":[1,2,3]},\"c\":\"x\"}" cases.push(j1) cases.push(".a.b[1]") // -> 2 cases.push(j1) cases.push(".c") // -> "x" cases.push(j1) cases.push(".a") // -> {"b":[1,2,3]} cases.push(j1) cases.push(".a.b") // -> [1,2,3] cases.push(j1) cases.push(".a.b[10]") // -> null cases.push(j1) cases.push(".x") // -> null cases.push(j1) cases.push(".a.b[0]") // -> 1 // Case 2 local j2 = "[ {\"k\":\"v\"}, 10, null ]" cases.push(j2) cases.push("[0].k") // -> "v" cases.push(j2) cases.push("[1]") // -> 10 cases.push(j2) cases.push("[2]") // -> null cases.push(j2) cases.push("[3]") // -> null (out of range) local i = 0 loop(i < cases.length()) { local json_text = cases.get(i) local path = cases.get(i + 1) // Parser-less path: slice JSON text directly for quick profile stability local out_text = this.eval_path_text(json_text, path) if out_text == null { print("null") } else { print(out_text) } i = i + 2 } return 0 } // Evaluate a simple JSON path by slicing JSON text directly (no full parse) // Returns a JSON substring for the value or null if not found eval_path_text(json_text, path) { local cur_text = json_text local i = 0 loop(i < path.length()) { local ch = path.substring(i, i + 1) if ch == "." { // parse identifier i = i + 1 local start = i loop(i < path.length()) { local c = path.substring(i, i + 1) if this.is_alnum(c) or c == "_" { i = i + 1 } else { break } } local key = path.substring(start, i) if key.length() == 0 { return null } // Get value text directly; then reset window to that text local next_text = this.object_get_text(cur_text, 0, cur_text.length(), key) if next_text == null { return null } cur_text = next_text } else { if ch == "[" { // parse index i = i + 1 local start = i loop(i < path.length() and this.is_digit(path.substring(i, i + 1))) { i = i + 1 } local idx_str = path.substring(start, i) if i >= path.length() or path.substring(i, i + 1) != "]" { return null } i = i + 1 // skip ']' local idx = this.parse_int(idx_str) local next_text = this.array_get_text(cur_text, 0, cur_text.length(), idx) if next_text == null { return null } cur_text = next_text } else { return null } } } return cur_text } // Local helpers (avoid external using in app) 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" } is_alpha(ch) { return (ch >= "a" and ch <= "z") or (ch >= "A" and ch <= "Z") } is_alnum(ch) { return this.is_alpha(ch) or this.is_digit(ch) } parse_int(s) { local i = 0 local neg = false if s.length() > 0 and s.substring(0,1) == "-" { neg = true i = 1 } local acc = 0 loop(i < s.length()) { local ch = s.substring(i, i + 1) if not this.is_digit(ch) { break } // ch to digit // 0..9 if ch == "0" { acc = acc * 10 + 0 } else { if ch == "1" { acc = acc * 10 + 1 } else { if ch == "2" { acc = acc * 10 + 2 } else { if ch == "3" { acc = acc * 10 + 3 } else { if ch == "4" { acc = acc * 10 + 4 } else { if ch == "5" { acc = acc * 10 + 5 } else { if ch == "6" { acc = acc * 10 + 6 } else { if ch == "7" { acc = acc * 10 + 7 } else { if ch == "8" { acc = acc * 10 + 8 } else { if ch == "9" { acc = acc * 10 + 9 } }}}}}}}}} i = i + 1 } if neg { return 0 - acc } else { return acc } } // --- Minimal JSON slicing helpers (object/array) --- // Find a key's value span within an object JSON slice [start,end) object_get_span(s, start, end, key) { local i = start if i < end and s.substring(i, i+1) == "{" { i = i + 1 } else { return null } loop(i < end) { i = this.skip_ws(s, i, end) if i >= end { return null } if s.substring(i, i+1) == "}" { return null } if s.substring(i, i+1) != "\"" { return null } local key_end = this.read_string_end(s, i, end) if key_end == -1 { return null } local key_text = s.substring(i+1, key_end-1) i = key_end i = this.skip_ws(s, i, end) if i >= end or s.substring(i, i+1) != ":" { return null } i = i + 1 i = this.skip_ws(s, i, end) local vspan = this.read_value_span(s, i, end) if vspan == null { return null } if key_text == key { return vspan } i = vspan.get(1) i = this.skip_ws(s, i, end) if i < end and s.substring(i, i+1) == "," { i = i + 1 continue } else { return null } } return null } // Return the value text for a key within an object slice, or null object_get_text(s, start, end, key) { local sp = this.object_get_span(s, start, end, key) if sp == null { return null } // sp is ArrayBox [i, j]; re-scan without using .get to avoid VM instance calls // Instead, rebuild by scanning again (small overhead, safer on current VM path) // We locate the key again and then extract value via read_value_span // Directly extracting indices from sp would require .get; so re-use scanner // Start from beginning for simplicity local i = start if i < end and s.substring(i, i+1) == "{" { i = i + 1 } else { return null } loop(i < end) { i = this.skip_ws(s, i, end) if i >= end { return null } if s.substring(i, i+1) == "}" { return null } if s.substring(i, i+1) != "\"" { return null } local key_end = this.read_string_end(s, i, end) if key_end == -1 { return null } local key_text = s.substring(i+1, key_end-1) i = key_end i = this.skip_ws(s, i, end) if i >= end or s.substring(i, i+1) != ":" { return null } i = i + 1 i = this.skip_ws(s, i, end) local vspan = this.read_value_span(s, i, end) if vspan == null { return null } if key_text == key { return s.substring(i, vspan.get(1)) } i = vspan.get(1) i = this.skip_ws(s, i, end) if i < end and s.substring(i, i+1) == "," { i = i + 1 continue } else { return null } } return null } // Get the span of the idx-th element at top-level of an array slice [start,end) array_get_span(s, start, end, idx) { local i = start if i < end and s.substring(i, i+1) == "[" { i = i + 1 } else { return null } local cur = 0 loop(i < end) { i = this.skip_ws(s, i, end) if i >= end { return null } if s.substring(i, i+1) == "]" { return null } local vspan = this.read_value_span(s, i, end) if vspan == null { return null } if cur == idx { return vspan } i = vspan.get(1) i = this.skip_ws(s, i, end) if i < end and s.substring(i, i+1) == "," { i = i + 1 cur = cur + 1 continue } else { return null } } return null } // Return the text of idx-th element within an array slice, or null array_get_text(s, start, end, idx) { local sp = this.array_get_span(s, start, end, idx) if sp == null { return null } // Re-scan to compute exact [i,j) and return substring without ArrayBox.get local i = start if i < end and s.substring(i, i+1) == "[" { i = i + 1 } else { return null } local cur = 0 loop(i < end) { i = this.skip_ws(s, i, end) if i >= end { return null } if s.substring(i, i+1) == "]" { return null } local vspan = this.read_value_span(s, i, end) if vspan == null { return null } if cur == idx { return s.substring(i, vspan.get(1)) } i = vspan.get(1) i = this.skip_ws(s, i, end) if i < end and s.substring(i, i+1) == "," { i = i + 1 cur = cur + 1 continue } else { return null } } return null } // Read a JSON value span starting at i; returns [start,end) read_value_span(s, i, end) { if i >= end { return null } local ch = s.substring(i, i+1) if ch == "\"" { local j = this.read_string_end(s, i, end) if j == -1 { return null } else { local out = new ArrayBox() out.push(i) out.push(j) return out } } if ch == "{" { local j = this.matching_brace(s, i, end, "{", "}") if j == -1 { return null } else { local out = new ArrayBox() out.push(i) out.push(j) return out } } if ch == "[" { local j = this.matching_brace(s, i, end, "[", "]") if j == -1 { return null } else { local out = new ArrayBox() out.push(i) out.push(j) return out } } // number/bool/null: read until comma or closing local j = i loop(j < end) { local c = s.substring(j, j+1) if c == "," or c == "}" or c == "]" or this.is_ws_char(c) { break } j = j + 1 } local out = new ArrayBox() out.push(i) out.push(j) return out } // Find end index (exclusive) of a JSON string literal starting at i ('"') read_string_end(s, i, end) { local j = i + 1 loop(j < end) { local c = s.substring(j, j+1) if c == "\\" { j = j + 2 continue } if c == "\"" { return j + 1 } j = j + 1 } return -1 } // Match paired braces/brackets with simple string-awareness; returns end index (exclusive) matching_brace(s, i, end, open, close) { local depth = 0 local j = i loop(j < end) { local c = s.substring(j, j+1) if c == "\"" { j = this.read_string_end(s, j, end) if j == -1 { return -1 } continue } if c == open { depth = depth + 1 } else { if c == close { depth = depth - 1 if depth == 0 { return j + 1 } } } j = j + 1 } return -1 } // Whitespace utilities skip_ws(s, i, end) { local j = i loop(j < end and this.is_ws_char(s.substring(j, j+1))) { j = j + 1 } return j } is_ws_char(ch) { return ch == " " or ch == "\t" or ch == "\n" or ch == "\r" } // Note: normalization now lives in JsonNode (object_get/array_get) }