From c47b7a196239b9d79a4c4d81f20232888e77de62 Mon Sep 17 00:00:00 2001 From: Aire-One Date: Wed, 12 Oct 2022 20:21:40 +0200 Subject: [PATCH 1/7] feat: add modules for generator and filesystem --- src/awesomewm.d.tl/filesystem/file_writer.tl | 31 +++++++ src/awesomewm.d.tl/filesystem/init.tl | 3 + src/awesomewm.d.tl/generator/init.tl | 88 +------------------ .../generator/teal_type_definitions.tl | 64 ++++++++++++++ 4 files changed, 102 insertions(+), 84 deletions(-) create mode 100644 src/awesomewm.d.tl/filesystem/file_writer.tl create mode 100644 src/awesomewm.d.tl/filesystem/init.tl create mode 100644 src/awesomewm.d.tl/generator/teal_type_definitions.tl diff --git a/src/awesomewm.d.tl/filesystem/file_writer.tl b/src/awesomewm.d.tl/filesystem/file_writer.tl new file mode 100644 index 0000000..17ba061 --- /dev/null +++ b/src/awesomewm.d.tl/filesystem/file_writer.tl @@ -0,0 +1,31 @@ +local file = require "pl.file" +local log = require "logger" +local path = require "pl.path" + +local function write_file(file_content: string, file_path: string): boolean, nil | string + -- Make sure the directory we want to write the file to exists + local directory = path.dirname(file_path) + if not path.isdir(directory) then + path.mkdir(directory) + end + + return file.write(file_path, file_content, false) +end + +local module = {} + +function module.write(file_content: string, file_path: string) + local success, error_message = write_file(file_path, file_content) + + if not success then + log:error { + "generator.write error", + error = error_message, + } + return + end + + log:info { "Successfully wrote file", file = file_path } +end + +return module diff --git a/src/awesomewm.d.tl/filesystem/init.tl b/src/awesomewm.d.tl/filesystem/init.tl new file mode 100644 index 0000000..5a74251 --- /dev/null +++ b/src/awesomewm.d.tl/filesystem/init.tl @@ -0,0 +1,3 @@ +return { + file_writer = require "filesystem.file_writer", +} diff --git a/src/awesomewm.d.tl/generator/init.tl b/src/awesomewm.d.tl/generator/init.tl index d72d20d..a769ed8 100644 --- a/src/awesomewm.d.tl/generator/init.tl +++ b/src/awesomewm.d.tl/generator/init.tl @@ -1,84 +1,4 @@ -local file = require "pl.file" -local template = require "pl.template" -local path = require "pl.path" -local log = require "logger" -local utils = require "utils" -local snippets = require "generator.snippets" - -local tmpl = (function(mod: string): string - local package_path = utils.do_or_fail(path.package_path, mod) - local package_dir = path.dirname(package_path) - return utils.do_or_fail(file.read, package_dir .. "/template.tl.tmpl", false) -end)(...) - -local generator = {} - -local record Generate_Teal_Data_Record - section: string - items: { snippets.Anonymous_Function_Record } -end -function generator.generate_teal(data: { Generate_Teal_Data_Record }): string - -- TODO : add the required modules to the generated code - -- TODO : replace this with a proper way to get the module name (will also probably need the module path) - local record Module_Data_Record - name: string - static_functions: { snippets.Anonymous_Function_Record } - constructors: { snippets.Anonymous_Function_Record } - methods: { string } - properties: { snippets.Anonymous_Function_Record } - signals: { snippets.Anonymous_Function_Record } - end - local module_data: Module_Data_Record = { name = "module_name" } - for _, item in ipairs(data) do - if item.section == "Static functions" then - -- TODO - module_data.static_functions = item.items - elseif item.section == "Constructors" then - -- TODO - module_data.constructors = item.items - elseif item.section == "Object properties" then - -- TODO - module_data.properties = item.items - elseif item.section == "Object methods" then - module_data.methods = {} - -- TODO : add the self parameter - -- TODO : define overrides to use the signal type in connect/emit functions - for _, i in ipairs(item.items) do - table.insert( - module_data.methods, - i.name .. ": " .. snippets.anonymous_function(i) - ) - end - elseif item.section == "Signals" then - module_data.signals = item.items - end - end - - local env = { - ipairs = ipairs, - module = module_data, - } - return utils.do_or_fail(template.substitute, tmpl, env) -end - -function generator.write(file_content:string, file_path: string) - -- Make sure the directory we want to write the file to exists - local directory = path.dirname(file_path) - if not path.isdir(directory) then - path.mkdir(directory) - end - - local success, error_message = file.write(file_path, file_content, false) - - if not success then - log:error { - "generator.write error", - error = error_message, - } - return - end - - log:info { "Successfully wrote file", file = file_path } -end - -return generator +return { + teal_type_definitions = require "generator.teal_type_definitions", + snippets = require "generator.snippets", +} diff --git a/src/awesomewm.d.tl/generator/teal_type_definitions.tl b/src/awesomewm.d.tl/generator/teal_type_definitions.tl new file mode 100644 index 0000000..65c1c81 --- /dev/null +++ b/src/awesomewm.d.tl/generator/teal_type_definitions.tl @@ -0,0 +1,64 @@ +local file = require "pl.file" +local template = require "pl.template" +local path = require "pl.path" +local utils = require "utils" +local snippets = require "generator.snippets" + +local tmpl = (function(mod: string): string + local package_path = utils.do_or_fail(path.package_path, mod) + local package_dir = path.dirname(package_path) + return utils.do_or_fail(file.read, package_dir .. "/template.tl.tmpl", false) +end)(...) + +local module = {} + +local record Generate_Teal_Data_Record + section: string + items: { snippets.Anonymous_Function_Record } +end + +function module.generate_teal(data: { Generate_Teal_Data_Record }): string + -- TODO : add the required modules to the generated code + -- TODO : replace this with a proper way to get the module name (will also probably need the module path) + local record Module_Data_Record + name: string + static_functions: { snippets.Anonymous_Function_Record } + constructors: { snippets.Anonymous_Function_Record } + methods: { string } + properties: { snippets.Anonymous_Function_Record } + signals: { snippets.Anonymous_Function_Record } + end + local module_data: Module_Data_Record = { name = "module_name" } + for _, item in ipairs(data) do + if item.section == "Static functions" then + -- TODO + module_data.static_functions = item.items + elseif item.section == "Constructors" then + -- TODO + module_data.constructors = item.items + elseif item.section == "Object properties" then + -- TODO + module_data.properties = item.items + elseif item.section == "Object methods" then + module_data.methods = {} + -- TODO : add the self parameter + -- TODO : define overrides to use the signal type in connect/emit functions + for _, i in ipairs(item.items) do + table.insert( + module_data.methods, + i.name .. ": " .. snippets.anonymous_function(i) + ) + end + elseif item.section == "Signals" then + module_data.signals = item.items + end + end + + local env = { + ipairs = ipairs, + module = module_data, + } + return utils.do_or_fail(template.substitute, tmpl, env) +end + +return module -- 2.40.1 From e0f61e1699d117ee87a465a2f55bba21c9377281 Mon Sep 17 00:00:00 2001 From: Aire-One Date: Sat, 22 Oct 2022 15:00:28 +0200 Subject: [PATCH 2/7] fix(filesystem): write_file call param position --- src/awesomewm.d.tl/filesystem/file_writer.tl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/awesomewm.d.tl/filesystem/file_writer.tl b/src/awesomewm.d.tl/filesystem/file_writer.tl index 17ba061..e15b8b2 100644 --- a/src/awesomewm.d.tl/filesystem/file_writer.tl +++ b/src/awesomewm.d.tl/filesystem/file_writer.tl @@ -15,7 +15,7 @@ end local module = {} function module.write(file_content: string, file_path: string) - local success, error_message = write_file(file_path, file_content) + local success, error_message = write_file(file_content, file_path) if not success then log:error { -- 2.40.1 From 11d93536b91d92f89de5e512bacc81b627b99695 Mon Sep 17 00:00:00 2001 From: Aire-One Date: Sat, 22 Oct 2022 15:02:47 +0200 Subject: [PATCH 3/7] run: `set_path.tl` build -> src --- set_paths.tl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/set_paths.tl b/set_paths.tl index 5473872..0899575 100644 --- a/set_paths.tl +++ b/set_paths.tl @@ -10,7 +10,7 @@ end package.path = lua_module_paths("lua_modules/share/lua/" .. version) .. lua_module_paths "types" - .. lua_module_paths "build/awesomewm.d.tl" + .. lua_module_paths "src/awesomewm.d.tl" .. package.path package.cpath = "lua_modules/lib/lua/" .. version .. "/?.so;" .. package.cpath -- 2.40.1 From b08528a710e36ac93af7f58fe135637e7fa6384c Mon Sep 17 00:00:00 2001 From: Aire-One Date: Sat, 22 Oct 2022 15:04:21 +0200 Subject: [PATCH 4/7] fix(utils): pcall protect `do_or_fail` --- src/awesomewm.d.tl/utils.tl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/awesomewm.d.tl/utils.tl b/src/awesomewm.d.tl/utils.tl index 768af7f..4cd385c 100644 --- a/src/awesomewm.d.tl/utils.tl +++ b/src/awesomewm.d.tl/utils.tl @@ -58,9 +58,9 @@ end -- At some point, we should probably write a wrapper to make penlight's function work with pcalls. function utils.do_or_fail(func: function(...: any): (T | nil, string), ...: any): T local log = require "logger" - local res, err = func(...) + local ok, res, err = pcall(func, ...) - if not res then + if not (ok and res) then log:error { "do_or_fail failed!", error = err } error(err) end -- 2.40.1 From 2aaf602d6c574d3c3ce6efea87f9fdc91abf3594 Mon Sep 17 00:00:00 2001 From: Aire-One Date: Sat, 22 Oct 2022 15:05:26 +0200 Subject: [PATCH 5/7] fix(entities): Function_Info expose Parameter --- src/awesomewm.d.tl/entities/Function_Info.tl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/awesomewm.d.tl/entities/Function_Info.tl b/src/awesomewm.d.tl/entities/Function_Info.tl index 5f0b802..ff9c151 100644 --- a/src/awesomewm.d.tl/entities/Function_Info.tl +++ b/src/awesomewm.d.tl/entities/Function_Info.tl @@ -1,14 +1,13 @@ local List = require "pl.List" -local record Parameter - name: string - type: string -end - local record Function_Info metamethod __call: function(Function_Info): Function_Info Function_Info: Function_Info + record Parameter + name: string + type: string + end name: string parameters: List -- 2.40.1 From d8cb5918a740f9efabd1774b48a871422714afe6 Mon Sep 17 00:00:00 2001 From: Aire-One Date: Sat, 22 Oct 2022 15:06:24 +0200 Subject: [PATCH 6/7] fix(entities): Function_Info initialize Lists --- src/awesomewm.d.tl/entities/Function_Info.tl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/awesomewm.d.tl/entities/Function_Info.tl b/src/awesomewm.d.tl/entities/Function_Info.tl index ff9c151..4b9c1e1 100644 --- a/src/awesomewm.d.tl/entities/Function_Info.tl +++ b/src/awesomewm.d.tl/entities/Function_Info.tl @@ -19,7 +19,11 @@ end local __Function_Info: metatable = { __call = function(_self: Function_Info): Function_Info - return {} + return { + name = "", + parameters = List(), + return_types = List(), + } end, } -- 2.40.1 From ddf9858aa081099b4fb5034383eaa63c916e5139 Mon Sep 17 00:00:00 2001 From: Aire-One Date: Sat, 22 Oct 2022 15:08:33 +0200 Subject: [PATCH 7/7] =?UTF-8?q?feat(generator):=20re-implement=20generator?= =?UTF-8?q?=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/awesomewm.d.tl/generator/snippets.tl | 90 ++++++++----------- .../generator/teal_type_definitions.tl | 82 +++++++---------- src/awesomewm.d.tl/generator/template.tl.tmpl | 20 ----- src/awesomewm.d.tl/init.tl | 14 +-- 4 files changed, 75 insertions(+), 131 deletions(-) delete mode 100644 src/awesomewm.d.tl/generator/template.tl.tmpl diff --git a/src/awesomewm.d.tl/generator/snippets.tl b/src/awesomewm.d.tl/generator/snippets.tl index 78ad3dc..ccd4084 100644 --- a/src/awesomewm.d.tl/generator/snippets.tl +++ b/src/awesomewm.d.tl/generator/snippets.tl @@ -1,66 +1,50 @@ +local Function_Info = require "entities.Function_Info" +local List = require "pl.List" local utils = require "utils" local template = require "pl.template" --- Refactor scraper code to use pl.List objects -local function join(arr: { string }, delim: string): string +local snippets = {} + +function snippets.indent(str: string, level: number): string + level = level or 1 local ret = "" - for i, t in ipairs(arr) do - ret = ret .. t - if i < #arr then - ret = ret .. delim - end + for line in str:gmatch("[^\n]+") do + ret = ret .. string.rep(" ", level * 3) .. line .. "\n" end return ret end -local record Anonymous_Function_Parameter_Record - name: string - type: { string } -end - -local record Anonymous_Function_Record - name: string - parameters: { Anonymous_Function_Parameter_Record } - returns: { string } -end - -local snippets = { - Anonymous_Function_Parameter_Record = Anonymous_Function_Parameter_Record, - Anonymous_Function_Record = Anonymous_Function_Record, -} - -function snippets.types_list(types: { string }): string - return join(types, ", ") -end - -function snippets.anonymous_function(item: Anonymous_Function_Record): string - local parameters_string = "" - if item.parameters then - for i, param in ipairs(item.parameters) do - parameters_string = parameters_string .. param.name - local types = "any" - if param.type then - types = snippets.types_list(param.type) - end - parameters_string = parameters_string .. ": " .. types - if i < #item.parameters then - parameters_string = parameters_string .. ", " - end - end - end - - local returns_string = "" - if item.returns then - returns_string = snippets.types_list(item.returns) - end - +function snippets.render_typed_variable(name: string, types: List): string local tmpl = - [[function($(parameters_string))$(#returns_string > 0 and (": " .. returns_string))]] + [[$(name): $(types)]] - return utils.do_or_fail(template.substitute, tmpl, { - parameters_string = parameters_string, - returns_string = returns_string, - }) + local tmpl_args = { + name = name, + types = types:concat(", "), + } + + return utils.do_or_fail(template.substitute, tmpl, tmpl_args) +end + +function snippets.render_anonymous_function_signature(item: Function_Info.Function_Info): string + local tmpl = + [[$(function_name): function($(function_parameter))$(#function_return > 0 and (": " .. function_return))]] + + local tmpl_args = { + function_name = item.name, + function_parameter = item.parameters:map(function(param: Function_Info.Parameter): string + return snippets.render_typed_variable(param.name, List({param.type})) -- TODO : add support for multiple types + end):concat(", "), + function_return = item.return_types:concat(", "), + } + + return utils.do_or_fail(template.substitute, tmpl, tmpl_args) +end + +function snippets.render_record_functions(items: List): string + return items:map(function(item: Function_Info.Function_Info): string + return snippets.render_anonymous_function_signature(item) + end):concat("\n") end return snippets diff --git a/src/awesomewm.d.tl/generator/teal_type_definitions.tl b/src/awesomewm.d.tl/generator/teal_type_definitions.tl index 65c1c81..14568b1 100644 --- a/src/awesomewm.d.tl/generator/teal_type_definitions.tl +++ b/src/awesomewm.d.tl/generator/teal_type_definitions.tl @@ -1,64 +1,42 @@ -local file = require "pl.file" +local Module_Doc = require "entities.Module_Doc" local template = require "pl.template" -local path = require "pl.path" local utils = require "utils" local snippets = require "generator.snippets" -local tmpl = (function(mod: string): string - local package_path = utils.do_or_fail(path.package_path, mod) - local package_dir = path.dirname(package_path) - return utils.do_or_fail(file.read, package_dir .. "/template.tl.tmpl", false) -end)(...) +-- The long therm goal is to have so many `snippets.render_*` functions that +-- we can render the whole file with the smallest template possible. +local tmpl = [[ +-- Auto generated file (Do not manually edit this file!) + +# if module.signals then +local enum signals +# for _, signal in ipairs(module.signals) do + "$(signal.name)" +# end +end + +# end -- /signals +local record $(mod_name) +# if #module.methods then + -- Object methods +$(snippets.indent(snippets.render_record_functions(module.methods))) +# end -- /methods +end + +return $(mod_name) +]] local module = {} -local record Generate_Teal_Data_Record - section: string - items: { snippets.Anonymous_Function_Record } -end - -function module.generate_teal(data: { Generate_Teal_Data_Record }): string - -- TODO : add the required modules to the generated code - -- TODO : replace this with a proper way to get the module name (will also probably need the module path) - local record Module_Data_Record - name: string - static_functions: { snippets.Anonymous_Function_Record } - constructors: { snippets.Anonymous_Function_Record } - methods: { string } - properties: { snippets.Anonymous_Function_Record } - signals: { snippets.Anonymous_Function_Record } - end - local module_data: Module_Data_Record = { name = "module_name" } - for _, item in ipairs(data) do - if item.section == "Static functions" then - -- TODO - module_data.static_functions = item.items - elseif item.section == "Constructors" then - -- TODO - module_data.constructors = item.items - elseif item.section == "Object properties" then - -- TODO - module_data.properties = item.items - elseif item.section == "Object methods" then - module_data.methods = {} - -- TODO : add the self parameter - -- TODO : define overrides to use the signal type in connect/emit functions - for _, i in ipairs(item.items) do - table.insert( - module_data.methods, - i.name .. ": " .. snippets.anonymous_function(i) - ) - end - elseif item.section == "Signals" then - module_data.signals = item.items - end - end - - local env = { +function module.generate_teal(mod: string, data: Module_Doc.Module_Doc): string + local tmpl_args = { ipairs = ipairs, - module = module_data, + mod_name = mod, + module = data, + snippets = snippets, } - return utils.do_or_fail(template.substitute, tmpl, env) + return utils.do_or_fail(template.substitute, tmpl, tmpl_args) + end return module diff --git a/src/awesomewm.d.tl/generator/template.tl.tmpl b/src/awesomewm.d.tl/generator/template.tl.tmpl deleted file mode 100644 index 84df6b4..0000000 --- a/src/awesomewm.d.tl/generator/template.tl.tmpl +++ /dev/null @@ -1,20 +0,0 @@ --- Auto generated file (Do not manually edit this file!) - -# if module.signals then -local enum signals -# for _, signal in ipairs(module.signals) do - "$(signal.name)" -# end -end - -# end -- /signals -local record $(module.name) -# if module.methods then - -- Object methods -# for _, method in ipairs(module.methods) do - $(method) -# end -# end -- /methods -end - -return $(module.name) diff --git a/src/awesomewm.d.tl/init.tl b/src/awesomewm.d.tl/init.tl index 686c1ee..be63003 100644 --- a/src/awesomewm.d.tl/init.tl +++ b/src/awesomewm.d.tl/init.tl @@ -1,9 +1,10 @@ local crawler = require "crawler" +local filesystem = require "filesystem" local inspect = require "inspect" local log = require "logger" local properties = require "properties" local scraper = require "scraper" --- local generator = require "generator" +local generator = require "generator" log:info( inspect { message = "Start extraction", base_url = properties.base_url } @@ -28,12 +29,13 @@ log:info(inspect { modules_found = #module_infos }) local html = crawler.fetch(properties.base_url .. "/widgets/wibox.widget.textbox.html") local module_doc = scraper.module_doc.get_doc_from_page(html) -log:info(inspect { module_doc = module_doc }) +-- log:info(inspect { module_doc = module_doc }) -- local items = scraper.get_doc_from_page(page) -- log:info(inspect { items }) --- generator.write( --- generator.generate_teal(items), --- properties.out_directory .. "/test.tl" --- ) +local mod = "textbox" +filesystem.file_writer.write( + generator.teal_type_definitions.generate_teal(mod, module_doc), + properties.out_directory .. "/" .. mod .. ".d.tl" +) -- 2.40.1