shithub: mc

Download patch

ref: fb01f695fa5bfde9246492452cf65698e35c0b73
parent: 6c385887e98469bf92e40f435d2167ac613984ae
author: Ori Bernstein <ori@eigenstate.org>
date: Wed Oct 26 17:21:09 EDT 2016

Be a bit stricter about parsing json.

--- /dev/null
+++ b/lib/json/j.myr
@@ -1,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)
+	;;
+}
--- a/lib/json/parse.myr
+++ b/lib/json/parse.myr
@@ -23,7 +23,22 @@
 		.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 parsearr = {p
+const parsearr = {p -> std.result(elt#, err)
 	var elts
 	var err
 
@@ -230,7 +245,14 @@
 			;;
 		| '"':
 			-> `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 @@
 	-> 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':
--- a/lib/json/test/parse.myr
+++ b/lib/json/test/parse.myr
@@ -3,6 +3,11 @@
 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 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)
+	;;
+}