Skip to content

Commit 734b4ec

Browse files
committed
Break out tests into their own files.
1 parent 6caf706 commit 734b4ec

File tree

5 files changed

+146
-20
lines changed

5 files changed

+146
-20
lines changed

src/jsony.nim

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import macros, strutils
1+
import macros, strutils, tables, unicode
22

33
type JsonError = object of ValueError
44

@@ -7,9 +7,9 @@ const whiteSpace = {' ', '\n', '\t', '\r'}
77
proc parseJson[T](s: string, i: var int, v: var seq[T])
88
proc parseJson[T:enum](s: string, i: var int, v: var T)
99
proc parseJson[T:object|ref object](s: string, i: var int, v: var T)
10+
proc parseJson[T](s: string, i: var int, v: var Table[string, T])
1011

11-
12-
template error(msg: string) =
12+
template error(msg: string, i: int) =
1313
## Short cut to raise an exception.
1414
raise newException(JsonError, msg)
1515

@@ -28,11 +28,11 @@ proc eat(s: string, i: var int, c: char) =
2828
## Will raise an exception if `c` is not found.
2929
eatSpace(s, i)
3030
if i >= s.len:
31-
error("Expected " & c & " but end reached.")
31+
error("Expected " & c & " but end reached.", i)
3232
if s[i] == c:
3333
inc i
3434
else:
35-
error("Expected " & c & " at offset " & $i & ".")
35+
error("Expected " & c & " at offset.", i)
3636

3737
proc parseSymbol(s: string, i: var int): string =
3838
## Will read a symbol and return it.
@@ -56,7 +56,7 @@ proc parseJson(s: string, i: var int, v: var bool) =
5656
of "false":
5757
v = false
5858
else:
59-
error("Boolean true or false expected at offset " & $i & ".")
59+
error("Boolean true or false expected.", i)
6060

6161
proc parseJson(s: string, i: var int, v: var SomeInteger) =
6262
## Will parse int8, uint8, int16, uint16, int32, uint32, int64, uint64 or
@@ -69,15 +69,40 @@ proc parseJson(s: string, i: var int, v: var SomeFloat) =
6969

7070
proc parseJson(s: string, i: var int, v: var string) =
7171
## Parse string.
72+
#echo "S:", s[i .. min(i + 80, s.len-1)]
73+
eatSpace(s, i)
74+
if s[i] == 'n':
75+
let what = parseSymbol(s, i)
76+
if what == "null":
77+
return
78+
else:
79+
error("Expected \" or null at offset.", i)
7280
eat(s, i, '"')
7381
var j = i
7482
while i < s.len:
75-
case s[i]
83+
let c = s[i]
84+
case c
7685
of '"':
77-
v = s[j ..< i]
7886
break
87+
of '\\':
88+
inc i
89+
let c = s[i]
90+
case c
91+
of '"', '\\', '/': v.add(c)
92+
of 'b': v.add '\b'
93+
of 'f': v.add '\f'
94+
of 'n': v.add '\n'
95+
of 'r': v.add '\r'
96+
of 't': v.add '\t'
97+
of 'u':
98+
inc i
99+
let u = parseHexInt(s[i ..< i + 4])
100+
i += 3
101+
v.add(Rune(u).toUTF8())
102+
else:
103+
v.add(c)
79104
else:
80-
discard
105+
v.add(c)
81106
inc i
82107
eat(s, i, '"')
83108

@@ -100,8 +125,10 @@ proc parseJson[T](s: string, i: var int, v: var seq[T]) =
100125

101126
proc skipValue(s: string, i: var int) =
102127
## Used to skip values of extra fields.
128+
#echo "Skip:", s[i .. min(i + 80, s.len-1)]
103129
eatSpace(s, i)
104130
if s[i] == '{':
131+
#echo "skip obj"
105132
eat(s, i, '{')
106133
while i < s.len:
107134
eatSpace(s, i)
@@ -115,6 +142,7 @@ proc skipValue(s: string, i: var int) =
115142
inc i
116143
eat(s, i, '}')
117144
elif s[i] == '[':
145+
#echo "skip arr"
118146
eat(s, i, '[')
119147
while i < s.len:
120148
eatSpace(s, i)
@@ -126,13 +154,11 @@ proc skipValue(s: string, i: var int) =
126154
inc i
127155
eat(s, i, ']')
128156
elif s[i] == '"':
129-
eat(s, i, '"')
130-
while i < s.len:
131-
inc i
132-
if s[i] == '"':
133-
break
134-
eat(s, i, '"')
157+
#echo "skip str"
158+
var str: string
159+
parseJson(s, i, str)
135160
else:
161+
#echo "skip sym"
136162
discard parseSymbol(s, i)
137163

138164
proc camelCase(s: string): string =
@@ -189,8 +215,8 @@ proc parseJson[T:enum](s: string, i: var int, v: var T) =
189215
var strV: string
190216
if s[i] == '"':
191217
parseJson(s, i, strV)
192-
when compiles(enumHook[T](strV)):
193-
v = enumHook[T](strV)
218+
when compiles(enumHook(strV, v)):
219+
enumHook(strV, v)
194220
else:
195221
v = parseEnum[T](strV)
196222
else:
@@ -199,6 +225,13 @@ proc parseJson[T:enum](s: string, i: var int, v: var T) =
199225

200226
proc parseJson[T:object|ref object](s: string, i: var int, v: var T) =
201227
## Parse an object.
228+
eatSpace(s, i)
229+
if s[i] == 'n':
230+
let what = parseSymbol(s, i)
231+
if what == "null":
232+
return
233+
else:
234+
error("Expected {} or null at offset.", i)
202235
eat(s, i, '{')
203236
when compiles(newHook(v)):
204237
newHook(v)
@@ -219,6 +252,25 @@ proc parseJson[T:object|ref object](s: string, i: var int, v: var T) =
219252
break
220253
eat(s, i, '}')
221254

255+
proc parseJson[T](s: string, i: var int, v: var Table[string, T]) =
256+
## Parse an object.
257+
eat(s, i, '{')
258+
while i < s.len:
259+
eatSpace(s, i)
260+
if s[i] == '}':
261+
break
262+
var key: string
263+
parseJson(s, i, key)
264+
eat(s, i, ':')
265+
var element: T
266+
parseJson(s, i, element)
267+
v[key] = element
268+
if s[i] == ',':
269+
inc i
270+
else:
271+
break
272+
eat(s, i, '}')
273+
222274
proc fromJson*[T](s: string): T =
223275
## Takes json and outputs the object it represents.
224276
## * Create little intermediate values.

tests/test_enums.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ type Color2 = enum
1818
c2Blue
1919
c2Green
2020

21-
proc enumHook[Color2](v: string): Color2 =
22-
case v:
21+
proc enumHook(s: string, v: var Color2) =
22+
v = case s:
2323
of "RED": c2Red
2424
of "BLUE": c2Blue
2525
of "GREEN": c2Green

tests/test_objects.nim

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ block:
1818
doAssert v.a == "b"
1919
doAssert v.ratio == 22.5
2020

21-
2221
type
2322
Bar3 = ref object
2423
name: string
@@ -131,3 +130,17 @@ doAssert snakeCase("color_Rule") == "color_rule"
131130
doAssert snakeCase("color_rule") == "color_rule"
132131
doAssert snakeCase("httpGet") == "http_get"
133132
doAssert snakeCase("restAPI") == "rest_api"
133+
134+
block:
135+
type Entry5 = object
136+
color: string
137+
var s = "null"
138+
var v = fromJson[Entry5](s)
139+
doAssert v.color == ""
140+
141+
block:
142+
type Entry6 = ref object
143+
color: string
144+
var s = "null"
145+
var v = fromJson[Entry6](s)
146+
doAssert v == nil

tests/test_strings.nim

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import jsony
2+
3+
block:
4+
var s = """ "hello" """
5+
var v = fromJson[string](s)
6+
doAssert v == "hello"
7+
8+
block:
9+
var s = """ "new\nline" """
10+
var v = fromJson[string](s)
11+
doAssert v == "new\nline"
12+
13+
block:
14+
var s = """ "quote\"inside" """
15+
var v = fromJson[string](s)
16+
doAssert v == "quote\"inside"
17+
18+
block:
19+
var s = """ "special: \"\\\/\b\f\n\r\t chars" """
20+
var v = fromJson[string](s)
21+
doAssert v == "special: \"\\/\b\f\n\r\t chars"
22+
23+
block:
24+
var s = """ "unicode: \u0020 \u0F88 \u1F21" """
25+
var v = fromJson[string](s)
26+
doAssert v == "unicode: \u0020 \u0F88 \u1F21"

tests/test_tables.nim

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import jsony, tables
2+
3+
block:
4+
5+
var s = "{}"
6+
var v = fromJson[Table[string, int]](s)
7+
doAssert v.len == 0
8+
9+
block:
10+
var s = """{"a":2}"""
11+
var v = fromJson[Table[string, int]](s)
12+
doAssert v.len == 1
13+
doAssert v["a"] == 2
14+
15+
block:
16+
var s = """{"a":2, "b":3, "c" : 4}"""
17+
var v = fromJson[Table[string, uint8]](s)
18+
doAssert v.len == 3
19+
doAssert v["a"] == 2
20+
doAssert v["b"] == 3
21+
doAssert v["c"] == 4
22+
23+
block:
24+
type Entry = object
25+
color: string
26+
var s = """{
27+
"a": {"color":"red"},
28+
"b": {"color":"green"},
29+
"c": {"color":"blue"}
30+
}"""
31+
var v = fromJson[Table[string, Entry]](s)
32+
doAssert v.len == 3
33+
doAssert v["a"].color == "red"
34+
doAssert v["b"].color == "green"
35+
doAssert v["c"].color == "blue"

0 commit comments

Comments
 (0)