Files
Basalt2/tools/annotationParser.lua
2025-03-04 14:26:27 +01:00

225 lines
7.8 KiB
Lua

local function findClassName(content)
return content:match("%-%-%-@class%s+(%w+)")
end
local function parseProperties(content)
local properties = {}
for line in content:gmatch("[^\r\n]+") do
local name, type, default, desc = line:match("%-%-%-@property%s+(%w+)%s+(%w+)%s+(.-)%s+(.*)")
if name and type then
properties[#properties + 1] = {
name = name,
type = type,
default = default,
description = desc
}
end
end
return properties
end
local function parseCombinedProperty(content)
local combinedProperties = {}
for line in content:gmatch("[^\r\n]+") do
local name, props, desc = line:match("%-%-%-@combinedProperty%s+(%w+)%s+{(.-)%}%s+(.*)")
if name and props then
local propList = {}
for prop in props:gmatch("(%w+)") do
table.insert(propList, prop)
end
combinedProperties[#combinedProperties + 1] = {
name = name,
properties = propList,
description = desc
}
end
end
return combinedProperties
end
local function parseEvents(content)
local events = {}
for line in content:gmatch("[^\r\n]+") do
local name, description = line:match("%-%-%-@event%s+([%w_]+)%s+(.+)")
if name then
local functionName = name:gsub("_(%w)", function(c) return c:upper() end)
functionName = "on" .. functionName:sub(1,1):upper() .. functionName:sub(2)
events[#events + 1] = {
name = name,
functionName = functionName,
description = description
}
end
end
return events
end
local function collectAllClassNames(folder)
local classes = {}
for file in io.popen('ls "' .. folder .. '"'):lines() do
if file:match("%.lua$") then
local f = io.open(folder .. "/" .. file, "r")
if f then
local content = f:read("*a")
f:close()
local className = findClassName(content)
if className and className ~= "BaseFrame" then
table.insert(classes, className)
end
end
end
end
return classes
end
local function generateClassContent(className, properties, combinedProperties, events, allClasses)
if #properties == 0 and #events == 0 and className ~= "Container" then
return nil
end
local content = {}
table.insert(content, string.format("---@class %s", className))
for _, prop in ipairs(properties) do
table.insert(content, string.format("---@field %s %s", prop.name, prop.type))
end
table.insert(content, string.format("local %s = {}", className))
table.insert(content, "")
for _, prop in ipairs(properties) do
table.insert(content, string.format("--- Gets the %s", prop.description))
table.insert(content, string.format("---@generic Element: %s", className))
table.insert(content, "---@param self Element")
table.insert(content, string.format("---@return %s", prop.type))
table.insert(content, string.format("function %s:get%s()",
className,
prop.name:sub(1,1):upper() .. prop.name:sub(2)
))
table.insert(content, string.format(" return self.%s", prop.name))
table.insert(content, "end")
table.insert(content, "")
table.insert(content, string.format("--- Sets the %s", prop.description))
table.insert(content, string.format("---@generic Element: %s", className))
table.insert(content, "---@param self Element")
table.insert(content, string.format("---@param %s %s", prop.name, prop.type))
table.insert(content, "---@return Element")
table.insert(content, string.format("function %s:set%s(%s)",
className,
prop.name:sub(1,1):upper() .. prop.name:sub(2),
prop.name
))
table.insert(content, string.format(" self.%s = %s", prop.name, prop.name))
table.insert(content, " return self")
table.insert(content, "end")
table.insert(content, "")
end
for _, combinedProp in ipairs(combinedProperties)do
table.insert(content, string.format("--- Gets the %s", combinedProp.description))
table.insert(content, string.format("---@generic Element: %s", className))
table.insert(content, "---@param self Element")
table.insert(content, string.format("---@return %s", combinedProp.type))
table.insert(content, string.format("function %s:get%s()",
className,
combinedProp.name:sub(1,1):upper() .. combinedProp.name:sub(2)
))
table.insert(content, string.format(" return self.%s", combinedProp.name))
table.insert(content, "end")
table.insert(content, "")
table.insert(content, string.format("--- Sets the %s", combinedProp.description))
table.insert(content, string.format("---@generic Element: %s", className))
table.insert(content, "---@param self Element")
table.insert(content, string.format("---@param %s %s", combinedProp.name, combinedProp.type))
table.insert(content, "---@return Element")
table.insert(content, string.format("function %s:set%s(%s)",
className,
combinedProp.name:sub(1,1):upper() .. combinedProp.name:sub(2),
combinedProp.name
))
table.insert(content, string.format(" self.%s = %s", combinedProp.name, combinedProp.name))
table.insert(content, " return self")
table.insert(content, "end")
end
for _, event in ipairs(events) do
table.insert(content, string.format([[
--- %s
---@generic Element: %s
---@param self Element
---@param callback function
---@return Element
function %s:%s(callback)
return self
end]], event.description, className, className, event.functionName))
table.insert(content, "")
end
if className == "Container" then
for _, cls in ipairs(allClasses) do
table.insert(content, string.format([[
--- Adds a new %s to the container
---@generic Element: Container
---@param self Element
---@return %s
function Container:add%s()
return self
end
--- Adds a new delayed %s to the container
---@generic Element: Container
---@param self Element
---@return %s
function Container:addDelayed%s()
return self
end]], cls, cls, cls, cls, cls, cls))
table.insert(content, "")
end
end
return table.concat(content, "\n")
end
local function parseFolder(folder, destinationFile)
local allClasses = collectAllClassNames(folder)
local allContent = {}
for file in io.popen('ls "' .. folder .. '"'):lines() do
if file:match("%.lua$") then
local f = io.open(folder .. "/" .. file, "r")
if f then
local content = f:read("*a")
f:close()
local className = findClassName(content)
if className then
local properties = parseProperties(content)
local combinedProperties = parseCombinedProperty(content)
local events = parseEvents(content)
local classContent = generateClassContent(className, properties, combinedProperties, events, allClasses)
if classContent then
table.insert(allContent, classContent)
end
end
end
end
end
local f = io.open(destinationFile, "w")
if f then
f:write(table.concat(allContent, "\n\n"))
f:close()
end
end
local args = {...}
if #args == 2 then
parseFolder(args[1], args[2])
else
print("Usage: annotationParser <sourceFolder> <destinationFile>")
end