├── Makefile ├── README.md ├── bin ├── moondoc └── moondoc.moon ├── moondoc-dev-1.rockspec ├── moondoc.lua ├── moondoc.moon ├── moondoc ├── cmd.lua ├── cmd.moon ├── format.lua ├── format.moon ├── formatters │ ├── class.lua │ ├── class.moon │ ├── fndef.lua │ ├── fndef.moon │ ├── number.lua │ ├── number.moon │ ├── self.lua │ ├── self.moon │ ├── string.lua │ ├── string.moon │ ├── table.lua │ └── table.moon ├── init.lua └── init.moon └── spec └── moondoc_spec.moon /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test local build 2 | 3 | test: 4 | busted 5 | 6 | local: build 7 | luarocks make --local moondoc-dev-1.rockspec 8 | 9 | build: 10 | moonc moondoc.moon moondoc 11 | echo '#!/usr/bin/env lua' > bin/moondoc 12 | moonc -p bin/moondoc.moon >> bin/moondoc 13 | chmod +x bin/moondoc 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # moondoc 3 | 4 | `moondoc` is a command line tool that parses a MoonScript file and returns a 5 | Lua file with metadata about the classes, functions, and constants exported. 6 | It's designed to be part of a pipeline to generate documentation for MoonScript 7 | projects. 8 | 9 | It works by usingthe MoonScript parser to extract the AST, then it searches for 10 | for near by comments to add additional metadata. Information is extracted 11 | statically, without running the code, so dynamically generated parts might need 12 | additional annotation. 13 | -------------------------------------------------------------------------------- /bin/moondoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | return require("moondoc.cmd").run(...) 3 | -------------------------------------------------------------------------------- /bin/moondoc.moon: -------------------------------------------------------------------------------- 1 | require("moondoc.cmd").run ... 2 | -------------------------------------------------------------------------------- /moondoc-dev-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "moondoc" 2 | version = "dev-1" 3 | 4 | source = { 5 | url = "git://github.com/leafo/moondoc.git", 6 | branch = "master", 7 | } 8 | 9 | description = { 10 | summary = "Documentation page generator for MoonScript", 11 | license = "MIT", 12 | maintainer = "Leaf Corcoran ", 13 | } 14 | 15 | dependencies = { 16 | "lua ~> 5.1", 17 | "moonscript", 18 | "loadkit", 19 | "sitegen", 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["moondoc"] = "moondoc/init.lua", 26 | ["moondoc.cmd"] = "moondoc/cmd.lua", 27 | ["moondoc.format"] = "moondoc/format.lua", 28 | ["moondoc.formatters.class"] = "moondoc/formatters/class.lua", 29 | ["moondoc.formatters.fndef"] = "moondoc/formatters/fndef.lua", 30 | ["moondoc.formatters.number"] = "moondoc/formatters/number.lua", 31 | ["moondoc.formatters.self"] = "moondoc/formatters/self.lua", 32 | ["moondoc.formatters.string"] = "moondoc/formatters/string.lua", 33 | ["moondoc.formatters.table"] = "moondoc/formatters/table.lua", 34 | }, 35 | install = { 36 | bin = { "bin/moondoc" } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /moondoc.lua: -------------------------------------------------------------------------------- 1 | return require("moondoc.init") 2 | -------------------------------------------------------------------------------- /moondoc.moon: -------------------------------------------------------------------------------- 1 | require "moondoc.init" 2 | -------------------------------------------------------------------------------- /moondoc/cmd.lua: -------------------------------------------------------------------------------- 1 | local parse_flags 2 | parse_flags = function(input) 3 | local flags = { } 4 | local filtered 5 | do 6 | local _accum_0 = { } 7 | local _len_0 = 1 8 | for _index_0 = 1, #input do 9 | local _continue_0 = false 10 | repeat 11 | local arg = input[_index_0] 12 | do 13 | local flag = arg:match("^%-%-?(.+)$") 14 | if flag then 15 | local k, v = flag:match("(.-)=(.*)") 16 | if k then 17 | flags[k] = v 18 | else 19 | flags[flag] = true 20 | end 21 | _continue_0 = true 22 | break 23 | end 24 | end 25 | local _value_0 = arg 26 | _accum_0[_len_0] = _value_0 27 | _len_0 = _len_0 + 1 28 | _continue_0 = true 29 | until true 30 | if not _continue_0 then 31 | break 32 | end 33 | end 34 | filtered = _accum_0 35 | end 36 | return flags, filtered 37 | end 38 | local actions = { 39 | scan_modules = function(args, flags) 40 | local scan_for_modules 41 | scan_for_modules = require("moondoc").scan_for_modules 42 | local path = unpack(args) or "." 43 | local _list_0 = scan_for_modules(path) 44 | for _index_0 = 1, #_list_0 do 45 | local _des_0 = _list_0[_index_0] 46 | local fname, mod 47 | fname, mod = _des_0[1], _des_0[2] 48 | print(mod) 49 | end 50 | end, 51 | module_exports = function(args, flags) 52 | local parse_module_by_name 53 | parse_module_by_name = require("moondoc").parse_module_by_name 54 | local mod_name = unpack(args) or error("Missing module name") 55 | local data = parse_module_by_name(mod_name) 56 | return require("moon").p(data) 57 | end, 58 | help = function(self) end 59 | } 60 | local run 61 | run = function(...) 62 | local flags, args = parse_flags({ 63 | ... 64 | }) 65 | local action_name = args[1] 66 | if not (action_name) then 67 | action_name = "help" 68 | end 69 | table.remove(args, 1) 70 | assert(actions[action_name], "unknown command: " .. tostring(action_name)) 71 | return actions[action_name](args, flags) 72 | end 73 | return { 74 | run = run 75 | } 76 | -------------------------------------------------------------------------------- /moondoc/cmd.moon: -------------------------------------------------------------------------------- 1 | 2 | parse_flags = (input) -> 3 | flags = {} 4 | 5 | filtered = for arg in *input 6 | if flag = arg\match "^%-%-?(.+)$" 7 | k,v = flag\match "(.-)=(.*)" 8 | if k 9 | flags[k] = v 10 | else 11 | flags[flag] = true 12 | continue 13 | arg 14 | 15 | flags, filtered 16 | 17 | actions = { 18 | scan_modules: (args, flags) -> 19 | import scan_for_modules from require "moondoc" 20 | path = unpack(args) or "." 21 | for {fname, mod} in *scan_for_modules path 22 | print mod 23 | 24 | module_exports: (args, flags) -> 25 | import parse_module_by_name from require "moondoc" 26 | mod_name = unpack(args) or error "Missing module name" 27 | data = parse_module_by_name mod_name 28 | require("moon").p data 29 | 30 | help: => 31 | 32 | } 33 | 34 | run = (...) -> 35 | flags, args = parse_flags {...} 36 | action_name = args[1] 37 | action_name = "help" unless action_name 38 | 39 | table.remove args, 1 40 | 41 | assert actions[action_name], "unknown command: #{action_name}" 42 | actions[action_name] args, flags 43 | 44 | {:run} 45 | -------------------------------------------------------------------------------- /moondoc/format.lua: -------------------------------------------------------------------------------- 1 | local format_stm 2 | format_stm = function(node, more_props) 3 | if not (node) then 4 | return 5 | end 6 | local node_type = node[1] 7 | assert(type(node_type) == "string", "invalid node type: " .. tostring(type(node_type))) 8 | local formatter 9 | formatter = require("moondoc.formatters." .. tostring(node_type)) 10 | if not (formatter) then 11 | return 12 | end 13 | local out = formatter(node) 14 | if out then 15 | if more_props then 16 | for k, v in pairs(more_props) do 17 | out[k] = v 18 | end 19 | end 20 | return out 21 | end 22 | end 23 | return { 24 | format_stm = format_stm 25 | } 26 | -------------------------------------------------------------------------------- /moondoc/format.moon: -------------------------------------------------------------------------------- 1 | 2 | format_stm = (node, more_props) -> 3 | return unless node 4 | node_type = node[1] 5 | assert type(node_type) == "string", 6 | "invalid node type: #{type node_type}" 7 | 8 | local formatter 9 | formatter = require "moondoc.formatters.#{node_type}" 10 | 11 | return unless formatter 12 | out = formatter node 13 | 14 | if out 15 | if more_props 16 | for k,v in pairs more_props 17 | out[k] = v 18 | 19 | out 20 | 21 | {:format_stm} 22 | 23 | -------------------------------------------------------------------------------- /moondoc/formatters/class.lua: -------------------------------------------------------------------------------- 1 | local format_stm 2 | format_stm = require("moondoc.format").format_stm 3 | local types 4 | types = require("tableshape").types 5 | local is_props = types.shape({ 6 | "props" 7 | }, { 8 | open = true 9 | }) 10 | local is_key_literal = types.shape({ 11 | "key_literal", 12 | types.string 13 | }, { 14 | open = true 15 | }) 16 | local is_self = types.shape({ 17 | "self", 18 | types.string 19 | }, { 20 | open = true 21 | }) 22 | local is_function = types.shape({ 23 | "fndef" 24 | }, { 25 | open = true 26 | }) 27 | local is_method = types.shape({ 28 | "props", 29 | types.shape({ 30 | is_key_literal, 31 | is_function 32 | }) 33 | }) 34 | local is_class_method = types.shape({ 35 | "props", 36 | types.shape({ 37 | is_self, 38 | is_function 39 | }) 40 | }) 41 | return function(node) 42 | local contents = node[4] 43 | local methods = { } 44 | local class_methods = { } 45 | for _index_0 = 1, #contents do 46 | local p = contents[_index_0] 47 | local add_item 48 | add_item = function(list, p) 49 | local key, value 50 | do 51 | local _obj_0 = p[2] 52 | key, value = _obj_0[1], _obj_0[2] 53 | end 54 | local property_name = key[2] 55 | local formatted = format_stm(value, { 56 | name = property_name 57 | }) 58 | return table.insert(list, formatted) 59 | end 60 | if is_method(p) then 61 | add_item(methods, p) 62 | end 63 | if is_class_method(p) then 64 | add_item(class_methods, p) 65 | end 66 | end 67 | return { 68 | type = "class", 69 | methods = methods, 70 | class_methods = class_methods 71 | } 72 | end 73 | -------------------------------------------------------------------------------- /moondoc/formatters/class.moon: -------------------------------------------------------------------------------- 1 | import format_stm from require "moondoc.format" 2 | 3 | import types from require "tableshape" 4 | is_props = types.shape { "props" }, open: true 5 | is_key_literal = types.shape { "key_literal", types.string }, open: true 6 | is_self = types.shape { "self", types.string }, open: true 7 | 8 | is_function = types.shape { "fndef" }, open: true 9 | 10 | is_method = types.shape { 11 | "props" 12 | types.shape { 13 | is_key_literal 14 | is_function 15 | } 16 | } 17 | 18 | is_class_method = types.shape { 19 | "props" 20 | types.shape { 21 | is_self 22 | is_function 23 | } 24 | } 25 | 26 | (node) -> 27 | -- TODO: extract fields 28 | contents = node[4] 29 | 30 | methods = {} 31 | class_methods = {} 32 | 33 | for p in *contents 34 | add_item = (list, p) -> 35 | { key, value } = p[2] 36 | property_name = key[2] 37 | 38 | formatted = format_stm value, { 39 | name: property_name 40 | } 41 | 42 | table.insert list, formatted 43 | 44 | if is_method p 45 | add_item methods, p 46 | 47 | if is_class_method p 48 | add_item class_methods, p 49 | 50 | { 51 | type: "class" 52 | :methods 53 | :class_methods 54 | } 55 | -------------------------------------------------------------------------------- /moondoc/formatters/fndef.lua: -------------------------------------------------------------------------------- 1 | local format_stm 2 | format_stm = require("moondoc.format").format_stm 3 | local types 4 | types = require("tableshape").types 5 | local is_method = types.shape({ 6 | "fndef", 7 | types.table, 8 | types.table, 9 | "fat" 10 | }, { 11 | open = true 12 | }) 13 | return function(node) 14 | local arguments = { } 15 | local raw_args = node[2] 16 | for _index_0 = 1, #raw_args do 17 | local _des_0 = raw_args[_index_0] 18 | local name, default 19 | name, default = _des_0[1], _des_0[2] 20 | if default then 21 | default = format_stm(default) 22 | end 23 | table.insert(arguments, { 24 | name = name, 25 | default = default 26 | }) 27 | end 28 | return { 29 | type = is_method(node) and "method" or "function", 30 | arguments = arguments 31 | } 32 | end 33 | -------------------------------------------------------------------------------- /moondoc/formatters/fndef.moon: -------------------------------------------------------------------------------- 1 | import format_stm from require "moondoc.format" 2 | import types from require "tableshape" 3 | 4 | is_method = types.shape { 5 | "fndef", types.table, types.table, "fat" 6 | }, open: true 7 | 8 | (node) -> 9 | arguments = {} 10 | raw_args = node[2] 11 | 12 | for {name, default} in *raw_args 13 | if default 14 | default = format_stm default 15 | 16 | table.insert arguments, { 17 | :name 18 | :default 19 | } 20 | 21 | { 22 | type: is_method(node) and "method" or "function" 23 | :arguments 24 | } 25 | -------------------------------------------------------------------------------- /moondoc/formatters/number.lua: -------------------------------------------------------------------------------- 1 | return function(node) 2 | return { 3 | type = "number", 4 | value = node[2] 5 | } 6 | end 7 | -------------------------------------------------------------------------------- /moondoc/formatters/number.moon: -------------------------------------------------------------------------------- 1 | (node) -> 2 | { 3 | type: "number" 4 | value: node[2] 5 | } 6 | 7 | -------------------------------------------------------------------------------- /moondoc/formatters/self.lua: -------------------------------------------------------------------------------- 1 | return function(node) 2 | require("moon").p(node) 3 | return { 4 | type = "self" 5 | } 6 | end 7 | -------------------------------------------------------------------------------- /moondoc/formatters/self.moon: -------------------------------------------------------------------------------- 1 | (node) -> 2 | require("moon").p node 3 | 4 | { 5 | type: "self" 6 | name: node[2] 7 | } 8 | 9 | -------------------------------------------------------------------------------- /moondoc/formatters/string.lua: -------------------------------------------------------------------------------- 1 | return function(node) 2 | return { 3 | type = "string", 4 | value = node[3] 5 | } 6 | end 7 | -------------------------------------------------------------------------------- /moondoc/formatters/string.moon: -------------------------------------------------------------------------------- 1 | (node) -> 2 | { 3 | type: "string" 4 | value: node[3] 5 | } 6 | 7 | -------------------------------------------------------------------------------- /moondoc/formatters/table.lua: -------------------------------------------------------------------------------- 1 | local types 2 | types = require("tableshape").types 3 | local is_empty = types.shape({ 4 | "table", 5 | types.shape({ }) 6 | }, { 7 | open = true 8 | }) 9 | return function(node) 10 | return { 11 | type = "table" 12 | } 13 | end 14 | -------------------------------------------------------------------------------- /moondoc/formatters/table.moon: -------------------------------------------------------------------------------- 1 | 2 | import types from require "tableshape" 3 | 4 | is_empty = types.shape { 5 | "table" 6 | types.shape { } 7 | }, open: true 8 | 9 | (node) -> 10 | { type: "table" } 11 | 12 | -------------------------------------------------------------------------------- /moondoc/init.lua: -------------------------------------------------------------------------------- 1 | local parse = require("moonscript.parse") 2 | local loadkit = require("loadkit") 3 | local pos_to_line 4 | pos_to_line = require("moonscript.util").pos_to_line 5 | local types 6 | types = require("tableshape").types 7 | local format_stm 8 | format_stm = require("moondoc.format").format_stm 9 | local t 10 | t = function(k) 11 | return types.shape(k, { 12 | open = true 13 | }) 14 | end 15 | local is_class = t({ 16 | "class", 17 | types.string 18 | }) 19 | local is_ref = t({ 20 | "ref", 21 | types.string 22 | }) 23 | local is_table = t({ 24 | "table", 25 | types.table 26 | }) 27 | local is_assign = t({ 28 | "assign", 29 | types.table, 30 | types.table 31 | }) 32 | local is_key_literal = t({ 33 | "key_literal", 34 | types.string 35 | }) 36 | local ref_to_string 37 | ref_to_string = function(r) 38 | assert(type(r[2]) == "string", "don't know how to convert ref") 39 | return r[2] 40 | end 41 | local Buffer 42 | do 43 | local _class_0 44 | local _base_0 = { 45 | pos_to_line = function(self, pos) 46 | if not (pos) then 47 | return nil 48 | end 49 | return pos_to_line(self.buffer, pos) 50 | end, 51 | get_proceeding_comment = function(self, pos) end 52 | } 53 | _base_0.__index = _base_0 54 | _class_0 = setmetatable({ 55 | __init = function(self, buffer) 56 | self.buffer = buffer 57 | end, 58 | __base = _base_0, 59 | __name = "Buffer" 60 | }, { 61 | __index = _base_0, 62 | __call = function(cls, ...) 63 | local _self_0 = setmetatable({}, _base_0) 64 | cls.__init(_self_0, ...) 65 | return _self_0 66 | end 67 | }) 68 | _base_0.__class = _class_0 69 | Buffer = _class_0 70 | end 71 | local assigns_by_name 72 | assigns_by_name = function(assign) 73 | local assigns = { } 74 | for idx, name in ipairs(assign[2]) do 75 | local _continue_0 = false 76 | repeat 77 | local value = assign[3][idx] 78 | if not (value) then 79 | _continue_0 = true 80 | break 81 | end 82 | if is_ref(name) then 83 | name = ref_to_string(name) 84 | elseif types.string(name) then 85 | name = name 86 | else 87 | _continue_0 = true 88 | break 89 | end 90 | assigns[name] = value 91 | _continue_0 = true 92 | until true 93 | if not _continue_0 then 94 | break 95 | end 96 | end 97 | return assigns 98 | end 99 | local parse_expresion 100 | parse_expresion = function(code) 101 | local tree = assert(parse.string(code)) 102 | local exp = assert(unpack(tree), "missing expression") 103 | return format_stm(exp) 104 | end 105 | local parse_module 106 | parse_module = function(code, opts) 107 | if opts == nil then 108 | opts = { } 109 | end 110 | local buffer = Buffer(code) 111 | local tree = assert(parse.string(code)) 112 | local locals = { } 113 | for _index_0 = 1, #tree do 114 | local stm = tree[_index_0] 115 | if is_class(stm) then 116 | locals[stm[2]] = stm 117 | elseif is_assign(stm) then 118 | local assigns = assigns_by_name(stm) 119 | for k, v in pairs(assigns) do 120 | locals[k] = v 121 | end 122 | end 123 | end 124 | local out = { } 125 | local last = tree[#tree] 126 | if is_table(last) then 127 | out.type = "table" 128 | out.exports = { } 129 | local _list_0 = last[2] 130 | for _index_0 = 1, #_list_0 do 131 | local _continue_0 = false 132 | repeat 133 | local _des_0 = _list_0[_index_0] 134 | local key, value 135 | key, value = _des_0[1], _des_0[2] 136 | if not (is_key_literal(key)) then 137 | _continue_0 = true 138 | break 139 | end 140 | local export_name = key[2] 141 | if not (is_ref(value)) then 142 | _continue_0 = true 143 | break 144 | end 145 | local local_name = value[2] 146 | do 147 | local export_value = locals[local_name] 148 | if export_value then 149 | local formatted = format_stm(export_value, { 150 | name = export_name, 151 | line_number = buffer:pos_to_line(export_value[-1]) 152 | }) 153 | if not (formatted) then 154 | _continue_0 = true 155 | break 156 | end 157 | table.insert(out.exports, formatted) 158 | end 159 | end 160 | _continue_0 = true 161 | until true 162 | if not _continue_0 then 163 | break 164 | end 165 | end 166 | else 167 | error("not yet") 168 | end 169 | return out 170 | end 171 | local filename_for_module 172 | filename_for_module = function(module_name) 173 | local loader = loadkit.make_loader("moon", nil, "./?.lua") 174 | return assert(loader(module_name)) 175 | end 176 | local parse_module_by_name 177 | parse_module_by_name = function(module_name, fname) 178 | fname = fname or filename_for_module(module_name) 179 | local f = assert(io.open(fname)) 180 | local file = f:read("*a") 181 | f:close() 182 | local out = parse_module(file) 183 | out.name = module_name 184 | return out 185 | end 186 | local shell_escape 187 | shell_escape = function(str) 188 | return str:gsub("'", "''") 189 | end 190 | local scan_for_modules 191 | scan_for_modules = function(dir) 192 | if dir == nil then 193 | dir = "." 194 | end 195 | local f = assert(io.popen("find '" .. tostring(shell_escape(dir)) .. "' -type f -iname '*.moon' -print0")) 196 | local files = f:read("*a") 197 | local out 198 | do 199 | local _accum_0 = { } 200 | local _len_0 = 1 201 | for file in files:gmatch("[^%z]+") do 202 | local module_name = file:gsub("^%./", ""):gsub("%.moon$", ""):gsub("/", ".") 203 | if module_name:match("%.init$") then 204 | module_name = module_name:sub(1, -(#".init" + 1)) 205 | end 206 | local _value_0 = { 207 | file, 208 | module_name 209 | } 210 | _accum_0[_len_0] = _value_0 211 | _len_0 = _len_0 + 1 212 | end 213 | out = _accum_0 214 | end 215 | table.sort(out, function(a, b) 216 | return a[2] < b[2] 217 | end) 218 | return out 219 | end 220 | return { 221 | parse_module = parse_module, 222 | parse_expresion = parse_expresion, 223 | parse_module_by_name = parse_module_by_name, 224 | scan_for_modules = scan_for_modules 225 | } 226 | -------------------------------------------------------------------------------- /moondoc/init.moon: -------------------------------------------------------------------------------- 1 | 2 | parse = require "moonscript.parse" 3 | 4 | loadkit = require "loadkit" 5 | 6 | import pos_to_line from require "moonscript.util" 7 | import types from require "tableshape" 8 | import format_stm from require "moondoc.format" 9 | 10 | t = (k) -> types.shape k, open: true 11 | 12 | is_class = t { "class", types.string } 13 | is_ref = t { "ref", types.string } 14 | is_table = t { "table", types.table } 15 | is_assign = t { "assign", types.table, types.table } 16 | is_key_literal = t { "key_literal", types.string } 17 | 18 | ref_to_string = (r) -> 19 | assert type(r[2]) == "string", "don't know how to convert ref" 20 | r[2] 21 | 22 | class Buffer 23 | new: (@buffer) => 24 | 25 | pos_to_line: (pos) => 26 | return nil unless pos 27 | pos_to_line @buffer, pos 28 | 29 | get_proceeding_comment: (pos) => 30 | 31 | assigns_by_name = (assign) -> 32 | assigns = {} 33 | 34 | for idx, name in ipairs assign[2] 35 | value = assign[3][idx] 36 | continue unless value 37 | 38 | name = if is_ref name 39 | ref_to_string name 40 | elseif types.string name 41 | name 42 | else 43 | continue 44 | 45 | assigns[name] = value 46 | 47 | assigns 48 | 49 | -- a quick helper to test format statements 50 | parse_expresion = (code) -> 51 | tree = assert parse.string code 52 | exp = assert unpack(tree), "missing expression" 53 | format_stm exp 54 | 55 | parse_module = (code, opts={}) -> 56 | buffer = Buffer code 57 | 58 | tree = assert parse.string code 59 | -- things available for export indexed by their name 60 | locals = {} 61 | 62 | for stm in *tree 63 | if is_class stm 64 | locals[stm[2]] = stm 65 | elseif is_assign stm 66 | assigns = assigns_by_name stm 67 | for k,v in pairs assigns 68 | locals[k] = v 69 | 70 | out = {} 71 | 72 | -- get the implicit return 73 | last = tree[#tree] 74 | 75 | if is_table last 76 | out.type = "table" 77 | out.exports = {} 78 | for {key,value} in *last[2] 79 | continue unless is_key_literal key 80 | export_name = key[2] 81 | continue unless is_ref value 82 | local_name = value[2] 83 | 84 | if export_value = locals[local_name] 85 | formatted = format_stm export_value, { 86 | name: export_name 87 | line_number: buffer\pos_to_line export_value[-1] 88 | } 89 | 90 | continue unless formatted 91 | table.insert out.exports, formatted 92 | 93 | else 94 | -- exporting single thing 95 | error "not yet" 96 | 97 | out 98 | 99 | filename_for_module = (module_name) -> 100 | loader = loadkit.make_loader "moon", nil, "./?.lua" 101 | assert loader module_name 102 | 103 | parse_module_by_name = (module_name, fname) -> 104 | fname or= filename_for_module module_name 105 | 106 | f = assert io.open fname 107 | file = f\read "*a" 108 | f\close! 109 | 110 | out = parse_module file 111 | out.name = module_name 112 | out 113 | 114 | shell_escape = (str) -> 115 | str\gsub "'", "''" 116 | 117 | scan_for_modules = (dir=".") -> 118 | f = assert io.popen "find '#{shell_escape dir}' -type f -iname '*.moon' -print0" 119 | files = f\read "*a" 120 | 121 | out = for file in files\gmatch "[^%z]+" 122 | module_name = file\gsub("^%./", "")\gsub("%.moon$", "")\gsub "/", "." 123 | 124 | -- trim init module 125 | if module_name\match "%.init$" 126 | module_name = module_name\sub 1, -(#".init" + 1) 127 | 128 | { file, module_name } 129 | 130 | table.sort out, (a,b) -> 131 | a[2] < b[2] 132 | 133 | out 134 | 135 | { :parse_module, :parse_expresion, :parse_module_by_name, :scan_for_modules } 136 | 137 | -------------------------------------------------------------------------------- /spec/moondoc_spec.moon: -------------------------------------------------------------------------------- 1 | 2 | import types from require "tableshape" 3 | 4 | describe "parse_expresion", -> 5 | import parse_expresion from require "moondoc" 6 | 7 | it "parses function", -> 8 | assert.same { 9 | type: "function" 10 | arguments: { 11 | {name: "a"} 12 | {name: "b"} 13 | } 14 | }, parse_expresion [[(a,b) ->]] 15 | 16 | it "parses function with argument defaults", -> 17 | assert.same { 18 | type: "function" 19 | type: "function" 20 | arguments: { 21 | {name: "a"} 22 | {name: "b"} 23 | {name: "c", default: { 24 | type: "number" 25 | value: "5" 26 | }} 27 | } 28 | }, parse_expresion [[(a,b,c=5) ->]] 29 | 30 | it "parses class", -> 31 | assert.same { 32 | type: "class" 33 | class_methods: {} 34 | methods: { 35 | { 36 | name: "new" 37 | type: "method" 38 | arguments: { 39 | {name: "a"} 40 | } 41 | } 42 | } 43 | }, parse_expresion [[ 44 | class Something 45 | new: (a) => 46 | ]] 47 | 48 | describe "parse_module", -> 49 | import parse_module, parse_module_by_name from require "moondoc" 50 | 51 | it "loads module from path", -> 52 | out = parse_module_by_name "moondoc.init" 53 | s = types.shape { 54 | name: "moondoc.init" 55 | }, open: true 56 | assert s out 57 | 58 | describe "module types", -> 59 | it "loads table module", -> 60 | out = parse_module [[{}]] 61 | assert.same { 62 | exports: {} 63 | type: "table" 64 | }, out 65 | 66 | it "loads class module", -> pending "todo" 67 | it "loads value module", -> pending "todo" 68 | it "loads expression module", -> pending "todo" 69 | 70 | it "parses exports with table and function", -> 71 | out = parse_module [[ 72 | f = -> 73 | class Something 74 | {:Something, :f} 75 | ]] 76 | 77 | assert.same { 78 | type: "table" 79 | exports: { 80 | { 81 | type: "class" 82 | name: "Something" 83 | methods: {} 84 | class_methods: {} 85 | line_number: 2 86 | } 87 | 88 | { 89 | type: "function" 90 | name: "f" 91 | arguments: {} 92 | line_number: 1 93 | } 94 | } 95 | 96 | }, out 97 | 98 | it "parses function with arguments", -> 99 | out = parse_module [[ 100 | cool = (a,b,c=5)-> 101 | {out: cool} 102 | ]] 103 | assert.same { 104 | type: "table" 105 | exports: { 106 | { 107 | type: "function" 108 | name: "out" 109 | line_number: 1 110 | arguments: { 111 | {name: "a"} 112 | {name: "b"} 113 | {name: "c", default: { 114 | type: "number" 115 | value: "5" 116 | }} 117 | } 118 | } 119 | } 120 | }, out 121 | 122 | --------------------------------------------------------------------------------