Files
Ronald Caesar 986d1e78e1 [38] tools/cdoc/parser: extract file level documentation
Introduces parse_file_level_docs() which extracts file level
documentation prefixed with //!.
2026-08-07 02:23:24 -04:00

144 lines
3.6 KiB
Lua

local log = require("log")
local M = {}
local SECTIONS = {
"Safety", "Errors", "Examples", "Returns", "Warning", "Ownership", "Platform Support",
}
local section_set = {}
for _, section in ipairs(SECTIONS) do
section_set[section] = true
end
--- Strip comment prefixes (///, //!, /**, *, */) from raw comment text.
--- Returns clean lines as a table.
local function strip_comment_markers(raw)
if not raw or raw == "" then
return {}
end
local lines = {}
for line in raw:gmatch("[^\r\n]+") do
local cleaned = line
-- Strip leading whitespace
cleaned = cleaned:match("^%s*(.*)$") or ""
-- Strip /// or //!
if cleaned:sub(1, 3) == "///" or cleaned:sub(1, 3) == "//!" then
cleaned = cleaned:sub(4)
-- Strip /** or /*!
elseif cleaned:sub(1, 3) == "/**" or cleaned:sub(1, 3) == "/*!" then
cleaned = cleaned:sub(4)
-- Strip */
elseif cleaned:sub(1, 2) == "*/" then
goto continue
-- Strip leading *
elseif cleaned:sub(1, 1) == "*" then
cleaned = cleaned:sub(2)
end
-- Strip one leading space after marker
if cleaned:sub(1, 1) == " " then
cleaned = cleaned:sub(2)
end
lines[#lines + 1] = cleaned
::continue::
end
return lines
end
--- Extract [`SYMBOL`] links from text. Returns list of symbol names.
local function extract_links(text)
local links = {}
for symbol in text:gmatch("%[`([^`]+)`%]") do
table.insert(links, symbol)
end
return links
end
--- Parse a raw doc comment string into a structured doc table.
--- Returns:
--- {
--- summary = string, -- text before first # Section
--- sections = { -- ordered list of { name, content }
--- { name = "Safety", content = "..." },
--- },
--- links = { "SYM1", ... }, -- all [`SYM`] references found
--- raw = string, -- original text
--- }
function M.parse(raw)
if not raw or raw == "" then
return nil
end
log.trace("parsing comment (%d chars)", #raw)
local lines = strip_comment_markers(raw)
if #lines == 0 then
return nil
end
local doc = {
summary = "",
sections = {},
links = {},
raw = raw,
}
local current_section = nil
local summary_lines = {}
local section_lines = {}
local function flush_section()
if current_section then
local content = table.concat(section_lines, "\n")
doc.sections[#doc.sections + 1] = {
name = current_section,
content = content,
}
log.debug("doc: parsed section '# %s' (%d chars)", current_section, #content)
section_lines = {}
end
end
for _, line in ipairs(lines) do
-- Check for section header: "# Word" at start of line
local header = line:match("^#%s+(%w+)%s*$")
if header and section_set[header] then
flush_section()
current_section = header
elseif current_section then
section_lines[#section_lines + 1] = line
else
summary_lines[#summary_lines + 1] = line
end
-- Extract links from every line
local line_links = extract_links(line)
for _, sym in ipairs(line_links) do
doc.links[#doc.links + 1] = sym
end
end
flush_section()
doc.summary = table.concat(summary_lines, "\n"):match("^%s*(.-)%s*$") or ""
log.debug("doc: summary=%d chars, sections=%d, links=%d", #doc.summary, #doc.sections, #doc.links)
return doc
end
return M