WIP: awesomerc.tl
should work #85
|
@ -211,4 +211,60 @@ describe("Teal type definition Printer", function()
|
|||
|
||||
return Timer
|
||||
]]))
|
||||
|
||||
it("should print a module with descendants", gen(
|
||||
{
|
||||
children = {
|
||||
{
|
||||
children = {},
|
||||
name = "Signal",
|
||||
token = "enum",
|
||||
},
|
||||
{
|
||||
parameters = {},
|
||||
return_types = {},
|
||||
name = "draw_to_cairo_context",
|
||||
token = "function",
|
||||
},
|
||||
{
|
||||
children = {
|
||||
{
|
||||
children = {},
|
||||
name = "Signal",
|
||||
token = "enum",
|
||||
},
|
||||
{
|
||||
parameters = {},
|
||||
return_types = {},
|
||||
name = "imagebox",
|
||||
metamethod = "__call",
|
||||
token = "metamethod",
|
||||
}
|
||||
},
|
||||
name = "Imagebox",
|
||||
module_path = "wibox.widget.imagebox",
|
||||
dependencies = {},
|
||||
global = false,
|
||||
token = "module",
|
||||
}
|
||||
},
|
||||
name = "Widget",
|
||||
module_path = "wibox.widget",
|
||||
dependencies = {},
|
||||
global = false,
|
||||
token = "module",
|
||||
},
|
||||
[[
|
||||
-- This file was auto-generated.
|
||||
local type Imagebox = require("wibox.widget.imagebox")
|
||||
|
||||
local record Widget
|
||||
enum Signal
|
||||
end
|
||||
draw_to_cairo_context: function()
|
||||
imagebox: Imagebox
|
||||
end
|
||||
|
||||
return Widget
|
||||
]]))
|
||||
end)
|
||||
|
|
|
@ -70,6 +70,40 @@ local function create_node(token: Node.Token, name: string, ...: any): Node
|
|||
return node
|
||||
end
|
||||
|
||||
-- TODO : be less permissive and allow to merge only module ?
|
||||
local function merge_nodes(node: Node, other: Node): Node
|
||||
if node.token ~= other.token then
|
||||
error("Cannot merge nodes of different types")
|
||||
end
|
||||
if node.name ~= other.name then
|
||||
error("Cannot merge nodes of different names")
|
||||
end
|
||||
if node.module_path ~= other.module_path then
|
||||
error("Cannot merge nodes of different module paths")
|
||||
end
|
||||
|
||||
-- Basic fields should be overwritten by the new values
|
||||
-- Teal wants us to use enums value to index a record, but it fails to understand my enum with
|
||||
-- any other key from Node, so I'm forcing it to cooperate for now
|
||||
local enum NodeField
|
||||
"types"
|
||||
end
|
||||
for _, field in ipairs({"global", "types", "parameters", "return_types", "metamethod"}) do
|
||||
local f = field as NodeField
|
||||
node[f] = other[f]
|
||||
end
|
||||
|
||||
-- Children should be merged
|
||||
for _, child in ipairs(other.children) do
|
||||
table.insert(node.children, child)
|
||||
end
|
||||
|
||||
-- Dependencies should be merged
|
||||
for name, path in pairs(other.dependencies) do
|
||||
node.dependencies[name] = path
|
||||
end
|
||||
end
|
||||
|
||||
local function iter_children(node: Node): function(): integer, Node
|
||||
if node.children == nil then
|
||||
return function(): integer, Node end
|
||||
|
@ -77,6 +111,7 @@ local function iter_children(node: Node): function(): integer, Node
|
|||
return ipairs(node.children)
|
||||
end
|
||||
|
||||
-- TODO : deprecate in favor of the iterator definition ?
|
||||
local function in_order_visitor(node: Node, visitor: function(Node))
|
||||
for _, child in iter_children(node) do
|
||||
in_order_visitor(child, visitor)
|
||||
|
@ -84,8 +119,28 @@ local function in_order_visitor(node: Node, visitor: function(Node))
|
|||
visitor(node)
|
||||
end
|
||||
|
||||
local function iter_in_order(node: Node): function(): Node
|
||||
local stack: { Node } = {}
|
||||
|
||||
local function traverse(n: Node)
|
||||
for _, child in iter_children(n) do
|
||||
traverse(child)
|
||||
end
|
||||
table.insert(stack, n)
|
||||
end
|
||||
traverse(node)
|
||||
|
||||
local i = 0
|
||||
return function(): Node
|
||||
i = i + 1
|
||||
return stack[i]
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
create_node = create_node,
|
||||
merge_nodes = merge_nodes,
|
||||
iter_children = iter_children,
|
||||
in_order_visitor = in_order_visitor,
|
||||
iter_in_order = iter_in_order,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
local ast = require("awesomewmdtl.ast")
|
||||
local type Dag = require("awesomewmdtl.types.Dag")
|
||||
local type Node = require("awesomewmdtl.types.Node")
|
||||
local utils <const> = require("awesomewmdtl.utils")
|
||||
|
@ -10,26 +11,98 @@ local function new(): Dag
|
|||
return dag
|
||||
end
|
||||
|
||||
local function push_module(dag: Dag, module_path: string, root_node: Node)
|
||||
dag.modules[module_path] = root_node
|
||||
local function push_module(dag: Dag, module_path: string, node: Node)
|
||||
local parent: Node = { children = dag.modules }
|
||||
local current_path = ""
|
||||
for breadcrumb in module_path:gmatch("([^%.]+)") do
|
||||
current_path = current_path == "" and breadcrumb or current_path .. "." .. breadcrumb
|
||||
|
||||
if current_path == node.module_path then
|
||||
local found = utils.find(parent.children, function(n: Node): boolean
|
||||
return n.module_path == node.module_path
|
||||
end)
|
||||
if found then
|
||||
ast.merge_nodes(found, node)
|
||||
else
|
||||
table.insert(parent.children, node)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local current: Node = nil
|
||||
for _, n in ipairs(parent.children) do
|
||||
if n.module_path == current_path then
|
||||
current = n
|
||||
break
|
||||
end
|
||||
end
|
||||
if current == nil then
|
||||
current = {
|
||||
children = {},
|
||||
name = utils.capitalize(breadcrumb),
|
||||
module_path = current_path,
|
||||
dependencies = {},
|
||||
token = "module",
|
||||
}
|
||||
push_module(dag, current_path, current)
|
||||
end
|
||||
|
||||
parent = current
|
||||
end
|
||||
end
|
||||
|
||||
local function push_global_nodes(dag: Dag, nodes: { Node })
|
||||
utils.spread(dag.global_nodes, nodes)
|
||||
end
|
||||
|
||||
local function iter_modules(dag: Dag): (function(): string, Node)
|
||||
return pairs(dag.modules)
|
||||
local function iter_modules(dag: Dag): (function(): Node)
|
||||
local stack: { Node } = {}
|
||||
for _, root in ipairs(dag.modules) do
|
||||
for node in ast.iter_in_order(root) do
|
||||
if node.token == "module" then
|
||||
table.insert(stack, node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local i = 0
|
||||
return function(): Node
|
||||
i = i + 1
|
||||
return stack[i]
|
||||
end
|
||||
end
|
||||
|
||||
local function iter_global_nodes(dag: Dag): (function(): integer, Node)
|
||||
return pairs(dag.global_nodes)
|
||||
end
|
||||
|
||||
local function find_module(dag: Dag, module_path: string): Node
|
||||
local current_path = ""
|
||||
for breadcrumb in module_path:gmatch("([^%.]+)") do
|
||||
current_path = current_path == "" and breadcrumb or current_path .. "." .. breadcrumb
|
||||
local current: Node = nil
|
||||
|
||||
if current_path == module_path then
|
||||
return current
|
||||
end
|
||||
|
||||
for _, n in ipairs(dag.modules) do
|
||||
if n.module_path == current_path then
|
||||
current = n
|
||||
break
|
||||
end
|
||||
end
|
||||
if current == nil then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
new = new,
|
||||
push_module = push_module,
|
||||
push_global_nodes = push_global_nodes,
|
||||
iter_modules = iter_modules,
|
||||
iter_global_nodes = iter_global_nodes,
|
||||
find_module = find_module,
|
||||
}
|
||||
|
|
|
@ -9,14 +9,12 @@ local filesystem <const> = require("awesomewmdtl.filesystem")
|
|||
local printer <const> = require("awesomewmdtl.printer")
|
||||
local List <const> = require("pl.List")
|
||||
local logger <const> = require("awesomewmdtl.logger")
|
||||
local Map <const> = require("pl.Map")
|
||||
local Module_Info <const> = require("awesomewmdtl.entity.Module_Info")
|
||||
local module_dependencies <const> = require("awesomewmdtl.visitors.module_dependencies")
|
||||
local type Node = require("awesomewmdtl.types.Node")
|
||||
local node_fixer <const> = require("awesomewmdtl.visitors.node_fixer")
|
||||
local property <const> = require("awesomewmdtl.property")
|
||||
local scraper <const> = require("awesomewmdtl.scraper")
|
||||
local stringx <const> = require("pl.stringx")
|
||||
local type_mapping <const> = require("awesomewmdtl.visitors.type_mapping")
|
||||
local utils <const> = require("awesomewmdtl.utils")
|
||||
|
||||
|
@ -46,53 +44,6 @@ local function module_lists(
|
|||
return all_module_infos, module_infos, global_module_infos
|
||||
end
|
||||
|
||||
-- The module's children list produced can contain duplicates.
|
||||
-- We ignore them for now because they are dismissed when building a Map for the printer.
|
||||
local function modules_tree(modules: List<Module_Info.Module_Info>): Map<string, List<string>>
|
||||
local tree: Map<string, List<string>> = Map()
|
||||
for module in modules:iter() do
|
||||
local parent = module.name:gmatch("(.*)%.(.*)$")()
|
||||
if parent then
|
||||
local ancestors = stringx.split(parent, ".")
|
||||
for i = 1, #ancestors - 1 do
|
||||
local ancestor = ancestors:slice(1, i):join(".")
|
||||
if not tree:get(ancestor) then
|
||||
tree:set(ancestor, List())
|
||||
end
|
||||
if not tree:get(ancestor):contains(parent) then
|
||||
tree:get(ancestor):append(parent)
|
||||
end
|
||||
end
|
||||
local parent_node = tree:get(parent)
|
||||
if not parent_node then
|
||||
tree:set(parent, List())
|
||||
parent_node = tree:get(parent)
|
||||
end
|
||||
parent_node:append(module.name)
|
||||
end
|
||||
end
|
||||
return tree
|
||||
end
|
||||
|
||||
--- TODO : rewrite this to use the DAG
|
||||
local function do_module_init_definition(
|
||||
module_infos: List<Module_Info.Module_Info>
|
||||
)
|
||||
local tree = modules_tree(module_infos)
|
||||
for module, children in tree:iter() do
|
||||
-- TODO : this map should be coupled with the all_module_infos list
|
||||
local requires: Map<string, string> = Map()
|
||||
for child in children:iter() do
|
||||
local name = child:gmatch(".*%.(.*)$")()
|
||||
requires:set(name, child)
|
||||
end
|
||||
filesystem.file_writer.write(
|
||||
printer.module_init_definition.generate_teal(requires),
|
||||
property.out_directory .. "/" .. stringx.split(module, "."):slice(1, -1):join("/") .. "/init.d.tl"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
--- TODO : rewrite the module_info thingy
|
||||
local all_module_infos, module_infos, global_module_infos = module_lists(
|
||||
property.base_url .. property.index_uri,
|
||||
|
@ -130,7 +81,7 @@ for module in module_infos:iter() do
|
|||
end
|
||||
|
||||
-- Run the visitors
|
||||
for _,root in dag.iter_modules(module_dag) do
|
||||
for root in dag.iter_modules(module_dag) do
|
||||
ast.in_order_visitor(root, function(node: Node)
|
||||
node_fixer.visit(node, root)
|
||||
end)
|
||||
|
@ -172,17 +123,17 @@ end
|
|||
-- Write the DAG to a file
|
||||
-- local inspect = require("inspect")
|
||||
-- filesystem.file_writer.write(
|
||||
-- inspect(module_dag, { newline = "\n", indent = " ", depth = 2 }),
|
||||
-- inspect(module_dag, { newline = "\n", indent = " " }),
|
||||
-- "generated_dag.lua"
|
||||
-- )
|
||||
|
||||
log:info("Preprocessing finished")
|
||||
|
||||
-- Write modules types definitions to files
|
||||
for module_path, root in dag.iter_modules(module_dag) do
|
||||
for root in dag.iter_modules(module_dag) do
|
||||
filesystem.file_writer.write(
|
||||
printer.teal_type_definition.printer(root),
|
||||
property.out_directory .. "/" .. module_path:gsub("%.", "/") .. ".d.tl"
|
||||
property.out_directory .. "/" .. root.module_path:gsub("%.", "/") .. ".d.tl"
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -193,5 +144,4 @@ filesystem.file_writer.write(
|
|||
property.out_directory .. "/global_env_def.d.tl"
|
||||
)
|
||||
|
||||
do_module_init_definition(module_infos)
|
||||
log:info("Module init files generated")
|
||||
|
|
|
@ -6,6 +6,25 @@ local utils = require("awesomewmdtl.utils")
|
|||
|
||||
local log = logger.log("scraper")
|
||||
|
||||
local GEN_TEXT <const> = "-- This file was auto-generated.\n"
|
||||
|
||||
local function get_package_descendants(node: Node): { Node }
|
||||
return utils.filter(
|
||||
node.children,
|
||||
function(child: Node): boolean
|
||||
return child.token == "module"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
local function nodes_to_string_map(nodes: { Node }): { string : string }
|
||||
local map <const>: { string : string } = {}
|
||||
for _, node in ipairs(nodes) do
|
||||
map[node.name] = node.module_path
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
local function render_types(types: { string }, separator: string, with_colon_prefix: boolean): string
|
||||
if not types or #types == 0 then
|
||||
return ""
|
||||
|
@ -57,35 +76,62 @@ local function render_require(dependencies: { string : string }): string
|
|||
return generated
|
||||
end
|
||||
|
||||
local function render_descendant(descendants: { Node }): string
|
||||
local generated = ""
|
||||
for _, descendant in ipairs(descendants) do
|
||||
generated = generated .. string.format("%s: %s\n", utils.lowercase(descendant.name), descendant.name)
|
||||
end
|
||||
return generated
|
||||
end
|
||||
|
||||
local record Node_Printer_Function
|
||||
on_node: function(node: Node, indent_level: integer): string, integer
|
||||
before_node: nil | function(node: Node, indent_level: integer): string, integer
|
||||
after_node: nil | function(node: Node, indent_level: integer): string, integer
|
||||
on_node: function(node: Node, indent_level: integer, parent_token: Node.Token): string, integer
|
||||
-- optional functions
|
||||
before_node: function(node: Node, indent_level: integer, parent_token: Node.Token): string, integer
|
||||
after_node: function(node: Node, indent_level: integer, parent_token: Node.Token): string, integer
|
||||
end
|
||||
|
||||
-- pre-declare functions to prevent forward reference errors
|
||||
local print_teal: function(node: Node, indent_level: integer | nil): string
|
||||
local print_teal: function(node: Node, indent_level: integer, parent_node: Node): string
|
||||
local print_children: function(node: Node): string
|
||||
|
||||
local node_printer <total>: { Node.Token : Node_Printer_Function } = {
|
||||
["module"] = {
|
||||
before_node = function(node: Node, indent_level: integer): string, integer
|
||||
before_node = function(node: Node, indent_level: integer, parent_token: Node.Token): string, integer
|
||||
if "module" == parent_token then
|
||||
return "", indent_level
|
||||
end
|
||||
if node.global then
|
||||
return render_code(
|
||||
"-- This file was auto-generated.\n",
|
||||
GEN_TEXT,
|
||||
indent_level), indent_level
|
||||
end
|
||||
return render_code(
|
||||
string.format(
|
||||
"-- This file was auto-generated.\n%s\nlocal record %s",
|
||||
render_require(node.dependencies), -- last require statement will have a newline
|
||||
"%s%s\nlocal record %s",
|
||||
GEN_TEXT,
|
||||
render_require(
|
||||
utils.merge_map(
|
||||
node.dependencies,
|
||||
utils.pipe(
|
||||
get_package_descendants,
|
||||
nodes_to_string_map)(node) as { string : string } -- pipe needs to be promoted
|
||||
)), -- last require statement will have a newline
|
||||
node.name),
|
||||
indent_level), indent_level + 1
|
||||
end,
|
||||
on_node = function(node: Node, indent_level: integer): string, integer
|
||||
on_node = function(node: Node, indent_level: integer, parent_token: Node.Token): string, integer
|
||||
if "module" == parent_token then
|
||||
return render_code(
|
||||
render_descendant({ node }),
|
||||
indent_level), indent_level
|
||||
end
|
||||
return render_code(print_children(node), indent_level), indent_level
|
||||
end,
|
||||
after_node = function(node: Node, indent_level: integer): string, integer
|
||||
after_node = function(node: Node, indent_level: integer, parent_token: Node.Token): string, integer
|
||||
if "module" == parent_token then
|
||||
return "", indent_level
|
||||
end
|
||||
if node.global then
|
||||
return "", indent_level
|
||||
end
|
||||
|
@ -188,19 +234,22 @@ local node_printer <total>: { Node.Token : Node_Printer_Function } = {
|
|||
},
|
||||
}
|
||||
|
||||
function print_teal(node: Node, indent_level: integer | nil): string
|
||||
function print_teal(node: Node,
|
||||
-- optional parameters
|
||||
indent_level: integer, parent_node: Node): string
|
||||
indent_level = indent_level or 0
|
||||
local parent_node_token = parent_node and parent_node.token
|
||||
local printer = node_printer[node.token]
|
||||
local generated = ""
|
||||
local full_generated = ""
|
||||
if printer.before_node then
|
||||
generated, indent_level = (printer.before_node as function(Node, integer): string, integer)(node, indent_level)
|
||||
generated, indent_level = printer.before_node(node, indent_level, parent_node_token)
|
||||
full_generated = generated
|
||||
end
|
||||
generated, indent_level = printer.on_node(node, indent_level)
|
||||
generated, indent_level = printer.on_node(node, indent_level, parent_node_token)
|
||||
full_generated = full_generated .. generated
|
||||
if printer.after_node then
|
||||
generated, indent_level = (printer.after_node as function(Node, integer): string, integer)(node, indent_level)
|
||||
generated, indent_level = printer.after_node(node, indent_level, parent_node_token)
|
||||
full_generated = full_generated .. generated
|
||||
end
|
||||
return full_generated
|
||||
|
@ -209,7 +258,7 @@ end
|
|||
function print_children(node: Node): string
|
||||
local generated = ""
|
||||
for _, child in ast.iter_children(node) do
|
||||
generated = generated .. print_teal(child)
|
||||
generated = generated .. print_teal(child, 0, node)
|
||||
end
|
||||
return generated
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
local type Node = require("awesomewmdtl.types.Node")
|
||||
|
||||
local record Dag
|
||||
modules: { string : Node } -- module_path (AKA "full name" `package.module.name`) -> root_node (token = "module")
|
||||
modules: { Node } -- list of root modules
|
||||
global_nodes: { Node }
|
||||
end
|
||||
|
||||
|
|
|
@ -13,6 +13,16 @@ function utils.has_item(t: table, item: any): any
|
|||
return nil
|
||||
end
|
||||
|
||||
function utils.find<T>(list: { T }, predicate: function(value: T, position: integer): boolean): T, integer
|
||||
for position, value in ipairs(list) do
|
||||
if predicate(value, position) then
|
||||
return value, position
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function utils.filter<T>(list: { T }, predicate: function(value: T, position: integer): boolean): { T }
|
||||
local filtered: { T } = {}
|
||||
|
||||
|
@ -101,4 +111,27 @@ function utils.pairsByKeys<Key, Value>(list: { Key : Value }, comp: function(Key
|
|||
end
|
||||
end
|
||||
|
||||
-- We expect the user to type at the call site
|
||||
function utils.pipe(...: function): function(...: any): any...
|
||||
local funcs = { ... }
|
||||
return function(...: any): any...
|
||||
local res = ...
|
||||
for _, f in ipairs(funcs) do
|
||||
res = f(res)
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
function utils.merge_map<T, U>(t1: { T : U }, t2: { T : U }): { T : U }
|
||||
local merged: { T : U } = {}
|
||||
for k, v in pairs(t1) do
|
||||
merged[k] = v
|
||||
end
|
||||
for k, v in pairs(t2) do
|
||||
merged[k] = v
|
||||
end
|
||||
return merged
|
||||
end
|
||||
|
||||
return utils
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local type Dag = require("awesomewmdtl.types.Dag")
|
||||
local dag = require("awesomewmdtl.dag")
|
||||
local type Node = require("awesomewmdtl.types.Node")
|
||||
local utils <const> = require("awesomewmdtl.utils")
|
||||
|
||||
|
@ -86,7 +87,7 @@ function Module_Dependencies.visit(node: Node, mod: Node, d: Dag)
|
|||
goto continue
|
||||
end
|
||||
|
||||
local dependency = d.modules[type_name] or d.modules[utils.lowercase(type_name)] or capi_class[type_name]
|
||||
local dependency = dag.find_module(d, type_name) or dag.find_module(d, utils.lowercase(type_name)) or capi_class[type_name]
|
||||
if dependency then
|
||||
if dependency.name ~= mod.name then
|
||||
mod.dependencies[dependency.name] = dependency.module_path
|
||||
|
|
Loading…
Reference in New Issue