diff --git a/Documentation/compile.bat b/Documentation/compile.bat new file mode 100644 index 000000000..2824964d6 --- /dev/null +++ b/Documentation/compile.bat @@ -0,0 +1,7 @@ +@echo off +setlocal +set LDOC_DIR=.\compiler\ldoc +set LUA_PATH=.\compiler\?.lua +set LUA_CPATH=.\compiler\?.dll +.\compiler\lua.exe %LDOC_DIR%\\ldoc.lua %* +exit /b %ERRORLEVEL% diff --git a/Documentation/compiler/ldoc/COPYRIGHT b/Documentation/compiler/ldoc/COPYRIGHT new file mode 100644 index 000000000..841f141b8 --- /dev/null +++ b/Documentation/compiler/ldoc/COPYRIGHT @@ -0,0 +1,22 @@ +LDoc License +----------- +Copyright (C) 2011 Steve Donovan. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/Documentation/compiler/ldoc/ldoc-scm-3.rockspec b/Documentation/compiler/ldoc/ldoc-scm-3.rockspec new file mode 100644 index 000000000..46d14f0ac --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc-scm-3.rockspec @@ -0,0 +1,66 @@ +package = "ldoc" +version = "scm-3" + +source = { + dir="LDoc", + url = "git+https://github.com/stevedonovan/LDoc.git" +} + +description = { + summary = "A Lua Documentation Tool", + detailed = [[ + LDoc is a LuaDoc-compatible documentation generator which can also + process C extension source. Markdown may be optionally used to + render comments, as well as integrated readme documentation and + pretty-printed example files + ]], + homepage='https://github.com/lunarmodules/LDoc', + maintainer='steve.j.donovan@gmail.com', + license = "MIT/X11", +} + +dependencies = { + "penlight","markdown" +} + +build = { + type = "builtin", + modules = { + ["ldoc.tools"] = "ldoc/tools.lua", + ["ldoc.lang"] = "ldoc/lang.lua", + ["ldoc.parse"] = "ldoc/parse.lua", + ["ldoc.html"] = "ldoc/html.lua", + ["ldoc.lexer"] = "ldoc/lexer.lua", + ["ldoc.markup"] = "ldoc/markup.lua", + ["ldoc.prettify"] = "ldoc/prettify.lua", + ["ldoc.markdown"] = "ldoc/markdown.lua", + ["ldoc.doc"] = "ldoc/doc.lua", + ["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua", + ["ldoc.html.ldoc_md_ltp"] = "ldoc/html/ldoc_md_ltp.lua", + ["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua", + ["ldoc.html._code_css"] = "ldoc/html/_code_css.lua", + ["ldoc.html._reset_css"] = "ldoc/html/_reset_css.lua", + ["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua", + ["ldoc.html.ldoc_pale_css"] = "ldoc/html/ldoc_pale_css.lua", + ["ldoc.html.ldoc_new_css"] = "ldoc/html/ldoc_new_css.lua", + ["ldoc.html.ldoc_fixed_css"] = "ldoc/html/ldoc_fixed_css.lua", + ["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua", + ["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua", + ["ldoc.builtin.global"] = "ldoc/builtin/global.lua", + ["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua", + ["ldoc.builtin.io"] = "ldoc/builtin/io.lua", + ["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua", + ["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua", + ["ldoc.builtin.math"] = "ldoc/builtin/math.lua", + ["ldoc.builtin.os"] = "ldoc/builtin/os.lua", + ["ldoc.builtin.package"] = "ldoc/builtin/package.lua", + ["ldoc.builtin.string"] = "ldoc/builtin/string.lua", + ["ldoc.builtin.table"] = "ldoc/builtin/table.lua", + }, + copy_directories = {'doc','tests'}, + install = { + bin = { + ldoc = "ldoc.lua" + } + } +} diff --git a/Documentation/compiler/ldoc/ldoc.lua b/Documentation/compiler/ldoc/ldoc.lua new file mode 100644 index 000000000..7ebd09072 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc.lua @@ -0,0 +1,866 @@ +#!/usr/bin/env lua +--------------- +-- ## ldoc, a Lua documentation generator. +-- +-- Compatible with luadoc-style annotations, but providing +-- easier customization options. +-- +-- C/C++ support for Lua extensions is provided. +-- +-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.4.3.zip) +-- +-- [Github Page](https://github.com/stevedonovan/ldoc) +-- +-- @author Steve Donovan +-- @copyright 2011 +-- @license MIT/X11 +-- @script ldoc + +local class = require 'pl.class' +local app = require 'pl.app' +local path = require 'pl.path' +local dir = require 'pl.dir' +local utils = require 'pl.utils' +local List = require 'pl.List' +local stringx = require 'pl.stringx' +local tablex = require 'pl.tablex' + +-- Penlight compatibility +utils.unpack = utils.unpack or unpack or table.unpack +local lapp = require 'pl.lapp' + +local version = '1.4.6' + +-- so we can find our private modules +app.require_here() + +--- @usage +local usage = [[ +ldoc, a documentation generator for Lua, v]]..version..[[ + + Invocation: + ldoc [options] + ldoc --version + + Options: + -d,--dir (default doc) output directory + -o,--output (default 'index') output name + -v,--verbose verbose + -a,--all show local functions, etc, in docs + -q,--quiet suppress output + -m,--module module docs as text + -s,--style (default !) directory for style sheet (ldoc.css) + -l,--template (default !) directory for template (ldoc.ltp) + -p,--project (default ldoc) project name + -t,--title (default Reference) page title + -f,--format (default plain) formatting - can be markdown, discount or plain + -b,--package (default .) top-level package basename (needed for module(...)) + -x,--ext (default html) output file extension + -c,--config (default config.ld) configuration name + -u,--unqualified don't show package name in sidebar links + -i,--ignore ignore any 'no doc comment or no module' warnings + -X,--not_luadoc break LuaDoc compatibility. Descriptions may continue after tags. + -D,--define (default none) set a flag to be used in config.ld + -C,--colon use colon style + -N,--no_args_infer don't infer arguments from source + -B,--boilerplate ignore first comment in source files + -M,--merge allow module merging + -S,--simple no return or params, no summary + -O,--one one-column output layout + -V,--version show version information + --date (default system) use this date in generated doc + --dump debug output dump + --filter (default none) filter output as Lua data (e.g pl.pretty.dump) + --tags (default none) show all references to given tags, comma-separated + --fatalwarnings non-zero exit status on any warning + --testing reproducible build; no date or version on output + --icon (default none) an image that will be displayed under the project name on all pages + + (string) source file or directory containing source + + `ldoc .` reads options from an `config.ld` file in same directory; + `ldoc -c path/to/myconfig.ld ` reads options from `path/to/myconfig.ld` + and processes if 'file' was not defined in the ld file. +]] +local args = lapp(usage) +local lfs = require 'lfs' +local doc = require 'ldoc.doc' +local lang = require 'ldoc.lang' +local tools = require 'ldoc.tools' +local global = require 'ldoc.builtin.globals' +local markup = require 'ldoc.markup' +local parse = require 'ldoc.parse' +local KindMap = tools.KindMap +local Item,File = doc.Item,doc.File +local quit = utils.quit + +if args.version then + print('LDoc v' .. version) + os.exit(0) +end + + +local ModuleMap = class(KindMap) +doc.ModuleMap = ModuleMap + +function ModuleMap:_init () + self.klass = ModuleMap + self.fieldname = 'section' +end + +local ProjectMap = class(KindMap) +ProjectMap.project_level = true + +function ProjectMap:_init () + self.klass = ProjectMap + self.fieldname = 'type' +end + +local lua, cc = lang.lua, lang.cc + +local file_types = { + ['.lua'] = lua, + ['.ldoc'] = lua, + ['.luadoc'] = lua, + ['.c'] = cc, + ['.h'] = cc, + ['.cpp'] = cc, + ['.cxx'] = cc, + ['.C'] = cc, + ['.mm'] = cc, + ['.cs'] = cc, + ['.moon'] = lang.moon, +} +------- ldoc external API ------------ + +-- the ldoc table represents the API available in `config.ld`. +local ldoc = { charset = 'UTF-8', version = version } + +local known_types, kind_names = {} + +local function lookup (itype,igroup,isubgroup) + local kn = kind_names[itype] + known_types[itype] = true + if kn then + if type(kn) == 'string' then + igroup = kn + else + igroup = kn[1] + isubgroup = kn[2] + end + end + return itype, igroup, isubgroup +end + +local function setup_kinds () + kind_names = ldoc.kind_names or {} + + ModuleMap:add_kind(lookup('function','Functions','Parameters')) + ModuleMap:add_kind(lookup('table','Tables','Fields')) + ModuleMap:add_kind(lookup('field','Fields')) + ModuleMap:add_kind(lookup('type','Types')) + ModuleMap:add_kind(lookup('lfunction','Local Functions','Parameters')) + ModuleMap:add_kind(lookup('annotation','Issues')) + + ProjectMap:add_kind(lookup('module','Modules')) + ProjectMap:add_kind(lookup('script','Scripts')) + ProjectMap:add_kind(lookup('classmod','Classes')) + ProjectMap:add_kind(lookup('topic','Topics')) + ProjectMap:add_kind(lookup('example','Examples')) + ProjectMap:add_kind(lookup('file','Source')) + + for k in pairs(kind_names) do + if not known_types[k] then + quit("unknown item type "..tools.quote(k).." in kind_names") + end + end +end + + +-- hacky way for doc module to be passed options... +doc.ldoc = ldoc + +-- if the corresponding argument was the default, then any ldoc field overrides +local function override (field,defval) + defval = defval or false + if args[field] == defval and ldoc[field] ~= nil then args[field] = ldoc[field] end +end + +-- aliases to existing tags can be defined. E.g. just 'p' for 'param' +function ldoc.alias (a,tag) + doc.add_alias(a,tag) +end + +-- standard aliases -- + +ldoc.alias('tparam',{'param',modifiers={type="$1"}}) +ldoc.alias('treturn',{'return',modifiers={type="$1"}}) +ldoc.alias('tfield',{'field',modifiers={type="$1"}}) + +function ldoc.tparam_alias (name,type) + type = type or name + ldoc.alias(name,{'param',modifiers={type=type}}) +end + +ldoc.alias ('error',doc.error_macro) + +ldoc.tparam_alias 'string' +ldoc.tparam_alias 'number' +ldoc.tparam_alias 'int' +ldoc.tparam_alias 'bool' +ldoc.tparam_alias 'func' +ldoc.tparam_alias 'tab' +ldoc.tparam_alias 'thread' + +function ldoc.add_language_extension(ext, lang) + lang = (lang=='c' and cc) or (lang=='lua' and lua) or quit('unknown language') + if ext:sub(1,1) ~= '.' then ext = '.'..ext end + file_types[ext] = lang +end + +function ldoc.add_section (name, title, subname) + ModuleMap:add_kind(name,title,subname) +end + +-- new tags can be added, which can be on a project level. +function ldoc.new_type (tag, header, project_level,subfield) + doc.add_tag(tag,doc.TAG_TYPE,project_level) + if project_level then + ProjectMap:add_kind(tag,header,subfield) + else + ModuleMap:add_kind(tag,header,subfield) + end +end + +function ldoc.manual_url (url) + global.set_manual_url(url) +end + +function ldoc.custom_see_handler(pat, handler) + doc.add_custom_see_handler(pat, handler) +end + +local ldoc_contents = { + 'alias','add_language_extension','custom_tags','new_type','add_section', 'tparam_alias', + 'file','project','title','package', 'icon','format','output','dir','ext', 'topics', + 'one','style','template','description','examples', 'pretty', 'charset', 'plain', + 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars', + 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', + 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', + 'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles', + 'unqualified', 'custom_display_name_handler', 'kind_names', 'custom_references', + 'dont_escape_underscore','global_lookup','prettify_files','convert_opt', 'user_keywords', + 'postprocess_html', + 'custom_css','version', + 'no_args_infer', + 'keep_menu_order' +} +ldoc_contents = tablex.makeset(ldoc_contents) + +local function loadstr (ldoc,txt) + local chunk, err + -- Penlight's Lua 5.2 compatibility has wobbled over the years... + if not rawget(_G,'loadin') then -- Penlight 0.9.5 + -- Penlight 0.9.7; no more global load() override + local load = load or utils.load + chunk,err = load(txt,'config',nil,ldoc) + else + -- luacheck: push ignore 113 + chunk,err = loadin(ldoc,txt) + -- luacheck: pop + end + return chunk, err +end + +-- any file called 'config.ld' found in the source tree will be +-- handled specially. It will be loaded using 'ldoc' as the environment. +local function read_ldoc_config (fname) + local directory = path.dirname(fname) + if directory == '' then + directory = '.' + end + local chunk, err, _ + if args.filter == 'none' then + print('reading configuration from '..fname) + end + local txt,not_found = utils.readfile(fname) + if txt then + chunk, err = loadstr(ldoc,txt) + if chunk then + if args.define ~= 'none' then ldoc[args.define] = true end + _,err = pcall(chunk) + end + end + if err then quit('error loading config file '..fname..': '..err) end + for k in pairs(ldoc) do + if not ldoc_contents[k] then + quit("this config file field/function is unrecognized: "..k) + end + end + return directory, not_found +end + +local quote = tools.quote +--- processing command line and preparing for output --- + +local file_list = List() +File.list = file_list +local config_dir + + +local ldoc_dir = arg[0]:gsub('[^/\\]+$','') +local doc_path = ldoc_dir..'/ldoc/builtin/?.lua' + +-- ldoc -m is expecting a Lua package; this converts this to a file path +if args.module then + -- first check if we've been given a global Lua lib function + if args.file:match '^%a+$' and global.functions[args.file] then + args.file = 'global.'..args.file + end + local fullpath,mod,_ = tools.lookup_existing_module_or_function (args.file, doc_path) + if not fullpath then + quit(mod) + else + args.file = fullpath + args.module = mod + end +end + +local abspath = tools.abspath + +-- a special case: 'ldoc .' can get all its parameters from config.ld +if args.file == '.' then + local err + config_dir,err = read_ldoc_config(args.config) + if err then quit("no "..quote(args.config).." found") end + local config_path = path.dirname(args.config) + if config_path ~= '' then + print('changing to directory',config_path) + lfs.chdir(config_path) + end + args.file = ldoc.file or '.' + if args.file == '.' then + args.file = lfs.currentdir() + elseif type(args.file) == 'table' then + for i,f in ipairs(args.file) do + args.file[i] = abspath(f) + end + else + args.file = abspath(args.file) + end +else + -- user-provided config file + if args.config ~= 'config.ld' then + local err + config_dir,err = read_ldoc_config(args.config) + if err then quit("no "..quote(args.config).." found") end + end + -- with user-provided file + if args.file == nil then + lapp.error('missing required parameter: file') + end + args.file = abspath(args.file) +end + +if type(ldoc.custom_tags) == 'table' then -- custom tags + for i, custom in ipairs(ldoc.custom_tags) do + if type(custom) == 'string' then + custom = {custom} + ldoc.custom_tags[i] = custom + end + doc.add_tag(custom[1], 'ML') + end +end -- custom tags + +local source_dir = args.file +if type(source_dir) == 'table' then + source_dir = source_dir[1] +end +if type(source_dir) == 'string' and path.isfile(source_dir) then + source_dir = path.splitpath(source_dir) +end +source_dir = source_dir:gsub('[/\\]%.$','') + +---------- specifying the package for inferring module names -------- +-- If you use module(...), or forget to explicitly use @module, then +-- ldoc has to infer the module name. There are three sensible values for +-- `args.package`: +-- +-- * '.' the actual source is in an immediate subdir of the path given +-- * '..' the path given points to the source directory +-- * 'NAME' explicitly give the base module package name +-- + +override ('package','.') + +local function setup_package_base() + if ldoc.package then args.package = ldoc.package end + if args.package == '.' then + args.package = source_dir + elseif args.package == '..' then + args.package = path.splitpath(source_dir) + elseif not args.package:find '[\\/]' then + local subdir,dir = path.splitpath(source_dir) + if dir == args.package then + args.package = subdir + elseif path.isdir(path.join(source_dir,args.package)) then + args.package = source_dir + else + quit("args.package is not the name of the source directory") + end + end +end + + +--------- processing files --------------------- +-- ldoc may be given a file, or a directory. `args.file` may also be specified in config.ld +-- where it is a list of files or directories. If specified on the command-line, we have +-- to find an optional associated config.ld, if not already loaded. + +if ldoc.ignore then args.ignore = true end + +local function process_file (f, flist) + local ext = path.extension(f) + local ftype = file_types[ext] + if ftype then + if args.verbose then print(f) end + ftype.extra = ldoc.parse_extra or {} + local F,err = parse.file(f,ftype,args) + if err then + if F then + F:warning("internal LDoc error") + end + quit(err) + end + flist:append(F) + end +end + +local process_file_list = tools.process_file_list + +setup_package_base() + +override 'no_args_infer' +override 'colon' +override 'merge' +override 'not_luadoc' +override 'module_file' +override 'boilerplate' +override 'all' + +setup_kinds() + +-- LDoc is doing plain ole C, don't want random Lua references! +if ldoc.parse_extra and ldoc.parse_extra.C then + ldoc.no_lua_ref = true +end + +if ldoc.merge_error_groups == nil then + ldoc.merge_error_groups = 'Error Message' +end + +-- ldoc.module_file establishes a partial ordering where the +-- master module files are processed first. +local function reorder_module_file () + if args.module_file then + local mf = {} + for mname, f in pairs(args.module_file) do + local fullpath = abspath(f) + mf[fullpath] = true + end + return function(x,y) + return mf[x] and not mf[y] + end + end +end + +-- process files, optionally in order that respects master module files +local function process_all_files(files) + local sortfn = reorder_module_file() + local files = tools.expand_file_list(files,'*.*') + if sortfn then files:sort(sortfn) end + for f in files:iter() do + process_file(f, file_list) + end + if #file_list == 0 then quit "no source files found" end +end + +if type(args.file) == 'table' then + -- this can only be set from config file so we can assume config is already read + process_all_files(args.file) + +elseif path.isdir(args.file) then + -- use any configuration file we find, if not already specified + if not config_dir then + local files = List(dir.getallfiles(args.file,'*.*')) + local config_files = files:filter(function(f) + return path.basename(f) == args.config + end) + if #config_files > 0 then + config_dir = read_ldoc_config(config_files[1]) + if #config_files > 1 then + print('warning: other config files found: '..config_files[2]) + end + end + end + + process_all_files({args.file}) + +elseif path.isfile(args.file) then + -- a single file may be accompanied by a config.ld in the same dir + if not config_dir then + config_dir = path.dirname(args.file) + if config_dir == '' then config_dir = '.' end + local config = path.join(config_dir,args.config) + if path.isfile(config) then + read_ldoc_config(config) + end + end + process_file(args.file, file_list) + if #file_list == 0 then quit "unsupported file extension" end +else + quit ("file or directory does not exist: "..quote(args.file)) +end + + +-- create the function that renders text (descriptions and summaries) +-- (this also will initialize the code prettifier used) +override ('format','plain') +override 'pretty' +ldoc.markup = markup.create(ldoc, args.format, args.pretty, ldoc.user_keywords) + +------ 'Special' Project-level entities --------------------------------------- +-- Examples and Topics do not contain code to be processed for doc comments. +-- Instead, they are intended to be rendered nicely as-is, whether as pretty-lua +-- or as Markdown text. Treating them as 'modules' does stretch the meaning of +-- of the term, but allows them to be treated much as modules or scripts. +-- They define an item 'body' field (containing the file's text) and a 'postprocess' +-- field which is used later to convert them into HTML. They may contain @{ref}s. + +local function add_special_project_entity (f,tags,process) + local F = File(f) + tags.name = path.basename(f) + local text = utils.readfile(f) + local item = F:new_item(tags,1) + if process then + text = process(F, text) + end + F:finish() + file_list:append(F) + item.body = text + return item, F +end + +local function prettify_source_files(files,class,linemap) + local prettify = require 'ldoc.prettify' + + process_file_list (files, '*.*', function(f) + local ext = path.extension(f) + local ftype = file_types[ext] + if ftype then + local item = add_special_project_entity(f,{ + class = class, + }) + -- wrap prettify for this example so it knows which file to blame + -- if there's a problem + local lang = ext:sub(2) + item.postprocess = function(code) + return '

'..path.basename(f)..'

\n' .. + prettify.lua(lang,f,code,0,true,linemap and linemap[f]) + end + end + end) +end + +if type(ldoc.examples) == 'string' then + ldoc.examples = {ldoc.examples} +end +if type(ldoc.examples) == 'table' then + prettify_source_files(ldoc.examples,"example") +end + +ldoc.is_file_prettified = {} + +if ldoc.prettify_files then + local files = List() + local linemap = {} + for F in file_list:iter() do + files:append(F.filename) + local mod = F.modules[1] + if mod then + local ls = List() + for item in mod.items:iter() do + ls:append(item.lineno) + end + linemap[F.filename] = ls + end + end + + if type(ldoc.prettify_files) == 'table' then + files = tools.expand_file_list(ldoc.prettify_files, '*.*') + elseif type(ldoc.prettify_files) == 'string' then + -- the gotcha is that if the person has a folder called 'show', only the contents + -- of that directory will be converted. So, we warn of this amibiguity + if ldoc.prettify_files == 'show' then + -- just fall through with all module files collected above + if path.exists 'show' then + print("Notice: if you only want to prettify files in `show`, then set prettify_files to `show/`") + end + else + files = tools.expand_file_list({ldoc.prettify_files}, '*.*') + end + end + + ldoc.is_file_prettified = tablex.makeset(files) + prettify_source_files(files,"file",linemap) +end + +if args.simple then + ldoc.no_return_or_parms=true + ldoc.no_summary=true +end + +ldoc.readme = ldoc.readme or ldoc.topics +if type(ldoc.readme) == 'string' then + ldoc.readme = {ldoc.readme} +end +if type(ldoc.readme) == 'table' then + process_file_list(ldoc.readme, '*.md', function(f) + local item, F = add_special_project_entity(f,{ + class = 'topic' + }, markup.add_sections) + -- add_sections above has created sections corresponding to the 2nd level + -- headers in the readme, which are attached to the File. So + -- we pass the File to the postprocesser, which will insert the section markers + -- and resolve inline @ references. + if ldoc.use_markdown_titles then + item.display_name = F.display_name + end + item.postprocess = function(txt) return ldoc.markup(txt,F) end + end) +end + +-- extract modules from the file objects, resolve references and sort appropriately --- + +local first_module +local project = ProjectMap() +local module_list = List() +module_list.by_name = {} + +local modcount = 0 + +for F in file_list:iter() do + for mod in F.modules:iter() do + if not first_module then first_module = mod end + if doc.code_tag(mod.type) then modcount = modcount + 1 end + module_list:append(mod) + module_list.by_name[mod.name] = mod + end +end + +local handle = io.open("output.xml", "w") +io.output(handle) +for mod in module_list:iter() do + mod:dumpToXML() +end +io.close(handle) + +for mod in module_list:iter() do + if not args.module then -- no point if we're just showing docs on the console + mod:resolve_references(module_list) + end + project:add(mod,module_list) +end + + +if ldoc.sort_modules then + table.sort(module_list,function(m1,m2) + return m1.name < m2.name + end) +end + +ldoc.single = modcount == 1 and first_module or nil + +--do return end + +-------- three ways to dump the object graph after processing ----- + +-- ldoc -m will give a quick & dirty dump of the module's documentation; +-- using -v will make it more verbose +if args.module then + if #module_list == 0 then quit("no modules found") end + if args.module == true then + file_list[1]:dump(args.verbose) + else + local M,name = module_list[1], args.module + local fun = M.items.by_name[name] + if not fun then + fun = M.items.by_name[M.mod_name..':'..name] + end + if not fun then quit(quote(name).." is not part of "..quote(args.file)) end + fun:dump(true) + end + return +end + +-- ldoc --dump will do the same as -m, except for the currently specified files +if args.dump then + for mod in module_list:iter() do + mod:dump(true) + end + os.exit() +end +if args.tags ~= 'none' then + local tagset = {} + for t in stringx.split(args.tags,','):iter() do + tagset[t] = true + end + for mod in module_list:iter() do + mod:dump_tags(tagset) + end + os.exit() +end + +-- ldoc --filter mod.name will load the module `mod` and pass the object graph +-- to the function `name`. As a special case --filter dump will use pl.pretty.dump. +if args.filter ~= 'none' then + doc.filter_objects_through_function(args.filter, module_list) + os.exit() +end + +-- can specify format, output, dir and ext in config.ld +override ('output','index') +override ('dir','doc') +override ('ext','html') +override 'one' + +-- handling styling and templates -- +ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp' + +-- special case: user wants to generate a .md file from a .lua file +if args.ext == 'md' then + if #module_list ~= 1 then + quit("can currently only generate Markdown output from one module only") + end + if not ldoc.template or ldoc.template == '!' then + ldoc.template = '!md' + end + args.output = module_list[1].name + args.dir = '.' + ldoc.template_escape = '>' + ldoc.style = false + args.ext = '.md' +end + +local function match_bang (s) + if type(s) ~= 'string' then return end + return s:match '^!(.*)' +end + +local function style_dir (sname) + local style = ldoc[sname] + local dir + if style==false and sname == 'style' then + args.style = false + ldoc.css = false + end + if style then + if style == true then + dir = config_dir + elseif type(style) == 'string' and (path.isdir(style) or match_bang(style)) then + dir = style + else + quit(quote(tostring(style)).." is not a directory") + end + args[sname] = dir + end +end + +-- the directories for template and stylesheet can be specified +-- either by command-line '--template','--style' arguments or by 'template and +-- 'style' fields in config.ld. +-- The assumption here is that if these variables are simply true then the directory +-- containing config.ld contains a ldoc.css and a ldoc.ltp respectively. Otherwise +-- they must be a valid subdirectory. + +style_dir 'style' +style_dir 'template' + +if not args.ext:find '^%.' then + args.ext = '.'..args.ext +end + +if args.one then + ldoc.style = '!one' +end + +local builtin_style, builtin_template = match_bang(args.style),match_bang(args.template) +if builtin_style or builtin_template then + -- '!' here means 'use built-in templates' + local user = path.expanduser('~'):gsub('[/\\: ]','_') + local tmpdir = path.join(path.is_windows and os.getenv('TMP') or (os.getenv('TMPDIR') or '/tmp'),'ldoc'..user) + if not path.isdir(tmpdir) then + lfs.mkdir(tmpdir) + end + local function tmpwrite (name) + local ok,text = pcall(require,'ldoc.html.'..name:gsub('%.','_')) + if not ok then + quit("cannot find builtin template "..name.." ("..text..")") + end + if not utils.writefile(path.join(tmpdir,name),text) then + quit("cannot write to temp directory "..tmpdir) + end + end + if builtin_style then + if builtin_style ~= '' then + ldoc.css = 'ldoc_'..builtin_style..'.css' + end + tmpwrite(ldoc.css) + args.style = tmpdir + end + if builtin_template then + if builtin_template ~= '' then + ldoc.templ = 'ldoc_'..builtin_template..'.ltp' + end + tmpwrite(ldoc.templ) + args.template = tmpdir + end +end + +-- default icon to nil +if args.icon == 'none' then args.icon = nil end + +ldoc.log = print +ldoc.kinds = project +ldoc.modules = module_list +ldoc.title = ldoc.title or args.title +ldoc.project = ldoc.project or args.project +ldoc.package = args.package:match '%a+' and args.package or nil +ldoc.icon = ldoc.icon or args.icon + +local source_date_epoch = os.getenv("SOURCE_DATE_EPOCH") +if args.testing then + ldoc.updatetime = "2015-01-01 12:00:00" + ldoc.version = 'TESTING' +elseif source_date_epoch == nil then + if args.date == 'system' then + ldoc.updatetime = os.date("%Y-%m-%d %H:%M:%S") + else + ldoc.updatetime = args.date + end +else + ldoc.updatetime = os.date("!%Y-%m-%d %H:%M:%S",source_date_epoch) +end + +local html = require 'ldoc.html' + +html.generate_output(ldoc, args, project) + +if args.verbose then + print 'modules' + for k in pairs(module_list.by_name) do print(k) end +end + +if args.fatalwarnings and Item.had_warning then + os.exit(1) +end diff --git a/Documentation/compiler/ldoc/ldoc/SciTE.properties b/Documentation/compiler/ldoc/ldoc/SciTE.properties new file mode 100644 index 000000000..788e228eb --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/SciTE.properties @@ -0,0 +1,3 @@ +tabsize=3 +indent.size=3 +use.tabs=0 diff --git a/Documentation/compiler/ldoc/ldoc/builtin/coroutine.lua b/Documentation/compiler/ldoc/ldoc/builtin/coroutine.lua new file mode 100644 index 000000000..25e2f8267 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/coroutine.lua @@ -0,0 +1,50 @@ +--- creating and controlling coroutines. +-- @module coroutine + +local coroutine = {} + +--- +-- Creates a new coroutine, with body `f`. `f` must be a Lua +-- function. Returns this new coroutine, an object with type `"thread"`. +function coroutine.create(f) end + +--- +-- Starts or continues the execution of coroutine `co`. The first time +-- you resume a coroutine, it starts running its body. The values +-- ... are passed as the arguments to the body function. If the coroutine +-- has yielded, `resume` restarts it; the values ... are passed +-- as the results from the yield. +-- If the coroutine runs without any errors, `resume` returns true plus any +-- values passed to `yield` (if the coroutine yields) or any values returned +-- by the body function (if the coroutine terminates). If there is any error, +-- `resume` returns false plus the error message. +function coroutine.resume(co , ...) end + +--- +-- Returns the running coroutine. Or nil when called by the main thread. +function coroutine.running() end + +--- +-- Returns the status of coroutine `co`. Result is a string: `"running"`, if +-- the coroutine is running (that is, it called `status`); `"suspended"`, if +-- the coroutine is suspended in a call to `yield`, or if it has not started +-- running yet; `"normal"` if the coroutine is active but not running (that +-- is, it has resumed another coroutine); and `"dead"` if the coroutine has +-- finished its body function, or if it has stopped with an error. +function coroutine.status(co) end + +--- +-- Creates a new coroutine, with body `f`. `f` must be a Lua +-- function. Returns a function that resumes the coroutine each time it is +-- called. Any arguments passed to the function behave as the extra arguments to +-- `resume`. Returns the same values returned by `resume`, except the first +-- boolean. In case of error, propagates the error. +function coroutine.wrap(f) end + +--- +-- Suspends the execution of the calling coroutine. The coroutine cannot +-- be running a C function, a metamethod, or an iterator. Any arguments to +-- `yield` are passed as extra results to `resume`. +function coroutine.yield(...) end + +return coroutine diff --git a/Documentation/compiler/ldoc/ldoc/builtin/debug.lua b/Documentation/compiler/ldoc/ldoc/builtin/debug.lua new file mode 100644 index 000000000..6121a8f0d --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/debug.lua @@ -0,0 +1,124 @@ +--- getting runtime debug information. +-- @module debug + +local debug = {} +--- +-- Enters an interactive mode with the user, running each string that +-- the user enters. Using simple commands and other debug facilities, +-- the user can inspect global and local variables, change their values, +-- evaluate expressions, and so on. A line containing only the word `cont` +-- finishes this function, so that the caller continues its execution. +-- Note that commands for `debug.debug` are not lexically nested within any +-- function, and so have no direct access to local variables. +function debug.debug() end + +--- +-- Returns the environment of object `o`. +function debug.getfenv(o) end + +--- +-- Returns the current hook settings of the thread, as three values: the +-- current hook function, the current hook mask, and the current hook count +-- (as set by the `debug.sethook` function). +function debug.gethook(thread) end + +--- +-- Returns a table with information about a function. You can give the +-- function directly, or you can give a number as the value of `function`, +-- which means the function running at level `function` of the call stack +-- of the given thread: level 0 is the current function (`getinfo` itself); +-- level 1 is the function that called `getinfo`; and so on. If `function` +-- is a number larger than the number of active functions, then `getinfo` +-- returns nil. +-- +-- `thread` and `what` are optional. +-- +-- The returned table can contain all the fields returned by `lua_getinfo`, +-- with the string `what` describing which fields to fill in. The default for +-- `what` is to get all information available, except the table of valid +-- lines. If present, the option '`f`' adds a field named `func` with +-- the function itself. If present, the option '`L`' adds a field named +-- `activelines` with the table of valid lines. +-- For instance, the expression `debug.getinfo(1,"n").name` returns a table +-- with a name for the current function, if a reasonable name can be found, +-- and the expression `debug.getinfo(print)` returns a table with all available +-- information about the `print` function. +function debug.getinfo(thread, func , what) end + +--- +-- This function returns the name and the value of the local variable with +-- index `loc` of the function at level `level` of the stack. (The first +-- parameter or local variable has index 1, and so on, until the last active +-- local variable.) The function returns nil if there is no local variable +-- with the given index, and raises an error when called with a `level` out +-- of range. (You can call `debug.getinfo` to check whether the level is valid.) +-- Variable names starting with '`(`' (open parentheses) represent internal +-- variables (loop control variables, temporaries, and C function locals). +function debug.getlocal(thread, level, loc) end + +--- +-- Returns the metatable of the given `object` or nil if it does not have +-- a metatable. +function debug.getmetatable(object) end + +--- +-- Returns the registry table (see ยง3.5). +function debug.getregistry() end + +--- +-- This function returns the name and the value of the upvalue with index +-- `up` of the function `func`. The function returns nil if there is no +-- upvalue with the given index. +function debug.getupvalue(func, up) end + +--- +-- Sets the environment of the given `object` to the given `table`. Returns +-- `object`. +function debug.setfenv(object, table) end + +--- +-- Sets the given function as a hook. The string `mask` and the number +-- `count` describe when the hook will be called. The string mask may have +-- the following characters, with the given meaning: +-- +-- * `"c"`: the hook is called every time Lua calls a function; +-- * `"r"`: the hook is called every time Lua returns from a function; +-- * `"l"`: the hook is called every time Lua enters a new line of code. +-- +-- With a `count` different from zero, the hook is called after every `count` +-- instructions. +-- +-- When called without arguments, `debug.sethook` turns off the hook. +-- +-- When the hook is called, its first parameter is a string describing +-- the event that has triggered its call: `"call"`, `"return"` (or `"tail +-- return"`, when simulating a return from a tail call), `"line"`, and +-- `"count"`. For line events, the hook also gets the new line number as its +-- second parameter. Inside a hook, you can call `getinfo` with level 2 to +-- get more information about the running function (level 0 is the `getinfo` +-- function, and level 1 is the hook function), unless the event is `"tail +-- return"`. In this case, Lua is only simulating the return, and a call to +-- `getinfo` will return invalid data. +function debug.sethook(thread, hook, mask , count) end + +--- +-- This function assigns the value `value` to the local variable with +-- index `loc` of the function at level `level` of the stack. The function +-- returns nil if there is no local variable with the given index, and raises +-- an error when called with a `level` out of range. (You can call `getinfo` +-- to check whether the level is valid.) Otherwise, it returns the name of +-- the local variable. +function debug.setlocal(thread, level, loc, value) end + +--- +-- Sets the metatable for the given `object` to the given `table` (which +-- can be nil). +function debug.setmetatable(object, table) end + +--- +-- This function assigns the value `value` to the upvalue with index `up` +-- of the function `func`. The function returns nil if there is no upvalue +-- with the given index. Otherwise, it returns the name of the upvalue. +function debug.setupvalue(func, up, value) end + +return debug diff --git a/Documentation/compiler/ldoc/ldoc/builtin/global.lua b/Documentation/compiler/ldoc/ldoc/builtin/global.lua new file mode 100644 index 000000000..b1799c341 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/global.lua @@ -0,0 +1,243 @@ +--- Lua global functions. + +module 'global' + +-- luacheck: ignore 121 + +--- +-- Issues an error when its argument `v` is false. +-- That is, nil or false. otherwise, returns all its arguments. +-- `message` is an error when absent, it defaults to "assertion failed!" +function assert(v , message) end + +--- +-- This function is a generic interface to the garbage collector. It +-- performs different functions according to its first argument, `opt`: +-- +-- * "stop": stops the garbage collector. +-- * "restart": restarts the garbage collector. +-- * "collect": performs a full garbage-collection cycle. +-- * "count": returns the total memory in use by Lua (in Kbytes). +-- * "step": performs a garbage-collection step. The step "size" is controlled +-- by `arg` (larger values mean more steps) in a non-specified way. If you +-- want to control the step size you must experimentally tune the value of +-- * "arg". Returns true if the step finished a collection cycle. +-- * "setpause": sets `arg` as the new value for the *pause* of the collector +-- (see 2.10). Returns the previous value for *pause*. +-- * "setstepmul": sets `arg` as the new value for the *step multiplier* +-- of the collector (see 2.10). Returns the previous value for *step*. +-- +function collectgarbage(opt , arg) end + +--- +-- Opens the named file and executes its contents as a Lua chunk. When +-- called without arguments, +-- `dofile` executes the contents of the standard input (`stdin`). Returns +-- all values returned by the chunk. In case of errors, `dofile` propagates +-- the error to its caller (that is, `dofile` does not run in protected mode). +function dofile(filename) end + +--- +-- Terminates the last protected function called. +-- Returns `message` as the error message. +-- Function `error` never returns. +-- Usually, `error` adds some information about the error position at the +-- beginning of the message. The `level` argument specifies how to get the +-- error position. With level 1 (the default), the error position is where the +-- `error` function was called. Level 2 points the error to where the function +-- that called `error` was called; and so on. Passing a level 0 avoids the +-- addition of error position information to the message. +function error(message , level) end + +--- +-- A global variable (not a function) that holds the global environment +-- (that is, `_G._G = _G`). Lua itself does not use this variable; changing +-- its value does not affect any environment, nor vice-versa. (Set `__ENV` +-- to change environments in functions) +-- @table _G + +--- +-- If `object` does not have a metatable, returns nil. Otherwise, if the +-- object's metatable has a `"__metatable"` field, returns the associated +-- value. Otherwise, returns the metatable of the given object. +function getmetatable(object) end + +--- +-- For iterating over sequences. Returns three values: an iterator function, the table `t`, and 0, +-- so that the construction +-- for i,v in ipairs(t) do *body* end +-- will iterate over the pairs (`1,t[1]`), (`2,t[2]`), ..., up to the +-- first integer key absent from the table. +function ipairs(t) end + +--- +-- Loads a chunk. +-- If `ld` is a string, the chunk is this string. +-- If `ld` is a function, load calls it repeatedly to get the chunk pieces. Each call to `ld` must return a +-- string that concatenates with previous results. A return of an empty string, nil, or no value +-- signals the end of the chunk. +-- If there are no syntactic errors, returns the compiled chunk as a function; +-- otherwise, returns nil plus the error message. +-- If the resulting function has upvalues, the first upvalue is set to the value of the global environment or to `env`, +-- if that parameter is given. When loading main chunks, the first upvalue will be the`_ENV` variable (see 2.2). +-- `source` is used as the source of the chunk for error messages and debug information (see 4.9). +-- When absent, it defaults to `ld`, if `ld` is a string, or to "=(load)" otherwise. +-- The string `mode` controls whether the chunk can be text or binary (that is, a precompiled chunk). +-- It may be the string "b" (only binary chunks), "t" (only text chunks), or "bt" (both binary and text). +-- The default is "bt" +function load (ld , source , mode , env) end + +--- +-- Similar to `load`, but gets the chunk from file `filename`. Or from the +-- standard input, if no file name is given. +function loadfile (filename , mode , env) end + +--- +-- Allows a program to traverse all fields of a table. Its first argument is +-- a table and its second argument is an index in this table. `next` returns +-- the next index of the table and its associated value. +-- +-- When called with nil +-- as its second argument, `next` returns an initial index and its associated +-- value. When called with the last index, or with nil in an empty table, `next` +-- returns nil. +-- +-- If the second argument is absent, then it is interpreted as +-- nil. In particular, you can use `next(t)` to check whether a table is empty. +-- The order in which the indices are enumerated is not specified, *even for +-- numeric indices*. (To traverse a table in numeric order, use a numerical +-- for or the `ipairs` function.) +-- +-- The behavior of `next` is *undefined* if, during the traversal, you assign +-- any value to a non-existent field in the table. You may however modify +-- existing fields. In particular, you may clear existing fields. +function next(table , index) end + +--- +-- For iterating over all key-value pairs of a table. +-- Returns three values: the `next` function, the table `t`, and nil, +-- so that the construction +-- for k,v in pairs(t) do *body* end +-- will iterate over all key-value pairs of table `t`. +-- See function `next` for the caveats of modifying the table during its +-- traversal. +function pairs(t) end + +--- +-- Calls function `f` with the given arguments in *protected mode*. This +-- means that any error inside `f` is not propagated; instead, `pcall` catches +-- the error and returns a status code. Its first result is the status code (a +-- boolean), which is true if the call succeeds without errors. In such case, +-- `pcall` also returns all results from the call, after this first result. In +-- case of any error, `pcall` returns false plus the error message. +function pcall(f, arg1, ...) end + +--- +--Prints any number of values to `stdout`. +-- Uses the `tostring` function to convert them to strings. `print` is not +-- intended for formatted output, but only as a quick way to show a value, +-- typically for debugging. For formatted output, use `string.format`. +function print(...) end + +--- +-- Checks whether `v1` is equal to `v2`. Does not invoke any +-- metamethod. Returns a boolean. +function rawequal(v1, v2) end + +--- +-- Gets the real value of `table[index]`. Does not invoke any +-- metamethod. `table` must be a table; `index` may be any value. +function rawget(table, index) end + +--- +-- Sets the real value of `table[index]` to `value`. Does not invoke any +-- metamethod. `table` must be a table, `index` any value different from nil, +-- and `value` any Lua value. +-- This function returns `table`. +function rawset(table, index, value) end + +--- +-- Returns all arguments after argument number +-- `index`. Otherwise, `index` must be the string `"#"`, and `select` returns +-- the total number of extra arguments it received. +function select(index, ...) end + +--- +-- Sets the metatable for the given table. (You cannot change the metatable +-- of other types from Lua, only from C.) If `metatable` is nil, removes the +-- metatable of the given table. If the original metatable has a `"__metatable"` +-- field, raises an error. +-- This function returns `table`. +function setmetatable(table, metatable) end + +--- +-- Tries to convert its argument to a number. If the argument is already +-- a number or a string convertible to a number, then `tonumber` returns this +-- number; otherwise, it returns nil. +-- An optional argument specifies the base to interpret the numeral. The base +-- may be any integer between 2 and 36, inclusive. In bases above 10, the +-- letter '`A`' (in either upper or lower case) represents 10, '`B`' represents +-- 11, and so forth, with '`Z`' representing 35. In base 10 (the default), +-- the number can have a decimal part, as well as an optional exponent part +-- (see 2.1). In other bases, only unsigned integers are accepted. +function tonumber(e , base) end + +--- +-- Converts any value to a string in a reasonable format. +-- For complete control of how numbers are converted, use `string.format`. +-- If the metatable of `e` has a `"__tostring"` field, then `tostring` calls +-- the corresponding value with `e` as argument, and uses the result of the +-- call as its result. +function tostring(e) end + +--- +-- Returns the type of its only argument, coded as a string. The possible +-- results of this function are " +-- `nil`" (a string, not the value nil), "`number`", "`string`", "`boolean`", +-- "`table`", "`function`", "`thread`", and "`userdata`". +function type(v) end + +--- +-- A global variable (not a function) that holds a string containing the +-- current interpreter version. The current contents of this variable is +-- "`Lua 5.1`". +-- @table _VERSION + +--- +-- This function is similar to `pcall`, except that you can set a new +-- error handler. +-- `xpcall` calls function `f` in protected mode, using `err` as the error +-- handler. Any error inside `f` is not propagated; instead, `xpcall` catches +-- the error, calls the `err` function with the original error object, and +-- returns a status code. Its first result is the status code (a boolean), +-- which is true if the call succeeds without errors. In this case, `xpcall` +-- also returns all results from the call, after this first result. In case +-- of any error, `xpcall` returns false plus the result from `err`. +function xpcall(f, err) end + +--- +-- Loads the given module. The function starts by looking into the +-- `package.loaded` table to determine whether `modname` is already +-- loaded. If it is, then `require` returns the value stored at +-- `package.loaded[modname]`. Otherwise, it tries to find a *loader* for +-- the module. +-- To find a loader, `require` is guided by the `package.loaders` array. By +-- changing this array, we can change how `require` looks for a module. The +-- following explanation is based on the default configuration for +-- `package.loaders`. +-- First `require` queries `package.preload[modname]`. If it has a value, +-- this value (which should be a function) is the loader. Otherwise `require` +-- searches for a Lua loader using the path stored in `package.path`. If +-- that also fails, it searches for a C loader using the path stored in +-- `package.cpath`. If that also fails, it tries an *all-in-one* loader (see +-- `package.loaders`). +-- Once a loader is found, `require` calls the loader with a single argument, +-- `modname`. If the loader returns any value, `require` assigns the returned +-- value to `package.loaded[modname]`. If the loader returns no value and +-- has not assigned any value to `package.loaded[modname]`, then `require` +-- assigns true to this entry. In any case, `require` returns the final value +-- of `package.loaded[modname]`. +-- If there is any error loading or running the module, or if it cannot find +-- any loader for the module, then `require` signals an error. +function require(modname) end + diff --git a/Documentation/compiler/ldoc/ldoc/builtin/globals.lua b/Documentation/compiler/ldoc/ldoc/builtin/globals.lua new file mode 100644 index 000000000..e86903205 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/globals.lua @@ -0,0 +1,186 @@ +------- +-- global functions and tables +local tools = require 'ldoc.tools' +local globals = {} +local lua52 = _VERSION:match '5.2' +local lua53 = _VERSION:match '5.3' +local lua54 = _VERSION:match '5.4' + + +globals.functions = { + assert = true, + collectgarbage = true, + dofile = true, + error = true, + getmetatable = true, + setmetatable = true, + pairs = true, + ipairs = true, + load = true, + loadfile = true, + loadstring = true, + next = true, + pcall = true, + print = true, + rawequal = true, + rawget = true, + rawset = true, + select = true, + tonumber = true, + tostring = true, + type = true, + xpcall = true, + module = true, + require = true, +} +local functions = globals.functions + +if lua54 then + functions.warn = true + functions.rawlen = true +elseif lua52 or lua53 then + functions.rawlen = true +else + functions.setfenv = true + functions.getfenv = true + functions.unpack = true +end + +local manual, fun_ref + +function globals.set_manual_url(url) + manual = url .. '#' + fun_ref = manual..'pdf-' +end + +if lua54 then + globals.tables = { + io = '6.8', + package = '6.3', + math = '6.7', + os = '6.9', + string = '6.4', + table = '6.6', + coroutine = '6.2', + debug = '6.10' + } + globals.set_manual_url 'https://www.lua.org/manual/5.4/manual.html' +elseif lua53 then + globals.tables = { + io = '6.8', + package = '6.3', + math = '6.7', + os = '6.9', + string = '6.4', + table = '6.6', + coroutine = '6.2', + debug = '6.10' + } + globals.set_manual_url 'https://www.lua.org/manual/5.3/manual.html' +elseif lua52 then + globals.tables = { + io = '6.8', + package = '6.3', + math = '6.6', + os = '6.9', + string = '6.4', + table = '6.5', + coroutine = '6.2', + debug = '6.10' + } + globals.set_manual_url 'https://www.lua.org/manual/5.2/manual.html' +else + globals.tables = { + io = '5.7', + package = '5.3', + math = '5.6', + os = '5.8', + string = '5.4', + table = '5.5', + coroutine = '5.2', + debug = '5.9' + } + globals.set_manual_url 'https://www.lua.org/manual/5.1/manual.html' +end + +local file_methods = { + close = true, + flush = true, + lines = true, + read = true, + seek = true, + setvbuf = true, + write = true, +} + +-- external libs tracked by LDoc using LDoc style +local xlibs = { + lfs='lfs.html', lpeg='lpeg.html', +} +local xlib_url = 'http://stevedonovan.github.io/lua-stdlibs/modules/' + +local tables = globals.tables + +local function function_ref (name,tbl) + local href + if not tbl then -- can only be a standard Lua global function + if globals.functions[name] then + return {href = fun_ref..name, label = name} + else + return nil + end + end + if tbl == 'file' then -- special case: file objects! + if not file_methods[name] then + return nil + end + name = 'file:'..name + href = fun_ref..name + elseif tables[tbl] then -- function inside standard Lua table + local t = rawget(_G,tbl) -- do a quick sanity check + if not rawget(t,name) then + return nil + end + name = tbl..'.'..name + href = fun_ref..name + elseif xlibs[tbl] then -- in external libs, use LDoc style + local t = require('ldoc.builtin.'..tbl) + if not rawget(t,name) then + return nil + end + href = xlib_url..xlibs[tbl]..'#'..name + name = tbl..'.'..name + else + return nil + end + return {href = href, label = name} +end + +local function module_ref (tbl) + local href + if tables[tbl] ~= nil then -- standard Lua table + href = manual..tables[tbl] + elseif xlibs[tbl] then -- external lib + href = xlib_url..xlibs[tbl] + else + return nil + end + return {href = href, label = tbl} +end + +function globals.lua_manual_ref (name) + local tbl,fname = tools.split_dotted_name(name) + local ref + if not tbl then -- plain symbol + ref = function_ref(name) + if ref then return ref end + ref = module_ref(name) + if ref then return ref end + else + ref = function_ref(fname,tbl) + if ref then return ref end + end + return nil +end + +return globals diff --git a/Documentation/compiler/ldoc/ldoc/builtin/io.lua b/Documentation/compiler/ldoc/ldoc/builtin/io.lua new file mode 100644 index 000000000..c97cb4e85 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/io.lua @@ -0,0 +1,162 @@ +--- Reading and Writing Files. +-- @module io + +local io = {} + +-- luacheck: ignore 241 +local file = {} + +--- +-- Equivalent to `file:close()`. Without a `file`, closes the default +-- output file. +function io.close(file) end + +--- +-- Equivalent to `file:flush` over the default output file. +function io.flush() end + +--- +-- When called with a file name, it opens the named file (in text mode), +-- and sets its handle as the default input file. When called with a file +-- handle, it simply sets this file handle as the default input file. When +-- called without parameters, it returns the current default input file. +-- In case of errors this function raises the error, instead of returning an +-- error code. +function io.input(file) end + +--- +-- Opens the given file name in read mode and returns an iterator function +-- that, each time it is called, returns a new line from the file. Therefore, +-- the construction +-- for line in io.lines(filename) do *body* end +-- will iterate over all lines of the file. When the iterator function detects +-- the end of file, it returns nil (to finish the loop) and automatically +-- closes the file. +-- The call `io.lines()` (with no file name) is equivalent to +-- `io.input():lines()`; that is, it iterates over the lines of the default +-- input file. In this case it does not close the file when the loop ends. +function io.lines(filename) end + +--- +-- This function opens a file, in the mode specified in the string `mode`. It +-- returns a new file handle, or, in case of errors, nil plus an error message. +-- The `mode` string can be any of the following: +-- "r": read mode (the default); +-- "w": write mode; +-- "a": append mode; +-- "r+": update mode, all previous data is preserved; +-- "w+": update mode, all previous data is erased; +-- "a+": append update mode, previous data is preserved, writing is only +-- allowed at the end of file. +-- The `mode` string can also have a '`b`' at the end, which is needed in +-- some systems to open the file in binary mode. This string is exactly what +-- is used in the standard C function `fopen`. +function io.open(filename , mode) end + +--- +-- Similar to `io.input`, but operates over the default output file. +function io.output(file) end + +--- +-- Starts program `prog` in a separated process and returns a file handle +-- that you can use to read data from this program (if `mode` is `"r"`, +-- the default) or to write data to this program (if `mode` is `"w"`). +-- This function is system dependent and is not available on all platforms. +function io.popen(prog , mode) end + +--- +-- Equivalent to `io.input():read`. +function io.read(...) end + +-- * `io.stderr`: Standard error. +-- * `io.stdin`: Standard in. +-- * `io.stdout`: Standard out. + +--- +-- Returns a handle for a temporary file. This file is opened in update +-- mode and it is automatically removed when the program ends. +function io.tmpfile() end + +--- +-- Checks whether `obj` is a valid file handle. Returns the string `"file"` +-- if `obj` is an open file handle, `"closed file"` if `obj` is a closed file +-- handle, or nil if `obj` is not a file handle. +function io.type(obj) end + +--- +-- Equivalent to `io.output():write`. +function io.write(...) end + +--- +-- Closes `file`. Note that files are automatically closed when their +-- handles are garbage collected, but that takes an unpredictable amount of +-- time to happen. +function file:close() end + +--- +-- Saves any written data to `file`. +function file:flush() end + +--- +-- Returns an iterator function that, each time it is called, returns a +-- new line from the file. Therefore, the construction +-- for line in file:lines() do *body* end +-- will iterate over all lines of the file. (Unlike `io.lines`, this function +-- does not close the file when the loop ends.) +function file:lines() end + +--- +-- Reads the file `file`, according to the given formats, which specify +-- what to read. For each format, the function returns a string (or a number) +-- with the characters read, or nil if it cannot read data with the specified +-- format. When called without formats, it uses a default format that reads +-- the entire next line (see below). +-- The available formats are +-- "*n": reads a number; this is the only format that returns a number +-- instead of a string. +-- "*a": reads the whole file, starting at the current position. On end of +-- file, it returns the empty string. +-- "*l": reads the next line (skipping the end of line), returning nil on +-- end of file. This is the default format. +-- *number*: reads a string with up to this number of characters, returning +-- nil on end of file. If number is zero, it reads nothing and returns an +-- empty string, or nil on end of file. +function file:read(...) end + +--- +-- Sets and gets the file position, measured from the beginning of the +-- file, to the position given by `offset` plus a base specified by the string +-- `whence`, as follows: +-- "set": base is position 0 (beginning of the file); +-- "cur": base is current position; +-- "end": base is end of file; +-- In case of success, function `seek` returns the final file position, +-- measured in bytes from the beginning of the file. If this function fails, +-- it returns nil, plus a string describing the error. +-- The default value for `whence` is `"cur"`, and for `offset` is 0. Therefore, +-- the call `file:seek()` returns the current file position, without changing +-- it; the call `file:seek("set")` sets the position to the beginning of the +-- file (and returns 0); and the call `file:seek("end")` sets the position +-- to the end of the file, and returns its size. +function file:seek(whence , offset) end + +--- +-- Sets the buffering mode for an output file. There are three available +-- modes: +-- +-- * "no": no buffering; the result of any output operation appears immediately. +-- * "full": full buffering; output operation is performed only when the +-- buffer is full (or when you explicitly `flush` the file (see `io.flush`)). +-- * "line": line buffering; output is buffered until a newline is output or +-- there is any input from some special files (such as a terminal device). +-- For the last two cases, `size` specifies the size of the buffer, in +-- bytes. The default is an appropriate size. +function file:setvbuf(mode , size) end + +--- +-- Writes the value of each of its arguments to the `file`. The arguments +-- must be strings or numbers. To write other values, use `tostring` or +-- `string.format` before `write`. +function file:write(...) end + +return io diff --git a/Documentation/compiler/ldoc/ldoc/builtin/lfs.lua b/Documentation/compiler/ldoc/ldoc/builtin/lfs.lua new file mode 100644 index 000000000..4f692faa0 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/lfs.lua @@ -0,0 +1,125 @@ +--- File and Directory manipulation +-- @module lfs + +local lfs = {} + +--- +-- Returns a table with the file attributes corresponding to filepath (or nil +-- followed by an error message in case of error). If the second optional +-- argument is given, then only the value of the named attribute is returned +-- (this use is equivalent to lfs.attributes(filepath).aname, but the table is +-- not created and only one attribute is retrieved from the O.S.). The +-- attributes are described as follows; attribute mode is a string, all the +-- others are numbers, and the time related attributes use the same time +-- reference of os.time: +-- +-- - dev: on Unix systems, this represents the device that the inode resides on. +-- On Windows systems, represents the drive number of the disk containing +-- the file +-- - ino: on Unix systems, this represents the inode number. On Windows systems +-- this has no meaning +-- - mode: string representing the associated protection mode (the values could +-- be file, directory, link, socket, named pipe, char device, block +-- device or other) +-- - nlink: number of hard links to the file +-- - uid: user-id of owner (Unix only, always 0 on Windows) +-- - gid: group-id of owner (Unix only, always 0 on Windows) +-- - rdev: on Unix systems, represents the device type, for special file inodes. +-- On Windows systems represents the same as dev +-- - access: time of last access +-- - modification: time of last data modification +-- - change: time of last file status change +-- - size: file size, in bytes +-- - blocks: block allocated for file; (Unix only) +-- - blksize: optimal file system I/O blocksize; (Unix only) +-- This function uses stat internally thus if the given filepath is a symbolic +-- link, it is followed (if it points to another link the chain is followed +-- recursively) and the information is about the file it refers to. To obtain +-- information about the link itself, see function lfs.symlinkattributes. +function lfs.attributes(filepath , aname) end + +--- +-- Changes the current working directory to the given path. +-- Returns true in case of success or nil plus an error string. +function lfs.chdir(path) end + +--- +-- Creates a lockfile (called lockfile.lfs) in path if it does not exist and +-- returns the lock. If the lock already exists checks it it's stale, using the +-- second parameter (default for the second parameter is INT_MAX, which in +-- practice means the lock will never be stale. To free the the lock call +-- lock:free(). +-- In case of any errors it returns nil and the error message. In particular, +-- if the lock exists and is not stale it returns the "File exists" message. +function lfs.lock_dir(path, seconds_stale) end + +--- +-- Returns a string with the current working directory or nil plus an error +-- string. +function lfs.currentdir() end + +--- +-- Lua iterator over the entries of a given directory. Each time the iterator is +-- called with dir_obj it returns a directory entry's name as a string, or nil +-- if there are no more entries. You can also iterate by calling `dir_obj:next()`, +-- and explicitly close the directory before the iteration finished with +-- `dir_obj:close()`. Raises an error if path is not a directory. +function lfs.dir(path) end + +--- +-- Locks a file or a part of it. This function works on open files; the file +-- handle should be specified as the first argument. The string mode could be +-- either r (for a read/shared lock) or w (for a write/exclusive lock). The +-- optional arguments start and length can be used to specify a starting point +-- and its length; both should be numbers. +-- Returns true if the operation was successful; in case of error, it returns +-- nil plus an error string. +function lfs.lock(filehandle, mode, start, length) end + +--- +-- Creates a new directory. The argument is the name of the new directory. +-- Returns true if the operation was successful; in case of error, it returns +-- nil plus an error string. +function lfs.mkdir(dirname) end + +--- +-- Removes an existing directory. The argument is the name of the directory. +-- Returns true if the operation was successful; in case of error, it returns +-- nil plus an error string. +function lfs.rmdir(dirname) end + +--- +-- Sets the writing mode for a file. The mode string can be either binary or +-- text. Returns the previous mode string for the file. This function is only +-- available in Windows, so you may want to make sure that lfs.setmode exists +-- before using it. +function lfs.setmode(file, mode) end + +--- +-- Identical to lfs.attributes except that it obtains information about the link +-- itself (not the file it refers to). This function is not available in Windows +-- so you may want to make sure that lfs.symlinkattributes exists before using +-- it. +function lfs.symlinkattributes(filepath , aname) end + +--- +-- Set access and modification times of a file. This function is a bind to utime +-- function. The first argument is the filename, the second argument (atime) is +-- the access time, and the third argument (mtime) is the modification time. +-- Both times are provided in seconds (which should be generated with Lua +-- standard function os.time). If the modification time is omitted, the access +-- time provided is used; if both times are omitted, the current time is used. +-- Returns true if the operation was successful; in case of error, it returns +-- nil plus an error string. +function lfs.touch(filepath , atime , mtime) end + +--- +-- Unlocks a file or a part of it. This function works on open files; the file +-- handle should be specified as the first argument. The optional arguments +-- start and length can be used to specify a starting point and its length; both +-- should be numbers. +-- Returns true if the operation was successful; in case of error, it returns +-- nil plus an error string. +function lfs.unlock(filehandle, start, length) end + +return lfs diff --git a/Documentation/compiler/ldoc/ldoc/builtin/lpeg.lua b/Documentation/compiler/ldoc/ldoc/builtin/lpeg.lua new file mode 100644 index 000000000..f9ce14fc0 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/lpeg.lua @@ -0,0 +1,214 @@ +--- LPeg PEG pattern matching. +-- @module lpeg + +local lpeg = {} + +--- +-- The matching function. It attempts to match the given pattern against the +-- subject string. If the match succeeds, returns the index in the subject of +-- the first character after the match, or the captured values (if the pattern +-- captured any value). +-- +-- An optional numeric argument init makes the match starts at that position in +-- the subject string. As usual in Lua libraries, a negative value counts from +-- the end. +-- +-- Unlike typical pattern-matching functions, match works only in anchored mode; +-- that is, it tries to match the pattern with a prefix of the given subject +-- string (at position init), not with an arbitrary substring of the subject. +-- So, if we want to find a pattern anywhere in a string, we must either write a +-- loop in Lua or write a pattern that matches anywhere. This second approach is +-- easy and quite efficient; see examples. +function lpeg.match(pattern, subject , init) end + +--- +-- If the given value is a pattern, returns the string "pattern". Otherwise +-- returns nil. +function lpeg.type(value) end + +--- +-- Returns a string with the running version of LPeg. +function lpeg.version() end + +--- +-- Sets the maximum size for the backtrack stack used by LPeg to track calls and +-- choices. Most well-written patterns need little backtrack levels and +-- therefore you seldom need to change this maximum; but a few useful patterns +-- may need more space. Before changing this maximum you should try to rewrite +-- your pattern to avoid the need for extra space. +function lpeg.setmaxstack(max) end + +--- +-- Converts the given value into a proper pattern, according to the following +-- rules: +-- * If the argument is a pattern, it is returned unmodified. +-- * If the argument is a string, it is translated to a pattern that matches +-- literally the string. +-- * If the argument is a non-negative number n, the result is a pattern that +-- matches exactly n characters. +-- * If the argument is a negative number -n, the result is a pattern that +-- succeeds only if the input string does not have n characters: lpeg.P(-n) +-- is equivalent to -lpeg.P(n) (see the unary minus operation). +-- * If the argument is a boolean, the result is a pattern that always +-- succeeds or always fails (according to the boolean value), without +-- consuming any input. +-- * If the argument is a table, it is interpreted as a grammar (see +-- Grammars). +-- * If the argument is a function, returns a pattern equivalent to a +-- match-time capture over the empty string. +function lpeg.P(value) end + +--- +-- Returns a pattern that matches any single character belonging to one of the +-- given ranges. Each range is a string xy of length 2, representing all +-- characters with code between the codes of x and y (both inclusive). +-- As an example, the pattern `lpeg.R("09")` matches any digit, and `lpeg.R("az", +-- "AZ")` matches any ASCII letter. +function lpeg.R(range) end + +--- +-- Returns a pattern that matches any single character that appears in the given +-- string. (The S stands for Set.) +-- As an example, the pattern lpeg.S("+-*/") matches any arithmetic operator. +-- Note that, if s is a character (that is, a string of length 1), then +-- lpeg.P(s) is equivalent to lpeg.S(s) which is equivalent to lpeg.R(s..s). +-- Note also that both lpeg.S("") and lpeg.R() are patterns that always fail. +function lpeg.S(string) end + +--- +-- This operation creates a non-terminal (a variable) for a grammar. The created +-- non-terminal refers to the rule indexed by v in the enclosing grammar. (See +-- Grammars for details.) +function lpeg.V(v) end + +--- +-- Returns a table with patterns for matching some character classes according +-- to the current locale. The table has fields: +-- +-- * alnum +-- * alpha +-- * cntrl +-- * digit +-- * graph +-- * lower +-- * print +-- * punct +-- * space +-- * upper +-- * xdigit +-- +-- each one containing a +-- correspondent pattern. Each pattern matches any single character that belongs +-- to its class. +-- +-- If called with an argument table, then it creates those fields inside the +-- given table and returns that table. +function lpeg.locale(table) end + +--- +-- Creates a simple capture, which captures the substring of the subject that +-- matches patt. The captured value is a string. If patt has other captures, +-- their values are returned after this one. +function lpeg.C(patt) end + +--- +-- Creates an argument capture. This pattern matches the empty string and +-- produces the value given as the nth extra argument given in the call to +-- lpeg.match. +function lpeg.Carg(n) end + +--- +-- Creates a back capture. This pattern matches the empty string and produces +-- the values produced by the most recent group capture named name. +-- Most recent means the last complete outermost group capture with the given +-- name. A Complete capture means that the entire pattern corresponding to the +-- capture has matched. An Outermost capture means that the capture is not +-- inside another complete capture. +function lpeg.Cb(name) end + +--- +-- Creates a constant capture. This pattern matches the empty string and +-- produces all given values as its captured values. +function lpeg.Cc(...) end + +--- +-- Creates a fold capture. If patt produces a list of captures C1 C2 ... Cn, +-- this capture will produce the value func(...func(func(C1, C2), C3)..., Cn), +-- that is, it will fold (or accumulate, or reduce) the captures from patt using +-- function func. +-- +-- This capture assumes that patt should produce at least one capture with at +-- least one value (of any type), which becomes the initial value of an +-- accumulator. (If you need a specific initial value, you may prefix a constant +-- capture to patt.) For each subsequent capture LPeg calls func with this +-- accumulator as the first argument and all values produced by the capture as +-- extra arguments; the value returned by this call becomes the new value for +-- the accumulator. The final value of the accumulator becomes the captured +-- value. +-- +-- As an example, the following pattern matches a list of numbers separated by +-- commas and returns their addition: +-- +-- -- matches a numeral and captures its value +-- number = lpeg.R"09"^1 / tonumber +-- -- matches a list of numbers, captures their values +-- list = number * ("," * number)^0 +-- -- auxiliary function to add two numbers +-- function add (acc, newvalue) return acc + newvalue end +-- -- folds the list of numbers adding them +-- sum = lpeg.Cf(list, add) +-- -- example of use +-- print(sum:match("10,30,43")) --> 83 +-- +function lpeg.Cf(patt, func) end + +--- +-- Creates a group capture. It groups all values returned by patt into a single +-- capture. The group may be anonymous (if no name is given) or named with the +-- given name. +-- An anonymous group serves to join values from several captures into a single +-- capture. A named group has a different behavior. In most situations, a named +-- group returns no values at all. Its values are only relevant for a following +-- back capture or when used inside a table capture. +function lpeg.Cg(patt , name) end + +--- +-- Creates a position capture. It matches the empty string and captures the +-- position in the subject where the match occurs. The captured value is a +-- number. +function lpeg.Cp() end + +--- +-- Creates a substitution capture, which captures the substring of the subject +-- that matches patt, with substitutions. For any capture inside patt with a +-- value, the substring that matched the capture is replaced by the capture +-- value (which should be a string). The final captured value is the string +-- resulting from all replacements. +function lpeg.Cs(patt) end + +--- +-- Creates a table capture. This capture creates a table and puts all values +-- from all anonymous captures made by patt inside this table in successive +-- integer keys, starting at 1. Moreover, for each named capture group created +-- by patt, the first value of the group is put into the table with the group +-- name as its key. The captured value is only the table. +function lpeg.Ct(patt) end + +--- +-- Creates a match-time capture. Unlike all other captures, this one is +-- evaluated immediately when a match occurs. It forces the immediate evaluation +-- of all its nested captures and then calls func. +-- The given function gets as arguments the entire subject, the current position +-- (after the match of patt), plus any capture values produced by patt. +-- The first value returned by function defines how the match happens. If the +-- call returns a number, the match succeeds and the returned number becomes the +-- new current position. (Assuming a subject s and current position i, the +-- returned number must be in the range [i, len(s) + 1].) If the call returns +-- true, the match succeeds without consuming any input. (So, to return true is +-- equivalent to return i.) If the call returns false, nil, or no value, the +-- match fails. +-- Any extra values returned by the function become the values produced by the +-- capture. +function lpeg.Cmt(patt, func) end + +return lpeg diff --git a/Documentation/compiler/ldoc/ldoc/builtin/math.lua b/Documentation/compiler/ldoc/ldoc/builtin/math.lua new file mode 100644 index 000000000..3f83624c9 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/math.lua @@ -0,0 +1,144 @@ +--- standard mathematical functions. +-- @module math + +local math = {} + +--- +-- Returns the absolute value of `x`. +function math.abs(x) end + +--- +-- Returns the arc cosine of `x` (in radians). +function math.acos(x) end + +--- +-- Returns the arc sine of `x` (in radians). +function math.asin(x) end + +--- +-- Returns the arc tangent of `x` (in radians). +function math.atan(x) end + +--- +-- Returns the arc tangent of `y/x` (in radians), but uses the signs +-- of both parameters to find the quadrant of the result. (It also handles +-- correctly the case of `x` being zero.) +function math.atan2(y, x) end + +--- +-- Returns the smallest integer larger than or equal to `x`. +function math.ceil(x) end + +--- +-- Returns the cosine of `x` (assumed to be in radians). +function math.cos(x) end + +--- +-- Returns the hyperbolic cosine of `x`. +function math.cosh(x) end + +--- +-- Returns the angle `x` (given in radians) in degrees. +function math.deg(x) end + +--- +-- Returns the value *e^x*. +function math.exp(x) end + +--- +-- Returns the largest integer smaller than or equal to `x`. +function math.floor(x) end + +--- +-- Returns the remainder of the division of `x` by `y` that rounds the +-- quotient towards zero. +function math.fmod(x, y) end + +--- +-- Returns `m` and `e` such that *x = m2^e*, `e` is an integer and the +-- absolute value of `m` is in the range *[0.5, 1)* (or zero when `x` is zero). +function math.frexp(x) end + +--- +-- The value `HUGE_VAL`, a value larger than or equal to any other +-- numerical value. +-- function math.huge end +-- * `math.HUGE_VAL`: math.HUGE_VAL + +--- +-- Returns *m2^e* (`e` should be an integer). +function math.ldexp(m, e) end + +--- +-- Returns the natural logarithm of `x`. +function math.log(x) end + +--- +-- Returns the base-10 logarithm of `x`. +function math.log10(x) end + +--- +-- Returns the maximum value among its arguments. +function math.max(x, ...) end + +--- +-- Returns the minimum value among its arguments. +function math.min(x, ...) end + +--- +-- Returns two numbers, the integral part of `x` and the fractional part of +-- `x`. +function math.modf(x) end + +--- +-- The value of *pi*. +-- function math.pi end +-- * `math.pi`: math.pi + +--- +-- Returns *x^y*. (You can also use the expression `x^y` to compute this +-- value.) +function math.pow(x, y) end + +--- +-- Returns the angle `x` (given in degrees) in radians. +function math.rad(x) end + +--- +-- This function is an interface to the simple pseudo-random generator +-- function `rand` provided by ANSI C. (No guarantees can be given for its +-- statistical properties.) +-- When called without arguments, returns a uniform pseudo-random real +-- number in the range *[0,1)*. When called with an integer number `m`, +-- `math.random` returns a uniform pseudo-random integer in the range *[1, +-- m]*. When called with two integer numbers `m` and `n`, `math.random` +-- returns a uniform pseudo-random integer in the range *[m, n]*. +function math.random(m , n) end + +--- +-- Sets `x` as the "seed" for the pseudo-random generator: equal seeds +-- produce equal sequences of numbers. +function math.randomseed(x) end + +--- +-- Returns the sine of `x` (assumed to be in radians). +function math.sin(x) end + +--- +-- Returns the hyperbolic sine of `x`. +function math.sinh(x) end + +--- +-- Returns the square root of `x`. (You can also use the expression `x^0.5` +-- to compute this value.) +function math.sqrt(x) end + +--- +-- Returns the tangent of `x` (assumed to be in radians). +function math.tan(x) end + +--- +-- Returns the hyperbolic tangent of `x`. +function math.tanh(x) end + +return math diff --git a/Documentation/compiler/ldoc/ldoc/builtin/os.lua b/Documentation/compiler/ldoc/ldoc/builtin/os.lua new file mode 100644 index 000000000..3243b4ebf --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/os.lua @@ -0,0 +1,112 @@ +--- Operating System facilities like date, time and program execution. +-- @module os + +local os = {} + +--- +-- Returns an approximation of the amount in seconds of CPU time used by +-- the program. +function os.clock() end + +--- +-- Returns a string or a table containing date and time, formatted according +-- to the given string `format`. +-- +-- If the `time` argument is present, this is the time to be formatted +-- (see the `os.time` function for a description of this value). Otherwise, +-- `date` formats the current time. +-- +-- If `format` starts with '`!`', then the date is formatted in Coordinated +-- Universal Time. After this optional character, if `format` is the string +-- "`*t`", then `date` returns a table with the following fields: +-- +-- * `year` (four digits) +-- * `month` (1--12) +-- * `day` (1--31) +-- * `hour` (0--23) +-- * `min` (0--59) +-- * `sec` (0--61) +-- * `wday` (weekday, Sunday is 1) +-- * `yday` (day of the year) +-- * `isdst` (daylight saving flag, a boolean). +-- +-- If `format` is not "`*t`", then `date` returns the date as a string, +-- formatted according to the same rules as the C function `strftime`. +-- When called without arguments, `date` returns a reasonable date and time +-- representation that depends on the host system and on the current locale +-- (that is, `os.date()` is equivalent to `os.date("%c")`). +function os.date(format , time) end + +--- +-- Returns the number of seconds from time `t1` to time `t2`. In POSIX, +-- Windows, and some other systems, this value is exactly `t2`*-*`t1`. +function os.difftime(t2, t1) end + +--- +-- This function is equivalent to the C function `system`. It passes +-- `command` to be executed by an operating system shell. It returns a status +-- code, which is system-dependent. If `command` is absent, then it returns +-- nonzero if a shell is available and zero otherwise. +function os.execute(command) end + +--- +-- Calls the C function `exit`, with an optional `code`, to terminate the +-- host program. The default value for `code` is the success code. +function os.exit(code) end + +--- +-- Returns the value of the process environment variable `varname`, or +-- nil if the variable is not defined. +function os.getenv(varname) end + +--- +-- Deletes the file or directory with the given name. Directories must be +-- empty to be removed. If this function fails, it returns nil, plus a string +-- describing the error. +function os.remove(filename) end + +--- +-- Renames file or directory named `oldname` to `newname`. If this function +-- fails, it returns nil, plus a string describing the error. +function os.rename(oldname, newname) end + +--- +-- Sets the current locale of the program. `locale` is a string specifying +-- a locale; `category` is an optional string describing which category to +-- change: `"all"`, `"collate"`, `"ctype"`, `"monetary"`, `"numeric"`, or +-- `"time"`; the default category is `"all"`. The function returns the name +-- of the new locale, or nil if the request cannot be honored. +-- If `locale` is the empty string, the current locale is set to an +-- implementation-defined native locale. If `locale` is the string "`C`", +-- the current locale is set to the standard C locale. +-- When called with nil as the first argument, this function only returns +-- the name of the current locale for the given category. +function os.setlocale(locale , category) end + +--- +-- Returns the current time when called without arguments, or a time +-- representing the date and time specified by the given table. This table +-- must have fields `year`, `month`, and `day`, and may have fields `hour`, +-- `min`, `sec`, and `isdst` (for a description of these fields, see the +-- `os.date` function). +-- The returned value is a number, whose meaning depends on your system. In +-- POSIX, Windows, and some other systems, this number counts the number +-- of seconds since some given start time (the "epoch"). In other systems, +-- the meaning is not specified, and the number returned by `time` can be +-- used only as an argument to `date` and `difftime`. +function os.time(table) end + +--- +-- Returns a string with a file name that can be used for a temporary +-- file. The file must be explicitly opened before its use and explicitly +-- removed when no longer needed. +-- On some systems (POSIX), this function also creates a file with that +-- name, to avoid security risks. (Someone else might create the file with +-- wrong permissions in the time between getting the name and creating the +-- file.) You still have to open the file to use it and to remove it (even +-- if you do not use it). +-- When possible, you may prefer to use `io.tmpfile`, which automatically +-- removes the file when the program ends. +function os.tmpname() end + +return os diff --git a/Documentation/compiler/ldoc/ldoc/builtin/package.lua b/Documentation/compiler/ldoc/ldoc/builtin/package.lua new file mode 100644 index 000000000..a7ff40837 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/package.lua @@ -0,0 +1,97 @@ +--- controlling how `require` finds packages. +-- @module package + +local package = {} + +--- +-- The path used by `require` to search for a C loader. +-- Lua initializes the C path `package.cpath` in the same way it initializes +-- the Lua path `package.path`, using the environment variable `LUA_CPATH` +-- or a default path defined in `luaconf.h`. +-- function package.cpath end +-- * `package.cpath`: package.cpath + +--- +-- A table used by `require` to control which modules are already +-- loaded. When you require a module `modname` and `package.loaded[modname]` +-- is not false, `require` simply returns the value stored there. +-- function package.loaded end +-- * `package.loaded`: package.loaded + +--- +-- A table used by `require` to control how to load modules. +-- Each entry in this table is a *searcher function*. When looking for a module, +-- `require` calls each of these searchers in ascending order, with the module +-- name (the argument given to `require`) as its sole parameter. The function +-- can return another function (the module *loader*) or a string explaining +-- why it did not find that module (or nil if it has nothing to say). Lua +-- initializes this table with four functions. +-- The first searcher simply looks for a loader in the `package.preload` table. +-- The second searcher looks for a loader as a Lua library, using the path +-- stored at `package.path`. A path is a sequence of *templates* separated by +-- semicolons. For each template, the searcher will change each interrogation +-- mark in the template by `filename`, which is the module name with each dot +-- replaced by a "directory separator" (such as "`/`" in Unix); then it will +-- try to open the resulting file name. So, for instance, if the Lua path is +-- the string +-- "./?.lua;./?.lc;/usr/local/?/init.lua" +-- the search for a Lua file for module `foo` will try to open the files +-- `./foo.lua`, `./foo.lc`, and `/usr/local/foo/init.lua`, in that order. +-- The third searcher looks for a loader as a C library, using the path given +-- by the variable `package.cpath`. For instance, if the C path is the string +-- "./?.so;./?.dll;/usr/local/?/init.so" +-- the searcher for module `foo` will try to open the files `./foo.so`, +-- `./foo.dll`, and `/usr/local/foo/init.so`, in that order. Once it finds +-- a C library, this searcher first uses a dynamic link facility to link the +-- application with the library. Then it tries to find a C function inside the +-- library to be used as the loader. The name of this C function is the string +-- "`luaopen_`" concatenated with a copy of the module name where each dot +-- is replaced by an underscore. Moreover, if the module name has a hyphen, +-- its prefix up to (and including) the first hyphen is removed. For instance, +-- if the module name is `a.v1-b.c`, the function name will be `luaopen_b_c`. +-- The fourth searcher tries an *all-in-one loader*. It searches the C +-- path for a library for the root name of the given module. For instance, +-- when requiring `a.b.c`, it will search for a C library for `a`. If found, +-- it looks into it for an open function for the submodule; in our example, +-- that would be `luaopen_a_b_c`. With this facility, a package can pack +-- several C submodules into one single library, with each submodule keeping +-- its original open function. +-- function package.loaders end +-- * `package.loaders`: package.loaders + +--- +-- Dynamically links the host program with the C library `libname`. Inside +-- this library, looks for a function `funcname` and returns this function as a +-- C function. (So, `funcname` must follow the protocol (see `lua_CFunction`)). +-- This is a low-level function. It completely bypasses the package and module +-- system. Unlike `require`, it does not perform any path searching and does +-- not automatically adds extensions. `libname` must be the complete file name +-- of the C library, including if necessary a path and extension. `funcname` +-- must be the exact name exported by the C library (which may depend on the +-- C compiler and linker used). +-- This function is not supported by ANSI C. As such, it is only available +-- on some platforms (Windows, Linux, Mac OS X, Solaris, BSD, plus other Unix +-- systems that support the `dlfcn` standard). +function package.loadlib(libname, funcname) end + +--- +-- The path used by `require` to search for a Lua loader. +-- At start-up, Lua initializes this variable with the value of the environment +-- variable `LUA_PATH` or with a default path defined in `luaconf.h`, if +-- the environment variable is not defined. Any "`;;`" in the value of the +-- environment variable is replaced by the default path. +-- function package.path end +-- * `package.path`: package.path + +--- +-- A table to store loaders for specific modules (see `require`). +-- function package.preload end +-- * `package.preload`: package.preload + +--- +-- Sets a metatable for `module` with its `__index` field referring to the +-- global environment, so that this module inherits values from the global +-- environment. To be used as an option to function `module`. +function package.seeall(module) end + +return package diff --git a/Documentation/compiler/ldoc/ldoc/builtin/string.lua b/Documentation/compiler/ldoc/ldoc/builtin/string.lua new file mode 100644 index 000000000..cb4d2ebf9 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/string.lua @@ -0,0 +1,191 @@ +--- string operations like searching and matching. +-- @module string + +local string = {} + +--- +-- Returns the internal numerical codes of the characters `s[i]`, `s[i+1]`, +-- ..., `s[j]`. The default value for `i` is 1; the default value for `j` +-- is `i`. +-- Note that numerical codes are not necessarily portable across platforms. +function string.byte(s , i , j) end + +--- +-- Receives zero or more integers. Returns a string with length equal to +-- the number of arguments, in which each character has the internal numerical +-- code equal to its corresponding argument. +-- Note that numerical codes are not necessarily portable across platforms. +function string.char(...) end + +--- +-- Returns a string containing a binary representation of the given +-- function, so that a later `loadstring` on this string returns a copy of +-- the function. `function` must be a Lua function without upvalues. +function string.dump(func) end + +--- +-- Looks for the first match of `pattern` in the string `s`. If it finds a +-- match, then `find` returns the indices of `s` where this occurrence starts +-- and ends; otherwise, it returns nil. A third, optional numerical argument +-- `init` specifies where to start the search; its default value is 1 and +-- can be negative. A value of true as a fourth, optional argument `plain` +-- turns off the pattern matching facilities, so the function does a plain +-- "find substring" operation, with no characters in `pattern` being considered +-- "magic". Note that if `plain` is given, then `init` must be given as well. +-- If the pattern has captures, then in a successful match the captured values +-- are also returned, after the two indices. +function string.find(s, pattern , init , plain) end + +--- +-- Returns a formatted version of its variable number of arguments following +-- the description given in its first argument (which must be a string). The +-- format string follows the same rules as the `printf` family of standard C +-- functions. The only differences are that the options/modifiers `*`, `l`, +-- `L`, `n`, `p`, and `h` are not supported and that there is an extra option, +-- `q`. The `q` option formats a string in a form suitable to be safely read +-- back by the Lua interpreter: the string is written between double quotes, +-- and all double quotes, newlines, embedded zeros, and backslashes in the +-- string are correctly escaped when written. For instance, the call +-- +-- string.format('%q', 'a string with "quotes" and \n new line') +-- +-- will produce the string: +-- +-- "a string with \"quotes\" and \ +-- new line" +-- +-- The options `c`, `d`, `E`, `e`, `f`, `g`, `G`, `i`, `o`, `u`, `X`, and +-- `x` all expect a number as argument, whereas `q` and `s` expect a string. +-- This function does not accept string values containing embedded zeros, +-- except as arguments to the `q` option. +function string.format(formatstring, ...) end + +--- +-- Returns an iterator function that, each time it is called, returns the +-- next captures from `pattern` over string `s`. If `pattern` specifies no +-- captures, then the whole match is produced in each call. +-- As an example, the following loop +-- +-- s = "hello world from Lua" +-- for w in string.gmatch(s, "%a+") do +-- print(w) +-- end +-- +-- will iterate over all the words from string `s`, printing one per line. The +-- next example collects all pairs `key=value` from the given string into +-- a table: +-- +-- t = {} +-- s = "from=world, to=Lua" +-- for k, v in string.gmatch(s, "(%w+)=(%w+)") do +-- t[k] = v +-- end +-- +-- For this function, a '`^`' at the start of a pattern does not work as an +-- anchor, as this would prevent the iteration. +function string.gmatch(s, pattern) end + +--- +-- Returns a copy of `s` in which all (or the first `n`, if given) +-- occurrences of the `pattern` have been replaced by a replacement string +-- specified by `repl`, which can be a string, a table, or a function. `gsub` +-- also returns, as its second value, the total number of matches that occurred. +-- +-- If `repl` is a string, then its value is used for replacement. The character +-- `%` works as an escape character: any sequence in `repl` of the form `%n`, +-- with *n* between 1 and 9, stands for the value of the *n*-th captured +-- substring (see below). The sequence `%0` stands for the whole match. The +-- sequence `%%` stands for a single `%`. +-- +-- If `repl` is a table, then the table is queried for every match, using +-- the first capture as the key; if the pattern specifies no captures, then +-- the whole match is used as the key. +-- +-- If `repl` is a function, then this function is called every time a match +-- occurs, with all captured substrings passed as arguments, in order; if +-- the pattern specifies no captures, then the whole match is passed as a +-- sole argument. +-- +-- If the value returned by the table query or by the function call is a +-- string or a number, then it is used as the replacement string; otherwise, +-- if it is false or nil, then there is no replacement (that is, the original +-- match is kept in the string). +-- +-- Here are some examples: +-- x = string.gsub("hello world", "(%w+)", "%1 %1") +-- --> x="hello hello world world" +-- x = string.gsub("hello world", "%w+", "%0 %0", 1) +-- --> x="hello hello world" +-- x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") +-- --> x="world hello Lua from" +-- x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv) +-- --> x="home = /home/roberto, user = roberto" +-- x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s) +-- return loadstring(s)() +-- end) +-- --> x="4+5 = 9" +-- local t = {name="lua", version="5.1"} +-- x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t) +-- --> x="lua-5.1.tar.gz" +function string.gsub(s, pattern, repl , n) end + +--- +-- Receives a string and returns its length. The empty string `""` has +-- length 0. Embedded zeros are counted, so `"a\000bc\000"` has length 5. +function string.len(s) end + +--- +-- Receives a string and returns a copy of this string with all uppercase +-- letters changed to lowercase. All other characters are left unchanged. The +-- definition of what an uppercase letter is depends on the current locale. +function string.lower(s) end + +--- +-- Looks for the first *match* of `pattern` in the string `s`. If it +-- finds one, then `match` returns the captures from the pattern; otherwise +-- it returns nil. If `pattern` specifies no captures, then the whole match +-- is returned. A third, optional numerical argument `init` specifies where +-- to start the search; its default value is 1 and can be negative. +function string.match(s, pattern , init) end + +--- +-- Returns a string that is the concatenation of `n` copies of the string +-- `s`. +function string.rep(s, n) end + +--- +-- Returns a string that is the string `s` reversed. +function string.reverse(s) end + +--- +-- Returns the substring of `s` that starts at `i` and continues until +-- `j`; `i` and `j` can be negative. If `j` is absent, then it is assumed to +-- be equal to -1 (which is the same as the string length). In particular, +-- the call `string.sub(s,1,j)` returns a prefix of `s` with length `j`, and +-- `string.sub(s, -i)` returns a suffix of `s` with length `i`. +function string.sub(s, i , j) end + +--- +-- Receives a string and returns a copy of this string with all lowercase +-- letters changed to uppercase. All other characters are left unchanged. The +-- definition of what a lowercase letter is depends on the current locale. +function string.upper(s) end + +--- +-- (5.3) Returns a binary string containing the values v1, v2, etc. packed (that is, serialized in binary form) +--- according to the format string fmt (see 6.4.2). +function string.pack (fmt, v1, v2, ...) end + +--- +-- (5.3) Returns the size of a string resulting from string.pack with the given format. +-- The format string cannot have the variable-length options 's' or 'z' (see 6.4.2). +function string.packsize (fmt) end + +--- +-- (5.3) Returns the values packed in string s (see string.pack) according to the format string fmt (see 6.4.2). +-- An optional pos marks where to start reading in s (default is 1) +-- After the read values, this function also returns the index of the first unread byte in s. +function string.unpack (fmt, s , pos) end + +return string + diff --git a/Documentation/compiler/ldoc/ldoc/builtin/table.lua b/Documentation/compiler/ldoc/ldoc/builtin/table.lua new file mode 100644 index 000000000..92704e7e7 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/table.lua @@ -0,0 +1,52 @@ +--- manipulating Lua tables. +-- @module table + +local table = {} + +--- +-- Given an array where all elements are strings or numbers, returns +-- `table[i]..sep..table[i+1] ... sep..table[j]`. The default value for +-- `sep` is the empty string, the default for `i` is 1, and the default for +-- `j` is the length of the table. If `i` is greater than `j`, returns the +-- empty string. +function table.concat(table , sep , i , j) end + +--- +-- Inserts element `value` at position `pos` in `table`, shifting up +-- other elements to open space, if necessary. The default value for `pos` is +-- `n+1`, where `n` is the length of the table (see ยง2.5.5), so that a call +-- `table.insert(t,x)` inserts `x` at the end of table `t`. +function table.insert(table, pos, value) end + +--- +-- Removes from `table` the element at position `pos`, shifting down other +-- elements to close the space, if necessary. Returns the value of the removed +-- element. The default value for `pos` is `n`, where `n` is the length of the +-- table, so that a call `table.remove(t)` removes the last element of table +-- `t`. +function table.remove(table , pos) end + +--- +-- Returns a new table with all parameters stored into keys 1, 2, etc. and with a field "n" with +-- the total number of parameters. Note that the resulting table may not be a sequence. +function table.pack (...) end +--- +-- Sorts table elements in a given order, +-- *in-place*, from `table[1]` to `table[n]`, where `n` is the length of the +-- table. If `comp` is given, then it must be a function that receives two +-- table elements, and returns true when the first is less than the second +-- (so that `not comp(a[i+1],a[i])` will be true after the sort). If `comp` +-- is not given, then the '<' operator will be used. +function table.sort(table , comp) end + +-- luacheck: ignore 121 + +--- +-- Returns the elements from the given table. This function is equivalent to +-- return list[i], list[i+1], ..., list[j] +-- except that the above code can be written only for a fixed number of +-- elements. By default, `i` is 1 and `j` is the length of the list, as +-- defined by the length operator (see ยง2.5.5). +function unpack(list , i , j) end + +return table diff --git a/Documentation/compiler/ldoc/ldoc/builtin/utf8.lua b/Documentation/compiler/ldoc/ldoc/builtin/utf8.lua new file mode 100644 index 000000000..ec91c4f37 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/builtin/utf8.lua @@ -0,0 +1,48 @@ +--- This library provides basic support for UTF-8 encoding. +-- @module utf8 + +local utf8 = {} + +--- +-- Receives zero or more integers, converts each one to its corresponding UTF-8 byte sequence and returns +-- a string with the concatenation of all these sequences. +function utf8.char (...) end + +--- +-- The pattern "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" , which matches exactly one +-- UTF-8 byte sequence, assuming that the subject is a valid UTF-8 string. +-- @field charpattern + +--- +-- Iterate over all characters in string. +-- +-- for p, c in utf8.codes(s) do body end +-- +-- will iterate over all characters in string s, with p being the position (in bytes) and c the code point +-- of each character. It raises an error if it meets any invalid byte sequence. +function utf8.codes (s) end + +--- +-- Returns the codepoints (as integers) from all characters in s that start between byte position i and j (both included). +-- The default for i is 1 and for j is i. It raises an error if it meets any invalid byte sequence. +function utf8.codepoint (s , i , j) end + +--- +-- Returns the number of UTF-8 characters in string s that start between positions i and j (both inclusive). +-- The default for i is 1 and for j is -1. If it finds any invalid byte sequence, returns a false value plus +-- the position of the first invalid byte. +function utf8.len (s , i , j) end + +--- +-- Returns the position (in bytes) where the encoding of the n-th character of s (counting from position i) starts. +-- A negative n gets characters before position i. The default for i is 1 when n is non-negative +-- and #s + 1 otherwise, so that utf8.offset(s, -n) gets the offset of the n-th character from the end +-- of the string. +-- If the specified character is neither in the subject nor right after its end, the function returns nil. +-- +-- As a special case, when n is 0 the function returns the start of the encoding of the character that contains the i-th byte of s. +-- +-- This function assumes that s is a valid UTF-8 string. +function utf8.offset (s, n , i) end + +return utf8 diff --git a/Documentation/compiler/ldoc/ldoc/config.ld b/Documentation/compiler/ldoc/ldoc/config.ld new file mode 100644 index 000000000..9c220baab --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/config.ld @@ -0,0 +1,11 @@ +project = 'Lua' +description = 'Lua Standard Libraries' +full_description = [[ +These are the built-in libraries of Lua 5.1 + +Plus documentation for lpeg and luafilesystem. +]] +file = {'builtin',exclude = {'builtin/globals.lua'}} +no_summary = true +no_return_or_parms = true +format = 'discount' diff --git a/Documentation/compiler/ldoc/ldoc/doc.lua b/Documentation/compiler/ldoc/ldoc/doc.lua new file mode 100644 index 000000000..30f8eafc5 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/doc.lua @@ -0,0 +1,1515 @@ +------ +-- Defining the ldoc document model. + + +local class = require 'pl.class' +local utils = require 'pl.utils' +local List = require 'pl.List' +local Map = require 'pl.Map' +local text = require 'pl.text' + +local doc = {} +local global = require 'ldoc.builtin.globals' +local tools = require 'ldoc.tools' +local split_dotted_name = tools.split_dotted_name + +local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG,TAG_MULTI_LINE = 'M','id','S','T','N','ML' + +-- these are the basic tags known to ldoc. They come in several varieties: +-- - 'M' tags with multiple values like 'param' (TAG_MULTI) +-- - 'ML' tags which have a single multi-lined value like 'usage' (TAG_MULTI_LINE) +-- - 'id' tags which are identifiers, like 'name' (TAG_ID) +-- - 'S' tags with a single value, like 'release' (TAG_SINGLE) +-- - 'N' tags which have no associated value, like 'local` (TAG_FLAG) +-- - 'T' tags which represent a type, like 'function' (TAG_TYPE) +local known_tags = { + param = 'M', see = 'M', comment = 'M', advancedDesc = 'ML', usage = 'ML', ['return'] = 'M', field = 'M', author='M',set='M'; + class = 'id', name = 'id', pragma = 'id', alias = 'id', + copyright = 'S', summary = 'S', description = 'S', release = 'S', license = 'S', + fixme = 'S', todo = 'S', warning = 'S', raise = 'S', charset = 'S', within = 'S', + ['local'] = 'N', export = 'N', private = 'N', constructor = 'N', static = 'N',include = 'S', + -- project-level + module = 'T', script = 'T', example = 'T', topic = 'T', submodule='T', classmod='T', file='T', + -- module-level + ['function'] = 'T', lfunction = 'T', table = 'T', section = 'T', type = 'T', + annotation = 'T', factory = 'T'; + +} +known_tags._alias = {} +known_tags._project_level = { + module = true, + script = true, + example = true, + topic = true, + submodule = true, + classmod = true, + file = true, +} + +known_tags._code_types = { + module = true, + script = true, + classmod = true, +} + +known_tags._presentation_names = { + classmod = 'Class', +} + +known_tags._module_info = { + 'copyright','release','license','author' +} + +local see_reference_handlers = {} + + +doc.TAG_MULTI,doc.TAG_ID,doc.TAG_SINGLE,doc.TAG_TYPE,doc.TAG_FLAG = + TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG + +-- add a new tag. +function doc.add_tag(tag,type,project_level) + if not known_tags[tag] then + known_tags[tag] = type + known_tags._project_level[tag] = project_level + end +end + +function doc.add_custom_see_handler(pat,action) + see_reference_handlers[pat] = action +end + +-- add an alias to an existing tag (exposed through ldoc API) +function doc.add_alias (a,tag) + known_tags._alias[a] = tag +end + +-- get the tag alias value, if it exists. +function doc.get_alias(tag) + return known_tags._alias[tag] +end + +-- is it a'project level' tag, such as 'module' or 'script'? +function doc.project_level(tag) + return known_tags._project_level[tag] +end + +-- is it a project level tag containing code? +function doc.code_tag (tag) + return known_tags._code_types[tag] +end + +-- is it a section tag? +function doc.section_tag (tag) + return tag == 'section' or doc.class_tag(tag) +end + +-- is it a class tag, like 'type' or 'factory'? +function doc.class_tag (tag) + return tag == 'type' or tag == 'factory' +end + +-- how the type wants to be formally presented; e.g. 'module' becomes 'Module' +-- but 'classmod' will become 'Class' +function doc.presentation_name (tag) + local name = known_tags._presentation_names[tag] + if not name then + name = tag:gsub('(%a)(%a*)',function(f,r) + return f:upper()..r + end) + end + return name +end + +function doc.module_info_tags () + return List.iter(known_tags._module_info) +end + +-- annotation tags can appear anywhere in the code and may contain any of these tags: +known_tags._annotation_tags = { + fixme = true, todo = true, warning = true +} + +local acount = 1 + +function doc.expand_annotation_item (tags, last_item) + if tags.summary ~= '' or last_item == nil then return false end + local item_name = last_item.tags.name + for tag, value in pairs(tags) do + if known_tags._annotation_tags[tag] then + tags.summary = nil + tags:add('class','annotation') + tags:add('summary',value) + tags:add('name',item_name..'-'..tag..acount) + acount = acount + 1 + return true + elseif tags.error and tag == 'return' then + last_item:set_tag(tag,value) + end + end + return false +end + +-- we process each file, resulting in a File object, which has a list of Item objects. +-- Items can be modules, scripts ('project level') or functions, tables, etc. +-- (In the code 'module' refers to any project level tag.) +-- When the File object is finalized, we specialize some items as modules which +-- are 'container' types containing functions and tables, etc. + +local File = class() +local Item = class() +local Module = class(Item) -- a specialized kind of Item + +doc.File = File +doc.Item = Item +doc.Module = Module + +function File:_init(filename) + self.filename = filename + self.items = List() + self.modules = List() + self.sections = List() +end + +function File:new_item(tags,line) + local item = Item(tags,self,line or 1) + self.items:append(item) + return item +end + +function File:export_item (name) + for item in self.items:iter() do + local tags = item.tags + if tags.name == name then + tags.export = true + if tags['local'] then + tags['local'] = nil + end + return + end + end + -- warn if any of these guys are not found, indicating no + -- documentation was given. + self:warning('no docs '..tools.quote(name)) +end + +local function mod_section_type (this_mod) + return this_mod and this_mod.section and this_mod.section.type +end + +function File:find_module_in_files (name) + for f in File.list:iter() do + for m in f.modules:iter() do + if m.name == name then + return m,f.filename + end + end + end +end + +local function init_within_section (mod,name) + mod.kinds:add_kind(name, name) + mod.enclosing_section = mod.section + mod.section = nil + return name +end + +function File:finish() + local this_mod + local items = self.items + local tagged_inside + self.args = self.args or {} + for item in items:iter() do + if mod_section_type(this_mod) == 'factory' and item.tags then + local klass = '@{'..this_mod.section.name..'}' + -- Factory constructors return the object type, and methods all have implicit self argument + if item.tags.constructor and not item.tags['return'] then + item.tags['return'] = List{klass} + elseif item.tags.param then + item.tags.param:put('self '..klass) + end + end + item:finish() + -- the default is not to show local functions in the documentation. + -- luacheck: push ignore 542 + if not self.args.all and (item.type=='lfunction' or (item.tags and item.tags['local'])) then + -- don't add to the module -- + elseif doc.project_level(item.type) then + this_mod = item + local package,mname,submodule + if item.type == 'module' or item.type == 'classmod' then + -- if name is 'package.mod', then mod_name is 'mod' + package,mname = split_dotted_name(this_mod.name) + if self.args.merge then + local mod,mf = self:find_module_in_files(item.name) + if mod then + print('found master module',mf) + this_mod = mod + if this_mod.section then + print '***closing section from master module***' + this_mod.section = nil + end + submodule = true + end + end + elseif item.type == 'submodule' then + local _ + submodule = true + this_mod,_ = self:find_module_in_files(item.name) + if this_mod == nil then + self:error("'"..item.name.."' not found for submodule") + end + tagged_inside = tools.this_module_name(self.base,self.filename)..' Functions' + this_mod.kinds:add_kind(tagged_inside, tagged_inside) + end + if not package then + mname = this_mod.name + package = '' + end + if not submodule then + this_mod.package = package + this_mod.mod_name = mname + this_mod.kinds = doc.ModuleMap() -- the iterator over the module contents + self.modules:append(this_mod) + end + elseif doc.section_tag(item.type) then + local display_name = item.name + if display_name == 'end' then + this_mod.section = nil + else + local summary = item.summary:gsub('%.$','') + local lookup_name + if doc.class_tag(item.type) then + display_name = 'Class '..item.name + lookup_name = item.name + item.module = this_mod + this_mod.items.by_name[item.name] = item + else + display_name = summary + lookup_name = summary + item.summary = '' + end + item.display_name = display_name + this_mod.section = item + -- the purpose of this little hack is to properly distinguish + -- between built-in kinds and any user-defined kinds. + this_mod.kinds:add_kind(display_name,display_name..' ',nil,item) + this_mod.sections:append(item) + this_mod.sections.by_name[lookup_name:gsub('%A','_')] = item + end + else + local to_be_removed + -- add the item to the module's item list + if this_mod then + -- new-style modules will have qualified names like 'mod.foo' + if item.name == nil then + self:error("item's name is nil") + end + local mod,fname = split_dotted_name(item.name) + -- warning for inferred unqualified names in new style modules + -- (retired until we handle methods like Set:unset() properly) + if not mod and not this_mod.old_style and item.inferred then + --item:warning(item.name .. ' is declared in global scope') + end + -- the function may be qualified with a module alias... + local alias = this_mod.tags.alias + if (alias and mod == alias) or mod == 'M' or mod == '_M' then + mod = this_mod.mod_name + end + -- if that's the mod_name, then we want to only use 'foo' + if mod == this_mod.mod_name and this_mod.tags.pragma ~= 'nostrip' then + item.name = fname + end + + if tagged_inside then + item.tags.within = tagged_inside + end + if item.tags.within then + init_within_section(this_mod,item.tags.within) + end + + -- right, this item was within a section or a 'class' + local section_description + local classmod = this_mod.type == 'classmod' + if this_mod.section or classmod then + local stype + local this_section = this_mod.section + if this_section then + item.section = this_section.display_name + stype = this_section.type + end + -- if it was a class, then if the name is unqualified then it becomes + -- 'Class:foo' (unless flagged as being a constructor, static or not a function) + if doc.class_tag(stype) or classmod then + if not item.name:match '[:%.]' then -- not qualified name! + -- a class is either a @type section or a @classmod module. Is this a _method_? + local class = classmod and this_mod.name or this_section.name + local static = item.tags.constructor or item.tags.static or item.type ~= 'function' + -- methods and metamethods go into their own special sections... + if classmod and item.type == 'function' then + local inferred_section + if item.name:match '^__' then + inferred_section = 'Metamethods' + elseif not static then + inferred_section = 'Methods' + end + if inferred_section then + item.tags.within = init_within_section(this_mod,inferred_section) + end + end + -- Whether to use '.' or the language's version of ':' (e.g. \ for Moonscript) + item.name = class..(not static and this_mod.file.lang.method_call or '.')..item.name + end + if stype == 'factory' then + if item.tags.private then to_be_removed = true + elseif item.type == 'lfunction' then + item.type = 'function' + end + if item.tags.constructor then + item.section = item.type + end + end + end + if this_section then + --section_description = this_section.summary..' '..(this_section.description or '') + --this_section.summary = '' + elseif item.tags.within then + item.section = item.tags.within + else + if item.type == 'function' or item.type == 'lfunction' then + section_description = "Methods" + end + item.section = item.type + end + elseif item.tags.within then -- ad-hoc section... + item.section = item.tags.within + else -- otherwise, just goes into the default sections (Functions,Tables,etc) + item.section = item.type; + end + + item.module = this_mod + if not to_be_removed then + local these_items = this_mod.items + these_items.by_name[item.name] = item + these_items:append(item) + this_mod.kinds:add(item,these_items,section_description) + end + + -- restore current section after a 'within' + if this_mod.enclosing_section then + this_mod.section = this_mod.enclosing_section + this_mod.enclosing_section = nil + end + + else + -- must be a free-standing function (sometimes a problem...) + end + end + -- luacheck: pop + item.names_hierarchy = require('pl.utils').split( + item.name, + '[.:]' + ) + end +end + +-- some serious hackery. We force sections into this 'module', +-- and ensure that there is a dummy item so that the section +-- is not empty. + +function File:add_document_section(title) + local section = title:gsub('%W','_') + self:new_item { + name = section, + class = 'section', + summary = title + } + self:new_item { + name = 'dumbo', + class = 'function', + } + return section +end + +function Item:_init(tags,file,line) + self.file = file + self.lineno = line + self.summary = tags.summary + self.description = tags.description + tags.summary = nil + tags.description = nil + self.tags = {} + self.formal_args = tags.formal_args + tags.formal_args = nil + local iter = tags.iter or Map.iter + for tag in iter(tags) do + self:set_tag(tag,tags[tag]) + end +end + +function Item:add_to_description (rest) + if type(rest) == 'string' then + self.description = (self.description or '') .. rest + end +end + +function Item:trailing_warning (kind,tag,rest) + if type(rest)=='string' and #rest > 0 then + Item.warning(self,kind.." tag: '"..tag..'" has trailing text ; use not_luadoc=true if you want description to continue between tags\n"'..rest..'"') + end +end + +local function is_list (l) + return getmetatable(l) == List +end + +function Item:set_tag (tag,value) + local ttype = known_tags[tag] + local args = self.file.args + + if ttype == TAG_MULTI or ttype == TAG_MULTI_LINE then -- value is always a List! + local ovalue = self.tags[tag] + if ovalue then -- already defined, must be a list + --print(tag,ovalue,value) + if is_list(value) then + ovalue:extend(value) + else + ovalue:append(value) + end + value = ovalue + end + -- these multiple values are always represented as lists + if not is_list(value) then + value = List{value} + end + if ttype ~= TAG_MULTI_LINE and args and args.not_luadoc then + local last = value[#value] + if type(last) == 'string' and last:match '\n' then + local line,rest = last:match('([^\n]+)(.*)') + value[#value] = line + self:add_to_description(rest) + end + end + self.tags[tag] = value + elseif ttype == TAG_ID then + if type(value) == 'table' then + if value.append then -- it was a List! + -- such tags are _not_ multiple, e.g. name + if tag == 'class' and value:contains 'example' then + self:error("cannot use 'example' tag for functions or tables. Use 'usage'") + else + self:error("'"..tag.."' cannot have multiple values; "..tostring(value)) + end + end + value = value[1] + end + if value == nil then self:error("Tag without value: "..tag) end + local id, rest = tools.extract_identifier(value) + self.tags[tag] = id + if args and args.not_luadoc then + self:add_to_description(rest) + else + self:trailing_warning('id',tag,rest) + end + elseif ttype == TAG_SINGLE then + self.tags[tag] = value + elseif ttype == TAG_FLAG then + self.tags[tag] = true + if args.not_luadoc then + self:add_to_description(value) + else + self:trailing_warning('flag',tag,value) + end + else + Item.warning(self,"unknown tag: '"..tag.."' "..tostring(ttype)) + end +end + +-- preliminary processing of tags. We check for any aliases, and for tags +-- which represent types. This implements the shortcut notation. +function Item.check_tag(tags,tag, value, modifiers) + local alias = doc.get_alias(tag) + if alias then + if type(alias) == 'string' then + tag = alias + elseif type(alias) == 'table' then --{ tag, value=, modifiers = } + local avalue,amod + tag, avalue, amod = alias[1],alias.value,alias.modifiers + if avalue then value = avalue..' '..value end + if amod then + modifiers = modifiers or {} + for m,v in pairs(amod) do + local idx = tonumber(v:match('^%$(%d+)')) + if idx then + v, value = value:match('(%S+)(.*)') + end + modifiers[m] = v + end + end + else -- has to be a function that at least returns tag, value + return alias(tags,value,modifiers) + end + end + local ttype = known_tags[tag] + if ttype == TAG_TYPE then + tags:add('class',tag) + tag = 'name' + end + return tag, value, modifiers +end + +-- any tag (except name and classs) may have associated modifiers, +-- in the form @tag[m1,...] where m1 is either name1=value1 or name1. +-- At this stage, these are encoded +-- in the tag value table and need to be extracted. + +local function extract_value_modifier (p) + if type(p)~='table' then + return p, { } + else + return p[1], p.modifiers or { } + end +end + +local function extract_tag_modifiers (tags) + local modifiers, mods = {} + for tag, value in pairs(tags) do + if type(value)=='table' and value.append then -- i.e. it is a List! + local tmods = {} + for i, v in ipairs(value) do + v, mods = extract_value_modifier(v) + tmods[i] = mods + value[i] = v + end + modifiers[tag] = tmods + else + value, mods = extract_value_modifier(value) + modifiers[tag] = mods + tags[tag] = value + end + end + return modifiers +end + +local function read_del (tags,name) + local ret = tags[name] + tags[name] = nil + return ret +end + +local build_arg_list, split_iden -- forward declaration + +function Item:split_param (line) + local name, comment = line:match('%s*([%w_%.:]+)(.*)') + if not name then + self:error("bad param name format '"..line.."'. Are you missing a parameter name?") + end + return name, comment +end + +function Item:finish() + local tags = self.tags + local quote = tools.quote + self.name = read_del(tags,'name') + self.type = read_del(tags,'class') + self.modifiers = extract_tag_modifiers(tags) + self.usage = read_del(tags,'usage') + self.advancedDesc = read_del(tags,'advancedDesc') + tags.see = read_del(tags,'see') + if tags.see then + tags.see = tools.identifier_list(tags.see) + end + if self.usage then + for i = 1,#self.usage do + local usage = self.usage[i]:gsub('^%s*\n','') + self.usage[i] = text.dedent(usage) + end + end + + if self.advancedDesc then + self.advancedDesc.lines = {} + self.advancedDesc.formatLines = {} + for i = 1,#self.advancedDesc do + + local currentBlock = 0 + local advancedDesc = self.advancedDesc[i] + advancedDesc = text.dedent(advancedDesc) + local prevIsCode = false + local start = true + + for line in advancedDesc:gmatch("[^\n]*\n?") do + if line:find("^\t") or (prevIsCode and line:find("^\n$")) then + if not prevIsCode then + currentBlock = currentBlock + 1 + self.advancedDesc.lines[currentBlock] = "" + self.advancedDesc.formatLines[currentBlock] = true + end + self.advancedDesc.lines[currentBlock] = self.advancedDesc.lines[currentBlock] .. line:gsub("^\t", "") + prevIsCode = true + else + if prevIsCode or start then + currentBlock = currentBlock + 1 + self.advancedDesc.lines[currentBlock] = "" + self.advancedDesc.formatLines[currentBlock] = false + end + self.advancedDesc.lines[currentBlock] = self.advancedDesc.lines[currentBlock] .. line + prevIsCode = false + end + start = false + end + end + end + if doc.project_level(self.type) then + -- we are a module, so become one! + self.items = List() + self.sections = List() + self.items.by_name = {} + self.sections.by_name = {} + setmetatable(self,Module) + elseif not doc.section_tag(self.type) then + -- params are either a function's arguments, or a table's fields, etc. + if self.type == 'function' then + self.parameter = 'param' + self.ret = read_del(tags,'return') + self.raise = read_del(tags,'raise') + if tags['local'] then + self.type = 'lfunction' + end + else + self.parameter = 'field' + end + local field = self.parameter + local params = read_del(tags,field) + -- use of macros like @string (which is short for '@tparam string') + -- can lead to param tags associated with a table. + if self.parameter == 'field' and tags.param then + local tparams = read_del(tags,'param') + if params then + params:extend(tparams) + List(self.modifiers.field):extend(self.modifiers.param) + else + params = tparams + self.modifiers.field = self.modifiers.param + end + end + local param_names, comments = List(), List() + if params then + for line in params:iter() do + local name, comment = self:split_param(line) + param_names:append(name) + comments:append(comment) + end + end + self.modifiers['return'] = self.modifiers['return'] or List() + self.modifiers[field] = self.modifiers[field] or List() + -- we use the formal arguments (if available) as the authoritative list. + -- If there are both params and formal args, then they must match; + -- (A formal argument of ... may match any number of params at the end, however.) + -- If there are formal args and no params, we see if the args have any suitable comments. + -- Params may have subfields. + local fargs, formal = self.formal_args + if fargs then + if #param_names == 0 then + --docs may be embedded in argument comments; in either case, use formal arg names + local ret + formal,comments,ret = self:parse_formal_arguments(fargs) + if ret and not self.ret then self.ret = ret end + elseif #fargs > 0 then -- consistency check! + local varargs = fargs[#fargs] == '...' + if varargs then table.remove(fargs) end + if tags.export then + if fargs[1] == 'self' then + table.remove(fargs,1) + else + tags.static = true + end + end + local k = 0 + for _,pname in ipairs(param_names) do + local _,field = split_iden(pname) + if not field then + k = k + 1 + if k > #fargs then + if not varargs then + self:warning("extra param with no formal argument: "..quote(pname)) + end + elseif pname ~= fargs[k] then + self:warning("param and formal argument name mismatch: "..quote(pname).." "..quote(fargs[k])) + end + end + end + if k < #fargs then + for i = k+1,#fargs do if fargs[i] ~= '...' then + self:warning("undocumented formal argument: "..quote(fargs[i])) + end end + end + end -- #fargs > 0 + -- formal arguments may come with types, inferred by the + -- appropriate code in ldoc.lang + if fargs.types then + self.modifiers[field] = List() + for t in fargs.types:iter() do + self:add_type(field,t) + end + if fargs.return_type then + if not self.ret then -- type, but no comment; no worries + self.ret = List{''} + end + self.modifiers['return'] = List() + self:add_type('return',fargs.return_type) + end + end + end -- fargs + + -- the comments are associated with each parameter by + -- adding name-value pairs to the params list (this is + -- also done for any associated modifiers) + -- (At this point we patch up any subparameter references) + local pmods = self.modifiers[field] + local params, fields = List() + local original_names = formal and formal or param_names + local names = List() + self.subparams = {} + params.map = {} + + for i,name in ipairs(original_names) do + if type(name) ~= 'string' then + self:error("declared table cannot have array entries") + end + local pname,field = split_iden(name) + if field then + if not fields then + fields = List() + self.subparams[pname] = fields + end + fields:append(name) + else + names:append(name) + params:append(name) + fields = nil + end + + params.map[name] = comments[i] + if pmods then + pmods[name] = pmods[i] + end + end + self.params = params + self.args = build_arg_list (names,pmods) + end + if self.ret then + self:build_return_groups() + end +end + +function Item:add_type(field,type) + self.modifiers[field]:append {type = type} +end + +-- ldoc allows comments in the formal arg list to be used, if they aren't specified with @param +-- Further, these comments may start with a type followed by a colon, and are then equivalent +-- to a @tparam +function Item:parse_argument_comment (comment,field) + if comment then + comment = comment:gsub('^%-+%s*','') + local type,rest = comment:match '([^:]+):(.*)' + if type then + self:add_type(field,type) + comment = rest + end + end + return comment or '' +end + +function Item:parse_formal_arguments (fargs) + local formal, comments, ret = List(), List() + if fargs.return_comment then + local retc = self:parse_argument_comment(fargs.return_comment,'return') + ret = List{retc} + end + for i, name in ipairs(fargs) do + formal:append(name) + comments:append(self:parse_argument_comment(fargs.comments[name],self.parameter)) + end + return formal, comments, ret +end + +function split_iden (name) + if name == '...' then return name end + local pname,field = name:match('(.-)%.(.+)') + if not pname then + return name + else + return pname,field + end +end + +function build_arg_list (names,pmods) + -- build up the string representation of the argument list, + -- using any opt and optchain modifiers if present. + -- For instance, '(a [, b])' if b is marked as optional + -- with @param[opt] b + local buffer, npending = { }, 0 + local function acc(x) table.insert(buffer, x) end + -- a number of trailing [opt]s will be usually converted to [opt],[optchain],... + -- *unless* a person uses convert_opt. + if pmods and not doc.ldoc.convert_opt then + local m = pmods[#names] + if m and m.opt then + m.optchain = m.opt + for i = #names-1,1,-1 do + m = pmods[i] + if not m or not m.opt then break end + m.optchain = m.opt + end + end + end + for i = 1, #names do + local m = pmods and pmods[i] + local opt + if m then + if not m.optchain then + acc ((']'):rep(npending)) + npending=0 + end + opt = m.optchain or m.opt + if opt then + acc('[') + npending=npending+1 + end + end + if i>1 then acc (', ') end + acc(names[i]) + if opt and opt ~= true then acc('='..opt) end + end + acc ((']'):rep(npending)) + return '('..table.concat(buffer)..')' +end + +------ retrieving information about parameters ----- +-- The template leans on these guys heavily.... + +function Item:param_modifiers (p) + local mods = self.modifiers[self.parameter] + if not mods then return '' end + return rawget(mods,p) +end + +function Item:type_of_param(p) + local mparam = self:param_modifiers(p) + return mparam and mparam.type or '' +end + +-- default value for param; if optional but no default, it's just `true`. +function Item:default_of_param(p) + local m = self:param_modifiers(p) + if not m then return nil end + local opt = m.optchain or m.opt + return opt +end + +function Item:readonly(p) + local m = self:param_modifiers(p) + if not m then return nil end + return m.readonly +end + +function Item:subparam(p) + local subp = rawget(self.subparams,p) + if subp then + return subp,p + else + return {p},nil + end +end + +function Item:display_name_of(p) + local pname,field = split_iden(p) + if field then + return field + else + return pname + end +end + +-------- return values and types ------- + +function Item:type_of_ret(idx) + local rparam = self.modifiers['return'][idx] + return rparam and rparam.type or '' +end + +local function integer_keys(t) + if type(t) ~= 'table' then return 0 end + for k in pairs(t) do + local num = tonumber(k) + if num then return num end + end + return 0 +end + +function Item:return_type(r) + if not r.type then return '' end + return r.type, r.ctypes +end + +function Item:build_return_groups() + local quote = tools.quote + local modifiers = self.modifiers + local retmod = modifiers['return'] + local groups = List() + local lastg, group + for i,ret in ipairs(self.ret) do + local mods = retmod[i] + local g = integer_keys(mods) + if g ~= lastg then + group = List() + group.g = g + groups:append(group) + lastg = g + end + --require 'pl.pretty'.dump(ret) + if not mods then + self:error(quote(self.name)..' had no return?') + end + group:append({text=ret, type = mods and (mods.type or '') or '',mods = mods}) + end + -- order by groups to force error groups to the end + table.sort(groups,function(g1,g2) return g1.g < g2.g end) + self.retgroups = groups + --require 'pl.pretty'.dump(groups) + -- cool, now see if there are any treturns that have tfields to associate with + local fields = self.tags.field + if fields then + local fcomments = List() + for i,f in ipairs(fields) do + local name, comment = self:split_param(f) + fields[i] = name + fcomments[i] = comment + end + local fmods = modifiers.field + for group in groups:iter() do for r in group:iter() do + if r.mods and r.mods.type then + local ctypes, T = List(), r.mods.type + for i,f in ipairs(fields) do if fmods[i][T] then + ctypes:append {name=f,type=fmods[i].type,comment=fcomments[i]} + end end + r.ctypes = ctypes + --require 'pl.pretty'.dump(ctypes) + end + end end + end +end + +local ecount = 0 + +-- this alias macro implements @error. +-- Alias macros need to return the same results as Item:check_tags... +function doc.error_macro(tags,value,modifiers) + local merge_groups = doc.ldoc.merge_error_groups + local g = '2' -- our default group id + -- Were we given an explicit group modifier? + local key = integer_keys(modifiers) + if key > 0 then + g = tostring(key) + else + local l = tags:get 'return' + if l then -- there were returns already...... + -- maximum group of _existing_ error return + local grp, lastr = 0 + for r in l:iter() do if type(r) == 'table' then + local rg = r.modifiers._err + if rg then + lastr = r + grp = math.max(grp,rg) + end + end end + if grp > 0 then -- cool, create new group + if not merge_groups then + g = tostring(grp+1) + else + local mods, text, T = lastr.modifiers + local new = function(text) + return mods._collected..' '..text,{type='string',[T]=true} + end + if not mods._collected then + text = lastr[1] + lastr[1] = merge_groups + T = '@'..ecount + mods.type = T + mods._collected = 1 + ecount = ecount + 1 + tags:add('field',new(text)) + else + T = mods.type + end + mods._collected = mods._collected + 1 + return 'field',new(value) + end + end + end + end + tags:add('return','',{[g]=true,type='nil'}) + -- note that this 'return' is tagged with _err! + return 'return', value, {[g]=true,_err=tonumber(g),type='string'} +end + +---------- bothering the user -------------------- + +function Item:warning(msg) + local file = self.file and self.file.filename + if type(file) == 'table' then require 'pl.pretty'.dump(file); file = '?' end + file = file or '?' + io.stderr:write(file,':',self.lineno or '1',': ',self.name or '?',': ',msg,'\n') + Item.had_warning = true + return nil +end + +function Item:error(msg) + self:warning(msg) + os.exit(1) +end + +Module.warning, Module.error = Item.warning, Item.error + +-------- Resolving References ----------------- + +function Module:hunt_for_reference (packmod, modules) + local mod_ref + local package = self.package or '' + repeat -- same package? + local nmod = package..'.'..packmod + mod_ref = modules.by_name[nmod] + if mod_ref then break end -- cool + package = split_dotted_name(package) + until not package + return mod_ref +end + +local function custom_see_references (s) + for pat, action in pairs(see_reference_handlers) do + if s:match(pat) then + local label, href = action(s:match(pat)) + if not label then print('custom rule failed',s,pat,href) end + return {href = href, label = label} + end + end +end + +local function reference (s, mod_ref, item_ref) + local name = item_ref and item_ref.name or '' + -- this is deeply hacky; classes have 'Class ' prepended. +--~ if item_ref and doc.class_tag(item_ref.type) then +--~ name = 'Class_'..name +--~ end + return {mod = mod_ref, name = name, label=s} +end + +function Module:lookup_class_item (packmod, s) + local klass = packmod --"Class_"..packmod + local qs = klass..':'..s + local klass_section = self.sections.by_name[klass] + if not klass_section then return nil end -- no such class + for item in self.items:iter() do + --print('item',qs,item.name) + if s == item.name or qs == item.name then + return reference(s,self,item) + end + end + return nil +end + +function Module:process_see_reference (s,modules,istype) + if s == nil then return nil end + local fun_ref + local ref = custom_see_references(s) + if ref then return ref end + if not s:match '^[%w_%.\\%:%-]+$' or not s:match '[%w_]$' then + return nil, "malformed see reference: '"..s..'"' + end + + -- `istype` means that we are looking up strictly in a _type_ context, so then only + -- allow `classmod` module references. + local function ismod(item) + if item == nil then return false end + if not istype then return true + else + return item.type == 'classmod' + or item.type == 'tenprimitive' + or item.type == 'tenclass' + or item.type == 'luautil' + or item.type == 'tentable' + or item.type == 'enum' + end + end + + -- it is _entirely_ possible that someone does not want auto references for standard Lua libraries! + local lua_manual_ref + local ldoc = tools.item_ldoc(self) + if ldoc and ldoc.no_lua_ref then + lua_manual_ref = function(s) return false end + else + lua_manual_ref = global.lua_manual_ref + end + -- pure C projects use global lookup (no namespaces) + if ldoc and ldoc.global_lookup == nil then + local using_c = ldoc.parse_extra and ldoc.parse_extra.C + ldoc.global_lookup = using_c or false + end + + -- is this a fully qualified module name? + local mod_ref = modules.by_name[s] + if ismod(mod_ref) then return reference(s, mod_ref,nil) end + -- module reference? + mod_ref = self:hunt_for_reference(s, modules) + if ismod(mod_ref) then return mod_ref end + -- method reference? (These are of form CLASS.NAME) + fun_ref = self.items.by_name[s] + if fun_ref then return reference(s,self,fun_ref) end + -- otherwise, start splitting! + local packmod,name = split_dotted_name(s) -- e.g. 'pl.utils','split' + if packmod then -- qualified name + mod_ref = modules.by_name[packmod] -- fully qualified mod name? + if not mod_ref then + mod_ref = self:hunt_for_reference(packmod, modules) + if not mod_ref then + local ref = self:lookup_class_item(packmod,s) + if ref then return ref end + local mod, klass = split_dotted_name(packmod) + mod_ref = modules.by_name[mod] + if mod_ref then + ref = mod_ref:lookup_class_item(klass,name) + if ref then return ref end + end + ref = lua_manual_ref(s) + if ref then return ref end + return nil,"module not found: "..packmod + end + end + fun_ref = mod_ref:get_fun_ref(name) + if fun_ref then + return reference(s,mod_ref,fun_ref) + else + fun_ref = mod_ref.sections.by_name[name] + if not fun_ref then + return nil,"function or section not found: "..s.." in "..mod_ref.name + else + return reference(fun_ref.name:gsub('_',' '),mod_ref,fun_ref) + end + end + else -- plain jane name; module in this package, function in this module + if ldoc and ldoc.global_lookup then + for m in modules:iter() do + fun_ref = m:get_fun_ref(s) + if fun_ref then return reference(s,m,fun_ref) end + end + return nil,"function: "..s.." not found globally" + end + mod_ref = modules.by_name[self.package..'.'..s] + if ismod(mod_ref) then return reference(s, mod_ref,nil) end + + fun_ref = self.items.by_name[self.name..'.'..s] + if fun_ref then return reference(self.name..'.'..s,self,fun_ref) end + + fun_ref = self:get_fun_ref(s) + if fun_ref then return reference(s,self,fun_ref) + else + local ref = lua_manual_ref (s) + if ref then return ref end + return nil, "function not found: "..s.." in this module" + end + end +end + +function Module:get_fun_ref(s) + local fun_ref = self.items.by_name[s] + -- did not get an exact match, so try to match by the unqualified fun name + if not fun_ref then + local patt = '[.:]'..s..'$' + for qname,ref in pairs(self.items.by_name) do + if qname:match(patt) then + fun_ref = ref + break + end + end + end + return fun_ref +end + + +-- resolving @see references. A word may be either a function in this module, +-- or a module in this package. A MOD.NAME reference is within this package. +-- Otherwise, the full qualified name must be used. +-- First, check whether it is already a fully qualified module name. +-- Then split it and see if the module part is a qualified module +-- and try look up the name part in that module. +-- If this isn't successful then try prepending the current package to the reference, +-- and try to to resolve this. +function Module:resolve_references(modules) + local found = List() + -- Resolve see references in item. Can be Module or Item type. + local function resolve_item_references(item) + local see = item.tags.see + if see then -- this guy has @see references + item.see = List() + for s in see:iter() do + local href, err = self:process_see_reference(s,modules) + if href then + item.see:append (href) + found:append{item,s} + elseif err then + item:warning(err) + end + end + end + end + + resolve_item_references(self); -- Resolve module-level see references. + for item in self.items:iter() do + resolve_item_references(item); -- Resolve item-level see references. + end + -- mark as found, so we don't waste time re-searching + for f in found:iter() do + f[1].tags.see:remove_value(f[2]) + end +end + +function Item:dump_tags (taglist) + for tag, value in pairs(self.tags) do + if not taglist or taglist[tag] then + Item.warning(self,tag..' '..tostring(value)) + end + end +end + +function Module:dump_tags (taglist) + Item.dump_tags(self,taglist) + for item in self.items:iter() do + item:dump_tags(taglist) + end +end + +--------- dumping out modules and items ------------- + +local function dump_tags (tags) + if next(tags) then + print 'tags:' + for tag, value in pairs(tags) do + print('\t',tag,value) + end + end +end + +-- ANSI colour codes for making important stuff BOLD +-- (but not on Windows) +local bold,default = '\x1B[1m','\x1B[0m' +if utils.dir_separator == '\\' then + bold,default = '','' +end + +function Module:dump(verbose) + if not doc.project_level(self.type) then return end + print '----' + print(self.type..':',bold..self.name,self.summary..default) + if self.description then print(self.description) end + dump_tags (self.tags) + for item in self.items:iter() do + item:dump(verbose) + end +end + +function Item:isFunction() + return self.type == 'function' +end + +function Module:dumpToXML() + if not doc.project_level(self.type) then return end + for item in self.items:iter() do + if item:isFunction() then + item:dumpToXML(self.name) + end + end +end + +function Item:dumpToXML(moduleName) + local nIndents = 0 + function printAndIndent(str) + io.write('\n'..string.rep('\t', nIndents) .. str) + nIndents = nIndents + 1 + end + + function justPrint(str) + io.write(str) + end + + function printAndUnindent(str, newLine) + nIndents = nIndents - 1 + if newLine then + io.write('\n',string.rep('\t', nIndents),str) + else + + io.write(str) + end + end + + function trim(str) + return str:gsub("^%s*(.-)%s*$", "%1") + end + + local callerType = self.name:match("(%w+)") + local funcName = self.name:match("%w+[:%.](%w+)") + if not funcName then + funcName = callerType + callerType = nil + end + + printAndIndent('') + + printAndIndent '' + justPrint(moduleName) + printAndUnindent '' + if callerType then + printAndIndent '' + justPrint(callerType) + printAndUnindent '' + end + if funcName then + printAndIndent '' + justPrint(funcName) + printAndUnindent '' + end + + if self.summary:len() > 0 then + printAndIndent '' + justPrint(self.summary) + printAndUnindent '' + end + + if self.description and self.description:match '%S' then + printAndIndent '' + justPrint(trim(self.description)) + printAndUnindent '' + end + if self.params and #self.params > 0 then + printAndIndent '' + for i,p in ipairs(self.params) do + printAndIndent '' + printAndIndent '' + justPrint(p) + printAndUnindent '' + + local paramType = self:type_of_param(i) + printAndIndent("") + if p == "..." then + paramType = "..." + elseif paramType:len() == 0 then + print("Parameter " .. i .. " of function " .. funcName .. + " in module " .. moduleName .. " has no type") + end + justPrint(paramType) + printAndUnindent("") + local descText = trim(self.params.map[p]) + if descText:len() > 0 then + printAndIndent '' + justPrint(descText) + printAndUnindent '' + end + printAndUnindent('', true) + end + printAndUnindent('', true) + end + if self.ret and #self.ret > 0 then + printAndIndent("") + for i,r in ipairs(self.ret) do + printAndIndent("") + local returnType = self:type_of_ret(i) + printAndIndent("") + if returnType:len() == 0 then + print("Return value " .. i .. " of function " .. funcName .. + " in module " .. moduleName .. " has no type") + end + justPrint(returnType) + + printAndUnindent("") + + printAndIndent("") + justPrint(r) + + printAndUnindent("") + printAndUnindent("", true) + end + printAndUnindent("", true) + end + printAndUnindent ('\n', true) +end + +-- make a text dump of the contents of this File object. +-- The level of detail is controlled by the 'verbose' parameter. +-- Primarily intended as a debugging tool. +function File:dump(verbose) + for mod in self.modules:iter() do + mod:dump(verbose) + end +end + +function Item:dump(verbose) + local name = self.name + if self.type == 'function' then + name = name .. self.args + end + if verbose then + print() + io.write(bold) + print(self.type,name) + io.write(default) + print(self.summary) + if self.description and self.description:match '%S' then + print 'description:' + print(self.description) + end + if self.params and #self.params > 0 then + print 'parameters:' + for _,p in ipairs(self.params) do + print('',p,self.params.map[p]) + end + end + if self.ret and #self.ret > 0 then + print 'returns:' + for _,r in ipairs(self.ret) do + print('',r) + end + end + dump_tags(self.tags) + else + print('* '..bold..name..default..' - '..self.summary) + end +end + +function doc.filter_objects_through_function(filter, module_list) + local quit, quote = utils.quit, tools.quote + if filter == 'dump' then filter = 'pl.pretty.dump' end + local mod,name = tools.split_dotted_name(filter) + local ok,P = pcall(require,mod) + if not ok then quit("cannot find module "..quote(mod)) end + local ok,f = pcall(function() return P[name] end) + if not ok or type(f) ~= 'function' then quit("dump module: no function "..quote(name)) end + + -- clean up some redundant and cyclical references-- + module_list.by_name = nil + for mod in module_list:iter() do + mod.kinds = nil + mod.file = mod.file.filename + for item in mod.items:iter() do + item.module = nil + item.file = nil + item.formal_args = nil + item.tags['return'] = nil + item.see = nil + end + mod.items.by_name = nil + end + + local ok,err = pcall(f,module_list) + if not ok then quit("dump failed: "..err) end +end + +return doc diff --git a/Documentation/compiler/ldoc/ldoc/html.lua b/Documentation/compiler/ldoc/ldoc/html.lua new file mode 100644 index 000000000..ba820a6b2 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html.lua @@ -0,0 +1,402 @@ +------ generating HTML output --------- +-- Although this can be generalized for outputting any format, since the template +-- is language-agnostic, this implementation concentrates on HTML. +-- This does the actual generation of HTML, and provides support functions in the ldoc +-- table for the template +-- +-- A fair amount of the complexity comes from operating in two basic modes; first, where +-- there is a number of modules (classic LuaDoc) or otherwise, where there is only one +-- module and the index contains the documentation for that module. +-- +-- Like LuaDoc, LDoc puts similar kinds of documentation files in their own directories. +-- So module docs go into 'modules/', scripts go into 'scripts/', and so forth. LDoc +-- generalizes the idea of these project-level categories and in fact custom categories +-- can be created (refered to as 'kinds' in the code) + +local List = require 'pl.List' +local utils = require 'pl.utils' +local path = require 'pl.path' +local stringx = require 'pl.stringx' +local template = require 'pl.template' +local tablex = require 'pl.tablex' +local OrderedMap = require 'pl.OrderedMap' +local tools = require 'ldoc.tools' +local markup = require 'ldoc.markup' +local prettify = require 'ldoc.prettify' +local doc = require 'ldoc.doc' +local unpack = utils.unpack +local html = {} + + +local quit = utils.quit + +local function cleanup_whitespaces(text) + local lines = stringx.splitlines(text) + for i = 1, #lines do + lines[i] = stringx.rstrip(lines[i]) + end + lines[#lines + 1] = "" -- Little trick: file should end with newline + return table.concat(lines, "\n") +end + +local function get_module_info(m) + local info = OrderedMap() + for tag in doc.module_info_tags() do + local val = m.tags[tag] + if type(val)=='table' then + val = table.concat(val,',') + end + tag = stringx.title(tag) + info:set(tag,val) + end + if #info:keys() > 0 then + return info + end +end + +local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" } + +function html.generate_output(ldoc, args, project) + local check_directory, check_file, writefile = tools.check_directory, tools.check_file, tools.writefile + local original_ldoc + + local function save_and_set_ldoc (set) + if not set then return end + if not original_ldoc then + original_ldoc = tablex.copy(ldoc) + end + for s in set:iter() do + local var,val = s:match('([^=]+)=(.+)') + local num = tonumber(val) + if num then val = num + elseif val == 'true' then val = true + elseif val == 'false' then val = false + end + print('setting',var,val) + ldoc[var] = val + end + end + + local function restore_ldoc () + if original_ldoc then + ldoc = original_ldoc + end + end + + function ldoc.escape(str) + return (str:gsub("['&<>\"]", escape_table)) + end + + function ldoc.prettify(str) + return prettify.code('lua','usage',str,0,false) + end + + -- Item descriptions come from combining the summary and description fields + function ldoc.descript(item) + return tools.join(' ', item.summary, item.description) + end + + function ldoc.module_name (mod) + local name = mod.name + if args.unqualified and (mod.type == 'module' or mod.type == 'classmod') then -- leave out package + name = name:gsub('^.-%.','') + elseif mod.type == 'topic' then + if mod.display_name then + name = mod.display_name + else -- leave out md extension + name = name:gsub('%..*$','') + end + end + return name + end + + -- this generates the internal module/function references + function ldoc.href(see) + if see.href then -- explict reference, e.g. to Lua manual + return see.href + elseif doc.Module:class_of(see) then + return ldoc.ref_to_module(see) + else + return ldoc.ref_to_module(see.mod)..'#'..see.name + end + end + + -- this is either called from the 'root' (index or single module) or + -- from the 'modules' etc directories. If we are in one of those directories, + -- then linking to another kind is `../kind/name`; to the same kind is just `name`. + -- If we are in the root, then it is `kind/name`. + function ldoc.ref_to_module (mod) + local base = "" -- default: same directory + mod = mod or ldoc.module + local kind, module = mod.kind, ldoc.module + local name = mod.name -- default: name of module + if not ldoc.single then + if module then -- we are in kind/ + if module.type ~= type then -- cross ref to ../kind/ + base = "../"..kind.."/" + end + else -- we are in root: index + base = kind..'/' + end + else -- single module + if mod == ldoc.single then + name = ldoc.output + if not ldoc.root then base = '../' end + elseif ldoc.root then -- ref to other kinds (like examples) + base = kind..'/' + else + if module.type ~= type then -- cross ref to ../kind/ + base = "../"..kind.."/" + end + end + end + return base..name..'.html' + end + + function ldoc.include_file (file) + local text,_ = utils.readfile(file) + if not text then quit("unable to include "..file) + else + return text + end + end + +-- these references are never from the index...? +function ldoc.source_ref (fun) + local modname = fun.module.name + local pack,name = tools.split_dotted_name(modname) + if not pack then + name = modname + end + return (ldoc.single and "" or "../").."source/"..name..'.lua.html#'..fun.lineno + end + + function ldoc.use_li(ls) + if #ls > 1 then return '
  • ','
  • ' else return '','' end + end + + function ldoc.default_display_name(item) + -- Project-level items: + if doc.project_level(item.type) then + return ldoc.module_name(item) + end + -- Module-level items: + local name = item.display_name or item.name + if item.type == 'function' or item.type == 'lfunction' then + if not ldoc.no_space_before_args then + name = name..' ' + end + return name..item.args + else + return name + end + end + + function ldoc.display_name(item) + if ldoc.custom_display_name_handler then + return ldoc.custom_display_name_handler(item, ldoc.default_display_name) + else + return ldoc.default_display_name(item) + end + end + + function ldoc.no_spaces(s) + s = s:gsub('%s*$','') + return (s:gsub('%W','_')) + end + + function ldoc.module_typename(m) + return doc.presentation_name(m.type) + end + + function ldoc.is_list (t) + return type(t) == 'table' and t.append + end + + function ldoc.strip_header (s) + if not s then return s end + return s:gsub('^%s*#+%s+','') + end + + function ldoc.typename (tp) + if not tp or tp == '' or tp:match '^@' then return '' end + local optional + -- ? is short for ?nil| + if tp:match("^%?") and not tp:match '|' then + tp = '?|'..tp:sub(2) + end + local tp2 = tp:match("%?|?(.*)") + if tp2 then + optional = true + tp = tp2 + end + + local types = {} + for name in tp:gmatch("[^|]+") do + local sym = name:match '([%w%.%:]+)' + local ref,_ = markup.process_reference(sym,true) + if ref then + if ref.label and sym == name then + name = ref.label + end + local shortName = name:match("%.(%w*)$") + if shortName then + name = shortName + end + types[#types+1] = ('%s'):format(ldoc.href(ref),name) + else + types[#types+1] = ''..name..'' + end + end + local names = table.concat(types, ", ", 1, math.max(#types-1, 1)) + if #types > 1 then names = names.." or "..types[#types] end + if optional then + if names ~= '' then + if #types == 1 then names = "optional "..names end + else + names = "optional" + end + end + return names + end + + -- the somewhat tangled logic that controls whether a type appears in the + -- navigation sidebar. (At least it's no longer in the template ;)) + function ldoc.allowed_in_contents(type,module) + local allowed = true + if ldoc.kinds_allowed then + allowed = ldoc.kinds_allowed[type] + elseif ldoc.prettify_files and type == 'file' then + allowed = ldoc.prettify_files == 'show' or (module and module.type == 'file') + end + return allowed + end + + local function set_charset (ldoc,m) + m = m or ldoc.module + ldoc.doc_charset = (m and m.tags.charset) or ldoc.charset + end + + local module_template,_ = utils.readfile (path.join(args.template,ldoc.templ)) + if not module_template then + quit("template not found at '"..args.template.."' Use -l to specify directory containing ldoc.ltp") + end + + -- Runs a template on a module to generate HTML page. + local function templatize(template_str, ldoc, module) + local out, err = template.substitute(template_str, { + ldoc = ldoc, + module = module, + _escape = ldoc.template_escape + }) + if not out then + quit(("template failed for %s: %s"):format( + module and module.name or ldoc.output or "index", + err)) + end + if ldoc.postprocess_html then + out = ldoc.postprocess_html(out, module) + end + return cleanup_whitespaces(out) + end + + local css, custom_css = ldoc.css, ldoc.custom_css + ldoc.output = args.output + ldoc.ipairs = ipairs + ldoc.pairs = pairs + ldoc.print = print + + -- Bang out the index. + -- in single mode there is one module and the 'index' is the + -- documentation for that module. + ldoc.module = ldoc.single + if ldoc.single and args.one then + ldoc.kinds_allowed = {module = true, topic = true} + ldoc.one = true + end + ldoc.root = true + if ldoc.module then + ldoc.module.info = get_module_info(ldoc.module) + ldoc.module.ldoc = ldoc + save_and_set_ldoc(ldoc.module.tags.set) + end + set_charset(ldoc) + local out = templatize(module_template, ldoc, ldoc.module) + ldoc.root = false + restore_ldoc() + + check_directory(args.dir) -- make sure output directory is ok + + -- project icon + if ldoc.icon then + local dir_data = args.dir .. '/data' + if not path.isdir(dir_data) then + -- luacheck: push ignore lfs + lfs.mkdir(dir_data) + -- luacheck: pop + end + local file = require 'pl.file' + file.copy(ldoc.icon, dir_data) + end + + args.dir = args.dir .. path.sep + + if css then -- has CSS been copied? + check_file(args.dir..css, path.join(args.style,css)) + end + + if custom_css then -- has custom CSS been copied? + check_file(args.dir..custom_css, custom_css) + end + + -- write out the module index + out = cleanup_whitespaces(out) + writefile(args.dir..args.output..args.ext,out) + + -- in single mode, we exclude any modules since the module has been done; + -- ext step is then only for putting out any examples or topics + local mods = List() + for kind, modules in project() do + local lkind = kind:lower() + if not ldoc.single or ldoc.single and lkind ~= 'modules' then + mods:append {kind, lkind, modules} + end + end + + -- write out the per-module documentation + -- note that we reset the internal ordering of the 'kinds' so that + -- e.g. when reading a topic the other Topics will be listed first. + if css then + ldoc.css = '../'..css + end + if custom_css then + ldoc.custom_css = '../'..custom_css + end + for m in mods:iter() do + local kind, lkind, modules = unpack(m) + check_directory(args.dir..lkind) + if not ldoc.keep_menu_order then + project:put_kind_first(kind) + end + for m in modules() do + ldoc.module = m + ldoc.body = m.body + m.ldoc = ldoc + if m.tags.set then + save_and_set_ldoc(m.tags.set) + end + set_charset(ldoc) + m.info = get_module_info(m) + if ldoc.body and m.postprocess then + ldoc.body = m.postprocess(ldoc.body) + end + local out = templatize(module_template, ldoc, m) + writefile(args.dir..lkind..'/'..m.name..args.ext,out) + restore_ldoc() + end + end + if not args.quiet then print('output written to '..tools.abspath(args.dir)) end +end + +return html + diff --git a/Documentation/compiler/ldoc/ldoc/html/_code_css.lua b/Documentation/compiler/ldoc/ldoc/html/_code_css.lua new file mode 100644 index 000000000..36cffa1ea --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/_code_css.lua @@ -0,0 +1,19 @@ +return [[ + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + +]] diff --git a/Documentation/compiler/ldoc/ldoc/html/_reset_css.lua b/Documentation/compiler/ldoc/ldoc/html/_reset_css.lua new file mode 100644 index 000000000..ef7e0d70a --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/_reset_css.lua @@ -0,0 +1,66 @@ +return [[ +/* BEGIN RESET + +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 +*/ +html { + color: #000; + background: #FFF; +} +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { + margin: 0; + padding: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +fieldset,img { + border: 0; +} +address,caption,cite,code,dfn,em,strong,th,var,optgroup { + font-style: inherit; + font-weight: inherit; +} +del,ins { + text-decoration: none; +} +li { + margin-left: 20px; +} +caption,th { + text-align: left; +} +h1,h2,h3,h4,h5,h6 { + font-size: 100%; + font-weight: bold; +} +q:before,q:after { + content: ''; +} +abbr,acronym { + border: 0; + font-variant: normal; +} +sup { + vertical-align: baseline; +} +sub { + vertical-align: baseline; +} +legend { + color: #000; +} +input,button,textarea,select,optgroup,option { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; +} +input,button,textarea,select {*font-size:100%; +} +/* END RESET */ +]] diff --git a/Documentation/compiler/ldoc/ldoc/html/ldoc_css.lua b/Documentation/compiler/ldoc/ldoc/html/ldoc_css.lua new file mode 100644 index 000000000..1be506f6d --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/ldoc_css.lua @@ -0,0 +1,225 @@ +return require('ldoc.html._reset_css') .. [[ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 20px 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 14em; + vertical-align: top; + background-color: #f0f0f0; + overflow: visible; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; + padding: 1em; + width: 700px; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +ul.nowrap { + overflow:auto; + white-space:nowrap; +} + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + +]] +.. require('ldoc.html._code_css') diff --git a/Documentation/compiler/ldoc/ldoc/html/ldoc_fixed_css.lua b/Documentation/compiler/ldoc/ldoc/html/ldoc_fixed_css.lua new file mode 100644 index 000000000..98187c79c --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/ldoc_fixed_css.lua @@ -0,0 +1,233 @@ +return require('ldoc.html._reset_css') .. [[ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 0 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #ffffff; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color:#FFFFFF; // #f0f0f0; + border-left: 1px solid #cccccc; +} + +#navigation { + position: fixed; + top: 0; + left: 0; + float: left; + width: 14em; + vertical-align: top; + background-color:#FFFFFF; // #f0f0f0; + border-right: 2px solid #cccccc; + overflow: visible; + overflow-y: scroll; + height: 100%; + padding-left: 1em; +} + +#navigation h2 { + background-color:#FFFFFF;//:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; + padding: 1em; + padding-left: 2em; + width: 700px; + border-left: 2px solid #cccccc; + // border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding-left: 1em; + margin-left: 14em; // avoid the damn sidebar! + border-top: 2px solid #cccccc; + border-left: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; } +table.module_list td.summary { width: 100%; } + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +ul.nowrap { + overflow:auto; + whitespace:nowrap; +} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + +]] +.. require('ldoc.html._code_css') diff --git a/Documentation/compiler/ldoc/ldoc/html/ldoc_ltp.lua b/Documentation/compiler/ldoc/ldoc/html/ldoc_ltp.lua new file mode 100644 index 000000000..31b09d72c --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/ldoc_ltp.lua @@ -0,0 +1,324 @@ +return [==[ + + + + + $(ldoc.title) + +# if ldoc.custom_css then -- add custom CSS file if configured. + +# end + + + +
    + +
    + +
    +
    +
    + + +
    + +# local no_spaces = ldoc.no_spaces +# local use_li = ldoc.use_li +# local display_name = ldoc.display_name +# local iter = ldoc.modules.iter +# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end +# local nowrap = ldoc.wrap and '' or 'nowrap' + + + + + +
    + +# if ldoc.body then -- verbatim HTML as contents; 'non-code' entries + $(ldoc.body) +# elseif module then -- module documentation +

    $(ldoc.module_typename(module)) $(module.name)

    +

    $(M(module.summary,module))

    +

    $(M(module.description,module))

    +# if module.tags.include then + $(M(ldoc.include_file(module.tags.include))) +# end +# if module.see then +# local li,il = use_li(module.see) +

    See also:

    +
      +# for see in iter(module.see) do + $(li)$(see.label)$(il) +# end -- for +
    +# end -- if see +# if module.usage then +# local li,il = use_li(module.usage) +

    Usage:

    +
      +# for usage in iter(module.usage) do + $(li)
      $(ldoc.escape(usage))
      $(il) +# end -- for +
    +# end -- if usage +# if module.info then +

    Info:

    +
      +# for tag, value in module.info:iter() do +
    • $(tag): $(M(value,module))
    • +# end +
    +# end -- if module.info + + +# if not ldoc.no_summary then +# -- bang out the tables of item types for this module (e.g Functions, Tables, etc) +# for kind,items in module.kinds() do +

    $(kind)

    + +# for item in items() do + + + + +# end -- for items +
    $(display_name(item))$(M(item.summary,item))
    +#end -- for kinds + +
    +
    + +#end -- if not no_summary + +# --- currently works for both Functions and Tables. The params field either contains +# --- function parameters or table fields. +# local show_return = not ldoc.no_return_or_parms +# local show_parms = show_return +# for kind, items in module.kinds() do +# local kitem = module.kinds:get_item(kind) +# local has_description = kitem and ldoc.descript(kitem) ~= "" +

    $(kind)

    + $(M(module.kinds:get_section_description(kind),nil)) +# if kitem then +# if has_description then +
    + $(M(ldoc.descript(kitem),kitem)) +
    +# end +# if kitem.usage then +

    Usage:

    +
    $(ldoc.prettify(kitem.usage[1]))
    +# end +# end +
    +# for item in items() do +
    + + $(display_name(item)) +# if ldoc.prettify_files and ldoc.is_file_prettified[item.module.file.filename] then + line $(item.lineno) +# end +
    +
    + $(M(ldoc.descript(item),item)) + +# if ldoc.custom_tags then +# for custom in iter(ldoc.custom_tags) do +# local tag = item.tags[custom[1]] +# if tag and not custom.hidden then +# local li,il = use_li(tag) +

    $(custom.title or custom[1]):

    +
      +# for value in iter(tag) do + $(li)$(custom.format and custom.format(value) or M(value))$(il) +# end -- for +# end -- if tag +
    +# end -- iter tags +# end + +# if show_parms and item.params and #item.params > 0 then +# local subnames = module.kinds:type_of(item).subnames +# if subnames then +

    $(subnames):

    +# end +
      +# for parm in iter(item.params) do +# local param,sublist = item:subparam(parm) +# if sublist then +
    • $(sublist)$(M(item.params.map[sublist],item)) +
        +# end +# for p in iter(param) do +# local name,tp,def = item:display_name_of(p), ldoc.typename(item:type_of_param(p)), item:default_of_param(p) +
      • $(name) +# if tp ~= '' then + $(tp) +# end + $(M(item.params.map[p],item)) +# if def == true then + (optional) +# elseif def then + (default $(def)) +# end +# if item:readonly(p) then + readonly +# end +
      • +# end +# if sublist then +
      +# end +# end -- for +
    +# end -- if params + +# if show_return and item.retgroups then local groups = item.retgroups +

    Returns:

    +# for i,group in ldoc.ipairs(groups) do local li,il = use_li(group) +
      +# for r in group:iter() do local type, ctypes = item:return_type(r); local rt = ldoc.typename(type) + $(li) +# if rt ~= '' then + $(rt) +# end + $(M(r.text,item))$(il) +# if ctypes then +
        +# for c in ctypes:iter() do +
      • $(c.name) + $(ldoc.typename(c.type)) + $(M(c.comment,item))
      • +# end +
      +# end -- if ctypes +# end -- for r +
    +# if i < #groups then +

    Or

    +# end +# end -- for group +# end -- if returns + +# if show_return and item.raise then +

    Raises:

    + $(M(item.raise,item)) +# end + +# if item.see then +# local li,il = use_li(item.see) +

    See also:

    +
      +# for see in iter(item.see) do + $(li)$(see.label)$(il) +# end -- for +
    +# end -- if see + +# if item.usage then +# local li,il = use_li(item.usage) +

    Usage:

    +
      +# for usage in iter(item.usage) do + $(li)
      $(ldoc.prettify(usage))
      $(il) +# end -- for +
    +# end -- if usage + +
    +# end -- for items +
    +# end -- for kinds + +# else -- if module; project-level contents + +# if ldoc.description then +

    $(M(ldoc.description,nil))

    +# end +# if ldoc.full_description then +

    $(M(ldoc.full_description,nil))

    +# end + +# for kind, mods in ldoc.kinds() do +

    $(kind)

    +# kind = kind:lower() + +# for m in mods() do + + + + +# end -- for modules +
    $(m.name)$(M(ldoc.strip_header(m.summary),m))
    +# end -- for kinds +# end -- if module + +
    +
    +
    +generated by LDoc $(ldoc.version) +Last updated $(ldoc.updatetime) +
    +
    + + +]==] + diff --git a/Documentation/compiler/ldoc/ldoc/html/ldoc_md_ltp.lua b/Documentation/compiler/ldoc/ldoc/html/ldoc_md_ltp.lua new file mode 100644 index 000000000..ca39766ee --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/ldoc_md_ltp.lua @@ -0,0 +1,17 @@ +return [[ +> local lev = ldoc.level or 2 +> local lev1,lev2 = ('#'):rep(lev),('#'):rep(lev+1) +> for kind, items in module.kinds() do +> local kitem = module.kinds:get_item(kind) +> if kitem then +$(lev1) $(ldoc.descript(kitem)) + +> end +> for item in items() do +$(lev2) $(ldoc.display_name(item)) + +$(ldoc.descript(item)) + +> end +> end +]] diff --git a/Documentation/compiler/ldoc/ldoc/html/ldoc_new_css.lua b/Documentation/compiler/ldoc/ldoc/html/ldoc_new_css.lua new file mode 100644 index 000000000..eb57e9b04 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/ldoc_new_css.lua @@ -0,0 +1,292 @@ +return [[ +body { + color: #47555c; + font-size: 16px; + font-family: "Open Sans", sans-serif; + margin: 0; + background: #eff4ff; +} + +a:link { color: #008fee; } +a:visited { color: #008fee; } +a:hover { color: #22a7ff; } + +h1 { font-size:26px; font-weight: normal; } +h2 { font-size:22px; font-weight: normal; } +h3 { font-size:18px; font-weight: normal; } +h4 { font-size:16px; font-weight: bold; } + +hr { + height: 1px; + background: #c1cce4; + border: 0px; + margin: 15px 0; +} + +code, tt { + font-family: monospace; +} +span.parameter { + font-family: monospace; + font-weight: bold; + color: rgb(99, 115, 131); +} +span.parameter:after { + content:":"; +} +span.types:before { + content:"("; +} +span.types:after { + content:")"; +} +.type { + font-weight: bold; font-style:italic +} + +p.name { + font-family: "Andale Mono", monospace; +} + +#navigation { + float: left; + background-color: white; + border-right: 1px solid #d3dbec; + border-bottom: 1px solid #d3dbec; + + width: 14em; + vertical-align: top; + overflow: visible; +} + +#navigation br { + display: none; +} + +#navigation h1 { + background-color: white; + border-bottom: 1px solid #d3dbec; + padding: 15px; + margin-top: 0px; + margin-bottom: 0px; +} + +#navigation h2 { + font-size: 18px; + background-color: white; + border-bottom: 1px solid #d3dbec; + padding-left: 15px; + padding-right: 15px; + padding-top: 10px; + padding-bottom: 10px; + margin-top: 30px; + margin-bottom: 0px; +} + +#content h1 { + background-color: #2c3e67; + color: white; + padding: 15px; + margin: 0px; +} + +#content h2 { + background-color: #6c7ea7; + color: white; + padding: 15px; + padding-top: 15px; + padding-bottom: 15px; + margin-top: 0px; +} + +#content h2 a { + background-color: #6c7ea7; + color: white; + text-decoration: none; +} + +#content h2 a:hover { + text-decoration: underline; +} + +#content h3 { + font-style: italic; + padding-top: 15px; + padding-bottom: 4px; + margin-right: 15px; + margin-left: 15px; + margin-bottom: 5px; + border-bottom: solid 1px #bcd; +} + +#content h4 { + margin-right: 15px; + margin-left: 15px; + border-bottom: solid 1px #bcd; +} + +#content pre { + margin: 15px; +} + +pre { + background-color: rgb(50, 55, 68); + color: white; + border-radius: 3px; + /* border: 1px solid #C0C0C0; /* silver */ + padding: 15px; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +#content ul pre.example { + margin-left: 0px; +} + +table.index { +/* border: 1px #00007f; */ +} +table.index td { text-align: left; vertical-align: top; } + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; +} + +#content p { + padding-left: 15px; + padding-right: 15px; +} + +#content table { + padding-left: 15px; + padding-right: 15px; + background-color: white; +} + +#content p, #content table, #content ol, #content ul, #content dl { + max-width: 900px; +} + +#about { + padding: 15px; + padding-left: 16em; + background-color: white; + border-top: 1px solid #d3dbec; + border-bottom: 1px solid #d3dbec; +} + +table.module_list, table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; + margin: 15px; +} +table.module_list td, table.function_list td { + border-width: 1px; + padding-left: 10px; + padding-right: 10px; + padding-top: 5px; + padding-bottom: 5px; + border: solid 1px rgb(193, 204, 228); +} +table.module_list td.name, table.function_list td.name { + background-color: white; min-width: 200px; border-right-width: 0px; +} +table.module_list td.summary, table.function_list td.summary { + background-color: white; width: 100%; border-left-width: 0px; +} + +dl.function { + margin-right: 15px; + margin-left: 15px; + border-bottom: solid 1px rgb(193, 204, 228); + border-left: solid 1px rgb(193, 204, 228); + border-right: solid 1px rgb(193, 204, 228); + background-color: white; +} + +dl.function dt { + color: rgb(99, 123, 188); + font-family: monospace; + border-top: solid 1px rgb(193, 204, 228); + padding: 15px; +} + +dl.function dd { + margin-left: 15px; + margin-right: 15px; + margin-top: 5px; + margin-bottom: 15px; +} + +#content dl.function dd h3 { + margin-top: 0px; + margin-left: 0px; + padding-left: 0px; + font-size: 16px; + color: rgb(128, 128, 128); + border-bottom: solid 1px #def; +} + +#content dl.function dd ul, #content dl.function dd ol { + padding: 0px; + padding-left: 15px; + list-style-type: none; +} + +ul.nowrap { + overflow:auto; + white-space:nowrap; +} + +.section-description { + padding-left: 15px; + padding-right: 15px; +} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + + +/* styles for prettification of source */ +pre .comment { color: #bbccaa; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #ffc090; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #c040c0; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } +]] diff --git a/Documentation/compiler/ldoc/ldoc/html/ldoc_one_css.lua b/Documentation/compiler/ldoc/ldoc/html/ldoc_one_css.lua new file mode 100644 index 000000000..956461f6a --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/ldoc_one_css.lua @@ -0,0 +1,202 @@ +return require('ldoc.html._reset_css') .. [[ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 10px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 0 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #2808FF; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f5f5f5; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; +} + +#navigation { + float: top; + vertical-align: top; + background-color: #f5f5f5; + overflow: visible; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + padding: 1em; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f5f5f5; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { + background-color: #f5f5f5; + white-space: normal; /* voids the "nowrap" in HTML */ +} +table.function_list td.summary { width: 100%; } + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +]] +.. require('ldoc.html._code_css') diff --git a/Documentation/compiler/ldoc/ldoc/html/ldoc_pale_css.lua b/Documentation/compiler/ldoc/ldoc/html/ldoc_pale_css.lua new file mode 100644 index 000000000..ffc91a286 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/html/ldoc_pale_css.lua @@ -0,0 +1,225 @@ +return require('ldoc.html._reset_css') .. [[ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 0 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #ffffff; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color:#FFFFFF; // #f0f0f0; + //border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 14em; + vertical-align: top; + background-color:#FFFFFF; // #f0f0f0; + border-right: 2px solid #cccccc; + overflow: visible; +} + +#navigation h2 { + background-color:#FFFFFF;//:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + //border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; + padding: 1em; + width: 700px; + border-left: 2px solid #cccccc; + // border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; } +table.module_list td.summary { width: 100%; } + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +ul.nowrap { + overflow:auto; + whitespace:nowrap; +} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + +]] +.. require('ldoc.html._code_css') diff --git a/Documentation/compiler/ldoc/ldoc/lang.lua b/Documentation/compiler/ldoc/ldoc/lang.lua new file mode 100644 index 000000000..4ef1859d3 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/lang.lua @@ -0,0 +1,379 @@ +------------ +-- Language-dependent parsing of code. +-- This encapsulates the different strategies needed for parsing C and Lua +-- source code. + +local class = require 'pl.class' +local utils = require 'pl.utils' +local List = require 'pl.List' +local tools = require 'ldoc.tools' +local lexer = require 'ldoc.lexer' +local quit = utils.quit +local tnext = lexer.skipws + + +local Lang = class() + +function Lang:trim_comment (s) + return s:gsub(self.line_comment,'') +end + +function Lang:start_comment (v) + local line = v:match (self.start_comment_) + if line and self.end_comment_ and v:match (self.end_comment_) then + return nil + end + local block = v:match(self.block_comment) + return line or block, block +end + +function Lang:empty_comment (v) + return v:match(self.empty_comment_) +end + +function Lang:grab_block_comment(v,tok) + v = v:gsub(self.block_comment,'') + return tools.grab_block_comment(v,tok,self.end_comment) +end + +function Lang:find_module(tok,t,v) + return '...',t,v +end + +function Lang:item_follows(t,v) + return false +end + +function Lang:finalize() + self.empty_comment_ = self.start_comment_..'%s*$' +end + +function Lang:search_for_token (tok,type,value,t,v) + while t and not (t == type and v == value) do + if t == 'comment' and self:start_comment(v) then return nil,t,v end + t,v = tnext(tok) + end + return t ~= nil,t,v +end + +function Lang:parse_extra (tags,tok) +end + +function Lang:is_module_modifier () + return false +end + +function Lang:parse_module_modifier (tags, tok) + return nil, "@usage or @exports deduction not implemented for this language" +end + + +local Lua = class(Lang) + +function Lua:_init() + self.line_comment = '^%-%-+' -- used for stripping + self.start_comment_ = '^%-%-%-+' -- used for doc comment line start + self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments + self.end_comment_ = '[^%-]%-%-+[^-]*\n$' ---- exclude --- this kind of comment --- + self.method_call = ':' + self:finalize() +end + +function Lua.lexer(fname) + local f,e = io.open(fname) + if not f then quit(e) end + return lexer.lua(f,{}),f +end + +function Lua:grab_block_comment(v,tok) + local equals = v:match('^%-%-%[(=*)%[') + if not equals then return v end + v = v:gsub(self.block_comment,'') + return tools.grab_block_comment(v,tok,'%]'..equals..'%]') +end + + +-- luacheck: push ignore 312 +function Lua:parse_module_call(tok,t,v) + t,v = tnext(tok) + if t == '(' then t,v = tnext(tok) end + if t == 'string' then -- explicit name, cool + return v,t,v + elseif t == '...' then -- we have to guess! + return '...',t,v + end +end +-- luacheck: pop + +-- If a module name was not provided, then we look for an explicit module() +-- call. However, we should not try too hard; if we hit a doc comment then +-- we should go back and process it. Likewise, module(...) also means +-- that we must infer the module name. +function Lua:find_module(tok,t,v) + local res + res,t,v = self:search_for_token(tok,'iden','module',t,v) + if not res then return nil,t,v end + return self:parse_module_call(tok,t,v) +end + +local function parse_lua_parameters (tags,tok) + tags.formal_args = tools.get_parameters(tok) + tags:add('class','function') +end + +local function parse_lua_function_header (tags,tok) + if not tags.name then + tags:add('name',tools.get_fun_name(tok)) + end + if not tags.name then return 'function has no name' end + parse_lua_parameters(tags,tok) +end + +local function parse_lua_table (tags,tok) + tags.formal_args = tools.get_parameters(tok,'}',function(s) + return s == ',' or s == ';' + end) +end + +--------------- function and variable inferrence ----------- +-- After a doc comment, there may be a local followed by: +-- [1] (l)function: function NAME +-- [2] (l)function: NAME = function +-- [3] table: NAME = { +-- [4] field: NAME = (this is a module-level field) +-- +-- Depending on the case successfully detected, returns a function which +-- will be called later to fill in inferred item tags +function Lua:item_follows(t,v,tok) + local parser, case + local is_local = t == 'keyword' and v == 'local' + if is_local then t,v = tnext(tok) end + if t == 'keyword' and v == 'function' then -- case [1] + case = 1 + parser = parse_lua_function_header + elseif t == 'iden' then + local name,t,_ = tools.get_fun_name(tok,v) + if t ~= '=' then return nil,"not 'name = function,table or value'" end + t,v = tnext(tok) + if t == 'keyword' and v == 'function' then -- case [2] + tnext(tok) -- skip '(' + case = 2 + parser = function(tags,tok) + tags:add('name',name) + parse_lua_parameters(tags,tok) + end + elseif t == '{' then -- case [3] + case = 3 + parser = function(tags,tok) + tags:add('class','table') + tags:add('name',name) + parse_lua_table (tags,tok) + end + else -- case [4] + case = 4 + parser = function(tags) + tags:add('class','field') + tags:add('name',name) + end + end + elseif t == 'keyword' and v == 'return' then + t, v = tnext(tok) + if t == 'keyword' and v == 'function' then + -- return function(a, b, c) + tnext(tok) -- skip '(' + case = 2 + parser = parse_lua_parameters + elseif t == '{' then + -- return {...} + case = 5 + parser = function(tags,tok) + tags:add('class','table') + parse_lua_table(tags,tok) + end + else + return nil,'not returning function or table' + end + else + return nil,"not 'name=value' or 'return value'" + end + return parser, is_local, case +end + + +-- we only call the function returned by the item_follows above if there +-- is not already a name and a type. +-- Otherwise, this is called. Currrently only tries to fill in the fields +-- of a table from a table definition as identified above +function Lua:parse_extra (tags,tok,case) + if tags.class == 'table' and not tags.field and case == 3 then + parse_lua_table(tags,tok) + end +end + +-- For Lua, a --- @usage comment means that a long +-- string containing the usage follows, which we +-- use to update the module usage tag. Likewise, the @export +-- tag alone in a doc comment refers to the following returned +-- Lua table of functions + + +function Lua:is_module_modifier (tags) + return tags.summary == '' and (tags.usage or tags.export) +end + +-- Allow for private name convention. +function Lua:is_private_var (name) + return name:match '^_' or name:match '_$' +end + +function Lua:parse_module_modifier (tags, tok, F) + if tags.usage then + if tags.class ~= 'field' then return nil,"cannot deduce @usage" end + local t1= tnext(tok) + if t1 ~= '[' then return nil, t1..' '..': not a long string' end + local _, v = tools.grab_block_comment('',tok,'%]%]') + return true, v, 'usage' + elseif tags.export then + if tags.class ~= 'table' then return nil, "cannot deduce @export" end + for f in tags.formal_args:iter() do + if not self:is_private_var(f) then + F:export_item(f) + end + end + return true + end +end + + +-- note a difference here: we scan C/C++ code in full-text mode, not line by line. +-- This is because we can't detect multiline comments in line mode. +-- Note: this applies to C/C++ code used to generate _Lua_ documentation! + +local CC = class(Lang) + +function CC:_init() + self.line_comment = '^//+' + self.start_comment_ = '^///+' + self.block_comment = '^/%*%*+' + self.method_call = ':' + self:finalize() +end + +function CC.lexer(f) + local err + f,err = utils.readfile(f) + if not f then quit(err) end + return lexer.cpp(f,{},nil,true) +end + +function CC:grab_block_comment(v,tok) + v = v:gsub(self.block_comment,''):gsub('\n%s*%*','\n') + return 'comment',v:sub(1,-3) +end + +--- here the argument name is always last, and the type is composed of any tokens before +function CC:extract_arg (tl,idx) + idx = idx or 1 + local res = List() + for i = idx,#tl-1 do + res:append(tl[i][2]) + end + local type = res:join ' ' + return tl[#tl][2], type +end + +function CC:item_follows (t,v,tok) + if not self.extra.C then + return false + end + if t == 'iden' or t == 'keyword' then -- + local _ + if v == self.extra.export then -- this is not part of the return type! + _,v = tnext(tok) + end + -- types may have multiple tokens: example, const char *bonzo(...) + local return_type, name = v + _,v = tnext(tok) + name = v + t,v = tnext(tok) + while t ~= '(' do + return_type = return_type .. ' ' .. name + name = v + t,v = tnext(tok) + end + --print ('got',name,t,v,return_type) + return function(tags,tok) + if not tags.name then + tags:add('name',name) + end + tags:add('class','function') + if t == '(' then + tags.formal_args,t,_ = tools.get_parameters(tok,')',',',self) + if return_type ~= 'void' then + tags.formal_args.return_type = return_type + end + end + end + end + return false +end + +local Moon = class(Lua) + +function Moon:_init() + self.line_comment = '^%-%-+' -- used for stripping + self.start_comment_ = '^%s*%-%-%-+' -- used for doc comment line start + self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments + self.end_comment_ = '[^%-]%-%-+\n$' ---- exclude --- this kind of comment --- + self.method_call = '\\' + self:finalize() +end + +--- much like Lua, BUT auto-assign parameters start with @ +function Moon:extract_arg (tl,idx) + idx = idx or 1 + local auto_assign = tl[idx][1] == '@' + if auto_assign then idx = idx + 1 end + local res = tl[idx][2] + return res +end + +function Moon:item_follows (t,v,tok) + if t == '.' then -- enclosed in with statement + t,v = tnext(tok) + end + if t == 'iden' then + local name,t,v = tools.get_fun_name(tok,v,'') + if name == 'class' then + local _ + name,_,_ = tools.get_fun_name(tok,v,'') + -- class! + return function(tags,tok) + tags:add('class','type') + tags:add('name',name) + end + elseif t == '=' or t == ':' then -- function/method + local _ + t,_ = tnext(tok) + return function(tags,tok) + if not tags.name then + tags:add('name',name) + end + if t == '(' then + tags.formal_args,t,_ = tools.get_parameters(tok,')',',',self) + else + tags.formal_args = List() + end + t,_ = tnext(tok) + tags:add('class','function') + if t ~= '>' then + tags.static = true + end + end + else + return nil, "expecting '=' or ':'" + end + end +end + +return { lua = Lua(), cc = CC(), moon = Moon() } diff --git a/Documentation/compiler/ldoc/ldoc/lexer.lua b/Documentation/compiler/ldoc/ldoc/lexer.lua new file mode 100644 index 000000000..aafb07d30 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/lexer.lua @@ -0,0 +1,504 @@ +--- Lexical scanner for creating a sequence of tokens from text.
    +--

    lexer.scan(s) returns an iterator over all tokens found in the +-- string s. This iterator returns two values, a token type string +-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the +-- token. +--

    +-- Versions specialized for Lua and C are available; these also handle block comments +-- and classify keywords as 'keyword' tokens. For example: +--

    +-- > s = 'for i=1,n do'
    +-- > for t,v in lexer.lua(s)  do print(t,v) end
    +-- keyword for
    +-- iden    i
    +-- =       =
    +-- number  1
    +-- ,       ,
    +-- iden    n
    +-- keyword do
    +-- 
    +-- +-- Based on pl.lexer from Penlight + +local strfind = string.find +local strsub = string.sub +local append = table.insert + +local function assert_arg(idx,val,tp) + if type(val) ~= tp then + error("argument "..idx.." must be "..tp, 2) + end +end + +local lexer = {} + +local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER2 = '^[%+%-]?%d+%.?%d*' +local NUMBER3 = '^0x[%da-fA-F]+' +local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER5 = '^%d+%.?%d*' +local IDEN = '^[%a_][%w_]*' +local WSPACE = '^%s+' +local STRING1 = [[^'.-[^\\]']] +local STRING2 = [[^".-[^\\]"]] +local STRING3 = "^((['\"])%2)" -- empty string +local PREPRO = '^#.-[^\\]\n' + +local plain_matches,lua_matches,cpp_matches,cpp_matches_no_string,lua_keyword,cpp_keyword + +local function tdump(tok) + return tok,tok +end + +local function ndump(tok,options) + if options and options.number then + tok = tonumber(tok) + end + return "number",tok +end + +-- regular strings, single or double quotes; usually we want them +-- without the quotes +local function sdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "string",tok +end + +-- strings enclosed in back ticks +local function bdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "backtick",tok +end + +-- long Lua strings need extra work to get rid of the quotes +local function sdump_l(tok,options) + if options and options.string then + tok = tok:sub(3,-3) + end + return "string",tok +end + +local function chdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "char",tok +end + +local function cdump(tok) + return 'comment',tok +end + +local function wsdump (tok) + return "space",tok +end + +local function pdump (tok) + return 'prepro',tok +end + +local function plain_vdump(tok) + return "iden",tok +end + +local function lua_vdump(tok) + if lua_keyword[tok] then + return "keyword",tok + else + return "iden",tok + end +end + +local function cpp_vdump(tok) + if cpp_keyword[tok] then + return "keyword",tok + else + return "iden",tok + end +end + +local function count_lines(line, text) + local index, limit = 1, #text + while index <= limit do + local start, stop = text:find('\r\n', index, true) + if not start then + start, stop = text:find('[\r\n\f]', index) + if not start then break end + end + index = stop + 1 + line = line + 1 + end + return line +end + +local multiline = { comment = true, space = true } + + +--- create a plain token iterator from a string or file-like object. +-- @param s the string +-- @param matches an optional match table (set of pattern-action pairs) +-- @param filter a table of token types to exclude, by default {space=true} +-- @param options a table of options; by default, {number=true,string=true}, +-- which means convert numbers and strip string quotes. +function lexer.scan (s,matches,filter,options) + --assert_arg(1,s,'string') + local file = type(s) ~= 'string' and s + filter = filter or {space=true} + options = options or {number=true,string=true} + if filter then + if filter.space then filter[wsdump] = true end + if filter.comments then + filter[cdump] = true + end + end + if not matches then + if not plain_matches then + plain_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,plain_vdump}, + {NUMBER1,ndump}, + {NUMBER2,ndump}, + {STRING3,sdump}, + {STRING1,sdump}, + {STRING2,sdump}, + {'^.',tdump} + } + end + matches = plain_matches + end + local i1,i2,tok,pat,fun + local line = 1 + if file then + s = file:read() + if not s then return nil end -- empty file + if s:match '^\239\187' then -- UTF-8 BOM Abomination + s = s:sub(4) + end + s = s ..'\n' + end + local sz = #s + local idx = 1 + if sz == 0 then return nil end -- empty file + + local res = {} + local mt = {} + mt.__index = mt + setmetatable(res,mt) + + function mt.lineno() return line end + + function mt.getline() + if idx < sz then + tok = strsub(s,idx,-2) + idx = sz + 1 + line = line + 1 + return tok + else + idx = sz + 1 + line = line + 1 + return file:read() + end + end + + function mt.next (tok) + local t,v = tok() + while t == 'space' do + t,v = tok() + end + return t,v + end + + function mt.__call () + if not s then return end + while true do + for _,m in ipairs(matches) do + pat,fun = m[1],m[2] + if fun == nil then error("no match for "..pat) end + i1,i2 = strfind(s,pat,idx) + if i1 then + tok = strsub(s,i1,i2) + idx = i2 + 1 + if not (filter and filter[fun]) then + lexer.finished = idx > sz + local t,v = fun(tok,options) + if not file and multiline[t] then + line = count_lines(line,v) + end + return t,v + end + end + end + if idx > sz then + if file then + line = line + 1 + s = file:read() + if not s then return end + s = s .. '\n' + idx ,sz = 1,#s + else + return + end + end + end + end + return res +end + +--- get everything in a stream upto a newline. +-- @param tok a token stream +-- @return a string +function lexer.getline (tok) + return tok:getline() +end + +--- get current line number.
    +-- Only available if the input source is a file-like object. +-- @param tok a token stream +-- @return the line number and current column +function lexer.lineno (tok) + return tok:lineno() +end + +--- get the Lua keywords as a set-like table. +-- So res["and"] etc would be true. +-- @return a table +function lexer.get_keywords () + if not lua_keyword then + lua_keyword = { + ["and"] = true, ["break"] = true, ["do"] = true, + ["else"] = true, ["elseif"] = true, ["end"] = true, + ["false"] = true, ["for"] = true, ["function"] = true, + ["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true, + ["not"] = true, ["or"] = true, ["repeat"] = true, + ["return"] = true, ["then"] = true, ["true"] = true, + ["until"] = true, ["while"] = true + } + end + return lua_keyword +end + + +--- create a Lua token iterator from a string or file-like object. +-- Will return the token type and value. +-- @param s the string +-- @param filter a table of token types to exclude, by default {space=true,comments=true} +-- @param options a table of options; by default, {number=true,string=true}, +-- which means convert numbers and strip string quotes. +function lexer.lua(s,filter,options) + filter = filter or {space=true,comments=true} + lexer.get_keywords() + if not lua_matches then + lua_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,lua_vdump}, + {NUMBER4,ndump}, + {NUMBER5,ndump}, + {STRING3,sdump}, + {STRING1,sdump}, + {STRING2,sdump}, + {'^`[^`]+`',bdump}, + {'^%-%-%[(=*)%[.-%]%1%]',cdump}, + {'^%-%-.-\n',cdump}, + {'^%-%-.-$',cdump}, + {'^%[(=*)%[.-%]%1%]',sdump_l}, + {'^==',tdump}, + {'^~=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^%.%.%.',tdump}, + {'^%.%.',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s,lua_matches,filter,options) +end + +--- create a C/C++ token iterator from a string or file-like object. +-- Will return the token type type and value. +-- @param s the string +-- @param filter a table of token types to exclude, by default {space=true,comments=true} +-- @param options a table of options; by default, {number=true,string=true}, +-- which means convert numbers and strip string quotes. +function lexer.cpp(s,filter,options,no_string) + filter = filter or {comments=true} + if not cpp_keyword then + cpp_keyword = { + ["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true, + ["else"] = true, ["continue"] = true, ["struct"] = true, + ["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true, + ["private"] = true, ["protected"] = true, ["goto"] = true, + ["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true, + ["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true, + ["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true, + ["double"] = true, ["while"] = true, ["new"] = true, + ["namespace"] = true, ["try"] = true, ["catch"] = true, + ["switch"] = true, ["case"] = true, ["extern"] = true, + ["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true, + ["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true, + } + end + if not cpp_matches then + cpp_matches = { + {WSPACE,wsdump}, + {PREPRO,pdump}, + {NUMBER3,ndump}, + {IDEN,cpp_vdump}, + {NUMBER4,ndump}, + {NUMBER5,ndump}, + {STRING3,sdump}, + {STRING1,chdump}, + {STRING2,sdump}, + {'^//.-\n',cdump}, + {'^//.-$',cdump}, + {'^/%*.-%*/',cdump}, + {'^==',tdump}, + {'^!=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^->',tdump}, + {'^&&',tdump}, + {'^||',tdump}, + {'^%+%+',tdump}, + {'^%-%-',tdump}, + {'^%+=',tdump}, + {'^%-=',tdump}, + {'^%*=',tdump}, + {'^/=',tdump}, + {'^|=',tdump}, + {'^%^=',tdump}, + {'^::',tdump}, + {'^%.%.%.',tdump}, + {'^.',tdump} + } + end + if not cpp_matches_no_string then + cpp_matches_no_string = { + {WSPACE,wsdump}, + {PREPRO,pdump}, + {NUMBER3,ndump}, + {IDEN,cpp_vdump}, + {NUMBER4,ndump}, + {NUMBER5,ndump}, + {'^//.-\n',cdump}, + {'^/%*.-%*/',cdump}, + {'^==',tdump}, + {'^!=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^->',tdump}, + {'^&&',tdump}, + {'^||',tdump}, + {'^%+%+',tdump}, + {'^%-%-',tdump}, + {'^%+=',tdump}, + {'^%-=',tdump}, + {'^%*=',tdump}, + {'^/=',tdump}, + {'^|=',tdump}, + {'^%^=',tdump}, + {'^::',tdump}, + {'^%.%.%.',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s, + not no_string and cpp_matches or cpp_matches_no_string, + filter,options) +end + +--- get a list of parameters separated by a delimiter from a stream. +-- @param tok the token stream +-- @param endtoken end of list (default ')'). Can be '\n' +-- @param delim separator (default ',') +-- @return a list of token lists. +function lexer.get_separated_list(tok,endtoken,delim) + endtoken = endtoken or ')' + delim = delim or ',' + local function tappend (tl,t,val) + val = val or t + append(tl,{t,val}) + end + local is_end + if endtoken == '\n' then + is_end = function(t,val) + return t == 'space' and val:find '\n' + end + else + is_end = function (t) + return t == endtoken + end + end + local is_delim + if type(delim) == 'function' then + is_delim = delim + else + is_delim = function(t) + return t == delim + end + end + local parm_values = {} + local level = 1 -- used to count ( and ) + local tl = {} + local token,value + while true do + token,value=tok() + if not token then return nil,'EOS' end -- end of stream is an error! + if is_end(token,value) and level == 1 then + if next(tl) then + append(parm_values,tl) + end + break + elseif token == '(' then + level = level + 1 + tappend(tl,'(') + elseif token == ')' then + level = level - 1 + if level == 0 then -- finished with parm list + append(parm_values,tl) + break + else + tappend(tl,')') + end + elseif level == 1 and is_delim(token) then + append(parm_values,tl) -- a new parm + tl = {} + else + tappend(tl,token,value) + end + end + return parm_values,{token,value} +end + +--- get the next non-space token from the stream. +-- @param tok the token stream. +function lexer.skipws (tok) + return tok:next() +end + +local skipws = lexer.skipws + +--- get the next token, which must be of the expected type. +-- Throws an error if this type does not match! +-- @param tok the token stream +-- @param expected_type the token type +-- @param no_skip_ws whether we should skip whitespace +function lexer.expecting (tok,expected_type,no_skip_ws) + assert_arg(1,tok,'function') + assert_arg(2,expected_type,'string') + local t,v + if no_skip_ws then + t,v = tok() + else + t,v = skipws(tok) + end + if t ~= expected_type then error ("expecting "..expected_type,2) end + return v +end + +return lexer diff --git a/Documentation/compiler/ldoc/ldoc/markdown.lua b/Documentation/compiler/ldoc/ldoc/markdown.lua new file mode 100644 index 000000000..2044bb707 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/markdown.lua @@ -0,0 +1,1357 @@ +#!/usr/bin/env lua + +--[[ +# markdown.lua -- version 0.32 + + + +**Author:** Niklas Frykholm, +**Date:** 31 May 2008 + +This is an implementation of the popular text markup language Markdown in pure Lua. +Markdown can convert documents written in a simple and easy to read text format +to well-formatted HTML. For a more thourough description of Markdown and the Markdown +syntax, see . + +The original Markdown source is written in Perl and makes heavy use of advanced +regular expression techniques (such as negative look-ahead, etc) which are not available +in Lua's simple regex engine. Therefore this Lua port has been rewritten from the ground +up. It is probably not completely bug free. If you notice any bugs, please report them to +me. A unit test that exposes the error is helpful. + +## Usage + + require "markdown" + markdown(source) + +``markdown.lua`` exposes a single global function named ``markdown(s)`` which applies the +Markdown transformation to the specified string. + +``markdown.lua`` can also be used directly from the command line: + + lua markdown.lua test.md + +Creates a file ``test.html`` with the converted content of ``test.md``. Run: + + lua markdown.lua -h + +For a description of the command-line options. + +``markdown.lua`` uses the same license as Lua, the MIT license. + +## License + +Copyright © 2008 Niklas Frykholm. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Version history + +- **0.32** -- 31 May 2008 + - Fix for links containing brackets +- **0.31** -- 1 Mar 2008 + - Fix for link definitions followed by spaces +- **0.30** -- 25 Feb 2008 + - Consistent behavior with Markdown when the same link reference is reused +- **0.29** -- 24 Feb 2008 + - Fix for
     blocks with spaces in them
    +-	**0.28** -- 18 Feb 2008
    +	-	Fix for link encoding
    +-	**0.27** -- 14 Feb 2008
    +	-	Fix for link database links with ()
    +-	**0.26** -- 06 Feb 2008
    +	-	Fix for nested italic and bold markers
    +-	**0.25** -- 24 Jan 2008
    +	-	Fix for encoding of naked <
    +-	**0.24** -- 21 Jan 2008
    +	-	Fix for link behavior.
    +-	**0.23** -- 10 Jan 2008
    +	-	Fix for a regression bug in longer expressions in italic or bold.
    +-	**0.22** -- 27 Dec 2007
    +	-	Fix for crash when processing blocks with a percent sign in them.
    +-	**0.21** -- 27 Dec 2007
    +	- 	Fix for combined strong and emphasis tags
    +-	**0.20** -- 13 Oct 2007
    +	-	Fix for < as well in image titles, now matches Dingus behavior
    +-	**0.19** -- 28 Sep 2007
    +	-	Fix for quotation marks " and ampersands & in link and image titles.
    +-	**0.18** -- 28 Jul 2007
    +	-	Does not crash on unmatched tags (behaves like standard markdown)
    +-	**0.17** -- 12 Apr 2007
    +	-	Fix for links with %20 in them.
    +-	**0.16** -- 12 Apr 2007
    +	-	Do not require arg global to exist.
    +-	**0.15** -- 28 Aug 2006
    +	-	Better handling of links with underscores in them.
    +-	**0.14** -- 22 Aug 2006
    +	-	Bug for *`foo()`*
    +-	**0.13** -- 12 Aug 2006
    +	-	Added -l option for including stylesheet inline in document.
    +	-	Fixed bug in -s flag.
    +	-	Fixed emphasis bug.
    +-	**0.12** -- 15 May 2006
    +	-	Fixed several bugs to comply with MarkdownTest 1.0 
    +-	**0.11** -- 12 May 2006
    +	-	Fixed bug for escaping `*` and `_` inside code spans.
    +	-	Added license terms.
    +	-	Changed join() to table.concat().
    +-	**0.10** -- 3 May 2006
    +	-	Initial public release.
    +
    +// Niklas
    +]]
    +
    +
    +-- Set up a table for holding local functions to avoid polluting the global namespace
    +local M = {}
    +local unpack = unpack or table.unpack
    +local MT = {__index = _G}
    +setmetatable(M, MT)
    +
    +----------------------------------------------------------------------
    +-- Utility functions
    +----------------------------------------------------------------------
    +
    +-- Locks table t from changes, writes an error if someone attempts to change the table.
    +-- This is useful for detecting variables that have "accidently" been made global. Something
    +-- I tend to do all too much.
    +function M.lock(t)
    +	local function lock_new_index(t, k, v)
    +		error("module has been locked -- " .. k .. " must be declared local", 2)
    +	end
    +
    +	local mt = {__newindex = lock_new_index}
    +	if getmetatable(t) then
    +      mt.__index = getmetatable(t).__index
    +   end
    +	setmetatable(t, mt)
    +end
    +
    +-- Returns the result of mapping the values in table t through the function f
    +local function map(t, f)
    +	local out = {}
    +	for k,v in pairs(t) do out[k] = f(v,k) end
    +	return out
    +end
    +
    +-- Functional style if statement. (NOTE: no short circuit evaluation)
    +local function iff(t, a, b) if t then return a else return b end end
    +
    +-- Splits the text into an array of separate lines.
    +local function split(text, sep)
    +	sep = sep or "\n"
    +	local lines = {}
    +	local pos = 1
    +	while true do
    +		local b,e = text:find(sep, pos)
    +		if not b then table.insert(lines, text:sub(pos)) break end
    +		table.insert(lines, text:sub(pos, b-1))
    +		pos = e + 1
    +	end
    +	return lines
    +end
    +
    +-- Converts tabs to spaces
    +local function detab(text)
    +	local tab_width = 4
    +	local function rep(match)
    +		local spaces = -match:len()
    +		while spaces<1 do spaces = spaces + tab_width end
    +		return match .. string.rep(" ", spaces)
    +	end
    +	text = text:gsub("([^\n]-)\t", rep)
    +	return text
    +end
    +
    +-- Applies string.find for every pattern in the list and returns the first match
    +local function find_first(s, patterns, index)
    +	local res = {}
    +	for _,p in ipairs(patterns) do
    +		local match = {s:find(p, index)}
    +		if #match>0 and (#res==0 or match[1] < res[1]) then res = match end
    +	end
    +	return unpack(res)
    +end
    +
    +-- If a replacement array is specified, the range [start, stop] in the array is replaced
    +-- with the replacement array and the resulting array is returned. Without a replacement
    +-- array the section of the array between start and stop is returned.
    +local function splice(array, start, stop, replacement)
    +	if replacement then
    +		local n = stop - start + 1
    +		while n > 0 do
    +			table.remove(array, start)
    +			n = n - 1
    +		end
    +		for i,v in ipairs(replacement) do
    +			table.insert(array, start, v)
    +		end
    +		return array
    +	else
    +		local res = {}
    +		for i = start,stop do
    +			table.insert(res, array[i])
    +		end
    +		return res
    +	end
    +end
    +
    +-- Outdents the text one step.
    +local function outdent(text)
    +	text = "\n" .. text
    +	text = text:gsub("\n  ? ? ?", "\n")
    +	text = text:sub(2)
    +	return text
    +end
    +
    +-- Indents the text one step.
    +local function indent(text)
    +	text = text:gsub("\n", "\n    ")
    +	return text
    +end
    +
    +-- Does a simple tokenization of html data. Returns the data as a list of tokens.
    +-- Each token is a table with a type field (which is either "tag" or "text") and
    +-- a text field (which contains the original token data).
    +local function tokenize_html(html)
    +	local tokens = {}
    +	local pos = 1
    +	while true do
    +		local start = find_first(html, {"", start)
    +		elseif html:match("^<%?", start) then
    +			_,stop = html:find("?>", start)
    +		else
    +			_,stop = html:find("%b<>", start)
    +		end
    +		if not stop then
    +			-- error("Could not match html tag " .. html:sub(start,start+30))
    +			table.insert(tokens, {type="text", text=html:sub(start, start)})
    +			pos = start + 1
    +		else
    +			table.insert(tokens, {type="tag", text=html:sub(start, stop)})
    +			pos = stop + 1
    +		end
    +	end
    +	return tokens
    +end
    +
    +----------------------------------------------------------------------
    +-- Hash
    +----------------------------------------------------------------------
    +
    +-- This is used to "hash" data into alphanumeric strings that are unique
    +-- in the document. (Note that this is not cryptographic hash, the hash
    +-- function is not one-way.) The hash procedure is used to protect parts
    +-- of the document from further processing.
    +
    +local HASH = {
    +	-- Has the hash been inited.
    +	inited = false,
    +
    +	-- The unique string prepended to all hash values. This is to ensure
    +	-- that hash values do not accidently coincide with an actual existing
    +	-- string in the document.
    +	identifier = "",
    +
    +	-- Counter that counts up for each new hash instance.
    +	counter = 0,
    +
    +	-- Hash table.
    +	table = {}
    +}
    +
    +-- Inits hashing. Creates a hash_identifier that doesn't occur anywhere
    +-- in the text.
    +local function init_hash(text)
    +	HASH.inited = true
    +	HASH.identifier = ""
    +	HASH.counter = 0
    +	HASH.table = {}
    +
    +	local s = "HASH"
    +	local counter = 0
    +	local id
    +	while true do
    +		id  = s .. counter
    +		if not text:find(id, 1, true) then break end
    +		counter = counter + 1
    +	end
    +	HASH.identifier = id
    +end
    +
    +-- Returns the hashed value for s.
    +local function hash(s)
    +	assert(HASH.inited)
    +	if not HASH.table[s] then
    +		HASH.counter = HASH.counter + 1
    +		local id = HASH.identifier .. HASH.counter .. "X"
    +		HASH.table[s] = id
    +	end
    +	return HASH.table[s]
    +end
    +
    +----------------------------------------------------------------------
    +-- Protection
    +----------------------------------------------------------------------
    +
    +-- The protection module is used to "protect" parts of a document
    +-- so that they are not modified by subsequent processing steps.
    +-- Protected parts are saved in a table for later unprotection
    +
    +-- Protection data
    +local PD = {
    +	-- Saved blocks that have been converted
    +	blocks = {},
    +
    +	-- Block level tags that will be protected
    +	tags = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote",
    +	"pre", "table", "dl", "ol", "ul", "script", "noscript", "form", "fieldset",
    +	"iframe", "math", "ins", "del"}
    +}
    +
    +-- Pattern for matching a block tag that begins and ends in the leftmost
    +-- column and may contain indented subtags, i.e.
    +-- 
    +-- A nested block. +--
    +-- Nested data. +--
    +--
    +local function block_pattern(tag) + return "\n<" .. tag .. ".-\n[ \t]*\n" +end + +-- Pattern for matching a block tag that begins and ends with a newline +local function line_pattern(tag) + return "\n<" .. tag .. ".-[ \t]*\n" +end + +-- Protects the range of characters from start to stop in the text and +-- returns the protected string. +local function protect_range(text, start, stop) + local s = text:sub(start, stop) + local h = hash(s) + PD.blocks[h] = s + text = text:sub(1,start) .. h .. text:sub(stop) + return text +end + +-- Protect every part of the text that matches any of the patterns. The first +-- matching pattern is protected first, etc. +local function protect_matches(text, patterns) + while true do + local start, stop = find_first(text, patterns) + if not start then break end + text = protect_range(text, start, stop) + end + return text +end + +-- Protects blocklevel tags in the specified text +local function protect(text) + -- First protect potentially nested block tags + text = protect_matches(text, map(PD.tags, block_pattern)) + -- Then protect block tags at the line level. + text = protect_matches(text, map(PD.tags, line_pattern)) + -- Protect
    and comment tags + text = protect_matches(text, {"\n]->[ \t]*\n"}) + text = protect_matches(text, {"\n[ \t]*\n"}) + return text +end + +-- Returns true if the string s is a hash resulting from protection +local function is_protected(s) + return PD.blocks[s] +end + +-- Unprotects the specified text by expanding all the nonces +local function unprotect(text) + for k,v in pairs(PD.blocks) do + v = v:gsub("%%", "%%%%") + text = text:gsub(k, v) + end + return text +end + + +---------------------------------------------------------------------- +-- Block transform +---------------------------------------------------------------------- + +-- The block transform functions transform the text on the block level. +-- They work with the text as an array of lines rather than as individual +-- characters. + +-- Returns true if the line is a ruler of (char) characters. +-- The line must contain at least three char characters and contain only spaces and +-- char characters. +local function is_ruler_of(line, char) + if not line:match("^[ %" .. char .. "]*$") then return false end + if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end + return true +end + +-- Identifies the block level formatting present in the line +local function classify(line) + local info = {line = line, text = line} + + if line:match("^ ") then + info.type = "indented" + info.outdented = line:sub(5) + return info + end + + for _,c in ipairs({'*', '-', '_', '='}) do + if is_ruler_of(line, c) then + info.type = "ruler" + info.ruler_char = c + return info + end + end + + if line == "" then + info.type = "blank" + return info + end + + if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then + local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") + info.type = "header" + info.level = m1:len() + info.text = m2 + return info + end + + if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then + local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") + info.type = "list_item" + info.list_type = "numeric" + info.number = 0 + number + info.text = text + return info + end + + if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then + local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") + info.type = "list_item" + info.list_type = "bullet" + info.bullet = bullet + info.text= text + return info + end + + if line:match("^>[ \t]?(.*)") then + info.type = "blockquote" + info.text = line:match("^>[ \t]?(.*)") + return info + end + + if is_protected(line) then + info.type = "raw" + info.html = unprotect(line) + return info + end + + info.type = "normal" + return info +end + +-- Find headers constisting of a normal line followed by a ruler and converts them to +-- header entries. +local function headers(array) + local i = 1 + while i <= #array - 1 do + if array[i].type == "normal" and array[i+1].type == "ruler" and + (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then + local info = {line = array[i].line} + info.text = info.line + info.type = "header" + info.level = iff(array[i+1].ruler_char == "=", 1, 2) + table.remove(array, i+1) + array[i] = info + end + i = i + 1 + end + return array +end + +local block_transform, blocks_to_html, encode_code, span_transform, encode_backslash_escapes + +-- Find list blocks and convert them to protected data blocks +local function lists(array, sublist) + local function process_list(arr) + local function any_blanks(arr) + for i = 1, #arr do + if arr[i].type == "blank" then return true end + end + return false + end + + local function split_list_items(arr) + local acc = {arr[1]} + local res = {} + for i=2,#arr do + if arr[i].type == "list_item" then + table.insert(res, acc) + acc = {arr[i]} + else + table.insert(acc, arr[i]) + end + end + table.insert(res, acc) + return res + end + + local function process_list_item(lines, block) + while lines[#lines].type == "blank" do + table.remove(lines) + end + + local itemtext = lines[1].text + for i=2,#lines do + itemtext = itemtext .. "\n" .. outdent(lines[i].line) + end + if block then + itemtext = block_transform(itemtext, true) + if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    +				return "    
  • " .. itemtext .. "
  • " + else + local lines = split(itemtext) + lines = map(lines, classify) + lines = lists(lines, true) + lines = blocks_to_html(lines, true) + itemtext = table.concat(lines, "\n") + if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    +				return "    
  • " .. itemtext .. "
  • " + end + end + + local block_list = any_blanks(arr) + local items = split_list_items(arr) + local out = "" + for _, item in ipairs(items) do + out = out .. process_list_item(item, block_list) .. "\n" + end + if arr[1].list_type == "numeric" then + return "
      \n" .. out .. "
    " + else + return "
      \n" .. out .. "
    " + end + end + + -- Finds the range of lines composing the first list in the array. A list + -- starts with (^ list_item) or (blank list_item) and ends with + -- (blank* $) or (blank normal). + -- + -- A sublist can start with just (list_item) does not need a blank... + local function find_list(array, sublist) + local function find_list_start(array, sublist) + if array[1].type == "list_item" then return 1 end + if sublist then + for i = 1,#array do + if array[i].type == "list_item" then return i end + end + else + for i = 1, #array-1 do + if array[i].type == "blank" and array[i+1].type == "list_item" then + return i+1 + end + end + end + return nil + end + local function find_list_end(array, start) + local pos = #array + for i = start, #array-1 do + if array[i].type == "blank" and array[i+1].type ~= "list_item" + and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then + pos = i-1 + break + end + end + while pos > start and array[pos].type == "blank" do + pos = pos - 1 + end + return pos + end + + local start = find_list_start(array, sublist) + if not start then return nil end + return start, find_list_end(array, start) + end + + while true do + local start, stop = find_list(array, sublist) + if not start then break end + local text = process_list(splice(array, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + array = splice(array, start, stop, {info}) + end + + -- Convert any remaining list items to normal + for _,line in ipairs(array) do + if line.type == "list_item" then line.type = "normal" end + end + + return array +end + +-- Find and convert blockquote markers. +local function blockquotes(lines) + local function find_blockquote(lines) + local start + for i,line in ipairs(lines) do + if line.type == "blockquote" then + start = i + break + end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + -- luacheck: push ignore 542 + if lines[i].type == "blank" or lines[i].type == "blockquote" then + elseif lines[i].type == "normal" then + if lines[i-1].type == "blank" then stop = i-1 break end + else + stop = i-1 break + end + -- luacheck: pop + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_blockquote(lines) + local raw = lines[1].text + for i = 2,#lines do + raw = raw .. "\n" .. lines[i].text + end + local bt = block_transform(raw) + if not bt:find("
    ") then bt = indent(bt) end
    +		return "
    \n " .. bt .. + "\n
    " + end + + while true do + local start, stop = find_blockquote(lines) + if not start then break end + local text = process_blockquote(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Find and convert codeblocks. +local function codeblocks(lines) + local function find_codeblock(lines) + local start + for i,line in ipairs(lines) do + if line.type == "indented" then start = i break end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + if lines[i].type ~= "indented" and lines[i].type ~= "blank" then + stop = i-1 + break + end + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_codeblock(lines) + local raw = detab(encode_code(outdent(lines[1].line))) + for i = 2,#lines do + raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) + end + return "
    " .. raw .. "\n
    " + end + + while true do + local start, stop = find_codeblock(lines) + if not start then break end + local text = process_codeblock(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Convert lines to html code +function blocks_to_html(lines, no_paragraphs) + local out = {} + local i = 1 + while i <= #lines do + local line = lines[i] + if line.type == "ruler" then + table.insert(out, "
    ") + elseif line.type == "raw" then + table.insert(out, line.html) + elseif line.type == "normal" then + local s = line.line + + while i+1 <= #lines and lines[i+1].type == "normal" do + i = i + 1 + s = s .. "\n" .. lines[i].line + end + + if no_paragraphs then + table.insert(out, span_transform(s)) + else + table.insert(out, "

    " .. span_transform(s) .. "

    ") + end + elseif line.type == "header" then + local s = "" .. span_transform(line.text) .. "" + table.insert(out, s) + else + table.insert(out, line.line) + end + i = i + 1 + end + return out +end + +-- Perform all the block level transforms +function block_transform(text, sublist) + local lines = split(text) + lines = map(lines, classify) + lines = headers(lines) + lines = lists(lines, sublist) + lines = codeblocks(lines) + lines = blockquotes(lines) + lines = blocks_to_html(lines) + local text = table.concat(lines, "\n") + return text +end + +---------------------------------------------------------------------- +-- Span transform +---------------------------------------------------------------------- + +-- Functions for transforming the text at the span level. + +-- These characters may need to be escaped because they have a special +-- meaning in markdown. +local escape_chars = "'\\`*_{}[]()>#+-.!'" +local escape_table = {} + +local function init_escape_table() + escape_table = {} + for i = 1,#escape_chars do + local c = escape_chars:sub(i,i) + escape_table[c] = hash(c) + end +end + +-- Adds a new escape to the escape table. +local function add_escape(text) + if not escape_table[text] then + escape_table[text] = hash(text) + end + return escape_table[text] +end + +-- Escape characters that should not be disturbed by markdown. +local function escape_special_chars(text) + local tokens = tokenize_html(text) + + local out = "" + for _, token in ipairs(tokens) do + local t = token.text + if token.type == "tag" then + -- In tags, encode * and _ so they don't conflict with their use in markdown. + t = t:gsub("%*", escape_table["*"]) + t = t:gsub("%_", escape_table["_"]) + else + t = encode_backslash_escapes(t) + end + out = out .. t + end + return out +end + +-- Encode backspace-escaped characters in the markdown source. +function encode_backslash_escapes(t) + for i=1,escape_chars:len() do + local c = escape_chars:sub(i,i) + t = t:gsub("\\%" .. c, escape_table[c]) + end + return t +end + +-- Unescape characters that have been encoded. +local function unescape_special_chars(t) + local tin = t + for k,v in pairs(escape_table) do + k = k:gsub("%%", "%%%%") + t = t:gsub(v,k) + end + if t ~= tin then t = unescape_special_chars(t) end + return t +end + +-- Encode/escape certain characters inside Markdown code runs. +-- The point is that in code, these characters are literals, +-- and lose their special Markdown meanings. +function encode_code(s) + s = s:gsub("%&", "&") + s = s:gsub("<", "<") + s = s:gsub(">", ">") + for k,v in pairs(escape_table) do + s = s:gsub("%"..k, v) + end + return s +end + +-- Handle backtick blocks. +local function code_spans(s) + s = s:gsub("\\\\", escape_table["\\"]) + s = s:gsub("\\`", escape_table["`"]) + + local pos = 1 + while true do + local start, stop = s:find("`+", pos) + if not start then return s end + local count = stop - start + 1 + -- Find a matching numbert of backticks + local estart, estop = s:find(string.rep("`", count), stop+1) + local brstart = s:find("\n", stop+1) + if estart and (not brstart or estart < brstart) then + local code = s:sub(stop+1, estart-1) + code = code:gsub("^[ \t]+", "") + code = code:gsub("[ \t]+$", "") + code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) + code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) + code = "" .. encode_code(code) .. "" + code = add_escape(code) + s = s:sub(1, start-1) .. code .. s:sub(estop+1) + pos = start + code:len() + else + pos = stop + 1 + end + end +end + +-- Encode alt text... enodes &, and ". +local function encode_alt(s) + if not s then return s end + s = s:gsub('&', '&') + s = s:gsub('"', '"') + s = s:gsub('<', '<') + return s +end + +local link_database + +-- Handle image references +local function images(text) + local function reference_link(alt, id) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + id = id:match("%[(.*)%]"):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape ('' .. alt .. '") + end + + local function inline_link(alt, link) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + url = url or link:match("%(?%)") + url = encode_alt(url) + title = encode_alt(title) + if title then + return add_escape('' .. alt .. '') + else + return add_escape('' .. alt .. '') + end + end + + text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("!(%b[])(%b())", inline_link) + return text +end + +-- Handle anchor references +local function anchors(text) + local function reference_link(text, id) + text = text:match("%b[]"):sub(2,-2) + id = id:match("%b[]"):sub(2,-2):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape("") .. text .. add_escape("") + end + + local function inline_link(text, link) + text = text:match("%b[]"):sub(2,-2) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + title = encode_alt(title) + url = url or link:match("%(?%)") or "" + url = encode_alt(url) + if title then + return add_escape("") .. text .. "" + else + return add_escape("") .. text .. add_escape("") + end + end + + text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("(%b[])(%b())", inline_link) + return text +end + +-- Handle auto links, i.e. . +local function auto_links(text) + local function link(s) + return add_escape("") .. s .. "" + end + -- Encode chars as a mix of dec and hex entitites to (perhaps) fool + -- spambots. + local function encode_email_address(s) + -- Use a deterministic encoding to make unit testing possible. + -- Code 45% hex, 45% dec, 10% plain. + local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} + local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} + local plain = {code = function(c) return c end, count = 0, rate = 0.1} + local codes = {hex, dec, plain} + local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end + + local out = "" + for i = 1,s:len() do + for _,code in ipairs(codes) do code.count = code.count + code.rate end + if codes[1].count < codes[2].count then swap(codes,1,2) end + if codes[2].count < codes[3].count then swap(codes,2,3) end + if codes[1].count < codes[2].count then swap(codes,1,2) end + + local code = codes[1] + local c = s:sub(i,i) + -- Force encoding of "@" to make email address more invisible. + if c == "@" and code == plain then code = codes[2] end + out = out .. code.code(c) + code.count = code.count - 1 + end + return out + end + local function mail(s) + s = unescape_special_chars(s) + local address = encode_email_address("mailto:" .. s) + local text = encode_email_address(s) + return add_escape("") .. text .. "" + end + -- links + text = text:gsub("<(https?:[^'\">%s]+)>", link) + text = text:gsub("<(ftp:[^'\">%s]+)>", link) + + -- mail + text = text:gsub("%s]+)>", mail) + text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) + return text +end + +-- Encode free standing amps (&) and angles (<)... note that this does not +-- encode free >. +local function amps_and_angles(s) + -- encode amps not part of &..; expression + local pos = 1 + while true do + local amp = s:find("&", pos) + if not amp then break end + local semi = s:find(";", amp+1) + local stop = s:find("[ \t\n&]", amp+1) + if not semi or (stop and stop < semi) or (semi - amp) > 15 then + s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) + pos = amp+1 + else + pos = amp+1 + end + end + + -- encode naked <'s + s = s:gsub("<([^a-zA-Z/?$!])", "<%1") + s = s:gsub("<$", "<") + + -- what about >, nothing done in the original markdown source to handle them + return s +end + +-- Handles emphasis markers (* and _) in the text. +local function emphasis(text) + for _, s in ipairs {"%*%*", "%_%_"} do + text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") + text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") + end + for _, s in ipairs {"%*", "%_"} do + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "%1") + text = text:gsub(s .. "([^<>_]-[^<>_]-[^<>_]-)" .. s, "%1") + end + return text +end + +-- Handles line break markers in the text. +local function line_breaks(text) + return text:gsub(" +\n", "
    \n") +end + +-- Perform all span level transforms. +function span_transform(text) + text = code_spans(text) + text = escape_special_chars(text) + text = images(text) + text = anchors(text) + text = auto_links(text) + text = amps_and_angles(text) + text = emphasis(text) + text = line_breaks(text) + return text +end + +---------------------------------------------------------------------- +-- Markdown +---------------------------------------------------------------------- + +-- Cleanup the text by normalizing some possible variations to make further +-- processing easier. +local function cleanup(text) + -- Standardize line endings + text = text:gsub("\r\n", "\n") -- DOS to UNIX + text = text:gsub("\r", "\n") -- Mac to UNIX + + -- Convert all tabs to spaces + text = detab(text) + + -- Strip lines with only spaces and tabs + while true do + local subs + text, subs = text:gsub("\n[ \t]+\n", "\n\n") + if subs == 0 then break end + end + + return "\n" .. text .. "\n" +end + +-- Strips link definitions from the text and stores the data in a lookup table. +local function strip_link_definitions(text) + local linkdb = {} + + local function link_def(id, url, title) + id = id:match("%[(.+)%]"):lower() + linkdb[id] = linkdb[id] or {} + linkdb[id].url = url or linkdb[id].url + linkdb[id].title = title or linkdb[id].title + return "" + end + + local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*]+)>?[ \t]*" + local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" + + text = text:gsub(def_title1, link_def) + text = text:gsub(def_title2, link_def) + text = text:gsub(def_title3, link_def) + text = text:gsub(def_no_title, link_def) + return text, linkdb +end + +link_database = {} + +-- Main markdown processing function +local function markdown(text) + init_hash(text) + init_escape_table() + + text = cleanup(text) + text = protect(text) + text, link_database = strip_link_definitions(text) + text = block_transform(text) + text = unescape_special_chars(text) + return text +end + +---------------------------------------------------------------------- +-- End of module +---------------------------------------------------------------------- + +M.lock(M) + +-- Expose markdown function to the world +_G.markdown = M.markdown + +-- Class for parsing command-line options +local OptionParser = {} +OptionParser.__index = OptionParser + +-- Creates a new option parser +function OptionParser:new() + local o = {short = {}, long = {}} + setmetatable(o, self) + return o +end + +-- Calls f() whenever a flag with specified short and long name is encountered +function OptionParser:flag(short, long, f) + local info = {type = "flag", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(param) whenever a parameter flag with specified short and long name is encountered +function OptionParser:param(short, long, f) + local info = {type = "param", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(v) for each non-flag argument +function OptionParser:arg(f) + self.arg = f +end + +-- Runs the option parser for the specified set of arguments. Returns true if all arguments +-- where successfully parsed and false otherwise. +function OptionParser:run(args) + local pos = 1 + local param + while pos <= #args do + local arg = args[pos] + if arg == "--" then + -- luacheck: push ignore 512 + for i=pos+1,#args do + if self.arg then self.arg(args[i]) end + return true + end + -- luacheck: pop + end + if arg:match("^%-%-") then + local info = self.long[arg:sub(3)] + if not info then print("Unknown flag: " .. arg) return false end + if info.type == "flag" then + info.f() + pos = pos + 1 + else + param = args[pos+1] + if not param then print("No parameter for flag: " .. arg) return false end + info.f(param) + pos = pos+2 + end + elseif arg:match("^%-") then + for i=2,arg:len() do + local c = arg:sub(i,i) + local info = self.short[c] + if not info then print("Unknown flag: -" .. c) return false end + if info.type == "flag" then + info.f() + else + if i == arg:len() then + param = args[pos+1] + if not param then print("No parameter for flag: -" .. c) return false end + info.f(param) + pos = pos + 1 + else + param = arg:sub(i+1) + info.f(param) + end + break + end + end + pos = pos + 1 + else + if self.arg then self.arg(arg) end + pos = pos + 1 + end + end + return true +end + +-- Handles the case when markdown is run from the command line +local function run_command_line(arg) + -- Generate output for input s given options + local function run(s, options) + s = markdown(s) + if not options.wrap_header then return s end + local header + if options.header then + local f = io.open(options.header) or error("Could not open file: " .. options.header) + header = f:read("*a") + f:close() + else + header = [[ + + + + + TITLE + + + +]] + local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or + s:match("

    (.-)

    ") or "Untitled" + header = header:gsub("TITLE", title) + if options.inline_style then + local style = "" + local f = io.open(options.stylesheet) + if f then + style = f:read("*a") f:close() + else + error("Could not include style sheet " .. options.stylesheet .. ": File not found") + end + header = header:gsub('', + "") + else + header = header:gsub("STYLESHEET", options.stylesheet) + end + header = header:gsub("CHARSET", options.charset) + end + local footer = "" + if options.footer then + local f = io.open(options.footer) or error("Could not open file: " .. options.footer) + footer = f:read("*a") + f:close() + end + return header .. s .. footer + end + + -- Generate output path name from input path name given options. + local function outpath(path, options) + if options.append then return path .. ".html" end + local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end + m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end + return path .. ".html" + end + + -- Default commandline options + local options = { + wrap_header = true, + header = nil, + footer = nil, + charset = "utf-8", + title = nil, + stylesheet = "default.css", + inline_style = false + } + local help = [[ +Usage: markdown.lua [OPTION] [FILE] +Runs the markdown text markup to HTML converter on each file specified on the +command line. If no files are specified, runs on standard input. + +No header: + -n, --no-wrap Don't wrap the output in ... tags. +Custom header: + -e, --header FILE Use content of FILE for header. + -f, --footer FILE Use content of FILE for footer. +Generated header: + -c, --charset SET Specifies charset (default utf-8). + -i, --title TITLE Specifies title (default from first

    tag). + -s, --style STYLE Specifies style sheet file (default default.css). + -l, --inline-style Include the style sheet file inline in the header. +Generated files: + -a, --append Append .html extension (instead of replacing). +Other options: + -h, --help Print this help text. + -t, --test Run the unit tests. +]] + + local run_stdin = true + local op = OptionParser:new() + op:flag("n", "no-wrap", function () options.wrap_header = false end) + op:param("e", "header", function (x) options.header = x end) + op:param("f", "footer", function (x) options.footer = x end) + op:param("c", "charset", function (x) options.charset = x end) + op:param("i", "title", function(x) options.title = x end) + op:param("s", "style", function(x) options.stylesheet = x end) + op:flag("l", "inline-style", function(x) options.inline_style = true end) + op:flag("a", "append", function() options.append = true end) + op:flag("t", "test", function() + local n = arg[0]:gsub("markdown.lua", "markdown-tests.lua") + local f = io.open(n) + if f then + f:close() dofile(n) + else + error("Cannot find markdown-tests.lua") + end + run_stdin = false + end) + op:flag("h", "help", function() print(help) run_stdin = false end) + op:arg(function(path) + local file = io.open(path) or error("Could not open file: " .. path) + local s = file:read("*a") + file:close() + s = run(s, options) + file = io.open(outpath(path, options), "w") or error("Could not open output file: " .. outpath(path, options)) + file:write(s) + file:close() + run_stdin = false + end + ) + + if not op:run(arg) then + print(help) + run_stdin = false + end + + if run_stdin then + local s = io.read("*a") + s = run(s, options) + io.write(s) + end +end + +-- If we are being run from the command-line, act accordingly +if arg and arg[0]:find("markdown%.lua$") then + run_command_line(arg) +else + return markdown +end diff --git a/Documentation/compiler/ldoc/ldoc/markup.lua b/Documentation/compiler/ldoc/ldoc/markup.lua new file mode 100644 index 000000000..425fb0723 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/markup.lua @@ -0,0 +1,420 @@ +-------------- +-- Handling markup transformation. +-- Currently just does Markdown, but this is intended to +-- be the general module for managing other formats as well. + +local doc = require 'ldoc.doc' +local utils = require 'pl.utils' +local stringx = require 'pl.stringx' +local prettify = require 'ldoc.prettify' +local concat = table.concat +local markup = {} + +local backtick_references + +-- inline use same lookup as @see +local function resolve_inline_references (ldoc, txt, item, plain) + local do_escape = not plain and not ldoc.dont_escape_underscore + local res = (txt:gsub('@{([^}]-)}',function (name) + if name:match '^\\' then return '@{'..name:sub(2)..'}' end + local qname,label = utils.splitv(name,'%s*|') + if not qname then + qname = name + end + local ref, err + local custom_ref, refname = utils.splitv(qname,':') + if custom_ref and ldoc.custom_references then + custom_ref = ldoc.custom_references[custom_ref] + if custom_ref then + ref,err = custom_ref(refname) + end + end + if not ref then + ref,err = markup.process_reference(qname) + end + if not ref then + err = err .. ' ' .. qname + if item and item.warning then item:warning(err) + else + io.stderr:write('nofile error: ',err,'\n') + end + return '???' + end + if not label then + label = ref.label + end + if label and do_escape then -- a nastiness with markdown.lua and underscores + label = label:gsub('_','\\_') + end + local html = ldoc.href(ref) or '#' + label = ldoc.escape(label or qname) + local res = ('%s'):format(html,label) + return res + end)) + if backtick_references then + res = res:gsub('`([^`]+)`',function(name) + local ref,_ = markup.process_reference(name) + local label = name + if name and do_escape then + label = name:gsub('_', '\\_') + end + label = ldoc.escape(label) + if ref then + return ('%s'):format(ldoc.href(ref),label) + else + return ''..label..'' + end + end) + end + return res +end + +-- for readme text, the idea here is to create module sections at ## so that +-- they can appear in the contents list as a ToC. +function markup.add_sections(F, txt) + local sections, L, first = {}, 1, true + local title_pat + local lstrip = stringx.lstrip + for line in stringx.lines(txt) do + if first then + local level,header = line:match '^(#+)%s*(.+)' + if level then + level = level .. '#' + else + level = '##' + end + title_pat = '^'..level..'([^#]%s*.+)' + title_pat = lstrip(title_pat) + first = false + F.display_name = header + end + local title = line:match (title_pat) + if title then + --- Windows line endings are the cockroaches of text + title = title:gsub('\r$','') + -- Markdown allows trailing '#'... + title = title:gsub('%s*#+$','') + sections[L] = F:add_document_section(lstrip(title)) + end + L = L + 1 + end + F.sections = sections + return txt +end + +local function indent_line (line) + line = line:gsub('\t',' ') -- support for barbarians ;) + local indent = #line:match '^%s*' + return indent,line +end + +local function blank (line) + return not line:find '%S' +end + +local global_context, local_context + +-- before we pass Markdown documents to markdown/discount, we need to do three things: +-- - resolve any @{refs} and (optionally) `refs` +-- - any @lookup directives that set local context for ref lookup +-- - insert any section ids which were generated by add_sections above +-- - prettify any code blocks + +local function process_multiline_markdown(ldoc, txt, F, filename, deflang) + local res, L, append = {}, 0, table.insert + local err_item = { + warning = function (self,msg) + io.stderr:write(filename..':'..L..': '..msg,'\n') + end + } + local get = stringx.lines(txt) + local getline = function() + L = L + 1 + return get() + end + local function pretty_code (code, lang) + code = concat(code,'\n') + if code ~= '' then + local _ + -- If we omit the following '\n', a '--' (or '//') comment on the + -- last line won't be recognized. + code, _ = prettify.code(lang,filename,code..'\n',L,false) + code = resolve_inline_references(ldoc, code, err_item,true) + append(res,'
    ')
    +         append(res, code)
    +         append(res,'
    ') + else + append(res,code) + end + end + local indent,start_indent + local_context = nil + local line = getline() + while line do + local name = line:match '^@lookup%s+(%S+)' + if name then + local_context = name .. '.' + line = getline() + end + local fence = line:match '^```(.*)' + if fence then + local plain = fence=='' + line = getline() + local code = {} + while not line:match '^```' do + if not plain then + append(code, line) + else + append(res, ' '..line) + end + line = getline() + end + pretty_code (code,fence) + line = getline() -- skip fence + if not line then break end + end + indent, line = indent_line(line) + if indent >= 4 then -- indented code block + local code = {} + local plain + while indent >= 4 or blank(line) do + if not start_indent then + start_indent = indent + if line:match '^%s*@plain%s*$' then + plain = true + line = getline() + end + end + if not plain then + append(code,line:sub(start_indent + 1)) + else + append(res,line) + end + line = getline() + if line == nil then break end + indent, line = indent_line(line) + end + start_indent = nil + while #code > 1 and blank(code[#code]) do -- trim blank lines. + table.remove(code) + end + pretty_code (code,deflang) + else + local section = F and F.sections[L] + if section then + append(res,(''):format(section)) + end + line = resolve_inline_references(ldoc, line, err_item) + append(res,line) + line = getline() + end + end + res = concat(res,'\n') + return res +end + + +-- Handle markdown formatters +-- Try to get the one the user has asked for, but if it's not available, +-- try all the others we know about. If they don't work, fall back to text. + +local function generic_formatter(format) + local ok, f = pcall(require, format) + return ok and f +end + + +local formatters = +{ + markdown = function(format) + local ok, markdown = pcall(require, 'markdown') + if not ok then + print('format: using built-in markdown') + ok, markdown = pcall(require, 'ldoc.markdown') + end + return ok and markdown + end, + discount = function(format) + local ok, markdown = pcall(require, 'discount') + if ok then + -- luacheck: push ignore 542 + if 'function' == type(markdown) then + -- lua-discount by A.S. Bradbury, https://luarocks.org/modules/luarocks/lua-discount + elseif 'table' == type(markdown) and ('function' == type(markdown.compile) or 'function' == type(markdown.to_html)) then + -- discount by Craig Barnes, https://luarocks.org/modules/craigb/discount + -- result of apt-get install lua-discount (links against libmarkdown2) + local mysterious_debian_variant = markdown.to_html ~= nil + markdown = markdown.compile or markdown.to_html + return function(text) + local result, errmsg = markdown(text) + if result then + if mysterious_debian_variant then + return result + else + return result.body + end + else + io.stderr:write('LDoc discount failed with error ',errmsg) + os.exit(1) + end + end + else + ok = false + end + -- luacheck: pop + end + if not ok then + print('format: using built-in markdown') + ok, markdown = pcall(require, 'ldoc.markdown') + end + return ok and markdown + end, + lunamark = function(format) + local ok, lunamark = pcall(require, format) + if ok then + local writer = lunamark.writer.html.new() + local parse = lunamark.reader.markdown.new(writer, + { smart = true }) + return function(text) return parse(text) end + end + end, + commonmark = function(format) + local ok, cmark = pcall(require, 'cmark') + if ok then + return function(text) + local doc = cmark.parse_document(text, string.len(text), cmark.OPT_DEFAULT) + return cmark.render_html(doc, cmark.OPT_DEFAULT) + end + end + end +} + + +local function get_formatter(format) + local used_format = format + local formatter = (formatters[format] or generic_formatter)(format) + if not formatter then -- try another equivalent processor + for name, f in pairs(formatters) do + formatter = f(name) + if formatter then + print('format: '..format..' not found, using '..name) + used_format = name + break + end + end + end + return formatter, used_format +end + +local function text_processor(ldoc) + return function(txt,item) + if txt == nil then return '' end + -- hack to separate paragraphs with blank lines + txt = txt:gsub('\n\n','\n

    ') + return resolve_inline_references(ldoc, txt, item, true) + end +end + +local plain_processor + +local function markdown_processor(ldoc, formatter) + return function (txt,item,plain) + if txt == nil then return '' end + if plain then + if not plain_processor then + plain_processor = text_processor(ldoc) + end + return plain_processor(txt,item) + end + local is_file = utils.is_type(item,doc.File) + local is_module = not is_file and item and doc.project_level(item.type) + if is_file or is_module then + local deflang = 'lua' + if ldoc.parse_extra and ldoc.parse_extra.C then + deflang = 'c' + end + if is_module then + txt = process_multiline_markdown(ldoc, txt, nil, item.file.filename, deflang) + else + txt = process_multiline_markdown(ldoc, txt, item, item.filename, deflang) + end + else + txt = resolve_inline_references(ldoc, txt, item) + end + txt = formatter(txt) + -- We will add our own paragraph tags, if needed. + return (txt:gsub('^%s*

    (.+)

    %s*$','%1')) + end +end + +local function get_processor(ldoc, format) + if format == 'plain' then return text_processor(ldoc) end + + local formatter,actual_format = get_formatter(format) + if formatter then + markup.plain = false + -- AFAIK only markdown.lua has underscore-in-identifier problem... + if ldoc.dont_escape_underscore ~= nil then + ldoc.dont_escape_underscore = actual_format ~= 'markdown' + end + return markdown_processor(ldoc, formatter) + end + + print('format: '..format..' not found, falling back to text') + return text_processor(ldoc) +end + + +function markup.create (ldoc, format, pretty, user_keywords) + local processor + markup.plain = true + if format == 'backtick' then + ldoc.backtick_references = true + format = 'plain' + end + backtick_references = ldoc.backtick_references + global_context = ldoc.package and ldoc.package .. '.' + prettify.set_prettifier(pretty) + prettify.set_user_keywords(user_keywords) + + markup.process_reference = function(name,istype) + if local_context == 'none.' and not name:match '%.' then + return nil,'not found' + end + local mod = ldoc.single or ldoc.module or ldoc.modules[1] + local ref,err = mod:process_see_reference(name, ldoc.modules, istype) + if ref then return ref end + if global_context then + local qname = global_context .. name + ref = mod:process_see_reference(qname, ldoc.modules, istype) + if ref then return ref end + end + if local_context then + local qname = local_context .. name + ref = mod:process_see_reference(qname, ldoc.modules, istype) + if ref then return ref end + end + -- note that we'll return the original error! + return ref,err + end + + markup.href = function(ref) + return ldoc.href(ref) + end + + processor = get_processor(ldoc, format) + if not markup.plain and backtick_references == nil then + backtick_references = true + end + + markup.resolve_inline_references = function(txt, errfn) + return resolve_inline_references(ldoc, txt, errfn, markup.plain) + end + markup.processor = processor + prettify.resolve_inline_references = function(txt, errfn) + return resolve_inline_references(ldoc, txt, errfn, true) + end + return processor +end + +return markup diff --git a/Documentation/compiler/ldoc/ldoc/parse.lua b/Documentation/compiler/ldoc/ldoc/parse.lua new file mode 100644 index 000000000..11ade606c --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/parse.lua @@ -0,0 +1,430 @@ +-- parsing code for doc comments + +local utils = require 'pl.utils' +local List = require 'pl.List' +-- local Map = require 'pl.Map' +local stringio = require 'pl.stringio' +local lexer = require 'ldoc.lexer' +local tools = require 'ldoc.tools' +local doc = require 'ldoc.doc' +local Item,File = doc.Item,doc.File +local unpack = utils.unpack + +------ Parsing the Source -------------- +-- This uses the lexer from PL, but it should be possible to use Peter Odding's +-- excellent Lpeg based lexer instead. + +local parse = {} + +local tnext, append = lexer.skipws, table.insert + +-- a pattern particular to LuaDoc tag lines: the line must begin with @TAG, +-- followed by the value, which may extend over several lines. +local luadoc_tag = '^%s*@(%w+)' +local luadoc_tag_value = luadoc_tag..'(.*)' +local luadoc_tag_mod_and_value = luadoc_tag..'%[([^%]]*)%](.*)' + +-- assumes that the doc comment consists of distinct tag lines +local function parse_at_tags(text) + local lines = stringio.lines(text) + local preamble, line = tools.grab_while_not(lines,luadoc_tag) + local tag_items = {} + local follows + while line do + local tag, mod_string, rest = line :match(luadoc_tag_mod_and_value) + if not tag then tag, rest = line :match (luadoc_tag_value) end + local modifiers + if mod_string then + modifiers = { } + for x in mod_string :gmatch "[^,]+" do + local k, v = x :match "^([^=]+)=(.*)$" + if not k then k, v = x, true end -- wuz x, x + modifiers[k] = v + end + end + -- follows: end of current tag + -- line: beginning of next tag (for next iteration) + follows, line = tools.grab_while_not(lines,luadoc_tag) + append(tag_items,{tag, rest .. '\n' .. follows, modifiers}) + end + return preamble,tag_items +end + +--local colon_tag = '%s*(%a+):%s' +local colon_tag = '%s*(%S-):%s' +local colon_tag_value = colon_tag..'(.*)' + +local function parse_colon_tags (text) + local lines = stringio.lines(text) + local preamble, line = tools.grab_while_not(lines,colon_tag) + local tag_items, follows = {} + while line do + local tag, rest = line:match(colon_tag_value) + follows, line = tools.grab_while_not(lines,colon_tag) + local value = rest .. '\n' .. follows + if tag:match '^[%?!]' then + tag = tag:gsub('^!','') + value = tag .. ' ' .. value + tag = 'tparam' + end + append(tag_items,{tag, value}) + end + return preamble,tag_items +end + +-- Tags are stored as an ordered multi map from strings to strings +-- If the same key is used, then the value becomes a list +local Tags = {} +Tags.__index = Tags + +function Tags.new (t,name) + local class + if name then + class = t + t = {} + end + t._order = List() + local tags = setmetatable(t,Tags) + if name then + tags:add('class',class) + tags:add('name',name) + end + return tags +end + +function Tags:add (tag,value,modifiers) + if modifiers then -- how modifiers are encoded + value = {value,modifiers=modifiers} + end + local ovalue = self:get(tag) + if ovalue then + ovalue:append(value) + value = ovalue + end + rawset(self,tag,value) + if not ovalue then + self._order:append(tag) + end +end + +function Tags:get (tag) + local ovalue = rawget(self,tag) + if ovalue then -- previous value? + if getmetatable(ovalue) ~= List then + ovalue = List{ovalue} + end + return ovalue + end +end + +function Tags:iter () + return self._order:iter() +end + +local function comment_contains_tags (comment,args) + return (args.colon and comment:find ': ') or (not args.colon and comment:find '@') +end + +-- This takes the collected comment block, and uses the docstyle to +-- extract tags and values. Assume that the summary ends in a period or a question +-- mark, and everything else in the preamble is the description. +-- If a tag appears more than once, then its value becomes a list of strings. +-- Alias substitution and @TYPE NAME shortcutting is handled by Item.check_tag +local function extract_tags (s,args) + local preamble,tag_items + if s:match '^%s*$' then return {} end + if args.colon then --and s:match ':%s' and not s:match '@%a' then + preamble,tag_items = parse_colon_tags(s) + else + preamble,tag_items = parse_at_tags(s) + end + local strip = tools.strip + local summary, description = preamble:match('^(.-[%.?])(%s.+)') + if not summary then + -- perhaps the first sentence did not have a . or ? terminating it. + -- Then try split at linefeed + summary, description = preamble:match('^(.-\n\n)(.+)') + if not summary then + summary = preamble + end + end -- and strip(description) ? + local tags = Tags.new{summary=summary and strip(summary) or '',description=description or ''} + for _,item in ipairs(tag_items) do + local tag, value, modifiers = Item.check_tag(tags,unpack(item)) + -- treat multiline values more gently.. + if not value:match '\n[^\n]+\n' then + value = strip(value) + end + + tags:add(tag,value,modifiers) + end + return tags --Map(tags) +end + + +-- parses a Lua or C file, looking for ldoc comments. These are like LuaDoc comments; +-- they start with multiple '-'. (Block commments are allowed) +-- If they don't define a name tag, then by default +-- it is assumed that a function definition follows. If it is the first comment +-- encountered, then ldoc looks for a call to module() to find the name of the +-- module if there isn't an explicit module name specified. + +local function parse_file(fname, lang, package, args) + local F = File(fname) + local module_found, first_comment = false,true + local current_item, module_item + + F.args = args + F.lang = lang + F.base = package + + local tok,f = lang.lexer(fname) + if not tok then return nil end + + local function lineno () + return tok:lineno() + end + + function F:warning (msg,kind,line) + line = line or lineno() + Item.had_warning = true + io.stderr:write(fname..':'..line..': '..msg,'\n') + end + + function F:error (msg) + self:warning(msg,'error') + io.stderr:write('LDoc error\n') + os.exit(1) + end + + local function add_module(tags,module_found,old_style) + tags:add('name',module_found) + tags:add('class','module') + local item = F:new_item(tags,lineno()) + item.old_style = old_style + module_item = item + end + + local mod + local t,v = tnext(tok) + -- with some coding styles first comment is standard boilerplate; option to ignore this. + if args.boilerplate and t == 'comment' then + -- hack to deal with boilerplate inside Lua block comments + if v:match '%s*%-%-%[%[' then lang:grab_block_comment(v,tok) end + t,v = tnext(tok) + end + if t == '#' then -- skip Lua shebang line, if present + while t and t ~= 'comment' do t,v = tnext(tok) end + if t == nil then + F:warning('empty file') + return nil + end + end + if lang.parse_module_call and t ~= 'comment' then + local prev_token + while t do + if prev_token ~= '.' and prev_token ~= ':' and t == 'iden' and v == 'module' then + break + end + prev_token = t + t, v = tnext(tok) + end + if not t then + if not args.ignore then + F:warning("no module() call found; no initial doc comment") + end + --return nil + else + mod,t,v = lang:parse_module_call(tok,t,v) + if mod and mod ~= '...' then + add_module(Tags.new{summary='(no description)'},mod,true) + first_comment = false + module_found = true + end + end + end + local ok, err = xpcall(function() + while t do + if t == 'comment' then + local comment = {} + local ldoc_comment,block = lang:start_comment(v) + + if ldoc_comment and block then + t,v = lang:grab_block_comment(v,tok) + end + + if lang:empty_comment(v) then -- ignore rest of empty start comments + t,v = tok() + if t == 'space' and not v:match '\n' then + t,v = tok() + end + end + + while t and t == 'comment' do + v = lang:trim_comment(v) + append(comment,v) + t,v = tok() + if t == 'space' and not v:match '\n' then + t,v = tok() + end + end + + if t == 'space' then t,v = tnext(tok) end + + local item_follows, tags, is_local, case, parse_error + if ldoc_comment then + comment = table.concat(comment) + if comment:match '^%s*$' then + ldoc_comment = nil + end + end + if ldoc_comment then + if first_comment then + first_comment = false + else + item_follows, is_local, case = lang:item_follows(t,v,tok) + if not item_follows then + parse_error = is_local + is_local = false + end + end + + if item_follows or comment_contains_tags(comment,args) then + tags = extract_tags(comment,args) + + -- explicitly named @module (which is recommended) + if doc.project_level(tags.class) then + module_found = tags.name + -- might be a module returning a single function! + if tags.param or tags['return'] then + local parms, ret = tags.param, tags['return'] + local name = tags.name + tags.param = nil + tags['return'] = nil + tags['class'] = nil + tags['name'] = nil + add_module(tags,name,false) + tags = { + summary = '', + name = 'returns...', + class = 'function', + ['return'] = ret, + param = parms + } + end + end + doc.expand_annotation_item(tags,current_item) + -- if the item has an explicit name or defined meaning + -- then don't continue to do any code analysis! + -- Watch out for the case where there are field or param tags + -- but no class, since these will be fixed up later as module/class + -- entities + if (tags.field or tags.param) and not tags.class then + parse_error = false + end + if tags.name then + if not tags.class then + F:warning("no type specified, assuming function: '"..tags.name.."'") + tags:add('class','function') + end + item_follows, is_local, parse_error = false, false, false + elseif args.no_args_infer then + F:error("No name and type provided (no_args_infer)") + elseif lang:is_module_modifier (tags) then + if not item_follows then + F:warning("@usage or @export followed by unknown code") + break + end + item_follows(tags,tok) + local res, value, tagname = lang:parse_module_modifier(tags,tok,F) + if not res then F:warning(value); break + else + if tagname then + module_item:set_tag(tagname,value) + end + -- don't continue to make an item! + ldoc_comment = false + end + end + end + if parse_error then + F:warning('definition cannot be parsed - '..parse_error) + end + end + -- some hackery necessary to find the module() call + if not module_found and ldoc_comment then + local old_style + module_found,t,v = lang:find_module(tok,t,v) + -- right, we can add the module object ... + old_style = module_found ~= nil + if not module_found or module_found == '...' then + -- we have to guess the module name + module_found = tools.this_module_name(package,fname) + end + if not tags then tags = extract_tags(comment,args) end + add_module(tags,module_found,old_style) + tags = nil + if not t then + F:warning('contains no items','warning',1) + break; + end -- run out of file! + -- if we did bump into a doc comment, then we can continue parsing it + end + + -- end of a block of document comments + if ldoc_comment and tags then + local line = lineno() + if t ~= nil then + if item_follows then -- parse the item definition + local err = item_follows(tags,tok) + if err then F:error(err) end + elseif parse_error then + F:warning('definition cannot be parsed - '..parse_error) + else + lang:parse_extra(tags,tok,case) + end + end + if is_local or tags['local'] then + tags:add('local',true) + end + -- support for standalone fields/properties of classes/modules + if (tags.field or tags.param) and not tags.class then + -- the hack is to take a subfield and pull out its name, + -- (see Tag:add above) but let the subfield itself go through + -- with any modifiers. + local fp = tags.field or tags.param + if type(fp) == 'table' then fp = fp[1] end + fp = tools.extract_identifier(fp) + tags:add('name',fp) + tags:add('class','field') + end + if tags.name then + current_item = F:new_item(tags,line) + current_item.inferred = item_follows ~= nil + if doc.project_level(tags.class) then + if module_item then + F:error("Module already declared!") + end + module_item = current_item + end + end + if not t then break end + end + end + if t ~= 'comment' then t,v = tok() end + end + end,debug.traceback) + if not ok then return F, err end + if f then f:close() end + return F +end + +function parse.file(name,lang, args) + local F,err = parse_file(name,lang,args.package,args) + if err or not F then return F,err end + local ok,err = xpcall(function() F:finish() end,debug.traceback) + if not ok then return F,err end + return F +end + +return parse diff --git a/Documentation/compiler/ldoc/ldoc/prettify.lua b/Documentation/compiler/ldoc/ldoc/prettify.lua new file mode 100644 index 000000000..1dc625c12 --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/prettify.lua @@ -0,0 +1,131 @@ +-- Making Lua source code look pretty. +-- A simple scanner based prettifier, which scans comments for @{ref} and code +-- for known modules and functions. +-- A module reference to an example `test-fun.lua` would look like +-- `@{example:test-fun}`. +local List = require 'pl.List' +local tablex = require 'pl.tablex' +local globals = require 'ldoc.builtin.globals' +local prettify = {} + +local user_keywords = {} + +local escaped_chars = { + ['&'] = '&', + ['<'] = '<', + ['>'] = '>', +} +local escape_pat = '[&<>]' + +local function escape(str) + return (str:gsub(escape_pat,escaped_chars)) +end + +local function span(t,val) + return ('%s'):format(t,val) +end + +local spans = {keyword=true,number=true,string=true,comment=true,global=true,backtick=true} + +local cpp_lang = {C = true, c = true, cpp = true, cxx = true, h = true} + +function prettify.lua (lang, fname, code, initial_lineno, pre, linenos) + local res, lexer = List(), require 'ldoc.lexer' + local tokenizer + local ik = 1 + if not cpp_lang[lang] then + tokenizer = lexer.lua + else + tokenizer = lexer.cpp + end + + if pre then + res:append '
    \n'
    +   end
    +   initial_lineno = initial_lineno or 0
    +
    +   local tok = tokenizer(code,{},{})
    +   local error_reporter = {
    +      warning = function (self,msg)
    +         io.stderr:write(fname..':'..tok:lineno()+initial_lineno..': '..msg,'\n')
    +      end
    +   }
    +   local last_t, last_val
    +   local t,val = tok()
    +   if not t then return nil,"empty file" end
    +   while t do
    +      val = escape(val)
    +      if linenos and tok:lineno() == linenos[ik] then
    +         res:append('')
    +         ik = ik + 1
    +      end
    +      if globals.functions[val] or globals.tables[val] then
    +         t = 'global'
    +      end
    +      if user_keywords[val] then
    +        res:append(span('user-keyword keyword-' .. val,val))
    +      elseif spans[t] then
    +         if t == 'comment' or t == 'backtick' then -- may contain @{ref} or `..`
    +            val = prettify.resolve_inline_references(val,error_reporter)
    +         end
    +         res:append(span(t,val))
    +      else
    +         res:append(val)
    +      end
    +      last_t, last_val = t,val
    +      t,val = tok()
    +   end
    +   if last_t == 'comment' then
    +      res[#res] = span('comment',last_val:gsub('\r*\n$',''))
    +   end
    +   local last = res[#res]
    +   if last:match '\n$' then
    +      res[#res] = last:gsub('\n+','')
    +   end
    +   if pre then
    +      res:append '
    \n' + end + return res:join () +end + +local lxsh + +local lxsh_highlighers = {bib=true,c=true,lua=true,sh=true} + +function prettify.code (lang,fname,code,initial_lineno,pre) + if not lxsh then + return prettify.lua (lang,fname, code, initial_lineno, pre) + else + if not lxsh_highlighers[lang] then + lang = 'lua' + end + code = lxsh.highlighters[lang](code, { + formatter = lxsh.formatters.html, + external = true + }) + if not pre then + code = code:gsub("^(.-)%s*

    $", '%1') + end + return code + end +end + +function prettify.set_prettifier (pretty) + local ok + if pretty == 'lxsh' then + ok,lxsh = pcall(require,'lxsh') + if not ok then + print('pretty: '..pretty..' not found, using built-in Lua') + lxsh = nil + end + end +end + +function prettify.set_user_keywords(keywords) + if keywords then + user_keywords = tablex.makeset(keywords) + end +end + +return prettify + diff --git a/Documentation/compiler/ldoc/ldoc/tools.lua b/Documentation/compiler/ldoc/ldoc/tools.lua new file mode 100644 index 000000000..7cc61abdb --- /dev/null +++ b/Documentation/compiler/ldoc/ldoc/tools.lua @@ -0,0 +1,544 @@ +--------- +-- General utility functions for ldoc +-- @module tools + +local class = require 'pl.class' +local List = require 'pl.List' +local path = require 'pl.path' +local utils = require 'pl.utils' +local tablex = require 'pl.tablex' +local stringx = require 'pl.stringx' +local dir = require 'pl.dir' +local tools = {} +local M = tools +local append = table.insert +local lexer = require 'ldoc.lexer' +local quit = utils.quit + +-- at rendering time, can access the ldoc table from any module item, +-- or the item itself if it's a module +function M.item_ldoc (item) + local mod = item and (item.module or item) + return mod and mod.ldoc +end + +-- this constructs an iterator over a list of objects which returns only +-- those objects where a field has a certain value. It's used to iterate +-- only over functions or tables, etc. If the list of item has a module +-- with a context, then use that to pre-sort the fltered items. +-- (something rather similar exists in LuaDoc) +function M.type_iterator (list,field,value) + return function() + local fls = list:filter(function(item) + return item[field] == value + end) + local ldoc = M.item_ldoc(fls[1]) + if ldoc and ldoc.sort then + fls:sort(function(ia,ib) + return ia.name < ib.name + end) + end + return fls:iter() + end +end + +-- KindMap is used to iterate over a set of categories, called _kinds_, +-- and the associated iterator over all items in that category. +-- For instance, a module contains functions, tables, etc and we will +-- want to iterate over these categories in a specified order: +-- +-- for kind, items in module.kinds() do +-- print('kind',kind) +-- for item in items() do print(item.name) end +-- end +-- +-- The kind is typically used as a label or a Title, so for type 'function' the +-- kind is 'Functions' and so on. + +local KindMap = class() +M.KindMap = KindMap + +-- calling a KindMap returns an iterator. This returns the kind, the iterator +-- over the items of that type, and the actual type tag value. +function KindMap:__call () + local i = 1 + local klass = self.klass + return function() + local kind = klass.kinds[i] + if not kind then return nil end -- no more kinds + while not self[kind] do + i = i + 1 + kind = klass.kinds[i] + if not kind then return nil end + end + i = i + 1 + local type = klass.types_by_kind [kind].type + return kind, self[kind], type + end +end + +function KindMap:put_kind_first (kind) + -- find this kind in our kind list + local kinds = self.klass.kinds + local idx = tablex.find(kinds,kind) + -- and swop with the start! + if idx then + kinds[1],kinds[idx] = kinds[idx],kinds[1] + end +end + +function KindMap:type_of (item) + local klass = self.klass + local kind = klass.types_by_tag[item.type] + return klass.types_by_kind [kind] +end + +function KindMap:get_section_description (kind) + return self.klass.descriptions[kind] +end + +function KindMap:get_item (kind) + return self.klass.items_by_kind[kind] +end + +-- called for each new item. It does not actually create separate lists, +-- (although that would not break the interface) but creates iterators +-- for that item type if not already created. +function KindMap:add (item,items,description) + local group = item[self.fieldname] -- which wd be item's type or section + local kname = self.klass.types_by_tag[group] -- the kind name + if not self[kname] then + self[kname] = M.type_iterator (items,self.fieldname,group) + self.klass.descriptions[kname] = description + end + item.kind = kname:lower() +end + +-- KindMap has a 'class constructor' which is used to modify +-- any new base class. +function KindMap._class_init (klass) + klass.kinds = {} -- list in correct order of kinds + klass.types_by_tag = {} -- indexed by tag + klass.types_by_kind = {} -- indexed by kind + klass.descriptions = {} -- optional description for each kind + klass.items_by_kind = {} -- some kinds are items +end + + +function KindMap.add_kind (klass,tag,kind,subnames,item) + if not klass.types_by_kind[kind] then + klass.types_by_tag[tag] = kind + klass.types_by_kind[kind] = {type=tag,subnames=subnames} + if item then + klass.items_by_kind[kind] = item + end + append(klass.kinds,kind) + end +end + +----- some useful utility functions ------ + +function M.module_basepath() + local lpath = List.split(package.path,';') + for p in lpath:iter() do + local p = path.dirname(p) + if path.isabs(p) then + return p + end + end +end + +-- split a qualified name into the module part and the name part, +-- e.g 'pl.utils.split' becomes 'pl.utils' and 'split'. Also +-- must understand colon notation! +function M.split_dotted_name (s) + local s1,s2 = s:match '^(.+)[%.:](.+)$' + if s1 then -- we can split + return s1,s2 + else + return nil + end +--~ local s1,s2 = path.splitext(s) +--~ if s2=='' then return nil +--~ else return s1,s2:sub(2) +--~ end +end + +-- grab lines from a line iterator `iter` until the line matches the pattern. +-- Returns the joined lines and the line, which may be nil if we run out of +-- lines. +function M.grab_while_not(iter,pattern) + local line = iter() + local res = {} + while line and not line:match(pattern) do + append(res,line) + line = iter() + end + res = table.concat(res,'\n') + return res,line +end + + +function M.extract_identifier (value) + return value:match('([%.:%-_%w]+)(.*)$') +end + +function M.identifier_list (ls) + local ns = List() + if type(ls) == 'string' then ls = List{ns} end + for s in ls:iter() do + if s:match ',' then + ns:extend(List.split(s,'[,%s]+')) + else + ns:append(s) + end + end + return ns +end + +function M.strip (s) + return s:gsub('^%s+',''):gsub('%s+$','') +end + +-- Joins strings using a separator. +-- +-- Empty strings and nil arguments are ignored: +-- +-- assert(join('+', 'one', '', 'two', nil, 'three') == 'one+two+three') +-- assert(join(' ', '', '') == '') +-- +-- This is especially useful for the last case demonstrated above, +-- where "conventional" solutions (".." or table.concat) would result +-- in a spurious space. +function M.join(sep, ...) + local contents = {} + for i = 1, select('#', ...) do + local value = select(i, ...) + if value and value ~= "" then + contents[#contents + 1] = value + end + end + return table.concat(contents, sep) +end + +function M.check_directory(d) + if not path.isdir(d) then + if not dir.makepath(d) then + quit("Could not create "..d.." directory") + end + end +end + +function M.check_file (f,original) + if not path.exists(f) + or not path.exists(original) + or path.getmtime(original) > path.getmtime(f) then + local text,err = utils.readfile(original) + local _ + if text then + _,err = utils.writefile(f,text) + end + if err then + quit("Could not copy "..original.." to "..f) + end + end +end + +function M.writefile(name,text) + local f,err = io.open(name,"wb") +--~ local ok,err = utils.writefile(name,text) + if err then quit(err) end + f:write(text) + f:close() +end + +function M.name_of (lpath) + local _ + lpath,_ = path.splitext(lpath) + return lpath +end + +function M.this_module_name (basename,fname) + if basename == '' then + return M.name_of(fname) + end + basename = path.abspath(basename) + if basename:sub(-1,-1) ~= path.sep then + basename = basename..path.sep + end + local lpath,cnt = fname:gsub('^'..utils.escape(basename),'') + --print('deduce',lpath,cnt,basename) + if cnt ~= 1 then quit("module(...) name deduction failed: base "..basename.." "..fname) end + lpath = lpath:gsub(path.sep,'.') + return (M.name_of(lpath):gsub('%.init$','')) +end + +function M.find_existing_module (name, dname, searchfn) + local fullpath,lua = searchfn(name) + local mod = true + if not fullpath then -- maybe it's a function reference? + -- try again with the module part + local mpath,fname = M.split_dotted_name(name) + if mpath then + fullpath,lua = searchfn(mpath) + else + fullpath = nil + end + if not fullpath then + return nil, "module or function '"..dname.."' not found on module path" + else + mod = fname + end + end + if not lua then return nil, "module '"..name.."' is a binary extension" end + return fullpath, mod +end + +function M.lookup_existing_module_or_function (name, docpath) + -- first look up on the Lua module path + local on_docpath + local fullpath, mod = M.find_existing_module(name,name,path.package_path) + -- no go; but see if we can find it on the doc path + if not fullpath then + fullpath, mod = M.find_existing_module("ldoc.builtin." .. name,name,path.package_path) + on_docpath = true +--~ fullpath, mod = M.find_existing_module(name, function(name) +--~ local fpath = package.searchpath(name,docpath) +--~ return fpath,true -- result must always be 'lua'! +--~ end) + end + return fullpath, mod, on_docpath -- `mod` can be the error message +end + + +--------- lexer tools ----- + +local tnext = lexer.skipws + +local function type_of (tok) return tok and tok[1] or 'end' end +local function value_of (tok) return tok[2] end + +-- This parses Lua formal argument lists. It will return a list of argument +-- names, which also has a comments field, which will contain any commments +-- following the arguments. ldoc will use these in addition to explicit +-- param tags. + +function M.get_parameters (tok,endtoken,delim,lang) + tok = M.space_skip_getter(tok) + local args = List() + args.comments = {} + local ltl,tt = lexer.get_separated_list(tok,endtoken,delim) + + if not ltl or not ltl[1] or #ltl[1] == 0 then return args end -- no arguments + + local strip_comment, extract_arg + + if lang then + strip_comment = utils.bind1(lang.trim_comment,lang) + extract_arg = utils.bind1(lang.extract_arg,lang) + else + strip_comment = function(text) + return text:match("%s*%-%-+%s*(.*)") + end + extract_arg = function(tl,idx) + idx = idx or 1 + local res = value_of(tl[idx]) + if res == '[' then -- we do allow array indices in tables now + res = '['..value_of(tl[idx + 1])..']' + end + return res + end + end + + local function set_comment (idx,tok) + local text = stringx.rstrip(value_of(tok)) + text = strip_comment(text) + local arg = args[idx] + local current_comment = args.comments[arg] + if current_comment then + text = current_comment .. " " .. text + end + args.comments[arg] = text + end + + local function add_arg (tl,idx) + local name, type = extract_arg(tl,idx) + args:append(name) + if type then + if not args.types then args.types = List() end + args.types:append(type) + end + end + + for i = 1,#ltl do + local tl = ltl[i] -- token list for argument + if #tl > 0 then + local j = 1 + if type_of(tl[1]) == 'comment' then + -- the comments for the i-1 th arg are in the i th arg... + if i > 1 then + while type_of(tl[j]) == 'comment' do + set_comment(i-1,tl[j]) + j = j + 1 + end + else -- first comment however is for the function return comment! + args.return_comment = strip_comment(value_of(tl[i])) + j = j + 1 + end + if #tl > 1 then + add_arg(tl,j) + end + else + add_arg(tl,1) + end + if i == #ltl and #tl > 1 then + while j <= #tl and type_of(tl[j]) ~= 'comment' do + j = j + 1 + end + if j > #tl then break end -- was no comments! + while type_of(tl[j]) == 'comment' do + set_comment(i,tl[j]) + j = j + 1 + end + end + else + return nil,"empty argument" + end + end + + -- we had argument comments + -- but the last one may be outside the parens! (Geoff style) + -- (only try this stunt if it's a function parameter list!) + if (not endtoken or endtoken == ')') and (#args > 0 or next(args.comments)) then + local n = #args + local last_arg = args[n] + if not args.comments[last_arg] then + while true do + tt = {tok()} + if type_of(tt) == 'comment' then + set_comment(n,tt) + else + break + end + end + end + end + -- return what token we ended on as well - can be token _past_ ')' + return args,tt[1],tt[2] +end + +-- parse a Lua identifier - contains names separated by . and (optionally) :. +-- Set `colon` to be the secondary separator, '' for none. +function M.get_fun_name (tok,first,colon) + local res = {} + local t,name,sep,_ + colon = colon or ':' + if not first then + t,name = tnext(tok) + else + t,name = 'iden',first + end + if t ~= 'iden' then return nil end + t,sep = tnext(tok) + while sep == '.' or sep == colon do + append(res,name) + append(res,sep) + _,name = tnext(tok) + t,sep = tnext(tok) + end + append(res,name) + return table.concat(res),t,sep +end + +-- space-skipping version of token iterator +function M.space_skip_getter(tok) + return function () + local t,v = tok() + while t and t == 'space' do + t,v = tok() + end + return t,v + end +end + +function M.quote (s) + return "'"..s.."'" +end + +-- The PL Lua lexer does not do block comments +-- when used in line-grabbing mode, so this function grabs each line +-- until we meet the end of the comment +function M.grab_block_comment (v,tok,patt) + local res = {v} + repeat + v = lexer.getline(tok) + if v:match (patt) then break end + append(res,v) + append(res,'\n') + until false + res = table.concat(res) + --print(res) + return 'comment',res +end + +local prel = path.normcase('/[^/]-/%.%.') + + +function M.abspath (f) + local count + local res = path.normcase(path.abspath(f)) + while true do + res,count = res:gsub(prel,'') + if count == 0 then break end + end + return res +end + +function M.getallfiles(root,mask) + local res = List(dir.getallfiles(root,mask)) + res:sort() + return res +end + +function M.expand_file_list (list, mask) + local exclude_list = list.exclude and M.files_from_list(list.exclude, mask) + local files = List() + local function process (f) + f = M.abspath(f) + if not exclude_list or exclude_list and exclude_list:index(f) == nil then + files:append(f) + end + end + for _,f in ipairs(list) do + if path.isdir(f) then + local dfiles = M.getallfiles(f,mask) + for f in dfiles:iter() do + process(f) + end + elseif path.isfile(f) then + process(f) + else + quit("file or directory does not exist: "..M.quote(f)) + end + end + return files +end + +function M.process_file_list (list, mask, operation, ...) + local files = M.expand_file_list(list,mask) + for f in files:iter() do + operation(f,...) + end +end + +function M.files_from_list (list, mask) + local excl = List() + M.process_file_list (list, mask, function(f) + excl:append(f) + end) + return excl +end + + + +return tools diff --git a/Documentation/packed/lfs.dll b/Documentation/compiler/lfs.dll similarity index 100% rename from Documentation/packed/lfs.dll rename to Documentation/compiler/lfs.dll diff --git a/Documentation/packed/lua.exe b/Documentation/compiler/lua.exe similarity index 100% rename from Documentation/packed/lua.exe rename to Documentation/compiler/lua.exe diff --git a/Documentation/packed/pl/Date.lua b/Documentation/compiler/pl/Date.lua similarity index 100% rename from Documentation/packed/pl/Date.lua rename to Documentation/compiler/pl/Date.lua diff --git a/Documentation/packed/pl/List.lua b/Documentation/compiler/pl/List.lua similarity index 100% rename from Documentation/packed/pl/List.lua rename to Documentation/compiler/pl/List.lua diff --git a/Documentation/packed/pl/Map.lua b/Documentation/compiler/pl/Map.lua similarity index 100% rename from Documentation/packed/pl/Map.lua rename to Documentation/compiler/pl/Map.lua diff --git a/Documentation/packed/pl/MultiMap.lua b/Documentation/compiler/pl/MultiMap.lua similarity index 100% rename from Documentation/packed/pl/MultiMap.lua rename to Documentation/compiler/pl/MultiMap.lua diff --git a/Documentation/packed/pl/OrderedMap.lua b/Documentation/compiler/pl/OrderedMap.lua similarity index 100% rename from Documentation/packed/pl/OrderedMap.lua rename to Documentation/compiler/pl/OrderedMap.lua diff --git a/Documentation/packed/pl/Set.lua b/Documentation/compiler/pl/Set.lua similarity index 100% rename from Documentation/packed/pl/Set.lua rename to Documentation/compiler/pl/Set.lua diff --git a/Documentation/packed/pl/app.lua b/Documentation/compiler/pl/app.lua similarity index 100% rename from Documentation/packed/pl/app.lua rename to Documentation/compiler/pl/app.lua diff --git a/Documentation/packed/pl/array2d.lua b/Documentation/compiler/pl/array2d.lua similarity index 100% rename from Documentation/packed/pl/array2d.lua rename to Documentation/compiler/pl/array2d.lua diff --git a/Documentation/packed/pl/class.lua b/Documentation/compiler/pl/class.lua similarity index 100% rename from Documentation/packed/pl/class.lua rename to Documentation/compiler/pl/class.lua diff --git a/Documentation/packed/pl/compat.lua b/Documentation/compiler/pl/compat.lua similarity index 100% rename from Documentation/packed/pl/compat.lua rename to Documentation/compiler/pl/compat.lua diff --git a/Documentation/packed/pl/comprehension.lua b/Documentation/compiler/pl/comprehension.lua similarity index 100% rename from Documentation/packed/pl/comprehension.lua rename to Documentation/compiler/pl/comprehension.lua diff --git a/Documentation/packed/pl/config.lua b/Documentation/compiler/pl/config.lua similarity index 100% rename from Documentation/packed/pl/config.lua rename to Documentation/compiler/pl/config.lua diff --git a/Documentation/packed/pl/data.lua b/Documentation/compiler/pl/data.lua similarity index 100% rename from Documentation/packed/pl/data.lua rename to Documentation/compiler/pl/data.lua diff --git a/Documentation/packed/pl/dir.lua b/Documentation/compiler/pl/dir.lua similarity index 100% rename from Documentation/packed/pl/dir.lua rename to Documentation/compiler/pl/dir.lua diff --git a/Documentation/packed/pl/file.lua b/Documentation/compiler/pl/file.lua similarity index 100% rename from Documentation/packed/pl/file.lua rename to Documentation/compiler/pl/file.lua diff --git a/Documentation/packed/pl/func.lua b/Documentation/compiler/pl/func.lua similarity index 100% rename from Documentation/packed/pl/func.lua rename to Documentation/compiler/pl/func.lua diff --git a/Documentation/packed/pl/import_into.lua b/Documentation/compiler/pl/import_into.lua similarity index 100% rename from Documentation/packed/pl/import_into.lua rename to Documentation/compiler/pl/import_into.lua diff --git a/Documentation/packed/pl/init.lua b/Documentation/compiler/pl/init.lua similarity index 100% rename from Documentation/packed/pl/init.lua rename to Documentation/compiler/pl/init.lua diff --git a/Documentation/packed/pl/input.lua b/Documentation/compiler/pl/input.lua similarity index 100% rename from Documentation/packed/pl/input.lua rename to Documentation/compiler/pl/input.lua diff --git a/Documentation/packed/pl/lapp.lua b/Documentation/compiler/pl/lapp.lua similarity index 100% rename from Documentation/packed/pl/lapp.lua rename to Documentation/compiler/pl/lapp.lua diff --git a/Documentation/packed/pl/lexer.lua b/Documentation/compiler/pl/lexer.lua similarity index 100% rename from Documentation/packed/pl/lexer.lua rename to Documentation/compiler/pl/lexer.lua diff --git a/Documentation/packed/pl/luabalanced.lua b/Documentation/compiler/pl/luabalanced.lua similarity index 100% rename from Documentation/packed/pl/luabalanced.lua rename to Documentation/compiler/pl/luabalanced.lua diff --git a/Documentation/packed/pl/operator.lua b/Documentation/compiler/pl/operator.lua similarity index 100% rename from Documentation/packed/pl/operator.lua rename to Documentation/compiler/pl/operator.lua diff --git a/Documentation/packed/pl/path.lua b/Documentation/compiler/pl/path.lua similarity index 100% rename from Documentation/packed/pl/path.lua rename to Documentation/compiler/pl/path.lua diff --git a/Documentation/packed/pl/permute.lua b/Documentation/compiler/pl/permute.lua similarity index 100% rename from Documentation/packed/pl/permute.lua rename to Documentation/compiler/pl/permute.lua diff --git a/Documentation/packed/pl/pretty.lua b/Documentation/compiler/pl/pretty.lua similarity index 100% rename from Documentation/packed/pl/pretty.lua rename to Documentation/compiler/pl/pretty.lua diff --git a/Documentation/packed/pl/seq.lua b/Documentation/compiler/pl/seq.lua similarity index 100% rename from Documentation/packed/pl/seq.lua rename to Documentation/compiler/pl/seq.lua diff --git a/Documentation/packed/pl/sip.lua b/Documentation/compiler/pl/sip.lua similarity index 100% rename from Documentation/packed/pl/sip.lua rename to Documentation/compiler/pl/sip.lua diff --git a/Documentation/packed/pl/strict.lua b/Documentation/compiler/pl/strict.lua similarity index 100% rename from Documentation/packed/pl/strict.lua rename to Documentation/compiler/pl/strict.lua diff --git a/Documentation/packed/pl/stringio.lua b/Documentation/compiler/pl/stringio.lua similarity index 100% rename from Documentation/packed/pl/stringio.lua rename to Documentation/compiler/pl/stringio.lua diff --git a/Documentation/packed/pl/stringx.lua b/Documentation/compiler/pl/stringx.lua similarity index 100% rename from Documentation/packed/pl/stringx.lua rename to Documentation/compiler/pl/stringx.lua diff --git a/Documentation/packed/pl/tablex.lua b/Documentation/compiler/pl/tablex.lua similarity index 100% rename from Documentation/packed/pl/tablex.lua rename to Documentation/compiler/pl/tablex.lua diff --git a/Documentation/packed/pl/template.lua b/Documentation/compiler/pl/template.lua similarity index 100% rename from Documentation/packed/pl/template.lua rename to Documentation/compiler/pl/template.lua diff --git a/Documentation/packed/pl/test.lua b/Documentation/compiler/pl/test.lua similarity index 100% rename from Documentation/packed/pl/test.lua rename to Documentation/compiler/pl/test.lua diff --git a/Documentation/packed/pl/text.lua b/Documentation/compiler/pl/text.lua similarity index 100% rename from Documentation/packed/pl/text.lua rename to Documentation/compiler/pl/text.lua diff --git a/Documentation/packed/pl/types.lua b/Documentation/compiler/pl/types.lua similarity index 100% rename from Documentation/packed/pl/types.lua rename to Documentation/compiler/pl/types.lua diff --git a/Documentation/packed/pl/url.lua b/Documentation/compiler/pl/url.lua similarity index 100% rename from Documentation/packed/pl/url.lua rename to Documentation/compiler/pl/url.lua diff --git a/Documentation/packed/pl/utils.lua b/Documentation/compiler/pl/utils.lua similarity index 100% rename from Documentation/packed/pl/utils.lua rename to Documentation/compiler/pl/utils.lua diff --git a/Documentation/packed/pl/xml.lua b/Documentation/compiler/pl/xml.lua similarity index 100% rename from Documentation/packed/pl/xml.lua rename to Documentation/compiler/pl/xml.lua diff --git a/Documentation/config.ld b/Documentation/config.ld index aa5c935bd..323531f29 100644 --- a/Documentation/config.ld +++ b/Documentation/config.ld @@ -51,4 +51,4 @@ custom_display_name_handler = function(item, default_handler) end local hand = default_handler(item) return hand -end +end \ No newline at end of file diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index 36cab0925..82c28d495 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -503,7 +503,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Flow.html b/Documentation/doc/1 modules/Flow.html index 322aa4f2c..df72adc0c 100644 --- a/Documentation/doc/1 modules/Flow.html +++ b/Documentation/doc/1 modules/Flow.html @@ -874,7 +874,6 @@ Specify which translations in the strings table correspond to which languages.
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Input.html b/Documentation/doc/1 modules/Input.html index 2e1a2d074..75e058be5 100644 --- a/Documentation/doc/1 modules/Input.html +++ b/Documentation/doc/1 modules/Input.html @@ -283,7 +283,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Inventory.html b/Documentation/doc/1 modules/Inventory.html index 4d73b7d8c..26fe3bbd9 100644 --- a/Documentation/doc/1 modules/Inventory.html +++ b/Documentation/doc/1 modules/Inventory.html @@ -253,7 +253,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Logic.html b/Documentation/doc/1 modules/Logic.html index c3ccb26fd..981a95c1d 100644 --- a/Documentation/doc/1 modules/Logic.html +++ b/Documentation/doc/1 modules/Logic.html @@ -522,7 +522,6 @@ end
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Objects.html b/Documentation/doc/1 modules/Objects.html index b7230bae0..759386efb 100644 --- a/Documentation/doc/1 modules/Objects.html +++ b/Documentation/doc/1 modules/Objects.html @@ -476,7 +476,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Sound.html b/Documentation/doc/1 modules/Sound.html index a8be399fd..62d55a635 100644 --- a/Documentation/doc/1 modules/Sound.html +++ b/Documentation/doc/1 modules/Sound.html @@ -393,7 +393,6 @@ Returns nil if no voice track is playing or no subtitle present.
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html index 7d896c3af..9d3391bc2 100644 --- a/Documentation/doc/1 modules/Strings.html +++ b/Documentation/doc/1 modules/Strings.html @@ -216,7 +216,6 @@ with a call to ShowString, or
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/Util.html b/Documentation/doc/1 modules/Util.html index a6d5fbcd5..84fe4ee6f 100644 --- a/Documentation/doc/1 modules/Util.html +++ b/Documentation/doc/1 modules/Util.html @@ -484,7 +484,6 @@ PrintLog('test error log', LogLevel.ERROR)
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/1 modules/View.html b/Documentation/doc/1 modules/View.html index 82115b6ed..ca9ce58c0 100644 --- a/Documentation/doc/1 modules/View.html +++ b/Documentation/doc/1 modules/View.html @@ -543,7 +543,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Flow.Animations.html b/Documentation/doc/2 classes/Flow.Animations.html index 9ce248e97..fdeae2ec5 100644 --- a/Documentation/doc/2 classes/Flow.Animations.html +++ b/Documentation/doc/2 classes/Flow.Animations.html @@ -117,7 +117,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Flow.Fog.html b/Documentation/doc/2 classes/Flow.Fog.html index 9ae91e9a2..0bdcf6da3 100644 --- a/Documentation/doc/2 classes/Flow.Fog.html +++ b/Documentation/doc/2 classes/Flow.Fog.html @@ -244,7 +244,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Flow.InventoryItem.html b/Documentation/doc/2 classes/Flow.InventoryItem.html index 0a30d92bf..213815475 100644 --- a/Documentation/doc/2 classes/Flow.InventoryItem.html +++ b/Documentation/doc/2 classes/Flow.InventoryItem.html @@ -202,7 +202,6 @@ EXAMINE
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html index cec53ad48..4cd1cc687 100644 --- a/Documentation/doc/2 classes/Flow.Level.html +++ b/Documentation/doc/2 classes/Flow.Level.html @@ -547,7 +547,6 @@ Must be at least 4.

    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Flow.Mirror.html b/Documentation/doc/2 classes/Flow.Mirror.html index 1de997a9b..05b66c7cc 100644 --- a/Documentation/doc/2 classes/Flow.Mirror.html +++ b/Documentation/doc/2 classes/Flow.Mirror.html @@ -117,7 +117,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Flow.Settings.html b/Documentation/doc/2 classes/Flow.Settings.html index 536442843..d574e4c48 100644 --- a/Documentation/doc/2 classes/Flow.Settings.html +++ b/Documentation/doc/2 classes/Flow.Settings.html @@ -157,7 +157,6 @@ has an unrecoverable error, the game will close.
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Flow.SkyLayer.html b/Documentation/doc/2 classes/Flow.SkyLayer.html index 3c360b685..4283ab0b5 100644 --- a/Documentation/doc/2 classes/Flow.SkyLayer.html +++ b/Documentation/doc/2 classes/Flow.SkyLayer.html @@ -215,7 +215,6 @@ Less is more. City of The Dead, for example, uses a speed value of 16.
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.AIObject.html b/Documentation/doc/2 classes/Objects.AIObject.html index 5930857d6..a3133e901 100644 --- a/Documentation/doc/2 classes/Objects.AIObject.html +++ b/Documentation/doc/2 classes/Objects.AIObject.html @@ -418,7 +418,6 @@ aiObj:SetObjectID(TEN.Objects.ObjID.AI_PATROL1)
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.Camera.html b/Documentation/doc/2 classes/Objects.Camera.html index f5f80964c..1075bf54f 100644 --- a/Documentation/doc/2 classes/Objects.Camera.html +++ b/Documentation/doc/2 classes/Objects.Camera.html @@ -331,7 +331,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.LaraObject.html b/Documentation/doc/2 classes/Objects.LaraObject.html index 022286afa..d9cd30ae8 100644 --- a/Documentation/doc/2 classes/Objects.LaraObject.html +++ b/Documentation/doc/2 classes/Objects.LaraObject.html @@ -740,7 +740,6 @@ ROCKETLAUNCHER
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.Moveable.html b/Documentation/doc/2 classes/Objects.Moveable.html index 7d2b1d5c6..ee0bf4d2b 100644 --- a/Documentation/doc/2 classes/Objects.Moveable.html +++ b/Documentation/doc/2 classes/Objects.Moveable.html @@ -1915,7 +1915,6 @@ sas:SetPosition(newPos, false)
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.Room.html b/Documentation/doc/2 classes/Objects.Room.html index f24314a8d..577030295 100644 --- a/Documentation/doc/2 classes/Objects.Room.html +++ b/Documentation/doc/2 classes/Objects.Room.html @@ -370,7 +370,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.Sink.html b/Documentation/doc/2 classes/Objects.Sink.html index a7147a401..8a57ca526 100644 --- a/Documentation/doc/2 classes/Objects.Sink.html +++ b/Documentation/doc/2 classes/Objects.Sink.html @@ -281,7 +281,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.SoundSource.html b/Documentation/doc/2 classes/Objects.SoundSource.html index 6775e2bce..b6f81a9cb 100644 --- a/Documentation/doc/2 classes/Objects.SoundSource.html +++ b/Documentation/doc/2 classes/Objects.SoundSource.html @@ -279,7 +279,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.Static.html b/Documentation/doc/2 classes/Objects.Static.html index a22b25a27..bb270eaa9 100644 --- a/Documentation/doc/2 classes/Objects.Static.html +++ b/Documentation/doc/2 classes/Objects.Static.html @@ -565,7 +565,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Objects.Volume.html b/Documentation/doc/2 classes/Objects.Volume.html index eef56095d..f7ff425f2 100644 --- a/Documentation/doc/2 classes/Objects.Volume.html +++ b/Documentation/doc/2 classes/Objects.Volume.html @@ -442,7 +442,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/Strings.DisplayString.html b/Documentation/doc/2 classes/Strings.DisplayString.html index 61a44f786..392189d0f 100644 --- a/Documentation/doc/2 classes/Strings.DisplayString.html +++ b/Documentation/doc/2 classes/Strings.DisplayString.html @@ -467,7 +467,6 @@ varDisplayString:SetFlags({ TEN.Strings.DisplayStringOption.SHADOW, TEN.Strings.
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/2 classes/View.DisplaySprite.html b/Documentation/doc/2 classes/View.DisplaySprite.html index f4ced4853..46ff6e9af 100644 --- a/Documentation/doc/2 classes/View.DisplaySprite.html +++ b/Documentation/doc/2 classes/View.DisplaySprite.html @@ -525,7 +525,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/3 primitive classes/Color.html b/Documentation/doc/3 primitive classes/Color.html index 88665dc2d..0285cb260 100644 --- a/Documentation/doc/3 primitive classes/Color.html +++ b/Documentation/doc/3 primitive classes/Color.html @@ -333,7 +333,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html index 339a2177b..b78e23d34 100644 --- a/Documentation/doc/3 primitive classes/Rotation.html +++ b/Documentation/doc/3 primitive classes/Rotation.html @@ -266,7 +266,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/3 primitive classes/Vec2.html b/Documentation/doc/3 primitive classes/Vec2.html index 7ab77ac4c..72b3dc1b8 100644 --- a/Documentation/doc/3 primitive classes/Vec2.html +++ b/Documentation/doc/3 primitive classes/Vec2.html @@ -483,7 +483,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/3 primitive classes/Vec3.html b/Documentation/doc/3 primitive classes/Vec3.html index 509a57a8d..8b19f15ef 100644 --- a/Documentation/doc/3 primitive classes/Vec3.html +++ b/Documentation/doc/3 primitive classes/Vec3.html @@ -506,7 +506,6 @@
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Effects.BlendID.html b/Documentation/doc/4 enums/Effects.BlendID.html index 9476c9157..476ab4b49 100644 --- a/Documentation/doc/4 enums/Effects.BlendID.html +++ b/Documentation/doc/4 enums/Effects.BlendID.html @@ -163,7 +163,6 @@ ALPHABLEND
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Effects.EffectID.html b/Documentation/doc/4 enums/Effects.EffectID.html index 278f3ac6b..07c44b86f 100644 --- a/Documentation/doc/4 enums/Effects.EffectID.html +++ b/Documentation/doc/4 enums/Effects.EffectID.html @@ -161,7 +161,6 @@ CUSTOM
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Flow.GameStatus.html b/Documentation/doc/4 enums/Flow.GameStatus.html index 86cf8d296..10215b11c 100644 --- a/Documentation/doc/4 enums/Flow.GameStatus.html +++ b/Documentation/doc/4 enums/Flow.GameStatus.html @@ -129,7 +129,6 @@
    NORMAL
     NEW_GAME
     LOAD_GAME
    -SAVE_GAME
     EXIT_TO_TITLE
     EXIT_GAME
     LARA_DEAD
    @@ -161,7 +160,6 @@ LEVEL_COMPLETE
      
     
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Input.ActionID.html b/Documentation/doc/4 enums/Input.ActionID.html index 4264dfd18..f6d048e46 100644 --- a/Documentation/doc/4 enums/Input.ActionID.html +++ b/Documentation/doc/4 enums/Input.ActionID.html @@ -197,7 +197,6 @@ LOAD
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Objects.AmmoType.html b/Documentation/doc/4 enums/Objects.AmmoType.html index bf79850ee..ec2df23d5 100644 --- a/Documentation/doc/4 enums/Objects.AmmoType.html +++ b/Documentation/doc/4 enums/Objects.AmmoType.html @@ -168,7 +168,6 @@ ROCKET
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Objects.MoveableStatus.html b/Documentation/doc/4 enums/Objects.MoveableStatus.html index fea9d7c4e..044dfa5ea 100644 --- a/Documentation/doc/4 enums/Objects.MoveableStatus.html +++ b/Documentation/doc/4 enums/Objects.MoveableStatus.html @@ -157,7 +157,6 @@ INVISIBLE
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index 9279a447b..3d3edf241 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -1335,7 +1335,6 @@ PC_SAVE_INV_ITEM
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Objects.RoomFlagID.html b/Documentation/doc/4 enums/Objects.RoomFlagID.html index ea9670993..c6f0c9ac9 100644 --- a/Documentation/doc/4 enums/Objects.RoomFlagID.html +++ b/Documentation/doc/4 enums/Objects.RoomFlagID.html @@ -160,7 +160,6 @@ NOLENSFLARE
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Objects.RoomReverb.html b/Documentation/doc/4 enums/Objects.RoomReverb.html index 969d36433..eacf086f7 100644 --- a/Documentation/doc/4 enums/Objects.RoomReverb.html +++ b/Documentation/doc/4 enums/Objects.RoomReverb.html @@ -158,7 +158,6 @@ PIPE
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Sound.SoundTrackType.html b/Documentation/doc/4 enums/Sound.SoundTrackType.html index 4baf2f42e..eb3576a80 100644 --- a/Documentation/doc/4 enums/Sound.SoundTrackType.html +++ b/Documentation/doc/4 enums/Sound.SoundTrackType.html @@ -156,7 +156,6 @@ VOICE
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/Util.LogLevel.html b/Documentation/doc/4 enums/Util.LogLevel.html index 9dc1b9184..bf71913c6 100644 --- a/Documentation/doc/4 enums/Util.LogLevel.html +++ b/Documentation/doc/4 enums/Util.LogLevel.html @@ -155,7 +155,6 @@ ERROR
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/View.AlignMode.html b/Documentation/doc/4 enums/View.AlignMode.html index a4e8ef30c..0296cd8b0 100644 --- a/Documentation/doc/4 enums/View.AlignMode.html +++ b/Documentation/doc/4 enums/View.AlignMode.html @@ -162,7 +162,6 @@ BOTTOM_RIGHT
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/View.CameraType.html b/Documentation/doc/4 enums/View.CameraType.html index bc88d3335..d80adfad8 100644 --- a/Documentation/doc/4 enums/View.CameraType.html +++ b/Documentation/doc/4 enums/View.CameraType.html @@ -159,7 +159,6 @@ OBJECT
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/View.PostProcessMode.html b/Documentation/doc/4 enums/View.PostProcessMode.html index 31112f7ce..e508d2498 100644 --- a/Documentation/doc/4 enums/View.PostProcessMode.html +++ b/Documentation/doc/4 enums/View.PostProcessMode.html @@ -157,7 +157,6 @@ EXCLUSION
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/4 enums/View.ScaleMode.html b/Documentation/doc/4 enums/View.ScaleMode.html index 0c0c8d715..1412a584d 100644 --- a/Documentation/doc/4 enums/View.ScaleMode.html +++ b/Documentation/doc/4 enums/View.ScaleMode.html @@ -156,7 +156,6 @@ STRETCH
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/5 lua utility modules/EventSequence.html b/Documentation/doc/5 lua utility modules/EventSequence.html index 9d16623c1..5e6451f29 100644 --- a/Documentation/doc/5 lua utility modules/EventSequence.html +++ b/Documentation/doc/5 lua utility modules/EventSequence.html @@ -358,7 +358,6 @@ LevelFuncs.SpawnBaddy = function(baddy, name, pos)
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/5 lua utility modules/Timer.html b/Documentation/doc/5 lua utility modules/Timer.html index 95fd42f9e..17501b111 100644 --- a/Documentation/doc/5 lua utility modules/Timer.html +++ b/Documentation/doc/5 lua utility modules/Timer.html @@ -531,7 +531,6 @@ LevelFuncs.TriggerTimer = function(obj)
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/doc/index.html b/Documentation/doc/index.html index ff78856a6..75857d49e 100644 --- a/Documentation/doc/index.html +++ b/Documentation/doc/index.html @@ -338,7 +338,6 @@ local door = GetMoveableByName("door_type4_14")
    generated by TEN-LDoc (a fork of LDoc 1.4.6) -Last updated 2024-02-11 22:25:15
    diff --git a/Documentation/ldoc.ltp b/Documentation/ldoc.ltp index 3c1b19e61..77472d077 100644 --- a/Documentation/ldoc.ltp +++ b/Documentation/ldoc.ltp @@ -316,7 +316,6 @@
    generated by TEN-LDoc (a fork of LDoc $(ldoc.version)) -Last updated $(ldoc.updatetime)
    diff --git a/Documentation/ldocpacked.bat b/Documentation/ldocpacked.bat deleted file mode 100644 index abee421e6..000000000 --- a/Documentation/ldocpacked.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -setlocal -set LDOC_DIR=.\packed\TEN-LDoc -set LUA_PATH=.\packed\?.lua -set LUA_CPATH=.\packed\?.dll -.\packed\lua.exe %LDOC_DIR%\\ldoc.lua %* -exit /b %ERRORLEVEL% diff --git a/Documentation/packed/README.txt b/Documentation/packed/README.txt deleted file mode 100644 index 58f7123dd..000000000 --- a/Documentation/packed/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -Put this folder, "packed", in the TombEngine/Documentation directory. -Then take ldocpacked.bat and put it in the TombEngine/Documentation directory, too. -Finally, run: -ldocpacked.bat . -from the command line. diff --git a/Documentation/packed/ldocpacked.bat b/Documentation/packed/ldocpacked.bat deleted file mode 100644 index abee421e6..000000000 --- a/Documentation/packed/ldocpacked.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -setlocal -set LDOC_DIR=.\packed\TEN-LDoc -set LUA_PATH=.\packed\?.lua -set LUA_CPATH=.\packed\?.dll -.\packed\lua.exe %LDOC_DIR%\\ldoc.lua %* -exit /b %ERRORLEVEL% diff --git a/TombEngine/Scripting/Internal/TEN/Flow/GameStatuses.h b/TombEngine/Scripting/Internal/TEN/Flow/GameStatuses.h index c22ac6cfd..8e40d8269 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/GameStatuses.h +++ b/TombEngine/Scripting/Internal/TEN/Flow/GameStatuses.h @@ -17,7 +17,6 @@ The following constants are inside Flow.GameStatus. NORMAL NEW_GAME LOAD_GAME - SAVE_GAME EXIT_TO_TITLE EXIT_GAME LARA_DEAD diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index e77fbc6f3..46b5fb160 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -138,7 +138,10 @@ - CD $(ProjectDir)Specific\savegame\schema\ + CD $(ProjectDir)..\Documentation\ +CALL compile.bat . + +CD $(ProjectDir)Specific\savegame\schema\ CALL gen.bat md "$(SolutionDir)Build\$(Configuration)\Shaders" @@ -149,7 +152,7 @@ xcopy /Y "$(SolutionDir)Libs\bass\x86\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\lua\x86\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\ois\x86\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\zlib\x86\*.dll" "$(TargetDir)" - Generating savegame flatbuffer and copying needed files... + Generating documentation, savegame flatbuffer and copying needed files... @@ -185,7 +188,10 @@ xcopy /Y "$(SolutionDir)Libs\zlib\x86\*.dll" "$(TargetDir)" - CD $(ProjectDir)Specific\savegame\schema\ + CD $(ProjectDir)..\Documentation\ +CALL compile.bat . + +CD $(ProjectDir)Specific\savegame\schema\ CALL gen.bat md "$(SolutionDir)Build\$(Configuration)\Shaders" @@ -196,7 +202,7 @@ xcopy /Y "$(SolutionDir)Libs\bass\x64\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\lua\x64\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\ois\x64\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\zlib\x64\*.dll" "$(TargetDir)" - Generating savegame flatbuffer and copying needed files... + Generating documentation, savegame flatbuffer and copying needed files... @@ -245,7 +251,10 @@ del "$(TargetDir)*.exp" /q del "$(TargetDir)OIS_d.dll" /q - CD $(ProjectDir)Specific\savegame\schema\ + CD $(ProjectDir)..\Documentation\ +CALL compile.bat . + +CD $(ProjectDir)Specific\savegame\schema\ CALL gen.bat md "$(SolutionDir)Build\$(Configuration)\Shaders" @@ -258,7 +267,7 @@ xcopy /Y "$(SolutionDir)Libs\ois\x86\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\zlib\x86\*.dll" "$(TargetDir)" - Generating savegame flatbuffer and copying needed files... + Generating documentation, savegame flatbuffer and copying needed files... @@ -306,7 +315,10 @@ del "$(TargetDir)*.exp" /q del "$(TargetDir)OIS_d.dll" /q - CD $(ProjectDir)Specific\savegame\schema\ + CD $(ProjectDir)..\Documentation\ +CALL compile.bat . + +CD $(ProjectDir)Specific\savegame\schema\ CALL gen.bat md "$(SolutionDir)Build\$(Configuration)\Shaders" @@ -319,7 +331,7 @@ xcopy /Y "$(SolutionDir)Libs\ois\x64\*.dll" "$(TargetDir)" xcopy /Y "$(SolutionDir)Libs\zlib\x64\*.dll" "$(TargetDir)" - Generating savegame flatbuffer and copying needed files... + Generating documentation, savegame flatbuffer and copying needed files...