123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- -----------------------
- -- simple proto parser
- -----------------------
- local lpeg = require "lpeg"
- local P = lpeg.P
- local S = lpeg.S
- local R = lpeg.R
- local C = lpeg.C
- local Ct = lpeg.Ct
- local Cg = lpeg.Cg
- local Cc = lpeg.Cc
- local V = lpeg.V
- local next = next
- local error = error
- local tonumber = tonumber
- local pairs = pairs
- local ipairs = ipairs
- local rawset = rawset
- local tinsert = table.insert
- local smatch = string.match
- local sbyte = string.byte
- local internal_type = {
- double = "TYPE_DOUBLE",
- float = "TYPE_FLOAT",
- uint64 = "TYPE_UINT64",
- int = "TYPE_INT32",
- int32 = "TYPE_INT32",
- int64 = "TYPE_INT64",
- fixed64 = "TYPE_FIXED64",
- fixed32 = "TYPE_FIXED32",
- bool = "TYPE_BOOL",
- string = "TYPE_STRING",
- bytes = "TYPE_BYTES",
- uint32 = "TYPE_UINT32",
- sfixed32 = "TYPE_SFIXED32",
- sfixed64 = "TYPE_SFIXED64",
- sint32 = "TYPE_SINT32",
- sint64 = "TYPE_SINT64",
- }
- local function count_lines(_,pos, parser_state)
- if parser_state.pos < pos then
- parser_state.line = parser_state.line + 1
- parser_state.pos = pos
- end
- return pos
- end
- local exception = lpeg.Cmt( lpeg.Carg(1) , function ( _ , pos, parser_state)
- error( "syntax error at [" .. (parser_state.file or "") .."] (" .. parser_state.line ..")" )
- return pos
- end)
- local eof = P(-1)
- local newline = lpeg.Cmt((P"\n" + "\r\n") * lpeg.Carg(1) ,count_lines)
- local line_comment = "//" * (1 - newline) ^0 * (newline + eof)
- local blank = S" \t" + newline + line_comment
- local blank0 = blank ^ 0
- local blanks = blank ^ 1
- local alpha = R"az" + R"AZ" + "_"
- local alnum = alpha + R"09"
- local str_c = (1 - S("\\\"")) + P("\\") * 1
- local str = P"\"" * C(str_c^0) * "\""
- local dotname = ("." * alpha * alnum ^ 0) ^ 0
- local typename = C(alpha * alnum ^ 0 * dotname)
- local name = C(alpha * alnum ^ 0)
- local filename = P"\"" * C((alnum + "/" + "." + "-")^1) * "\""
- local id = R"09" ^ 1 / tonumber + "max" * Cc(-1)
- local bool = "true" * Cc(true) + "false" * Cc(false)
- local value = str + bool + name + id
- local patterns = {}
- local enum_item = Cg(name * blank0 * "=" * blank0 * id * blank0 * ";" * blank0)
- local function insert(tbl, k,v)
- tinsert(tbl, { name = k , number = v })
- return tbl
- end
- patterns.ENUM = Ct(Cg("enum","type") * blanks * Cg(typename,"name") * blank0 *
- "{" * blank0 *
- Cg(lpeg.Cf(Ct"" * enum_item^1 , insert),"value")
- * "}" * blank0)
- local prefix_field = P"required" * Cc"LABEL_REQUIRED" +
- P"optional" * Cc"LABEL_OPTIONAL" +
- P"repeated" * Cc"LABEL_REPEATED"
- local postfix_pair = blank0 * Cg(name * blank0 * "=" * blank0 * value * blank0)
- local postfix_pair_2 = blank0 * "," * postfix_pair
- local postfix_field = "[" * postfix_pair * postfix_pair_2^0 * blank0 * "]"
- local options = lpeg.Cf(Ct"" * postfix_field , rawset) ^ -1
- local function setoption(t, options)
- if next(options) then
- t.options = options
- end
- return t
- end
- local message_field = lpeg.Cf (
- Ct( Cg(prefix_field,"label") * blanks *
- Cg(typename,"type_name") * blanks *
- Cg(name,"name") * blank0 * "=" * blank0 *
- Cg(id,"number")
- ) * blank0 * options ,
- setoption) * blank0 * ";" * blank0
- local extensions = Ct(
- Cg("extensions" , "type") * blanks *
- Cg(id,"start") * blanks * "to" * blanks *
- Cg(id,"end") * blank0 * ";" * blank0
- )
- patterns.EXTEND = Ct(
- Cg("extend", "type") * blanks *
- Cg(typename, "name") * blank0 * "{" * blank0 *
- Cg(Ct((message_field) ^ 1),"extension") * "}" * blank0
- )
- patterns.MESSAGE = P { Ct(
- Cg("message","type") * blanks *
- Cg(typename,"name") * blank0 * "{" * blank0 *
- Cg(Ct((message_field + patterns.ENUM + extensions + patterns.EXTEND + V(1)) ^ 0),"items") * "}" * blank0
- ) }
- patterns.OPTION = Ct(
- Cg("option" , "type") * blanks *
- Cg(name, "name") * blank0 * "=" * blank0 *
- Cg(value, "value")
- ) * blank0 * ";" * blank0
- patterns.IMPORT = Ct( Cg("import" , "type") * blanks * Cg(filename, "name") ) * blank0 * ";" * blank0
- patterns.PACKAGE = Ct( Cg("package", "type") * blanks * Cg(typename, "name") ) * blank0 * ";" * blank0
- local proto_tbl = { "PROTO" }
- do
- local k, v = next(patterns)
- local p = V(k)
- proto_tbl[k] = v
- for k,v in next , patterns , k do
- proto_tbl[k] = v
- p = p + V(k)
- end
- proto_tbl.PROTO = Ct(blank0 * p ^ 1)
- end
- local proto = P(proto_tbl)
- local deal = {}
- function deal:import(v)
- self.dependency = self.dependency or {}
- tinsert(self.dependency , v.name)
- end
- function deal:package(v)
- self.package = v.name
- end
- function deal:enum(v)
- self.enum_type = self.enum_type or {}
- tinsert(self.enum_type , v)
- end
- function deal:option(v)
- self.options = self.options or {}
- self.options[v.name] = v.value
- end
- function deal:extend(v)
- self.extension = self.extension or {}
- local extendee = v.name
- for _,v in ipairs(v.extension) do
- v.extendee = extendee
- v.type = internal_type[v.type_name]
- if v.type then
- v.type_name = nil
- end
- tinsert(self.extension , v)
- end
- end
- function deal:extensions(v)
- self.extension_range = self.extension_range or {}
- tinsert(self.extension_range, v)
- end
- local function _add_nested_message(self, item)
- if item.type == nil then
- item.type = internal_type[item.type_name]
- if item.type then
- item.type_name = nil
- end
- self.field = self.field or {}
- tinsert(self.field, item)
- else
- local f = deal[item.type]
- item.type = nil
- f(self , item)
- end
- end
- function deal:message(v)
- self.nested_type = self.nested_type or {}
- local m = { name = v.name }
- tinsert(self.nested_type , m)
- for _,v in ipairs(v.items) do
- _add_nested_message(m, v)
- end
- end
- local function fix(r)
- local p = {}
- for _,v in ipairs(r) do
- local f = deal[v.type]
- v.type = nil
- f(p , v)
- end
- p.message_type = p.nested_type
- p.nested_type = nil
- return p
- end
- --- fix message name
- local NULL = {}
- local function _match_name(namespace , n , all)
- if sbyte(n) == 46 then
- return n
- end
- repeat
- local name = namespace .. "." .. n
- if all[name] then
- return name
- end
- namespace = smatch(namespace,"(.*)%.[%w_]+$")
- until namespace == nil
- end
- local function _fix_field(namespace , field, all)
- local type_name = field.type_name
- if type_name == "" then
- field.type_name = nil
- return
- elseif type_name == nil then
- return
- end
- local full_name = assert(_match_name(namespace, field.type_name, all) , field.type_name , all)
- field.type_name = full_name
- field.type = all[full_name]
- local options = field.options
- if options then
- if options.default then
- field.default_value = tostring(options.default)
- options.default = nil
- end
- if next(options) == nil then
- field.options = nil
- end
- end
- end
- local function _fix_extension(namespace, ext, all)
- for _,field in ipairs(ext or NULL) do
- field.extendee = assert(_match_name(namespace, field.extendee,all),field.extendee)
- _fix_field(namespace , field , all)
- end
- end
- local function _fix_message(msg , all)
- for _,field in ipairs(msg.field or NULL) do
- _fix_field(assert(all[msg],msg.name) , field , all)
- end
- for _,nest in ipairs(msg.nested_type or NULL) do
- _fix_message(nest , all)
- end
- _fix_extension(all[msg] , msg.extension , all)
- end
- local function _fix_typename(file , all)
- for _,message in ipairs(file.message_type or NULL) do
- _fix_message(message , all)
- end
- _fix_extension(file.package , file.extension , all)
- end
- --- merge messages
- local function _enum_fullname(prefix, enum , all)
- local fullname
- if sbyte(enum.name) == 46 then
- fullname = enum.name
- else
- fullname = prefix .. "." .. enum.name
- end
- all[fullname] = "TYPE_ENUM"
- all[enum] = fullname
- end
- local function _message_fullname(prefix , msg , all)
- local fullname
- if sbyte(msg.name) == 46 then
- fullname = msg.name
- else
- fullname = prefix .. "." .. msg.name
- end
- all[fullname] = "TYPE_MESSAGE"
- all[msg] = fullname
- for _,nest in ipairs(msg.nested_type or NULL) do
- _message_fullname(fullname , nest , all)
- end
- for _,enum in ipairs(msg.enum_type or NULL) do
- _enum_fullname(fullname , enum , all)
- end
- end
- local function _gen_fullname(file , all)
- local prefix = ""
- if file.package then
- prefix = "." .. file.package
- end
- for _,message in ipairs(file.message_type or NULL) do
- _message_fullname(prefix , message , all)
- end
- for _,enum in ipairs(file.enum_type or NULL) do
- _enum_fullname(prefix , enum , all)
- end
- end
- --- parser
- local parser = {}
- local function parser_one(text,filename)
- local state = { file = filename, pos = 0, line = 1 }
- local r = lpeg.match(proto * -1 + exception , text , 1, state )
- local t = fix(r)
- return t
- end
- function parser.parser(text,filename)
- local t = parser_one(text,filename)
- local all = {}
- _gen_fullname(t,all)
- _fix_typename(t , all)
- return t
- end
- local pb = require "protobuf"
- function parser.register(fileset , path)
- local all = {}
- local files = {}
- if type(fileset) == "string" then
- fileset = { fileset }
- end
- for _, filename in ipairs(fileset) do
- local fullname
- if path then
- fullname = path .. "/" .. filename
- else
- fullname = filename
- end
- local f = assert(io.open(fullname , "r"))
- local buffer = f:read "*a"
- f:close()
- local t = parser_one(buffer,filename)
- _gen_fullname(t,all)
- t.name = filename
- tinsert(files , t)
- end
- for _,file in ipairs(files) do
- _fix_typename(file,all)
- end
- local pbencode = pb.encode("google.protobuf.FileDescriptorSet" , { file = files })
- if pbencode == nil then
- error(pb.lasterror())
- end
- pb.register(pbencode)
- return files
- end
- return parser
|