rufus-lua-moon 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+