--[[
MIT License

Copyright (c) 2018 Xavier Wang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
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 Lexer = meta "Lexer" do

local escape = {
   a = "\a", b = "\b", f = "\f", n = "\n",
   r = "\r", t = "\t", v = "\v"
}

local function tohex(x) return string.byte(tonumber(x, 16)) end
local function todec(x) return string.byte(tonumber(x, 10)) end
local function toesc(x) return escape[x] or x end

function Lexer.new(name, src)
   local self = {
      name = name,
      src = src,
      pos = 1
   }
   return setmetatable(self, Lexer)
end

function Lexer:__call(patt, pos)
   return self.src:match(patt, pos or self.pos)
end

function Lexer:test(patt)
   self:whitespace()
   local pos = self('^'..patt..'%s*()')
   if not pos then return false end
   self.pos = pos
   return true
end

function Lexer:expected(patt, name)
   if not self:test(patt) then
      return self:error((name or "'"..patt.."'").." expected")
   end
   return self
end

function Lexer:pos2loc(pos)
   local linenr = 1
   pos = pos or self.pos
   for start, stop in self.src:gmatch "()[^\n]*()\n?" do
      if start <= pos and pos <= stop then
         return linenr, pos - start + 1
      end
      linenr = linenr + 1
   end
end

function Lexer:error(fmt, ...)
   local ln, co = self:pos2loc()
   return error(("%s:%d:%d: "..fmt):format(self.name, ln, co, ...))
end

function Lexer:opterror(opt, msg)
   if not opt then return self:error(msg) end
   return nil
end

function Lexer:whitespace()
   local pos, c = self "^%s*()(%/?)"
   self.pos = pos
   if c == '' then return self end
   return self:comment()
end

function Lexer:comment()
   local pos = self "^%/%/[^\n]*\n?()"
   if not pos then
      if self "^%/%*" then
         pos = self "^%/%*.-%*%/()"
         if not pos then
            self:error "unfinished comment"
         end
      end
   end
   if not pos then return self end
   self.pos = pos
   return self:whitespace()
end

function Lexer:line_end(opt)
   self:whitespace()
   local pos = self '^[%s;]*%s*()'
   if not pos then
      return self:opterror(opt, "';' expected")
   end
   self.pos = pos
   return pos
end

function Lexer:eof()
   self:whitespace()
   return self.pos > #self.src
end

function Lexer:keyword(kw, opt)
   self:whitespace()
   local ident, pos = self "^([%a_][%w_]*)%s*()"
   if not ident or ident ~= kw then
      return self:opterror(opt, "''"..kw..'" expected')
   end
   self.pos = pos
   return kw
end

function Lexer:ident(name, opt)
   self:whitespace()
   local b, ident, pos = self "^()([%a_][%w_]*)%s*()"
   if not ident then
      return self:opterror(opt, (name or 'name')..' expected')
   end
   self.pos = pos
   return ident, b
end

function Lexer:full_ident(name, opt)
   self:whitespace()
   local b, ident, pos = self "^()([%a_][%w_.]*)%s*()"
   if not ident or ident:match "%.%.+" then
      return self:opterror(opt, (name or 'name')..' expected')
   end
   self.pos = pos
   return ident, b
end

function Lexer:integer(opt)
   self:whitespace()
   local ns, oct, hex, s, pos =
      self "^([+-]?)(0?)([xX]?)([0-9a-fA-F]+)%s*()"
   local n
   if oct == '0' and hex == '' then
      n = tonumber(s, 8)
   elseif oct == '' and hex == '' then
      n = tonumber(s, 10)
   elseif oct == '0' and hex ~= '' then
      n = tonumber(s, 16)
   end
   if not n then
      return self:opterror(opt, 'integer expected')
   end
   self.pos = pos
   return ns == '-' and -n or n
end

function Lexer:number(opt)
   self:whitespace()
   if self:test "nan%f[%A]" then
      return 0.0/0.0
   elseif self:test "inf%f[%A]" then
      return 1.0/0.0
   end
   local ns, d1, s, d2, s2, pos = self "^([+-]?)(%.?)([0-9]+)(%.?)([0-9]*)()"
   if not ns then
      return self:opterror(opt, 'floating-point number expected')
   end
   local es, pos2 = self("(^[eE][+-]?[0-9]+)%s*()", pos)
   if d1 == "." and d2 == "." then
      return self:error "malformed floating-point number"
   end
   self.pos = pos2 or pos
   local n = tonumber(d1..s..d2..s2..(es or ""))
   return ns == '-' and -n or n
end

function Lexer:quote(opt)
   self:whitespace()
   local q, start = self '^(["\'])()'
   if not start then
      return self:opterror(opt, 'string expected')
   end
   self.pos = start
   local patt = '()(\\?'..q..')%s*()'
   while true do
      local stop, s, pos = self(patt)
      if not stop then
         self.pos = start-1
         return self:error "unfinished string"
      end
      self.pos = pos
      if s == q then
         return self.src:sub(start, stop-1)
                        :gsub("\\x(%x+)", tohex)
                        :gsub("\\(%d+)", todec)
                        :gsub("\\(.)", toesc)
      end
   end
end

function Lexer:structure(opt)
   self:whitespace()
   if not self:test "{" then
      return self:opterror(opt, 'opening curly brace expected')
   end
   local t = {}
   while not self:test "}" do
      local ident = self:full_ident "field name"     -- TODO: full_ident?
      self:test ":"
      local value = self:constant()
      self:test ","
      self:line_end "opt"
      t[ident] = value
   end
   return t
end

function Lexer:array(opt)
   self:whitespace()
   if not self:test "%[" then
      return self:opterror(opt, 'opening square bracket expected')
   end
   local t = {}
   while not self:test "]" do
      local value = self:constant()
      self:test ","
      t[#t + 1] = value
   end
   return t
end

function Lexer:constant(opt)
   local c = self:full_ident('constant', 'opt') or
             self:number('opt') or
             self:quote('opt') or
             self:structure('opt') or
             self:array('opt')
   if not c and not opt then
      return self:error "constant expected"
   end
   return c
end

function Lexer:option_name()
   local ident
   if self:test "%(" then
      ident = self:full_ident "option name"
      self:expected "%)"
   else
      ident = self:ident "option name"
   end
   while self:test "%." do
      ident = ident .. "." .. self:ident()
   end
   return ident
end

function Lexer:type_name()
   if self:test "%." then
      local id, pos = self:full_ident "type name"
      return "."..id, pos
   else
      return self:full_ident "type name"
   end
end

end

local Parser = meta "Parser" do
Parser.typemap = {}
Parser.loaded  = {}
Parser.paths   = { "", "." }

function Parser.new()
   local self = {}
   self.typemap = {}
   self.loaded  = {}
   self.paths   = { "", "." }
   return setmetatable(self, Parser)
end

function Parser:error(msg)
   return self.lex:error(msg)
end

function Parser:addpath(path)
   insert_tab(self.paths, path)
end

function Parser:parsefile(name)
   local info = self.loaded[name]
   if info then return info end
   local errors = {}
   for _, path in ipairs(self.paths) do
      local fn = path ~= "" and path.."/"..name or name
      local fh, err = io.open(fn)
      if fh then
         local content = fh:read "*a"
         info = self:parse(content, name)
         fh:close()
         return info
      end
      insert_tab(errors, err or fn..": ".."unknown error")
   end
   if self.import_fallback then
      info = self.import_fallback(name)
   end
   if not info then
      error("module load error: "..name.."\n\t"..table.concat(errors, "\n\t"))
   end
   return info
end

-- parser

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
   end
   info.options = options
   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)
   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
   if label == labels.optional and self.syntax == "proto3" then
      return lex:error("proto3 disallow 'optional' label")
   end
   info, map_entry = field(self, lex, lex:type_name())
   info.label = label
   return info, map_entry
end

local toplevel = {} do

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 msg_body = {} do

function msg_body:message(lex, info)
   local nested_type = default(info, 'nested_type')
   insert_tab(nested_type, toplevel.message(self, lex))
   return self
end

function msg_body:enum(lex, info)
   local nested_type = default(info, 'enum_type')
   insert_tab(nested_type, toplevel.enum(self, lex))
   return self
end

function msg_body: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 msg_body:extensions(lex, info)
   local rt = default(info, 'extension_range')
   repeat
      local start = lex:integer "field number range"
      local stop = math.floor(2^29)
      lex:keyword 'to'
      if not lex:keyword('max', 'opt') then
         stop = lex:integer "field number range end or 'max'"
      end
      insert_tab(rt, { start = start, ['end'] = stop })
   until not lex:test ','
   lex:line_end()
   return self
end

function msg_body: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
            local stop = lex:integer 'field number range end'
            insert_tab(rt, { start = start, ['end'] = stop })
         else
            insert_tab(rt, { start = start, ['end'] = start })
         end
         first = false
      until not lex:test ','
   end
   lex:line_end()
   return self
end

function msg_body: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, "no_label")
         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 msg_body:option(lex, info)
   toplevel.option(self, lex, default(info, 'options'))
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 = msg_body[ident]
      if body_parser then
         body_parser(self, lex, typ)
      else
         local fs = default(typ, 'field')
         local f, t = label_field(self, lex, ident)
         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 = lex:ident 'enum name'
   local enum = { name = name }
   register_type(self, lex, name, types.enum)
   lex:expected "{"
   while not lex:test "}" do
      local ident = lex:ident 'enum constant name'
      if ident == 'option' then
         toplevel.option(self, lex, default(enum, 'options'))
      else
         local values  = default(enum, 'value')
         local number  = lex:expected '=' :integer()
         lex:line_end()
         insert_tab(values, {
            name    = ident,
            number  = number,
            options = inline_option(lex)
         })
      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, default(rpc, 'options'))
      end
   end
   lex:line_end "opt"
   local t = default(info, "method")
   insert_tab(t, rpc)
end

function svr_body:option(lex, info)
   toplevel.option(self, lex, default(info, 'options'))     -- TODO: should be deeper in the info?
end

function svr_body.stream(_, lex)
   lex:error "stream not implement yet"
end

end

function toplevel:service(lex, info)
   local name = lex:ident 'service name'
   local svr = { name = name }
   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

local function make_context(self, lex)
   local ctx = {
      syntax  = "proto2";
      locmap  = {};
      prefix  = ".";
      lex     = lex;
      parser  = self;
   }
   ctx.loaded  = self.loaded
   ctx.typemap = self.typemap
   ctx.paths   = self.paths

   function ctx.import_fallback(import_name)
      if self.unknown_import == true then
         return true
      elseif type(self.unknown_import) == 'string' then
         return import_name:match(self.unknown_import) and true or nil
      elseif self.unknown_import then
         return self:unknown_import(import_name)
      end
   end

   function ctx.type_fallback(type_name)
      if self.unknown_type == true then
         return true
      elseif type(self.unknown_type) == 'string' then
         return type_name:match(self.unknown_type) and true
      elseif self.unknown_type then
         return self:unknown_type(type_name)
      end
   end

   function ctx.on_import(info)
      if self.on_import then
         return self.on_import(info)
      end
   end

   return setmetatable(ctx, Parser)
end

function Parser:parse(src, name)
   local loaded = self.loaded[name]
   if loaded then
      if loaded == true then
         error("loop loaded: "..name)
      end
      return loaded
   end

   name = name or "<input>"
   self.loaded[name] = true
   local lex = Lexer.new(name, src)
   local ctx = make_context(self, lex)
   local info = { name = lex.name, syntax = ctx.syntax }

   local syntax = lex:keyword('syntax', 'opt')
   if syntax then
      info.syntax = lex:expected '=' :quote()
      ctx.syntax  = info.syntax
      lex:line_end()
   end

   while not lex:eof() do
      local ident = lex:ident()
      local top_parser = toplevel[ident]
      if top_parser then
         top_parser(ctx, lex, info)
      else
         lex:error("unknown keyword '"..ident.."'")
      end
      lex:line_end "opt"
   end
   self.loaded[name] = name ~= "<input>" and info or nil
   return ctx:resolve(lex, info)
end

-- resolver

local function empty() end

local function iter(t, k)
   local v = t[k]
   if v then return ipairs(v) end
   return empty
end

local function check_dup(self, lex, typ, map, k, v)
   local old = map[v[k]]
   if old then
      local ln, co = lex:pos2loc(self.locmap[old])
      lex:error("%s '%s' exists, previous at %d:%d",
                typ, v[k], ln, co)
   end
   map[v[k]] = v
end

local function check_type(self, lex, tname)
   if tname:match "^%." then
      local t = self.typemap[tname]
      if not t then
         return lex:error("unknown type '%s'", tname)
      end
      return t, tname
   end
   local prefix = self.prefix
   for i = #prefix+1, 1, -1 do
      local op = prefix[i]
      prefix[i] = tname
      local tn = table.concat(prefix, ".", 1, i)
      prefix[i] = op
      local t = self.typemap[tn]
      if t then return t, tn end
   end
   local tn, t
   if self.type_fallback then
      tn, t = self.type_fallback(tname)
   end
   if tn then
      t = types[t or "message"]
      if tn == true then tn = "."..tname end
      return t, tn
   end
   return lex:error("unknown type '%s'", tname)
end

local function check_field(self, lex, info)
   if info.extendee then
      local t, tn = check_type(self, lex, info.extendee)
      if t ~= types.message then
         lex:error("message type expected in extension")
      end
      info.extendee = tn
   end
   if info.type_name then
      local t, tn = check_type(self, lex, info.type_name)
      info.type      = t
      info.type_name = tn
   end
end

local function check_enum(self, lex, info)
   local names, numbers = {}, {}
   for _, v in iter(info, 'value') do
      lex.pos = self.locmap[v]
      check_dup(self, lex, 'enum name', names, 'name', v)
      if not (info.options
              and info.options.options
              and info.options.options.allow_alias) then
          check_dup(self, lex, 'enum number', numbers, 'number', v)
      end
   end
end

local function check_message(self, lex, info)
   insert_tab(self.prefix, info.name)
   local names, numbers = {}, {}
   for _, v in iter(info, 'field') do
      lex.pos = assert(self.locmap[v])
      check_dup(self, lex, 'field name', names, 'name', v)
      check_dup(self, lex, 'field number', numbers, 'number', v)
      check_field(self, lex, v)
   end
   for _, v in iter(info, 'nested_type') do
      check_message(self, lex, v)
   end
   for _, v in iter(info, 'extension') do
      lex.pos = assert(self.locmap[v])
      check_field(self, lex, v)
   end
   self.prefix[#self.prefix] = nil
end

local function check_service(self, lex, info)
   local names = {}
   for _, v in iter(info, 'method') do
      lex.pos = self.locmap[v]
      check_dup(self, lex, 'rpc name', names, 'name', v)
      local t, tn = check_type(self, lex, v.input_type)
      v.input_type = tn
      if t ~= types.message then
         lex:error "message type expected in parameter"
      end
      t, tn = check_type(self, lex, v.output_type)
      v.output_type = tn
      if t ~= types.message then
         lex:error "message type expected in return"
      end
   end
end

function Parser:resolve(lex, info)
   self.prefix = { "", info.package }
   for _, v in iter(info, 'message_type') do
      check_message(self, lex, v)
   end
   for _, v in iter(info, 'enum_type') do
      check_enum(self, lex, v)
   end
   for _, v in iter(info, 'service') do
      check_service(self, lex, v)
   end
   for _, v in iter(info, 'extension') do
      lex.pos = assert(self.locmap[v])
      check_field(self, lex, v)
   end
   self.prefix = nil
   return info
end

end

local has_pb, pb = pcall(require, "pb") do
if has_pb then
   local descriptor_pb =
   "\10\249#\10\16descriptor.proto\18\15google.protobuf\"G\10\17FileDescript"..
   "orSet\0182\10\4file\24\1 \3(\0112$.google.protobuf.FileDescriptorProto\""..
   "\219\3\10\19FileDescriptorProto\18\12\10\4name\24\1 \1(\9\18\15\10\7pack"..
   "age\24\2 \1(\9\18\18\10\10dependency\24\3 \3(\9\18\25\10\17public_depend"..
   "ency\24\10 \3(\5\18\23\10\15weak_dependency\24\11 \3(\5\0186\10\12messag"..
   "e_type\24\4 \3(\0112 .google.protobuf.DescriptorProto\0187\10\9enum_type"..
   "\24\5 \3(\0112$.google.protobuf.EnumDescriptorProto\0188\10\7service\24"..
   "\6 \3(\0112'.google.protobuf.ServiceDescriptorProto\0188\10\9extension"..
   "\24\7 \3(\0112%.google.protobuf.FieldDescriptorProto\18-\10\7options\24"..
   "\8 \1(\0112\28.google.protobuf.FileOptions\0189\10\16source_code_info\24"..
   "\9 \1(\0112\31.google.protobuf.SourceCodeInfo\18\14\10\6syntax\24\12 \1("..
   "\9\"\228\3\10\15DescriptorProto\18\12\10\4name\24\1 \1(\9\0184\10\5field"..
   "\24\2 \3(\0112%.google.protobuf.FieldDescriptorProto\0188\10\9extension"..
   "\24\6 \3(\0112%.google.protobuf.FieldDescriptorProto\0185\10\11nested_ty"..
   "pe\24\3 \3(\0112 .google.protobuf.DescriptorProto\0187\10\9enum_type\24"..
   "\4 \3(\0112$.google.protobuf.EnumDescriptorProto\18H\10\15extension_rang"..
   "e\24\5 \3(\0112/.google.protobuf.DescriptorProto.ExtensionRange\0189\10"..
   "\10oneof_decl\24\8 \3(\0112%.google.protobuf.OneofDescriptorProto\0180"..
   "\10\7options\24\7 \1(\0112\31.google.protobuf.MessageOptions\26,\10\14Ex"..
   "tensionRange\18\13\10\5start\24\1 \1(\5\18\11\10\3end\24\2 \1(\5\"\169\5"..
   "\10\20FieldDescriptorProto\18\12\10\4name\24\1 \1(\9\18\14\10\6number\24"..
   "\3 \1(\5\18:\10\5label\24\4 \1(\0142+.google.protobuf.FieldDescriptorPro"..
   "to.Label\0188\10\4type\24\5 \1(\0142*.google.protobuf.FieldDescriptorPro"..
   "to.Type\18\17\10\9type_name\24\6 \1(\9\18\16\10\8extendee\24\2 \1(\9\18"..
   "\21\10\13default_value\24\7 \1(\9\18\19\10\11oneof_index\24\9 \1(\5\18."..
   "\10\7options\24\8 \1(\0112\29.google.protobuf.FieldOptions\"\182\2\10\4T"..
   "ype\18\15\10\11TYPE_DOUBLE\16\1\18\14\10\10TYPE_FLOAT\16\2\18\14\10\10TY"..
   "PE_INT64\16\3\18\15\10\11TYPE_UINT64\16\4\18\14\10\10TYPE_INT32\16\5\18"..
   "\16\10\12TYPE_FIXED64\16\6\18\16\10\12TYPE_FIXED32\16\7\18\13\10\9TYPE_B"..
   "OOL\16\8\18\15\10\11TYPE_STRING\16\9\18\14\10\10TYPE_GROUP\16\10\18\16"..
   "\10\12TYPE_MESSAGE\16\11\18\14\10\10TYPE_BYTES\16\12\18\15\10\11TYPE_UIN"..
   "T32\16\13\18\13\10\9TYPE_ENUM\16\14\18\17\10\13TYPE_SFIXED32\16\15\18\17"..
   "\10\13TYPE_SFIXED64\16\16\18\15\10\11TYPE_SINT32\16\17\18\15\10\11TYPE_S"..
   "INT64\16\18\"C\10\5Label\18\18\10\14LABEL_OPTIONAL\16\1\18\18\10\14LABEL"..
   "_REQUIRED\16\2\18\18\10\14LABEL_REPEATED\16\3\"$\10\20OneofDescriptorPro"..
   "to\18\12\10\4name\24\1 \1(\9\"\140\1\10\19EnumDescriptorProto\18\12\10\4"..
   "name\24\1 \1(\9\0188\10\5value\24\2 \3(\0112).google.protobuf.EnumValueD"..
   "escriptorProto\18-\10\7options\24\3 \1(\0112\28.google.protobuf.EnumOpti"..
   "ons\"l\10\24EnumValueDescriptorProto\18\12\10\4name\24\1 \1(\9\18\14\10"..
   "\6number\24\2 \1(\5\0182\10\7options\24\3 \1(\0112!.google.protobuf.Enum"..
   "ValueOptions\"\144\1\10\22ServiceDescriptorProto\18\12\10\4name\24\1 \1("..
   "\9\0186\10\6method\24\2 \3(\0112&.google.protobuf.MethodDescriptorProto"..
   "\0180\10\7options\24\3 \1(\0112\31.google.protobuf.ServiceOptions\"\193"..
   "\1\10\21MethodDescriptorProto\18\12\10\4name\24\1 \1(\9\18\18\10\10input"..
   "_type\24\2 \1(\9\18\19\10\11output_type\24\3 \1(\9\18/\10\7options\24\4 "..
   "\1(\0112\30.google.protobuf.MethodOptions\18\31\10\16client_streaming\24"..
   "\5 \1(\8:\5false\18\31\10\16server_streaming\24\6 \1(\8:\5false\"\231\4"..
   "\10\11FileOptions\18\20\10\12java_package\24\1 \1(\9\18\28\10\20java_out"..
   "er_classname\24\8 \1(\9\18\"\10\19java_multiple_files\24\10 \1(\8:\5fals"..
   "e\18,\10\29java_generate_equals_and_hash\24\20 \1(\8:\5false\18%\10\22ja"..
   "va_string_check_utf8\24\27 \1(\8:\5false\18F\10\12optimize_for\24\9 \1("..
   "\0142).google.protobuf.FileOptions.OptimizeMode:\5SPEED\18\18\10\10go_pa"..
   "ckage\24\11 \1(\9\18\"\10\19cc_generic_services\24\16 \1(\8:\5false\18$"..
   "\10\21java_generic_services\24\17 \1(\8:\5false\18\"\10\19py_generic_ser"..
   "vices\24\18 \1(\8:\5false\18\25\10\10deprecated\24\23 \1(\8:\5false\18"..
   "\31\10\16cc_enable_arenas\24\31 \1(\8:\5false\18\25\10\17objc_class_pref"..
   "ix\24$ \1(\9\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.google.pro"..
   "tobuf.UninterpretedOption\":\10\12OptimizeMode\18\9\10\5SPEED\16\1\18\13"..
   "\10\9CODE_SIZE\16\2\18\16\10\12LITE_RUNTIME\16\3*\9\8\232\7\16\128\128"..
   "\128\128\2\"\230\1\10\14MessageOptions\18&\10\23message_set_wire_format"..
   "\24\1 \1(\8:\5false\18.\10\31no_standard_descriptor_accessor\24\2 \1(\8:"..
   "\5false\18\25\10\10deprecated\24\3 \1(\8:\5false\18\17\10\9map_entry\24"..
   "\7 \1(\8\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.google.protobu"..
   "f.UninterpretedOption*\9\8\232\7\16\128\128\128\128\2\"\160\2\10\12Field"..
   "Options\18:\10\5ctype\24\1 \1(\0142#.google.protobuf.FieldOptions.CType:"..
   "\6STRING\18\14\10\6packed\24\2 \1(\8\18\19\10\4lazy\24\5 \1(\8:\5false"..
   "\18\25\10\10deprecated\24\3 \1(\8:\5false\18\19\10\4weak\24\10 \1(\8:\5f"..
   "alse\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.google.protobuf.Un"..
   "interpretedOption\"/\10\5CType\18\10\10\6STRING\16\0\18\8\10\4CORD\16\1"..
   "\18\16\10\12STRING_PIECE\16\2*\9\8\232\7\16\128\128\128\128\2\"\141\1\10"..
   "\11EnumOptions\18\19\10\11allow_alias\24\2 \1(\8\18\25\10\10deprecated"..
   "\24\3 \1(\8:\5false\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.goo"..
   "gle.protobuf.UninterpretedOption*\9\8\232\7\16\128\128\128\128\2\"}\10"..
   "\16EnumValueOptions\18\25\10\10deprecated\24\1 \1(\8:\5false\18C\10\20un"..
   "interpreted_option\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptio"..
   "n*\9\8\232\7\16\128\128\128\128\2\"{\10\14ServiceOptions\18\25\10\10depr"..
   "ecated\24! \1(\8:\5false\18C\10\20uninterpreted_option\24\231\7 \3(\0112"..
   "$.google.protobuf.UninterpretedOption*\9\8\232\7\16\128\128\128\128\2\"z"..
   "\10\13MethodOptions\18\25\10\10deprecated\24! \1(\8:\5false\18C\10\20uni"..
   "nterpreted_option\24\231\7 \3(\0112$.google.protobuf.UninterpretedOption"..
   "*\9\8\232\7\16\128\128\128\128\2\"\158\2\10\19UninterpretedOption\18;\10"..
   "\4name\24\2 \3(\0112-.google.protobuf.UninterpretedOption.NamePart\18\24"..
   "\10\16identifier_value\24\3 \1(\9\18\26\10\18positive_int_value\24\4 \1("..
   "\4\18\26\10\18negative_int_value\24\5 \1(\3\18\20\10\12double_value\24\6"..
   "\32\1(\1\18\20\10\12string_value\24\7 \1(\12\18\23\10\15aggregate_value"..
   "\24\8 \1(\9\0263\10\8NamePart\18\17\10\9name_part\24\1 \2(\9\18\20\10\12"..
   "is_extension\24\2 \2(\8\"\213\1\10\14SourceCodeInfo\18:\10\8location\24"..
   "\1 \3(\0112(.google.protobuf.SourceCodeInfo.Location\26\134\1\10\8Locati"..
   "on\18\16\10\4path\24\1 \3(\5B\2\16\1\18\16\10\4span\24\2 \3(\5B\2\16\1"..
   "\18\24\10\16leading_comments\24\3 \1(\9\18\25\10\17trailing_comments\24"..
   "\4 \1(\9\18!\10\25leading_detached_comments\24\6 \3(\9B)\10\19com.google"..
   ".protobufB\16DescriptorProtosH\1"

function Parser.reload()
   assert(pb.load(descriptor_pb), "load descriptor msg failed")
end

local function do_compile(self, f, ...)
   if self.include_imports then
      local old = self.on_import
      local infos = {}
      function self.on_import(info)
         insert_tab(infos, info)
      end
      local r = f(...)
      insert_tab(infos, r)
      self.on_import = old
      return { file = infos }
   end
   return { file = { f(...) } }
end

function Parser:compile(s, name)
   if self == Parser then self = Parser.new() end
   local set = do_compile(self, self.parse, self, s, name)
   return pb.encode('.google.protobuf.FileDescriptorSet', set)
end

function Parser:compilefile(fn)
   if self == Parser then self = Parser.new() end
   local set = do_compile(self, self.parsefile, self, fn)
   return pb.encode('.google.protobuf.FileDescriptorSet', set)
end

function Parser:load(s, name)
   if self == Parser then self = Parser.new() end
   local ret, pos = pb.load(self:compile(s, name))
   if ret then return ret, pos end
   error("load failed at offset "..pos)
end

function Parser:loadfile(fn)
   if self == Parser then self = Parser.new() end
   local ret, pos = pb.load(self:compilefile(fn))
   if ret then return ret, pos end
   error("load failed at offset "..pos)
end

Parser.reload()

end
end

return Parser

