/
JSONGrammar.ion
173 lines (136 loc) · 4.87 KB
/
JSONGrammar.ion
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
Grammar clone do [
def Constant[name,
block[text, state,
text substring[0, name size] equals[name]
((text.substr(0, name.length) == name) ? [text.substr(name.length), state] : null)
]
]
KeywordRule
Main = Any[KeywordRule, NumberRule, StringRule, ObjectRule, ArrayRule]
]
__END__
/*
What we've learned:
- grammars could be transformed to fit simpler parsers
- rules order and grouping matters
- Y combinator rocks
*/
var JSONGrammar = function(All, Any, Capture, Char, NotChar, Optional, Y, EOF, Terminator, Before, After)
{
var Plus = function(rule)
{
return Y(function(seq){
return Any(All(rule, seq), rule)
})
}
return Y(function(Value){
var lineSpace = Plus(Char(" \t"))
var space = Plus(Char(" \t\n\r"))
var optLineSpace = Optional(lineSpace)
var optSpace = Optional(space)
var Constant = function(name)
{
return function(text, state) {
return ((text.substr(0, name.length) == name) ? [text.substr(name.length), state] : null)
}
}
keywords = {'true':true, 'false':false, 'null':null}
var Keyword = Capture(Any(Constant("true"), Constant("false"), Constant("null")), function(buf, state){ return keywords[buf] })
var StringGrammar = (function()
{
var controlCharMap = {
"b": "\b",
"f": "\f",
"n": "\n",
"r": "\r",
"t": "\t"
}
var init = function(s) { return "" }
var anyCapture = function(buf, s) { return s + buf }
var ctrlCapture = function(buf, s) { return s + (controlCharMap[buf] || buf) }
var content = function(quote)
{
return Y(function(content){
var anyChar = NotChar(quote + "\\")
// js accepts anything after backslash
var controlChar = NotChar("") // Char("\'\"\\/bfnrt")
anyChar = Capture(anyChar, anyCapture)
controlChar = Capture(controlChar, ctrlCapture)
var item = Any(
All(
Char("\\"),
controlChar
),
anyChar
)
return Any(All(item, content), item)
})
}
var SingleQuotedString = Before(All(
Char("\'"), Optional(content("\'")), Char("\'")
), init)
var DoubleQuotedString = Before(All(
Char("\""), Optional(content("\"")), Char("\"")
), init)
return Any(SingleQuotedString, DoubleQuotedString)
})()
var ObjectGrammar = (function()
{
var init = function(s) { return {} }
var beforeTuple = function(obj) { return [] }
var afterTuple = function(obj, tuple) { obj[tuple[0]] = tuple[1]; return obj }
var afterKey = function(tuple, str) { tuple.push(str); return tuple }
var afterValue = function(tuple, val) { tuple.push(val); return tuple }
var seq = Y(function(seq){
var item = All(After(StringGrammar, afterKey),
optSpace,
Char(":"),
optSpace,
After(Value, afterValue))
item = After(Before(item, beforeTuple), afterTuple)
return Any(All(item, optSpace, Char(","), optSpace, seq), item)
})
return Before(All(
Char("{"),
optSpace, Optional(seq), optSpace, Optional(Char(",")), optSpace,
Char("}")
), init)
})()
var ArrayGrammar = (function()
{
var init = function(s) { return [] }
var afterItem = function(arr, val) { return arr.concat([val]) }
var seq = Y(function(seq){
var item = After(Value, afterItem)
return Any(All(item, optSpace, Char(","), optSpace, seq), item)
})
return Before(All(
Char("["),
optSpace, Optional(seq), optSpace, Optional(Char(",")), optSpace,
Char("]")
), init)
})()
var NumberGrammar = (function(){
var zero = Char("0")
var digit19 = Char("123456789")
var digit = Char("1234567890")
var sign = Char("+-")
var exp = Char("eE")
var digits = Y(function(digits){
return Any(All(digit, digits), digit)
})
var capture = function(buf, s) { return eval("(" + buf + ")") }
var integer = Any(zero, All(digit19, Optional(digits)))
var exponent = All(exp, Optional(sign), digits)
var floating = All(Char("."), digits, Optional(exponent))
var unsigned = All(integer, Optional(floating))
return Capture(All(Optional(sign), optSpace, unsigned), capture)
})()
return Any(StringGrammar, ObjectGrammar, ArrayGrammar, Keyword, NumberGrammar)
})
}
var KeywordGrammar = JSONGrammar
var NumberGrammar = JSONGrammar
var StringGrammar = JSONGrammar
var ArrayGrammar = JSONGrammar
var ObjectGrammar = JSONGrammar