-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlcon.ts
162 lines (151 loc) · 6.68 KB
/
lcon.ts
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
/// <reference path="typings/underscore.d.ts" />
import parser = require("./lcon-parser")
import lexer = require("./lcon-lexer")
import orderedJson = require("./ordered-json")
import _ = require("underscore")
class TreeBuilderBase {
closeObject(object: any, end: lexer.SourceLocation): void { }
closeArray(array: any, end: lexer.SourceLocation): void { }
lastElementOfArray = _.last
processString(value: string, start: lexer.SourceLocation, end: lexer.SourceLocation) {
return value}
processNumber(value: number, start: lexer.SourceLocation, end: lexer.SourceLocation) {
return value}
processBoolean(value: boolean, start: lexer.SourceLocation, end: lexer.SourceLocation) {
return value}
processNull(start: lexer.SourceLocation, end: lexer.SourceLocation) {
return null}
}
class JsonTreeBuilder extends TreeBuilderBase implements parser.SyntaxTreeBuilder<any> {
private keyQueue: string[] = []
initObject(start: lexer.SourceLocation) {return {}}
initArray(start: lexer.SourceLocation) {return []}
appendKeyToObject(key: string, object: any, start: lexer.SourceLocation,
end: lexer.SourceLocation): void { this.keyQueue.push(key)}
appendValueToArray(value: any, array: any): void {array.push(value)}
appendValueToObject(value: any, object: any): void {object[this.keyQueue.shift()] = value}
isObject(thing: any) {return !_.isArray(thing) && !_.isString(thing) && _.isObject(thing)}
}
class OrderedJsonTreeBuilder extends TreeBuilderBase implements parser.SyntaxTreeBuilder<any[]> {
private keyQueue: string[] = []
initObject(start: lexer.SourceLocation) {return [false] }
initArray(start: lexer.SourceLocation) {return [true] }
appendKeyToObject(key: string, object: any, start: lexer.SourceLocation,
end: lexer.SourceLocation): void { object.push(key) }
appendValueToArray(value: any, array: any): void { array.push(value) }
appendValueToObject(value: any, object: any): void { object.push(value) }
isObject(thing: any) {return _.isArray(thing) && thing[0] === false }
}
export var orderedToUnordered = orderedJson.orderedToUnordered
export var parseWithBuilder = parser.parseWithBuilder
export function parseOrdered(src: string): any {
return parser.parseWithBuilder(src, new OrderedJsonTreeBuilder())
}
export function parseUnordered(src: string): any {
return parser.parseWithBuilder(src, new JsonTreeBuilder())
}
function isLegalWithoutQuotes(str: string): boolean {
if (str == "true" || str == "false" || str == "null" || str == "-") return false
var match = lexer.Lexer.UNQUOTED_STRING.exec(str)
if (match && match[0].length === str.length) {
match = lexer.Lexer.NUMBER.exec(str)
return !match || match[0].length !== str.length
} else return false
}
export function stringifyUnordered(data: any): string {
// There's a lot of repetition here that could have been refactored out into
// subroutines, but deeply-nested data could easily cause this recursive
// function to overflow the stack. Adding subroutines would only make the
// problem worse.
var output: string, first: boolean = true, i: number
if (_.isArray(data)) {
output = "["
for (i = 0; i < data.length; i++) {
if (first) first = false
else output += ", "
output += stringifyUnordered(data[i])
}
return output + "]"
} else if (_.isObject(data)) {
var keys = _.keys(data)
if (keys.length === 1) {
return stringifyUnordered(keys[0]) + " " + stringifyUnordered(data[keys[0]])
} else {
// I could have used _.foldl, but, once again, stack space...
output = "("
for (i = 0; i < keys.length; i++) {
if (first) first = false
else output += ", "
output += stringifyUnordered(keys[i]) + " " + stringifyUnordered(data[keys[i]])
}
return output + ")"
}
} else if (_.isString(data)) {
if (isLegalWithoutQuotes(<string>data)) return <string>data
else return JSON.stringify(data)
} else if (_.isNumber(data) || _.isBoolean(data) || _.isNull(data)) {
return JSON.stringify(data)
} else {
throw new TypeError("Cannot stringify: object contains non-JSON data (such as functions).")
}
}
export function stringifyOrdered(data: any): string {
var output: string, first: boolean = true, i: number
if (_.isArray(data) && data.length > 0) {
output = (data[0] ? "[" : "(")
if (data[0]) for (i = 1; i < data.length; i++) {
if (first) first = false
else output += ", "
output += stringifyOrdered(data[i])
} else if (data.length === 3) {
return stringifyOrdered(data[1]) + " " + stringifyOrdered(data[2])
} else for (i = 1; i < data.length; i += 2) {
if (first) first = false
else output += ", "
output += stringifyOrdered(data[i]) + " " + stringifyOrdered(data[i + 1])
}
return output + (data[0] ? "]" : ")")
} else if (_.isString(data)) {
if (isLegalWithoutQuotes(<string>data)) return <string>data
else return JSON.stringify(data)
} else if (_.isNumber(data) || _.isBoolean(data) || _.isNull(data)) {
return JSON.stringify(data)
} else {
throw new TypeError(
"Cannot stringify: object contains non-ordered-JSON data (such as objects or functions).")
}
}
export function stringifyOrderedJSON(data: any, indent?: number, indentStep?: number): string {
if (!indent) indent = 0
if (!indentStep) indentStep = indent
var output: string, first: boolean = true, i: number, j: number
if (_.isArray(data) && data.length > 0) {
output = (data[0] ? "[" : "{") + (indent > 0 && data.length > 1 ? "\n" : "")
if (data[0]) {
for (i = 1; i < data.length; i++) {
if (first) first = false
else output += (indent > 0 ? ",\n" : ",")
for (j = 0; j < indent; j++) output += " "
output += stringifyOrderedJSON(data[i], indent + indentStep, indentStep)
}
} else {
for (i = 1; i < data.length; i += 2) {
if (first) first = false
else output += (indent > 0 ? ",\n" : ",")
for (j = 0; j < indent; j++) output += " "
output += JSON.stringify(data[i]) + ": " +
stringifyOrderedJSON(data[i + 1], indent + indentStep, indentStep)
}
}
if (indent > 0 && data.length > 1) {
output += "\n"
for (j = 0; j < indent - indentStep; j++) output += " "
}
return output + (data[0] ? "]" : "}")
} else if (_.isString(data) || _.isNumber(data) || _.isBoolean(data) || _.isNull(data)) {
return JSON.stringify(data)
} else {
throw new TypeError(
"Cannot stringify: object contains non-ordered-JSON data (such as objects or functions).")
}
}