443 lines
13 KiB
Lua
443 lines
13 KiB
Lua
local ffi = require("ffi")
|
|
local eolian = require("eolian")
|
|
local keyref = require("docgen.keyref")
|
|
|
|
local M = {}
|
|
|
|
local writer
|
|
|
|
local func_type_str = {
|
|
[eolian.function_type.PROPERTY] = "property",
|
|
[eolian.function_type.PROP_GET] = "property",
|
|
[eolian.function_type.PROP_SET] = "property",
|
|
[eolian.function_type.METHOD] = "method"
|
|
}
|
|
|
|
M.tok_ref_resolve = function(tok, eos, root)
|
|
local tp, d1, d2 = tok:ref_resolve(eos)
|
|
local ret = {}
|
|
for tokp in d1:name_get():gmatch("[^%.]+") do
|
|
ret[#ret + 1] = tokp:lower()
|
|
end
|
|
if tp == eolian.object_type.FUNCTION then
|
|
ret[#ret + 1] = func_type_str[d2:type_get()]
|
|
ret[#ret + 1] = d2:name_get():lower()
|
|
elseif tp == eolian.object_type.EVENT then
|
|
ret[#ret + 1] = "event"
|
|
ret[#ret + 1] = d2:name_get():lower()
|
|
end
|
|
if root ~= nil then
|
|
ret[#ret + 1] = not not root
|
|
end
|
|
return ret
|
|
end
|
|
|
|
M.obj_matches_filter = function(obj)
|
|
local ns = M.obj_nspaces_get(obj)
|
|
if #ns and (ns[1] == "efl" or ns[1] == "eina") then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
M.classes_get_filtered = function(eos)
|
|
local ret = {}
|
|
for cl in eos:classes_get() do
|
|
if M.obj_matches_filter(cl) then
|
|
ret[#ret + 1] = cl
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
M.obj_id_get = function(obj)
|
|
return tonumber(ffi.cast("uintptr_t", obj))
|
|
end
|
|
|
|
M.obj_nspaces_get = function(obj, root)
|
|
local tbl = obj:namespaces_get():to_array()
|
|
for i = 1, #tbl do
|
|
tbl[i] = tbl[i]:lower()
|
|
end
|
|
tbl[#tbl + 1] = obj:short_name_get():lower()
|
|
if root ~= nil then
|
|
tbl[#tbl + 1] = not not root
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
M.event_nspaces_get = function(obj, cl, root)
|
|
local tbl = M.obj_nspaces_get(cl)
|
|
tbl[#tbl + 1] = "event"
|
|
tbl[#tbl + 1] = obj:name_get():lower():gsub(",", "_")
|
|
if root ~= nil then
|
|
tbl[#tbl + 1] = not not root
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
M.func_nspaces_get = function(obj, cl, root)
|
|
local tbl = M.obj_nspaces_get(cl)
|
|
tbl[#tbl + 1] = func_type_str[obj:type_get()]
|
|
tbl[#tbl + 1] = obj:name_get():lower()
|
|
if root ~= nil then
|
|
tbl[#tbl + 1] = not not root
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
local class_type_str = {
|
|
[eolian.class_type.REGULAR] = "class",
|
|
[eolian.class_type.ABSTRACT] = "class",
|
|
[eolian.class_type.MIXIN] = "mixin",
|
|
[eolian.class_type.INTERFACE] = "interface"
|
|
}
|
|
|
|
M.class_type_str_get = function(cl)
|
|
return class_type_str[cl:type_get()]
|
|
end
|
|
|
|
local param_dirs = {
|
|
[eolian.parameter_dir.IN] = "in",
|
|
[eolian.parameter_dir.OUT] = "out",
|
|
[eolian.parameter_dir.INOUT] = "inout"
|
|
}
|
|
|
|
M.param_get_dir_name = function(param)
|
|
return assert(param_dirs[param:direction_get()], "unknown parameter direction")
|
|
end
|
|
|
|
M.type_cstr_get = function(tp, suffix)
|
|
tp = tp or "void"
|
|
local ct = (type(tp) == "string") and tp or tp:c_type_get(eolian.c_type_type.DEFAULT)
|
|
if not suffix then
|
|
return ct
|
|
end
|
|
if ct:sub(#ct) == "*" then
|
|
return ct .. suffix
|
|
else
|
|
return ct .. " " .. suffix
|
|
end
|
|
end
|
|
|
|
local serialize_type
|
|
local serialize_tdecl
|
|
local serialize_tdecl_c
|
|
local serialize_var
|
|
local serialize_var_c
|
|
|
|
local wrap_type_attrs = function(tp, str)
|
|
if tp:is_const() then
|
|
str = "const(" .. str .. ")"
|
|
end
|
|
-- TODO: implement new ownership system into docs
|
|
--if tp:is_own() then
|
|
-- str = "own(" .. str .. ")"
|
|
--end
|
|
local ffunc = tp:free_func_get()
|
|
if ffunc then
|
|
str = "free(" .. str .. ", " .. ffunc .. ")"
|
|
end
|
|
if tp:is_ptr() then
|
|
str = "ptr(" .. str .. ")"
|
|
end
|
|
return str
|
|
end
|
|
|
|
serialize_type = function(tp)
|
|
local tpt = tp:type_get()
|
|
if tpt == eolian.type_type.UNKNOWN then
|
|
error("unknown type: " .. tp:name_get())
|
|
elseif tpt == eolian.type_type.VOID then
|
|
return wrap_type_attrs(tp, "void")
|
|
elseif tpt == eolian.type_type.UNDEFINED then
|
|
return wrap_type_attrs(tp, "__undefined_type")
|
|
elseif tpt == eolian.type_type.REGULAR or tpt == eolian.type_type.CLASS then
|
|
local stp = tp:base_type_get()
|
|
if stp then
|
|
local stypes = {}
|
|
while stp do
|
|
stypes[#stypes + 1] = serialize_type(stp)
|
|
stp = stp:next_type_get()
|
|
end
|
|
return wrap_type_attrs(tp, tp:name_get() .. "<"
|
|
.. table.concat(stypes, ", ") .. ">")
|
|
end
|
|
return wrap_type_attrs(tp, tp:name_get())
|
|
end
|
|
error("unhandled type type: " .. tpt)
|
|
end
|
|
|
|
local add_typedecl_attrs = function(tp, buf)
|
|
if tp:is_extern() then
|
|
buf[#buf + 1] = "@extern "
|
|
end
|
|
local ffunc = tp:free_func_get()
|
|
if ffunc then
|
|
buf[#buf + 1] = "@free("
|
|
buf[#buf + 1] = ffunc
|
|
buf[#buf + 1] = ") "
|
|
end
|
|
end
|
|
|
|
serialize_tdecl = function(tp)
|
|
local tpt = tp:type_get()
|
|
if tpt == eolian.typedecl_type.UNKNOWN then
|
|
error("unknown typedecl: " .. tp:name_get())
|
|
elseif tpt == eolian.typedecl_type.STRUCT or
|
|
tpt == eolian.typedecl_type.STRUCT_OPAQUE then
|
|
local buf = { "struct " }
|
|
add_typedecl_attrs(tp, buf)
|
|
buf[#buf + 1] = tp:name_get()
|
|
if tpt == eolian.typedecl_type.STRUCT_OPAQUE then
|
|
buf[#buf + 1] = ";"
|
|
return table.concat(buf)
|
|
end
|
|
local fields = tp:struct_fields_get():to_array()
|
|
if #fields == 0 then
|
|
buf[#buf + 1] = " {}"
|
|
return table.concat(buf)
|
|
end
|
|
buf[#buf + 1] = " {\n"
|
|
for i, fld in ipairs(fields) do
|
|
buf[#buf + 1] = " "
|
|
buf[#buf + 1] = fld:name_get()
|
|
buf[#buf + 1] = ": "
|
|
buf[#buf + 1] = serialize_type(fld:type_get())
|
|
buf[#buf + 1] = ";\n"
|
|
end
|
|
buf[#buf + 1] = "}"
|
|
return table.concat(buf)
|
|
elseif tpt == eolian.typedecl_type.ENUM then
|
|
local buf = { "enum " }
|
|
add_typedecl_attrs(tp, buf)
|
|
buf[#buf + 1] = tp:name_get()
|
|
local fields = tp:enum_fields_get():to_array()
|
|
if #fields == 0 then
|
|
buf[#buf + 1] = " {}"
|
|
return table.concat(buf)
|
|
end
|
|
buf[#buf + 1] = " {\n"
|
|
for i, fld in ipairs(fields) do
|
|
buf[#buf + 1] = " "
|
|
buf[#buf + 1] = fld:name_get()
|
|
local val = fld:value_get()
|
|
if val then
|
|
buf[#buf + 1] = ": "
|
|
buf[#buf + 1] = val:serialize()
|
|
end
|
|
if i == #fields then
|
|
buf[#buf + 1] = "\n"
|
|
else
|
|
buf[#buf + 1] = ",\n"
|
|
end
|
|
end
|
|
buf[#buf + 1] = "}"
|
|
return table.concat(buf)
|
|
elseif tpt == eolian.typedecl_type.ALIAS then
|
|
local buf = { "type " }
|
|
add_typedecl_attrs(tp, buf)
|
|
buf[#buf + 1] = tp:name_get()
|
|
buf[#buf + 1] = ": "
|
|
buf[#buf + 1] = serialize_type(tp:base_type_get())
|
|
buf[#buf + 1] = ";"
|
|
return table.concat(buf)
|
|
elseif tpt == eolian.typedecl_type.FUNCTION_POINTER then
|
|
return "TODO"
|
|
end
|
|
error("unhandled typedecl type: " .. tpt)
|
|
end
|
|
|
|
serialize_tdecl_c = function(tp, ns)
|
|
local tpt = tp:type_get()
|
|
if tpt == eolian.typedecl_type.UNKNOWN then
|
|
error("unknown typedecl: " .. tp:name_get())
|
|
elseif tpt == eolian.typedecl_type.STRUCT or
|
|
tpt == eolian.typedecl_type.STRUCT_OPAQUE then
|
|
local buf = { "typedef struct " }
|
|
local fulln = tp:name_get():gsub("%.", "_");
|
|
keyref.add(fulln, ns, "c")
|
|
buf[#buf + 1] = "_" .. fulln;
|
|
if tpt == eolian.typedecl_type.STRUCT_OPAQUE then
|
|
buf[#buf + 1] = " " .. fulln .. ";"
|
|
return table.concat(buf)
|
|
end
|
|
local fields = tp:struct_fields_get():to_array()
|
|
if #fields == 0 then
|
|
buf[#buf + 1] = " {} " .. fulln .. ";"
|
|
return table.concat(buf)
|
|
end
|
|
buf[#buf + 1] = " {\n"
|
|
for i, fld in ipairs(fields) do
|
|
buf[#buf + 1] = " "
|
|
buf[#buf + 1] = M.type_cstr_get(fld:type_get(), fld:name_get())
|
|
buf[#buf + 1] = ";\n"
|
|
end
|
|
buf[#buf + 1] = "} " .. fulln .. ";"
|
|
return table.concat(buf)
|
|
elseif tpt == eolian.typedecl_type.ENUM then
|
|
local buf = { "typedef enum" }
|
|
local fulln = tp:name_get():gsub("%.", "_");
|
|
keyref.add(fulln, ns, "c")
|
|
local fields = tp:enum_fields_get():to_array()
|
|
if #fields == 0 then
|
|
buf[#buf + 1] = " {} " .. fulln .. ";"
|
|
return table.concat(buf)
|
|
end
|
|
buf[#buf + 1] = " {\n"
|
|
for i, fld in ipairs(fields) do
|
|
buf[#buf + 1] = " "
|
|
local cn = fld:c_name_get()
|
|
buf[#buf + 1] = cn
|
|
keyref.add(cn, ns, "c")
|
|
local val = fld:value_get()
|
|
if val then
|
|
buf[#buf + 1] = " = "
|
|
local ev = val:eval(eolian.expression_mask.INT)
|
|
local lit = ev:to_literal()
|
|
buf[#buf + 1] = lit
|
|
local ser = val:serialize()
|
|
if ser and ser ~= lit then
|
|
buf[#buf + 1] = " /* " .. ser .. " */"
|
|
end
|
|
end
|
|
if i == #fields then
|
|
buf[#buf + 1] = "\n"
|
|
else
|
|
buf[#buf + 1] = ",\n"
|
|
end
|
|
end
|
|
buf[#buf + 1] = "} " .. fulln .. ";"
|
|
return table.concat(buf)
|
|
elseif tpt == eolian.typedecl_type.ALIAS then
|
|
local fulln = tp:name_get():gsub("%.", "_");
|
|
keyref.add(fulln, ns, "c")
|
|
return "typedef "
|
|
.. M.type_cstr_get(tp:base_type_get(), fulln) .. ";"
|
|
elseif tpt == eolian.typedecl_type.FUNCTION_POINTER then
|
|
return "TODO"
|
|
end
|
|
error("unhandled typedecl type: " .. tpt)
|
|
end
|
|
|
|
serialize_var = function(var)
|
|
local buf = {}
|
|
if var:type_get() == eolian.variable_type.GLOBAL then
|
|
buf[#buf + 1] = "var "
|
|
else
|
|
buf[#buf + 1] = "const "
|
|
end
|
|
if var:is_extern() then
|
|
buf[#buf + 1] = "@extern "
|
|
end
|
|
buf[#buf + 1] = var:name_get()
|
|
buf[#buf + 1] = ": "
|
|
buf[#buf + 1] = serialize_type(var:base_type_get())
|
|
local val = var:value_get()
|
|
if val then
|
|
buf[#buf + 1] = " = "
|
|
buf[#buf + 1] = val:serialize()
|
|
end
|
|
buf[#buf + 1] = ";"
|
|
return table.concat(buf)
|
|
end
|
|
|
|
serialize_var_c = function(var, ns)
|
|
local buf = {}
|
|
local bt = var:base_type_get()
|
|
local fulln = var:name_get():gsub("%.", "_"):upper()
|
|
keyref.add(fulln, ns, "c")
|
|
if var:type_get() == eolian.variable_type.GLOBAL then
|
|
local ts = bt:c_type_get(eolian.c_type_type.DEFAULT)
|
|
buf[#buf + 1] = ts
|
|
if ts:sub(#ts) ~= "*" then
|
|
buf[#buf + 1] = " "
|
|
end
|
|
buf[#buf + 1] = fulln
|
|
local val = var:value_get()
|
|
if val then
|
|
buf[#buf + 1] = " = "
|
|
local vt = val:eval_type(bt)
|
|
local lv = vt:to_literal()
|
|
local sv = val:serialize()
|
|
buf[#buf + 1] = lv
|
|
if lv ~= sv then
|
|
buf[#buf + 1] = "/* " .. sv .. " */"
|
|
end
|
|
end
|
|
buf[#buf + 1] = ";"
|
|
else
|
|
buf[#buf + 1] = "#define "
|
|
buf[#buf + 1] = fulln
|
|
buf[#buf + 1] = " "
|
|
local val = var:value_get()
|
|
local vt = val:eval_type(bt)
|
|
local lv = vt:to_literal()
|
|
local sv = val:serialize()
|
|
buf[#buf + 1] = lv
|
|
if lv ~= sv then
|
|
buf[#buf + 1] = "/* " .. sv .. " */"
|
|
end
|
|
end
|
|
return table.concat(buf)
|
|
end
|
|
|
|
local sert = {
|
|
[eolian.object_type.TYPE] = serialize_type,
|
|
[eolian.object_type.TYPEDECL] = serialize_tdecl,
|
|
[eolian.object_type.VARIABLE] = serialize_var
|
|
}
|
|
|
|
M.obj_serialize = function(obj)
|
|
local f = sert[obj:object_get():type_get()]
|
|
assert(f, "cannot serialize object")
|
|
return f(obj)
|
|
end
|
|
|
|
local sertc = {
|
|
[eolian.object_type.TYPEDECL] = serialize_tdecl_c,
|
|
[eolian.object_type.VARIABLE] = serialize_var_c
|
|
}
|
|
|
|
M.obj_serialize_c = function(obj, ns)
|
|
local f = sertc[obj:object_get():type_get()]
|
|
assert(f, "cannot serialize object")
|
|
return f(obj, ns)
|
|
end
|
|
|
|
M.impl_is_overridden = function(obj, cl)
|
|
return obj:class_get() ~= cl
|
|
end
|
|
|
|
M.impl_fallback_doc_get = function(obj)
|
|
local ig, is = obj:is_prop_get(), obj:is_prop_set()
|
|
if ig and not is then
|
|
return obj:documentation_get(eolian.function_type.PROP_GET)
|
|
elseif is and not ig then
|
|
return obj:documentation_get(eolian.function_type.PROP_SET)
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local revh = {}
|
|
|
|
M.class_children_get = function(cl)
|
|
return revh[cl:name_get()] or {}
|
|
end
|
|
|
|
M.build_class_children = function(eos)
|
|
for cl in eos:classes_get() do
|
|
for icl in cl:inherits_get() do
|
|
local icln = icl:name_get()
|
|
local t = revh[icln]
|
|
if not t then
|
|
t = {}
|
|
revh[icln] = t
|
|
end
|
|
t[#t + 1] = cl
|
|
end
|
|
end
|
|
end
|
|
|
|
return M
|