local lpeg = require 'lpeg' local tonumber = tonumber local table_concat = table.concat lpeg.locale(lpeg) local S = lpeg.S local P = lpeg.P local R = lpeg.R local C = lpeg.C local V = lpeg.V local Cg = lpeg.Cg local Ct = lpeg.Ct local Cc = lpeg.Cc local Cs = lpeg.Cs local Cp = lpeg.Cp local Cmt = lpeg.Cmt local jass local file local comments local line_count local line_pos local function errorpos(pos, str) local endpos = jass:find('[\r\n]', pos) or (#jass+1) local sp = (' '):rep(pos-line_pos) local line = ('%s|\r\n%s\r\n%s|'):format(sp, jass:sub(line_pos, endpos-1), sp) error(('[%s]第[%d]行: %s:\n===========================\n%s\n==========================='):format(file, line_count, str, line)) end local function err(str) return Cp() / function(pos) errorpos(pos, str) end end local function newline(pos) line_count = line_count + 1 line_pos = pos end local function comment(str) if comments[line_count] then print('注释行重复:' .. line_count) print(comments[line_count]) print(str) end comments[line_count] = str end local w = (1-S' \t\r\n()[]')^0 local function expect(p, ...) if select('#', ...) == 1 then local str = ... return p + w * err(str) else local m, str = ... return p + m * err(str) end end local function keyvalue(key, value) return Cg(Cc(value), key) end local function currentline() return Cg(P(true) / function() return file end, 'file') * Cg(P(true) / function() return line_count end, 'line') end local function endline() return Cg(P(true) / function() return line_count end, 'endline') end local function binary(...) local e1, op = ... if not op then return e1 end local args = {...} local e1 = args[1] for i = 2, #args, 2 do op, e2 = args[i], args[i+1] e1 = { type = op, [1] = e1, [2] = e2, } end return e1 end local function unary(...) local e1, op = ... if not op then return e1 end local args = {...} local e1 = args[#args] for i = #args - 1, 1, -1 do op = args[i] e1 = { type = op, [1] = e1, } end return e1 end local nl = (P'\r\n' + S'\r\n') * Cp() / newline local com = P'//' * C((1-nl)^0) / comment local sp = (S' \t' + P'\xEF\xBB\xBF' + com)^0 local sps = (S' \t' + P'\xEF\xBB\xBF' + com)^1 local cl = com^0 * nl local spl = sp * cl local Keys = {'globals', 'endglobals', 'constant', 'native', 'array', 'and', 'or', 'not', 'type', 'extends', 'function', 'endfunction', 'nothing', 'takes', 'returns', 'call', 'set', 'return', 'if', 'endif', 'elseif', 'else', 'loop', 'endloop', 'exitwhen'} for _, key in ipairs(Keys) do Keys[key] = true end local Id = P{ 'Def', Def = C(V'Id') * Cp() / function(id, pos) if Keys[id] then errorpos(pos-#id, ('不能使用关键字[%s]作为函数名或变量名'):format(id)) end end, Id = R('az', 'AZ') * R('az', 'AZ', '09', '__')^0, } local Cut = #(1-R('az', 'AZ', '09', '__')) + (-P(1)) local function Whole(word) return P(word) * Cut end local Null = Ct(keyvalue('type', 'null') * P'null') local Bool = P{ 'Def', Def = Ct(keyvalue('type', 'boolean') * Cg(V'True' + V'False', 'value')), True = Whole'true' * Cc(true), False = Whole'false' * Cc(false), } local Str = P{ 'Def', Def = Ct(keyvalue('type', 'string') * Cg(V'Str', 'value')), Str = '"' * Cs((nl + V'Char')^0) * '"', Char = V'Esc' + '\\' * err'不合法的转义字符' + (1-P'"'), Esc = P'\\b' + P'\\t' + P'\\r' + P'\\n' + P'\\f' + P'\\"' + P'\\\\', } local Real = P{ 'Def', Def = Ct(keyvalue('type', 'real') * Cg(V'Real', 'value')), Real = V'Neg' * V'Char' / function(neg, n) return neg and -n or n end, Neg = Cc(true) * P'-' * sp + Cc(false), Char = (P'.' * expect(R'09'^1, '不合法的实数') + R'09'^1 * P'.' * R'09'^0) / tonumber, } local Int = P{ 'Def', Def = Ct(keyvalue('type', 'integer') * Cg(V'Int', 'value')), Int = V'Neg' * (V'Int16' + V'Int10' + V'Int256') / function(neg, n) return neg and -n or n end, Neg = Cc(true) * P'-' * sp + Cc(false), Int10 = (P'0' + R'19' * R'09'^0) / tonumber, Int16 = (P'$' + P'0' * S'xX') * expect(R('af', 'AF', '09')^1 / function(n) return tonumber('0x'..n) end, '不合法的16进制整数'), Int256 = "'" * expect((V'C4' + V'C1') * "'", '256进制整数必须是由1个或者4个字符组成'), C4 = V'C4W' * V'C4W' * V'C4W' * V'C4W' / function(n) return ('>I4'):unpack(n) end, C4W = expect(1-P"'"-P'\\', '\\' * P(1), '4个字符组成的256进制整数不能使用转义字符'), C1 = ('\\' * expect(V'Esc', P(1), '不合法的转义字符') + C(1-P"'")) / function(n) return ('I1'):unpack(n) end, Esc = P'b' / function() return '\b' end + P't' / function() return '\t' end + P'r' / function() return '\r' end + P'n' / function() return '\n' end + P'f' / function() return '\f' end + P'"' / function() return '\"' end + P'\\' / function() return '\\' end, } local Value = sp * (Null + Bool + Str + Real + Int) * sp local Exp = P{ 'Def', -- 由低优先级向高优先级递归 Def = V'And', And = V'Or' * (C(Whole'and') * sp * V'Or')^0 / binary, Or = V'Compare' * (C(Whole'or') * sp * V'Compare')^0 / binary, Compare = V'Not' * (C(S'><=!' * P'=' + S'><') * sp * V'Not')^0 / binary, Not = sp * (C(Whole'not') * sp)^0 * V'AddSub' / unary, AddSub = V'MulDiv' * (C(S'+-') * sp * V'MulDiv')^0 / binary, MulDiv = V'Exp' * (C(S'*/') * sp * V'Exp')^0 / binary, Exp = V'Paren' + V'Code' + V'Call' + Value + V'Neg' + V'Vari' + V'Var', Paren = sp * '(' * V'Def' * ')' * sp, Code = Ct(keyvalue('type', 'code') * sp * Whole'function' * sps * Cg(Id, 'name') * sp), Call = Ct(keyvalue('type', 'call') * sp * Cg(Id, 'name') * sp * '(' * V'Args' * ')' * sp), Vari = Ct(keyvalue('type', 'vari') * sp * Cg(Id, 'name') * sp * '[' * Cg(V'Def', 1) * ']' * sp), Var = Ct(keyvalue('type', 'var') * sp * Cg(Id, 'name') * sp), Neg = Ct(keyvalue('type', 'neg') * sp * '-' * sp * Cg(V'Exp', 1)), Args = V'Def' * (',' * V'Def')^0 + sp, } local Type = P{ 'Def', Def = Ct(sp * Whole'type' * keyvalue('type', 'type') * currentline() * expect(sps * Cg(Id, 'name'), '变量类型定义错误') * expect(V'Ext', '类型继承错误')), Ext = sps * Whole'extends' * sps * Cg(Id, 'extends'), } local Global = P{ 'Global', Global = Ct(sp * Whole'globals' * keyvalue('type', 'globals') * currentline() * V'Vals' * V'End'), Vals = (spl + V'Def' * spl)^0, Def = Ct(currentline() * sp * (Whole'constant' * sps * keyvalue('constant', true) + P(true)) * Cg(Id, 'type') * sps * (Whole'array' * sps * keyvalue('array', true) + P(true)) * Cg(Id, 'name') * (sp * '=' * Cg(Exp) + P(true)) ), End = expect(sp * Whole'endglobals', '缺少endglobals'), } local Local = P{ 'Def', Def = Ct(currentline() * sp * Whole'local' * sps * Cg(Id, 'type') * sps * (Whole'array' * sps * keyvalue('array', true) + P(true)) * Cg(Id, 'name') * (sp * '=' * Cg(Exp) + P(true)) ), } local Line = P{ 'Def', Def = sp * (V'Call' + V'Set' + V'Seti' + V'Return' + V'Exit'), Call = Ct(keyvalue('type', 'call') * currentline() * Whole'call' * sps * Cg(Id, 'name') * sp * '(' * V'Args' * ')' * sp), Args = Exp * (',' * Exp)^0 + sp, Set = Ct(keyvalue('type', 'set') * currentline() * Whole'set' * sps * Cg(Id, 'name') * sp * '=' * Exp), Seti = Ct(keyvalue('type', 'seti') * currentline() * Whole'set' * sps * Cg(Id, 'name') * sp * '[' * Cg(Exp, 1) * ']' * sp * '=' * Cg(Exp, 2)), Return = Ct(keyvalue('type', 'return') * currentline() * Whole'return' * (Cg(Exp, 1) + P(true))), Exit = Ct(keyvalue('type', 'exit') * currentline() * Whole'exitwhen' * Cg(Exp, 1)), } local Logic = P{ 'Def', Def = V'If' + V'Loop', If = Ct(keyvalue('type', 'if') * currentline() * sp * V'Ifif' * V'Ifelseif'^0 * V'Ifelse'^-1 * sp * 'endif' * endline() ), Ifif = Ct(keyvalue('type', 'if') * currentline() * sp * Whole'if' * Cg(Exp, 'condition') * Whole'then' * spl * V'Ifdo'), Ifelseif = Ct(keyvalue('type', 'elseif') * currentline() * sp * Whole'elseif' * Cg(Exp, 'condition') * Whole'then' * spl * V'Ifdo'), Ifelse = Ct(keyvalue('type', 'else') * currentline() * sp * Whole'else' * spl * V'Ifdo'), Ifdo = (spl + V'Def' + Line * spl)^0, Loop = Ct(keyvalue('type', 'loop') * currentline() * sp * Whole'loop' * spl * (spl + V'Def' + Line * spl)^0 * sp * Whole'endloop' * endline() ), } local Function = P{ 'Def', Def = Ct(keyvalue('type', 'function') * currentline() * (V'Common' + V'Native')), Native = sp * (Whole'constant' * keyvalue('constant', true) + P(true)) * sp * Whole'native' * keyvalue('native', true) * V'Head', Common = sp * (Whole'constant' * keyvalue('constant', true) + P(true)) * sp * Whole'function' * V'Head' * V'Content' * V'End', Head = sps * Cg(Id, 'name') * sps * Whole'takes' * sps * V'Takes' * sps * Whole'returns' * sps * V'Returns' * spl, Takes = (Whole'nothing' + Cg(V'Args', 'args')), Args = Ct(sp * V'Arg' * (sp * ',' * sp * V'Arg')^0), Arg = Ct(Cg(Id, 'type') * sps * Cg(Id, 'name')), Returns = Whole'nothing' + Cg(Id, 'returns'), Content = sp * Cg(V'Locals', 'locals') * V'Lines', Locals = Ct((spl + Local * spl)^0), Lines = (spl + Logic * spl + Line * spl)^0, End = expect(sp * Whole'endfunction', '缺少endfunction') * endline(), } local pjass = expect(sps + cl + Type + Function + Global, P(1), '语法不正确')^0 local mt = {} setmetatable(mt, mt) mt.Value = Value mt.Id = Id mt.Exp = Exp mt.Global = Global mt.Local = Local mt.Line = Line mt.Logic = Logic mt.Function = Function function mt:__call(_jass, _file, mode) jass = _jass file = _file comments = {} line_count = 1 line_pos = 1 lpeg.setmaxstack(1000) if mode then return Ct(expect(mt[mode] + spl, P(1), '语法不正确')^0):match(_jass) else return Ct(pjass):match(_jass), comments end end return mt