Created
May 26, 2021 15:06
-
-
Save edubart/e905faa40ad1f61726b0b43fa411e259 to your computer and use it in GitHub Desktop.
Nelua doc tool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local fs = require 'nelua.utils.fs' | |
local traits = require 'nelua.utils.traits' | |
local stringer = require 'nelua.utils.stringer' | |
local parser = require 'nelua.syntaxdefs'().parser | |
local re = require 'nelua.thirdparty.relabel' | |
local filename = 'lib/string.nelua' | |
local filecode = fs.ereadfile(filename) | |
local ast = parser:parse(filecode, filename) | |
local patt = re.compile([[ | |
comments <- {| (long_comment / short_comment / .)* |} | |
short_comment <- {| {} '--' '-'* %s* {~( | |
(!linebreak .) / (linebreak (%s* '--' %s*)->'') | |
)*~} {} |} linebreak? | |
long_comment <- {| {} (open ( {contents} close)) {} |} | |
contents <- (!close .)* | |
open <- '--[' {:eq: '='*:} '[' '-'* %s* | |
close <- %s* ']' =eq ']' | |
]] .. | |
"linebreak <- [%nl]'\r' / '\r'[%nl] / [%nl] / '\r'") | |
local comments = {} | |
local comments_by_line = {} | |
local captures = patt:match(filecode) | |
for i,v in ipairs(captures) do | |
local comment = {} | |
comments[i] = comment | |
comment.pos = v[1] | |
comment.text = v[2] | |
comment.endpos = v[3]-1 | |
local lineno, colno, line = stringer.calcline(filecode, comment.pos) | |
comment.lineno = lineno | |
if v.eq then -- adjust comments indentation | |
local indentspaces = line:match('^(%s*)--') | |
comment.text = comment.text:gsub('\n'..indentspaces, '\n') | |
end | |
comment.endline = stringer.calcline(filecode, comment.endpos) | |
for j=comment.lineno, comment.endline do | |
comments_by_line[j] = comment | |
end | |
end | |
local docsyms = {} | |
local topcomment = comments_by_line[1] | |
local modname = filename:match('(%w+).nelua$') | |
local modsym | |
if topcomment then | |
local sym = { | |
filename = filename, | |
name = modname, | |
kind = 'heading', | |
text = topcomment.text, | |
lineno = 1 | |
} | |
table.insert(docsyms, sym) | |
end | |
local function typenode2string(node) | |
if node and node.tag == 'Id' then | |
return node[1] | |
end | |
end | |
local function idnode2name(node) | |
if traits.is_string(node) then | |
return node | |
else | |
assert(traits.is_astnode(node)) | |
if node.tag == 'IdDecl' then | |
return node[1] | |
elseif node.tag == 'DotIndex' or node.tag == 'ColonIndex' then | |
local fieldname = node[1] | |
local indexnode = node[2] | |
assert(traits.is_string(fieldname)) | |
assert(traits.is_astnode(indexnode) and indexnode.tag == 'Id') | |
local sep = node.tag == 'DotIndex' and '.' or ':' | |
return indexnode[1]..sep..fieldname | |
else | |
print(node.tag) | |
error 'unexpected' | |
end | |
end | |
end | |
local function get_upper_comment(lineno) | |
local comment = comments_by_line[lineno-1] | |
if comment then | |
return comment.text | |
end | |
end | |
local function trimcode(code) | |
code = code:gsub('%-%-.*', '') -- remove comments | |
code = code:gsub('%s*$', '') -- remove trailing spaces | |
-- remove assignment for non types | |
if not code:match('=%s*@') then | |
code = code:gsub('%s*=.*', '') | |
end | |
code = code:gsub('%s*%b<>', '') -- remove annotations | |
return code | |
end | |
local function getcode(startpos, endpos) | |
local lineno, colno, line = stringer.calcline(filecode, startpos) | |
local indentspaces = line:match('^(%s*)--') | |
local code = filecode:sub(startpos, endpos - 2) | |
code = trimcode(code) | |
code = code:gsub('\n'..indentspaces, '\n') | |
return code, lineno | |
end | |
for node in ast:walk_nodes() do | |
if node.tag == 'FuncDef' then | |
local varscope, idnode, blocknode = node[1], node[2], node[6] | |
if varscope ~= 'local' then -- global/record functions | |
local name = idnode2name(idnode) | |
local code, lineno = getcode(node.pos, blocknode.pos-1) | |
local text = get_upper_comment(lineno) | |
local sym = { | |
filename = filename, | |
kind = 'funcdef', | |
code = code, | |
name = name, | |
lineno = lineno, | |
text = text, | |
} | |
table.insert(docsyms, sym) | |
end | |
elseif node.tag == 'VarDecl' then | |
local varscope, varnodes = node[1], node[2] | |
for _, varnode in ipairs(varnodes) do | |
if varnode.tag == 'IdDecl' then | |
local idnode, typenode = varnode[1], varnode[2] | |
local name = idnode2name(idnode) | |
if varscope == 'global' or name == modname..'T' then | |
local typename = typenode2string(typenode) | |
local code, lineno = getcode(node.pos, node.endpos - 2) | |
local text = get_upper_comment(lineno) | |
local sym = { | |
filename = filename, | |
kind = 'vardecl', | |
code = code, | |
name = name, | |
typename = typename, | |
lineno = lineno, | |
text = text, | |
} | |
if name == modname and code:match('record{%s*}') then | |
modsym = sym | |
else | |
table.insert(docsyms, sym) | |
end | |
end | |
end | |
end | |
end | |
end | |
print[[ | |
--- | |
layout: docs | |
title: Preview | |
permalink: /preview/ | |
categories: docs toc | |
toc: true | |
order: 10 | |
--- | |
]] | |
for _,sym in ipairs(docsyms) do | |
if sym.kind == 'heading' then | |
print('## ' .. sym.name) | |
print() | |
--[[ | |
if modsym then | |
print('```nelua') | |
print(modsym.code) | |
print('```') | |
print() | |
end | |
]] | |
print(sym.text) | |
print() | |
elseif sym.kind == 'vardecl' or sym.kind == 'funcdef' then | |
print('### ' .. sym.name) | |
print() | |
print('```nelua') | |
print(sym.code) | |
print('```') | |
print() | |
if sym.text then | |
print(sym.text) | |
end | |
print() | |
end | |
end | |
--[[ | |
TODO: | |
-- add link to file | |
-- add line number and link to function? | |
-- add index for each module? | |
]] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment