summaryrefslogtreecommitdiff
path: root/script/LuaJIT
diff options
context:
space:
mode:
authorfesily <fesil@foxmail.com>2023-05-12 13:52:34 +0800
committerfesily <fesil@foxmail.com>2023-05-12 13:52:34 +0800
commit76ef8053d06a2daf00abd33a2716838cb05bf468 (patch)
tree3248884642f62dcb43f948f156ac3633849ee8a5 /script/LuaJIT
parentc0702f050bfe3bf03214351ca0a7f090c0c5d489 (diff)
downloadlua-language-server-76ef8053d06a2daf00abd33a2716838cb05bf468.zip
add builder
Diffstat (limited to 'script/LuaJIT')
-rw-r--r--script/LuaJIT/init.lua178
1 files changed, 154 insertions, 24 deletions
diff --git a/script/LuaJIT/init.lua b/script/LuaJIT/init.lua
index b528e73e..c94295da 100644
--- a/script/LuaJIT/init.lua
+++ b/script/LuaJIT/init.lua
@@ -1,11 +1,27 @@
-local files = require 'files'
local searchCode = require 'LuaJIT.searchCode'
local cdefRerence = require 'LuaJIT.cdefRerence'
local cdriver = require 'LuaJIT.c-parser.cdriver'
local util = require 'utility'
+local SDBMHash = require 'SDBMHash'
local namespace <const> = 'ffi.namespace*.'
+local function nkeys(t)
+ local n = 0
+ for key, value in pairs(t) do
+ n = n + 1
+ end
+ return n
+end
+
+local function isSingleNode(ast)
+ if type(ast) ~= 'table' then
+ return false
+ end
+ local len = #ast
+ return len == 1 and len == nkeys(ast)
+end
+
--TODO:supprot 32bit ffi, need config
local knownTypes = {
["bool"] = 'boolean',
@@ -93,7 +109,7 @@ function builder:getType(name)
local isStruct
for _, n in ipairs(name) do
if type(n) == 'table' then
- n = n.name
+ n = n.full_name
end
if not isStruct then
isStruct = self:needDeref(self:getTypeAst(n))
@@ -127,7 +143,7 @@ function builder:isVoid(ast)
return self:isVoid(self:getTypeAst(typename))
end
-function builder:buildStructOrUnion(lines, ast, tt, name)
+function builder:buildStructOrUnion(lines, tt, name)
lines[#lines+1] = '---@class ' .. self:getType(name)
for _, field in ipairs(tt.fields or {}) do
if field.name and field.type then
@@ -136,7 +152,7 @@ function builder:buildStructOrUnion(lines, ast, tt, name)
end
end
-function builder:buildFunction(lines, ast, tt, name)
+function builder:buildFunction(lines, tt, name)
local param_names = {}
for i, param in ipairs(tt.params or {}) do
lines[#lines+1] = ('---@param %s %s'):format(param.name, self:getType(param.type))
@@ -153,18 +169,98 @@ function builder:buildFunction(lines, ast, tt, name)
lines[#lines+1] = ('function m.%s(%s) end'):format(name, table.concat(param_names, ', '))
end
-function builder:buildTypedef(lines, ast, tt, name)
+function builder:buildTypedef(lines, tt, name)
local def = tt.def[1]
if type(def) == 'table' and not def.name then
-- 这个时候没有主类型,只有一个别名,直接创建一个别名结构体
- self.switch_ast(def.type, self, lines, def, def, name)
+ self.switch_ast(def.type, self, lines, def, name)
else
lines[#lines+1] = ('---@alias %s %s'):format(name, self:getType(def))
end
end
-function builder:buildEnum(lines, ast, tt, name)
- --TODO
+local calculate
+
+local function binop(enumer, val, fn)
+ local e1, e2 = calculate(enumer, val[1]), calculate(enumer, val[2])
+ if type(e1) == "number" and type(e2) == "number" then
+ return fn(e1, e2)
+ else
+ return { e1, e2, op = val.op }
+ end
+end
+do
+ local ops = {
+ ['+'] = function (a, b) return a + b end,
+ ['-'] = function (a, b) return a - b end,
+ ['*'] = function (a, b) return a * b end,
+ ['/'] = function (a, b) return a / b end,
+ ['&'] = function (a, b) return a & b end,
+ ['|'] = function (a, b) return a | b end,
+ ['~'] = function (a, b)
+ if not b then
+ return ~a
+ end
+ return a ~ b
+ end,
+ ['<<'] = function (a, b) return a << b end,
+ ['>>'] = function (a, b) return a >> b end,
+ }
+ calculate = function (enumer, val)
+ if ops[val.op] then
+ return binop(enumer, val, ops[val.op])
+ end
+ if isSingleNode(val) then
+ val = val[1]
+ end
+ if type(val) == "string" then
+ if enumer[val] then
+ return enumer[val]
+ end
+ return tonumber(val)
+ end
+ return val
+ end
+end
+
+local function pushEnumValue(enumer, name, v)
+ if isSingleNode(v) then
+ v = tonumber(v[1])
+ end
+ enumer[name] = v
+ enumer[#enumer+1] = v
+ return v
+end
+
+function builder:buildEnum(lines, tt, name)
+ local enumer = {}
+ for i, val in ipairs(tt.values) do
+ local name = val.name
+ local v = val.value
+ if not v then
+ if i == 1 then
+ v = 0
+ else
+ v = tt.values[i - 1].realValue + 1
+ end
+ end
+ if type(v) == 'table' and v.op then
+ v = calculate(enumer, v)
+ end
+ if v then
+ val.realValue = pushEnumValue(enumer, name, v)
+ end
+ end
+ local alias = {}
+ for k, v in pairs(enumer) do
+ alias[#alias+1] = type(k) == 'number' and v or ([['%s']]):format(k)
+ if type(k) ~= 'number' then
+ lines[#lines+1] = ('m.%s = %s'):format(k, v)
+ end
+ end
+ if name then
+ lines[#lines+1] = ('---@alias %s %s'):format(self:getType(name), table.concat(alias, ' | '))
+ end
end
builder.switch_ast
@@ -178,12 +274,41 @@ builder.switch_ast
:case 'typedef'
:call(builder.buildTypedef)
-
+local function stringStartsWith(self, searchString, position)
+ if position == nil or position < 0 then
+ position = 0
+ end
+ return string.sub(self, position + 1, #searchString + position) == searchString
+end
local firstline = ('---@meta \n ---@class %s \n local %s = {}'):format(namespace, constName)
local m = {}
+local function compileCode(lines, asts, b)
+ for _, ast in ipairs(asts) do
+ local tt = ast.type
+
+ if tt.type == 'enum' and not stringStartsWith(ast.name, 'enum@') then
+ goto continue
+ end
+ if not tt.name then
+ if tt.type ~= 'enum' then
+ goto continue
+ end
+ --匿名枚举也要创建具体的值
+ lines = lines or { firstline }
+ builder.switch_ast(tt.type, b, lines, tt)
+ else
+ tt.full_name = ast.name
+ lines = lines or { firstline }
+ builder.switch_ast(tt.type, b, lines, tt, tt.full_name)
+ lines[#lines+1] = '\n'
+ end
+ ::continue::
+ end
+ return lines
+end
function m.compileCodes(codes)
---@class ffi.builder
- local b = setmetatable({ globalAsts = {} }, { __index = builder })
+ local b = setmetatable({ globalAsts = {}, cacheEnums = {} }, { __index = builder })
local lines
for _, code in ipairs(codes) do
@@ -191,24 +316,18 @@ function m.compileCodes(codes)
if not asts then
goto continue
end
- lines = lines or { firstline }
table.insert(b.globalAsts, asts)
- for _, ast in ipairs(asts) do
- local tt = ast.type
- if tt.name then
- tt.name = ast.name
- builder.switch_ast(tt.type, b, lines, ast, tt, tt.name)
- lines[#lines+1] = '\n'
- end
- end
+ lines = compileCode(lines, asts, b)
::continue::
end
return lines
end
----@async
-files.watch(function (ev, uri)
- if ev == 'compile' then
+function m.initBuilder()
+ local config = require 'config'
+ local fs = require 'bee.filesystem'
+ ---@async
+ return function (uri)
local refs = cdefRerence()
if not refs or #refs == 0 then
return
@@ -218,8 +337,19 @@ files.watch(function (ev, uri)
if not codes then
return
end
- local res = m.compileCodes(codes)
+
+ local texts = m.compileCodes(codes)
+ if not texts then
+ return
+ end
+
+ local hash = ('%08x'):format(SDBMHash():hash(uri))
+ local encoding = config.get(nil, 'Lua.runtime.fileEncoding')
+ local filePath = METAPATH .. '/ffi/' .. table.concat({ hash, encoding }, '_')
+
+ fs.create_directories(fs.path(filePath):parent_path())
+ util.saveFile(filePath .. '.d.lua', table.concat(texts, '\n'))
end
-end)
+end
return m