summaryrefslogtreecommitdiff
path: root/server/src/matcher/find_lib.lua
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/matcher/find_lib.lua')
-rw-r--r--server/src/matcher/find_lib.lua233
1 files changed, 233 insertions, 0 deletions
diff --git a/server/src/matcher/find_lib.lua b/server/src/matcher/find_lib.lua
new file mode 100644
index 00000000..f24dabd4
--- /dev/null
+++ b/server/src/matcher/find_lib.lua
@@ -0,0 +1,233 @@
+local lni = require 'lni'
+local fs = require 'bee.filesystem'
+
+local function mergeEnum(lib, locale)
+ if not lib or not locale then
+ return
+ end
+ local pack = {}
+ for _, enum in ipairs(lib) do
+ if enum.enum then
+ pack[enum.enum] = enum
+ end
+ end
+ for _, enum in ipairs(locale) do
+ if pack[enum.enum] then
+ pack[enum.enum].description = enum.description
+ end
+ end
+end
+
+local function mergeField(lib, locale)
+ if not lib or not locale then
+ return
+ end
+ local pack = {}
+ for _, field in ipairs(lib) do
+ if field.field then
+ pack[field.field] = field
+ end
+ end
+ for _, field in ipairs(locale) do
+ if pack[field.field] then
+ pack[field.field].description = field.description
+ end
+ end
+end
+
+local function mergeLocale(libs, locale)
+ if not libs or not locale then
+ return
+ end
+ for name in pairs(locale) do
+ if libs[name] then
+ libs[name].description = locale[name].description
+ mergeEnum(libs[name].enums, locale[name].enums)
+ mergeField(libs[name].fields, locale[name].fields)
+ end
+ end
+end
+
+local function mergeLibs(target, libs)
+ if not libs then
+ return
+ end
+ for name, lib in pairs(libs) do
+ target.names[#target.names+1] = name
+ target.libs[#target.libs+1] = lib
+ end
+end
+
+local function loadLocale(language, relative)
+ local localePath = ROOT / 'locale' / language / relative
+ local localeBuf = io.load(localePath)
+ if localeBuf then
+ local locale = table.container()
+ xpcall(lni.classics, log.error, localeBuf, localePath:string(), {locale})
+ return locale
+ end
+ return nil
+end
+
+local Libs
+local function getLibs()
+ if Libs then
+ return Libs
+ end
+ local language = require 'language'
+ Libs = setmetatable({
+ names = {},
+ libs = {},
+ }, {
+ __pairs = function (self)
+ local i = 0
+ return function ()
+ i = i + 1
+ return self.names[i], self.libs[i]
+ end
+ end,
+ })
+ for path in io.scan(ROOT / 'libs') do
+ local libs
+ local buf = io.load(path)
+ if buf then
+ libs = table.container()
+ xpcall(lni.classics, log.error, buf, path:string(), {libs})
+ end
+ local relative = fs.relative(path, ROOT)
+
+ local locale = loadLocale('en-US', relative)
+ mergeLocale(libs, locale)
+ if language ~= 'en-US' then
+ locale = loadLocale(language, relative)
+ mergeLocale(libs, locale)
+ end
+ mergeLibs(Libs, libs)
+ end
+
+ return Libs
+end
+
+local function isGlobal(var)
+ if var.type ~= 'field' then
+ return false
+ end
+ if not var.parent then
+ return false
+ end
+ return var.parent.key == '_ENV' or var.parent.key == '_G'
+end
+
+local function checkSourceAsGlobal(value, name, showname)
+ if value.key == name and isGlobal(value) then
+ return showname or name
+ end
+ return nil
+end
+
+local function checkSourceAsLibrary(value, name, showname)
+ if value.type ~= 'lib' then
+ return nil
+ end
+ if value.name == name then
+ return showname or name
+ end
+ return nil
+end
+
+local function checkSource(value, libname, lib)
+ if not lib.source then
+ return checkSourceAsGlobal(value, libname)
+ end
+ for _, source in ipairs(lib.source) do
+ if source.type == 'global' then
+ local fullKey = checkSourceAsGlobal(value, source.name or libname, source.nick or libname)
+ if fullKey then
+ return fullKey
+ end
+ elseif source.type == 'library' then
+ local fullKey = checkSourceAsLibrary(value, source.name or libname, source.nick or libname)
+ if fullKey then
+ return fullKey
+ end
+ end
+ end
+ return nil
+end
+
+local function checkParentAsGlobal(parentValue, name, parent)
+ local parentName = checkSourceAsGlobal(parentValue, parent.name, parent.nick)
+ if not parentName then
+ return nil
+ end
+ return ('%s.%s'):format(parentName, name)
+end
+
+local function checkParentAsLibrary(parentValue, name, parent)
+ local parentName = checkSourceAsLibrary(parentValue, parent.name, parent.nick)
+ if not parentName then
+ return nil
+ end
+ return ('%s.%s'):format(parentName, name)
+end
+
+local function checkParentAsObject(parentValue, name, parent)
+ if parentValue.type ~= parent.name then
+ return nil
+ end
+ return ('*%s:%s'):format(parent.name, name)
+end
+
+local function checkParent(value, name, lib)
+ if name ~= value.key then
+ return nil
+ end
+ local parentValue = value.parent
+ if not parentValue then
+ return nil
+ end
+ parentValue = parentValue.value or parentValue
+ for _, parent in ipairs(lib.parent) do
+ if parent.type == 'global' then
+ local fullKey = checkParentAsGlobal(parentValue, name, parent)
+ if fullKey then
+ return fullKey, false
+ end
+ elseif parent.type == 'library' then
+ local fullKey = checkParentAsLibrary(parentValue, name, parent)
+ if fullKey then
+ return fullKey, false
+ end
+ elseif parent.type == 'object' then
+ local fullKey = checkParentAsObject(parentValue, name, parent)
+ if fullKey then
+ return fullKey, true
+ end
+ end
+ end
+ return nil
+end
+
+local function findLib(var, libs)
+ local value = var.value or var
+ for libname, lib in pairs(libs) do
+ if lib.parent then
+ local fullKey, oo = checkParent(value, libname, lib)
+ if fullKey then
+ return lib, fullKey, oo
+ end
+ else
+ local fullKey = checkSource(value, libname, lib)
+ if fullKey then
+ return lib, fullKey, false
+ end
+ end
+ end
+ return nil, nil, nil
+end
+
+return function (var)
+ local libs = getLibs()
+ local lib, fullKey, oo = findLib(var, libs)
+ return lib, fullKey, oo
+end