edocgen/docgen/eolian_utils.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