Skip to content

Commit 35ac90b

Browse files
committed
More robust unevaluated* handling
1 parent f3f3a3e commit 35ac90b

File tree

4 files changed

+90
-164
lines changed

4 files changed

+90
-164
lines changed

sitelib/text/json/schema/validators/api.scm

Lines changed: 47 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,8 @@
7878
validator-context-lint-mode?
7979
validator-context:add-path!
8080
validator-context:detatch-report!
81-
validator-context:marks
82-
validator-context:mark!
8381
validator-context:mark-element!
84-
validator-context:update-difference!
85-
validator-context:marked?
82+
validator-context:update-marks!
8683
validator-context:marked-element?
8784
validator-context:unevaluated?
8885
validator-context:set-dynamic-context!
@@ -102,6 +99,7 @@
10299
(srfi :13 strings)
103100
(srfi :39 parameters)
104101
(srfi :117 list-queues)
102+
(srfi :126 hashtables)
105103
(text json pointer)
106104
(text json schema version))
107105

@@ -338,7 +336,6 @@
338336
(fields path
339337
parent
340338
reports
341-
marks
342339
dynamic-contexts
343340
evaluating-scopes ;; schema-ids
344341
evaluating-schemas ;; schema
@@ -347,8 +344,6 @@
347344
(make-raw-validator-context
348345
"/" #f
349346
(list-queue)
350-
;; relay on the fact that JSON must not have duplicate keys
351-
(make-hashtable equal-hash equal?)
352347
(make-hashtable equal-hash equal?)
353348
(list-queue)
354349
(list-queue)
@@ -367,7 +362,6 @@
367362
(build-validation-path base path)
368363
context
369364
(validator-context-reports context)
370-
(validator-context-marks context)
371365
(validator-context-dynamic-contexts context)
372366
(validator-context-evaluating-scopes context)
373367
(validator-context-evaluating-schemas context)
@@ -378,7 +372,6 @@
378372
(validator-context-path context)
379373
context
380374
(list-queue)
381-
(validator-context-marks context)
382375
(validator-context-dynamic-contexts context)
383376
(validator-context-evaluating-scopes context)
384377
(validator-context-evaluating-schemas context)
@@ -399,94 +392,43 @@
399392
(define (validator-context:reports context)
400393
(list-queue-list (validator-context-reports context)))
401394

402-
(define (validator-context:mark! context obj schema)
403-
;; (newline)
404-
;; (display obj) (newline)
405-
;; (display (schema-context-schema schema)) (newline)
406-
;; (for-each (lambda (s) (display "--> ") (display s) (newline))
407-
;; (list-queue-list (validator-context-evaluating-schemas context)))
408-
(let ((mark (validator-context-marks context)))
409-
(hashtable-update! mark obj
410-
(lambda (v)
411-
(cond ((assq schema v) v)
412-
(else (cons (cons schema (list-queue)) v))))
413-
'())))
414395
(define (validator-context:mark-element! context obj element schema success?)
415-
(let ((mark (validator-context-marks context)))
416-
(hashtable-update! mark obj
417-
(lambda (v)
418-
(cond ((assq schema v) =>
419-
(lambda (slot)
420-
(list-queue-add-front! (cdr slot) (cons element success?)))))
421-
v)
422-
'())
423-
success?))
424-
425-
(define (validator-context:update-difference! context obj snapshot success?)
426-
(define (swap-marks! q base diff)
427-
(list-queue-clear! q)
428-
(for-each (lambda (v) (list-queue-add-back! q v)) base)
429-
;; strip out failed validation
430-
;; NB: this is for unevaludated with `not not` case
431-
;; I think it should be invalid test case, but it's listed
432-
;; in the official test suite, so no argue.
433-
(for-each (lambda (v)
434-
(when success?
435-
(set-cdr! v #t)
436-
(list-queue-add-back! q v))) diff))
437-
(let ((slots (hashtable-ref (validator-context-marks context) obj '())))
438-
(for-each (lambda (slot)
439-
(let ((q (cdr slot)))
440-
(cond ((memq (car slot) snapshot) =>
441-
(lambda (base)
442-
(let* ((marks (list-queue-list q))
443-
(diff (drop-right marks (length base))))
444-
(swap-marks! q base diff))))
445-
(else
446-
(swap-marks! q '() (list-queue-list q))))))
447-
slots)
396+
(define target-schema (schema-context-schema schema))
397+
(let-values (((schema marks) (validator-context:evaluating-schema context)))
398+
(when (eq? target-schema schema)
399+
(let ((v (cons* schema element success?)))
400+
(hashtable-update! marks obj (lambda (r) (cons v r)) '()))))
401+
success?)
402+
403+
(define (validator-context:update-marks! context obj schema success?)
404+
(define target-schema (schema-context-schema schema))
405+
(define (flip-result v)
406+
(filter-map (lambda (s)
407+
(cond ((eq? target-schema (car s))
408+
(cond (success? (set-cdr! (cdr s) success?) s)
409+
(else #f)))
410+
(else s))) v))
411+
(let-values (((schema marks) (validator-context:evaluating-schema context)))
412+
(hashtable-update! marks obj flip-result '())
448413
success?))
449414

450-
(define (validator-context:marks context obj)
451-
(define (->snapshot slot)
452-
;; convert (schema (e result) ...)
453-
(cons (car slot) (list-queue-list (cdr slot))))
454-
455-
(let ((mark (validator-context-marks context)))
456-
(map ->snapshot (hashtable-ref mark obj '()))))
457-
458-
(define (validator-context:marked? context obj schema-context)
459-
(let ((slots (hashtable-ref (validator-context-marks context) obj '())))
460-
(cond ((assq schema-context slots))
461-
(else #f))))
462-
463415
(define (validator-context:marked-element? context obj element schema-context)
464-
(let ((slots (hashtable-ref (validator-context-marks context) obj '())))
465-
(cond ((assq schema-context slots) =>
466-
(lambda (slot) (assoc element (list-queue-list (cdr slot)))))
467-
(else #f))))
416+
(define target-schema (schema-context-schema schema-context))
417+
(let-values (((schema marks) (validator-context:evaluating-schema context)))
418+
(let ((slots (hashtable-ref marks obj '())))
419+
(cond ((find (lambda (v)
420+
(and (eq? (car v) target-schema) (equal? (cadr v) element)))
421+
slots) #t)
422+
(else #f)))))
468423

469424
(define (validator-context:unevaluated? context obj element schema-context)
470-
(define (collect element elements)
471-
(define (check r element v) (if (equal? (car v) element) (cons v r) r))
472-
(do ((elements elements (cdr elements))
473-
(r '() (check r element (car elements))))
474-
((null? elements) r)))
475-
476-
(define (check slot)
477-
(let ((marked-context (car slot)))
478-
(or (not (schema-context:same-root? schema-context marked-context))
479-
;; We need to exclude cousins, so check subschema
480-
(subschema? (schema-context-schema schema-context)
481-
(schema-context-schema marked-context)))))
482-
483-
(let ((e* (append-map (lambda (s) (list-queue-list (cdr s)))
484-
(filter check
485-
(hashtable-ref (validator-context-marks context) obj '())))))
486-
;; because of allOf, anyOf or oneOf applicators, the elements may contain
487-
;; multiple of the same element. So, collect everything and check if
488-
;; there's a successful evaluation or not
489-
(not (null? (filter-map cdr (collect element e*))))))
425+
(let-values (((schema marks) (validator-context:evaluating-schema context)))
426+
(and (eq? schema (schema-context-schema schema-context))
427+
(cond ((hashtable-ref marks obj #f) =>
428+
(lambda (v*)
429+
(find (lambda (v)
430+
(and (cddr v) (equal? (cadr v) element))) v*)))
431+
(else #f)))))
490432

491433
(define (validator-context:set-dynamic-context! context schema-context anchor)
492434
(let ((root (schema-context-root schema-context))
@@ -520,15 +462,26 @@
520462

521463
(define (validator-context:push-schema! context schema)
522464
(let ((queue (validator-context-evaluating-schemas context)))
523-
(list-queue-add-front! queue schema)))
465+
(list-queue-add-front! queue (cons schema (make-eq-hashtable)))))
524466

525467
(define (validator-context:pop-scope! context)
526468
(let ((queue (validator-context-evaluating-scopes context)))
527469
(list-queue-remove-front! queue)))
528470

529471
(define (validator-context:pop-schema! context)
530-
(let ((queue (validator-context-evaluating-schemas context)))
531-
(list-queue-remove-front! queue)))
472+
(let* ((queue (validator-context-evaluating-schemas context))
473+
(e (list-queue-remove-front! queue)))
474+
(unless (list-queue-empty? queue)
475+
(let ((ht (cdr (list-queue-front queue))))
476+
(hashtable-walk (cdr e)
477+
;; k = obj, v = (schema . element)
478+
;; it's a bit redundant, but keep it like this for my convenience
479+
(lambda (k v)
480+
(hashtable-update! ht k (lambda (r) (append v r)) '())))))))
481+
482+
(define (validator-context:evaluating-schema context)
483+
(let ((v (list-queue-front (validator-context-evaluating-schemas context))))
484+
(values (car v) (cdr v))))
532485

533486
;; Schema validator
534487
(define (update-cache! context validator)
@@ -702,24 +655,6 @@
702655
((eq? scope-context child-context))
703656
(else (in-scope? scope-context (schema-context-parent child-context)))))
704657

705-
(define (subschema? root-schema schema)
706-
(and (subschema-depth root-schema schema) #t))
707-
(define (subschema-depth root-schema schema)
708-
(define (rec root-schema schema depth)
709-
;; we don't modify the schema, so `eq?` works.
710-
(cond ((eq? root-schema schema) depth)
711-
((vector? root-schema)
712-
;; For now DFS, might be better to do BFS
713-
(let ((len (vector-length root-schema)))
714-
(let loop ((i 0))
715-
(cond ((= i len) #f)
716-
((rec (cdr (vector-ref root-schema i)) schema (+ depth 1)))
717-
(else (loop (+ i 1)))))))
718-
((list? root-schema)
719-
(exists (lambda (r) (rec r schema (+ depth 1))) root-schema))
720-
(else #f)))
721-
(rec root-schema schema 0))
722-
723658
(define (build-schema-path base child) (cons child base))
724659
(define (boolean->validator b) (lambda (e ctx) b))
725660

sitelib/text/json/schema/validators/array.scm

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@
7575
value)))
7676
(lambda (e ctx)
7777
(or (not (list? e))
78-
(and (validator-context:mark! ctx e context)
79-
(validate validators e ctx context))))))
78+
(validate validators e ctx context)))))
8079

8180
(define prefix-items-pointer (json-pointer "/prefixItems"))
8281
(define (json-schema:items value context schema-path)
@@ -99,33 +98,30 @@
9998
;; additionalItems and unevaluatedItems
10099
(define (handle-extra-items who value context schema-path pred)
101100
(define (filter ctx context o)
102-
(if (validator-context:marked? ctx o context)
103-
(let loop ((i 0) (e o) (r '()))
104-
(cond ((null? e) (reverse! r))
105-
((pred ctx o (cons i (car e)) context)
106-
(loop (+ i 1) (cdr e) r))
107-
(else
108-
(loop (+ i 1) (cdr e) (cons (cons i (car e)) r)))))
109-
'()))
101+
(let loop ((i 0) (e o) (r '()))
102+
(cond ((null? e) (reverse! r))
103+
((pred ctx o (cons i (car e)) context)
104+
(loop (+ i 1) (cdr e) r))
105+
(else
106+
(loop (+ i 1) (cdr e) (cons (cons i (car e)) r))))))
110107

111108
(unless (json-schema? value)
112109
(assertion-violation who "JSON Schema is required" value))
113110
(let ((validator (schema->core-validator value context schema-path)))
114111
(lambda (e ctx)
115112
(define lint-mode? (validator-context-lint-mode? ctx))
116113
(or (not (list? e))
117-
(and (validator-context:mark! ctx e context)
118-
(if lint-mode?
119-
(fold-left
120-
(lambda (acc v)
121-
(and (validator-context:mark-element!
122-
ctx e v context (validator (cdr v) ctx))
123-
acc))
124-
#t (filter ctx context e))
125-
(for-all
126-
(lambda (v)
127-
(validator-context:mark-element! ctx e v context
128-
(validator (cdr v) ctx))) (filter ctx context e))))))))
114+
(if lint-mode?
115+
(fold-left
116+
(lambda (acc v)
117+
(and (validator-context:mark-element!
118+
ctx e v context (validator (cdr v) ctx))
119+
acc))
120+
#t (filter ctx context e))
121+
(for-all
122+
(lambda (v)
123+
(validator-context:mark-element! ctx e v context
124+
(validator (cdr v) ctx))) (filter ctx context e)))))))
129125

130126
(define items-pointer (json-pointer "/items"))
131127
(define (json-schema:additional-items value context schema-path)
@@ -167,7 +163,6 @@
167163
(define (count validator e ctx)
168164
(length (filter-map (lambda (v) (validator v ctx)) e)))
169165
(define (count/mark validator e ctx)
170-
(validator-context:mark! ctx e context)
171166
(let loop ((i 0) (n 0) (v e))
172167
(if (null? v)
173168
n
@@ -218,8 +213,7 @@
218213
(let ((validator (schema->core-validator value context path)))
219214
(lambda (e ctx)
220215
(or (not (list? e))
221-
(and (validator-context:mark! ctx e context)
222-
(mark-all ctx e context (validate e ctx validator)))))))
216+
(mark-all ctx e context (validate e ctx validator))))))
223217
((and (list? value) (for-all json-schema? value))
224218
(items-handler value context path))
225219
(else (assertion-violation 'json-schema:items

sitelib/text/json/schema/validators/logics.scm

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,14 @@
8282
(define (json-schema:not value context schema-path)
8383
(unless (or (json-schema? value))
8484
(assertion-violation 'json-schema:not "JSON Schema is required" value))
85-
(let ((validator (schema-validator->core-validator
86-
(schema-context->schema-validator
87-
(make-schema-context value context) schema-path))))
85+
(let* ((schema-context (make-schema-context value context))
86+
(validator (schema-validator->core-validator
87+
(schema-context->schema-validator
88+
schema-context schema-path))))
8889
(core-validator->reporting-validator
8990
(lambda (e ctx)
90-
(let ((snapshot (validator-context:marks ctx e)))
91-
(validator-context:update-difference! ctx e snapshot
92-
(not (validator e ctx)))))
91+
(validator-context:update-marks! ctx e schema-context
92+
(not (validator e ctx))))
9393
schema-path)))
9494

9595
;; if-then-else is handled a bit differently from the other keywords.

sitelib/text/json/schema/validators/object.scm

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,13 @@
8686
(lambda (e ctx)
8787
(define lint-mode? (validator-context-lint-mode? ctx))
8888
(or (not (vector? e))
89-
(and (validator-context:mark! ctx e context)
90-
(if lint-mode?
91-
(vector-fold
92-
(lambda (acc v)
93-
(and (check-entry e context v ctx properties) acc)) #t e)
94-
(vector-every
95-
(lambda (v) (check-entry e context v ctx properties))
96-
e)))))))
89+
(if lint-mode?
90+
(vector-fold
91+
(lambda (acc v)
92+
(and (check-entry e context v ctx properties) acc)) #t e)
93+
(vector-every
94+
(lambda (v) (check-entry e context v ctx properties))
95+
e))))))
9796
(define json-schema:properties (properties-handler "properties" #f))
9897
(define json-schema:pattern-properties
9998
(properties-handler "patternProperties" #t))
@@ -134,19 +133,17 @@
134133
(lambda (e ctx)
135134
(define lint-mode? (validator-context-lint-mode? ctx))
136135
(or (not (vector? e))
137-
(and (validator-context:mark! ctx e context)
138-
(if lint-mode?
139-
(fold-left
140-
(lambda (acc v)
141-
(validator-context:mark-element! ctx e v context
142-
(validator (cdr v)
143-
(validator-context:add-path! ctx (car v)))))
144-
#t (filter-marked-items ctx context e))
145-
(for-all (lambda (v)
146-
(validator-context:mark-element! ctx e v context
147-
(validator (cdr v)
148-
(validator-context:add-path! ctx (car v)))))
149-
(filter-marked-items ctx context e))))))))
136+
(if lint-mode?
137+
(fold-left
138+
(lambda (acc v)
139+
(validator-context:mark-element! ctx e v context
140+
(validator (cdr v) (validator-context:add-path! ctx (car v)))))
141+
#t (filter-marked-items ctx context e))
142+
(for-all (lambda (v)
143+
(validator-context:mark-element! ctx e v context
144+
(validator (cdr v)
145+
(validator-context:add-path! ctx (car v)))))
146+
(filter-marked-items ctx context e)))))))
150147

151148
(define (json-schema:additional-properties value context schema-path)
152149
(handle-extras 'json-schema:additional-properties

0 commit comments

Comments
 (0)