@tilua/compiler 6.0.1 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -22
- package/dist/{chunk-EQE22LX7.js → chunk-NM3EVX2J.js} +569 -83
- package/dist/cli.cjs +572 -92
- package/dist/cli.js +4 -10
- package/dist/index.cjs +569 -84
- package/dist/index.d.cts +165 -115
- package/dist/index.d.ts +165 -115
- package/dist/index.js +1 -2
- package/package.json +2 -2
- package/runtime/json.luau +255 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
local __NAME__ = {}
|
|
2
|
+
|
|
3
|
+
-- JSON as tilua's values have it. A table is an array or an object by what
|
|
4
|
+
-- the program wrote — `[]` is an array and `{}` an object, empty or not —
|
|
5
|
+
-- which a Lua encoder, seeing only a table, cannot tell: `JSONEncode` writes
|
|
6
|
+
-- every empty table as `[]`. A table no tilua code made (a Lua library's) is
|
|
7
|
+
-- an array when its keys are 1 to its length, as before.
|
|
8
|
+
--
|
|
9
|
+
-- A table has no key whose value is nil, so two values stand in for what
|
|
10
|
+
-- JSON has and Luau does not: `null`, the key sent with no value, and
|
|
11
|
+
-- `undefined`, as JavaScript's — left out of an object, and `null` in an
|
|
12
|
+
-- array, where leaving it out would move what comes after it.
|
|
13
|
+
|
|
14
|
+
local NULL = setmetatable({}, { __tostring = function()
|
|
15
|
+
return "null"
|
|
16
|
+
end })
|
|
17
|
+
local UNDEFINED = setmetatable({}, { __tostring = function()
|
|
18
|
+
return "undefined"
|
|
19
|
+
end })
|
|
20
|
+
|
|
21
|
+
__NAME__.null = NULL
|
|
22
|
+
__NAME__.undefined = UNDEFINED
|
|
23
|
+
|
|
24
|
+
local function isList(t)
|
|
25
|
+
if __ARRAYS__[t] then
|
|
26
|
+
return true
|
|
27
|
+
end
|
|
28
|
+
if next(t) == nil then
|
|
29
|
+
return false
|
|
30
|
+
end
|
|
31
|
+
local count = 0
|
|
32
|
+
for _ in pairs(t) do
|
|
33
|
+
count = count + 1
|
|
34
|
+
end
|
|
35
|
+
return count == #t
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
local function escape(s)
|
|
39
|
+
return (s:gsub('[%c"\\]', function(c)
|
|
40
|
+
if c == '"' then
|
|
41
|
+
return '\\"'
|
|
42
|
+
elseif c == "\\" then
|
|
43
|
+
return "\\\\"
|
|
44
|
+
elseif c == "\n" then
|
|
45
|
+
return "\\n"
|
|
46
|
+
elseif c == "\r" then
|
|
47
|
+
return "\\r"
|
|
48
|
+
elseif c == "\t" then
|
|
49
|
+
return "\\t"
|
|
50
|
+
end
|
|
51
|
+
return string.format("\\u%04x", string.byte(c))
|
|
52
|
+
end))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
local encode
|
|
56
|
+
|
|
57
|
+
local function encodeTable(t, seen)
|
|
58
|
+
if seen[t] then
|
|
59
|
+
error("JSON cannot hold a table that holds itself", 0)
|
|
60
|
+
end
|
|
61
|
+
seen[t] = true
|
|
62
|
+
local parts = {}
|
|
63
|
+
if isList(t) then
|
|
64
|
+
for i = 1, #t do
|
|
65
|
+
local item = t[i]
|
|
66
|
+
parts[i] = item == UNDEFINED and "null" or encode(item, seen)
|
|
67
|
+
end
|
|
68
|
+
seen[t] = nil
|
|
69
|
+
return "[" .. table.concat(parts, ",") .. "]"
|
|
70
|
+
end
|
|
71
|
+
-- Written in order, so one value is always one string.
|
|
72
|
+
local keys = {}
|
|
73
|
+
for key, item in pairs(t) do
|
|
74
|
+
local kind = type(key)
|
|
75
|
+
if kind ~= "string" and kind ~= "number" then
|
|
76
|
+
error("JSON keys are strings: cannot write a " .. kind .. " key", 0)
|
|
77
|
+
end
|
|
78
|
+
if item ~= UNDEFINED then
|
|
79
|
+
keys[#keys + 1] = key
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
table.sort(keys, function(a, b)
|
|
83
|
+
return tostring(a) < tostring(b)
|
|
84
|
+
end)
|
|
85
|
+
for i, key in ipairs(keys) do
|
|
86
|
+
parts[i] = '"' .. escape(tostring(key)) .. '":' .. encode(t[key], seen)
|
|
87
|
+
end
|
|
88
|
+
seen[t] = nil
|
|
89
|
+
return "{" .. table.concat(parts, ",") .. "}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
encode = function(value, seen)
|
|
93
|
+
if value == nil or value == NULL or value == UNDEFINED then
|
|
94
|
+
return "null"
|
|
95
|
+
end
|
|
96
|
+
local kind = type(value)
|
|
97
|
+
if kind == "string" then
|
|
98
|
+
return '"' .. escape(value) .. '"'
|
|
99
|
+
elseif kind == "number" then
|
|
100
|
+
if value ~= value or value == math.huge or value == -math.huge then
|
|
101
|
+
return "null"
|
|
102
|
+
end
|
|
103
|
+
return tostring(value)
|
|
104
|
+
elseif kind == "boolean" then
|
|
105
|
+
return tostring(value)
|
|
106
|
+
elseif kind == "table" then
|
|
107
|
+
return encodeTable(value, seen)
|
|
108
|
+
end
|
|
109
|
+
error("JSON cannot hold a " .. (typeof or type)(value), 0)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
function __NAME__.encode(value)
|
|
113
|
+
return encode(value, {})
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
-- ============================================================
|
|
117
|
+
-- Reading it back
|
|
118
|
+
-- ============================================================
|
|
119
|
+
--
|
|
120
|
+
-- An array comes back an array (`typeof` says so) and an object an object,
|
|
121
|
+
-- empty or not. `null` is nil: a key that holds it is not there.
|
|
122
|
+
|
|
123
|
+
local ESCAPES = { ['"'] = '"', ["\\"] = "\\", ["/"] = "/", b = "\b", f = "\f", n = "\n", r = "\r", t = "\t" }
|
|
124
|
+
|
|
125
|
+
local function fail(text, at, what)
|
|
126
|
+
local line = 1
|
|
127
|
+
for _ in text:sub(1, at - 1):gmatch("\n") do
|
|
128
|
+
line = line + 1
|
|
129
|
+
end
|
|
130
|
+
error("Invalid JSON at line " .. line .. ": " .. what, 0)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
local function skip(text, at)
|
|
134
|
+
return text:find("[^ \t\r\n]", at) or #text + 1
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
local decodeValue
|
|
138
|
+
|
|
139
|
+
local function decodeString(text, at)
|
|
140
|
+
local parts = {}
|
|
141
|
+
local i = at + 1
|
|
142
|
+
while true do
|
|
143
|
+
local stop = text:find('["\\]', i)
|
|
144
|
+
if stop == nil then
|
|
145
|
+
fail(text, at, "a string that never ends")
|
|
146
|
+
end
|
|
147
|
+
parts[#parts + 1] = text:sub(i, stop - 1)
|
|
148
|
+
if text:sub(stop, stop) == '"' then
|
|
149
|
+
return table.concat(parts), stop + 1
|
|
150
|
+
end
|
|
151
|
+
local c = text:sub(stop + 1, stop + 1)
|
|
152
|
+
if c == "u" then
|
|
153
|
+
local hex = text:sub(stop + 2, stop + 5)
|
|
154
|
+
local code = tonumber(hex, 16)
|
|
155
|
+
if code == nil or #hex < 4 then
|
|
156
|
+
fail(text, stop, "a \\u escape without four hex digits")
|
|
157
|
+
end
|
|
158
|
+
i = stop + 6
|
|
159
|
+
-- A surrogate pair is one character.
|
|
160
|
+
if code >= 0xD800 and code <= 0xDBFF and text:sub(i, i + 1) == "\\u" then
|
|
161
|
+
local low = tonumber(text:sub(i + 2, i + 5), 16)
|
|
162
|
+
if low ~= nil and low >= 0xDC00 and low <= 0xDFFF then
|
|
163
|
+
code = 0x10000 + (code - 0xD800) * 0x400 + (low - 0xDC00)
|
|
164
|
+
i = i + 6
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
parts[#parts + 1] = utf8 ~= nil and utf8.char(code) or "?"
|
|
168
|
+
else
|
|
169
|
+
local escaped = ESCAPES[c]
|
|
170
|
+
if escaped == nil then
|
|
171
|
+
fail(text, stop, "an unknown escape '\\" .. c .. "'")
|
|
172
|
+
end
|
|
173
|
+
parts[#parts + 1] = escaped
|
|
174
|
+
i = stop + 2
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
decodeValue = function(text, at)
|
|
180
|
+
at = skip(text, at)
|
|
181
|
+
local c = text:sub(at, at)
|
|
182
|
+
if c == "{" then
|
|
183
|
+
local out = {}
|
|
184
|
+
at = skip(text, at + 1)
|
|
185
|
+
if text:sub(at, at) == "}" then
|
|
186
|
+
return out, at + 1
|
|
187
|
+
end
|
|
188
|
+
while true do
|
|
189
|
+
at = skip(text, at)
|
|
190
|
+
if text:sub(at, at) ~= '"' then
|
|
191
|
+
fail(text, at, "an object key that is not a string")
|
|
192
|
+
end
|
|
193
|
+
local key
|
|
194
|
+
key, at = decodeString(text, at)
|
|
195
|
+
at = skip(text, at)
|
|
196
|
+
if text:sub(at, at) ~= ":" then
|
|
197
|
+
fail(text, at, "a key without ':'")
|
|
198
|
+
end
|
|
199
|
+
local value
|
|
200
|
+
value, at = decodeValue(text, at + 1)
|
|
201
|
+
out[key] = value
|
|
202
|
+
at = skip(text, at)
|
|
203
|
+
local next = text:sub(at, at)
|
|
204
|
+
if next == "}" then
|
|
205
|
+
return out, at + 1
|
|
206
|
+
elseif next ~= "," then
|
|
207
|
+
fail(text, at, "an object without ',' or '}'")
|
|
208
|
+
end
|
|
209
|
+
at = at + 1
|
|
210
|
+
end
|
|
211
|
+
elseif c == "[" then
|
|
212
|
+
local out = __ARRAY__({})
|
|
213
|
+
local n = 0
|
|
214
|
+
at = skip(text, at + 1)
|
|
215
|
+
if text:sub(at, at) == "]" then
|
|
216
|
+
return out, at + 1
|
|
217
|
+
end
|
|
218
|
+
while true do
|
|
219
|
+
local value
|
|
220
|
+
value, at = decodeValue(text, at)
|
|
221
|
+
n = n + 1
|
|
222
|
+
out[n] = value
|
|
223
|
+
at = skip(text, at)
|
|
224
|
+
local next = text:sub(at, at)
|
|
225
|
+
if next == "]" then
|
|
226
|
+
return out, at + 1
|
|
227
|
+
elseif next ~= "," then
|
|
228
|
+
fail(text, at, "an array without ',' or ']'")
|
|
229
|
+
end
|
|
230
|
+
at = at + 1
|
|
231
|
+
end
|
|
232
|
+
elseif c == '"' then
|
|
233
|
+
return decodeString(text, at)
|
|
234
|
+
elseif text:sub(at, at + 3) == "true" then
|
|
235
|
+
return true, at + 4
|
|
236
|
+
elseif text:sub(at, at + 4) == "false" then
|
|
237
|
+
return false, at + 5
|
|
238
|
+
elseif text:sub(at, at + 3) == "null" then
|
|
239
|
+
return nil, at + 4
|
|
240
|
+
end
|
|
241
|
+
local number = text:match("^-?%d+%.?%d*[eE]?[-+]?%d*", at)
|
|
242
|
+
if number == nil or number == "" or tonumber(number) == nil then
|
|
243
|
+
fail(text, at, c == "" and "nothing where a value belongs" or ("an unexpected '" .. c .. "'"))
|
|
244
|
+
end
|
|
245
|
+
return tonumber(number), at + #number
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
function __NAME__.decode(text)
|
|
249
|
+
local value, at = decodeValue(text, 1)
|
|
250
|
+
at = skip(text, at)
|
|
251
|
+
if at <= #text then
|
|
252
|
+
fail(text, at, "more after the value")
|
|
253
|
+
end
|
|
254
|
+
return value
|
|
255
|
+
end
|