mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2025-04-02 10:41:54 -04:00
478 lines
13 KiB
Lua
478 lines
13 KiB
Lua
--lualoader, R"EOF(--
|
|
|
|
local string = string
|
|
local tonumber = tonumber
|
|
local setmetatable = setmetatable
|
|
local error = error
|
|
local ipairs = ipairs
|
|
local io = io
|
|
local table = table
|
|
local math = math
|
|
local assert = assert
|
|
local tostring = tostring
|
|
local type = type
|
|
local insert_tab = table.insert
|
|
|
|
local function meta(name, t)
|
|
t = t or {}
|
|
t.__name = name
|
|
t.__index = t
|
|
return t
|
|
end
|
|
|
|
local function default(t, k, def)
|
|
local v = t[k]
|
|
if not v then
|
|
v = def or {}
|
|
t[k] = v
|
|
end
|
|
return v
|
|
end
|
|
|
|
local toplevel = {} do
|
|
|
|
local labels = { optional = 1; required = 2; repeated = 3 }
|
|
|
|
local key_types = {
|
|
int32 = 5; int64 = 3; uint32 = 13;
|
|
uint64 = 4; sint32 = 17; sint64 = 18;
|
|
fixed32 = 7; fixed64 = 6; sfixed32 = 15;
|
|
sfixed64 = 16; bool = 8; string = 9;
|
|
}
|
|
|
|
local com_types = {
|
|
group = 10; message = 11; enum = 14;
|
|
}
|
|
|
|
local types = {
|
|
double = 1; float = 2; int32 = 5;
|
|
int64 = 3; uint32 = 13; uint64 = 4;
|
|
sint32 = 17; sint64 = 18; fixed32 = 7;
|
|
fixed64 = 6; sfixed32 = 15; sfixed64 = 16;
|
|
bool = 8; string = 9; bytes = 12;
|
|
group = 10; message = 11; enum = 14;
|
|
}
|
|
|
|
local function register_type(self, lex, tname, typ)
|
|
if not tname:match "%."then
|
|
tname = self.prefix..tname
|
|
end
|
|
if self.typemap[tname] then
|
|
return lex:error("type %s already defined", tname)
|
|
end
|
|
self.typemap[tname] = typ
|
|
end
|
|
|
|
local function type_info(lex, tname)
|
|
local tenum = types[tname]
|
|
if com_types[tname] then
|
|
return lex:error("invalid type name: "..tname)
|
|
elseif tenum then
|
|
tname = nil
|
|
end
|
|
return tenum, tname
|
|
end
|
|
|
|
local function map_info(lex)
|
|
local keyt = lex:ident "key type"
|
|
if not key_types[keyt] then
|
|
return lex:error("invalid key type: "..keyt)
|
|
end
|
|
local valt = lex:expected "," :type_name()
|
|
local name = lex:expected ">" :ident()
|
|
local ident = name:gsub("^%a", string.upper)
|
|
:gsub("_(%a)", string.upper).."Entry"
|
|
local kt, ktn = type_info(lex, keyt)
|
|
local vt, vtn = type_info(lex, valt)
|
|
return name, types.message, ident, {
|
|
name = ident,
|
|
field = {
|
|
{
|
|
name = "key",
|
|
number = 1;
|
|
label = labels.optional,
|
|
type = kt,
|
|
type_name = ktn
|
|
},
|
|
{
|
|
name = "value",
|
|
number = 2;
|
|
label = labels.optional,
|
|
type = vt,
|
|
type_name = vtn
|
|
},
|
|
},
|
|
options = { map_entry = true }
|
|
}
|
|
end
|
|
|
|
local function inline_option(lex, info)
|
|
if lex:test "%[" then
|
|
info = info or {}
|
|
while true do
|
|
local name = lex:option_name()
|
|
local value = lex:expected '=' :constant()
|
|
info[name] = value
|
|
if lex:test "%]" then
|
|
return info
|
|
end
|
|
lex:expected ','
|
|
end
|
|
end
|
|
end
|
|
|
|
local function field(self, lex, ident)
|
|
local name, typ, type_name, map_entry
|
|
if ident == "map" and lex:test "%<" then
|
|
name, typ, type_name, map_entry = map_info(lex)
|
|
self.locmap[map_entry.field[1]] = lex.pos
|
|
self.locmap[map_entry.field[2]] = lex.pos
|
|
register_type(self, lex, type_name, types.message)
|
|
else
|
|
typ, type_name = type_info(lex, ident)
|
|
name = lex:ident()
|
|
end
|
|
local info = {
|
|
name = name,
|
|
number = lex:expected "=":integer(),
|
|
label = ident == "map" and labels.repeated or labels.optional,
|
|
type = typ,
|
|
type_name = type_name
|
|
}
|
|
local options = inline_option(lex)
|
|
if options then
|
|
info.default_value, options.default = tostring(options.default), nil
|
|
info.json_name, options.json_name = options.json_name, nil
|
|
if options.packed and options.packed == "false" then
|
|
options.packed = false
|
|
end
|
|
info.options = options
|
|
end
|
|
if info.number <= 0 then
|
|
lex:error("invalid tag number: "..info.number)
|
|
end
|
|
return info, map_entry
|
|
end
|
|
|
|
local function label_field(self, lex, ident, parent)
|
|
local label = labels[ident]
|
|
local info, map_entry
|
|
if not label then
|
|
if self.syntax == "proto2" and ident ~= "map" then
|
|
return lex:error("proto2 disallow missing label")
|
|
end
|
|
return field(self, lex, ident)
|
|
end
|
|
local proto3_optional = label == labels.optional and self.syntax == "proto3"
|
|
if proto3_optional and not (self.proto3_optional and parent) then
|
|
return lex:error("proto3 disallow 'optional' label")
|
|
end
|
|
info, map_entry = field(self, lex, lex:type_name())
|
|
if proto3_optional then
|
|
local ot = default(parent, "oneof_decl")
|
|
info.oneof_index = #ot
|
|
ot[#ot+1] = { name = "optional_" .. info.name }
|
|
else
|
|
info.label = label
|
|
end
|
|
return info, map_entry
|
|
end
|
|
|
|
function toplevel:package(lex, info)
|
|
local package = lex:full_ident 'package name'
|
|
lex:line_end()
|
|
info.package = package
|
|
self.prefix = "."..package.."."
|
|
return self
|
|
end
|
|
|
|
function toplevel:import(lex, info)
|
|
local mode = lex:ident('"weak" or "public"', 'opt') or "public"
|
|
if mode ~= 'weak' and mode ~= 'public' then
|
|
return lex:error '"weak or "public" expected'
|
|
end
|
|
local name = lex:quote()
|
|
lex:line_end()
|
|
local result = self:parsefile(name)
|
|
if self.on_import then
|
|
self.on_import(result)
|
|
end
|
|
local dep = default(info, 'dependency')
|
|
local index = #dep
|
|
dep[index+1] = name
|
|
if mode == "public" then
|
|
local it = default(info, 'public_dependency')
|
|
insert_tab(it, index)
|
|
else
|
|
local it = default(info, 'weak_dependency')
|
|
insert_tab(it, index)
|
|
end
|
|
end
|
|
|
|
local msgbody = {} do
|
|
|
|
function msgbody:message(lex, info)
|
|
local nested_type = default(info, 'nested_type')
|
|
insert_tab(nested_type, toplevel.message(self, lex))
|
|
return self
|
|
end
|
|
|
|
function msgbody:enum(lex, info)
|
|
local nested_type = default(info, 'enum_type')
|
|
insert_tab(nested_type, toplevel.enum(self, lex))
|
|
return self
|
|
end
|
|
|
|
function msgbody:extend(lex, info)
|
|
local extension = default(info, 'extension')
|
|
local nested_type = default(info, 'nested_type')
|
|
local ft, mt = toplevel.extend(self, lex, {})
|
|
for _, v in ipairs(ft) do
|
|
insert_tab(extension, v)
|
|
end
|
|
for _, v in ipairs(mt) do
|
|
insert_tab(nested_type, v)
|
|
end
|
|
return self
|
|
end
|
|
|
|
function msgbody:extensions(lex, info)
|
|
local rt = default(info, 'extension_range')
|
|
local idx = #rt
|
|
repeat
|
|
local start = lex:integer "field number range"
|
|
local stop = math.floor(2^29)
|
|
if lex:keyword('to', 'opt') then
|
|
if not lex:keyword('max', 'opt') then
|
|
stop = lex:integer "field number range end or 'max'"
|
|
end
|
|
insert_tab(rt, { start = start, ['end'] = stop })
|
|
else
|
|
insert_tab(rt, { start = start, ['end'] = start })
|
|
end
|
|
until not lex:test ','
|
|
rt[idx+1].options = inline_option(lex)
|
|
lex:line_end()
|
|
return self
|
|
end
|
|
|
|
function msgbody:reserved(lex, info)
|
|
lex:whitespace()
|
|
if not lex '^%d' then
|
|
local rt = default(info, 'reserved_name')
|
|
repeat
|
|
insert_tab(rt, (lex:quote()))
|
|
until not lex:test ','
|
|
else
|
|
local rt = default(info, 'reserved_range')
|
|
local first = true
|
|
repeat
|
|
local start = lex:integer(first and 'field name or number range'
|
|
or 'field number range')
|
|
if lex:keyword('to', 'opt') then
|
|
if lex:keyword('max', 'opt') then
|
|
insert_tab(rt, { start = start, ['end'] = 2^29-1 })
|
|
else
|
|
local stop = lex:integer 'field number range end'
|
|
insert_tab(rt, { start = start, ['end'] = stop })
|
|
end
|
|
else
|
|
insert_tab(rt, { start = start, ['end'] = start })
|
|
end
|
|
first = false
|
|
until not lex:test ','
|
|
end
|
|
lex:line_end()
|
|
return self
|
|
end
|
|
|
|
function msgbody:oneof(lex, info)
|
|
local fs = default(info, "field")
|
|
local ts = default(info, "nested_type")
|
|
local ot = default(info, "oneof_decl")
|
|
local index = #ot + 1
|
|
local oneof = { name = lex:ident() }
|
|
lex:expected "{"
|
|
while not lex:test "}" do
|
|
local ident = lex:type_name()
|
|
if ident == "option" then
|
|
toplevel.option(self, lex, oneof)
|
|
else
|
|
local f, t = field(self, lex, ident)
|
|
self.locmap[f] = lex.pos
|
|
if t then insert_tab(ts, t) end
|
|
f.oneof_index = index - 1
|
|
insert_tab(fs, f)
|
|
end
|
|
lex:line_end 'opt'
|
|
end
|
|
ot[index] = oneof
|
|
end
|
|
|
|
function msgbody:option(lex, info)
|
|
toplevel.option(self, lex, info)
|
|
end
|
|
|
|
end
|
|
|
|
function toplevel:message(lex, info)
|
|
local name = lex:ident 'message name'
|
|
local typ = { name = name }
|
|
register_type(self, lex, name, types.message)
|
|
local prefix = self.prefix
|
|
self.prefix = prefix..name.."."
|
|
lex:expected "{"
|
|
while not lex:test "}" do
|
|
local ident, pos = lex:type_name()
|
|
local body_parser = msgbody[ident]
|
|
if body_parser then
|
|
body_parser(self, lex, typ)
|
|
else
|
|
local fs = default(typ, 'field')
|
|
local f, t = label_field(self, lex, ident, typ)
|
|
self.locmap[f] = pos
|
|
insert_tab(fs, f)
|
|
if t then
|
|
local ts = default(typ, 'nested_type')
|
|
insert_tab(ts, t)
|
|
end
|
|
end
|
|
lex:line_end 'opt'
|
|
end
|
|
lex:line_end 'opt'
|
|
if info then
|
|
info = default(info, 'message_type')
|
|
insert_tab(info, typ)
|
|
end
|
|
self.prefix = prefix
|
|
return typ
|
|
end
|
|
|
|
function toplevel:enum(lex, info)
|
|
local name, pos = lex:ident 'enum name'
|
|
local enum = { name = name }
|
|
self.locmap[enum] = pos
|
|
register_type(self, lex, name, types.enum)
|
|
lex:expected "{"
|
|
while not lex:test "}" do
|
|
local ident, pos = lex:ident 'enum constant name'
|
|
if ident == 'option' then
|
|
toplevel.option(self, lex, enum)
|
|
elseif ident == 'reserved' then
|
|
msgbody.reserved(self, lex, enum)
|
|
else
|
|
local values = default(enum, 'value')
|
|
local number = lex:expected '=' :integer()
|
|
local value = {
|
|
name = ident,
|
|
number = number,
|
|
options = inline_option(lex)
|
|
}
|
|
self.locmap[value] = pos
|
|
insert_tab(values, value)
|
|
end
|
|
lex:line_end 'opt'
|
|
end
|
|
lex:line_end 'opt'
|
|
if info then
|
|
info = default(info, 'enum_type')
|
|
insert_tab(info, enum)
|
|
end
|
|
return enum
|
|
end
|
|
|
|
function toplevel:option(lex, info)
|
|
local ident = lex:option_name()
|
|
lex:expected "="
|
|
local value = lex:constant()
|
|
lex:line_end()
|
|
local options = info and default(info, 'options') or {}
|
|
options[ident] = value
|
|
return options, self
|
|
end
|
|
|
|
function toplevel:extend(lex, info)
|
|
local name = lex:type_name()
|
|
local ft = info and default(info, 'extension') or {}
|
|
local mt = info and default(info, 'message_type') or {}
|
|
lex:expected "{"
|
|
while not lex:test "}" do
|
|
local ident, pos = lex:type_name()
|
|
local f, t = label_field(self, lex, ident)
|
|
self.locmap[f] = pos
|
|
f.extendee = name
|
|
insert_tab(ft, f)
|
|
insert_tab(mt, t)
|
|
lex:line_end 'opt'
|
|
end
|
|
return ft, mt
|
|
end
|
|
|
|
local svr_body = {} do
|
|
|
|
function svr_body:rpc(lex, info)
|
|
local name, pos = lex:ident "rpc name"
|
|
local rpc = { name = name }
|
|
self.locmap[rpc] = pos
|
|
local _, tn
|
|
lex:expected "%("
|
|
rpc.client_streaming = lex:keyword("stream", "opt")
|
|
_, tn = type_info(lex, lex:type_name())
|
|
if not tn then return lex:error "rpc input type must by message" end
|
|
rpc.input_type = tn
|
|
lex:expected "%)" :expected "returns" :expected "%("
|
|
rpc.server_streaming = lex:keyword("stream", "opt")
|
|
_, tn = type_info(lex, lex:type_name())
|
|
if not tn then return lex:error "rpc output type must by message" end
|
|
rpc.output_type = tn
|
|
lex:expected "%)"
|
|
if lex:test "{" then
|
|
while not lex:test "}" do
|
|
lex:line_end "opt"
|
|
lex:keyword "option"
|
|
toplevel.option(self, lex, rpc)
|
|
end
|
|
end
|
|
lex:line_end "opt"
|
|
local t = default(info, "method")
|
|
insert_tab(t, rpc)
|
|
end
|
|
|
|
function svr_body:option(lex, info)
|
|
return toplevel.option(self, lex, info)
|
|
end
|
|
|
|
function svr_body.stream(_, lex)
|
|
lex:error "stream not implement yet"
|
|
end
|
|
|
|
end
|
|
|
|
function toplevel:service(lex, info)
|
|
local name, pos = lex:ident 'service name'
|
|
local svr = { name = name }
|
|
self.locmap[svr] = pos
|
|
lex:expected "{"
|
|
while not lex:test "}" do
|
|
local ident = lex:type_name()
|
|
local body_parser = svr_body[ident]
|
|
if body_parser then
|
|
body_parser(self, lex, svr)
|
|
else
|
|
return lex:error "expected 'rpc' or 'option' in service body"
|
|
end
|
|
lex:line_end 'opt'
|
|
end
|
|
lex:line_end 'opt'
|
|
if info then
|
|
info = default(info, 'service')
|
|
insert_tab(info, svr)
|
|
end
|
|
return svr
|
|
end
|
|
|
|
end
|
|
|
|
return toplevel
|
|
|
|
-- )EOF"
|