pcsx-redux/third_party/lua-protobuf/lexer.lua
Nicolas Pixel Noble f506f642f5 Adding lua-protobuf
2022-12-18 23:05:47 -08:00

297 lines
6.6 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 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 pos, name, npos = self "^%s*()(%b[])()"
if not pos then
name = self:full_ident "field name"
else
self.pos = npos
end
self:test ":"
local value = self:constant()
self:test ","
self:line_end "opt"
t[name] = 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
return Lexer
-- )EOF"