Skip to content

Commit 76ef805

Browse files
committed
add builder
1 parent c0702f0 commit 76ef805

File tree

4 files changed

+212
-35
lines changed

4 files changed

+212
-35
lines changed

meta/template/ffi.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ local ctype
1111

1212
---@class ffi.cdecl*: string
1313
---@class ffi.cdata*: userdata
14+
---@class ffi.ref<T>: {[0]:T}, ffi.cdata*
15+
1416
---@alias ffi.ct* ffi.ctype*|ffi.cdecl*|ffi.cdata*
1517
---@class ffi.cb*: ffi.cdata*
1618
local cb

script/LuaJIT/init.lua

Lines changed: 154 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
local files = require 'files'
21
local searchCode = require 'LuaJIT.searchCode'
32
local cdefRerence = require 'LuaJIT.cdefRerence'
43
local cdriver = require 'LuaJIT.c-parser.cdriver'
54
local util = require 'utility'
5+
local SDBMHash = require 'SDBMHash'
66

77
local namespace <const> = 'ffi.namespace*.'
88

9+
local function nkeys(t)
10+
local n = 0
11+
for key, value in pairs(t) do
12+
n = n + 1
13+
end
14+
return n
15+
end
16+
17+
local function isSingleNode(ast)
18+
if type(ast) ~= 'table' then
19+
return false
20+
end
21+
local len = #ast
22+
return len == 1 and len == nkeys(ast)
23+
end
24+
925
--TODO:supprot 32bit ffi, need config
1026
local knownTypes = {
1127
["bool"] = 'boolean',
@@ -93,7 +109,7 @@ function builder:getType(name)
93109
local isStruct
94110
for _, n in ipairs(name) do
95111
if type(n) == 'table' then
96-
n = n.name
112+
n = n.full_name
97113
end
98114
if not isStruct then
99115
isStruct = self:needDeref(self:getTypeAst(n))
@@ -127,7 +143,7 @@ function builder:isVoid(ast)
127143
return self:isVoid(self:getTypeAst(typename))
128144
end
129145

130-
function builder:buildStructOrUnion(lines, ast, tt, name)
146+
function builder:buildStructOrUnion(lines, tt, name)
131147
lines[#lines+1] = '---@class ' .. self:getType(name)
132148
for _, field in ipairs(tt.fields or {}) do
133149
if field.name and field.type then
@@ -136,7 +152,7 @@ function builder:buildStructOrUnion(lines, ast, tt, name)
136152
end
137153
end
138154

139-
function builder:buildFunction(lines, ast, tt, name)
155+
function builder:buildFunction(lines, tt, name)
140156
local param_names = {}
141157
for i, param in ipairs(tt.params or {}) do
142158
lines[#lines+1] = ('---@param %s %s'):format(param.name, self:getType(param.type))
@@ -153,18 +169,98 @@ function builder:buildFunction(lines, ast, tt, name)
153169
lines[#lines+1] = ('function m.%s(%s) end'):format(name, table.concat(param_names, ', '))
154170
end
155171

156-
function builder:buildTypedef(lines, ast, tt, name)
172+
function builder:buildTypedef(lines, tt, name)
157173
local def = tt.def[1]
158174
if type(def) == 'table' and not def.name then
159175
-- 这个时候没有主类型,只有一个别名,直接创建一个别名结构体
160-
self.switch_ast(def.type, self, lines, def, def, name)
176+
self.switch_ast(def.type, self, lines, def, name)
161177
else
162178
lines[#lines+1] = ('---@alias %s %s'):format(name, self:getType(def))
163179
end
164180
end
165181

166-
function builder:buildEnum(lines, ast, tt, name)
167-
--TODO
182+
local calculate
183+
184+
local function binop(enumer, val, fn)
185+
local e1, e2 = calculate(enumer, val[1]), calculate(enumer, val[2])
186+
if type(e1) == "number" and type(e2) == "number" then
187+
return fn(e1, e2)
188+
else
189+
return { e1, e2, op = val.op }
190+
end
191+
end
192+
do
193+
local ops = {
194+
['+'] = function (a, b) return a + b end,
195+
['-'] = function (a, b) return a - b end,
196+
['*'] = function (a, b) return a * b end,
197+
['/'] = function (a, b) return a / b end,
198+
['&'] = function (a, b) return a & b end,
199+
['|'] = function (a, b) return a | b end,
200+
['~'] = function (a, b)
201+
if not b then
202+
return ~a
203+
end
204+
return a ~ b
205+
end,
206+
['<<'] = function (a, b) return a << b end,
207+
['>>'] = function (a, b) return a >> b end,
208+
}
209+
calculate = function (enumer, val)
210+
if ops[val.op] then
211+
return binop(enumer, val, ops[val.op])
212+
end
213+
if isSingleNode(val) then
214+
val = val[1]
215+
end
216+
if type(val) == "string" then
217+
if enumer[val] then
218+
return enumer[val]
219+
end
220+
return tonumber(val)
221+
end
222+
return val
223+
end
224+
end
225+
226+
local function pushEnumValue(enumer, name, v)
227+
if isSingleNode(v) then
228+
v = tonumber(v[1])
229+
end
230+
enumer[name] = v
231+
enumer[#enumer+1] = v
232+
return v
233+
end
234+
235+
function builder:buildEnum(lines, tt, name)
236+
local enumer = {}
237+
for i, val in ipairs(tt.values) do
238+
local name = val.name
239+
local v = val.value
240+
if not v then
241+
if i == 1 then
242+
v = 0
243+
else
244+
v = tt.values[i - 1].realValue + 1
245+
end
246+
end
247+
if type(v) == 'table' and v.op then
248+
v = calculate(enumer, v)
249+
end
250+
if v then
251+
val.realValue = pushEnumValue(enumer, name, v)
252+
end
253+
end
254+
local alias = {}
255+
for k, v in pairs(enumer) do
256+
alias[#alias+1] = type(k) == 'number' and v or ([['%s']]):format(k)
257+
if type(k) ~= 'number' then
258+
lines[#lines+1] = ('m.%s = %s'):format(k, v)
259+
end
260+
end
261+
if name then
262+
lines[#lines+1] = ('---@alias %s %s'):format(self:getType(name), table.concat(alias, ' | '))
263+
end
168264
end
169265

170266
builder.switch_ast
@@ -178,37 +274,60 @@ builder.switch_ast
178274
:case 'typedef'
179275
:call(builder.buildTypedef)
180276

181-
277+
local function stringStartsWith(self, searchString, position)
278+
if position == nil or position < 0 then
279+
position = 0
280+
end
281+
return string.sub(self, position + 1, #searchString + position) == searchString
282+
end
182283
local firstline = ('---@meta \n ---@class %s \n local %s = {}'):format(namespace, constName)
183284
local m = {}
285+
local function compileCode(lines, asts, b)
286+
for _, ast in ipairs(asts) do
287+
local tt = ast.type
288+
289+
if tt.type == 'enum' and not stringStartsWith(ast.name, 'enum@') then
290+
goto continue
291+
end
292+
if not tt.name then
293+
if tt.type ~= 'enum' then
294+
goto continue
295+
end
296+
--匿名枚举也要创建具体的值
297+
lines = lines or { firstline }
298+
builder.switch_ast(tt.type, b, lines, tt)
299+
else
300+
tt.full_name = ast.name
301+
lines = lines or { firstline }
302+
builder.switch_ast(tt.type, b, lines, tt, tt.full_name)
303+
lines[#lines+1] = '\n'
304+
end
305+
::continue::
306+
end
307+
return lines
308+
end
184309
function m.compileCodes(codes)
185310
---@class ffi.builder
186-
local b = setmetatable({ globalAsts = {} }, { __index = builder })
311+
local b = setmetatable({ globalAsts = {}, cacheEnums = {} }, { __index = builder })
187312

188313
local lines
189314
for _, code in ipairs(codes) do
190315
local asts = cdriver.process_context(code)
191316
if not asts then
192317
goto continue
193318
end
194-
lines = lines or { firstline }
195319
table.insert(b.globalAsts, asts)
196-
for _, ast in ipairs(asts) do
197-
local tt = ast.type
198-
if tt.name then
199-
tt.name = ast.name
200-
builder.switch_ast(tt.type, b, lines, ast, tt, tt.name)
201-
lines[#lines+1] = '\n'
202-
end
203-
end
320+
lines = compileCode(lines, asts, b)
204321
::continue::
205322
end
206323
return lines
207324
end
208325

209-
---@async
210-
files.watch(function (ev, uri)
211-
if ev == 'compile' then
326+
function m.initBuilder()
327+
local config = require 'config'
328+
local fs = require 'bee.filesystem'
329+
---@async
330+
return function (uri)
212331
local refs = cdefRerence()
213332
if not refs or #refs == 0 then
214333
return
@@ -218,8 +337,19 @@ files.watch(function (ev, uri)
218337
if not codes then
219338
return
220339
end
221-
local res = m.compileCodes(codes)
340+
341+
local texts = m.compileCodes(codes)
342+
if not texts then
343+
return
344+
end
345+
346+
local hash = ('%08x'):format(SDBMHash():hash(uri))
347+
local encoding = config.get(nil, 'Lua.runtime.fileEncoding')
348+
local filePath = METAPATH .. '/ffi/' .. table.concat({ hash, encoding }, '_')
349+
350+
fs.create_directories(fs.path(filePath):parent_path())
351+
util.saveFile(filePath .. '.d.lua', table.concat(texts, '\n'))
222352
end
223-
end)
353+
end
224354

225355
return m

test/ffi/builder.lua

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ local function removeEmpty(lines)
1313
end
1414

1515
local function formatLines(lines)
16+
if not lines or #lines == 0 then
17+
return {}
18+
end
1619
table.remove(lines, 1)
1720
return removeEmpty(lines)
1821
end
@@ -36,7 +39,34 @@ function TEST(wanted)
3639
end
3740
end
3841

39-
TEST[[
42+
TEST [[
43+
m.B = 5
44+
m.A = 0
45+
m.D = 7
46+
m.C = 6
47+
]] [[
48+
enum {
49+
A,
50+
B=5,
51+
C,
52+
D,
53+
};
54+
]]
55+
56+
TEST [[
57+
m.B = 2
58+
m.A = 1
59+
m.C = 5
60+
---@alias ffi.namespace*.enum@a 1 | 2 | 'B' | 'A' | 5 | 'C'
61+
]][[
62+
enum a {
63+
A = 1,
64+
B = 2,
65+
C = A|B+2,
66+
};
67+
]]
68+
69+
TEST [[
4070
---@param a boolean
4171
---@param b boolean
4272
---@param c integer
@@ -46,7 +76,7 @@ TEST[[
4676
void test(bool a, _Bool b, size_t c, ssize_t d);
4777
]]
4878

49-
TEST[[
79+
TEST [[
5080
---@param a integer
5181
---@param b integer
5282
---@param c integer
@@ -56,7 +86,7 @@ TEST[[
5686
void test(int8_t a, int16_t b, int32_t c, int64_t d);
5787
]]
5888

59-
TEST[[
89+
TEST [[
6090
---@param a integer
6191
---@param b integer
6292
---@param c integer
@@ -66,7 +96,7 @@ TEST[[
6696
void test(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
6797
]]
6898

69-
TEST[[
99+
TEST [[
70100
---@param a integer
71101
---@param b integer
72102
---@param c integer
@@ -76,7 +106,7 @@ TEST[[
76106
void test(unsigned char a, unsigned short b, unsigned long c, unsigned int d);
77107
]]
78108

79-
TEST[[
109+
TEST [[
80110
---@param a integer
81111
---@param b integer
82112
---@param c integer
@@ -86,7 +116,7 @@ TEST[[
86116
void test(unsigned char a, unsigned short b, unsigned long c, unsigned int d);
87117
]]
88118

89-
TEST[[
119+
TEST [[
90120
---@param a integer
91121
---@param b integer
92122
---@param c integer
@@ -96,7 +126,7 @@ TEST[[
96126
void test(signed char a, signed short b, signed long c, signed int d);
97127
]]
98128

99-
TEST[[
129+
TEST [[
100130
---@param a integer
101131
---@param b integer
102132
---@param c integer
@@ -106,7 +136,7 @@ TEST[[
106136
void test(char a, short b, long c, int d);
107137
]]
108138

109-
TEST[[
139+
TEST [[
110140
---@param a number
111141
---@param b number
112142
---@param c integer

0 commit comments

Comments
 (0)