// Expect retcode = 0 // 轻量 JSON 解析 / 输出器(递归下降 + 节点池),用于验证底层库的拼装,未来服务 LSP。 // 设计: // - 输入 ByteView,字符串/数字以“源切片”借用,不做解码,round-trip 保真; // - 节点池 Vec[JsonNode];数组/对象子项存 Vec[JsonChild],保留插入顺序; // - 错误带字节位置(error.spl);输出写 ByteBuffer;缩进/数字用 ByteBuffer/fmt。 // 限制:不做字符串转义解码、无递归深度保护、数字不做数值化(原样保存)。 type .{ .Error, .Status, .make, .at } = @import("error.spl"); type .{ .ByteView, .ViewResult, .FindResult } = @import("byte_view.spl"); type .{ .Vec } = @import("vec.spl"); type .{ .ByteBuffer } = @import("byte_buffer.spl"); type .{ .is_digit, .is_hex_digit, .is_space } = @import("ascii.spl"); type .{ .append_i64, .append_u64 } = @import("fmt.spl"); type JsonKind = enum { Null, True, False, Number, String, Array, Object, } // 数组元素或对象成员。数组时 key 为空视图;对象时 key 为源中的原始键文本。 type JsonChild = struct { key: ByteView, val: usize, } type JsonNode = struct { kind: JsonKind, text: ByteView, children: Vec, } // 解析结果文档:nodes 拥有全部节点;root 为根节点下标。 type Json = struct { nodes: Vec, root: usize, } type JsonResult = enum { Ok: Json, Err: Error, } type NodeResult = enum { Ok: usize, Err: Error, } type Parser = struct { src: ByteView, pos: usize, nodes: Vec, } fn blank_node(kind: JsonKind): JsonNode { ret JsonNode { .kind = kind, .text = ByteView.empty(), .children = Vec.blank(@sizeof(JsonChild), 8), }; } fn intern_node(p: *Parser, node: JsonNode): NodeResult { var index = p.nodes.len; var n = node; match Vec.push(&p.nodes, &n as *u8) { .Ok => {}, .Err(error) => { ret .Err(error); }, } ret .Ok(index); } fn node_at(doc: *Json, index: usize): *JsonNode { match Vec.get(&doc.nodes, index) { .Ok(ptr) => { ret ptr as *JsonNode; }, .Err(error) => { ret null; }, } ret null; } fn skip_ws(p: *Parser) { while p.pos < p.src.len && is_space(p.src.ptr[p.pos]) { p.pos = p.pos + 1; } } // -1 表示 EOF。 fn peek(p: *Parser): i32 { if p.pos >= p.src.len { ret -1; } ret p.src.ptr[p.pos] as i32; } // 当前字符是否等于 ch(EOF 时 false)。 fn at_char(p: *Parser, ch: u8): bool { var c = peek(p); if c < 0 { ret false; } ret (c as u8) == ch; } fn parse_string_raw(p: *Parser): ViewResult { if !at_char(p, '"') { ret .Err(at(.InvalidFormat, p.pos)); } p.pos = p.pos + 1; var start = p.pos; while p.pos < p.src.len { var c = p.src.ptr[p.pos]; if c == '"' { break; } if c == '\\' { // '\\' p.pos = p.pos + 1; if p.pos >= p.src.len { ret .Err(at(.UnexpectedEnd, p.pos)); } if p.src.ptr[p.pos] == 'u' { // 'u' var k: usize = 0; while k < 4 { p.pos = p.pos + 1; if p.pos >= p.src.len { ret .Err(at(.UnexpectedEnd, p.pos)); } if !is_hex_digit(p.src.ptr[p.pos]) { ret .Err(at(.InvalidDigit, p.pos)); } k = k + 1; } } } else if c < 32 { ret .Err(at(.InvalidFormat, p.pos)); } p.pos = p.pos + 1; } if p.pos >= p.src.len { ret .Err(at(.UnexpectedEnd, p.pos)); } var end = p.pos; p.pos = p.pos + 1; // 收尾引号 ret ByteView.slice(p.src, start, end); } fn parse_literal(p: *Parser, word: []u8, kind: JsonKind): NodeResult { var i: usize = 0; while i < word.len { if p.pos + i >= p.src.len { ret .Err(at(.UnexpectedEnd, p.src.len)); } if p.src.ptr[p.pos + i] != word.ptr[i] { ret .Err(at(.InvalidFormat, p.pos + i)); } i = i + 1; } p.pos = p.pos + word.len; var node = blank_node(kind); ret intern_node(p, node); } fn parse_number(p: *Parser): NodeResult { var start = p.pos; if at_char(p, '-') { p.pos = p.pos + 1; } // '-' if p.pos >= p.src.len || !is_digit(p.src.ptr[p.pos]) { ret .Err(at(.InvalidDigit, p.pos)); } if p.src.ptr[p.pos] == '0' { // 前导 0 后面不能接数字 p.pos = p.pos + 1; } else { while p.pos < p.src.len && is_digit(p.src.ptr[p.pos]) { p.pos = p.pos + 1; } } if p.pos < p.src.len && p.src.ptr[p.pos] == '.' { // '.' p.pos = p.pos + 1; if p.pos >= p.src.len || !is_digit(p.src.ptr[p.pos]) { ret .Err(at(.InvalidDigit, p.pos)); } while p.pos < p.src.len && is_digit(p.src.ptr[p.pos]) { p.pos = p.pos + 1; } } if p.pos < p.src.len && (p.src.ptr[p.pos] == 'e' || p.src.ptr[p.pos] == 'E') { // e/E p.pos = p.pos + 1; if p.pos < p.src.len && (p.src.ptr[p.pos] == '+' || p.src.ptr[p.pos] == '-') { p.pos = p.pos + 1; } if p.pos >= p.src.len || !is_digit(p.src.ptr[p.pos]) { ret .Err(at(.InvalidDigit, p.pos)); } while p.pos < p.src.len && is_digit(p.src.ptr[p.pos]) { p.pos = p.pos + 1; } } var text = ByteView.slice(p.src, start, p.pos); match text { .Ok(v) => { var node = blank_node(.Number); node.text = v; ret intern_node(p, node); }, .Err(error) => { ret .Err(error); }, } } fn parse_array(p: *Parser): NodeResult { p.pos = p.pos + 1; // '[' var kids = Vec.blank(@sizeof(JsonChild), 8); skip_ws(p); if at_char(p, ']') { // ']' p.pos = p.pos + 1; var empty = blank_node(.Array); empty.children = kids; ret intern_node(p, empty); } while true { var element = parse_value(p); var index: usize = 0; match element { .Ok(v) => { index = v; }, .Err(error) => { Vec.drop(&kids); ret .Err(error); }, } var child = JsonChild { .key = ByteView.empty(), .val = index }; match Vec.push(&kids, &child as *u8) { .Ok => {}, .Err(error) => { Vec.drop(&kids); ret .Err(error); }, } skip_ws(p); if at_char(p, ',') { // ',' p.pos = p.pos + 1; continue; } if at_char(p, ']') { // ']' p.pos = p.pos + 1; break; } Vec.drop(&kids); ret .Err(at(.InvalidFormat, p.pos)); } var node = blank_node(.Array); node.children = kids; ret intern_node(p, node); } fn parse_object(p: *Parser): NodeResult { p.pos = p.pos + 1; // '{' var kids = Vec.blank(@sizeof(JsonChild), 8); skip_ws(p); if at_char(p, '}') { // '}' p.pos = p.pos + 1; var empty = blank_node(.Object); empty.children = kids; ret intern_node(p, empty); } while true { skip_ws(p); var key_view = ByteView.empty(); match parse_string_raw(p) { .Ok(v) => { key_view = v; }, .Err(error) => { Vec.drop(&kids); ret .Err(error); }, } skip_ws(p); if !at_char(p, ':') { // ':' Vec.drop(&kids); ret .Err(at(.InvalidFormat, p.pos)); } p.pos = p.pos + 1; var value = parse_value(p); var index: usize = 0; match value { .Ok(v) => { index = v; }, .Err(error) => { Vec.drop(&kids); ret .Err(error); }, } var child = JsonChild { .key = key_view, .val = index }; match Vec.push(&kids, &child as *u8) { .Ok => {}, .Err(error) => { Vec.drop(&kids); ret .Err(error); }, } skip_ws(p); if at_char(p, ',') { // ',' p.pos = p.pos + 1; continue; } if at_char(p, '}') { // '}' p.pos = p.pos + 1; break; } Vec.drop(&kids); ret .Err(at(.InvalidFormat, p.pos)); } var node = blank_node(.Object); node.children = kids; ret intern_node(p, node); } fn parse_value(p: *Parser): NodeResult { skip_ws(p); var c = peek(p); if c < 0 { ret .Err(at(.UnexpectedEnd, p.pos)); } var ch = c as u8; if ch == '"' { // '"' var view = ByteView.empty(); match parse_string_raw(p) { .Ok(v) => { view = v; }, .Err(error) => { ret .Err(error); }, } var node = blank_node(.String); node.text = view; ret intern_node(p, node); } if ch == '{' { ret parse_object(p); } // '{' if ch == '[' { ret parse_array(p); } // '[' if ch == 't' { ret parse_literal(p, "true", .True); } if ch == 'f' { ret parse_literal(p, "false", .False); } if ch == 'n' { ret parse_literal(p, "null", .Null); } ret parse_number(p); } fn drop_node_pool(pool: *Vec) { var i: usize = 0; while i < pool.*.len { match Vec.get(pool, i) { .Ok(ptr) => { var n = ptr as *JsonNode; Vec.drop(&n.children); }, .Err(error) => {}, } i = i + 1; } Vec.drop(pool); } fn parse(src: ByteView): JsonResult { var p = Parser { .src = src, .pos = 0, .nodes = Vec.blank(@sizeof(JsonNode), 8), }; skip_ws(&p); var root: usize = 0; var result = parse_value(&p); match result { .Ok(index) => { root = index; }, .Err(error) => { drop_node_pool(&p.nodes); ret .Err(error); }, } skip_ws(&p); if p.pos != src.len { drop_node_pool(&p.nodes); ret .Err(at(.InvalidFormat, p.pos)); } ret .Ok(Json { .nodes = p.nodes, .root = root }); } fn drop(doc: *Json) { drop_node_pool(&doc.nodes); } fn root_index(doc: *Json): usize { ret doc.root; } fn kind_of(doc: *Json, index: usize): JsonKind { var n = node_at(doc, index); if n == null { ret .Null; } ret n.kind; } // 枚举 tag 取整(stage1 枚举 == 有缺陷,用 @enum2int 比较更稳妥)。 fn kind_code(kind: JsonKind): usize { ret @enum2int(kind) as usize; } fn text_of(doc: *Json, index: usize): ByteView { var n = node_at(doc, index); if n == null { ret ByteView.empty(); } ret n.text; } fn array_count(doc: *Json, index: usize): usize { var n = node_at(doc, index); if n == null { ret 0; } match n.kind { .Array => { ret n.children.len; }, _ => { ret 0; }, } ret 0; } fn array_at(doc: *Json, index: usize, i: usize): usize { var n = node_at(doc, index); if n == null { ret ~(0 as usize); } match n.kind { .Array => { if i >= n.children.len { ret ~(0 as usize); } match Vec.get(&n.children, i) { .Ok(ptr) => { ret (ptr as *JsonChild).val; }, .Err(error) => { ret ~(0 as usize); }, } }, _ => { ret ~(0 as usize); }, } ret ~(0 as usize); } // 对象按键查找(线性,保持插入顺序)。找到返回节点下标。 fn object_find(doc: *Json, index: usize, key: ByteView): FindResult { var n = node_at(doc, index); if n == null { ret .NotFound; } match n.kind { .Object => { var i: usize = 0; var count = n.children.len; while i < count { match Vec.get(&n.children, i) { .Ok(ptr) => { var child = ptr as *JsonChild; if ByteView.eq(child.key, key) { ret .Found(child.val); } }, .Err(error) => {}, } i = i + 1; } ret .NotFound; }, _ => { ret .NotFound; }, } ret .NotFound; } fn emit_quoted(out: *ByteBuffer, text: ByteView): Status { match ByteBuffer.append_byte(out, '"') { // '"' .Ok => {}, .Err(error) => { ret .Err(error); }, } match ByteBuffer.append(out, ByteView.as_slice(text)) { .Ok => {}, .Err(error) => { ret .Err(error); }, } ret ByteBuffer.append_byte(out, '"'); } fn emit_indent(out: *ByteBuffer, indent: usize, depth: usize): Status { match ByteBuffer.append_byte(out, '\n') { // '\n' .Ok => {}, .Err(error) => { ret .Err(error); }, } ret ByteBuffer.append_repeat(out, 32, depth * indent); } fn emit(out: *ByteBuffer, doc: *Json, index: usize, indent: usize, depth: usize): Status { var n = node_at(doc, index); if n == null { ret .Err(make(.OutOfRange)); } match n.kind { .Null => { ret ByteBuffer.append(out, "null"); }, .True => { ret ByteBuffer.append(out, "true"); }, .False => { ret ByteBuffer.append(out, "false"); }, .Number => { ret ByteBuffer.append(out, ByteView.as_slice(n.text)); }, .String => { ret emit_quoted(out, n.text); }, .Array => { ret emit_children(out, doc, n, false, indent, depth); }, .Object => { ret emit_children(out, doc, n, true, indent, depth); }, } ret .Ok; } fn emit_children(out: *ByteBuffer, doc: *Json, n: *JsonNode, is_object: bool, indent: usize, depth: usize): Status { var open_byte: u8 = '['; // '[' var close_byte: u8 = ']'; // ']' if is_object { open_byte = '{'; // '{' close_byte = '}'; // '}' } match ByteBuffer.append_byte(out, open_byte) { .Ok => {}, .Err(error) => { ret .Err(error); }, } var count = n.children.len; if count == 0 { ret ByteBuffer.append_byte(out, close_byte); } var i: usize = 0; while i < count { if i != 0 { match ByteBuffer.append_byte(out, ',') { // ',' .Ok => {}, .Err(error) => { ret .Err(error); }, } } if indent != 0 { match emit_indent(out, indent, depth + 1) { .Ok => {}, .Err(error) => { ret .Err(error); }, } } match Vec.get(&n.children, i) { .Ok(ptr) => { var child = ptr as *JsonChild; if is_object { match emit_quoted(out, child.key) { .Ok => {}, .Err(error) => { ret .Err(error); }, } match ByteBuffer.append_byte(out, ':') { // ':' .Ok => {}, .Err(error) => { ret .Err(error); }, } if indent != 0 { match ByteBuffer.append_byte(out, ' ') { // ' ' .Ok => {}, .Err(error) => { ret .Err(error); }, } } } match emit(out, doc, child.val, indent, depth + 1) { .Ok => {}, .Err(error) => { ret .Err(error); }, } }, .Err(error) => { ret .Err(error); }, } i = i + 1; } if indent != 0 { match emit_indent(out, indent, depth) { .Ok => {}, .Err(error) => { ret .Err(error); }, } } ret ByteBuffer.append_byte(out, close_byte); } fn emit_compact(out: *ByteBuffer, doc: *Json): Status { ret emit(out, doc, doc.root, 0, 0); } fn emit_pretty(out: *ByteBuffer, doc: *Json, indent: usize): Status { ret emit(out, doc, doc.root, indent, 0); } // ---- 构造辅助(写 JSON 片段;字符串需由调用方保证已转义)---- fn write_int(out: *ByteBuffer, value: i64): Status { ret append_i64(out, value, 10, false); } fn write_u64(out: *ByteBuffer, value: u64): Status { ret append_u64(out, value, 10, false); } // ---- 测试辅助 ---- fn expect_ok(result: Status) { match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected json error"); }, } } fn expect_bytes(out: *ByteBuffer, expected: []u8) { var actual = ByteBuffer.as_slice(out.*); @assert(actual.len == expected.len, "emitted length"); var i: usize = 0; while i < expected.len { @assert(actual.ptr[i] == expected.ptr[i], "emitted byte"); i = i + 1; } } fn expect_parse_error(result: JsonResult, pos: usize) { match result { .Ok(doc) => { @assert(false, "expected parse error"); }, .Err(error) => { @assert(error.has_position, "positioned error"); @assert(error.position == pos, "error position"); }, } } fn expect_kind(doc: *Json, index: usize, kind: JsonKind) { var n = node_at(doc, index); @assert(n != null, "node exists"); @assert(kind_code(n.kind) == kind_code(kind), "node kind"); } fn expect_text(doc: *Json, index: usize, expected: []u8) { var got = text_of(doc, index); var want = ByteView.from_slice(expected); @assert(ByteView.eq(got, want), "node text"); } fn test_scalars() { var r = parse(ByteView.from_slice("null")); match r { .Ok(doc) => { expect_kind(&doc, root_index(&doc), .Null); drop(&doc); }, .Err(error) => { @assert(false, "parse null"); }, } r = parse(ByteView.from_slice("true")); match r { .Ok(doc) => { expect_kind(&doc, root_index(&doc), .True); drop(&doc); }, .Err(error) => { @assert(false, "parse true"); }, } r = parse(ByteView.from_slice("false")); match r { .Ok(doc) => { expect_kind(&doc, root_index(&doc), .False); drop(&doc); }, .Err(error) => { @assert(false, "parse false"); }, } r = parse(ByteView.from_slice("-12.5e2")); match r { .Ok(doc) => { var idx = root_index(&doc); expect_kind(&doc, idx, .Number); expect_text(&doc, idx, "-12.5e2"); drop(&doc); }, .Err(error) => { @assert(false, "parse number"); }, } r = parse(ByteView.from_slice("\"hi\\n\"")); match r { .Ok(doc) => { var idx = root_index(&doc); expect_kind(&doc, idx, .String); expect_text(&doc, idx, "hi\\n"); // 原始切片,未解码 drop(&doc); }, .Err(error) => { @assert(false, "parse string"); }, } } fn test_containers_and_lookup() { var text = "{\"a\":1,\"b\":[true,null,\"x\"],\"c\":{\"d\":2}}"; var r = parse(ByteView.from_slice(text)); match r { .Ok(doc) => { var root = root_index(&doc); expect_kind(&doc, root, .Object); var a = ByteView.from_slice("a"); match object_find(&doc, root, a) { .Found(idx) => { expect_kind(&doc, idx, .Number); expect_text(&doc, idx, "1"); }, .NotFound => { @assert(false, "find a"); }, } var b = ByteView.from_slice("b"); var bidx: usize = 0; match object_find(&doc, root, b) { .Found(idx) => { bidx = idx; }, .NotFound => { @assert(false, "find b"); }, } expect_kind(&doc, bidx, .Array); @assert(array_count(&doc, bidx) == 3, "array count"); expect_kind(&doc, array_at(&doc, bidx, 0), .True); expect_kind(&doc, array_at(&doc, bidx, 1), .Null); expect_text(&doc, array_at(&doc, bidx, 2), "x"); var c = ByteView.from_slice("c"); var cidx: usize = 0; match object_find(&doc, root, c) { .Found(idx) => { cidx = idx; }, .NotFound => { @assert(false, "find c"); }, } var d = ByteView.from_slice("d"); match object_find(&doc, cidx, d) { .Found(idx) => { expect_text(&doc, idx, "2"); }, .NotFound => { @assert(false, "find c.d"); }, } var missing = ByteView.from_slice("zzz"); match object_find(&doc, root, missing) { .Found(idx) => { @assert(false, "unexpected find"); }, .NotFound => {}, } drop(&doc); }, .Err(error) => { @assert(false, "parse object"); }, } } fn test_roundtrip_compact() { var text = "{\"a\":1,\"b\":[true,null,\"x\\n\"],\"c\":{},\"d\":[],\"e\":-3.5e-2}"; var r = parse(ByteView.from_slice(text)); match r { .Ok(doc) => { var out = ByteBuffer.new(); expect_ok(emit_compact(&out, &doc)); expect_bytes(&out, text); ByteBuffer.drop(&out); drop(&doc); }, .Err(error) => { @assert(false, "parse for roundtrip"); }, } } fn test_pretty() { var text = "{\"a\":1,\"b\":[1,2]}"; var r = parse(ByteView.from_slice(text)); match r { .Ok(doc) => { var out = ByteBuffer.new(); expect_ok(emit_pretty(&out, &doc, 2)); var expected = "{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}"; expect_bytes(&out, expected); ByteBuffer.drop(&out); drop(&doc); }, .Err(error) => { @assert(false, "parse pretty"); }, } } fn test_errors() { expect_parse_error(parse(ByteView.from_slice("")), 0); expect_parse_error(parse(ByteView.from_slice("\"abc")), 4); expect_parse_error(parse(ByteView.from_slice("tru")), 3); expect_parse_error(parse(ByteView.from_slice("[1,]")), 3); expect_parse_error(parse(ByteView.from_slice("{\"a\":}")), 5); expect_parse_error(parse(ByteView.from_slice("1 2")), 2); expect_parse_error(parse(ByteView.from_slice("01")), 1); } fn test_construction_helpers() { var out = ByteBuffer.new(); expect_ok(ByteBuffer.append(&out, "{\"id\":")); expect_ok(write_u64(&out, 18446744073709551615)); expect_ok(ByteBuffer.append(&out, ",\"n\":")); expect_ok(write_int(&out, -7)); expect_ok(ByteBuffer.append(&out, "}")); expect_bytes(&out, "{\"id\":18446744073709551615,\"n\":-7}"); ByteBuffer.drop(&out); } fn test_large_array() { var out = ByteBuffer.new(); expect_ok(ByteBuffer.append_byte(&out, '[')); // '[' var i: usize = 0; while i < 1000 { if i != 0 { expect_ok(ByteBuffer.append_byte(&out, ',')); } expect_ok(ByteBuffer.append_byte(&out, '1')); // '1' i = i + 1; } expect_ok(ByteBuffer.append_byte(&out, ']')); // ']' var text = ByteBuffer.as_slice(out); var r = parse(ByteView.from_slice(text)); match r { .Ok(doc) => { expect_kind(&doc, root_index(&doc), .Array); @assert(array_count(&doc, root_index(&doc)) == 1000, "large array count"); expect_kind(&doc, array_at(&doc, root_index(&doc), 999), .Number); drop(&doc); }, .Err(error) => { @assert(false, "parse large array"); }, } ByteBuffer.drop(&out); } fn main() { test_scalars(); test_containers_and_lookup(); test_roundtrip_compact(); test_pretty(); test_errors(); test_construction_helpers(); test_large_array(); }