Skip to content

Commit 16c3ae8

Browse files
committed
fix(decode): handle bare list marker as empty object
1 parent 70140a8 commit 16c3ae8

File tree

4 files changed

+105
-90
lines changed

4 files changed

+105
-90
lines changed

src/com/vadelabs/toon/decode/arrays.cljc

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@
33
44
Handles inline, tabular, and list array formats."
55
(:require
6-
[clojure.string :as str]
7-
[com.vadelabs.toon.constants :as const]
8-
[com.vadelabs.toon.decode.parser :as parser]
9-
[com.vadelabs.toon.decode.scanner :as scanner]
10-
[com.vadelabs.toon.utils :as str-utils]))
11-
6+
[clojure.string :as str]
7+
[com.vadelabs.toon.constants :as const]
8+
[com.vadelabs.toon.decode.parser :as parser]
9+
[com.vadelabs.toon.decode.scanner :as scanner]
10+
[com.vadelabs.toon.utils :as str-utils]))
1211

1312
;; Forward declaration for mutual recursion
1413
(declare list-item)
1514

16-
1715
;; ============================================================================
1816
;; Validation Helpers
1917
;; ============================================================================
@@ -48,7 +46,6 @@
4846
:actual actual
4947
:suggestion (str "Update header to " header-fix " or add " (- expected actual) " more" type-suffix)})))))
5048

51-
5249
;; ============================================================================
5350
;; Inline Primitive Array Decoding
5451
;; ============================================================================
@@ -77,7 +74,6 @@
7774
(validate-array-length! length (count values) strict :inline)
7875
values))))
7976

80-
8177
;; ============================================================================
8278
;; Tabular Array Decoding
8379
;; ============================================================================
@@ -96,7 +92,6 @@
9692
(let [tokens (parser/delimited-values row-content delimiter)]
9793
(mapv #(parser/primitive-token % strict) tokens)))
9894

99-
10095
(defn- analyze-line-positions
10196
"Analyzes positions of colon and delimiter in a line.
10297
@@ -111,7 +106,6 @@
111106
{:colon-pos (str-utils/unquoted-char content \:)
112107
:delim-pos (str-utils/unquoted-char content delim-char)}))
113108

114-
115109
(defn- delimiter-before-colon?
116110
"Checks if delimiter appears before colon in position analysis.
117111
@@ -123,7 +117,6 @@
123117
[{:keys [colon-pos delim-pos]}]
124118
(and colon-pos delim-pos (< delim-pos colon-pos)))
125119

126-
127120
(defn- peek-next-line-has-delimiter-first?
128121
"Checks if next line at same depth has delimiter before colon.
129122
@@ -139,7 +132,6 @@
139132
(delimiter-before-colon? (analyze-line-positions (:content next-line) delimiter))
140133
false))
141134

142-
143135
(defn- row-or-key-value?
144136
"Determines if a line is a data row or key-value line.
145137
@@ -186,7 +178,6 @@
186178
:else
187179
:key-value))))
188180

189-
190181
(defn tabular-array
191182
"Decodes a tabular array into objects.
192183
@@ -235,7 +226,6 @@
235226
(conj objects obj)
236227
(inc row-count))))))))))
237228

238-
239229
;; ============================================================================
240230
;; List Array Decoding
241231
;; ============================================================================
@@ -272,8 +262,9 @@
272262
(do
273263
(validate-array-length! length item-count strict :list)
274264
[items remaining-cursor])
275-
;; Check if line starts with list marker
276-
(if-not (str/starts-with? (:content line) const/list-item-prefix)
265+
;; Check if line starts with list marker (prefix "- " or bare "-")
266+
(if-not (or (str/starts-with? (:content line) const/list-item-prefix)
267+
(= (:content line) const/list-item-marker))
277268
;; No list marker: end of list
278269
(do
279270
(validate-array-length! length item-count strict :list)
Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
(ns com.vadelabs.toon.decode.items
22
"List item type detection and decoding for TOON format."
33
(:require
4-
[clojure.string :as str]
5-
[com.vadelabs.toon.constants :as const]
6-
[com.vadelabs.toon.decode.arrays :as arrays]
7-
[com.vadelabs.toon.decode.objects :as objects]
8-
[com.vadelabs.toon.decode.parser :as parser]
9-
[com.vadelabs.toon.decode.scanner :as scanner]
10-
[com.vadelabs.toon.utils :as str-utils]))
11-
4+
[clojure.string :as str]
5+
[com.vadelabs.toon.constants :as const]
6+
[com.vadelabs.toon.decode.arrays :as arrays]
7+
[com.vadelabs.toon.decode.objects :as objects]
8+
[com.vadelabs.toon.decode.parser :as parser]
9+
[com.vadelabs.toon.decode.scanner :as scanner]
10+
[com.vadelabs.toon.utils :as str-utils]))
1211

1312
;; ============================================================================
1413
;; List Item Type Detection
@@ -28,7 +27,6 @@
2827
:else
2928
:primitive))
3029

31-
3230
;; ============================================================================
3331
;; List Item Decoding
3432
;; ============================================================================
@@ -46,24 +44,27 @@
4644
Returns:
4745
[decoded-value, new-cursor]"
4846
[line cursor depth delimiter strict]
49-
(let [content (:content line)
50-
;; Remove list marker prefix ("- ")
51-
after-marker (subs content (count const/list-item-prefix))
52-
item-type (list-item-type after-marker)]
53-
(case item-type
54-
:array
55-
;; Inline array: "- [3]: a,b,c"
56-
(let [header-info (parser/array-header-line after-marker)
57-
items (arrays/inline-primitive-array header-info strict)
58-
new-cursor (scanner/advance-cursor cursor)]
59-
[items new-cursor])
47+
(let [content (:content line)]
48+
;; Bare list marker: always an empty object
49+
(if (= content const/list-item-marker)
50+
[{} (scanner/advance-cursor cursor)]
51+
;; Normal list item with content after "- "
52+
(let [after-marker (subs content (count const/list-item-prefix))
53+
item-type (list-item-type after-marker)]
54+
(case item-type
55+
:array
56+
;; Inline array: "- [3]: a,b,c"
57+
(let [header-info (parser/array-header-line after-marker)
58+
items (arrays/inline-primitive-array header-info strict)
59+
new-cursor (scanner/advance-cursor cursor)]
60+
[items new-cursor])
6061

61-
:object
62-
;; Object with first field on hyphen line
63-
(objects/object-from-list-item line cursor depth delimiter strict list-item)
62+
:object
63+
;; Object with first field on hyphen line
64+
(objects/object-from-list-item line cursor depth delimiter strict list-item)
6465

65-
:primitive
66-
;; Simple primitive value
67-
(let [value (parser/primitive-token after-marker strict)
68-
new-cursor (scanner/advance-cursor cursor)]
69-
[value new-cursor]))))
66+
:primitive
67+
;; Simple primitive value
68+
(let [value (parser/primitive-token after-marker strict)
69+
new-cursor (scanner/advance-cursor cursor)]
70+
[value new-cursor]))))))

test/com/vadelabs/toon/decode/items_test.cljc

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
(ns com.vadelabs.toon.decode.items-test
22
"Tests for list item type detection and decoding."
33
(:require
4-
#?(:clj [clojure.test :refer [deftest is testing]]
5-
:cljs [cljs.test :refer [deftest is testing]])
6-
[com.vadelabs.toon.decode.items :as items]
7-
[com.vadelabs.toon.decode.scanner :as scanner]))
8-
4+
#?(:clj [clojure.test :refer [deftest is testing]]
5+
:cljs [cljs.test :refer [deftest is testing]])
6+
[com.vadelabs.toon.decode.items :as items]
7+
[com.vadelabs.toon.decode.scanner :as scanner]))
98

109
;; ============================================================================
1110
;; List Item Type Detection Tests
@@ -26,7 +25,6 @@
2625
(is (= :primitive (#'items/list-item-type "42")))
2726
(is (= :primitive (#'items/list-item-type "true"))))))
2827

29-
3028
;; ============================================================================
3129
;; List Item Decoding Tests
3230
;; ============================================================================
@@ -40,7 +38,6 @@
4038
[result _] (items/list-item line cursor 0 "," true)]
4139
(is (= "hello" result)))))
4240

43-
4441
(deftest list-item-array-test
4542
(testing "Decode array list item"
4643
(let [input "- [3]: 1,2,3"
@@ -50,7 +47,6 @@
5047
[result _] (items/list-item line cursor 0 "," true)]
5148
(is (= [1.0 2.0 3.0] result)))))
5249

53-
5450
(deftest list-item-object-test
5551
(testing "Decode object list item"
5652
(let [input "- id: 1\n name: Alice"
@@ -60,7 +56,6 @@
6056
[result _] (items/list-item line cursor 0 "," true)]
6157
(is (= {"id" 1.0 "name" "Alice"} result)))))
6258

63-
6459
(deftest list-item-multiline-object-test
6560
(testing "Decode multi-line object list item"
6661
(let [input "- name: Bob\n age: 25\n active: true"
@@ -69,3 +64,26 @@
6964
[line _] (scanner/next-cursor cursor)
7065
[result _] (items/list-item line cursor 0 "," true)]
7166
(is (= {"name" "Bob" "age" 25.0 "active" true} result)))))
67+
68+
;; ============================================================================
69+
;; Bare List Item Tests
70+
;; ============================================================================
71+
72+
(deftest bare-list-item-test
73+
(testing "Bare list marker decodes to empty object"
74+
(let [input "-"
75+
scan-result (scanner/to-parsed-lines input)
76+
cursor (scanner/cursor-from-scan-result scan-result)
77+
[line _] (scanner/next-cursor cursor)
78+
[result _] (items/list-item line cursor 0 "," true)]
79+
(is (= {} result)))))
80+
81+
(deftest bare-list-item-with-trailing-space-test
82+
(testing "Bare list marker with trailing space decodes to empty object"
83+
;; Note: scanner trims content, so "- " becomes "-"
84+
(let [input "- "
85+
scan-result (scanner/to-parsed-lines input)
86+
cursor (scanner/cursor-from-scan-result scan-result)
87+
[line _] (scanner/next-cursor cursor)
88+
[result _] (items/list-item line cursor 0 "," true)]
89+
(is (= {} result)))))

0 commit comments

Comments
 (0)