1428 lines
37 KiB
Lua
1428 lines
37 KiB
Lua
local util = require("util")
|
|
|
|
local eolian = require("eolian")
|
|
|
|
local keyref = require("docgen.keyref")
|
|
local dutil = require("docgen.util")
|
|
|
|
-- writer has to be loaded late to prevent cycles
|
|
local writer
|
|
|
|
local M = {}
|
|
local eos = eolian:new()
|
|
|
|
local get_cache = function(o, nm)
|
|
local ret = o[nm]
|
|
if not ret then
|
|
ret = {}
|
|
o[nm] = ret
|
|
return ret, false
|
|
end
|
|
return ret, true
|
|
end
|
|
|
|
local matches_filter = function(obj)
|
|
local ns = obj:nspaces_get()
|
|
if #ns and (ns[1] == "efl" or ns[1] == "eina") then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
M.Node = util.Object:clone {
|
|
scope = {
|
|
UNKNOWN = eolian.object_scope.UNKNOWN,
|
|
PUBLIC = eolian.object_scope.PUBLIC,
|
|
PRIVATE = eolian.object_scope.PRIVATE,
|
|
PROTECTED = eolian.object_scope.PROTECTED
|
|
},
|
|
|
|
__ctor = function(self, obj)
|
|
self._obj = obj
|
|
assert(self._obj)
|
|
end,
|
|
|
|
name_get = function(self)
|
|
return self._obj:name_get()
|
|
end,
|
|
|
|
short_name_get = function(self)
|
|
return self._obj:short_name_get()
|
|
end,
|
|
|
|
namespaces_get = function(self)
|
|
return self._obj:namespaces_get():to_array()
|
|
end,
|
|
|
|
nspaces_get = function(self, root)
|
|
local tbl = self:namespaces_get()
|
|
-- temporary workaround
|
|
if type(tbl) ~= "table" then
|
|
tbl = tbl:to_array()
|
|
end
|
|
for i = 1, #tbl do
|
|
tbl[i] = tbl[i]:lower()
|
|
end
|
|
|
|
tbl[#tbl + 1] = self:short_name_get():lower()
|
|
if root ~= nil then
|
|
tbl[#tbl + 1] = not not root
|
|
end
|
|
return tbl
|
|
end
|
|
}
|
|
local Node = M.Node
|
|
|
|
local gen_doc_refd = function(str)
|
|
if not writer then
|
|
writer = require("docgen.writer")
|
|
end
|
|
if not str then
|
|
return nil
|
|
end
|
|
local pars = eolian.documentation_string_split(str)
|
|
for i = 1, #pars do
|
|
pars[i] = writer.Buffer():write_par(pars[i]):finish()
|
|
end
|
|
return table.concat(pars, "\n\n")
|
|
end
|
|
|
|
local add_since = function(str, since)
|
|
if not writer then
|
|
writer = require("docgen.writer")
|
|
end
|
|
if not since then
|
|
return str
|
|
end
|
|
local buf = writer.Buffer()
|
|
if not str then
|
|
buf:write_i("Since " .. since)
|
|
return buf:finish()
|
|
end
|
|
buf:write_raw(str)
|
|
buf:write_nl(2)
|
|
buf:write_i("Since " .. since)
|
|
return buf:finish()
|
|
end
|
|
|
|
M.Doc = Node:clone {
|
|
-- duplicate ctor to disable assertion
|
|
__ctor = function(self, obj)
|
|
self._obj = obj
|
|
end,
|
|
|
|
summary_get = function(self)
|
|
if not self._obj then
|
|
return nil
|
|
end
|
|
return self._obj:summary_get()
|
|
end,
|
|
|
|
description_get = function(self)
|
|
if not self._obj then
|
|
return nil
|
|
end
|
|
return self._obj:description_get()
|
|
end,
|
|
|
|
since_get = function(self)
|
|
if not self._obj then
|
|
return nil
|
|
end
|
|
return self._obj:since_get()
|
|
end,
|
|
|
|
brief_get = function(self, doc2)
|
|
if not self._obj and (not doc2 or not doc2._obj) then
|
|
return "No description supplied."
|
|
end
|
|
local doc1, doc2 = self._obj, doc2 and doc2._obj or nil
|
|
if not doc1 then
|
|
doc1, doc2 = doc2, doc1
|
|
end
|
|
return gen_doc_refd(doc1:summary_get())
|
|
end,
|
|
|
|
full_get = function(self, doc2, write_since)
|
|
if not self._obj and (not doc2 or not doc2._obj) then
|
|
return "No description supplied."
|
|
end
|
|
local doc1, doc2 = self._obj, doc2 and doc2._obj or nil
|
|
if not doc1 then
|
|
doc1, doc2 = doc2, doc1
|
|
end
|
|
local sum1 = doc1:summary_get()
|
|
local desc1 = doc1:description_get()
|
|
local edoc = ""
|
|
local since
|
|
if doc2 then
|
|
local sum2 = doc2:summary_get()
|
|
local desc2 = doc2:description_get()
|
|
if not desc2 then
|
|
if sum2 then edoc = "\n\n" .. sum2 end
|
|
else
|
|
edoc = "\n\n" .. sum2 .. "\n\n" .. desc2
|
|
end
|
|
if write_since then
|
|
since = doc2:since_get()
|
|
end
|
|
end
|
|
if not since and write_since then
|
|
since = doc1:since_get()
|
|
end
|
|
if not desc1 then
|
|
return add_since(gen_doc_refd(sum1 .. edoc), since)
|
|
end
|
|
return add_since(gen_doc_refd(sum1 .. "\n\n" .. desc1 .. edoc), since)
|
|
end,
|
|
|
|
exists = function(self)
|
|
return not not self._obj
|
|
end
|
|
}
|
|
|
|
local revh = {}
|
|
|
|
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 = Node:clone {
|
|
-- class types
|
|
UNKNOWN = eolian.class_type.UNKNOWN,
|
|
REGULAR = eolian.class_type.REGULAR,
|
|
ABSTRACT = eolian.class_type.ABSTRACT,
|
|
MIXIN = eolian.class_type.MIXIN,
|
|
INTERFACE = eolian.class_type.INTERFACE,
|
|
|
|
type_get = function(self)
|
|
return self._obj:type_get()
|
|
end,
|
|
|
|
type_str_get = function(self)
|
|
return class_type_str[self:type_get()]
|
|
end,
|
|
|
|
theme_str_get = function(self)
|
|
return ({
|
|
[eolian.class_type.REGULAR] = "regular",
|
|
[eolian.class_type.ABSTRACT] = "abstract",
|
|
[eolian.class_type.MIXIN] = "mixin",
|
|
[eolian.class_type.INTERFACE] = "interface"
|
|
})[self:type_get()]
|
|
end,
|
|
|
|
doc_get = function(self)
|
|
return M.Doc(self._obj:documentation_get())
|
|
end,
|
|
|
|
legacy_prefix_get = function(self)
|
|
return self._obj:legacy_prefix_get()
|
|
end,
|
|
|
|
eo_prefix_get = function(self)
|
|
return self._obj:eo_prefix_get()
|
|
end,
|
|
|
|
inherits_get = function(self)
|
|
local ret = self._cache_inhc
|
|
if ret then
|
|
return ret
|
|
end
|
|
ret = {}
|
|
for cl in self._obj:inherits_get() do
|
|
ret[#ret + 1] = M.Class(cl)
|
|
end
|
|
self._cache_inhc = ret
|
|
return ret
|
|
end,
|
|
|
|
children_get = function(self)
|
|
return revh[self:name_get()] or {}
|
|
end,
|
|
|
|
functions_get = function(self, ft)
|
|
local ret = self._cache_funcs
|
|
if ret then
|
|
return ret
|
|
end
|
|
ret = {}
|
|
self._cache_funcs = ret
|
|
for fn in self._obj:functions_get(ft) do
|
|
ret[#ret + 1] = M.Function(fn)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
function_by_name_get = function(self, fn, ft)
|
|
local fun = self._cache_func
|
|
if fun then
|
|
return fun
|
|
end
|
|
fun = M.Function(self._obj:function_by_name_get(fn, ft))
|
|
self._cache_func = fun
|
|
return fun
|
|
end,
|
|
|
|
events_get = function(self)
|
|
local ret = {}
|
|
for ev in self._obj:events_get() do
|
|
ret[#ret + 1] = M.Event(ev)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
implements_get = function(self)
|
|
local ret = {}
|
|
for impl in self._obj:implements_get() do
|
|
ret[#ret + 1] = M.Implement(impl)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
c_get_function_name_get = function(self)
|
|
return self._obj:c_get_function_name_get()
|
|
end,
|
|
|
|
nspaces_get = function(self, root)
|
|
return M.Node.nspaces_get(self, root)
|
|
end,
|
|
|
|
is_same = function(self, other)
|
|
return self._obj == other._obj
|
|
end,
|
|
|
|
-- static getters
|
|
|
|
by_name_get = function(name)
|
|
local stor = get_cache(M.Class, "_cache_bn")
|
|
local ret = stor[name]
|
|
if ret then
|
|
return ret
|
|
end
|
|
local v = eos:class_by_name_get(name)
|
|
if not v then
|
|
return nil
|
|
end
|
|
ret = M.Class(v)
|
|
stor[name] = ret
|
|
return ret
|
|
end,
|
|
|
|
by_file_get = function(name)
|
|
local stor = get_cache(M.Class, "_cache_bf")
|
|
local ret = stor[name]
|
|
if ret then
|
|
return ret
|
|
end
|
|
local v = eos:class_by_file_get(name)
|
|
if not v then
|
|
return nil
|
|
end
|
|
ret = M.Class(v)
|
|
stor[name] = ret
|
|
return ret
|
|
end,
|
|
|
|
all_get = function()
|
|
local ret, had = get_cache(M.Class, "_cache_all")
|
|
if not had then
|
|
-- FIXME: unit
|
|
for cl in eos:classes_get() do
|
|
local cls = M.Class(cl)
|
|
if matches_filter(cls) then
|
|
ret[#ret + 1] = cls
|
|
end
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
}
|
|
|
|
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"
|
|
}
|
|
|
|
local ffi = require("ffi")
|
|
|
|
M.Function = Node:clone {
|
|
-- function types
|
|
UNRESOLVED = eolian.function_type.UNRESOLVED,
|
|
PROPERTY = eolian.function_type.PROPERTY,
|
|
PROP_SET = eolian.function_type.PROP_SET,
|
|
PROP_GET = eolian.function_type.PROP_GET,
|
|
METHOD = eolian.function_type.METHOD,
|
|
FUNCTION_POINTER = eolian.function_type.FUNCTION_POINTER,
|
|
|
|
type_get = function(self)
|
|
return self._obj:type_get()
|
|
end,
|
|
|
|
type_str_get = function(self)
|
|
return func_type_str[self:type_get()]
|
|
end,
|
|
|
|
scope_get = function(self, ft)
|
|
return self._obj:scope_get(ft)
|
|
end,
|
|
|
|
full_c_name_get = function(self, ft, legacy)
|
|
return self._obj:full_c_name_get(ft, legacy)
|
|
end,
|
|
|
|
legacy_get = function(self, ft)
|
|
return self._obj:legacy_get(ft)
|
|
end,
|
|
|
|
implement_get = function(self)
|
|
return M.Implement(self._obj:implement_get())
|
|
end,
|
|
|
|
is_legacy_only = function(self, ft)
|
|
return self._obj:is_legacy_only(ft)
|
|
end,
|
|
|
|
is_class = function(self)
|
|
return self._obj:is_class()
|
|
end,
|
|
|
|
is_beta = function(self)
|
|
return self._obj:is_beta()
|
|
end,
|
|
|
|
is_constructor = function(self, klass)
|
|
return self._obj:is_constructor(klass.class)
|
|
end,
|
|
|
|
is_function_pointer = function(self)
|
|
return self._obj:is_function_pointer()
|
|
end,
|
|
|
|
property_keys_get = function(self, ft)
|
|
local ret = {}
|
|
for par in self._obj:property_keys_get(ft) do
|
|
ret[#ret + 1] = M.Parameter(par)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
property_values_get = function(self, ft)
|
|
local ret = {}
|
|
for par in self._obj:property_values_get(ft) do
|
|
ret[#ret + 1] = M.Parameter(par)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
parameters_get = function(self)
|
|
local ret = {}
|
|
for par in self._obj:parameters_get() do
|
|
ret[#ret + 1] = M.Parameter(par)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
return_type_get = function(self, ft)
|
|
local v = self._obj:return_type_get(ft)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
return_default_value_get = function(self, ft)
|
|
local v = self._obj:return_default_value_get(ft)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Expression(v)
|
|
end,
|
|
|
|
return_doc_get = function(self, ft)
|
|
return M.Doc(self._obj:return_documentation_get(ft))
|
|
end,
|
|
|
|
return_is_warn_unused = function(self, ft)
|
|
return self._obj:return_is_warn_unused(ft)
|
|
end,
|
|
|
|
is_const = function(self)
|
|
return self._obj:is_const()
|
|
end,
|
|
|
|
nspaces_get = function(self, cl, root)
|
|
local tbl = cl:nspaces_get()
|
|
tbl[#tbl + 1] = self:type_str_get()
|
|
tbl[#tbl + 1] = self:name_get():lower()
|
|
if root ~= nil then
|
|
tbl[#tbl + 1] = not not root
|
|
end
|
|
return tbl
|
|
end,
|
|
|
|
is_same = function(self, other)
|
|
return self._obj == other._obj
|
|
end,
|
|
|
|
id_get = function(self)
|
|
return tonumber(ffi.cast("uintptr_t", self._obj))
|
|
end
|
|
}
|
|
|
|
M.Parameter = Node:clone {
|
|
UNKNOWN = eolian.parameter_dir.UNKNOWN,
|
|
IN = eolian.parameter_dir.IN,
|
|
OUT = eolian.parameter_dir.OUT,
|
|
INOUT = eolian.parameter_dir.INOUT,
|
|
|
|
direction_get = function(self)
|
|
return self._obj:direction_get()
|
|
end,
|
|
|
|
direction_name_get = function(self)
|
|
local dir_to_str = {
|
|
[self.IN] = "in",
|
|
[self.OUT] = "out",
|
|
[self.INOUT] = "inout"
|
|
}
|
|
return assert(dir_to_str[self:direction_get()],
|
|
"unknown parameter direction")
|
|
end,
|
|
|
|
type_get = function(self)
|
|
local v = self._obj:type_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
default_value_get = function(self)
|
|
local v = self._obj:default_value_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Expression(v)
|
|
end,
|
|
|
|
doc_get = function(self)
|
|
return M.Doc(self._obj:documentation_get())
|
|
end,
|
|
|
|
is_nonull = function(self)
|
|
return self._obj:is_nonull()
|
|
end,
|
|
|
|
is_nullable = function(self)
|
|
return self._obj:is_nullable()
|
|
end,
|
|
|
|
is_optional = function(self)
|
|
return self._obj:is_optional()
|
|
end,
|
|
|
|
is_same = function(self, other)
|
|
return self._obj == other._obj
|
|
end
|
|
}
|
|
|
|
M.Event = Node:clone {
|
|
type_get = function(self)
|
|
local v = self._obj:type_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
doc_get = function(self)
|
|
return M.Doc(self._obj:documentation_get())
|
|
end,
|
|
|
|
scope_get = function(self)
|
|
return self._obj:scope_get()
|
|
end,
|
|
|
|
c_name_get = function(self)
|
|
return self._obj:c_name_get()
|
|
end,
|
|
|
|
is_beta = function(self)
|
|
return self._obj:is_beta()
|
|
end,
|
|
|
|
is_hot = function(self)
|
|
return self._obj:is_hot()
|
|
end,
|
|
|
|
is_restart = function(self)
|
|
return self._obj:is_restart()
|
|
end,
|
|
|
|
nspaces_get = function(self, cl, root)
|
|
local tbl = cl:nspaces_get()
|
|
tbl[#tbl + 1] = "event"
|
|
tbl[#tbl + 1] = self:name_get():lower():gsub(",", "_")
|
|
if root ~= nil then
|
|
tbl[#tbl + 1] = not not root
|
|
end
|
|
return tbl
|
|
end
|
|
}
|
|
|
|
M.StructField = Node:clone {
|
|
doc_get = function(self)
|
|
return M.Doc(self._obj:documentation_get())
|
|
end,
|
|
|
|
type_get = function(self)
|
|
local v = self._obj:type_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end
|
|
}
|
|
|
|
M.EnumField = Node:clone {
|
|
c_name_get = function(self)
|
|
return self._obj:c_name_get()
|
|
end,
|
|
|
|
doc_get = function(self)
|
|
return M.Doc(self._obj:documentation_get())
|
|
end,
|
|
|
|
value_get = function(self, force)
|
|
local v = self._obj:value_get(force)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Expression(v)
|
|
end
|
|
}
|
|
|
|
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
|
|
|
|
M.Type = Node:clone {
|
|
UNKNOWN = eolian.type_type.UNKNOWN,
|
|
VOID = eolian.type_type.VOID,
|
|
REGULAR = eolian.type_type.REGULAR,
|
|
CLASS = eolian.type_type.CLASS,
|
|
UNDEFINED = eolian.type_type.UNDEFINED,
|
|
|
|
type_get = function(self)
|
|
return self._obj:type_get()
|
|
end,
|
|
|
|
file_get = function(self)
|
|
return self._obj:file_get()
|
|
end,
|
|
|
|
base_type_get = function(self)
|
|
local v = self._obj:base_type_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
next_type_get = function(self)
|
|
local v = self._obj:next_type_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
typedecl_get = function(self)
|
|
local v = self._obj:typedecl_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Typedecl(v)
|
|
end,
|
|
|
|
aliased_base_get = function(self)
|
|
local v = self._obj:aliased_base_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
class_get = function(self)
|
|
return self._obj:class_get()
|
|
end,
|
|
|
|
is_owned = function(self)
|
|
return self._obj:is_owned()
|
|
end,
|
|
|
|
is_const = function(self)
|
|
return self._obj:is_const()
|
|
end,
|
|
|
|
is_ptr = function(self)
|
|
return self._obj:is_ptr()
|
|
end,
|
|
|
|
c_type_get = function(self)
|
|
return self._obj:c_type_get(eolian.c_type_type.DEFAULT)
|
|
end,
|
|
|
|
free_func_get = function(self)
|
|
return self._obj:free_func_get()
|
|
end,
|
|
|
|
-- utils
|
|
|
|
serialize = function(self)
|
|
local tpt = self:type_get()
|
|
if tpt == self.UNKNOWN then
|
|
error("unknown type: " .. self:name_get())
|
|
elseif tpt == self.VOID then
|
|
return wrap_type_attrs(self, "void")
|
|
elseif tpt == self.UNDEFINED then
|
|
return wrap_type_attrs(self, "__undefined_type")
|
|
elseif tpt == self.REGULAR or tpt == self.CLASS then
|
|
local stp = self:base_type_get()
|
|
if stp then
|
|
local stypes = {}
|
|
while stp do
|
|
stypes[#stypes + 1] = stp:serialize()
|
|
stp = stp:next_type_get()
|
|
end
|
|
return wrap_type_attrs(self, self:name_get() .. "<"
|
|
.. table.concat(stypes, ", ") .. ">")
|
|
end
|
|
return wrap_type_attrs(self, self:name_get())
|
|
end
|
|
error("unhandled type type: " .. tpt)
|
|
end
|
|
}
|
|
|
|
M.type_cstr_get = function(tp, suffix)
|
|
tp = tp or "void"
|
|
local ct = (type(tp) == "string") and tp or tp:c_type_get()
|
|
if not suffix then
|
|
return ct
|
|
end
|
|
if ct:sub(#ct) == "*" then
|
|
return ct .. suffix
|
|
else
|
|
return ct .. " " .. suffix
|
|
end
|
|
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
|
|
|
|
M.Typedecl = Node:clone {
|
|
UNKNOWN = eolian.typedecl_type.UNKNOWN,
|
|
STRUCT = eolian.typedecl_type.STRUCT,
|
|
STRUCT_OPAQUE = eolian.typedecl_type.STRUCT_OPAQUE,
|
|
ENUM = eolian.typedecl_type.ENUM,
|
|
ALIAS = eolian.typedecl_type.ALIAS,
|
|
FUNCTION_POINTER = eolian.typedecl_type.FUNCTION_POINTER,
|
|
|
|
type_get = function(self)
|
|
return self._obj:type_get()
|
|
end,
|
|
|
|
type_str_get = function(self)
|
|
local strs = {
|
|
[eolian.typedecl_type.STRUCT] = "struct",
|
|
[eolian.typedecl_type.STRUCT_OPAQUE] = "struct",
|
|
[eolian.typedecl_type.ENUM] = "enum",
|
|
[eolian.typedecl_type.ALIAS] = "alias"
|
|
}
|
|
return strs[self:type_get()]
|
|
end,
|
|
|
|
struct_fields_get = function(self)
|
|
local ret = {}
|
|
for fl in self._obj:struct_fields_get() do
|
|
ret[#ret + 1] = M.StructField(fl)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
struct_field_get = function(self, name)
|
|
local v = self._obj:struct_field_get(name)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.StructField(v)
|
|
end,
|
|
|
|
enum_fields_get = function(self)
|
|
local ret = {}
|
|
for fl in self._obj:enum_fields_get() do
|
|
ret[#ret + 1] = M.EnumField(fl)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
enum_field_get = function(self, name)
|
|
local v = self._obj:enum_field_get(name)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.EnumField(v)
|
|
end,
|
|
|
|
enum_legacy_prefix_get = function(self)
|
|
return self._obj:enum_legacy_prefix_get()
|
|
end,
|
|
|
|
doc_get = function(self)
|
|
return M.Doc(self._obj:documentation_get())
|
|
end,
|
|
|
|
file_get = function(self)
|
|
return self._obj:file_get()
|
|
end,
|
|
|
|
base_type_get = function(self)
|
|
local v = self._obj:base_type_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
aliased_base_get = function(self)
|
|
local v = self._obj:aliased_base_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
is_extern = function(self)
|
|
return self._obj:is_extern()
|
|
end,
|
|
|
|
c_type_get = function(self)
|
|
return self._obj:c_type_get()
|
|
end,
|
|
|
|
free_func_get = function(self)
|
|
return self._obj:free_func_get()
|
|
end,
|
|
|
|
function_pointer_get = function(self)
|
|
local v = self._obj:function_pointer_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Function(v)
|
|
end,
|
|
|
|
nspaces_get = function(self, root)
|
|
return M.Node.nspaces_get(self, root)
|
|
end,
|
|
|
|
-- static getters
|
|
|
|
all_aliases_get = function()
|
|
local ret = {}
|
|
for tp in eos:aliases_get() do
|
|
local tpo = M.Typedecl(tp)
|
|
if matches_filter(tpo) then
|
|
ret[#ret + 1] = tpo
|
|
end
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
all_structs_get = function()
|
|
local ret = {}
|
|
for tp in eos:structs_get() do
|
|
local tpo = M.Typedecl(tp)
|
|
if matches_filter(tpo) then
|
|
ret[#ret + 1] = tpo
|
|
end
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
all_enums_get = function()
|
|
local ret = {}
|
|
for tp in eos:enums_get() do
|
|
local tpo = M.Typedecl(tp)
|
|
local tpn = tpo:nspaces_get()
|
|
if matches_filter(tpo) then
|
|
ret[#ret + 1] = tpo
|
|
end
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
aliases_by_file_get = function(fn)
|
|
local ret = {}
|
|
for tp in eos:aliases_by_file_get(fn) do
|
|
ret[#ret + 1] = M.Typedecl(tp)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
structs_by_file_get = function(fn)
|
|
local ret = {}
|
|
for tp in eos:struts_by_file_get(fn) do
|
|
ret[#ret + 1] = M.Typedecl(tp)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
enums_by_file_get = function(fn)
|
|
local ret = {}
|
|
for tp in eeos:enums_by_file_get(fn) do
|
|
ret[#ret + 1] = M.Typedecl(tp)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
alias_by_name_get = function(tn)
|
|
local v = eos:alias_by_name_get(tn)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Typedecl(v)
|
|
end,
|
|
|
|
struct_by_name_get = function(tn)
|
|
local v = eos:struct_by_name_get(tn)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Typedecl(v)
|
|
end,
|
|
|
|
enum_by_name_get = function(tn)
|
|
local v = eos:enum_by_name_get(tn)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Typedecl(v)
|
|
end,
|
|
|
|
-- utils
|
|
|
|
serialize = function(self)
|
|
local tpt = self:type_get()
|
|
if tpt == self.UNKNOWN then
|
|
error("unknown typedecl: " .. self:name_get())
|
|
elseif tpt == self.STRUCT or
|
|
tpt == self.STRUCT_OPAQUE then
|
|
local buf = { "struct " }
|
|
add_typedecl_attrs(self, buf)
|
|
buf[#buf + 1] = self:name_get()
|
|
if tpt == self.STRUCT_OPAQUE then
|
|
buf[#buf + 1] = ";"
|
|
return table.concat(buf)
|
|
end
|
|
local fields = self:struct_fields_get()
|
|
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] = fld:type_get():serialize()
|
|
buf[#buf + 1] = ";\n"
|
|
end
|
|
buf[#buf + 1] = "}"
|
|
return table.concat(buf)
|
|
elseif tpt == self.ENUM then
|
|
local buf = { "enum " }
|
|
add_typedecl_attrs(self, buf)
|
|
buf[#buf + 1] = self:name_get()
|
|
local fields = self:enum_fields_get()
|
|
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 == self.ALIAS then
|
|
local buf = { "type " }
|
|
add_typedecl_attrs(self, buf)
|
|
buf[#buf + 1] = self:name_get()
|
|
buf[#buf + 1] = ": "
|
|
buf[#buf + 1] = self:base_type_get():serialize()
|
|
buf[#buf + 1] = ";"
|
|
return table.concat(buf)
|
|
elseif tpt == self.FUNCTION_POINTER then
|
|
return "TODO"
|
|
end
|
|
error("unhandled typedecl type: " .. tpt)
|
|
end,
|
|
|
|
serialize_c = function(self, ns)
|
|
local tpt = self:type_get()
|
|
if tpt == self.UNKNOWN then
|
|
error("unknown typedecl: " .. self:name_get())
|
|
elseif tpt == self.STRUCT or
|
|
tpt == self.STRUCT_OPAQUE then
|
|
local buf = { "typedef struct " }
|
|
local fulln = self:name_get():gsub("%.", "_");
|
|
keyref.add(fulln, ns, "c")
|
|
buf[#buf + 1] = "_" .. fulln;
|
|
if tpt == self.STRUCT_OPAQUE then
|
|
buf[#buf + 1] = " " .. fulln .. ";"
|
|
return table.concat(buf)
|
|
end
|
|
local fields = self:struct_fields_get()
|
|
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 == self.ENUM then
|
|
local buf = { "typedef enum" }
|
|
local fulln = self:name_get():gsub("%.", "_");
|
|
keyref.add(fulln, ns, "c")
|
|
local fields = self:enum_fields_get()
|
|
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_enum()
|
|
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 == self.ALIAS then
|
|
local fulln = self:name_get():gsub("%.", "_");
|
|
keyref.add(fulln, ns, "c")
|
|
return "typedef "
|
|
.. M.type_cstr_get(self:base_type_get(), fulln) .. ";"
|
|
elseif tpt == self.FUNCTION_POINTER then
|
|
return "TODO"
|
|
end
|
|
error("unhandled typedecl type: " .. tpt)
|
|
end
|
|
}
|
|
|
|
M.Variable = Node:clone {
|
|
UNKNOWN = eolian.variable_type.UNKNOWN,
|
|
CONSTANT = eolian.variable_type.CONSTANT,
|
|
GLOBAL = eolian.variable_type.GLOBAL,
|
|
|
|
type_get = function(self)
|
|
return self._obj:type_get()
|
|
end,
|
|
|
|
type_str_get = function(self)
|
|
local strs = {
|
|
[eolian.variable_type.CONSTANT] = "constant",
|
|
[eolian.variable_type.GLOBAL] = "global"
|
|
}
|
|
return strs[self:type_get()]
|
|
end,
|
|
|
|
doc_get = function(self)
|
|
return M.Doc(self._obj:documentation_get())
|
|
end,
|
|
|
|
file_get = function(self)
|
|
return self._obj:file_get()
|
|
end,
|
|
|
|
base_type_get = function(self)
|
|
local v = self._obj:base_type_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Type(v)
|
|
end,
|
|
|
|
value_get = function(self)
|
|
local v = self._obj:value_get()
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Expression(v)
|
|
end,
|
|
|
|
is_extern = function(self)
|
|
return self._obj:is_extern()
|
|
end,
|
|
|
|
nspaces_get = function(self, root)
|
|
return M.Node.nspaces_get(self, root)
|
|
end,
|
|
|
|
serialize = function(self)
|
|
local buf = {}
|
|
if self:type_get() == self.GLOBAL then
|
|
buf[#buf + 1] = "var "
|
|
else
|
|
buf[#buf + 1] = "const "
|
|
end
|
|
if self:is_extern() then
|
|
buf[#buf + 1] = "@extern "
|
|
end
|
|
buf[#buf + 1] = self:name_get()
|
|
buf[#buf + 1] = ": "
|
|
buf[#buf + 1] = self:base_type_get():serialize()
|
|
local val = self:value_get()
|
|
if val then
|
|
buf[#buf + 1] = " = "
|
|
buf[#buf + 1] = val:serialize()
|
|
end
|
|
buf[#buf + 1] = ";"
|
|
return table.concat(buf)
|
|
end,
|
|
|
|
serialize_c = function(self, ns)
|
|
local buf = {}
|
|
local bt = self:base_type_get()
|
|
local fulln = self:name_get():gsub("%.", "_"):upper()
|
|
keyref.add(fulln, ns, "c")
|
|
if self:type_get() == self.GLOBAL then
|
|
local ts = bt:c_type_get()
|
|
buf[#buf + 1] = ts
|
|
if ts:sub(#ts) ~= "*" then
|
|
buf[#buf + 1] = " "
|
|
end
|
|
buf[#buf + 1] = fulln
|
|
local val = self: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 = self: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,
|
|
|
|
-- static getters
|
|
|
|
all_globals_get = function()
|
|
local ret = {}
|
|
for v in eos:globals_get() do
|
|
ret[#ret + 1] = M.Variable(v)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
all_constants_get = function()
|
|
local ret = {}
|
|
for v in eos:constants_get() do
|
|
ret[#ret + 1] = M.Variable(v)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
globals_by_file_get = function(fn)
|
|
local ret = {}
|
|
for v in eos:globals_by_file_get(fn) do
|
|
ret[#ret + 1] = M.Variable(v)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
constants_by_file_get = function(fn)
|
|
local ret = {}
|
|
for v in eos:constants_by_file_get(fn) do
|
|
ret[#ret + 1] = M.Variable(v)
|
|
end
|
|
return ret
|
|
end,
|
|
|
|
global_by_name_get = function(vn)
|
|
local v = eos:global_by_name_get(vn)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Variable(v)
|
|
end,
|
|
|
|
constant_by_name_get = function(vn)
|
|
local v = eos:constant_by_name_get(vn)
|
|
if not v then
|
|
return nil
|
|
end
|
|
return M.Variable(v)
|
|
end
|
|
}
|
|
|
|
M.Expression = Node:clone {
|
|
eval_enum = function(self)
|
|
return self._obj:eval(eolian.expression_mask.INT)
|
|
end,
|
|
|
|
eval_type = function(self, tp)
|
|
return self._obj:eval_type(tp.type)
|
|
end,
|
|
|
|
serialize = function(self)
|
|
return self._obj:serialize()
|
|
end
|
|
}
|
|
|
|
M.Implement = Node:clone {
|
|
class_get = function(self)
|
|
local ccl = self._cache_cl
|
|
if ccl then
|
|
return ccl
|
|
end
|
|
-- so that we don't re-instantiate, it gets cached over there too
|
|
ccl = M.Class.by_name_get(self._obj:class_get():name_get())
|
|
self._cache_cl = ccl
|
|
return ccl
|
|
end,
|
|
|
|
function_get = function(self)
|
|
local func, tp = self._cache_func, self._cache_tp
|
|
if func then
|
|
return func, tp
|
|
end
|
|
func, tp = self._obj:function_get()
|
|
func = M.Function(func)
|
|
self._cache_func, self._cache_tp = func, tp
|
|
return func, tp
|
|
end,
|
|
|
|
doc_get = function(self, ftype, inh)
|
|
return M.Doc(self._obj:documentation_get(ftype))
|
|
end,
|
|
|
|
fallback_doc_get = function(self, inh)
|
|
local ig, is = self:is_prop_get(), self:is_prop_set()
|
|
if ig and not is then
|
|
return self:doc_get(M.Function.PROP_GET, inh)
|
|
elseif is and not ig then
|
|
return self:doc_get(M.Function.PROP_SET, inh)
|
|
end
|
|
return nil
|
|
end,
|
|
|
|
is_auto = function(self, ftype)
|
|
return self._obj:is_auto(ftype)
|
|
end,
|
|
|
|
is_empty = function(self, ftype)
|
|
return self._obj:is_empty(ftype)
|
|
end,
|
|
|
|
is_pure_virtual = function(self, ftype)
|
|
return self._obj:is_pure_virtual(ftype)
|
|
end,
|
|
|
|
is_prop_get = function(self)
|
|
return self._obj:is_prop_get()
|
|
end,
|
|
|
|
is_prop_set = function(self)
|
|
return self._obj:is_prop_set()
|
|
end,
|
|
|
|
is_overridden = function(self, cl)
|
|
return cl.class ~= self._obj:class_get()
|
|
end
|
|
}
|
|
|
|
M.DocTokenizer = util.Object:clone {
|
|
UNKNOWN = eolian.doc_token_type.UNKNOWN,
|
|
TEXT = eolian.doc_token_type.TEXT,
|
|
REF = eolian.doc_token_type.REF,
|
|
MARK_NOTE = eolian.doc_token_type.MARK_NOTE,
|
|
MARK_WARNING = eolian.doc_token_type.MARK_WARNING,
|
|
MARK_REMARK = eolian.doc_token_type.MARK_REMARK,
|
|
MARK_TODO = eolian.doc_token_type.MARK_TODO,
|
|
MARKUP_MONOSPACE = eolian.doc_token_type.MARKUP_MONOSPACE,
|
|
|
|
__ctor = function(self, str)
|
|
self.tok = eolian.doc_token_init()
|
|
self.str = str
|
|
assert(self.str)
|
|
assert(self.tok)
|
|
end,
|
|
|
|
tokenize = function(self)
|
|
self.str = eolian.documentation_tokenize(self.str, self.tok)
|
|
return not not self.str
|
|
end,
|
|
|
|
text_get = function(self)
|
|
return self.tok:text_get()
|
|
end,
|
|
|
|
type_get = function(self)
|
|
return self.tok:type_get()
|
|
end,
|
|
|
|
ref_resolve = function(self, root)
|
|
-- FIXME: unit
|
|
local tp, d1, d2 = self.tok:ref_resolve(eos)
|
|
local reft = eolian.object_type
|
|
local ret = {}
|
|
if tp == reft.CLASS or tp == reft.FUNCTION or tp == reft.EVENT then
|
|
if not class_type_str[d1:type_get()] then
|
|
error("unknown class type for class '"
|
|
.. d1:name_get() .. "'")
|
|
end
|
|
elseif tp == reft.TYPEDECL then
|
|
elseif tp == reft.ENUM_FIELD or tp == reft.STRUCT_FIELD then
|
|
-- TODO: point to field
|
|
elseif tp == reft.VARIABLE then
|
|
else
|
|
error("invalid reference '" .. self:text_get() .. "'")
|
|
end
|
|
for tok in d1:name_get():gmatch("[^%.]+") do
|
|
ret[#ret + 1] = tok:lower()
|
|
end
|
|
if tp == reft.FUNCTION then
|
|
ret[#ret + 1] = func_type_str[d2:type_get()]
|
|
ret[#ret + 1] = d2:name_get():lower()
|
|
elseif tp == reft.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.scan_directory = function(dir)
|
|
if not dir then
|
|
if not eos:system_directory_add() then
|
|
error("failed scanning system directory")
|
|
end
|
|
return
|
|
end
|
|
if not eos:directory_add(dir) then
|
|
error("failed scanning directory: " .. dir)
|
|
end
|
|
end
|
|
|
|
M.parse = function(st)
|
|
if not eos:all_eot_files_parse() then
|
|
error("failed parsing eo type files")
|
|
end
|
|
if st and st:match("%.") then
|
|
if not eos:file_parse(st:gsub("%.", "_"):lower() .. ".eo") then
|
|
error("failed parsing eo file")
|
|
end
|
|
else
|
|
if not eos:all_eo_files_parse() then
|
|
error("failed parsing eo files")
|
|
end
|
|
end
|
|
-- build reverse inheritance hierarchy
|
|
for cl in eos:classes_get() do
|
|
local cln = cl:name_get()
|
|
for icl in cl:inherits_get() do
|
|
local t = revh[icl]
|
|
if not t then
|
|
t = {}
|
|
revh[icl] = t
|
|
end
|
|
t[#t + 1] = M.Class.by_name_get(cl:name_get())
|
|
end
|
|
end
|
|
end
|
|
|
|
return M
|