moonscript/moonscript/parse.lua
2016-04-07 20:55:36 -07:00

245 lines
13 KiB
Lua

local debug_grammar = false
local lpeg = require("lpeg")
lpeg.setmaxstack(10000)
local err_msg = "Failed to parse:%s\n [%d] >> %s"
local Stack
Stack = require("moonscript.data").Stack
local trim, pos_to_line, get_line
do
local _obj_0 = require("moonscript.util")
trim, pos_to_line, get_line = _obj_0.trim, _obj_0.pos_to_line, _obj_0.get_line
end
local unpack
unpack = require("moonscript.util").unpack
local wrap_env
wrap_env = require("moonscript.parse.env").wrap_env
local R, S, V, P, C, Ct, Cmt, Cg, Cb, Cc
R, S, V, P, C, Ct, Cmt, Cg, Cb, Cc = lpeg.R, lpeg.S, lpeg.V, lpeg.P, lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cb, lpeg.Cc
local White, Break, Stop, Comment, Space, SomeSpace, SpaceBreak, EmptyLine, AlphaNum, Num, Shebang, L, _Name
do
local _obj_0 = require("moonscript.parse.literals")
White, Break, Stop, Comment, Space, SomeSpace, SpaceBreak, EmptyLine, AlphaNum, Num, Shebang, L, _Name = _obj_0.White, _obj_0.Break, _obj_0.Stop, _obj_0.Comment, _obj_0.Space, _obj_0.SomeSpace, _obj_0.SpaceBreak, _obj_0.EmptyLine, _obj_0.AlphaNum, _obj_0.Num, _obj_0.Shebang, _obj_0.L, _obj_0.Name
end
local SpaceName = Space * _Name
Num = Space * (Num / function(v)
return {
"number",
v
}
end)
local Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, wrap_decorator, check_lua_string, self_assign
do
local _obj_0 = require("moonscript.parse.util")
Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, wrap_decorator, check_lua_string, self_assign = _obj_0.Indent, _obj_0.Cut, _obj_0.ensure, _obj_0.extract_line, _obj_0.mark, _obj_0.pos, _obj_0.flatten_or_mark, _obj_0.is_assignable, _obj_0.check_assignable, _obj_0.format_assign, _obj_0.format_single_assign, _obj_0.sym, _obj_0.symx, _obj_0.simple_string, _obj_0.wrap_func_arg, _obj_0.join_chain, _obj_0.wrap_decorator, _obj_0.check_lua_string, _obj_0.self_assign
end
local build_grammar = wrap_env(debug_grammar, function(root)
local _indent = Stack(0)
local _do_stack = Stack(0)
local state = {
last_pos = 0
}
local check_indent
check_indent = function(str, pos, indent)
state.last_pos = pos
return _indent:top() == indent
end
local advance_indent
advance_indent = function(str, pos, indent)
local top = _indent:top()
if top ~= -1 and indent > top then
_indent:push(indent)
return true
end
end
local push_indent
push_indent = function(str, pos, indent)
_indent:push(indent)
return true
end
local pop_indent
pop_indent = function()
assert(_indent:pop(), "unexpected outdent")
return true
end
local check_do
check_do = function(str, pos, do_node)
local top = _do_stack:top()
if top == nil or top then
return true, do_node
end
return false
end
local disable_do
disable_do = function()
_do_stack:push(false)
return true
end
local pop_do
pop_do = function()
assert(_do_stack:pop() ~= nil, "unexpected do pop")
return true
end
local DisableDo = Cmt("", disable_do)
local PopDo = Cmt("", pop_do)
local keywords = { }
local key
key = function(chars)
keywords[chars] = true
return Space * chars * -AlphaNum
end
local op
op = function(chars)
local patt = Space * C(chars)
if chars:match("^%w*$") then
keywords[chars] = true
patt = patt * -AlphaNum
end
return patt
end
local Name = Cmt(SpaceName, function(str, pos, name)
if keywords[name] then
return false
end
return true
end) / trim
local SelfName = Space * "@" * ("@" * (_Name / mark("self_class") + Cc("self.__class")) + _Name / mark("self") + Cc("self"))
local KeyName = SelfName + Space * _Name / mark("key_literal")
local VarArg = Space * P("...") / trim
local g = P({
root or File,
File = Shebang ^ -1 * (Block + Ct("")),
Block = Ct(Line * (Break ^ 1 * Line) ^ 0),
CheckIndent = Cmt(Indent, check_indent),
Line = (CheckIndent * Statement + Space * L(Stop)),
Statement = pos(Import + While + With + For + ForEach + Switch + Return + Local + Export + BreakLoop + Ct(ExpList) * (Update + Assign) ^ -1 / format_assign) * Space * ((key("if") * Exp * (key("else") * Exp) ^ -1 * Space / mark("if") + key("unless") * Exp / mark("unless") + CompInner / mark("comprehension")) * Space) ^ -1 / wrap_decorator,
Body = Space ^ -1 * Break * EmptyLine ^ 0 * InBlock + Ct(Statement),
Advance = L(Cmt(Indent, advance_indent)),
PushIndent = Cmt(Indent, push_indent),
PreventIndent = Cmt(Cc(-1), push_indent),
PopIndent = Cmt("", pop_indent),
InBlock = Advance * Block * PopIndent,
Local = key("local") * ((op("*") + op("^")) / mark("declare_glob") + Ct(NameList) / mark("declare_with_shadows")),
Import = key("import") * Ct(ImportNameList) * SpaceBreak ^ 0 * key("from") * Exp / mark("import"),
ImportName = (sym("\\") * Ct(Cc("colon") * Name) + Name),
ImportNameList = SpaceBreak ^ 0 * ImportName * ((SpaceBreak ^ 1 + sym(",") * SpaceBreak ^ 0) * ImportName) ^ 0,
BreakLoop = Ct(key("break") / trim) + Ct(key("continue") / trim),
Return = key("return") * (ExpListLow / mark("explist") + C("")) / mark("return"),
WithExp = Ct(ExpList) * Assign ^ -1 / format_assign,
With = key("with") * DisableDo * ensure(WithExp, PopDo) * key("do") ^ -1 * Body / mark("with"),
Switch = key("switch") * DisableDo * ensure(Exp, PopDo) * key("do") ^ -1 * Space ^ -1 * Break * SwitchBlock / mark("switch"),
SwitchBlock = EmptyLine ^ 0 * Advance * Ct(SwitchCase * (Break ^ 1 * SwitchCase) ^ 0 * (Break ^ 1 * SwitchElse) ^ -1) * PopIndent,
SwitchCase = key("when") * Ct(ExpList) * key("then") ^ -1 * Body / mark("case"),
SwitchElse = key("else") * Body / mark("else"),
IfCond = Exp * Assign ^ -1 / format_single_assign,
IfElse = (Break * CheckIndent) ^ -1 * EmptyLine ^ 0 * key("else") * Body / mark("else"),
IfElseIf = (Break * CheckIndent) ^ -1 * EmptyLine ^ 0 * key("elseif") * pos(IfCond) * key("then") ^ -1 * Body / mark("elseif"),
If = key("if") * IfCond * key("then") ^ -1 * Body * IfElseIf ^ 0 * IfElse ^ -1 / mark("if"),
Unless = key("unless") * IfCond * key("then") ^ -1 * Body * IfElseIf ^ 0 * IfElse ^ -1 / mark("unless"),
While = key("while") * DisableDo * ensure(Exp, PopDo) * key("do") ^ -1 * Body / mark("while"),
For = key("for") * DisableDo * ensure(Name * sym("=") * Ct(Exp * sym(",") * Exp * (sym(",") * Exp) ^ -1), PopDo) * key("do") ^ -1 * Body / mark("for"),
ForEach = key("for") * Ct(AssignableNameList) * key("in") * DisableDo * ensure(Ct(sym("*") * Exp / mark("unpack") + ExpList), PopDo) * key("do") ^ -1 * Body / mark("foreach"),
Do = key("do") * Body / mark("do"),
Comprehension = sym("[") * Exp * CompInner * sym("]") / mark("comprehension"),
TblComprehension = sym("{") * Ct(Exp * (sym(",") * Exp) ^ -1) * CompInner * sym("}") / mark("tblcomprehension"),
CompInner = Ct((CompForEach + CompFor) * CompClause ^ 0),
CompForEach = key("for") * Ct(NameList) * key("in") * (sym("*") * Exp / mark("unpack") + Exp) / mark("foreach"),
CompFor = key("for" * Name * sym("=") * Ct(Exp * sym(",") * Exp * (sym(",") * Exp) ^ -1) / mark("for")),
CompClause = CompFor + CompForEach + key("when") * Exp / mark("when"),
Assign = sym("=") * (Ct(With + If + Switch) + Ct(TableBlock + ExpListLow)) / mark("assign"),
Update = ((sym("..=") + sym("+=") + sym("-=") + sym("*=") + sym("/=") + sym("%=") + sym("or=") + sym("and=")) / trim) * Exp / mark("update"),
CharOperators = Space * C(S("+-*/%^><|&")),
WordOperators = op("or") + op("and") + op("<=") + op(">=") + op("~=") + op("!=") + op("==") + op("..") + op("<<") + op(">>") + op("//"),
BinaryOperator = (WordOperators + CharOperators) * SpaceBreak ^ 0,
Assignable = Cmt(Chain, check_assignable) + Name + SelfName,
Exp = Ct(Value * (BinaryOperator * Value) ^ 0) / flatten_or_mark("exp"),
SimpleValue = If + Unless + Switch + With + ClassDecl + ForEach + For + While + Cmt(Do, check_do) + sym("-") * -SomeSpace * Exp / mark("minus") + sym("#") * Exp / mark("length") + sym("~") * Exp / mark("bitnot") + key("not") * Exp / mark("not") + TblComprehension + TableLit + Comprehension + FunLit + Num,
ChainValue = (Chain + Callable) * Ct(InvokeArgs ^ -1) / join_chain,
Value = pos(SimpleValue + Ct(KeyValueList) / mark("table") + ChainValue + String),
SliceValue = SimpleValue + ChainValue,
String = Space * DoubleString + Space * SingleString + LuaString,
SingleString = simple_string("'"),
DoubleString = simple_string('"', true),
LuaString = Cg(LuaStringOpen, "string_open") * Cb("string_open") * Break ^ -1 * C((1 - Cmt(C(LuaStringClose) * Cb("string_open"), check_lua_string)) ^ 0) * LuaStringClose / mark("string"),
LuaStringOpen = sym("[") * P("=") ^ 0 * "[" / trim,
LuaStringClose = "]" * P("=") ^ 0 * "]",
Callable = pos(Name / mark("ref")) + SelfName + VarArg + Parens / mark("parens"),
Parens = sym("(") * SpaceBreak ^ 0 * Exp * SpaceBreak ^ 0 * sym(")"),
FnArgs = symx("(") * SpaceBreak ^ 0 * Ct(ExpList ^ -1) * SpaceBreak ^ 0 * sym(")") + sym("!") * -P("=") * Ct(""),
Chain = (Callable + String + -S(".\\")) * ChainItems / mark("chain") + Space * (DotChainItem * ChainItems ^ -1 + ColonChain) / mark("chain"),
ChainItems = ChainItem ^ 1 * ColonChain ^ -1 + ColonChain,
ChainItem = Invoke + DotChainItem + Slice + symx("[") * Exp / mark("index") * sym("]"),
DotChainItem = symx(".") * _Name / mark("dot"),
ColonChainItem = symx("\\") * _Name / mark("colon"),
ColonChain = ColonChainItem * (Invoke * ChainItems ^ -1) ^ -1,
Slice = symx("[") * (SliceValue + Cc(1)) * sym(",") * (SliceValue + Cc("")) * (sym(",") * SliceValue) ^ -1 * sym("]") / mark("slice"),
Invoke = FnArgs / mark("call") + SingleString / wrap_func_arg + DoubleString / wrap_func_arg + L(P("[")) * LuaString / wrap_func_arg,
TableValue = KeyValue + Ct(Exp),
TableLit = sym("{") * Ct(TableValueList ^ -1 * sym(",") ^ -1 * (SpaceBreak * TableLitLine * (sym(",") ^ -1 * SpaceBreak * TableLitLine) ^ 0 * sym(",") ^ -1) ^ -1) * White * sym("}") / mark("table"),
TableValueList = TableValue * (sym(",") * TableValue) ^ 0,
TableLitLine = PushIndent * ((TableValueList * PopIndent) + (PopIndent * Cut)) + Space,
TableBlockInner = Ct(KeyValueLine * (SpaceBreak ^ 1 * KeyValueLine) ^ 0),
TableBlock = SpaceBreak ^ 1 * Advance * ensure(TableBlockInner, PopIndent) / mark("table"),
ClassDecl = key("class") * -P(":") * (Assignable + Cc(nil)) * (key("extends") * PreventIndent * ensure(Exp, PopIndent) + C("")) ^ -1 * (ClassBlock + Ct("")) / mark("class"),
ClassBlock = SpaceBreak ^ 1 * Advance * Ct(ClassLine * (SpaceBreak ^ 1 * ClassLine) ^ 0) * PopIndent,
ClassLine = CheckIndent * ((KeyValueList / mark("props") + Statement / mark("stm") + Exp / mark("stm")) * sym(",") ^ -1),
Export = key("export") * (Cc("class") * ClassDecl + op("*") + op("^") + Ct(NameList) * (sym("=") * Ct(ExpListLow)) ^ -1) / mark("export"),
KeyValue = (sym(":") * -SomeSpace * Name * lpeg.Cp()) / self_assign + Ct((KeyName + sym("[") * Exp * sym("]") + DoubleString + SingleString) * symx(":") * (Exp + TableBlock + SpaceBreak ^ 1 * Exp)),
KeyValueList = KeyValue * (sym(",") * KeyValue) ^ 0,
KeyValueLine = CheckIndent * KeyValueList * sym(",") ^ -1,
FnArgsDef = sym("(") * Ct(FnArgDefList ^ -1) * (key("using") * Ct(NameList + Space * "nil") + Ct("")) * sym(")") + Ct("") * Ct(""),
FnArgDefList = FnArgDef * (sym(",") * FnArgDef) ^ 0 * (sym(",") * Ct(VarArg)) ^ 0 + Ct(VarArg),
FnArgDef = Ct((Name + SelfName) * (sym("=") * Exp) ^ -1),
FunLit = FnArgsDef * (sym("->") * Cc("slim") + sym("=>") * Cc("fat")) * (Body + Ct("")) / mark("fndef"),
NameList = Name * (sym(",") * Name) ^ 0,
NameOrDestructure = Name + TableLit,
AssignableNameList = NameOrDestructure * (sym(",") * NameOrDestructure) ^ 0,
ExpList = Exp * (sym(",") * Exp) ^ 0,
ExpListLow = Exp * ((sym(",") + sym(";")) * Exp) ^ 0,
InvokeArgs = -P("-") * (ExpList * (sym(",") * (TableBlock + SpaceBreak * Advance * ArgBlock * TableBlock ^ -1) + TableBlock) ^ -1 + TableBlock),
ArgBlock = ArgLine * (sym(",") * SpaceBreak * ArgLine) ^ 0 * PopIndent,
ArgLine = CheckIndent * ExpList
})
return g, state
end)
local file_parser
file_parser = function()
local g, state = build_grammar()
local file_grammar = White * g * White * -1
return {
match = function(self, str)
local tree
local _, err = xpcall((function()
tree = file_grammar:match(str)
end), function(err)
return debug.traceback(err, 2)
end)
if type(err) == "string" then
return nil, err
end
if not (tree) then
local msg
local err_pos = state.last_pos
if err then
local node
node, msg = unpack(err)
if msg then
msg = " " .. msg
end
err_pos = node[-1]
end
local line_no = pos_to_line(str, err_pos)
local line_str = get_line(str, line_no) or ""
return nil, err_msg:format(msg or "", line_no, trim(line_str))
end
return tree
end
}
end
return {
extract_line = extract_line,
build_grammar = build_grammar,
string = function(str)
return file_parser():match(str)
end
}