-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.js
134 lines (119 loc) · 3.89 KB
/
parser.js
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
function nullParser (input) {
if (input.startsWith('null')) { return [null, input.slice(4)] }
return null
}
function booleanParser (input) {
if (input.startsWith('true')) { return [true, input.slice(4)] }
if (input.startsWith('false')) { return [false, input.slice(5)] }
return null
}
function numberParser (input) {
const output = input.match(/^-?([1-9]\d*|0)(\.\d+)?([eE][+-]?\d+)?/)
if (output === null) { return null }
return [Number(output[0]), input.slice(output[0].length)]
}
function stringParser (input) {
input = input.trim()
if (!input.startsWith('"')) { return null }
input = input.slice(1)
let str = ''
const escChars = {
'"': '"',
'\\': '\\',
'/': '/',
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t',
u: null
}
while (input[0]) {
if (input[0] === '"') { return [str, input.slice(1)] } // " without escape occurs at end of string
if (input[0].match(/[\u0000-\u001f]/i)) { return null } // control characters
if (input[0] === '\\') { // escape / sould be followed by any of n,/,f,r,b,t,u,\"
if (input[1] === 'u') {
const temp = input.slice(2, 6)
if (temp.match(/[a-f0-9]{4}/i) === null) { return null } // validate hexcode
str += String.fromCharCode(parseInt(temp, 16))
input = input.slice(6)
} else if (Object.keys(escChars).includes(input[1])) {
str += escChars[input[1]]
input = input.slice(2)
} else { return null } // anything else after \ is invalid
} else {
str += input[0]
input = input.slice(1)
}
}
return null
}
function valueParser (input) {
input = input.trim()
return nullParser(input) || booleanParser(input) || numberParser(input) || stringParser(input) || arrayParser(input) || objectParser(input)
}
function arrayParser (input) {
input = input.trim()
if (!input.startsWith('[')) { return null }
input = input.slice(1).trim()
const arr = []
if (input[0] === ']') { return [arr, input.slice(1)] }
while (input[0]) {
const parsedVal = valueParser(input)
if (parsedVal === null) { return null }
arr.push(parsedVal[0])
input = parsedVal[1].trim()
if (input[0] === ']') { return [arr, input.slice(1)] }
if (input[0] !== ',') { return null }
input = input.slice(1)
}
return null
}
function objectParser (input) {
input = input.trim()
if (input[0] !== '{') { return null }
const obj = {}
input = input.slice(1).trim()
if (input[0] === '}') { return [obj, input.slice(1)] }
do {
let parsed = stringParser(input)
if (parsed === null) { return null }
const key = parsed[0] // key is objects property
input = parsed[1].trim()
if (input[0] !== ':') { return null } // porperty should be followed by :
parsed = valueParser(input.slice(1)) // parsing value after colon
if (parsed === null) { return null }
obj[key] = parsed[0]
input = parsed[1].trim()
if (input.startsWith('}')) { return [obj, input.slice(1)] }
if (!input.startsWith(',')) { return null } // bad JSON
input = input.slice(1).trim() // remove , from input
}
while (input[0])
return null
}
function JSONParser (input) {
input = input.trim()
const parsedValue = arrayParser(input) || objectParser(input)
if (parsedValue === null || parsedValue[1].length > 0) { return null }
return parsedValue[0]
}
// fail cases
const fs = require('fs')
for (let i = 1; i <= 33; i++) {
if (i !== 18) {
const data = fs.readFileSync(`./test/fail${i}.json`, 'utf8')
console.log(`fail${i}`, JSONParser(data))
}
}
// pass cases
for (let i = 1; i <= 5; i++) {
const data = fs.readFileSync(`./test/pass${i}.json`, 'utf8')
console.log(`pass${i}`, JSONParser(data))
}
// const fs = require('fs')
// const data = fs.readFileSync('./test/test.json', 'utf8')
// console.log(JSONParser(data))
// const fs = require('fs')
// const data = fs.readFileSync('./test/test.json', 'utf8')
// console.log(JSONParser(data))