efl/src/scripts/elua/apps/docgen/doctree.lua

1543 lines
40 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
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
},
nspaces_get = function(self, subn, 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
table.insert(tbl, 1, subn)
tbl[#tbl + 1] = self: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 {
__ctor = function(self, doc)
self.doc = doc
end,
summary_get = function(self)
if not self.doc then
return nil
end
return self.doc:summary_get()
end,
description_get = function(self)
if not self.doc then
return nil
end
return self.doc:description_get()
end,
since_get = function(self)
if not self.doc then
return nil
end
return self.doc:since_get()
end,
brief_get = function(self, doc2)
if not self.doc and (not doc2 or not doc2.doc) then
return "No description supplied."
end
local doc1, doc2 = self.doc, doc2 and doc2.doc 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.doc and (not doc2 or not doc2.doc) then
return "No description supplied."
end
local doc1, doc2 = self.doc, doc2 and doc2.doc 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.doc
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,
__ctor = function(self, cl)
self.class = cl
assert(self.class)
end,
full_name_get = function(self)
return self.class:full_name_get()
end,
name_get = function(self)
return self.class:name_get()
end,
namespaces_get = function(self)
return self.class:namespaces_get():to_array()
end,
type_get = function(self)
return self.class: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.class:documentation_get())
end,
legacy_prefix_get = function(self)
return self.class:legacy_prefix_get()
end,
eo_prefix_get = function(self)
return self.class:eo_prefix_get()
end,
inherits_get = function(self)
local ret = self._cache_inhc
if ret then
return ret
end
ret = {}
for cl in self.class:inherits_get() do
ret[#ret + 1] = M.Class(cl)
end
self._cache_inhc = ret
return ret
end,
children_get = function(self)
return revh[self:full_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.class:functions_get(ft) do
ret[#ret + 1] = M.Function(fn)
end
return ret
end,
function_get_by_name = function(self, fn, ft)
local fun = self._cache_func
if fun then
return fun
end
fun = M.Function(self.class:function_get_by_name(fn, ft))
self._cache_func = fun
return fun
end,
events_get = function(self)
local ret = {}
for ev in self.class:events_get() do
ret[#ret + 1] = M.Event(ev)
end
return ret
end,
implements_get = function(self)
local ret = {}
for impl in self.class:implements_get() do
ret[#ret + 1] = M.Implement(impl)
end
return ret
end,
c_get_function_name_get = function(self)
return self.class:c_get_function_name_get()
end,
nspaces_get = function(self, root)
return M.Node.nspaces_get(self, self:type_str_get(), root)
end,
is_same = function(self, other)
return self.class == other.class
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
-- FIXME: unit
local v = eolian.class_get_by_name(nil, 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
-- FIXME: unit
local v = eolian.class_get_by_file(nil, 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 eolian.all_classes_get(nil) do
ret[#ret + 1] = M.Class(cl)
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,
__ctor = function(self, fn)
self.func = fn
assert(self.func)
end,
name_get = function(self)
return self.func:name_get()
end,
type_get = function(self)
return self.func:type_get()
end,
type_str_get = function(self)
return func_type_str[self:type_get()]
end,
scope_get = function(self, ft)
return self.func:scope_get(ft)
end,
full_c_name_get = function(self, ft, legacy)
return self.func:full_c_name_get(ft, legacy)
end,
legacy_get = function(self, ft)
return self.func:legacy_get(ft)
end,
implement_get = function(self)
return M.Implement(self.func:implement_get())
end,
is_legacy_only = function(self, ft)
return self.func:is_legacy_only(ft)
end,
is_class = function(self)
return self.func:is_class()
end,
is_beta = function(self)
return self.func:is_beta()
end,
is_constructor = function(self, klass)
return self.func:is_constructor(klass.class)
end,
is_function_pointer = function(self)
return self.func:is_function_pointer()
end,
property_keys_get = function(self, ft)
local ret = {}
for par in self.func: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.func: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.func:parameters_get() do
ret[#ret + 1] = M.Parameter(par)
end
return ret
end,
return_type_get = function(self, ft)
local v = self.func: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.func: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.func:return_documentation_get(ft))
end,
return_is_warn_unused = function(self, ft)
return self.func:return_is_warn_unused(ft)
end,
is_const = function(self)
return self.func: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.func == other.func
end,
id_get = function(self)
return tonumber(ffi.cast("uintptr_t", self.func))
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,
__ctor = function(self, par)
self.param = par
assert(self.param)
end,
direction_get = function(self)
return self.param: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.param:type_get()
if not v then
return nil
end
return M.Type(v)
end,
default_value_get = function(self)
local v = self.param:default_value_get()
if not v then
return nil
end
return M.Expression(v)
end,
name_get = function(self)
return self.param:name_get()
end,
doc_get = function(self)
return M.Doc(self.param:documentation_get())
end,
is_nonull = function(self)
return self.param:is_nonull()
end,
is_nullable = function(self)
return self.param:is_nullable()
end,
is_optional = function(self)
return self.param:is_optional()
end,
is_same = function(self, other)
return self.param == other.param
end
}
M.Event = Node:clone {
__ctor = function(self, ev)
self.event = ev
assert(self.event)
end,
name_get = function(self)
return self.event:name_get()
end,
type_get = function(self)
local v = self.event:type_get()
if not v then
return nil
end
return M.Type(v)
end,
doc_get = function(self)
return M.Doc(self.event:documentation_get())
end,
scope_get = function(self)
return self.event:scope_get()
end,
c_name_get = function(self)
return self.event:c_name_get()
end,
is_beta = function(self)
return self.event:is_beta()
end,
is_hot = function(self)
return self.event:is_hot()
end,
is_restart = function(self)
return self.event: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 {
__ctor = function(self, fl)
self.field = fl
assert(self.field)
end,
name_get = function(self)
return self.field:name_get()
end,
doc_get = function(self)
return M.Doc(self.field:documentation_get())
end,
type_get = function(self)
local v = self.field:type_get()
if not v then
return nil
end
return M.Type(v)
end
}
M.EnumField = Node:clone {
__ctor = function(self, fl)
self.field = fl
assert(self.field)
end,
name_get = function(self)
return self.field:name_get()
end,
c_name_get = function(self)
return self.field:c_name_get()
end,
doc_get = function(self)
return M.Doc(self.field:documentation_get())
end,
value_get = function(self, force)
local v = self.field: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,
__ctor = function(self, tp)
self.type = tp
assert(self.type)
end,
type_get = function(self)
return self.type:type_get()
end,
file_get = function(self)
return self.type:file_get()
end,
base_type_get = function(self)
local v = self.type:base_type_get()
if not v then
return nil
end
return M.Type(v)
end,
next_type_get = function(self)
local v = self.type:next_type_get()
if not v then
return nil
end
return M.Type(v)
end,
typedecl_get = function(self)
local v = self.type:typedecl_get()
if not v then
return nil
end
return M.Typedecl(v)
end,
aliased_base_get = function(self)
local v = self.type:aliased_base_get()
if not v then
return nil
end
return M.Type(v)
end,
class_get = function(self)
-- FIXME: unit
return self.type:class_get(nil)
end,
is_owned = function(self)
return self.type:is_owned()
end,
is_const = function(self)
return self.type:is_const()
end,
is_ptr = function(self)
return self.type:is_ptr()
end,
c_type_get = function(self)
return self.type:c_type_get(eolian.c_type_type.DEFAULT)
end,
name_get = function(self)
return self.type:name_get()
end,
full_name_get = function(self)
return self.type:full_name_get()
end,
namespaces_get = function(self)
return self.type:namespaces_get()
end,
free_func_get = function(self)
return self.type:free_func_get()
end,
-- utils
serialize = function(self)
local tpt = self:type_get()
if tpt == self.UNKNOWN then
error("unknown type: " .. self:full_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:full_name_get() .. "<"
.. table.concat(stypes, ", ") .. ">")
end
return wrap_type_attrs(self, self:full_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,
__ctor = function(self, tp)
self.typedecl = tp
assert(self.typedecl)
end,
type_get = function(self)
return self.typedecl: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.typedecl:struct_fields_get() do
ret[#ret + 1] = M.StructField(fl)
end
return ret
end,
struct_field_get = function(self, name)
local v = self.typedecl: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.typedecl:enum_fields_get() do
ret[#ret + 1] = M.EnumField(fl)
end
return ret
end,
enum_field_get = function(self, name)
local v = self.typedecl:enum_field_get(name)
if not v then
return nil
end
return M.EnumField(v)
end,
enum_legacy_prefix_get = function(self)
return self.typedecl:enum_legacy_prefix_get()
end,
doc_get = function(self)
return M.Doc(self.typedecl:documentation_get())
end,
file_get = function(self)
return self.typedecl:file_get()
end,
base_type_get = function(self)
local v = self.typedecl:base_type_get()
if not v then
return nil
end
return M.Type(v)
end,
aliased_base_get = function(self)
local v = self.typedecl:aliased_base_get()
if not v then
return nil
end
return M.Type(v)
end,
is_extern = function(self)
return self.typedecl:is_extern()
end,
c_type_get = function(self)
-- FIXME: unit
return self.typedecl:c_type_get(nil)
end,
name_get = function(self)
return self.typedecl:name_get()
end,
full_name_get = function(self)
return self.typedecl:full_name_get()
end,
namespaces_get = function(self)
return self.typedecl:namespaces_get():to_array()
end,
free_func_get = function(self)
return self.typedecl:free_func_get()
end,
function_pointer_get = function(self)
local v = self.typedecl: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, self:type_str_get(), root)
end,
-- static getters
all_aliases_get = function()
local ret = {}
-- FIXME: unit
for tp in eolian.typedecl_all_aliases_get(nil) do
ret[#ret + 1] = M.Typedecl(tp)
end
return ret
end,
all_structs_get = function()
local ret = {}
-- FIXME: unit
for tp in eolian.typedecl_all_structs_get(nil) do
ret[#ret + 1] = M.Typedecl(tp)
end
return ret
end,
all_enums_get = function()
local ret = {}
-- FIXME: unit
for tp in eolian.typedecl_all_enums_get(nil) do
ret[#ret + 1] = M.Typedecl(tp)
end
return ret
end,
aliases_by_file_get = function(fn)
local ret = {}
-- FIXME: unit
for tp in eolian.typedecl_aliases_get_by_file(nil, fn) do
ret[#ret + 1] = M.Typedecl(tp)
end
return ret
end,
structs_by_file_get = function(fn)
local ret = {}
-- FIXME: unit
for tp in eolian.typedecl_structs_get_by_file(nil, fn) do
ret[#ret + 1] = M.Typedecl(tp)
end
return ret
end,
enums_by_file_get = function(fn)
local ret = {}
-- FIXME: unit
for tp in eolian.typedecl_enums_get_by_file(nil, fn) do
ret[#ret + 1] = M.Typedecl(tp)
end
return ret
end,
alias_by_name_get = function(tn)
-- FIXME: unit
local v = eolian.typedecl_alias_get_by_name(nil, tn)
if not v then
return nil
end
return M.Typedecl(v)
end,
struct_by_name_get = function(tn)
-- FIXME: unit
local v = eolian.typedecl_struct_get_by_name(nil, tn)
if not v then
return nil
end
return M.Typedecl(v)
end,
enum_by_name_get = function(tn)
-- FIXME: unit
local v = eolian.typedecl_enum_get_by_name(nil, 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:full_name_get())
elseif tpt == self.STRUCT or
tpt == self.STRUCT_OPAQUE then
local buf = { "struct " }
add_typedecl_attrs(self, buf)
buf[#buf + 1] = self:full_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:full_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:full_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:full_name_get())
elseif tpt == self.STRUCT or
tpt == self.STRUCT_OPAQUE then
local buf = { "typedef struct " }
local fulln = self:full_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:full_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:full_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,
__ctor = function(self, var)
self.variable = var
assert(self.variable)
end,
type_get = function(self)
return self.variable: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.variable:documentation_get())
end,
file_get = function(self)
return self.variable:file_get()
end,
base_type_get = function(self)
local v = self.variable:base_type_get()
if not v then
return nil
end
return M.Type(v)
end,
value_get = function(self)
local v = self.variable:value_get()
if not v then
return nil
end
return M.Expression(v)
end,
name_get = function(self)
return self.variable:name_get()
end,
full_name_get = function(self)
return self.variable:full_name_get()
end,
namespaces_get = function(self)
return self.variable:namespaces_get():to_array()
end,
is_extern = function(self)
return self.variable:is_extern()
end,
nspaces_get = function(self, root)
return M.Node.nspaces_get(self, self:type_str_get(), 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:full_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:full_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 = {}
-- FIXME: unit
for v in eolian.variable_all_globals_get(nil) do
ret[#ret + 1] = M.Variable(v)
end
return ret
end,
all_constants_get = function()
local ret = {}
-- FIXME: unit
for v in eolian.variable_all_constants_get(nil) do
ret[#ret + 1] = M.Variable(v)
end
return ret
end,
globals_by_file_get = function(fn)
local ret = {}
-- FIXME: unit
for v in eolian.variable_globals_get_by_file(nil, fn) do
ret[#ret + 1] = M.Variable(v)
end
return ret
end,
constants_by_file_get = function(fn)
local ret = {}
-- FIXME: unit
for v in eolian.variable_constants_get_by_file(nil, fn) do
ret[#ret + 1] = M.Variable(v)
end
return ret
end,
global_by_name_get = function(vn)
-- FIXME: unit
local v = eolian.variable_global_get_by_name(nil, vn)
if not v then
return nil
end
return M.Variable(v)
end,
constant_by_name_get = function(vn)
-- FIXME: unit
local v = eolian.variable_constant_get_by_name(nil, vn)
if not v then
return nil
end
return M.Variable(v)
end
}
M.Expression = Node:clone {
__ctor = function(self, expr)
self.expr = expr
assert(self.expr)
end,
eval_enum = function(self)
-- FIXME: unit
return self.expr:eval(nil, eolian.expression_mask.INT)
end,
eval_type = function(self, tp)
-- FIXME: unit
return self.expr:eval_type(nil, tp.type)
end,
serialize = function(self)
return self.expr:serialize()
end
}
M.Implement = Node:clone {
__ctor = function(self, impl)
self.impl = impl
assert(self.impl)
end,
full_name_get = function(self)
return self.impl:full_name_get()
end,
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.impl:class_get():full_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.impl: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.impl: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.impl:is_auto(ftype)
end,
is_empty = function(self, ftype)
return self.impl:is_empty(ftype)
end,
is_pure_virtual = function(self, ftype)
return self.impl:is_pure_virtual(ftype)
end,
is_prop_get = function(self)
return self.impl:is_prop_get()
end,
is_prop_set = function(self)
return self.impl:is_prop_set()
end,
is_overridden = function(self, cl)
return cl.class ~= self.impl:class_get()
end
}
M.DocTokenizer = Node: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_get = function(self, root)
-- FIXME: unit
local tp, d1, d2 = self.tok:ref_get(nil)
local reft = eolian.doc_ref_type
local ret
if tp == reft.CLASS or tp == reft.FUNC or tp == reft.EVENT then
ret = { class_type_str[d1:type_get()] }
if not ret[1] then
error("unknown class type for class '"
.. d1:full_name_get() .. "'")
end
elseif tp == reft.ALIAS then
ret = { "alias" }
elseif tp == reft.STRUCT or tp == reft.STRUCT_FIELD then
-- TODO: point to field
ret = { "struct" }
elseif tp == reft.ENUM or tp == reft.ENUM_FIELD then
-- TODO: point to field
ret = { "enum" }
elseif tp == reft.VAR then
ret = { "var" }
else
error("invalid reference '" .. self:text_get() .. "'")
end
for tok in d1:full_name_get():gmatch("[^%.]+") do
ret[#ret + 1] = tok:lower()
end
if tp == reft.FUNC 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_scan() then
error("failed scanning system directory")
end
return
end
if not eos:directory_scan(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, FIXME: unit
for cl in eolian.all_classes_get(nil) do
local cln = cl:full_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:full_name_get())
end
end
end
return M