rufus-lua-moon 0.2.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.
@@ -0,0 +1,87 @@
1
+ module("moonscript.data", package.seeall)
2
+ local concat = table.concat
3
+ Set = function(items)
4
+ local self = { }
5
+ local _list_0 = items
6
+ for _index_0 = 1, #_list_0 do
7
+ local key = _list_0[_index_0]
8
+ self[key] = true
9
+ end
10
+ return self
11
+ end
12
+ Stack = (function()
13
+ local _parent_0 = nil
14
+ local _base_0 = {
15
+ __tostring = function(self)
16
+ return "<Stack {" .. concat(self, ", ") .. "}>"
17
+ end,
18
+ pop = function(self)
19
+ return table.remove(self)
20
+ end,
21
+ push = function(self, value)
22
+ table.insert(self, value)
23
+ return value
24
+ end,
25
+ top = function(self)
26
+ return self[#self]
27
+ end
28
+ }
29
+ _base_0.__index = _base_0
30
+ if _parent_0 then
31
+ setmetatable(_base_0, _parent_0.__base)
32
+ end
33
+ local _class_0 = setmetatable({
34
+ __init = function(self, ...)
35
+ local _list_0 = {
36
+ ...
37
+ }
38
+ for _index_0 = 1, #_list_0 do
39
+ local v = _list_0[_index_0]
40
+ self:push(v)
41
+ end
42
+ return nil
43
+ end,
44
+ __base = _base_0,
45
+ __name = "Stack",
46
+ __parent = _parent_0
47
+ }, {
48
+ __index = function(cls, name)
49
+ local val = rawget(_base_0, name)
50
+ if val == nil and _parent_0 then
51
+ return _parent_0[name]
52
+ else
53
+ return val
54
+ end
55
+ end,
56
+ __call = function(cls, ...)
57
+ local _self_0 = setmetatable({}, _base_0)
58
+ cls.__init(_self_0, ...)
59
+ return _self_0
60
+ end
61
+ })
62
+ _base_0.__class = _class_0
63
+ return _class_0
64
+ end)()
65
+ lua_keywords = Set({
66
+ 'and',
67
+ 'break',
68
+ 'do',
69
+ 'else',
70
+ 'elseif',
71
+ 'end',
72
+ 'false',
73
+ 'for',
74
+ 'function',
75
+ 'if',
76
+ 'in',
77
+ 'local',
78
+ 'nil',
79
+ 'not',
80
+ 'or',
81
+ 'repeat',
82
+ 'return',
83
+ 'then',
84
+ 'true',
85
+ 'until',
86
+ 'while'
87
+ })
@@ -0,0 +1,42 @@
1
+ module("moonscript.dump", package.seeall)
2
+ local flat_value
3
+ flat_value = function(op, depth)
4
+ if depth == nil then
5
+ depth = 1
6
+ end
7
+ if type(op) == "string" then
8
+ return '"' .. op .. '"'
9
+ end
10
+ if type(op) ~= "table" then
11
+ return tostring(op)
12
+ end
13
+ local items = (function()
14
+ local _accum_0 = { }
15
+ local _len_0 = 0
16
+ local _list_0 = op
17
+ for _index_0 = 1, #_list_0 do
18
+ local item = _list_0[_index_0]
19
+ _len_0 = _len_0 + 1
20
+ _accum_0[_len_0] = flat_value(item, depth + 1)
21
+ end
22
+ return _accum_0
23
+ end)()
24
+ local pos = op[-1]
25
+ return "{" .. (pos and "[" .. pos .. "] " or "") .. table.concat(items, ", ") .. "}"
26
+ end
27
+ value = function(op)
28
+ return flat_value(op)
29
+ end
30
+ tree = function(block)
31
+ return (function()
32
+ local _accum_0 = { }
33
+ local _len_0 = 0
34
+ local _list_0 = block
35
+ for _index_0 = 1, #_list_0 do
36
+ value = _list_0[_index_0]
37
+ _len_0 = _len_0 + 1
38
+ _accum_0[_len_0] = print(flat_value(value))
39
+ end
40
+ return _accum_0
41
+ end)()
42
+ end
@@ -0,0 +1,65 @@
1
+ module("moonscript.errors", package.seeall)
2
+ local moon = require("moonscript")
3
+ local util = require("moonscript.util")
4
+ require("lpeg")
5
+ local concat, insert = table.concat, table.insert
6
+ local split, pos_to_line = util.split, util.pos_to_line
7
+ local lookup_line
8
+ lookup_line = function(fname, pos, cache)
9
+ if not cache[fname] then
10
+ do
11
+ local _with_0 = io.open(fname)
12
+ cache[fname] = _with_0:read("*a")
13
+ _with_0:close()
14
+ end
15
+ end
16
+ return pos_to_line(cache[fname], pos)
17
+ end
18
+ local reverse_line_number
19
+ reverse_line_number = function(fname, line_table, line_num, cache)
20
+ for i = line_num, 0, -1 do
21
+ if line_table[i] then
22
+ return lookup_line(fname, line_table[i], cache)
23
+ end
24
+ end
25
+ return "unknown"
26
+ end
27
+ rewrite_traceback = function(text, err)
28
+ local line_tables = moon.line_tables
29
+ local V, S, Ct, C = lpeg.V, lpeg.S, lpeg.Ct, lpeg.C
30
+ local header_text = "stack traceback:"
31
+ local Header, Line = V("Header"), V("Line")
32
+ local Break = lpeg.S("\n")
33
+ local g = lpeg.P({
34
+ Header,
35
+ Header = header_text * Break * Ct(Line ^ 1),
36
+ Line = "\t" * C((1 - Break) ^ 0) * (Break + -1)
37
+ })
38
+ local cache = { }
39
+ local rewrite_single
40
+ rewrite_single = function(trace)
41
+ local fname, line, msg = trace:match('^%[string "(.-)"]:(%d+): (.*)$')
42
+ local tbl = line_tables[fname]
43
+ if fname and tbl then
44
+ return concat({
45
+ fname,
46
+ ":",
47
+ reverse_line_number(fname, tbl, line, cache),
48
+ ": ",
49
+ msg
50
+ })
51
+ else
52
+ return trace
53
+ end
54
+ end
55
+ err = rewrite_single(err)
56
+ local match = g:match(text)
57
+ for i, trace in ipairs(match) do
58
+ match[i] = rewrite_single(trace)
59
+ end
60
+ return concat({
61
+ "moon:" .. err,
62
+ header_text,
63
+ "\t" .. concat(match, "\n\t")
64
+ }, "\n")
65
+ end
@@ -0,0 +1,90 @@
1
+ module("moonscript", package.seeall)
2
+ require("moonscript.compile")
3
+ require("moonscript.parse")
4
+ require("moonscript.util")
5
+ local concat, insert = table.concat, table.insert
6
+ local split, dump = util.split, util.dump
7
+ local lua = {
8
+ loadstring = loadstring
9
+ }
10
+ dirsep = "/"
11
+ line_tables = { }
12
+ local create_moonpath
13
+ create_moonpath = function(package_path)
14
+ local paths = split(package_path, ";")
15
+ for i, path in ipairs(paths) do
16
+ local p = path:match("^(.-)%.lua$")
17
+ if p then
18
+ paths[i] = p .. ".moon"
19
+ end
20
+ end
21
+ return concat(paths, ";")
22
+ end
23
+ to_lua = function(text)
24
+ if "string" ~= type(text) then
25
+ local t = type(text)
26
+ error("expecting string (got " .. t .. ")", 2)
27
+ end
28
+ local tree, err = parse.string(text)
29
+ if not tree then
30
+ error(err, 2)
31
+ end
32
+ local code, ltable, pos = compile.tree(tree)
33
+ if not code then
34
+ error(compile.format_error(ltable, pos, text), 2)
35
+ end
36
+ return code, ltable
37
+ end
38
+ moon_loader = function(name)
39
+ local name_path = name:gsub("%.", dirsep)
40
+ local file, file_path = nil, nil
41
+ local _list_0 = split(package.moonpath, ";")
42
+ for _index_0 = 1, #_list_0 do
43
+ local path = _list_0[_index_0]
44
+ file_path = path:gsub("?", name_path)
45
+ file = io.open(file_path)
46
+ if file then
47
+ break
48
+ end
49
+ end
50
+ if file then
51
+ local text = file:read("*a")
52
+ return loadstring(text, file_path)
53
+ else
54
+ return nil, "Could not find moon file"
55
+ end
56
+ end
57
+ if not package.moonpath then
58
+ package.moonpath = create_moonpath(package.path)
59
+ end
60
+ local init_loader
61
+ init_loader = function()
62
+ return insert(package.loaders, 2, moon_loader)
63
+ end
64
+ if not _G.moon_no_loader then
65
+ init_loader()
66
+ end
67
+ loadstring = function(str, chunk_name)
68
+ local passed, code, ltable = pcall(function()
69
+ return to_lua(str)
70
+ end)
71
+ if not passed then
72
+ error(chunk_name .. ": " .. code, 2)
73
+ end
74
+ if chunk_name then
75
+ line_tables[chunk_name] = ltable
76
+ end
77
+ return lua.loadstring(code, chunk_name or "=(moonscript.loadstring)")
78
+ end
79
+ loadfile = function(fname)
80
+ local file, err = io.open(fname)
81
+ if not file then
82
+ return nil, err
83
+ end
84
+ local text = assert(file:read("*a"))
85
+ return loadstring(text, fname)
86
+ end
87
+ dofile = function(fname)
88
+ local f = assert(loadfile(fname))
89
+ return f()
90
+ end
@@ -0,0 +1,505 @@
1
+ module("moonscript.parse", package.seeall)
2
+
3
+ local util = require"moonscript.util"
4
+
5
+ require"lpeg"
6
+
7
+ local data = require"moonscript.data"
8
+ local types = require"moonscript.types"
9
+
10
+ local ntype = types.ntype
11
+
12
+ local dump = util.dump
13
+ local trim = util.trim
14
+
15
+ local Stack = data.Stack
16
+
17
+ local function count_indent(str)
18
+ local sum = 0
19
+ for v in str:gmatch("[\t ]") do
20
+ if v == ' ' then sum = sum + 1 end
21
+ if v == '\t' then sum = sum + 4 end
22
+ end
23
+ return sum
24
+ end
25
+
26
+ local R, S, V, P = lpeg.R, lpeg.S, lpeg.V, lpeg.P
27
+ local C, Ct, Cmt, Cg, Cb, Cc = lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cb, lpeg.Cc
28
+
29
+ lpeg.setmaxstack(10000)
30
+
31
+ local White = S" \t\r\n"^0
32
+ local _Space = S" \t"^0
33
+ local Break = P"\r"^-1 * P"\n"
34
+ local Stop = Break + -1
35
+ local Indent = C(S"\t "^0) / count_indent
36
+
37
+ local Comment = P"--" * (1 - S"\r\n")^0 * #Stop
38
+ local Space = _Space * Comment^-1
39
+ local SomeSpace = S" \t"^1 * Comment^-1
40
+
41
+ local SpaceBreak = Space * Break
42
+ local EmptyLine = SpaceBreak
43
+
44
+ local AlphaNum = R("az", "AZ", "09", "__")
45
+
46
+ local _Name = C(R("az", "AZ", "__") * AlphaNum^0)
47
+ local Name = Space * _Name
48
+
49
+ local Num = P"0x" * R("09", "af", "AF")^1 +
50
+ R"09"^1 * (P"." * R"09"^1)^-1 * (S"eE" * P"-"^-1 * R"09"^1)^-1
51
+
52
+ Num = Space * (Num / function(value) return {"number", value} end)
53
+
54
+ local FactorOp = Space * C(S"+-")
55
+ local TermOp = Space * C(S"*/%^")
56
+
57
+ local Shebang = P"#!" * P(1 - Stop)^0
58
+
59
+ -- can't have P(false) because it causes preceding patterns not to run
60
+ local Cut = P(function() return false end)
61
+
62
+ local function ensure(patt, finally)
63
+ return patt * finally + finally * Cut
64
+ end
65
+
66
+ -- auto declare Proper variables with lpeg.V
67
+ local function wrap_env(fn)
68
+ local env = getfenv(fn)
69
+
70
+ return setfenv(fn, setmetatable({}, {
71
+ __index = function(self, name)
72
+ local value = env[name]
73
+ if value ~= nil then return value end
74
+
75
+ if name:match"^[A-Z][A-Za-z0-9]*$" then
76
+ local v = V(name)
77
+ rawset(self, name, v)
78
+ return v
79
+ end
80
+ error("unknown variable referenced: "..name)
81
+ end
82
+ }))
83
+ end
84
+
85
+ function extract_line(str, start_pos)
86
+ str = str:sub(start_pos)
87
+ m = str:match"^(.-)\n"
88
+ if m then return m end
89
+ return str:match"^.-$"
90
+ end
91
+
92
+ local function mark(name)
93
+ return function(...)
94
+ return {name, ...}
95
+ end
96
+ end
97
+
98
+ local function insert_pos(pos, value)
99
+ if type(value) == "table" then
100
+ value[-1] = pos
101
+ end
102
+ return value
103
+ end
104
+
105
+ local function pos(patt)
106
+ return (lpeg.Cp() * patt) / insert_pos
107
+ end
108
+
109
+ local function got(what)
110
+ return Cmt("", function(str, pos, ...)
111
+ local cap = {...}
112
+ print("++ got "..what, "["..extract_line(str, pos).."]")
113
+ return true
114
+ end)
115
+ end
116
+
117
+ local function flatten(tbl)
118
+ if #tbl == 1 then
119
+ return tbl[1]
120
+ end
121
+ return tbl
122
+ end
123
+
124
+ local function flatten_or_mark(name)
125
+ return function(tbl)
126
+ if #tbl == 1 then return tbl[1] end
127
+ table.insert(tbl, 1, name)
128
+ return tbl
129
+ end
130
+ end
131
+
132
+ -- makes sure the last item in a chain is an index
133
+ local _assignable = { index = true, dot = true, slice = true }
134
+ local function check_assignable(str, pos, value)
135
+ if ntype(value) == "chain" and _assignable[ntype(value[#value])]
136
+ or type(value) == "string"
137
+ then
138
+ return true, value
139
+ end
140
+ return false
141
+ end
142
+
143
+ local function sym(chars)
144
+ return Space * chars
145
+ end
146
+
147
+ local function symx(chars)
148
+ return chars
149
+ end
150
+
151
+ local function simple_string(delim, x)
152
+ return C(symx(delim)) * C((P('\\'..delim) +
153
+ "\\\\" +
154
+ (1 - S('\r\n'..delim)))^0) * sym(delim) / mark"string"
155
+ end
156
+
157
+ local function wrap_func_arg(value)
158
+ return {"call", {value}}
159
+ end
160
+
161
+ -- DOCME
162
+ local function flatten_func(callee, args)
163
+ if #args == 0 then return callee end
164
+
165
+ args = {"call", args}
166
+ if ntype(callee) == "chain" then
167
+ -- check for colon stub that needs arguments
168
+ if ntype(callee[#callee]) == "colon_stub" then
169
+ local stub = callee[#callee]
170
+ stub[1] = "colon"
171
+ table.insert(stub, args)
172
+ else
173
+ table.insert(callee, args)
174
+ end
175
+
176
+ return callee
177
+ end
178
+
179
+ return {"chain", callee, args}
180
+ end
181
+
182
+ -- wraps a statement that has a line decorator
183
+ local function wrap_decorator(stm, dec)
184
+ if not dec then return stm end
185
+
186
+ local arg = {stm, dec}
187
+
188
+ if dec[1] == "if" then
189
+ local _, cond, fail = unpack(dec)
190
+ if fail then fail = {"else", {fail}} end
191
+ stm = {"if", cond, {stm}, fail}
192
+ elseif dec[1] == "comprehension" then
193
+ local _, clauses = unpack(dec)
194
+ stm = {"comprehension", stm, clauses}
195
+ end
196
+
197
+ return stm
198
+ end
199
+
200
+ -- wrap if statement if there is a conditional decorator
201
+ local function wrap_if(stm, cond)
202
+ if cond then
203
+ local pass, fail = unpack(cond)
204
+ if fail then fail = {"else", {fail}} end
205
+ return {"if", cond[2], {stm}, fail}
206
+ end
207
+ return stm
208
+ end
209
+
210
+ local function check_lua_string(str, pos, right, left)
211
+ return #left == #right
212
+ end
213
+
214
+ -- :name in table literal
215
+ local function self_assign(name)
216
+ return {name, name}
217
+ end
218
+
219
+ local err_msg = "Failed to parse:\n [%d] >> %s (%d)"
220
+
221
+ local build_grammar = wrap_env(function()
222
+ local _indent = Stack(0) -- current indent
223
+
224
+ local last_pos = 0 -- used to know where to report error
225
+ local function check_indent(str, pos, indent)
226
+ last_pos = pos
227
+ return _indent:top() == indent
228
+ end
229
+
230
+ local function advance_indent(str, pos, indent)
231
+ local top = _indent:top()
232
+ if top ~= -1 and indent > _indent:top() then
233
+ _indent:push(indent)
234
+ return true
235
+ end
236
+ end
237
+
238
+ local function push_indent(str, pos, indent)
239
+ _indent:push(indent)
240
+ return true
241
+ end
242
+
243
+ local function pop_indent(str, pos)
244
+ if not _indent:pop() then error("unexpected outdent") end
245
+ return true
246
+ end
247
+
248
+ local keywords = {}
249
+ local function key(chars)
250
+ keywords[chars] = true
251
+ return Space * chars * -AlphaNum
252
+ end
253
+
254
+ local function op(word)
255
+ local patt = Space * C(word)
256
+ if word:match("^%w*$") then
257
+ keywords[word] = true
258
+ patt = patt * -AlphaNum
259
+ end
260
+ return patt
261
+ end
262
+
263
+ local SimpleName = Name -- for table key
264
+
265
+ -- make sure name is not a keyword
266
+ local Name = Cmt(Name, function(str, pos, name)
267
+ if keywords[name] then return false end
268
+ return true
269
+ end) / trim
270
+
271
+ local Name = sym"@" * Name / mark"self" + Name + Space * "..." / trim
272
+
273
+ local g = lpeg.P{
274
+ File,
275
+ File = Shebang^-1 * (Block + Ct""),
276
+ Block = Ct(Line * (Break^1 * Line)^0),
277
+ CheckIndent = Cmt(Indent, check_indent), -- validates line is in correct indent
278
+ Line = (CheckIndent * Statement + Space * #Stop),
279
+
280
+ Statement = (Import + While + With + For + ForEach + Switch + Return
281
+ + ClassDecl + Export + BreakLoop + Ct(ExpList) / flatten_or_mark"explist" * Space) * ((
282
+ -- statement decorators
283
+ key"if" * Exp * (key"else" * Exp)^-1 * Space / mark"if" +
284
+ CompInner / mark"comprehension"
285
+ ) * Space)^-1 / wrap_decorator,
286
+
287
+ Body = Space^-1 * Break * EmptyLine^0 * InBlock + Ct(Statement), -- either a statement, or an indented block
288
+
289
+ Advance = #Cmt(Indent, advance_indent), -- Advances the indent, gives back whitespace for CheckIndent
290
+ PushIndent = Cmt(Indent, push_indent),
291
+ PreventIndent = Cmt(Cc(-1), push_indent),
292
+ PopIndent = Cmt("", pop_indent),
293
+ InBlock = Advance * Block * PopIndent,
294
+
295
+ Import = key"import" * Ct(ImportNameList) * key"from" * Exp / mark"import",
296
+ ImportName = (sym"\\" * Ct(Cc"colon_stub" * Name) + Name),
297
+ ImportNameList = ImportName * (sym"," * ImportName)^0,
298
+
299
+ NameList = Name * (sym"," * Name)^0,
300
+
301
+ BreakLoop = Ct(key"break"/trim),
302
+
303
+ Return = key"return" * (ExpListLow/mark"explist" + C"") / mark"return",
304
+
305
+ With = key"with" * Exp * key"do"^-1 * Body / mark"with",
306
+
307
+ Switch = key"switch" * Exp * key"do"^-1 * Space^-1 * Break * SwitchBlock / mark"switch",
308
+
309
+ SwitchBlock = EmptyLine^0 * Advance * Ct(SwitchCase * (Break^1 * SwitchCase)^0 * (Break^1 * SwitchElse)^-1) * PopIndent,
310
+ SwitchCase = key"when" * Exp * key"then"^-1 * Body / mark"case",
311
+ SwitchElse = key"else" * Body / mark"else",
312
+
313
+ If = key"if" * Exp * key"then"^-1 * Body *
314
+ ((Break * CheckIndent)^-1 * EmptyLine^0 * key"elseif" * Exp * key"then"^-1 * Body / mark"elseif")^0 *
315
+ ((Break * CheckIndent)^-1 * EmptyLine^0 * key"else" * Body / mark"else")^-1 / mark"if",
316
+
317
+ While = key"while" * Exp * key"do"^-1 * Body / mark"while",
318
+
319
+ For = key"for" * (Name * sym"=" * Ct(Exp * sym"," * Exp * (sym"," * Exp)^-1)) *
320
+ key"do"^-1 * Body / mark"for",
321
+
322
+ ForEach = key"for" * Ct(NameList) * key"in" * (sym"*" * Exp / mark"unpack" + Exp) * key"do"^-1 * Body / mark"foreach",
323
+
324
+ Comprehension = sym"[" * Exp * CompInner * sym"]" / mark"comprehension",
325
+
326
+ TblComprehension = sym"{" * Exp * (sym"," * Exp)^-1 * CompInner * sym"}" / mark"tblcomprehension",
327
+
328
+ CompInner = Ct(CompFor * CompClause^0),
329
+ CompFor = key"for" * Ct(NameList) * key"in" * (sym"*" * Exp / mark"unpack" + Exp) / mark"for",
330
+ CompClause = CompFor + key"when" * Exp / mark"when",
331
+
332
+ Assign = Ct(AssignableList) * sym"=" * (Ct(With + If + Switch) + Ct(TableBlock + ExpListLow)) / mark"assign",
333
+ Update = Assignable * ((sym"..=" + sym"+=" + sym"-=" + sym"*=" + sym"/=" + sym"%=")/trim) * Exp / mark"update",
334
+
335
+ -- we can ignore precedence for now
336
+ OtherOps = op"or" + op"and" + op"<=" + op">=" + op"~=" + op"!=" + op"==" + op".." + op"<" + op">",
337
+
338
+ Assignable = Cmt(DotChain + Chain, check_assignable) + Name,
339
+ AssignableList = Assignable * (sym"," * Assignable)^0,
340
+
341
+ Exp = Ct(Value * ((OtherOps + FactorOp + TermOp) * Value)^0) / flatten_or_mark"exp",
342
+
343
+ -- Exp = Ct(Factor * (OtherOps * Factor)^0) / flatten_or_mark"exp",
344
+ -- Factor = Ct(Term * (FactorOp * Term)^0) / flatten_or_mark"exp",
345
+ -- Term = Ct(Value * (TermOp * Value)^0) / flatten_or_mark"exp",
346
+
347
+ SimpleValue =
348
+ If +
349
+ Switch +
350
+ With +
351
+ ForEach + For + While +
352
+ sym"-" * -SomeSpace * Exp / mark"minus" +
353
+ sym"#" * Exp / mark"length" +
354
+ key"not" * Exp / mark"not" +
355
+ TblComprehension +
356
+ TableLit +
357
+ Comprehension +
358
+ Assign + Update + FunLit + String +
359
+ Num,
360
+
361
+ ChainValue = -- a function call or an object access
362
+ ((Chain + DotChain + Callable) * Ct(InvokeArgs^-1)) / flatten_func,
363
+
364
+ Value = pos(
365
+ SimpleValue +
366
+ Ct(KeyValueList) / mark"table" +
367
+ ChainValue),
368
+
369
+ SliceValue = SimpleValue + ChainValue,
370
+
371
+ String = Space * DoubleString + Space * SingleString + LuaString,
372
+ SingleString = simple_string("'"),
373
+ DoubleString = simple_string('"'),
374
+
375
+ LuaString = Cg(LuaStringOpen, "string_open") * Cb"string_open" * Break^-1 *
376
+ C((1 - Cmt(C(LuaStringClose) * Cb"string_open", check_lua_string))^0) *
377
+ C(LuaStringClose) / mark"string",
378
+
379
+ LuaStringOpen = sym"[" * P"="^0 * "[" / trim,
380
+ LuaStringClose = "]" * P"="^0 * "]",
381
+
382
+ Callable = Name + Parens / mark"parens",
383
+ Parens = sym"(" * Exp * sym")",
384
+
385
+ FnArgs = symx"(" * Ct(ExpList^-1) * sym")" + sym"!" * -P"=" * Ct"",
386
+
387
+ ChainTail = (ChainItem^1 * ColonSuffix^-1 + ColonSuffix),
388
+
389
+ -- a list of funcalls and indexs on a callable
390
+ Chain = Callable * (ChainItem^1 * ColonSuffix^-1 + ColonSuffix) / mark"chain",
391
+
392
+ -- shorthand dot call for use in with statement
393
+ DotChain =
394
+ (sym"." * Cc(-1) * (_Name / mark"dot") * ChainTail^-1) / mark"chain" +
395
+ (sym"\\" * Cc(-1) * (
396
+ (_Name * Invoke / mark"colon") * ChainTail^-1 +
397
+ (_Name / mark"colon_stub")
398
+ )) / mark"chain",
399
+
400
+ ChainItem =
401
+ Invoke +
402
+ Slice +
403
+ symx"[" * Exp/mark"index" * sym"]" +
404
+ symx"." * _Name/mark"dot" +
405
+ ColonCall,
406
+
407
+ Slice = symx"[" * (SliceValue + Cc(1)) * sym"," * (SliceValue + Cc"") *
408
+ (sym"," * SliceValue)^-1 *sym"]" / mark"slice",
409
+
410
+ ColonCall = symx"\\" * (_Name * Invoke) / mark"colon",
411
+ ColonSuffix = symx"\\" * _Name / mark"colon_stub",
412
+
413
+ Invoke = FnArgs/mark"call" +
414
+ SingleString / wrap_func_arg +
415
+ DoubleString / wrap_func_arg,
416
+
417
+ TableValue = KeyValue + Ct(Exp),
418
+
419
+ TableLit = sym"{" * Ct(
420
+ TableValueList^-1 * sym","^-1 *
421
+ (SpaceBreak * TableLitLine * (sym","^-1 * SpaceBreak * TableLitLine)^0 * sym","^-1)^-1
422
+ ) * White * sym"}" / mark"table",
423
+
424
+ TableValueList = TableValue * (sym"," * TableValue)^0,
425
+ TableLitLine = PushIndent * ((TableValueList * PopIndent) + (PopIndent * Cut)) + Space,
426
+
427
+ -- the unbounded table
428
+ TableBlockInner = Ct(KeyValueLine * (SpaceBreak^1 * KeyValueLine)^0),
429
+ TableBlock = SpaceBreak^1 * Advance * ensure(TableBlockInner, PopIndent) / mark"table",
430
+
431
+ ClassDecl = key"class" * Name * (key"extends" * PreventIndent * ensure(Exp, PopIndent) + C"")^-1 * ClassBlock / mark"class",
432
+
433
+ ClassBlock = SpaceBreak^1 * Advance *
434
+ Ct(ClassLine * (SpaceBreak^1 * ClassLine)^0) * PopIndent,
435
+ ClassLine = CheckIndent * ((
436
+ KeyValueList / mark"props" +
437
+ Exp / mark"stm"
438
+ ) * sym","^-1),
439
+
440
+ Export = key"export" * (
441
+ Cc"class" * ClassDecl +
442
+ op"*" + op"^" +
443
+ Ct(NameList) * (sym"=" * Ct(ExpListLow))^-1) / mark"export",
444
+
445
+ KeyValue = (sym":" * Name) / self_assign + Ct((SimpleName + sym"[" * Exp * sym"]") * symx":" * (Exp + TableBlock)),
446
+ KeyValueList = KeyValue * (sym"," * KeyValue)^0,
447
+ KeyValueLine = CheckIndent * KeyValueList * sym","^-1,
448
+
449
+ FnArgsDef = sym"(" * Ct(FnArgDefList^-1) *
450
+ (key"using" * Ct(NameList + Space * "nil") + Ct"") *
451
+ sym")" + Ct"" * Ct"",
452
+
453
+ FnArgDefList = FnArgDef * (sym"," * FnArgDef)^0,
454
+ FnArgDef = Ct(Name * (sym"=" * Exp)^-1),
455
+
456
+ FunLit = FnArgsDef *
457
+ (sym"->" * Cc"slim" + sym"=>" * Cc"fat") *
458
+ (Body + Ct"") / mark"fndef",
459
+
460
+ NameList = Name * (sym"," * Name)^0,
461
+ ExpList = Exp * (sym"," * Exp)^0,
462
+ ExpListLow = Exp * ((sym"," + sym";") * Exp)^0,
463
+
464
+ InvokeArgs = ExpList * (sym"," * (TableBlock + SpaceBreak * Advance * ArgBlock * TableBlock^-1) + TableBlock)^-1 + TableBlock,
465
+ ArgBlock = ArgLine * (sym"," * SpaceBreak * ArgLine)^0 * PopIndent,
466
+ ArgLine = CheckIndent * ExpList
467
+ }
468
+
469
+ return {
470
+ _g = White * g * White * -1,
471
+ match = function(self, str, ...)
472
+
473
+ local pos_to_line = function(pos)
474
+ return util.pos_to_line(str, pos)
475
+ end
476
+
477
+ local get_line = function(num)
478
+ return util.get_line(str, num)
479
+ end
480
+
481
+ local tree
482
+ local args = {...}
483
+ local pass, err = assert(pcall(function()
484
+ tree = self._g:match(str, unpack(args))
485
+ end))
486
+
487
+ if not tree then
488
+ local line_no = pos_to_line(last_pos)
489
+ local line_str = get_line(line_no) or ""
490
+
491
+ return nil, err_msg:format(line_no, trim(line_str), _indent:top())
492
+ end
493
+ return tree
494
+ end
495
+ }
496
+
497
+ end)
498
+
499
+ -- parse a string
500
+ -- returns tree, or nil and error message
501
+ function string(str)
502
+ local g = build_grammar()
503
+ return g:match(str)
504
+ end
505
+