summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOri Bernstein <ori@eigenstate.org>2016-10-26 21:21:09 -0700
committerOri Bernstein <ori@eigenstate.org>2016-12-24 01:44:40 -0800
commitfb01f695fa5bfde9246492452cf65698e35c0b73 (patch)
treec4e5c11f9e4a250a9e00bcbbf01165e8e7b893fe
parent6c385887e98469bf92e40f435d2167ac613984ae (diff)
downloadmc-fb01f695fa5bfde9246492452cf65698e35c0b73.tar.gz
Be a bit stricter about parsing json.
-rw-r--r--lib/json/j.myr17
-rw-r--r--lib/json/parse.myr36
-rw-r--r--lib/json/test/parse.myr43
3 files changed, 93 insertions, 3 deletions
diff --git a/lib/json/j.myr b/lib/json/j.myr
new file mode 100644
index 0000000..dd202f9
--- /dev/null
+++ b/lib/json/j.myr
@@ -0,0 +1,17 @@
+use std
+use json
+
+const main = {args : byte[:][:]
+ var data
+
+ for arg in args[1:]
+ data = std.try(std.slurp(arg))
+ match json.parse(data)
+ | `std.Ok j:
+ std.put("{}\n", j)
+ json.free(j)
+ | `std.Err e: std.put("{}\n", e)
+ ;;
+ std.slfree(data)
+ ;;
+}
diff --git a/lib/json/parse.myr b/lib/json/parse.myr
index f7fad7e..61b0598 100644
--- a/lib/json/parse.myr
+++ b/lib/json/parse.myr
@@ -23,7 +23,22 @@ const parse = {str
.off = 1,
.idx = 0,
]
- -> parseelt(&parser)
+ match parseelt(&parser)
+ | `std.Ok j:
+ takespace(&parser)
+ std.put("parser: {}\n", parser)
+ if parser.idx != parser.str.len
+ free(j)
+ -> `std.Err [
+ .e=`Junk std.decode(parser.str[parser.idx:]),
+ .line=parser.line,
+ .off=parser.off
+ ]
+ ;;
+ -> `std.Ok j
+ | `std.Err e:
+ -> `std.Err e
+ ;;
}
const free = {j
@@ -126,7 +141,7 @@ const member = {p
;;
}
-const parsearr = {p
+const parsearr = {p -> std.result(elt#, err)
var elts
var err
@@ -230,7 +245,14 @@ const jsonstr = {p
;;
| '"':
-> `std.Ok std.sbfin(sb)
+ | std.Badchar:
+ err = [.e=`Junk std.Badchar, .line=p.line, .off=p.off]
+ goto error
| chr:
+ if !unescaped(chr)
+ err = [.e=`Junk chr, .line=p.line, .off=p.off]
+ goto error
+ ;;
std.sbputc(sb, chr)
idx += std.charlen(chr)
;;
@@ -249,8 +271,16 @@ const matchprefix = {p, pfx
-> false
}
+const unescaped = {c
+ const tab : byte[128]
+
+ -> c == 0x20 || c == 0x21 || \
+ (c >= 0x23 && c < 0x5b) || \
+ (c > 0x5d && c < 0x10ffff)
+}
+
const takespace = {p
- while true
+ while p.idx < p.str.len
match (p.str[p.idx] : char)
| ' ':
| '\t':
diff --git a/lib/json/test/parse.myr b/lib/json/test/parse.myr
index 77d747f..b4673ce 100644
--- a/lib/json/test/parse.myr
+++ b/lib/json/test/parse.myr
@@ -3,6 +3,11 @@ use json
use testr
const main = {
+ strtest()
+ filetest()
+}
+
+const strtest = {
testr.run([
[.name="null", .fn={ctx
var j = std.try(json.parse("null"))
@@ -85,3 +90,41 @@ const main = {
][:])
}
+const filetest = {
+ var dir, data, path
+
+ dir = std.try(std.diropen("test/inputs"))
+ for f in std.byentry(dir)
+ path = std.pathcat("test/inputs", f)
+ data = std.try(std.slurp(path))
+ /* 'n' indicates expected failure, 'y' indicates expected success, 'i' indicates implementation defined */
+ match std.decode(f)
+ | 'n':
+ testr.run([
+ [.name=f, .fn={ctx
+ match json.parse(data)
+ | `std.Err e: /* ok */
+ | `std.Ok r: testr.fail(ctx, "succeeded in parsing malformed json: {}\n", r)
+ std.die("hah")
+ ;;
+ }]
+ ][:])
+
+ | 'y':
+ testr.run([
+ [.name=f, .fn={ctx
+ match json.parse(data)
+ | `std.Err e: testr.fail(ctx, "failed to parse json\n")
+ | `std.Ok r: json.free(r)
+ ;;
+ }]
+ ][:])
+ | 'i':
+ std.put("ignoring implementation defined test {}\n", f)
+ | wat:
+ std.fatal("unknown test {}: needs to start with y or n\n", f)
+ ;;
+ std.slfree(data)
+ std.slfree(path)
+ ;;
+}