Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions bench/njson-bench.scm
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@
(define bench-ref-key (string-append "k" (number->string (quotient bench-top-key-count 2))))
(define bench-drop-key (string-append "k" (number->string (- bench-top-key-count 1))))
(define bench-set-value 999999)
(define bench-push-index (quotient bench-array-length 2))
(define bench-push-index bench-array-length)
(define bench-append-drop-index bench-array-length)
(define bench-push-value 777777)

(define bench-json-scm (ljson-string->json bench-json))
Expand Down Expand Up @@ -191,7 +192,7 @@
(njson-free h))
(ljson-push bench-json-scm "nums" bench-push-index bench-push-value)
(let ((h (string->njson bench-json)))
(let ((x (njson-push h "nums" bench-push-index bench-push-value)))
(let ((x (njson-append h "nums" bench-push-value)))
(njson-free x))
(njson-free h))
(ljson-drop bench-json-scm bench-drop-key)
Expand All @@ -203,8 +204,8 @@
(njson-set! h bench-ref-key bench-set-value)
(njson-free h))
(let ((h (string->njson bench-json)))
(njson-push! h "nums" bench-push-index bench-push-value)
(njson-drop! h "nums" bench-push-index)
(njson-append! h "nums" bench-push-value)
(njson-drop! h "nums" bench-append-drop-index)
(njson-free h))
(let ((h (string->njson bench-json)))
(njson-set! h bench-drop-key 1)
Expand Down Expand Up @@ -273,10 +274,10 @@
(lambda () (ljson-push bench-json-scm "nums" bench-push-index bench-push-value))
push-count
round-count))
(define njson-push-ns
(define njson-append-ns
(bench-ns-median
(lambda ()
(let ((h (njson-push push-handle "nums" bench-push-index bench-push-value)))
(let ((h (njson-append push-handle "nums" bench-push-value)))
(njson-free h)))
push-count
round-count))
Expand Down Expand Up @@ -306,12 +307,12 @@

(define push-x-handle (string->njson bench-json))
(njson-keys push-x-handle)
(define njson-push!-pair-ns
(define njson-append!-pair-ns
(bench-ns-median
(lambda ()
(njson-push! push-x-handle "nums" bench-push-index bench-push-value)
(njson-append! push-x-handle "nums" bench-push-value)
;; restore array shape to keep each iteration comparable
(njson-drop! push-x-handle "nums" bench-push-index))
(njson-drop! push-x-handle "nums" bench-append-drop-index))
push-count
round-count))
(check-true (njson-free push-x-handle))
Expand Down Expand Up @@ -357,11 +358,11 @@
(check-true (>= liii-set-ns 0))
(check-true (>= njson-set-ns 0))
(check-true (>= liii-push-ns 0))
(check-true (>= njson-push-ns 0))
(check-true (>= njson-append-ns 0))
(check-true (>= liii-drop-ns 0))
(check-true (>= njson-drop-ns 0))
(check-true (>= njson-set!-ns 0))
(check-true (>= njson-push!-pair-ns 0))
(check-true (>= njson-append!-pair-ns 0))
(check-true (>= njson-drop!-pair-ns 0))
(check-true (>= liii-contains-key-ns 0))
(check-true (>= njson-contains-key-ns 0))
Expand All @@ -378,7 +379,7 @@
(report-bench "序列化(json->string)" stringify-count round-count liii-stringify-ns njson-stringify-ns)
(report-bench "读取(json-ref)" ref-count round-count liii-ref-ns njson-ref-ns)
(report-bench "修改(json-set)" set-count round-count liii-set-ns njson-set-ns)
(report-bench "插入(json-push)" push-count round-count liii-push-ns njson-push-ns)
(report-bench "插入(json-push vs njson-append)" push-count round-count liii-push-ns njson-append-ns)
(report-bench "删除(json-drop)" drop-count round-count liii-drop-ns njson-drop-ns)
(report-variant-bench "原地修改对比(liii-set vs njson-set!)"
set-count
Expand All @@ -387,13 +388,13 @@
liii-set-ns
"njson-set!"
njson-set!-ns)
(report-variant-bench "原地插入对比(liii-push vs njson-push!+drop!)"
(report-variant-bench "原地插入对比(liii-push vs njson-append!+drop!)"
push-count
round-count
"liii-push"
liii-push-ns
"njson-push!+drop!"
njson-push!-pair-ns)
"njson-append!+drop!"
njson-append!-pair-ns)
(report-variant-bench "原地删除对比(liii-drop vs njson-set!+drop!)"
drop-count
round-count
Expand Down
85 changes: 85 additions & 0 deletions devel/214_9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# [214_9]

## 任务相关的代码文件
- bench/njson-bench.scm
- goldfish/liii/njson.scm
- src/goldfish.hpp
- tests/goldfish/liii/njson-test.scm
- devel/214_9.md

## 如何测试
```bash
xmake b goldfish
bin/goldfish tests/goldfish/liii/njson-test.scm
bin/goldfish bench/njson-bench.scm
```

## 2026/2/28 修改njson相关api的行为逻辑

### What
1. 更新接口命名与职责边界:
- 移除 `njson-push` / `njson-push!` 对外导出;
- 新增并保留 `njson-append` / `njson-append!`;
- `append` 仅用于“数组尾部追加”,不再复用 `set` 做插入语义。
2. 调整 `set` 行为:
- object:键存在覆盖,键不存在新建(upsert);
- array:仅允许 `idx < size` 覆盖,`idx >= size` 统一报错。
3. 路径相关错误统一收敛到 `key-error`:
- `njson-ref` 路径缺失抛 `key-error`;
- `njson-drop` / `njson-drop!` 删除目标不存在时抛 `key-error`;
5. 测试与注释文档大幅补充:
- `njson-test.scm` 对各 API 增加“行为逻辑/返回值/错误类型”说明;
- 增加 `capture-key-error-message`,对错误消息内容做精确断言;
- 增强已释放句柄、非法路径、越界、缺参等边界覆盖。
6. 基准脚本同步迁移:
- `bench/njson-bench.scm` 中 `push` 对比路径改为 `append`/`append!` 相关命名与调用。

### Why
1. 设计参考了 Python 的容器语义,目标是让接口行为更直觉、可预测:
- `dict` 赋值(`d[k] = v`)天然是 upsert,对应本次 `set/set!` 在 object 上“存在覆盖、不存在新建”;
- `list` 赋值(`a[i] = v`)要求索引已存在,不负责追加,对应本次 `set/set!` 在 array 上 `idx >= size` 报错;
- `list.append(v)` 与“按索引写入”是两个独立动作,对应本次将追加语义明确放到 `append/append!`;
- 删除或访问不存在路径在 Python 中会抛异常(如 `KeyError/IndexError`),对应本次统一为可捕获的 `key-error`。
2. 之前同属“路径问题”的报错分散在 `type-error/value-error/空返回`,调用方很难写稳定的统一处理逻辑。
3. `set` 与“数组追加/插入”语义混用后,边界不清晰,容易出现“看起来成功、实际语义错误”的调用。
4. 需要让调用方在失败时拿到可判定错误类型和可定位错误消息,降低线上排查与日志解析成本。
5. 现有测试更多是“是否通过”检查,缺少“报错类型+报错内容是否准确”的断言,因此本次同步加强。

### How
1. C++ 路径查找逻辑重构为统一 core:
- 引入单一 `njson_lookup_core`(路径缺失直接报错);
- 保留语义清晰的 wrapper(`lookup_path_const` / `lookup_path_mutable` / `lookup_path_parent_mutable`)。
2. 更新 `njson_update_op`:
- `push` 分支替换为 `append`;
- `njson_parse_update_request` 对 `append` 使用 `expected (json [key ...] value)` 规则。
3. `njson_apply_update_on_root` 行为调整:
- `append` 分支只允许目标为 array,调用 `push_back`;
- `drop` 对缺失 object key、数组越界、非容器删除均返回明确 `key-error`;
- `set` 在数组越界时返回统一越界错误信息。
4. 在 `njson_run_update` 增加单参数 `append` 判别逻辑:
- 新增 `njson_maybe_raise_append_single_arg_error`;
- 针对“根是 object 且仅一个尾参”优先判断路径是否存在,再给出更准确错误。
5. Scheme 层(`njson.scm`)同步导出与包装:
- 新增 `njson-append` / `njson-append!`;
- 统一参数缺失时抛 `key-error`。
6. 测试层完成配套迁移与扩展:
- 全面替换 `push/push!` 用例;
- 增加 `ref/set/append/drop/contains-key?` 多场景错误断言;
- 将 `njson-schema-report`、文件 I/O、格式化、互转等章节补齐行为文档。
7. 基准层同步到新语义:
- `bench/njson-bench.scm` 的插入路径改为 `njson-append/njson-append!`;
- 避免继续使用 `set` 触发 `idx == size` 越界,从而让基准脚本与新 API 语义一致。

### 兼容性说明
1. `njson-push` / `njson-push!` 不再可用,调用方需迁移到 `njson-append` / `njson-append!`。
2. `njson-ref` 路径不存在时不再返回空值,改为抛 `key-error`。
3. `njson-drop` / `njson-drop!` 目标不存在时不再 no-op,改为抛 `key-error`。
4. `njson-set` / `njson-set!` 路径不存在时会抛 `key-error`。
5. `njson-set` / `njson-set!` 在 array 上 `idx >= size` 将报错;追加请使用 `append` 系列。
6. `njson-contains-key?` 的 key 非字符串场景错误类型变更为 `key-error`。

## 验证结果
本次文档编写前已执行:
1. ✅ `xmake b goldfish` 通过。
2. ✅ `bin/goldfish tests/goldfish/liii/njson-test.scm` 通过(`; *** checks *** : 296 correct, 0 failed.`)。
3. ✅ `bin/goldfish bench/njson-bench.scm` 通过(`; *** checks *** : 29 correct, 0 failed.`)。
36 changes: 20 additions & 16 deletions goldfish/liii/njson.scm
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
let-njson
njson-ref
njson-set
njson-append
njson-set!
njson-push
njson-push!
njson-append!
njson-drop
njson-drop!
njson-contains-key?
Expand Down Expand Up @@ -220,6 +220,16 @@
(type-error "njson-set: json must be njson-handle" json))
(apply g_njson-set (cons json (cons key (cons val keys)))))

;; Append value to target array:
;; (njson-append j value) ; root must be array
;; (njson-append j k1 k2 ... kn value) ; target path must be array
(define (njson-append json . args)
(unless (njson? json)
(type-error "njson-append: json must be njson-handle" json))
(when (null? args)
(key-error "njson-append: expected (json [key ...] value)" json))
(apply g_njson-append (cons json args)))

;; In-place update style:
;; (njson-set! j key value)
;; (njson-set! j k1 k2 ... kn value)
Expand All @@ -228,21 +238,15 @@
(type-error "njson-set!: json must be njson-handle" json))
(apply g_njson-set! (cons json (cons key (cons val keys)))))

;; Same calling style as (liii json):
;; (njson-push j key value)
;; (njson-push j k1 k2 ... kn value)
(define (njson-push json key val . keys)
(unless (njson? json)
(type-error "njson-push: json must be njson-handle" json))
(apply g_njson-push (cons json (cons key (cons val keys)))))

;; In-place update style:
;; (njson-push! j key value)
;; (njson-push! j k1 k2 ... kn value)
(define (njson-push! json key val . keys)
;; Append value to target array in place:
;; (njson-append! j value) ; root must be array
;; (njson-append! j k1 k2 ... kn value) ; target path must be array
(define (njson-append! json . args)
(unless (njson? json)
(type-error "njson-push!: json must be njson-handle" json))
(apply g_njson-push! (cons json (cons key (cons val keys)))))
(type-error "njson-append!: json must be njson-handle" json))
(when (null? args)
(key-error "njson-append!: expected (json [key ...] value)" json))
(apply g_njson-append! (cons json args)))

(define (njson-drop json key . keys)
(unless (njson? json)
Expand Down
Loading