1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
local cdefines = {}
local c99 = require("plugins.ffi.c-parser.c99")
local cpp = require("plugins.ffi.c-parser.cpp")
local typed = require("plugins.ffi.c-parser.typed")
local function add_type(lst, name, typ)
lst[name] = typ
table.insert(lst, { name = name, type = typ })
end
local base_c_types = {
CONST_CHAR_PTR = { "const", "char", "*" },
CONST_CHAR = { "const", "char" },
LONG_LONG = { "long", "long" },
LONG = { "long" },
DOUBLE = { "double" },
INT = { "int" },
}
local function get_binop_type(e1, e2)
if e1[1] == "double" or e2[1] == "double" then
return base_c_types.DOUBLE
end
if e1[2] == "long" or e2[2] == "long" then
return base_c_types.LONG_LONG
end
if e1[1] == "long" or e2[1] == "long" then
return base_c_types.LONG
end
return base_c_types.INT
end
local binop_set = {
["+"] = true,
["-"] = true,
["*"] = true,
["/"] = true,
["%"] = true,
}
local relop_set = {
["<"] = true,
[">"] = true,
[">="] = true,
["<="] = true,
["=="] = true,
["!="] = true,
}
local bitop_set = {
["<<"] = true,
[">>"] = true,
["&"] = true,
["^"] = true,
["|"] = true,
}
-- Best-effort assessment of the type of a #define
local get_type_of_exp
get_type_of_exp = typed("Exp, TypeList -> {string}?", function(exp, lst)
if type(exp[1]) == "string" and exp[2] == nil then
local val = exp[1]
if val:sub(1,1) == '"' or val:sub(1,2) == 'L"' then
return base_c_types.CONST_CHAR_PTR
elseif val:sub(1,1) == "'" or val:sub(1,2) == "L'" then
return base_c_types.CONST_CHAR
elseif val:match("^[0-9]*LL$") then
return base_c_types.LONG_LONG
elseif val:match("^[0-9]*L$") then
return base_c_types.LONG
elseif val:match("%.") then
return base_c_types.DOUBLE
else
return base_c_types.INT
end
end
if type(exp[1]) == "string" and exp[2] and exp[2].args then
local fn = lst[exp[1]]
if not fn or not fn.ret then
return nil -- unknown function, or not a function
end
local r = fn.ret.type
return table.move(r, 1, #r, 1, {}) -- shallow_copy(r)
end
if exp.unop == "*" then
local etype = get_type_of_exp(exp[1], lst)
if not etype then
return nil
end
local rem = table.remove(etype)
assert(rem == "*")
return etype
elseif exp.unop == "-" then
return get_type_of_exp(exp[1], lst)
elseif exp.op == "?" then
return get_type_of_exp(exp[2], lst)
elseif exp.op == "," then
return get_type_of_exp(exp[#exp], lst)
elseif binop_set[exp.op] then
local e1 = get_type_of_exp(exp[1], lst)
if not e1 then
return nil
end
-- some binops are also unops (e.g. - and *)
if exp[2] then
local e2 = get_type_of_exp(exp[2], lst)
if not e2 then
return nil
end
return get_binop_type(e1, e2)
else
return e1
end
elseif relop_set[exp.op] then
return base_c_types.INT
elseif bitop_set[exp.op] then
return get_type_of_exp(exp[1], lst) -- ...or should it be int?
elseif exp.op then
print("FIXME unsupported op", exp.op)
end
return nil
end)
function cdefines.register_define(lst, name, text, define_set)
local exp, err, line, col = c99.match_language_expression_grammar(text .. " ")
if not exp then
-- failed parsing expression
-- print(("failed parsing: %d:%d: %s\n"):format(line, col, text))
return
end
local typ = get_type_of_exp(exp, lst)
if typ then
add_type(lst, name, { type = typ })
end
end
function cdefines.register_defines(lst, define_set)
for name, def in pairs(define_set) do
if #def == 0 then
goto continue
end
local text = cpp.expand_macro(name, define_set)
cdefines.register_define(lst, name, text, define_set)
::continue::
end
end
return cdefines
|