-
Notifications
You must be signed in to change notification settings - Fork 801
/
Copy pathpseudo_json.mjs
102 lines (92 loc) · 2.28 KB
/
pseudo_json.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
class Stream {
constructor(str) {
this.str = str
this.pos = 0
}
err(msg) {
throw new SyntaxError(msg + " at " + this.pos + " in " + JSON.stringify(this.str))
}
space() {
for (;;) {
let next = this.next
if (next == 32 || next == 9 || next == 10 || next == 13) this.pos++
else break
}
}
get next() {
return this.str.charCodeAt(this.pos)
}
ahead(n) {
this.pos += n
this.space()
}
}
export function parse(str) {
let stream = new Stream(str)
stream.space()
let value = parseValue(stream)
if (stream.pos != stream.str.length) stream.err("Extra characters at end of input")
return value
}
function parseValue(stream) {
let next = stream.next
if (next == 123) return parseObj(stream)
if (next == 91) return parseArr(stream)
if (next == 34) return parseStr(stream)
return parseWord(stream)
}
function parseObj(stream) {
stream.ahead(1)
let obj = {}
for (;;) {
if (stream.next == 125) break
let prop = parseWord(stream, true)
if (stream.next != 58) stream.err("Expected ':'")
stream.ahead(1)
obj[prop] = parseValue(stream)
if (stream.next == 44) stream.ahead(1)
}
stream.ahead(1)
return obj
}
function parseArr(stream) {
stream.ahead(1)
let arr = []
for (;;) {
if (stream.next == 93) break
arr.push(parseValue(stream))
if (stream.next == 44) stream.ahead(1)
}
stream.ahead(1)
return arr
}
function parseStr(stream) {
let start = stream.pos
stream.pos++
for (let escaped = false;;) {
let next = stream.next
stream.pos++
if (next == 34 && !escaped) break
else if (isNaN(next)) stream.err("Unterminated string")
escaped = next == 92
}
stream.space()
return JSON.parse(stream.str.slice(start, stream.pos))
}
function parseWord(stream, prop) {
let start = stream.pos
for (;;) {
let next = stream.next
if ((next >= 97 && next <= 122) || (next >= 65 && next <= 90) || next == 95 || (next >= 48 && next <= 57)) stream.pos++
else break
}
let word = stream.str.slice(start, stream.pos)
if (!word) stream.err("Expected word")
stream.space()
if (/^(?:0x[\da-f]+|\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?)$/i.test(word)) return JSON.parse(word)
if (!prop) {
if (word == "true") return true
if (word == "false") return false
}
return word
}