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 diff --git a/src/awesomewm.d.tl/entities/Function_Info.tl b/src/awesomewm.d.tl/entities/Function_Info.tl index 5f0b802..4b9c1e1 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 @@ -20,7 +19,11 @@ end local __Function_Info: metatable = { __call = function(_self: Function_Info): Function_Info - return {} + return { + name = "", + parameters = List(), + return_types = List(), + } end, } 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..e15b8b2 --- /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_content, file_path) + + 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/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 new file mode 100644 index 0000000..14568b1 --- /dev/null +++ b/src/awesomewm.d.tl/generator/teal_type_definitions.tl @@ -0,0 +1,42 @@ +local Module_Doc = require "entities.Module_Doc" +local template = require "pl.template" +local utils = require "utils" +local snippets = require "generator.snippets" + +-- 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 = {} + +function module.generate_teal(mod: string, data: Module_Doc.Module_Doc): string + local tmpl_args = { + ipairs = ipairs, + mod_name = mod, + module = data, + snippets = snippets, + } + 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" +) 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