SoUI 0.5版本占坑
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

324 lines
11 KiB

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