mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Merge branch 'develop' into sezz_fx_info_branch_port
This commit is contained in:
commit
0460b76685
682 changed files with 79801 additions and 26493 deletions
|
@ -1,3 +1,89 @@
|
|||
Version 1.4
|
||||
===========
|
||||
|
||||
* Fixed drawing of display sprites in title level.
|
||||
* Fixed drawing of smoke sprites and various other sprites.
|
||||
* Fixed player holster state and current vehicle not preserved correctly on level jump.
|
||||
* Fixed fade-in and fade-out effects not canceling correctly when next level is loaded.
|
||||
* Fixed shadows still being visible after shattering a moveable.
|
||||
* Fixed FOV interpolation at the end of the flyby sequence.
|
||||
* Fixed sounds resuming in pause mode while switching between apps.
|
||||
* Fixed slide directions.
|
||||
* Fixed occasional collision warnings in a log when teeth spikes object was activated.
|
||||
* Auto-switch to a crawl state if player start position is in a crawlspace.
|
||||
* Revise wall spikes:
|
||||
- Wall spikes now stop when they touch a pushable, another spike wall or a normal wall.
|
||||
- Wall spikes will shatter any shatter in its path.
|
||||
- Wall spikes can be stopped by normal antitrigger or with a volume.
|
||||
* Added hub system to preserve level state on level jumps.
|
||||
* Added ember emitter.
|
||||
* Added fish emitter.
|
||||
* Added laser beam object.
|
||||
* Added TR2 dragon.
|
||||
* Added TR3 Winston (requires updated TEN .wad2 on TombEngine.com).
|
||||
* Added TR4 squishy blocks (requires updated TEN .wad2 on TombEngine.com).
|
||||
|
||||
Lua API changes:
|
||||
* Added resetHub flag to Flow.Level, which allows to reset hub data.
|
||||
* Added Flow.GetFlipMapStatus() function to get current flipmap status.
|
||||
* Added Moveable:GetMeshCount() function to get number of moveable meshes.
|
||||
* Added Static:GetHP() and Static:SetHP() functions to change shatterable static mesh hit points.
|
||||
* Fixed Moveable:SetOnCollidedWithObject() callback.
|
||||
|
||||
Version 1.3
|
||||
===========
|
||||
|
||||
* Fixed crash if title logo is removed from Textures folder.
|
||||
* Fixed crash if unknown player state ID is encountered.
|
||||
* Fixed bug with OCB 2 on pushables, and some other pushable bugs.
|
||||
* Fixed pushable camera bug during edge slip.
|
||||
* Fixed lever switch turn off alignment animation.
|
||||
* Fixed lack of water splash in certain scenarios.
|
||||
* Fixed hydra flame not showing when charging.
|
||||
* Fixed shockwave light for hammer god.
|
||||
* Fixed camera shaking in some cases when the player is in quicksand room.
|
||||
* Fixed certain flame emitter OCBs emitting fire in wrong directions.
|
||||
* Fixed player not being able to pick up a torch when crouching.
|
||||
* Fixed jittery camera when performing crawl-to-hang.
|
||||
* Fixed several issues with limited pistol ammo.
|
||||
* Fixed player not being able to crawl if two-handed weapon is currently equipped.
|
||||
* Fixed playback issues with audio tracks placed in subfolders.
|
||||
* Fixed thin caustics outline on the edge of the blocks.
|
||||
* Fixed big static objects affected wrongly by dynamic lights.
|
||||
* Fixed legacy trigger leveljumps ignoring provided level index.
|
||||
* Fixed incorrect light collection in some cases.
|
||||
* Fixed normal mapping for rooms, items, and statics.
|
||||
* Added ambient occlusion (SSAO).
|
||||
* Added new post-process workflow (monochrome, negative, exclusion) with tinting.
|
||||
* Added SMAA antialiasing instead of MSAA.
|
||||
* Added previously missing player start position object functionality.
|
||||
* Added fast speed for fly cheat by holding Sprint input action.
|
||||
* Added speedometer to vehicles.
|
||||
* Added global node events.
|
||||
* Totally revised transparency handling.
|
||||
* Increased the maximum frames for animated sequences from 128 to 256.
|
||||
* Optimized the renderer.
|
||||
* Separate underwater wall and ceiling switch objects into two slots each.
|
||||
* Accurately rotate display sprites around the pivot defined by the align mode.
|
||||
* Allow walking on slopes when wading in water (similar to quicksand).
|
||||
* Allow player to pull certain levers with both hands when holding a flare.
|
||||
* Ported twin auto gun from TR3.
|
||||
* Revised keyhole OCBs to account for keeping or losing keys:
|
||||
- OCB 0: Play default animation and lose key.
|
||||
- Positive OCB: Play anim number and keep key.
|
||||
- Negative OCB: Play anim number and lose key.
|
||||
* Revised Wolf OCBs:
|
||||
- OCB 0: Wolf starts in walking animation, ready to chase Lara.
|
||||
- OCB 1: Wolf starts in sleeping animation.
|
||||
|
||||
Lua API changes:
|
||||
* Added Lara:GetInteractedMoveable() which returns currently interacted moveable by Lara.
|
||||
* Added Moveable:SetStatus() to set the current status of the moveable.
|
||||
* Added Room:GetColor() to get room's ambient light color.
|
||||
* Added Util.PickMoveableByDisplayPosition() and Util.PickStaticByDisplayPosition() functions.
|
||||
* Added View.GetCameraPosition(), View.GetCameraTarget() and View.GetCameraRoom() functions.
|
||||
* Added View.SetPostProcessMode(), View.SetPostProcessStrength() and View.SetPostProcessTint() functions.
|
||||
|
||||
Version 1.2
|
||||
===========
|
||||
|
||||
|
@ -12,6 +98,8 @@ Version 1.2
|
|||
* Fix TR1 bear various original AI issues.
|
||||
* Fix TR2 knife thrower AI.
|
||||
* Fix TR2 doberman crashing the game when killed by explosive weapons.
|
||||
* Fix random crashes when killing exploding enemies.
|
||||
* Fix random crashes in battle with more than 8 enemies.
|
||||
* Fix volume change in settings not affecting voice track.
|
||||
* Fix several lighting bugs.
|
||||
* Fix double drawing additive faces.
|
||||
|
@ -49,14 +137,14 @@ Lua API changes:
|
|||
* Split and organize functions in `Misc` namespace to appropriate new namespaces.
|
||||
* Make Vec2 and Vec3 objects float-based instead of integer-based.
|
||||
* Add DisplaySprite object.
|
||||
* Add Flow:EnableLoadSave() function to disable savegames.
|
||||
* Add Flow:EnablePointFilter() function to disable bilinear filtering.
|
||||
* Add Lara:GetAmmoType() function to read the ammo that player is using.
|
||||
* Add Moveable:GetEndFrame() function to get the end frame number of a moveable's active animation.
|
||||
* Add Flow.EnableLoadSave() function to disable savegames.
|
||||
* Add Flow.EnablePointFilter() function to disable bilinear filtering.
|
||||
* Add View.GetAspectRatio() function to get the screen resolution's aspect ratio.
|
||||
* Add Logic.HandleEvent() function to call node events.
|
||||
* Add Input.GetCursorDisplayPosition() function to get the cursor's position.
|
||||
* Add functions to load, save, delete and check existence of savegames.
|
||||
* Add Lara:GetAmmoType() function to read the ammo that player is using.
|
||||
* Add Moveable:GetEndFrame() function to get the end frame number of a moveable's current animation.
|
||||
* Add extra parameter to GiveItem() function to optionally display an inventory item given to the player in the pickup summary.
|
||||
* Add DisplayStringOption.RIGHT and DisplayStringOption.BLINK flags for DisplayString.
|
||||
* Add log messages warnings to functions AddCallback and RemoveCallback.
|
||||
|
|
7
Documentation/compile.bat
Normal file
7
Documentation/compile.bat
Normal file
|
@ -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%
|
22
Documentation/compiler/ldoc/COPYRIGHT
Normal file
22
Documentation/compiler/ldoc/COPYRIGHT
Normal file
|
@ -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.
|
||||
|
66
Documentation/compiler/ldoc/ldoc-scm-3.rockspec
Normal file
66
Documentation/compiler/ldoc/ldoc-scm-3.rockspec
Normal file
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
866
Documentation/compiler/ldoc/ldoc.lua
Normal file
866
Documentation/compiler/ldoc/ldoc.lua
Normal file
|
@ -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] <file>
|
||||
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
|
||||
|
||||
<file> (string) source file or directory containing source
|
||||
|
||||
`ldoc .` reads options from an `config.ld` file in same directory;
|
||||
`ldoc -c path/to/myconfig.ld <file>` reads options from `path/to/myconfig.ld`
|
||||
and processes <file> 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 '<h2>'..path.basename(f)..'</h2>\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
|
3
Documentation/compiler/ldoc/ldoc/SciTE.properties
Normal file
3
Documentation/compiler/ldoc/ldoc/SciTE.properties
Normal file
|
@ -0,0 +1,3 @@
|
|||
tabsize=3
|
||||
indent.size=3
|
||||
use.tabs=0
|
50
Documentation/compiler/ldoc/ldoc/builtin/coroutine.lua
Normal file
50
Documentation/compiler/ldoc/ldoc/builtin/coroutine.lua
Normal file
|
@ -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
|
124
Documentation/compiler/ldoc/ldoc/builtin/debug.lua
Normal file
124
Documentation/compiler/ldoc/ldoc/builtin/debug.lua
Normal file
|
@ -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
|
243
Documentation/compiler/ldoc/ldoc/builtin/global.lua
Normal file
243
Documentation/compiler/ldoc/ldoc/builtin/global.lua
Normal file
|
@ -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
|
||||
|
186
Documentation/compiler/ldoc/ldoc/builtin/globals.lua
Normal file
186
Documentation/compiler/ldoc/ldoc/builtin/globals.lua
Normal file
|
@ -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
|
162
Documentation/compiler/ldoc/ldoc/builtin/io.lua
Normal file
162
Documentation/compiler/ldoc/ldoc/builtin/io.lua
Normal file
|
@ -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
|
125
Documentation/compiler/ldoc/ldoc/builtin/lfs.lua
Normal file
125
Documentation/compiler/ldoc/ldoc/builtin/lfs.lua
Normal file
|
@ -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
|
214
Documentation/compiler/ldoc/ldoc/builtin/lpeg.lua
Normal file
214
Documentation/compiler/ldoc/ldoc/builtin/lpeg.lua
Normal file
|
@ -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
|
144
Documentation/compiler/ldoc/ldoc/builtin/math.lua
Normal file
144
Documentation/compiler/ldoc/ldoc/builtin/math.lua
Normal file
|
@ -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
|
112
Documentation/compiler/ldoc/ldoc/builtin/os.lua
Normal file
112
Documentation/compiler/ldoc/ldoc/builtin/os.lua
Normal file
|
@ -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
|
97
Documentation/compiler/ldoc/ldoc/builtin/package.lua
Normal file
97
Documentation/compiler/ldoc/ldoc/builtin/package.lua
Normal file
|
@ -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
|
191
Documentation/compiler/ldoc/ldoc/builtin/string.lua
Normal file
191
Documentation/compiler/ldoc/ldoc/builtin/string.lua
Normal file
|
@ -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
|
||||
|
52
Documentation/compiler/ldoc/ldoc/builtin/table.lua
Normal file
52
Documentation/compiler/ldoc/ldoc/builtin/table.lua
Normal file
|
@ -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
|
48
Documentation/compiler/ldoc/ldoc/builtin/utf8.lua
Normal file
48
Documentation/compiler/ldoc/ldoc/builtin/utf8.lua
Normal file
|
@ -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
|
11
Documentation/compiler/ldoc/ldoc/config.ld
Normal file
11
Documentation/compiler/ldoc/ldoc/config.ld
Normal file
|
@ -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'
|
1515
Documentation/compiler/ldoc/ldoc/doc.lua
Normal file
1515
Documentation/compiler/ldoc/ldoc/doc.lua
Normal file
File diff suppressed because it is too large
Load diff
402
Documentation/compiler/ldoc/ldoc/html.lua
Normal file
402
Documentation/compiler/ldoc/ldoc/html.lua
Normal file
|
@ -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 '<li>','</li>' 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
|
||||
-- ?<type> is short for ?nil|<type>
|
||||
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] = ('<a class="type" href="%s">%s</a>'):format(ldoc.href(ref),name)
|
||||
else
|
||||
types[#types+1] = '<span class="type">'..name..'</span>'
|
||||
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
|
||||
|
19
Documentation/compiler/ldoc/ldoc/html/_code_css.lua
Normal file
19
Documentation/compiler/ldoc/ldoc/html/_code_css.lua
Normal file
|
@ -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; }
|
||||
|
||||
]]
|
66
Documentation/compiler/ldoc/ldoc/html/_reset_css.lua
Normal file
66
Documentation/compiler/ldoc/ldoc/html/_reset_css.lua
Normal file
|
@ -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 */
|
||||
]]
|
225
Documentation/compiler/ldoc/ldoc/html/ldoc_css.lua
Normal file
225
Documentation/compiler/ldoc/ldoc/html/ldoc_css.lua
Normal file
|
@ -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')
|
233
Documentation/compiler/ldoc/ldoc/html/ldoc_fixed_css.lua
Normal file
233
Documentation/compiler/ldoc/ldoc/html/ldoc_fixed_css.lua
Normal file
|
@ -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')
|
324
Documentation/compiler/ldoc/ldoc/html/ldoc_ltp.lua
Normal file
324
Documentation/compiler/ldoc/ldoc/html/ldoc_ltp.lua
Normal file
|
@ -0,0 +1,324 @@
|
|||
return [==[
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=$(ldoc.doc_charset)"/>
|
||||
<head>
|
||||
<title>$(ldoc.title)</title>
|
||||
<link rel="stylesheet" href="$(ldoc.css)" type="text/css" />
|
||||
# if ldoc.custom_css then -- add custom CSS file if configured.
|
||||
<link rel="stylesheet" href="$(ldoc.custom_css)" type="text/css" />
|
||||
# end
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
# 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'
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>$(ldoc.project)</h1>
|
||||
|
||||
# if ldoc.icon then
|
||||
# if module then
|
||||
<img src="../data/$(ldoc.icon)" />
|
||||
# else
|
||||
<img src="data/$(ldoc.icon)" />
|
||||
# end
|
||||
# end
|
||||
|
||||
# if not ldoc.single and module then -- reference back to project index
|
||||
<ul>
|
||||
<li><a href="../$(ldoc.output).html">Index</a></li>
|
||||
</ul>
|
||||
# end
|
||||
|
||||
# --------- contents of module -------------
|
||||
# if module and not ldoc.no_summary and #module.items > 0 then
|
||||
<h2>Contents</h2>
|
||||
<ul>
|
||||
# for kind,items in module.kinds() do
|
||||
<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
|
||||
# end
|
||||
</ul>
|
||||
# end
|
||||
|
||||
|
||||
# if ldoc.no_summary and module and not ldoc.one then -- bang out the functions on the side
|
||||
# for kind, items in module.kinds() do
|
||||
<h2>$(kind)</h2>
|
||||
<ul class="nowrap">
|
||||
# for item in items() do
|
||||
<li><a href="#$(item.name)">$(display_name(item))</a></li>
|
||||
# end
|
||||
</ul>
|
||||
# end
|
||||
# end
|
||||
# -------- contents of project ----------
|
||||
# local this_mod = module and module.name
|
||||
# for kind, mods, type in ldoc.kinds() do
|
||||
# if ldoc.allowed_in_contents(type,module) then
|
||||
<h2>$(kind)</h2>
|
||||
<ul class="$(kind=='Topics' and '' or 'nowrap')">
|
||||
# for mod in mods() do local name = display_name(mod)
|
||||
# if mod.name == this_mod then
|
||||
<li><strong>$(name)</strong></li>
|
||||
# else
|
||||
<li><a href="$(ldoc.ref_to_module(mod))">$(name)</a></li>
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
</ul>
|
||||
# end
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
# if ldoc.body then -- verbatim HTML as contents; 'non-code' entries
|
||||
$(ldoc.body)
|
||||
# elseif module then -- module documentation
|
||||
<h1>$(ldoc.module_typename(module)) <code>$(module.name)</code></h1>
|
||||
<p>$(M(module.summary,module))</p>
|
||||
<p>$(M(module.description,module))</p>
|
||||
# if module.tags.include then
|
||||
$(M(ldoc.include_file(module.tags.include)))
|
||||
# end
|
||||
# if module.see then
|
||||
# local li,il = use_li(module.see)
|
||||
<h3>See also:</h3>
|
||||
<ul>
|
||||
# for see in iter(module.see) do
|
||||
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if see
|
||||
# if module.usage then
|
||||
# local li,il = use_li(module.usage)
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
# for usage in iter(module.usage) do
|
||||
$(li)<pre class="example">$(ldoc.escape(usage))</pre>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if usage
|
||||
# if module.info then
|
||||
<h3>Info:</h3>
|
||||
<ul>
|
||||
# for tag, value in module.info:iter() do
|
||||
<li><strong>$(tag)</strong>: $(M(value,module))</li>
|
||||
# end
|
||||
</ul>
|
||||
# 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
|
||||
<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
|
||||
<table class="function_list">
|
||||
# for item in items() do
|
||||
<tr>
|
||||
<td class="name" $(nowrap)><a href="#$(item.name)">$(display_name(item))</a></td>
|
||||
<td class="summary">$(M(item.summary,item))</td>
|
||||
</tr>
|
||||
# end -- for items
|
||||
</table>
|
||||
#end -- for kinds
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
#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) ~= ""
|
||||
<h2 class="section-header $(has_description and 'has-description')"><a name="$(no_spaces(kind))"></a>$(kind)</h2>
|
||||
$(M(module.kinds:get_section_description(kind),nil))
|
||||
# if kitem then
|
||||
# if has_description then
|
||||
<div class="section-description">
|
||||
$(M(ldoc.descript(kitem),kitem))
|
||||
</div>
|
||||
# end
|
||||
# if kitem.usage then
|
||||
<h3>Usage:</h3>
|
||||
<pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre>
|
||||
# end
|
||||
# end
|
||||
<dl class="function">
|
||||
# for item in items() do
|
||||
<dt>
|
||||
<a name = "$(item.name)"></a>
|
||||
<strong>$(display_name(item))</strong>
|
||||
# if ldoc.prettify_files and ldoc.is_file_prettified[item.module.file.filename] then
|
||||
<a style="float:right;" href="$(ldoc.source_ref(item))">line $(item.lineno)</a>
|
||||
# end
|
||||
</dt>
|
||||
<dd>
|
||||
$(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)
|
||||
<h3>$(custom.title or custom[1]):</h3>
|
||||
<ul>
|
||||
# for value in iter(tag) do
|
||||
$(li)$(custom.format and custom.format(value) or M(value))$(il)
|
||||
# end -- for
|
||||
# end -- if tag
|
||||
</ul>
|
||||
# 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
|
||||
<h3>$(subnames):</h3>
|
||||
# end
|
||||
<ul>
|
||||
# for parm in iter(item.params) do
|
||||
# local param,sublist = item:subparam(parm)
|
||||
# if sublist then
|
||||
<li><span class="parameter">$(sublist)</span>$(M(item.params.map[sublist],item))
|
||||
<ul>
|
||||
# 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)
|
||||
<li><span class="parameter">$(name)</span>
|
||||
# if tp ~= '' then
|
||||
<span class="types">$(tp)</span>
|
||||
# end
|
||||
$(M(item.params.map[p],item))
|
||||
# if def == true then
|
||||
(<em>optional</em>)
|
||||
# elseif def then
|
||||
(<em>default</em> $(def))
|
||||
# end
|
||||
# if item:readonly(p) then
|
||||
<em>readonly</em>
|
||||
# end
|
||||
</li>
|
||||
# end
|
||||
# if sublist then
|
||||
</li></ul>
|
||||
# end
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if params
|
||||
|
||||
# if show_return and item.retgroups then local groups = item.retgroups
|
||||
<h3>Returns:</h3>
|
||||
# for i,group in ldoc.ipairs(groups) do local li,il = use_li(group)
|
||||
<ol>
|
||||
# for r in group:iter() do local type, ctypes = item:return_type(r); local rt = ldoc.typename(type)
|
||||
$(li)
|
||||
# if rt ~= '' then
|
||||
<span class="types">$(rt)</span>
|
||||
# end
|
||||
$(M(r.text,item))$(il)
|
||||
# if ctypes then
|
||||
<ul>
|
||||
# for c in ctypes:iter() do
|
||||
<li><span class="parameter">$(c.name)</span>
|
||||
<span class="types">$(ldoc.typename(c.type))</span>
|
||||
$(M(c.comment,item))</li>
|
||||
# end
|
||||
</ul>
|
||||
# end -- if ctypes
|
||||
# end -- for r
|
||||
</ol>
|
||||
# if i < #groups then
|
||||
<h3>Or</h3>
|
||||
# end
|
||||
# end -- for group
|
||||
# end -- if returns
|
||||
|
||||
# if show_return and item.raise then
|
||||
<h3>Raises:</h3>
|
||||
$(M(item.raise,item))
|
||||
# end
|
||||
|
||||
# if item.see then
|
||||
# local li,il = use_li(item.see)
|
||||
<h3>See also:</h3>
|
||||
<ul>
|
||||
# for see in iter(item.see) do
|
||||
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if see
|
||||
|
||||
# if item.usage then
|
||||
# local li,il = use_li(item.usage)
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
# for usage in iter(item.usage) do
|
||||
$(li)<pre class="example">$(ldoc.prettify(usage))</pre>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if usage
|
||||
|
||||
</dd>
|
||||
# end -- for items
|
||||
</dl>
|
||||
# end -- for kinds
|
||||
|
||||
# else -- if module; project-level contents
|
||||
|
||||
# if ldoc.description then
|
||||
<h2>$(M(ldoc.description,nil))</h2>
|
||||
# end
|
||||
# if ldoc.full_description then
|
||||
<p>$(M(ldoc.full_description,nil))</p>
|
||||
# end
|
||||
|
||||
# for kind, mods in ldoc.kinds() do
|
||||
<h2>$(kind)</h2>
|
||||
# kind = kind:lower()
|
||||
<table class="module_list">
|
||||
# for m in mods() do
|
||||
<tr>
|
||||
<td class="name" $(nowrap)><a href="$(no_spaces(kind))/$(m.name).html">$(m.name)</a></td>
|
||||
<td class="summary">$(M(ldoc.strip_header(m.summary),m))</td>
|
||||
</tr>
|
||||
# end -- for modules
|
||||
</table>
|
||||
# end -- for kinds
|
||||
# end -- if module
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc $(ldoc.version)</a></i>
|
||||
<i style="float:right;">Last updated $(ldoc.updatetime) </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
||||
]==]
|
||||
|
17
Documentation/compiler/ldoc/ldoc/html/ldoc_md_ltp.lua
Normal file
17
Documentation/compiler/ldoc/ldoc/html/ldoc_md_ltp.lua
Normal file
|
@ -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
|
||||
]]
|
292
Documentation/compiler/ldoc/ldoc/html/ldoc_new_css.lua
Normal file
292
Documentation/compiler/ldoc/ldoc/html/ldoc_new_css.lua
Normal file
|
@ -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; }
|
||||
]]
|
202
Documentation/compiler/ldoc/ldoc/html/ldoc_one_css.lua
Normal file
202
Documentation/compiler/ldoc/ldoc/html/ldoc_one_css.lua
Normal file
|
@ -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')
|
225
Documentation/compiler/ldoc/ldoc/html/ldoc_pale_css.lua
Normal file
225
Documentation/compiler/ldoc/ldoc/html/ldoc_pale_css.lua
Normal file
|
@ -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')
|
379
Documentation/compiler/ldoc/ldoc/lang.lua
Normal file
379
Documentation/compiler/ldoc/ldoc/lang.lua
Normal file
|
@ -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 = <anything else> (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() }
|
504
Documentation/compiler/ldoc/ldoc/lexer.lua
Normal file
504
Documentation/compiler/ldoc/ldoc/lexer.lua
Normal file
|
@ -0,0 +1,504 @@
|
|||
--- Lexical scanner for creating a sequence of tokens from text. <br>
|
||||
-- <p><code>lexer.scan(s)</code> returns an iterator over all tokens found in the
|
||||
-- string <code>s</code>. This iterator returns two values, a token type string
|
||||
-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the
|
||||
-- token.
|
||||
-- <p>
|
||||
-- Versions specialized for Lua and C are available; these also handle block comments
|
||||
-- and classify keywords as 'keyword' tokens. For example:
|
||||
-- <pre class=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
|
||||
-- </pre>
|
||||
--
|
||||
-- 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. <br>
|
||||
-- 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 <code>res["and"]</code> etc would be <code>true</code>.
|
||||
-- @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
|
1357
Documentation/compiler/ldoc/ldoc/markdown.lua
Normal file
1357
Documentation/compiler/ldoc/ldoc/markdown.lua
Normal file
File diff suppressed because it is too large
Load diff
420
Documentation/compiler/ldoc/ldoc/markup.lua
Normal file
420
Documentation/compiler/ldoc/ldoc/markup.lua
Normal file
|
@ -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 <references> 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 = ('<a href="%s">%s</a>'):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 ('<a href="%s">%s</a>'):format(ldoc.href(ref),label)
|
||||
else
|
||||
return '<code>'..label..'</code>'
|
||||
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,'<pre>')
|
||||
append(res, code)
|
||||
append(res,'</pre>')
|
||||
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,('<a name="%s"></a>'):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<p>')
|
||||
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*<p>(.+)</p>%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
|
430
Documentation/compiler/ldoc/ldoc/parse.lua
Normal file
430
Documentation/compiler/ldoc/ldoc/parse.lua
Normal file
|
@ -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
|
131
Documentation/compiler/ldoc/ldoc/prettify.lua
Normal file
131
Documentation/compiler/ldoc/ldoc/prettify.lua
Normal file
|
@ -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 ('<span class="%s">%s</span>'):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 '<pre>\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('<a id="'..linenos[ik]..'"></a>')
|
||||
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 '</pre>\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("^<pre.->(.-)%s*</pre>$", '%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
|
||||
|
544
Documentation/compiler/ldoc/ldoc/tools.lua
Normal file
544
Documentation/compiler/ldoc/ldoc/tools.lua
Normal file
|
@ -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
|
BIN
Documentation/compiler/lfs.dll
Normal file
BIN
Documentation/compiler/lfs.dll
Normal file
Binary file not shown.
BIN
Documentation/compiler/lua.exe
Normal file
BIN
Documentation/compiler/lua.exe
Normal file
Binary file not shown.
675
Documentation/compiler/pl/Date.lua
Normal file
675
Documentation/compiler/pl/Date.lua
Normal file
|
@ -0,0 +1,675 @@
|
|||
--- Date and Date Format classes.
|
||||
-- See @{05-dates.md|the Guide}.
|
||||
--
|
||||
-- NOTE: the date module is deprecated! see
|
||||
-- https://github.com/lunarmodules/Penlight/issues/285
|
||||
--
|
||||
-- Dependencies: `pl.class`, `pl.stringx`, `pl.utils`
|
||||
-- @classmod pl.Date
|
||||
-- @pragma nostrip
|
||||
|
||||
local class = require 'pl.class'
|
||||
local os_time, os_date = os.time, os.date
|
||||
local stringx = require 'pl.stringx'
|
||||
local utils = require 'pl.utils'
|
||||
local assert_arg,assert_string = utils.assert_arg,utils.assert_string
|
||||
|
||||
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "the 'Date' module is deprecated, see https://github.com/lunarmodules/Penlight/issues/285",
|
||||
version_removed = "2.0.0",
|
||||
version_deprecated = "1.9.2",
|
||||
}
|
||||
|
||||
|
||||
local Date = class()
|
||||
Date.Format = class()
|
||||
|
||||
--- Date constructor.
|
||||
-- @param t this can be either
|
||||
--
|
||||
-- * `nil` or empty - use current date and time
|
||||
-- * number - seconds since epoch (as returned by `os.time`). Resulting time is UTC
|
||||
-- * `Date` - make a copy of this date
|
||||
-- * table - table containing year, month, etc as for `os.time`. You may leave out year, month or day,
|
||||
-- in which case current values will be used.
|
||||
-- * year (will be followed by month, day etc)
|
||||
--
|
||||
-- @param ... true if Universal Coordinated Time, or two to five numbers: month,day,hour,min,sec
|
||||
-- @function Date
|
||||
function Date:_init(t,...)
|
||||
local time
|
||||
local nargs = select('#',...)
|
||||
if nargs > 2 then
|
||||
local extra = {...}
|
||||
local year = t
|
||||
t = {
|
||||
year = year,
|
||||
month = extra[1],
|
||||
day = extra[2],
|
||||
hour = extra[3],
|
||||
min = extra[4],
|
||||
sec = extra[5]
|
||||
}
|
||||
end
|
||||
if nargs == 1 then
|
||||
self.utc = select(1,...) == true
|
||||
end
|
||||
if t == nil or t == 'utc' then
|
||||
time = os_time()
|
||||
self.utc = t == 'utc'
|
||||
elseif type(t) == 'number' then
|
||||
time = t
|
||||
if self.utc == nil then self.utc = true end
|
||||
elseif type(t) == 'table' then
|
||||
if getmetatable(t) == Date then -- copy ctor
|
||||
time = t.time
|
||||
self.utc = t.utc
|
||||
else
|
||||
if not (t.year and t.month) then
|
||||
local lt = os_date('*t')
|
||||
if not t.year and not t.month and not t.day then
|
||||
t.year = lt.year
|
||||
t.month = lt.month
|
||||
t.day = lt.day
|
||||
else
|
||||
t.year = t.year or lt.year
|
||||
t.month = t.month or (t.day and lt.month or 1)
|
||||
t.day = t.day or 1
|
||||
end
|
||||
end
|
||||
t.day = t.day or 1
|
||||
time = os_time(t)
|
||||
end
|
||||
else
|
||||
error("bad type for Date constructor: "..type(t),2)
|
||||
end
|
||||
self:set(time)
|
||||
end
|
||||
|
||||
--- set the current time of this Date object.
|
||||
-- @int t seconds since epoch
|
||||
function Date:set(t)
|
||||
self.time = t
|
||||
if self.utc then
|
||||
self.tab = os_date('!*t',t)
|
||||
else
|
||||
self.tab = os_date('*t',t)
|
||||
end
|
||||
end
|
||||
|
||||
--- get the time zone offset from UTC.
|
||||
-- @int ts seconds ahead of UTC
|
||||
function Date.tzone (ts)
|
||||
if ts == nil then
|
||||
ts = os_time()
|
||||
elseif type(ts) == "table" then
|
||||
if getmetatable(ts) == Date then
|
||||
ts = ts.time
|
||||
else
|
||||
ts = Date(ts).time
|
||||
end
|
||||
end
|
||||
local utc = os_date('!*t',ts)
|
||||
local lcl = os_date('*t',ts)
|
||||
lcl.isdst = false
|
||||
return os.difftime(os_time(lcl), os_time(utc))
|
||||
end
|
||||
|
||||
--- convert this date to UTC.
|
||||
function Date:toUTC ()
|
||||
local ndate = Date(self)
|
||||
if not self.utc then
|
||||
ndate.utc = true
|
||||
ndate:set(ndate.time)
|
||||
end
|
||||
return ndate
|
||||
end
|
||||
|
||||
--- convert this UTC date to local.
|
||||
function Date:toLocal ()
|
||||
local ndate = Date(self)
|
||||
if self.utc then
|
||||
ndate.utc = false
|
||||
ndate:set(ndate.time)
|
||||
--~ ndate:add { sec = Date.tzone(self) }
|
||||
end
|
||||
return ndate
|
||||
end
|
||||
|
||||
--- set the year.
|
||||
-- @int y Four-digit year
|
||||
-- @class function
|
||||
-- @name Date:year
|
||||
|
||||
--- set the month.
|
||||
-- @int m month
|
||||
-- @class function
|
||||
-- @name Date:month
|
||||
|
||||
--- set the day.
|
||||
-- @int d day
|
||||
-- @class function
|
||||
-- @name Date:day
|
||||
|
||||
--- set the hour.
|
||||
-- @int h hour
|
||||
-- @class function
|
||||
-- @name Date:hour
|
||||
|
||||
--- set the minutes.
|
||||
-- @int min minutes
|
||||
-- @class function
|
||||
-- @name Date:min
|
||||
|
||||
--- set the seconds.
|
||||
-- @int sec seconds
|
||||
-- @class function
|
||||
-- @name Date:sec
|
||||
|
||||
--- set the day of year.
|
||||
-- @class function
|
||||
-- @int yday day of year
|
||||
-- @name Date:yday
|
||||
|
||||
--- get the year.
|
||||
-- @int y Four-digit year
|
||||
-- @class function
|
||||
-- @name Date:year
|
||||
|
||||
--- get the month.
|
||||
-- @class function
|
||||
-- @name Date:month
|
||||
|
||||
--- get the day.
|
||||
-- @class function
|
||||
-- @name Date:day
|
||||
|
||||
--- get the hour.
|
||||
-- @class function
|
||||
-- @name Date:hour
|
||||
|
||||
--- get the minutes.
|
||||
-- @class function
|
||||
-- @name Date:min
|
||||
|
||||
--- get the seconds.
|
||||
-- @class function
|
||||
-- @name Date:sec
|
||||
|
||||
--- get the day of year.
|
||||
-- @class function
|
||||
-- @name Date:yday
|
||||
|
||||
|
||||
for _,c in ipairs{'year','month','day','hour','min','sec','yday'} do
|
||||
Date[c] = function(self,val)
|
||||
if val then
|
||||
assert_arg(1,val,"number")
|
||||
self.tab[c] = val
|
||||
self:set(os_time(self.tab))
|
||||
return self
|
||||
else
|
||||
return self.tab[c]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- name of day of week.
|
||||
-- @bool full abbreviated if true, full otherwise.
|
||||
-- @ret string name
|
||||
function Date:weekday_name(full)
|
||||
return os_date(full and '%A' or '%a',self.time)
|
||||
end
|
||||
|
||||
--- name of month.
|
||||
-- @int full abbreviated if true, full otherwise.
|
||||
-- @ret string name
|
||||
function Date:month_name(full)
|
||||
return os_date(full and '%B' or '%b',self.time)
|
||||
end
|
||||
|
||||
--- is this day on a weekend?.
|
||||
function Date:is_weekend()
|
||||
return self.tab.wday == 1 or self.tab.wday == 7
|
||||
end
|
||||
|
||||
--- add to a date object.
|
||||
-- @param t a table containing one of the following keys and a value:
|
||||
-- one of `year`,`month`,`day`,`hour`,`min`,`sec`
|
||||
-- @return this date
|
||||
function Date:add(t)
|
||||
local old_dst = self.tab.isdst
|
||||
local key,val = next(t)
|
||||
self.tab[key] = self.tab[key] + val
|
||||
self:set(os_time(self.tab))
|
||||
if old_dst ~= self.tab.isdst then
|
||||
self.tab.hour = self.tab.hour - (old_dst and 1 or -1)
|
||||
self:set(os_time(self.tab))
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- last day of the month.
|
||||
-- @return int day
|
||||
function Date:last_day()
|
||||
local d = 28
|
||||
local m = self.tab.month
|
||||
while self.tab.month == m do
|
||||
d = d + 1
|
||||
self:add{day=1}
|
||||
end
|
||||
self:add{day=-1}
|
||||
return self
|
||||
end
|
||||
|
||||
--- difference between two Date objects.
|
||||
-- @tparam Date other Date object
|
||||
-- @treturn Date.Interval object
|
||||
function Date:diff(other)
|
||||
local dt = self.time - other.time
|
||||
if dt < 0 then error("date difference is negative!",2) end
|
||||
return Date.Interval(dt)
|
||||
end
|
||||
|
||||
--- long numerical ISO data format version of this date.
|
||||
function Date:__tostring()
|
||||
local fmt = '%Y-%m-%dT%H:%M:%S'
|
||||
if self.utc then
|
||||
fmt = "!"..fmt
|
||||
end
|
||||
local t = os_date(fmt,self.time)
|
||||
if self.utc then
|
||||
return t .. 'Z'
|
||||
else
|
||||
local offs = self:tzone()
|
||||
if offs == 0 then
|
||||
return t .. 'Z'
|
||||
end
|
||||
local sign = offs > 0 and '+' or '-'
|
||||
local h = math.ceil(offs/3600)
|
||||
local m = (offs % 3600)/60
|
||||
if m == 0 then
|
||||
return t .. ('%s%02d'):format(sign,h)
|
||||
else
|
||||
return t .. ('%s%02d:%02d'):format(sign,h,m)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- equality between Date objects.
|
||||
function Date:__eq(other)
|
||||
return self.time == other.time
|
||||
end
|
||||
|
||||
--- ordering between Date objects.
|
||||
function Date:__lt(other)
|
||||
return self.time < other.time
|
||||
end
|
||||
|
||||
--- difference between Date objects.
|
||||
-- @function Date:__sub
|
||||
Date.__sub = Date.diff
|
||||
|
||||
--- add a date and an interval.
|
||||
-- @param other either a `Date.Interval` object or a table such as
|
||||
-- passed to `Date:add`
|
||||
function Date:__add(other)
|
||||
local nd = Date(self)
|
||||
if Date.Interval:class_of(other) then
|
||||
other = {sec=other.time}
|
||||
end
|
||||
nd:add(other)
|
||||
return nd
|
||||
end
|
||||
|
||||
Date.Interval = class(Date)
|
||||
|
||||
---- Date.Interval constructor
|
||||
-- @int t an interval in seconds
|
||||
-- @function Date.Interval
|
||||
function Date.Interval:_init(t)
|
||||
self:set(t)
|
||||
end
|
||||
|
||||
function Date.Interval:set(t)
|
||||
self.time = t
|
||||
self.tab = os_date('!*t',self.time)
|
||||
end
|
||||
|
||||
local function ess(n)
|
||||
if n > 1 then return 's '
|
||||
else return ' '
|
||||
end
|
||||
end
|
||||
|
||||
--- If it's an interval then the format is '2 hours 29 sec' etc.
|
||||
function Date.Interval:__tostring()
|
||||
local t, res = self.tab, ''
|
||||
local y,m,d = t.year - 1970, t.month - 1, t.day - 1
|
||||
if y > 0 then res = res .. y .. ' year'..ess(y) end
|
||||
if m > 0 then res = res .. m .. ' month'..ess(m) end
|
||||
if d > 0 then res = res .. d .. ' day'..ess(d) end
|
||||
if y == 0 and m == 0 then
|
||||
local h = t.hour
|
||||
if h > 0 then res = res .. h .. ' hour'..ess(h) end
|
||||
if t.min > 0 then res = res .. t.min .. ' min ' end
|
||||
if t.sec > 0 then res = res .. t.sec .. ' sec ' end
|
||||
end
|
||||
if res == '' then res = 'zero' end
|
||||
return res
|
||||
end
|
||||
|
||||
------------ Date.Format class: parsing and renderinig dates ------------
|
||||
|
||||
-- short field names, explicit os.date names, and a mask for allowed field repeats
|
||||
local formats = {
|
||||
d = {'day',{true,true}},
|
||||
y = {'year',{false,true,false,true}},
|
||||
m = {'month',{true,true}},
|
||||
H = {'hour',{true,true}},
|
||||
M = {'min',{true,true}},
|
||||
S = {'sec',{true,true}},
|
||||
}
|
||||
|
||||
--- Date.Format constructor.
|
||||
-- @string fmt. A string where the following fields are significant:
|
||||
--
|
||||
-- * d day (either d or dd)
|
||||
-- * y year (either yy or yyy)
|
||||
-- * m month (either m or mm)
|
||||
-- * H hour (either H or HH)
|
||||
-- * M minute (either M or MM)
|
||||
-- * S second (either S or SS)
|
||||
--
|
||||
-- Alternatively, if fmt is nil then this returns a flexible date parser
|
||||
-- that tries various date/time schemes in turn:
|
||||
--
|
||||
-- * [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601), like `2010-05-10 12:35:23Z` or `2008-10-03T14:30+02`
|
||||
-- * times like 15:30 or 8.05pm (assumed to be today's date)
|
||||
-- * dates like 28/10/02 (European order!) or 5 Feb 2012
|
||||
-- * month name like march or Mar (case-insensitive, first 3 letters); here the
|
||||
-- day will be 1 and the year this current year
|
||||
--
|
||||
-- A date in format 3 can be optionally followed by a time in format 2.
|
||||
-- Please see test-date.lua in the tests folder for more examples.
|
||||
-- @usage df = Date.Format("yyyy-mm-dd HH:MM:SS")
|
||||
-- @class function
|
||||
-- @name Date.Format
|
||||
function Date.Format:_init(fmt)
|
||||
if not fmt then
|
||||
self.fmt = '%Y-%m-%d %H:%M:%S'
|
||||
self.outf = self.fmt
|
||||
self.plain = true
|
||||
return
|
||||
end
|
||||
local append = table.insert
|
||||
local D,PLUS,OPENP,CLOSEP = '\001','\002','\003','\004'
|
||||
local vars,used = {},{}
|
||||
local patt,outf = {},{}
|
||||
local i = 1
|
||||
while i < #fmt do
|
||||
local ch = fmt:sub(i,i)
|
||||
local df = formats[ch]
|
||||
if df then
|
||||
if used[ch] then error("field appeared twice: "..ch,4) end
|
||||
used[ch] = true
|
||||
-- this field may be repeated
|
||||
local _,inext = fmt:find(ch..'+',i+1)
|
||||
local cnt = not _ and 1 or inext-i+1
|
||||
if not df[2][cnt] then error("wrong number of fields: "..ch,4) end
|
||||
-- single chars mean 'accept more than one digit'
|
||||
local p = cnt==1 and (D..PLUS) or (D):rep(cnt)
|
||||
append(patt,OPENP..p..CLOSEP)
|
||||
append(vars,ch)
|
||||
if ch == 'y' then
|
||||
append(outf,cnt==2 and '%y' or '%Y')
|
||||
else
|
||||
append(outf,'%'..ch)
|
||||
end
|
||||
i = i + cnt
|
||||
else
|
||||
append(patt,ch)
|
||||
append(outf,ch)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
-- escape any magic characters
|
||||
fmt = utils.escape(table.concat(patt))
|
||||
-- fmt = table.concat(patt):gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')
|
||||
-- replace markers with their magic equivalents
|
||||
fmt = fmt:gsub(D,'%%d'):gsub(PLUS,'+'):gsub(OPENP,'('):gsub(CLOSEP,')')
|
||||
self.fmt = fmt
|
||||
self.outf = table.concat(outf)
|
||||
self.vars = vars
|
||||
end
|
||||
|
||||
local parse_date
|
||||
|
||||
--- parse a string into a Date object.
|
||||
-- @string str a date string
|
||||
-- @return date object
|
||||
function Date.Format:parse(str)
|
||||
assert_string(1,str)
|
||||
if self.plain then
|
||||
return parse_date(str,self.us)
|
||||
end
|
||||
local res = {str:match(self.fmt)}
|
||||
if #res==0 then return nil, 'cannot parse '..str end
|
||||
local tab = {}
|
||||
for i,v in ipairs(self.vars) do
|
||||
local name = formats[v][1] -- e.g. 'y' becomes 'year'
|
||||
tab[name] = tonumber(res[i])
|
||||
end
|
||||
-- os.date() requires these fields; if not present, we assume
|
||||
-- that the time set is for the current day.
|
||||
if not (tab.year and tab.month and tab.day) then
|
||||
local today = Date()
|
||||
tab.year = tab.year or today:year()
|
||||
tab.month = tab.month or today:month()
|
||||
tab.day = tab.day or today:day()
|
||||
end
|
||||
local Y = tab.year
|
||||
if Y < 100 then -- classic Y2K pivot
|
||||
tab.year = Y + (Y < 35 and 2000 or 1999)
|
||||
elseif not Y then
|
||||
tab.year = 1970
|
||||
end
|
||||
return Date(tab)
|
||||
end
|
||||
|
||||
--- convert a Date object into a string.
|
||||
-- @param d a date object, or a time value as returned by @{os.time}
|
||||
-- @return string
|
||||
function Date.Format:tostring(d)
|
||||
local tm
|
||||
local fmt = self.outf
|
||||
if type(d) == 'number' then
|
||||
tm = d
|
||||
else
|
||||
tm = d.time
|
||||
if d.utc then
|
||||
fmt = '!'..fmt
|
||||
end
|
||||
end
|
||||
return os_date(fmt,tm)
|
||||
end
|
||||
|
||||
--- force US order in dates like 9/11/2001
|
||||
function Date.Format:US_order(yesno)
|
||||
self.us = yesno
|
||||
end
|
||||
|
||||
--local months = {jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12}
|
||||
local months
|
||||
local parse_date_unsafe
|
||||
local function create_months()
|
||||
local ld, day1 = parse_date_unsafe '2000-12-31', {day=1}
|
||||
months = {}
|
||||
for i = 1,12 do
|
||||
ld = ld:last_day()
|
||||
ld:add(day1)
|
||||
local mon = ld:month_name():lower()
|
||||
months [mon] = i
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Allowed patterns:
|
||||
- [day] [monthname] [year] [time]
|
||||
- [day]/[month][/year] [time]
|
||||
|
||||
]]
|
||||
|
||||
local function looks_like_a_month(w)
|
||||
return w:match '^%a+,*$' ~= nil
|
||||
end
|
||||
local is_number = stringx.isdigit
|
||||
local function tonum(s,l1,l2,kind)
|
||||
kind = kind or ''
|
||||
local n = tonumber(s)
|
||||
if not n then error(("%snot a number: '%s'"):format(kind,s)) end
|
||||
if n < l1 or n > l2 then
|
||||
error(("%s out of range: %s is not between %d and %d"):format(kind,s,l1,l2))
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
local function parse_iso_end(p,ns,sec)
|
||||
-- may be fractional part of seconds
|
||||
local _,nfrac,secfrac = p:find('^%.%d+',ns+1)
|
||||
if secfrac then
|
||||
sec = sec .. secfrac
|
||||
p = p:sub(nfrac+1)
|
||||
else
|
||||
p = p:sub(ns+1)
|
||||
end
|
||||
-- ISO 8601 dates may end in Z (for UTC) or [+-][isotime]
|
||||
-- (we're working with the date as lower case, hence 'z')
|
||||
if p:match 'z$' then -- we're UTC!
|
||||
return sec, {h=0,m=0}
|
||||
end
|
||||
p = p:gsub(':','') -- turn 00:30 to 0030
|
||||
local _,_,sign,offs = p:find('^([%+%-])(%d+)')
|
||||
if not sign then return sec, nil end -- not UTC
|
||||
|
||||
if #offs == 2 then offs = offs .. '00' end -- 01 to 0100
|
||||
local tz = { h = tonumber(offs:sub(1,2)), m = tonumber(offs:sub(3,4)) }
|
||||
if sign == '-' then tz.h = -tz.h; tz.m = -tz.m end
|
||||
return sec, tz
|
||||
end
|
||||
|
||||
function parse_date_unsafe (s,US)
|
||||
s = s:gsub('T',' ') -- ISO 8601
|
||||
local parts = stringx.split(s:lower())
|
||||
local i,p = 1,parts[1]
|
||||
local function nextp() i = i + 1; p = parts[i] end
|
||||
local year,min,hour,sec,apm
|
||||
local tz
|
||||
local _,nxt,day, month = p:find '^(%d+)/(%d+)'
|
||||
if day then
|
||||
-- swop for US case
|
||||
if US then
|
||||
day, month = month, day
|
||||
end
|
||||
_,_,year = p:find('^/(%d+)',nxt+1)
|
||||
nextp()
|
||||
else -- ISO
|
||||
year,month,day = p:match('^(%d+)%-(%d+)%-(%d+)')
|
||||
if year then
|
||||
nextp()
|
||||
end
|
||||
end
|
||||
if p and not year and is_number(p) then -- has to be date
|
||||
if #p < 4 then
|
||||
day = p
|
||||
nextp()
|
||||
else -- unless it looks like a 24-hour time
|
||||
year = true
|
||||
end
|
||||
end
|
||||
if p and looks_like_a_month(p) then -- date followed by month
|
||||
p = p:sub(1,3)
|
||||
if not months then
|
||||
create_months()
|
||||
end
|
||||
local mon = months[p]
|
||||
if mon then
|
||||
month = mon
|
||||
else error("not a month: " .. p) end
|
||||
nextp()
|
||||
end
|
||||
if p and not year and is_number(p) then
|
||||
year = p
|
||||
nextp()
|
||||
end
|
||||
|
||||
if p then -- time is hh:mm[:ss], hhmm[ss] or H.M[am|pm]
|
||||
_,nxt,hour,min = p:find '^(%d+):(%d+)'
|
||||
local ns
|
||||
if nxt then -- are there seconds?
|
||||
_,ns,sec = p:find ('^:(%d+)',nxt+1)
|
||||
--if ns then
|
||||
sec,tz = parse_iso_end(p,ns or nxt,sec)
|
||||
--end
|
||||
else -- might be h.m
|
||||
_,ns,hour,min = p:find '^(%d+)%.(%d+)'
|
||||
if ns then
|
||||
apm = p:match '[ap]m$'
|
||||
else -- or hhmm[ss]
|
||||
local hourmin
|
||||
_,nxt,hourmin = p:find ('^(%d+)')
|
||||
if nxt then
|
||||
hour = hourmin:sub(1,2)
|
||||
min = hourmin:sub(3,4)
|
||||
sec = hourmin:sub(5,6)
|
||||
if #sec == 0 then sec = nil end
|
||||
sec,tz = parse_iso_end(p,nxt,sec)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local today
|
||||
if year == true then year = nil end
|
||||
if not (year and month and day) then
|
||||
today = Date()
|
||||
end
|
||||
day = day and tonum(day,1,31,'day') or (month and 1 or today:day())
|
||||
month = month and tonum(month,1,12,'month') or today:month()
|
||||
year = year and tonumber(year) or today:year()
|
||||
if year < 100 then -- two-digit year pivot around year < 2035
|
||||
year = year + (year < 35 and 2000 or 1900)
|
||||
end
|
||||
hour = hour and tonum(hour,0,apm and 12 or 24,'hour') or 12
|
||||
if apm == 'pm' then
|
||||
hour = hour + 12
|
||||
end
|
||||
min = min and tonum(min,0,59) or 0
|
||||
sec = sec and tonum(sec,0,60) or 0 --60 used to indicate leap second
|
||||
local res = Date {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
|
||||
if tz then -- ISO 8601 UTC time
|
||||
local corrected = false
|
||||
if tz.h ~= 0 then res:add {hour = -tz.h}; corrected = true end
|
||||
if tz.m ~= 0 then res:add {min = -tz.m}; corrected = true end
|
||||
res.utc = true
|
||||
-- we're in UTC, so let's go local...
|
||||
if corrected then
|
||||
res = res:toLocal()
|
||||
end-- we're UTC!
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function parse_date (s)
|
||||
local ok, d = pcall(parse_date_unsafe,s)
|
||||
if not ok then -- error
|
||||
d = d:gsub('.-:%d+: ','')
|
||||
return nil, d
|
||||
else
|
||||
return d
|
||||
end
|
||||
end
|
||||
|
||||
return Date
|
||||
|
566
Documentation/compiler/pl/List.lua
Normal file
566
Documentation/compiler/pl/List.lua
Normal file
|
@ -0,0 +1,566 @@
|
|||
--- Python-style list class.
|
||||
--
|
||||
-- **Please Note**: methods that change the list will return the list.
|
||||
-- This is to allow for method chaining, but please note that `ls = ls:sort()`
|
||||
-- does not mean that a new copy of the list is made. In-place (mutable) methods
|
||||
-- are marked as returning 'the list' in this documentation.
|
||||
--
|
||||
-- See the Guide for further @{02-arrays.md.Python_style_Lists|discussion}
|
||||
--
|
||||
-- See <a href="http://www.python.org/doc/current/tut/tut.html">http://www.python.org/doc/current/tut/tut.html</a>, section 5.1
|
||||
--
|
||||
-- **Note**: The comments before some of the functions are from the Python docs
|
||||
-- and contain Python code.
|
||||
--
|
||||
-- Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`
|
||||
-- @classmod pl.List
|
||||
-- @pragma nostrip
|
||||
|
||||
local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort
|
||||
local setmetatable, getmetatable,type,tostring,string = setmetatable,getmetatable,type,tostring,string
|
||||
local tablex = require 'pl.tablex'
|
||||
local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues
|
||||
local tsub = tablex.sub
|
||||
local utils = require 'pl.utils'
|
||||
local class = require 'pl.class'
|
||||
|
||||
local array_tostring,split,assert_arg,function_arg = utils.array_tostring,utils.split,utils.assert_arg,utils.function_arg
|
||||
local normalize_slice = tablex._normalize_slice
|
||||
|
||||
-- metatable for our list and map objects has already been defined..
|
||||
local Multimap = utils.stdmt.MultiMap
|
||||
local List = utils.stdmt.List
|
||||
|
||||
local iter
|
||||
|
||||
class(nil,nil,List)
|
||||
|
||||
-- we want the result to be _covariant_, i.e. t must have type of obj if possible
|
||||
local function makelist (t,obj)
|
||||
local klass = List
|
||||
if obj then
|
||||
klass = getmetatable(obj)
|
||||
end
|
||||
return setmetatable(t,klass)
|
||||
end
|
||||
|
||||
local function simple_table(t)
|
||||
return type(t) == 'table' and not getmetatable(t) and #t > 0
|
||||
end
|
||||
|
||||
function List._create (src)
|
||||
if simple_table(src) then return src end
|
||||
end
|
||||
|
||||
function List:_init (src)
|
||||
if self == src then return end -- existing table used as self!
|
||||
if src then
|
||||
for v in iter(src) do
|
||||
tinsert(self,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a new list. Can optionally pass a table;
|
||||
-- passing another instance of List will cause a copy to be created;
|
||||
-- this will return a plain table with an appropriate metatable.
|
||||
-- we pass anything which isn't a simple table to iterate() to work out
|
||||
-- an appropriate iterator
|
||||
-- @see List.iterate
|
||||
-- @param[opt] t An optional list-like table
|
||||
-- @return a new List
|
||||
-- @usage ls = List(); ls = List {1,2,3,4}
|
||||
-- @function List.new
|
||||
|
||||
List.new = List
|
||||
|
||||
--- Make a copy of an existing list.
|
||||
-- The difference from a plain 'copy constructor' is that this returns
|
||||
-- the actual List subtype.
|
||||
function List:clone()
|
||||
local ls = makelist({},self)
|
||||
ls:extend(self)
|
||||
return ls
|
||||
end
|
||||
|
||||
--- Add an item to the end of the list.
|
||||
-- @param i An item
|
||||
-- @return the list
|
||||
function List:append(i)
|
||||
tinsert(self,i)
|
||||
return self
|
||||
end
|
||||
|
||||
List.push = tinsert
|
||||
|
||||
--- Extend the list by appending all the items in the given list.
|
||||
-- equivalent to 'a[len(a):] = L'.
|
||||
-- @tparam List L Another List
|
||||
-- @return the list
|
||||
function List:extend(L)
|
||||
assert_arg(1,L,'table')
|
||||
for i = 1,#L do tinsert(self,L[i]) end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Insert an item at a given position. i is the index of the
|
||||
-- element before which to insert.
|
||||
-- @int i index of element before whichh to insert
|
||||
-- @param x A data item
|
||||
-- @return the list
|
||||
function List:insert(i, x)
|
||||
assert_arg(1,i,'number')
|
||||
tinsert(self,i,x)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Insert an item at the begining of the list.
|
||||
-- @param x a data item
|
||||
-- @return the list
|
||||
function List:put (x)
|
||||
return self:insert(1,x)
|
||||
end
|
||||
|
||||
--- Remove an element given its index.
|
||||
-- (equivalent of Python's del s[i])
|
||||
-- @int i the index
|
||||
-- @return the list
|
||||
function List:remove (i)
|
||||
assert_arg(1,i,'number')
|
||||
tremove(self,i)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove the first item from the list whose value is given.
|
||||
-- (This is called 'remove' in Python; renamed to avoid confusion
|
||||
-- with table.remove)
|
||||
-- Return nil if there is no such item.
|
||||
-- @param x A data value
|
||||
-- @return the list
|
||||
function List:remove_value(x)
|
||||
for i=1,#self do
|
||||
if self[i]==x then tremove(self,i) return self end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove the item at the given position in the list, and return it.
|
||||
-- If no index is specified, a:pop() returns the last item in the list.
|
||||
-- The item is also removed from the list.
|
||||
-- @int[opt] i An index
|
||||
-- @return the item
|
||||
function List:pop(i)
|
||||
if not i then i = #self end
|
||||
assert_arg(1,i,'number')
|
||||
return tremove(self,i)
|
||||
end
|
||||
|
||||
List.get = List.pop
|
||||
|
||||
--- Return the index in the list of the first item whose value is given.
|
||||
-- Return nil if there is no such item.
|
||||
-- @function List:index
|
||||
-- @param x A data value
|
||||
-- @int[opt=1] idx where to start search
|
||||
-- @return the index, or nil if not found.
|
||||
|
||||
local tfind = tablex.find
|
||||
List.index = tfind
|
||||
|
||||
--- Does this list contain the value?
|
||||
-- @param x A data value
|
||||
-- @return true or false
|
||||
function List:contains(x)
|
||||
return tfind(self,x) and true or false
|
||||
end
|
||||
|
||||
--- Return the number of times value appears in the list.
|
||||
-- @param x A data value
|
||||
-- @return number of times x appears
|
||||
function List:count(x)
|
||||
local cnt=0
|
||||
for i=1,#self do
|
||||
if self[i]==x then cnt=cnt+1 end
|
||||
end
|
||||
return cnt
|
||||
end
|
||||
|
||||
--- Sort the items of the list, in place.
|
||||
-- @func[opt='<'] cmp an optional comparison function
|
||||
-- @return the list
|
||||
function List:sort(cmp)
|
||||
if cmp then cmp = function_arg(1,cmp) end
|
||||
tsort(self,cmp)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return a sorted copy of this list.
|
||||
-- @func[opt='<'] cmp an optional comparison function
|
||||
-- @return a new list
|
||||
function List:sorted(cmp)
|
||||
return List(self):sort(cmp)
|
||||
end
|
||||
|
||||
--- Reverse the elements of the list, in place.
|
||||
-- @return the list
|
||||
function List:reverse()
|
||||
local t = self
|
||||
local n = #t
|
||||
for i = 1,n/2 do
|
||||
t[i],t[n] = t[n],t[i]
|
||||
n = n - 1
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return the minimum and the maximum value of the list.
|
||||
-- @return minimum value
|
||||
-- @return maximum value
|
||||
function List:minmax()
|
||||
local vmin,vmax = 1e70,-1e70
|
||||
for i = 1,#self do
|
||||
local v = self[i]
|
||||
if v < vmin then vmin = v end
|
||||
if v > vmax then vmax = v end
|
||||
end
|
||||
return vmin,vmax
|
||||
end
|
||||
|
||||
--- Emulate list slicing. like 'list[first:last]' in Python.
|
||||
-- If first or last are negative then they are relative to the end of the list
|
||||
-- eg. slice(-2) gives last 2 entries in a list, and
|
||||
-- slice(-4,-2) gives from -4th to -2nd
|
||||
-- @param first An index
|
||||
-- @param last An index
|
||||
-- @return a new List
|
||||
function List:slice(first,last)
|
||||
return tsub(self,first,last)
|
||||
end
|
||||
|
||||
--- Empty the list.
|
||||
-- @return the list
|
||||
function List:clear()
|
||||
for i=1,#self do tremove(self) end
|
||||
return self
|
||||
end
|
||||
|
||||
local eps = 1.0e-10
|
||||
|
||||
--- Emulate Python's range(x) function.
|
||||
-- Include it in List table for tidiness
|
||||
-- @int start A number
|
||||
-- @int[opt] finish A number greater than start; if absent,
|
||||
-- then start is 1 and finish is start
|
||||
-- @int[opt=1] incr an increment (may be less than 1)
|
||||
-- @return a List from start .. finish
|
||||
-- @usage List.range(0,3) == List{0,1,2,3}
|
||||
-- @usage List.range(4) = List{1,2,3,4}
|
||||
-- @usage List.range(5,1,-1) == List{5,4,3,2,1}
|
||||
function List.range(start,finish,incr)
|
||||
if not finish then
|
||||
finish = start
|
||||
start = 1
|
||||
end
|
||||
if incr then
|
||||
assert_arg(3,incr,'number')
|
||||
if math.ceil(incr) ~= incr then finish = finish + eps end
|
||||
else
|
||||
incr = 1
|
||||
end
|
||||
assert_arg(1,start,'number')
|
||||
assert_arg(2,finish,'number')
|
||||
local t = List()
|
||||
for i=start,finish,incr do tinsert(t,i) end
|
||||
return t
|
||||
end
|
||||
|
||||
--- list:len() is the same as #list.
|
||||
function List:len()
|
||||
return #self
|
||||
end
|
||||
|
||||
-- Extended operations --
|
||||
|
||||
--- Remove a subrange of elements.
|
||||
-- equivalent to 'del s[i1:i2]' in Python.
|
||||
-- @int i1 start of range
|
||||
-- @int i2 end of range
|
||||
-- @return the list
|
||||
function List:chop(i1,i2)
|
||||
return tremovevalues(self,i1,i2)
|
||||
end
|
||||
|
||||
--- Insert a sublist into a list
|
||||
-- equivalent to 's[idx:idx] = list' in Python
|
||||
-- @int idx index
|
||||
-- @tparam List list list to insert
|
||||
-- @return the list
|
||||
-- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20})
|
||||
function List:splice(idx,list)
|
||||
assert_arg(1,idx,'number')
|
||||
idx = idx - 1
|
||||
local i = 1
|
||||
for v in iter(list) do
|
||||
tinsert(self,i+idx,v)
|
||||
i = i + 1
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- General slice assignment s[i1:i2] = seq.
|
||||
-- @int i1 start index
|
||||
-- @int i2 end index
|
||||
-- @tparam List seq a list
|
||||
-- @return the list
|
||||
function List:slice_assign(i1,i2,seq)
|
||||
assert_arg(1,i1,'number')
|
||||
assert_arg(1,i2,'number')
|
||||
i1,i2 = normalize_slice(self,i1,i2)
|
||||
if i2 >= i1 then self:chop(i1,i2) end
|
||||
self:splice(i1,seq)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Concatenation operator.
|
||||
-- @within metamethods
|
||||
-- @tparam List L another List
|
||||
-- @return a new list consisting of the list with the elements of the new list appended
|
||||
function List:__concat(L)
|
||||
assert_arg(1,L,'table')
|
||||
local ls = self:clone()
|
||||
ls:extend(L)
|
||||
return ls
|
||||
end
|
||||
|
||||
--- Equality operator ==. True iff all elements of two lists are equal.
|
||||
-- @within metamethods
|
||||
-- @tparam List L another List
|
||||
-- @return true or false
|
||||
function List:__eq(L)
|
||||
if #self ~= #L then return false end
|
||||
for i = 1,#self do
|
||||
if self[i] ~= L[i] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Join the elements of a list using a delimiter.
|
||||
-- This method uses tostring on all elements.
|
||||
-- @string[opt=''] delim a delimiter string, can be empty.
|
||||
-- @return a string
|
||||
function List:join (delim)
|
||||
delim = delim or ''
|
||||
assert_arg(1,delim,'string')
|
||||
return concat(array_tostring(self),delim)
|
||||
end
|
||||
|
||||
--- Join a list of strings. <br>
|
||||
-- Uses `table.concat` directly.
|
||||
-- @function List:concat
|
||||
-- @string[opt=''] delim a delimiter
|
||||
-- @return a string
|
||||
List.concat = concat
|
||||
|
||||
local function tostring_q(val)
|
||||
local s = tostring(val)
|
||||
if type(val) == 'string' then
|
||||
s = '"'..s..'"'
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
--- How our list should be rendered as a string. Uses join().
|
||||
-- @within metamethods
|
||||
-- @see List:join
|
||||
function List:__tostring()
|
||||
return '{'..self:join(',',tostring_q)..'}'
|
||||
end
|
||||
|
||||
--- Call the function on each element of the list.
|
||||
-- @func fun a function or callable object
|
||||
-- @param ... optional values to pass to function
|
||||
function List:foreach (fun,...)
|
||||
fun = function_arg(1,fun)
|
||||
for i = 1,#self do
|
||||
fun(self[i],...)
|
||||
end
|
||||
end
|
||||
|
||||
local function lookup_fun (obj,name)
|
||||
local f = obj[name]
|
||||
if not f then error(type(obj).." does not have method "..name,3) end
|
||||
return f
|
||||
end
|
||||
|
||||
--- Call the named method on each element of the list.
|
||||
-- @string name the method name
|
||||
-- @param ... optional values to pass to function
|
||||
function List:foreachm (name,...)
|
||||
for i = 1,#self do
|
||||
local obj = self[i]
|
||||
local f = lookup_fun(obj,name)
|
||||
f(obj,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a list of all elements which match a function.
|
||||
-- @func fun a boolean function
|
||||
-- @param[opt] arg optional argument to be passed as second argument of the predicate
|
||||
-- @return a new filtered list.
|
||||
function List:filter (fun,arg)
|
||||
return makelist(filter(self,fun,arg),self)
|
||||
end
|
||||
|
||||
--- Split a string using a delimiter.
|
||||
-- @string s the string
|
||||
-- @string[opt] delim the delimiter (default spaces)
|
||||
-- @return a List of strings
|
||||
-- @see pl.utils.split
|
||||
function List.split (s,delim)
|
||||
assert_arg(1,s,'string')
|
||||
return makelist(split(s,delim))
|
||||
end
|
||||
|
||||
--- Apply a function to all elements.
|
||||
-- Any extra arguments will be passed to the function.
|
||||
-- @func fun a function of at least one argument
|
||||
-- @param ... arbitrary extra arguments.
|
||||
-- @return a new list: {f(x) for x in self}
|
||||
-- @usage List{'one','two'}:map(string.upper) == {'ONE','TWO'}
|
||||
-- @see pl.tablex.imap
|
||||
function List:map (fun,...)
|
||||
return makelist(imap(fun,self,...),self)
|
||||
end
|
||||
|
||||
--- Apply a function to all elements, in-place.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @param ... arbitrary extra arguments.
|
||||
-- @return the list.
|
||||
function List:transform (fun,...)
|
||||
transform(fun,self,...)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Apply a function to elements of two lists.
|
||||
-- Any extra arguments will be passed to the function
|
||||
-- @func fun a function of at least two arguments
|
||||
-- @tparam List ls another list
|
||||
-- @param ... arbitrary extra arguments.
|
||||
-- @return a new list: {f(x,y) for x in self, for x in arg1}
|
||||
-- @see pl.tablex.imap2
|
||||
function List:map2 (fun,ls,...)
|
||||
return makelist(imap2(fun,self,ls,...),self)
|
||||
end
|
||||
|
||||
--- apply a named method to all elements.
|
||||
-- Any extra arguments will be passed to the method.
|
||||
-- @string name name of method
|
||||
-- @param ... extra arguments
|
||||
-- @return a new list of the results
|
||||
-- @see pl.seq.mapmethod
|
||||
function List:mapm (name,...)
|
||||
local res = {}
|
||||
for i = 1,#self do
|
||||
local val = self[i]
|
||||
local fn = lookup_fun(val,name)
|
||||
res[i] = fn(val,...)
|
||||
end
|
||||
return makelist(res,self)
|
||||
end
|
||||
|
||||
local function composite_call (method,f)
|
||||
return function(self,...)
|
||||
return self[method](self,f,...)
|
||||
end
|
||||
end
|
||||
|
||||
function List.default_map_with(T)
|
||||
return function(self,name)
|
||||
local m
|
||||
if T then
|
||||
local f = lookup_fun(T,name)
|
||||
m = composite_call('map',f)
|
||||
else
|
||||
m = composite_call('mapn',name)
|
||||
end
|
||||
getmetatable(self)[name] = m -- and cache..
|
||||
return m
|
||||
end
|
||||
end
|
||||
|
||||
List.default_map = List.default_map_with
|
||||
|
||||
--- 'reduce' a list using a binary function.
|
||||
-- @func fun a function of two arguments
|
||||
-- @return result of the function
|
||||
-- @see pl.tablex.reduce
|
||||
function List:reduce (fun)
|
||||
return reduce(fun,self)
|
||||
end
|
||||
|
||||
--- Partition a list using a classifier function.
|
||||
-- The function may return nil, but this will be converted to the string key '<nil>'.
|
||||
-- @func fun a function of at least one argument
|
||||
-- @param ... will also be passed to the function
|
||||
-- @treturn MultiMap a table where the keys are the returned values, and the values are Lists
|
||||
-- of values where the function returned that key.
|
||||
-- @see pl.MultiMap
|
||||
function List:partition (fun,...)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for i = 1,#self do
|
||||
local val = self[i]
|
||||
local klass = fun(val,...)
|
||||
if klass == nil then klass = '<nil>' end
|
||||
if not res[klass] then res[klass] = List() end
|
||||
res[klass]:append(val)
|
||||
end
|
||||
return setmetatable(res,Multimap)
|
||||
end
|
||||
|
||||
--- return an iterator over all values.
|
||||
function List:iter ()
|
||||
return iter(self)
|
||||
end
|
||||
|
||||
--- Create an iterator over a seqence.
|
||||
-- This captures the Python concept of 'sequence'.
|
||||
-- For tables, iterates over all values with integer indices.
|
||||
-- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function
|
||||
-- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55
|
||||
-- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p
|
||||
function List.iterate(seq)
|
||||
if type(seq) == 'string' then
|
||||
local idx = 0
|
||||
local n = #seq
|
||||
local sub = string.sub
|
||||
return function ()
|
||||
idx = idx + 1
|
||||
if idx > n then return nil
|
||||
else
|
||||
return sub(seq,idx,idx)
|
||||
end
|
||||
end
|
||||
elseif type(seq) == 'table' then
|
||||
local idx = 0
|
||||
local n = #seq
|
||||
return function()
|
||||
idx = idx + 1
|
||||
if idx > n then return nil
|
||||
else
|
||||
return seq[idx]
|
||||
end
|
||||
end
|
||||
elseif type(seq) == 'function' then
|
||||
return seq
|
||||
elseif type(seq) == 'userdata' and io.type(seq) == 'file' then
|
||||
return seq:lines()
|
||||
end
|
||||
end
|
||||
iter = List.iterate
|
||||
|
||||
return List
|
||||
|
120
Documentation/compiler/pl/Map.lua
Normal file
120
Documentation/compiler/pl/Map.lua
Normal file
|
@ -0,0 +1,120 @@
|
|||
--- A Map class.
|
||||
--
|
||||
-- > Map = require 'pl.Map'
|
||||
-- > m = Map{one=1,two=2}
|
||||
-- > m:update {three=3,four=4,two=20}
|
||||
-- > = m == M{one=1,two=20,three=3,four=4}
|
||||
-- true
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.class`, `pl.tablex`, `pl.pretty`
|
||||
-- @classmod pl.Map
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local stdmt = utils.stdmt
|
||||
local deepcompare = tablex.deepcompare
|
||||
|
||||
local pretty_write = require 'pl.pretty' . write
|
||||
local Map = stdmt.Map
|
||||
local Set = stdmt.Set
|
||||
|
||||
local class = require 'pl.class'
|
||||
|
||||
-- the Map class ---------------------
|
||||
class(nil,nil,Map)
|
||||
|
||||
function Map:_init (t)
|
||||
local mt = getmetatable(t)
|
||||
if mt == Set or mt == Map then
|
||||
self:update(t)
|
||||
else
|
||||
return t -- otherwise assumed to be a map-like table
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function makelist(t)
|
||||
return setmetatable(t, require('pl.List'))
|
||||
end
|
||||
|
||||
--- list of keys.
|
||||
Map.keys = tablex.keys
|
||||
|
||||
--- list of values.
|
||||
Map.values = tablex.values
|
||||
|
||||
--- return an iterator over all key-value pairs.
|
||||
function Map:iter ()
|
||||
return pairs(self)
|
||||
end
|
||||
|
||||
--- return a List of all key-value pairs, sorted by the keys.
|
||||
function Map:items()
|
||||
local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self))
|
||||
ls:sort(function(t1,t2) return t1[1] < t2[1] end)
|
||||
return ls
|
||||
end
|
||||
|
||||
--- set a value in the map if it doesn't exist yet.
|
||||
-- @param key the key
|
||||
-- @param default value to set
|
||||
-- @return the value stored in the map (existing value, or the new value)
|
||||
function Map:setdefault(key, default)
|
||||
local val = self[key]
|
||||
if val ~= nil then
|
||||
return val
|
||||
end
|
||||
self:set(key,default)
|
||||
return default
|
||||
end
|
||||
|
||||
--- size of map.
|
||||
-- note: this is a relatively expensive operation!
|
||||
-- @class function
|
||||
-- @name Map:len
|
||||
Map.len = tablex.size
|
||||
|
||||
--- put a value into the map.
|
||||
-- This will remove the key if the value is `nil`
|
||||
-- @param key the key
|
||||
-- @param val the value
|
||||
function Map:set (key,val)
|
||||
self[key] = val
|
||||
end
|
||||
|
||||
--- get a value from the map.
|
||||
-- @param key the key
|
||||
-- @return the value, or nil if not found.
|
||||
function Map:get (key)
|
||||
return rawget(self,key)
|
||||
end
|
||||
|
||||
local index_by = tablex.index_by
|
||||
|
||||
--- get a list of values indexed by a list of keys.
|
||||
-- @param keys a list-like table of keys
|
||||
-- @return a new list
|
||||
function Map:getvalues (keys)
|
||||
return makelist(index_by(self,keys))
|
||||
end
|
||||
|
||||
--- update the map using key/value pairs from another table.
|
||||
-- @tab table
|
||||
-- @function Map:update
|
||||
Map.update = tablex.update
|
||||
|
||||
--- equality between maps.
|
||||
-- @within metamethods
|
||||
-- @tparam Map m another map.
|
||||
function Map:__eq (m)
|
||||
-- note we explicitly ask deepcompare _not_ to use __eq!
|
||||
return deepcompare(self,m,true)
|
||||
end
|
||||
|
||||
--- string representation of a map.
|
||||
-- @within metamethods
|
||||
function Map:__tostring ()
|
||||
return pretty_write(self,'')
|
||||
end
|
||||
|
||||
return Map
|
54
Documentation/compiler/pl/MultiMap.lua
Normal file
54
Documentation/compiler/pl/MultiMap.lua
Normal file
|
@ -0,0 +1,54 @@
|
|||
--- MultiMap, a Map which has multiple values per key.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.class`, `pl.List`, `pl.Map`
|
||||
-- @classmod pl.MultiMap
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local class = require 'pl.class'
|
||||
local List = require 'pl.List'
|
||||
local Map = require 'pl.Map'
|
||||
|
||||
-- MultiMap is a standard MT
|
||||
local MultiMap = utils.stdmt.MultiMap
|
||||
|
||||
class(Map,nil,MultiMap)
|
||||
MultiMap._name = 'MultiMap'
|
||||
|
||||
function MultiMap:_init (t)
|
||||
if not t then return end
|
||||
self:update(t)
|
||||
end
|
||||
|
||||
--- update a MultiMap using a table.
|
||||
-- @param t either a Multimap or a map-like table.
|
||||
-- @return the map
|
||||
function MultiMap:update (t)
|
||||
utils.assert_arg(1,t,'table')
|
||||
if Map:class_of(t) then
|
||||
for k,v in pairs(t) do
|
||||
self[k] = List()
|
||||
self[k]:append(v)
|
||||
end
|
||||
else
|
||||
for k,v in pairs(t) do
|
||||
self[k] = List(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- add a new value to a key. Setting a nil value removes the key.
|
||||
-- @param key the key
|
||||
-- @param val the value
|
||||
-- @return the map
|
||||
function MultiMap:set (key,val)
|
||||
if val == nil then
|
||||
self[key] = nil
|
||||
else
|
||||
if not self[key] then
|
||||
self[key] = List()
|
||||
end
|
||||
self[key]:append(val)
|
||||
end
|
||||
end
|
||||
|
||||
return MultiMap
|
167
Documentation/compiler/pl/OrderedMap.lua
Normal file
167
Documentation/compiler/pl/OrderedMap.lua
Normal file
|
@ -0,0 +1,167 @@
|
|||
--- OrderedMap, a map which preserves ordering.
|
||||
--
|
||||
-- Derived from `pl.Map`.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.List`, `pl.Map`
|
||||
-- @classmod pl.OrderedMap
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local List = require 'pl.List'
|
||||
local index_by,tsort,concat = tablex.index_by,table.sort,table.concat
|
||||
|
||||
local class = require 'pl.class'
|
||||
local Map = require 'pl.Map'
|
||||
|
||||
local OrderedMap = class(Map)
|
||||
OrderedMap._name = 'OrderedMap'
|
||||
|
||||
local rawset = rawset
|
||||
|
||||
--- construct an OrderedMap.
|
||||
-- Will throw an error if the argument is bad.
|
||||
-- @param t optional initialization table, same as for @{OrderedMap:update}
|
||||
function OrderedMap:_init (t)
|
||||
rawset(self,'_keys',List())
|
||||
if t then
|
||||
local map,err = self:update(t)
|
||||
if not map then error(err,2) end
|
||||
end
|
||||
end
|
||||
|
||||
local assert_arg,raise = utils.assert_arg,utils.raise
|
||||
|
||||
--- update an OrderedMap using a table.
|
||||
-- If the table is itself an OrderedMap, then its entries will be appended.
|
||||
-- if it s a table of the form `{{key1=val1},{key2=val2},...}` these will be appended.
|
||||
--
|
||||
-- Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary.
|
||||
-- @tab t a table.
|
||||
-- @return the map, or nil in case of error
|
||||
-- @return the error message
|
||||
function OrderedMap:update (t)
|
||||
assert_arg(1,t,'table')
|
||||
if OrderedMap:class_of(t) then
|
||||
for k,v in t:iter() do
|
||||
self:set(k,v)
|
||||
end
|
||||
elseif #t > 0 then -- an array must contain {key=val} tables
|
||||
if type(t[1]) == 'table' then
|
||||
for _,pair in ipairs(t) do
|
||||
local key,value = next(pair)
|
||||
if not key then return raise 'empty pair initialization table' end
|
||||
self:set(key,value)
|
||||
end
|
||||
else
|
||||
return raise 'cannot use an array to initialize an OrderedMap'
|
||||
end
|
||||
else
|
||||
for k,v in pairs(t) do
|
||||
self:set(k,v)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- set the key's value. This key will be appended at the end of the map.
|
||||
--
|
||||
-- If the value is nil, then the key is removed.
|
||||
-- @param key the key
|
||||
-- @param val the value
|
||||
-- @return the map
|
||||
function OrderedMap:set (key,val)
|
||||
if rawget(self, key) == nil and val ~= nil then -- new key
|
||||
self._keys:append(key) -- we keep in order
|
||||
rawset(self,key,val) -- don't want to provoke __newindex!
|
||||
else -- existing key-value pair
|
||||
if val == nil then
|
||||
self._keys:remove_value(key)
|
||||
rawset(self,key,nil)
|
||||
else
|
||||
self[key] = val
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
OrderedMap.__newindex = OrderedMap.set
|
||||
|
||||
--- insert a key/value pair before a given position.
|
||||
-- Note: if the map already contains the key, then this effectively
|
||||
-- moves the item to the new position by first removing at the old position.
|
||||
-- Has no effect if the key does not exist and val is nil
|
||||
-- @int pos a position starting at 1
|
||||
-- @param key the key
|
||||
-- @param val the value; if nil use the old value
|
||||
function OrderedMap:insert (pos,key,val)
|
||||
local oldval = self[key]
|
||||
val = val or oldval
|
||||
if oldval then
|
||||
self._keys:remove_value(key)
|
||||
end
|
||||
if val then
|
||||
self._keys:insert(pos,key)
|
||||
rawset(self,key,val)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- return the keys in order.
|
||||
-- (Not a copy!)
|
||||
-- @return List
|
||||
function OrderedMap:keys ()
|
||||
return self._keys
|
||||
end
|
||||
|
||||
--- return the values in order.
|
||||
-- this is relatively expensive.
|
||||
-- @return List
|
||||
function OrderedMap:values ()
|
||||
return List(index_by(self,self._keys))
|
||||
end
|
||||
|
||||
--- sort the keys.
|
||||
-- @func cmp a comparison function as for @{table.sort}
|
||||
-- @return the map
|
||||
function OrderedMap:sort (cmp)
|
||||
tsort(self._keys,cmp)
|
||||
return self
|
||||
end
|
||||
|
||||
--- iterate over key-value pairs in order.
|
||||
function OrderedMap:iter ()
|
||||
local i = 0
|
||||
local keys = self._keys
|
||||
local idx
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > #keys then return nil end
|
||||
idx = keys[i]
|
||||
return idx,self[idx]
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over an ordered map (5.2).
|
||||
-- @within metamethods
|
||||
-- @function OrderedMap:__pairs
|
||||
OrderedMap.__pairs = OrderedMap.iter
|
||||
|
||||
--- string representation of an ordered map.
|
||||
-- @within metamethods
|
||||
function OrderedMap:__tostring ()
|
||||
local res = {}
|
||||
for i,v in ipairs(self._keys) do
|
||||
local val = self[v]
|
||||
local vs = tostring(val)
|
||||
if type(val) ~= 'number' then
|
||||
vs = '"'..vs..'"'
|
||||
end
|
||||
res[i] = tostring(v)..'='..vs
|
||||
end
|
||||
return '{'..concat(res,',')..'}'
|
||||
end
|
||||
|
||||
return OrderedMap
|
||||
|
||||
|
||||
|
222
Documentation/compiler/pl/Set.lua
Normal file
222
Documentation/compiler/pl/Set.lua
Normal file
|
@ -0,0 +1,222 @@
|
|||
--- A Set class.
|
||||
--
|
||||
-- > Set = require 'pl.Set'
|
||||
-- > = Set{'one','two'} == Set{'two','one'}
|
||||
-- true
|
||||
-- > fruit = Set{'apple','banana','orange'}
|
||||
-- > = fruit['banana']
|
||||
-- true
|
||||
-- > = fruit['hazelnut']
|
||||
-- nil
|
||||
-- > colours = Set{'red','orange','green','blue'}
|
||||
-- > = fruit,colours
|
||||
-- [apple,orange,banana] [blue,green,orange,red]
|
||||
-- > = fruit+colours
|
||||
-- [blue,green,apple,red,orange,banana]
|
||||
-- [orange]
|
||||
-- > more_fruits = fruit + 'apricot'
|
||||
-- > = fruit*colours
|
||||
-- > = more_fruits, fruit
|
||||
-- [banana,apricot,apple,orange] [banana,apple,orange]
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.Map`, (`pl.List` if __tostring is used)
|
||||
-- @classmod pl.Set
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local array_tostring, concat = utils.array_tostring, table.concat
|
||||
local merge,difference = tablex.merge,tablex.difference
|
||||
local Map = require 'pl.Map'
|
||||
local class = require 'pl.class'
|
||||
local stdmt = utils.stdmt
|
||||
local Set = stdmt.Set
|
||||
|
||||
-- the Set class --------------------
|
||||
class(Map,nil,Set)
|
||||
|
||||
-- note: Set has _no_ methods!
|
||||
Set.__index = nil
|
||||
|
||||
local function makeset (t)
|
||||
return setmetatable(t,Set)
|
||||
end
|
||||
|
||||
--- create a set. <br>
|
||||
-- @param t may be a Set, Map or list-like table.
|
||||
-- @class function
|
||||
-- @name Set
|
||||
function Set:_init (t)
|
||||
t = t or {}
|
||||
local mt = getmetatable(t)
|
||||
if mt == Set or mt == Map then
|
||||
for k in pairs(t) do self[k] = true end
|
||||
else
|
||||
for _,v in ipairs(t) do self[v] = true end
|
||||
end
|
||||
end
|
||||
|
||||
--- string representation of a set.
|
||||
-- @within metamethods
|
||||
function Set:__tostring ()
|
||||
return '['..concat(array_tostring(Set.values(self)),',')..']'
|
||||
end
|
||||
|
||||
--- get a list of the values in a set.
|
||||
-- @param self a Set
|
||||
-- @function Set.values
|
||||
-- @return a list
|
||||
Set.values = Map.keys
|
||||
|
||||
--- map a function over the values of a set.
|
||||
-- @param self a Set
|
||||
-- @param fn a function
|
||||
-- @param ... extra arguments to pass to the function.
|
||||
-- @return a new set
|
||||
function Set.map (self,fn,...)
|
||||
fn = utils.function_arg(1,fn)
|
||||
local res = {}
|
||||
for k in pairs(self) do
|
||||
res[fn(k,...)] = true
|
||||
end
|
||||
return makeset(res)
|
||||
end
|
||||
|
||||
--- union of two sets (also +).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
function Set.union (self,set)
|
||||
return merge(self,set,true)
|
||||
end
|
||||
|
||||
--- modifies '+' operator to allow addition of non-Set elements
|
||||
--- Preserves +/- semantics - does not modify first argument.
|
||||
local function setadd(self,other)
|
||||
local mt = getmetatable(other)
|
||||
if mt == Set or mt == Map then
|
||||
return Set.union(self,other)
|
||||
else
|
||||
local new = Set(self)
|
||||
new[other] = true
|
||||
return new
|
||||
end
|
||||
end
|
||||
|
||||
--- union of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__add
|
||||
|
||||
Set.__add = setadd
|
||||
|
||||
--- intersection of two sets (also *).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
-- @usage
|
||||
-- > s = Set{10,20,30}
|
||||
-- > t = Set{20,30,40}
|
||||
-- > = t
|
||||
-- [20,30,40]
|
||||
-- > = Set.intersection(s,t)
|
||||
-- [30,20]
|
||||
-- > = s*t
|
||||
-- [30,20]
|
||||
|
||||
function Set.intersection (self,set)
|
||||
return merge(self,set,false)
|
||||
end
|
||||
|
||||
--- intersection of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__mul
|
||||
Set.__mul = Set.intersection
|
||||
|
||||
--- new set with elements in the set that are not in the other (also -).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
function Set.difference (self,set)
|
||||
return difference(self,set,false)
|
||||
end
|
||||
|
||||
--- modifies "-" operator to remove non-Set values from set.
|
||||
--- Preserves +/- semantics - does not modify first argument.
|
||||
local function setminus (self,other)
|
||||
local mt = getmetatable(other)
|
||||
if mt == Set or mt == Map then
|
||||
return Set.difference(self,other)
|
||||
else
|
||||
local new = Set(self)
|
||||
new[other] = nil
|
||||
return new
|
||||
end
|
||||
end
|
||||
|
||||
--- difference of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__sub
|
||||
Set.__sub = setminus
|
||||
|
||||
-- a new set with elements in _either_ the set _or_ other but not both (also ^).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
function Set.symmetric_difference (self,set)
|
||||
return difference(self,set,true)
|
||||
end
|
||||
|
||||
--- symmetric difference of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__pow
|
||||
Set.__pow = Set.symmetric_difference
|
||||
|
||||
--- is the first set a subset of the second (also <)?.
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return true or false
|
||||
function Set.issubset (self,set)
|
||||
for k in pairs(self) do
|
||||
if not set[k] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- first set subset of second?
|
||||
-- @within metamethods
|
||||
-- @function Set.__lt
|
||||
Set.__lt = Set.issubset
|
||||
|
||||
--- is the set empty?.
|
||||
-- @param self a Set
|
||||
-- @return true or false
|
||||
function Set.isempty (self)
|
||||
return next(self) == nil
|
||||
end
|
||||
|
||||
--- are the sets disjoint? (no elements in common).
|
||||
-- Uses naive definition, i.e. that intersection is empty
|
||||
-- @param s1 a Set
|
||||
-- @param s2 another set
|
||||
-- @return true or false
|
||||
function Set.isdisjoint (s1,s2)
|
||||
return Set.isempty(Set.intersection(s1,s2))
|
||||
end
|
||||
|
||||
--- size of this set (also # for 5.2).
|
||||
-- @param s a Set
|
||||
-- @return size
|
||||
-- @function Set.len
|
||||
Set.len = tablex.size
|
||||
|
||||
--- cardinality of set (5.2).
|
||||
-- @within metamethods
|
||||
-- @function Set.__len
|
||||
Set.__len = Set.len
|
||||
|
||||
--- equality between sets.
|
||||
-- @within metamethods
|
||||
function Set.__eq (s1,s2)
|
||||
return Set.issubset(s1,s2) and Set.issubset(s2,s1)
|
||||
end
|
||||
|
||||
return Set
|
309
Documentation/compiler/pl/app.lua
Normal file
309
Documentation/compiler/pl/app.lua
Normal file
|
@ -0,0 +1,309 @@
|
|||
--- Application support functions.
|
||||
-- See @{01-introduction.md.Application_Support|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.path`
|
||||
-- @module pl.app
|
||||
|
||||
local io,package,require = _G.io, _G.package, _G.require
|
||||
local utils = require 'pl.utils'
|
||||
local path = require 'pl.path'
|
||||
|
||||
local app = {}
|
||||
|
||||
--- return the name of the current script running.
|
||||
-- The name will be the name as passed on the command line
|
||||
-- @return string filename
|
||||
function app.script_name()
|
||||
if _G.arg and _G.arg[0] then
|
||||
return _G.arg[0]
|
||||
end
|
||||
return utils.raise("No script name found")
|
||||
end
|
||||
|
||||
--- prefixes the current script's path to the Lua module path.
|
||||
-- Applies to both the source and the binary module paths. It makes it easy for
|
||||
-- the main file of a multi-file program to access its modules in the same directory.
|
||||
-- `base` allows these modules to be put in a specified subdirectory, to allow for
|
||||
-- cleaner deployment and resolve potential conflicts between a script name and its
|
||||
-- library directory.
|
||||
--
|
||||
-- Note: the path is prefixed, so it is searched first when requiring modules.
|
||||
-- @string base optional base directory (absolute, or relative path).
|
||||
-- @bool nofollow always use the invocation's directory, even if the invoked file is a symlink
|
||||
-- @treturn string the current script's path with a trailing slash
|
||||
function app.require_here (base, nofollow)
|
||||
local p = app.script_name()
|
||||
if not path.isabs(p) then
|
||||
p = path.join(path.currentdir(),p)
|
||||
end
|
||||
if not nofollow then
|
||||
local t = path.link_attrib(p)
|
||||
if t and t.mode == 'link' then
|
||||
t = t.target
|
||||
if not path.isabs(t) then
|
||||
t = path.join(path.dirname(p), t)
|
||||
end
|
||||
p = t
|
||||
end
|
||||
end
|
||||
p = path.normpath(path.dirname(p))
|
||||
if p:sub(-1,-1) ~= path.sep then
|
||||
p = p..path.sep
|
||||
end
|
||||
if base then
|
||||
if path.is_windows then
|
||||
base = base:gsub('/','\\')
|
||||
end
|
||||
if path.isabs(base) then
|
||||
p = base .. path.sep
|
||||
else
|
||||
p = p..base..path.sep
|
||||
end
|
||||
end
|
||||
local so_ext = path.is_windows and 'dll' or 'so'
|
||||
local lsep = package.path:find '^;' and '' or ';'
|
||||
local csep = package.cpath:find '^;' and '' or ';'
|
||||
package.path = ('%s?.lua;%s?%sinit.lua%s%s'):format(p,p,path.sep,lsep,package.path)
|
||||
package.cpath = ('%s?.%s%s%s'):format(p,so_ext,csep,package.cpath)
|
||||
return p
|
||||
end
|
||||
|
||||
--- return a suitable path for files private to this application.
|
||||
-- These will look like '~/.SNAME/file', with '~' as with expanduser and
|
||||
-- SNAME is the name of the script without .lua extension.
|
||||
-- If the directory does not exist, it will be created.
|
||||
-- @string file a filename (w/out path)
|
||||
-- @return a full pathname, or nil
|
||||
-- @return cannot create directory error
|
||||
-- @usage
|
||||
-- -- when run from a script called 'testapp' (on Windows):
|
||||
-- local app = require 'pl.app'
|
||||
-- print(app.appfile 'test.txt')
|
||||
-- -- C:\Documents and Settings\steve\.testapp\test.txt
|
||||
function app.appfile(file)
|
||||
local sfullname, err = app.script_name()
|
||||
if not sfullname then return utils.raise(err) end
|
||||
local sname = path.basename(sfullname)
|
||||
local name = path.splitext(sname)
|
||||
local dir = path.join(path.expanduser('~'),'.'..name)
|
||||
if not path.isdir(dir) then
|
||||
local ret = path.mkdir(dir)
|
||||
if not ret then return utils.raise('cannot create '..dir) end
|
||||
end
|
||||
return path.join(dir,file)
|
||||
end
|
||||
|
||||
--- return string indicating operating system.
|
||||
-- @return 'Windows','OSX' or whatever uname returns (e.g. 'Linux')
|
||||
function app.platform()
|
||||
if path.is_windows then
|
||||
return 'Windows'
|
||||
else
|
||||
local f = io.popen('uname')
|
||||
local res = f:read()
|
||||
if res == 'Darwin' then res = 'OSX' end
|
||||
f:close()
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
--- return the full command-line used to invoke this script.
|
||||
-- It will not include the scriptname itself, see `app.script_name`.
|
||||
-- @return command-line
|
||||
-- @return name of Lua program used
|
||||
-- @usage
|
||||
-- -- execute: lua -lluacov -e 'print(_VERSION)' myscript.lua
|
||||
--
|
||||
-- -- myscript.lua
|
||||
-- print(require("pl.app").lua()) --> "lua -lluacov -e 'print(_VERSION)'", "lua"
|
||||
function app.lua()
|
||||
local args = _G.arg
|
||||
if not args then
|
||||
return utils.raise "not in a main program"
|
||||
end
|
||||
|
||||
local cmd = {}
|
||||
local i = -1
|
||||
while true do
|
||||
table.insert(cmd, 1, args[i])
|
||||
if not args[i-1] then
|
||||
return utils.quote_arg(cmd), args[i]
|
||||
end
|
||||
i = i - 1
|
||||
end
|
||||
end
|
||||
|
||||
--- parse command-line arguments into flags and parameters.
|
||||
-- Understands GNU-style command-line flags; short (`-f`) and long (`--flag`).
|
||||
--
|
||||
-- These may be given a value with either '=' or ':' (`-k:2`,`--alpha=3.2`,`-n2`),
|
||||
-- a number value can be given without a space. If the flag is marked
|
||||
-- as having a value, then a space-separated value is also accepted (`-i hello`),
|
||||
-- see the `flags_with_values` argument).
|
||||
--
|
||||
-- Multiple short args can be combined like so: ( `-abcd`).
|
||||
--
|
||||
-- When specifying the `flags_valid` parameter, its contents can also contain
|
||||
-- aliasses, to convert short/long flags to the same output name. See the
|
||||
-- example below.
|
||||
--
|
||||
-- Note: if a flag is repeated, the last value wins.
|
||||
-- @tparam {string} args an array of strings (default is the global `arg`)
|
||||
-- @tab flags_with_values any flags that take values, either list or hash
|
||||
-- table e.g. `{ out=true }` or `{ "out" }`.
|
||||
-- @tab flags_valid (optional) flags that are valid, either list or hashtable.
|
||||
-- If not given, everything
|
||||
-- will be accepted(everything in `flags_with_values` will automatically be allowed)
|
||||
-- @return a table of flags (flag=value pairs)
|
||||
-- @return an array of parameters
|
||||
-- @raise if args is nil, then the global `args` must be available!
|
||||
-- @usage
|
||||
-- -- Simple form:
|
||||
-- local flags, params = app.parse_args(nil,
|
||||
-- { "hello", "world" }, -- list of flags taking values
|
||||
-- { "l", "a", "b"}) -- list of allowed flags (value ones will be added)
|
||||
--
|
||||
-- -- More complex example using aliasses:
|
||||
-- local valid = {
|
||||
-- long = "l", -- if 'l' is specified, it is reported as 'long'
|
||||
-- new = { "n", "old" }, -- here both 'n' and 'old' will go into 'new'
|
||||
-- }
|
||||
-- local values = {
|
||||
-- "value", -- will automatically be added to the allowed set of flags
|
||||
-- "new", -- will mark 'n' and 'old' as requiring a value as well
|
||||
-- }
|
||||
-- local flags, params = app.parse_args(nil, values, valid)
|
||||
--
|
||||
-- -- command: myapp.lua -l --old:hello --value world param1 param2
|
||||
-- -- will yield:
|
||||
-- flags = {
|
||||
-- long = true, -- input from 'l'
|
||||
-- new = "hello", -- input from 'old'
|
||||
-- value = "world", -- allowed because it was in 'values', note: space separated!
|
||||
-- }
|
||||
-- params = {
|
||||
-- [1] = "param1"
|
||||
-- [2] = "param2"
|
||||
-- }
|
||||
function app.parse_args (args,flags_with_values, flags_valid)
|
||||
if not args then
|
||||
args = _G.arg
|
||||
if not args then utils.raise "Not in a main program: 'arg' not found" end
|
||||
end
|
||||
|
||||
local with_values = {}
|
||||
for k,v in pairs(flags_with_values or {}) do
|
||||
if type(k) == "number" then
|
||||
k = v
|
||||
end
|
||||
with_values[k] = true
|
||||
end
|
||||
|
||||
local valid
|
||||
if not flags_valid then
|
||||
-- if no allowed flags provided, we create a table that always returns
|
||||
-- the keyname, no matter what you look up
|
||||
valid = setmetatable({},{ __index = function(_, key) return key end })
|
||||
else
|
||||
valid = {}
|
||||
for k,aliasses in pairs(flags_valid) do
|
||||
if type(k) == "number" then -- array/list entry
|
||||
k = aliasses
|
||||
end
|
||||
if type(aliasses) == "string" then -- single alias
|
||||
aliasses = { aliasses }
|
||||
end
|
||||
if type(aliasses) == "table" then -- list of aliasses
|
||||
-- it's the alternate name, so add the proper mappings
|
||||
for i, alias in ipairs(aliasses) do
|
||||
valid[alias] = k
|
||||
end
|
||||
end
|
||||
valid[k] = k
|
||||
end
|
||||
do
|
||||
local new_with_values = {} -- needed to prevent "invalid key to 'next'" error
|
||||
for k,v in pairs(with_values) do
|
||||
if not valid[k] then
|
||||
valid[k] = k -- add the with_value entry as a valid one
|
||||
new_with_values[k] = true
|
||||
else
|
||||
new_with_values[valid[k]] = true --set, but by its alias
|
||||
end
|
||||
end
|
||||
with_values = new_with_values
|
||||
end
|
||||
end
|
||||
|
||||
-- now check that all flags with values are reported as such under all
|
||||
-- of their aliasses
|
||||
for k, main_alias in pairs(valid) do
|
||||
if with_values[main_alias] then
|
||||
with_values[k] = true
|
||||
end
|
||||
end
|
||||
|
||||
local _args = {}
|
||||
local flags = {}
|
||||
local i = 1
|
||||
while i <= #args do
|
||||
local a = args[i]
|
||||
local v = a:match('^-(.+)')
|
||||
local is_long
|
||||
if not v then
|
||||
-- we have a parameter
|
||||
_args[#_args+1] = a
|
||||
else
|
||||
-- it's a flag
|
||||
if v:find '^-' then
|
||||
is_long = true
|
||||
v = v:sub(2)
|
||||
end
|
||||
if with_values[v] then
|
||||
if i == #args or args[i+1]:find '^-' then
|
||||
return utils.raise ("no value for '"..v.."'")
|
||||
end
|
||||
flags[valid[v]] = args[i+1]
|
||||
i = i + 1
|
||||
else
|
||||
-- a value can also be indicated with = or :
|
||||
local var,val = utils.splitv (v,'[=:]', false, 2)
|
||||
var = var or v
|
||||
val = val or true
|
||||
if not is_long then
|
||||
if #var > 1 then
|
||||
if var:find '.%d+' then -- short flag, number value
|
||||
val = var:sub(2)
|
||||
var = var:sub(1,1)
|
||||
else -- multiple short flags
|
||||
for i = 1,#var do
|
||||
local f = var:sub(i,i)
|
||||
if not valid[f] then
|
||||
return utils.raise("unknown flag '"..f.."'")
|
||||
else
|
||||
f = valid[f]
|
||||
end
|
||||
flags[f] = true
|
||||
end
|
||||
val = nil -- prevents use of var as a flag below
|
||||
end
|
||||
else -- single short flag (can have value, defaults to true)
|
||||
val = val or true
|
||||
end
|
||||
end
|
||||
if val then
|
||||
if not valid[var] then
|
||||
return utils.raise("unknown flag '"..var.."'")
|
||||
else
|
||||
var = valid[var]
|
||||
end
|
||||
flags[var] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return flags,_args
|
||||
end
|
||||
|
||||
return app
|
585
Documentation/compiler/pl/array2d.lua
Normal file
585
Documentation/compiler/pl/array2d.lua
Normal file
|
@ -0,0 +1,585 @@
|
|||
--- Operations on two-dimensional arrays.
|
||||
-- See @{02-arrays.md.Operations_on_two_dimensional_tables|The Guide}
|
||||
--
|
||||
-- The size of the arrays is determined by using the length operator `#` hence
|
||||
-- the module is not `nil` safe, and the usual precautions apply.
|
||||
--
|
||||
-- Note: all functions taking `i1,j1,i2,j2` as arguments will normalize the
|
||||
-- arguments using `default_range`.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.types`
|
||||
-- @module pl.array2d
|
||||
|
||||
local tonumber,tostring,io,ipairs,string,table =
|
||||
_G.tonumber,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table
|
||||
local setmetatable,getmetatable = setmetatable,getmetatable
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local types = require 'pl.types'
|
||||
local imap,tmap,reduce,keys,tmap2,tset,index_by = tablex.imap,tablex.map,tablex.reduce,tablex.keys,tablex.map2,tablex.set,tablex.index_by
|
||||
local remove = table.remove
|
||||
local splitv,fprintf,assert_arg = utils.splitv,utils.fprintf,utils.assert_arg
|
||||
local byte = string.byte
|
||||
local stdout = io.stdout
|
||||
local min = math.min
|
||||
|
||||
|
||||
local array2d = {}
|
||||
|
||||
local function obj (int,out)
|
||||
local mt = getmetatable(int)
|
||||
if mt then
|
||||
setmetatable(out,mt)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function makelist (res)
|
||||
return setmetatable(res, require('pl.List'))
|
||||
end
|
||||
|
||||
--- return the row and column size.
|
||||
-- Size is calculated using the Lua length operator #, so usual precautions
|
||||
-- regarding `nil` values apply.
|
||||
-- @array2d a a 2d array
|
||||
-- @treturn int number of rows (`#a`)
|
||||
-- @treturn int number of cols (`#a[1]`)
|
||||
function array2d.size (a)
|
||||
assert_arg(1,a,'table')
|
||||
return #a,#a[1]
|
||||
end
|
||||
|
||||
do
|
||||
local function index (t,k)
|
||||
return t[k]
|
||||
end
|
||||
|
||||
--- extract a column from the 2D array.
|
||||
-- @array2d a 2d array
|
||||
-- @param j column index
|
||||
-- @return 1d array
|
||||
function array2d.column (a,j)
|
||||
assert_arg(1,a,'table')
|
||||
return makelist(imap(index,a,j))
|
||||
end
|
||||
end
|
||||
local column = array2d.column
|
||||
|
||||
--- extract a row from the 2D array.
|
||||
-- Added in line with `column`, for read-only purposes directly
|
||||
-- accessing a[i] is more performant.
|
||||
-- @array2d a 2d array
|
||||
-- @param i row index
|
||||
-- @return 1d array (copy of the row)
|
||||
function array2d.row(a,i)
|
||||
assert_arg(1,a,'table')
|
||||
local row = a[i]
|
||||
local r = {}
|
||||
for n,v in ipairs(row) do
|
||||
r[n] = v
|
||||
end
|
||||
return makelist(r)
|
||||
end
|
||||
|
||||
--- map a function over a 2D array
|
||||
-- @func f a function of at least one argument
|
||||
-- @array2d a 2d array
|
||||
-- @param arg an optional extra argument to be passed to the function.
|
||||
-- @return 2d array
|
||||
function array2d.map (f,a,arg)
|
||||
assert_arg(2,a,'table')
|
||||
f = utils.function_arg(1,f)
|
||||
return obj(a,imap(function(row) return imap(f,row,arg) end, a))
|
||||
end
|
||||
|
||||
--- reduce the rows using a function.
|
||||
-- @func f a binary function
|
||||
-- @array2d a 2d array
|
||||
-- @return 1d array
|
||||
-- @see pl.tablex.reduce
|
||||
function array2d.reduce_rows (f,a)
|
||||
assert_arg(1,a,'table')
|
||||
return tmap(function(row) return reduce(f,row) end, a)
|
||||
end
|
||||
|
||||
--- reduce the columns using a function.
|
||||
-- @func f a binary function
|
||||
-- @array2d a 2d array
|
||||
-- @return 1d array
|
||||
-- @see pl.tablex.reduce
|
||||
function array2d.reduce_cols (f,a)
|
||||
assert_arg(1,a,'table')
|
||||
return tmap(function(c) return reduce(f,column(a,c)) end, keys(a[1]))
|
||||
end
|
||||
|
||||
--- reduce a 2D array into a scalar, using two operations.
|
||||
-- @func opc operation to reduce the final result
|
||||
-- @func opr operation to reduce the rows
|
||||
-- @param a 2D array
|
||||
function array2d.reduce2 (opc,opr,a)
|
||||
assert_arg(3,a,'table')
|
||||
local tmp = array2d.reduce_rows(opr,a)
|
||||
return reduce(opc,tmp)
|
||||
end
|
||||
|
||||
--- map a function over two arrays.
|
||||
-- They can be both or either 2D arrays
|
||||
-- @func f function of at least two arguments
|
||||
-- @int ad order of first array (`1` if `a` is a list/array, `2` if it is a 2d array)
|
||||
-- @int bd order of second array (`1` if `b` is a list/array, `2` if it is a 2d array)
|
||||
-- @tab a 1d or 2d array
|
||||
-- @tab b 1d or 2d array
|
||||
-- @param arg optional extra argument to pass to function
|
||||
-- @return 2D array, unless both arrays are 1D
|
||||
function array2d.map2 (f,ad,bd,a,b,arg)
|
||||
assert_arg(1,a,'table')
|
||||
assert_arg(2,b,'table')
|
||||
f = utils.function_arg(1,f)
|
||||
if ad == 1 and bd == 2 then
|
||||
return imap(function(row)
|
||||
return tmap2(f,a,row,arg)
|
||||
end, b)
|
||||
elseif ad == 2 and bd == 1 then
|
||||
return imap(function(row)
|
||||
return tmap2(f,row,b,arg)
|
||||
end, a)
|
||||
elseif ad == 1 and bd == 1 then
|
||||
return tmap2(f,a,b)
|
||||
elseif ad == 2 and bd == 2 then
|
||||
return tmap2(function(rowa,rowb)
|
||||
return tmap2(f,rowa,rowb,arg)
|
||||
end, a,b)
|
||||
end
|
||||
end
|
||||
|
||||
--- cartesian product of two 1d arrays.
|
||||
-- @func f a function of 2 arguments
|
||||
-- @array t1 a 1d table
|
||||
-- @array t2 a 1d table
|
||||
-- @return 2d table
|
||||
-- @usage product('..',{1,2},{'a','b'}) == {{'1a','2a'},{'1b','2b'}}
|
||||
function array2d.product (f,t1,t2)
|
||||
f = utils.function_arg(1,f)
|
||||
assert_arg(2,t1,'table')
|
||||
assert_arg(3,t2,'table')
|
||||
local res = {}
|
||||
for i,v in ipairs(t2) do
|
||||
res[i] = tmap(f,t1,v)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- flatten a 2D array.
|
||||
-- (this goes over columns first.)
|
||||
-- @array2d t 2d table
|
||||
-- @return a 1d table
|
||||
-- @usage flatten {{1,2},{3,4},{5,6}} == {1,2,3,4,5,6}
|
||||
function array2d.flatten (t)
|
||||
local res = {}
|
||||
local k = 1
|
||||
local rows, cols = array2d.size(t)
|
||||
for r = 1, rows do
|
||||
local row = t[r]
|
||||
for c = 1, cols do
|
||||
res[k] = row[c]
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- reshape a 2D array. Reshape the aray by specifying a new nr of rows.
|
||||
-- @array2d t 2d array
|
||||
-- @int nrows new number of rows
|
||||
-- @bool co use column-order (Fortran-style) (default false)
|
||||
-- @return a new 2d array
|
||||
function array2d.reshape (t,nrows,co)
|
||||
local nr,nc = array2d.size(t)
|
||||
local ncols = nr*nc / nrows
|
||||
local res = {}
|
||||
local ir,ic = 1,1
|
||||
for i = 1,nrows do
|
||||
local row = {}
|
||||
for j = 1,ncols do
|
||||
row[j] = t[ir][ic]
|
||||
if not co then
|
||||
ic = ic + 1
|
||||
if ic > nc then
|
||||
ir = ir + 1
|
||||
ic = 1
|
||||
end
|
||||
else
|
||||
ir = ir + 1
|
||||
if ir > nr then
|
||||
ic = ic + 1
|
||||
ir = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
res[i] = row
|
||||
end
|
||||
return obj(t,res)
|
||||
end
|
||||
|
||||
--- transpose a 2D array.
|
||||
-- @array2d t 2d array
|
||||
-- @return a new 2d array
|
||||
function array2d.transpose(t)
|
||||
assert_arg(1,t,'table')
|
||||
local _, c = array2d.size(t)
|
||||
return array2d.reshape(t,c,true)
|
||||
end
|
||||
|
||||
--- swap two rows of an array.
|
||||
-- @array2d t a 2d array
|
||||
-- @int i1 a row index
|
||||
-- @int i2 a row index
|
||||
-- @return t (same, modified 2d array)
|
||||
function array2d.swap_rows (t,i1,i2)
|
||||
assert_arg(1,t,'table')
|
||||
t[i1],t[i2] = t[i2],t[i1]
|
||||
return t
|
||||
end
|
||||
|
||||
--- swap two columns of an array.
|
||||
-- @array2d t a 2d array
|
||||
-- @int j1 a column index
|
||||
-- @int j2 a column index
|
||||
-- @return t (same, modified 2d array)
|
||||
function array2d.swap_cols (t,j1,j2)
|
||||
assert_arg(1,t,'table')
|
||||
for _, row in ipairs(t) do
|
||||
row[j1],row[j2] = row[j2],row[j1]
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
--- extract the specified rows.
|
||||
-- @array2d t 2d array
|
||||
-- @tparam {int} ridx a table of row indices
|
||||
-- @return a new 2d array with the extracted rows
|
||||
function array2d.extract_rows (t,ridx)
|
||||
return obj(t,index_by(t,ridx))
|
||||
end
|
||||
|
||||
--- extract the specified columns.
|
||||
-- @array2d t 2d array
|
||||
-- @tparam {int} cidx a table of column indices
|
||||
-- @return a new 2d array with the extracted colums
|
||||
function array2d.extract_cols (t,cidx)
|
||||
assert_arg(1,t,'table')
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
res[i] = index_by(t[i],cidx)
|
||||
end
|
||||
return obj(t,res)
|
||||
end
|
||||
|
||||
--- remove a row from an array.
|
||||
-- @function array2d.remove_row
|
||||
-- @array2d t a 2d array
|
||||
-- @int i a row index
|
||||
array2d.remove_row = remove
|
||||
|
||||
--- remove a column from an array.
|
||||
-- @array2d t a 2d array
|
||||
-- @int j a column index
|
||||
function array2d.remove_col (t,j)
|
||||
assert_arg(1,t,'table')
|
||||
for i = 1,#t do
|
||||
remove(t[i],j)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local function _parse (s)
|
||||
local r, c = s:match 'R(%d+)C(%d+)'
|
||||
if r then
|
||||
r,c = tonumber(r),tonumber(c)
|
||||
return r,c
|
||||
end
|
||||
c,r = s:match '(%a+)(%d+)'
|
||||
if c then
|
||||
local cv = 0
|
||||
for i = 1, #c do
|
||||
cv = cv * 26 + byte(c:sub(i,i)) - byte 'A' + 1
|
||||
end
|
||||
return tonumber(r), cv
|
||||
end
|
||||
error('bad cell specifier: '..s)
|
||||
end
|
||||
|
||||
--- parse a spreadsheet range or cell.
|
||||
-- The range/cell can be specified either as 'A1:B2' or 'R1C1:R2C2' or for
|
||||
-- single cells as 'A1' or 'R1C1'.
|
||||
-- @string s a range (case insensitive).
|
||||
-- @treturn int start row
|
||||
-- @treturn int start col
|
||||
-- @treturn int end row (or `nil` if the range was a single cell)
|
||||
-- @treturn int end col (or `nil` if the range was a single cell)
|
||||
function array2d.parse_range (s)
|
||||
assert_arg(1,s,'string')
|
||||
s = s:upper()
|
||||
if s:find ':' then
|
||||
local start,finish = splitv(s,':')
|
||||
local i1,j1 = _parse(start)
|
||||
local i2,j2 = _parse(finish)
|
||||
return i1,j1,i2,j2
|
||||
else -- single value
|
||||
local i,j = _parse(s)
|
||||
return i,j
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- get a slice of a 2D array.
|
||||
-- Same as `slice`.
|
||||
-- @see slice
|
||||
function array2d.range (...)
|
||||
return array2d.slice(...)
|
||||
end
|
||||
|
||||
local default_range do
|
||||
local function norm_value(v, max)
|
||||
if not v then return v end
|
||||
if v < 0 then
|
||||
v = max + v + 1
|
||||
end
|
||||
if v < 1 then v = 1 end
|
||||
if v > max then v = max end
|
||||
return v
|
||||
end
|
||||
|
||||
--- normalizes coordinates to valid positive entries and defaults.
|
||||
-- Negative indices will be counted from the end, too low, or too high
|
||||
-- will be limited by the array sizes.
|
||||
-- @array2d t a 2D array
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @return i1, j1, i2, j2
|
||||
function array2d.default_range (t,i1,j1,i2,j2)
|
||||
if (type(i1) == 'string') and not (j1 or i2 or j2) then
|
||||
i1, j1, i2, j2 = array2d.parse_range(i1)
|
||||
end
|
||||
local nr, nc = array2d.size(t)
|
||||
i1 = norm_value(i1 or 1, nr)
|
||||
j1 = norm_value(j1 or 1, nc)
|
||||
i2 = norm_value(i2 or nr, nr)
|
||||
j2 = norm_value(j2 or nc, nc)
|
||||
return i1,j1,i2,j2
|
||||
end
|
||||
default_range = array2d.default_range
|
||||
end
|
||||
|
||||
--- get a slice of a 2D array. Note that if the specified range has
|
||||
-- a 1D result, the rank of the result will be 1.
|
||||
-- @array2d t a 2D array
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @return an array, 2D in general but 1D in special cases.
|
||||
function array2d.slice (t,i1,j1,i2,j2)
|
||||
assert_arg(1,t,'table')
|
||||
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
|
||||
local res = {}
|
||||
for i = i1,i2 do
|
||||
local val
|
||||
local row = t[i]
|
||||
if j1 == j2 then
|
||||
val = row[j1]
|
||||
else
|
||||
val = {}
|
||||
for j = j1,j2 do
|
||||
val[#val+1] = row[j]
|
||||
end
|
||||
end
|
||||
res[#res+1] = val
|
||||
end
|
||||
if i1 == i2 then res = res[1] end
|
||||
return obj(t,res)
|
||||
end
|
||||
|
||||
--- set a specified range of an array to a value.
|
||||
-- @array2d t a 2D array
|
||||
-- @param value the value (may be a function, called as `val(i,j)`)
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @see tablex.set
|
||||
function array2d.set (t,value,i1,j1,i2,j2)
|
||||
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
|
||||
local i = i1
|
||||
if types.is_callable(value) then
|
||||
local old_f = value
|
||||
value = function(j)
|
||||
return old_f(i,j)
|
||||
end
|
||||
end
|
||||
while i <= i2 do
|
||||
tset(t[i],value,j1,j2)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
--- write a 2D array to a file.
|
||||
-- @array2d t a 2D array
|
||||
-- @param f a file object (default stdout)
|
||||
-- @string fmt a format string (default is just to use tostring)
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
function array2d.write (t,f,fmt,i1,j1,i2,j2)
|
||||
assert_arg(1,t,'table')
|
||||
f = f or stdout
|
||||
local rowop
|
||||
if fmt then
|
||||
rowop = function(row,j) fprintf(f,fmt,row[j]) end
|
||||
else
|
||||
rowop = function(row,j) f:write(tostring(row[j]),' ') end
|
||||
end
|
||||
local function newline()
|
||||
f:write '\n'
|
||||
end
|
||||
array2d.forall(t,rowop,newline,i1,j1,i2,j2)
|
||||
end
|
||||
|
||||
--- perform an operation for all values in a 2D array.
|
||||
-- @array2d t 2D array
|
||||
-- @func row_op function to call on each value; `row_op(row,j)`
|
||||
-- @func end_row_op function to call at end of each row; `end_row_op(i)`
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
function array2d.forall (t,row_op,end_row_op,i1,j1,i2,j2)
|
||||
assert_arg(1,t,'table')
|
||||
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
|
||||
for i = i1,i2 do
|
||||
local row = t[i]
|
||||
for j = j1,j2 do
|
||||
row_op(row,j)
|
||||
end
|
||||
if end_row_op then end_row_op(i) end
|
||||
end
|
||||
end
|
||||
|
||||
---- move a block from the destination to the source.
|
||||
-- @array2d dest a 2D array
|
||||
-- @int di start row in dest
|
||||
-- @int dj start col in dest
|
||||
-- @array2d src a 2D array
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
function array2d.move (dest,di,dj,src,i1,j1,i2,j2)
|
||||
assert_arg(1,dest,'table')
|
||||
assert_arg(4,src,'table')
|
||||
i1,j1,i2,j2 = default_range(src,i1,j1,i2,j2)
|
||||
local nr,nc = array2d.size(dest)
|
||||
i2, j2 = min(nr,i2), min(nc,j2)
|
||||
--i1, j1 = max(1,i1), max(1,j1)
|
||||
dj = dj - 1
|
||||
for i = i1,i2 do
|
||||
local drow, srow = dest[i+di-1], src[i]
|
||||
for j = j1,j2 do
|
||||
drow[j+dj] = srow[j]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over all elements in a 2D array, with optional indices.
|
||||
-- @array2d a 2D array
|
||||
-- @bool indices with indices (default false)
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @return either `value` or `i,j,value` depending on the value of `indices`
|
||||
function array2d.iter(a,indices,i1,j1,i2,j2)
|
||||
assert_arg(1,a,'table')
|
||||
i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2)
|
||||
local i,j = i1,j1-1
|
||||
local row = a[i]
|
||||
return function()
|
||||
j = j + 1
|
||||
if j > j2 then
|
||||
j = j1
|
||||
i = i + 1
|
||||
row = a[i]
|
||||
if i > i2 then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if indices then
|
||||
return i,j,row[j]
|
||||
else
|
||||
return row[j]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over all columns.
|
||||
-- @array2d a a 2D array
|
||||
-- @return column, column-index
|
||||
function array2d.columns(a)
|
||||
assert_arg(1,a,'table')
|
||||
local n = #a[1]
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > n then return nil end
|
||||
return column(a,i), i
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over all rows.
|
||||
-- Returns a copy of the row, for read-only purposes directly iterating
|
||||
-- is more performant; `ipairs(a)`
|
||||
-- @array2d a a 2D array
|
||||
-- @return row, row-index
|
||||
function array2d.rows(a)
|
||||
assert_arg(1,a,'table')
|
||||
local n = #a
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > n then return nil end
|
||||
return array2d.row(a,i), i
|
||||
end
|
||||
end
|
||||
|
||||
--- new array of specified dimensions
|
||||
-- @int rows number of rows
|
||||
-- @int cols number of cols
|
||||
-- @param val initial value; if it's a function then use `val(i,j)`
|
||||
-- @return new 2d array
|
||||
function array2d.new(rows,cols,val)
|
||||
local res = {}
|
||||
local fun = types.is_callable(val)
|
||||
for i = 1,rows do
|
||||
local row = {}
|
||||
if fun then
|
||||
for j = 1,cols do row[j] = val(i,j) end
|
||||
else
|
||||
for j = 1,cols do row[j] = val end
|
||||
end
|
||||
res[i] = row
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
return array2d
|
265
Documentation/compiler/pl/class.lua
Normal file
265
Documentation/compiler/pl/class.lua
Normal file
|
@ -0,0 +1,265 @@
|
|||
--- Provides a reuseable and convenient framework for creating classes in Lua.
|
||||
-- Two possible notations:
|
||||
--
|
||||
-- B = class(A)
|
||||
-- class.B(A)
|
||||
--
|
||||
-- The latter form creates a named class within the current environment. Note
|
||||
-- that this implicitly brings in `pl.utils` as a dependency.
|
||||
--
|
||||
-- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion}
|
||||
-- @module pl.class
|
||||
|
||||
local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type =
|
||||
_G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type
|
||||
local compat
|
||||
|
||||
-- this trickery is necessary to prevent the inheritance of 'super' and
|
||||
-- the resulting recursive call problems.
|
||||
local function call_ctor (c,obj,...)
|
||||
local init = rawget(c,'_init')
|
||||
local parent_with_init = rawget(c,'_parent_with_init')
|
||||
|
||||
if parent_with_init then
|
||||
if not init then -- inheriting an init
|
||||
init = rawget(parent_with_init, '_init')
|
||||
parent_with_init = rawget(parent_with_init, '_parent_with_init')
|
||||
end
|
||||
if parent_with_init then -- super() points to one above whereever _init came from
|
||||
rawset(obj,'super',function(obj,...)
|
||||
call_ctor(parent_with_init,obj,...)
|
||||
end)
|
||||
end
|
||||
else
|
||||
-- Without this, calling super() where none exists will sometimes loop and stack overflow
|
||||
rawset(obj,'super',nil)
|
||||
end
|
||||
|
||||
local res = init(obj,...)
|
||||
if parent_with_init then -- If this execution of call_ctor set a super, unset it
|
||||
rawset(obj,'super',nil)
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
--- initializes an __instance__ upon creation.
|
||||
-- @function class:_init
|
||||
-- @param ... parameters passed to the constructor
|
||||
-- @usage local Cat = class()
|
||||
-- function Cat:_init(name)
|
||||
-- --self:super(name) -- call the ancestor initializer if needed
|
||||
-- self.name = name
|
||||
-- end
|
||||
--
|
||||
-- local pussycat = Cat("pussycat")
|
||||
-- print(pussycat.name) --> pussycat
|
||||
|
||||
--- checks whether an __instance__ is derived from some class.
|
||||
-- Works the other way around as `class_of`. It has two ways of using;
|
||||
-- 1) call with a class to check against, 2) call without params.
|
||||
-- @function instance:is_a
|
||||
-- @param some_class class to check against, or `nil` to return the class
|
||||
-- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then
|
||||
-- it returns the class table of the instance
|
||||
-- @usage local pussycat = Lion() -- assuming Lion derives from Cat
|
||||
-- if pussycat:is_a(Cat) then
|
||||
-- -- it's true, it is a Lion, but also a Cat
|
||||
-- end
|
||||
--
|
||||
-- if pussycat:is_a() == Lion then
|
||||
-- -- It's true
|
||||
-- end
|
||||
local function is_a(self,klass)
|
||||
if klass == nil then
|
||||
-- no class provided, so return the class this instance is derived from
|
||||
return getmetatable(self)
|
||||
end
|
||||
local m = getmetatable(self)
|
||||
if not m then return false end --*can't be an object!
|
||||
while m do
|
||||
if m == klass then return true end
|
||||
m = rawget(m,'_base')
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- checks whether an __instance__ is derived from some class.
|
||||
-- Works the other way around as `is_a`.
|
||||
-- @function some_class:class_of
|
||||
-- @param some_instance instance to check against
|
||||
-- @return `true` if `some_instance` is derived from `some_class`
|
||||
-- @usage local pussycat = Lion() -- assuming Lion derives from Cat
|
||||
-- if Cat:class_of(pussycat) then
|
||||
-- -- it's true
|
||||
-- end
|
||||
local function class_of(klass,obj)
|
||||
if type(klass) ~= 'table' or not rawget(klass,'is_a') then return false end
|
||||
return klass.is_a(obj,klass)
|
||||
end
|
||||
|
||||
--- cast an object to another class.
|
||||
-- It is not clever (or safe!) so use carefully.
|
||||
-- @param some_instance the object to be changed
|
||||
-- @function some_class:cast
|
||||
local function cast (klass, obj)
|
||||
return setmetatable(obj,klass)
|
||||
end
|
||||
|
||||
|
||||
local function _class_tostring (obj)
|
||||
local mt = obj._class
|
||||
local name = rawget(mt,'_name')
|
||||
setmetatable(obj,nil)
|
||||
local str = tostring(obj)
|
||||
setmetatable(obj,mt)
|
||||
if name then str = name ..str:gsub('table','') end
|
||||
return str
|
||||
end
|
||||
|
||||
local function tupdate(td,ts,dont_override)
|
||||
for k,v in pairs(ts) do
|
||||
if not dont_override or td[k] == nil then
|
||||
td[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function _class(base,c_arg,c)
|
||||
-- the class `c` will be the metatable for all its objects,
|
||||
-- and they will look up their methods in it.
|
||||
local mt = {} -- a metatable for the class to support __call and _handler
|
||||
-- can define class by passing it a plain table of methods
|
||||
local plain = type(base) == 'table' and not getmetatable(base)
|
||||
if plain then
|
||||
c = base
|
||||
base = c._base
|
||||
else
|
||||
c = c or {}
|
||||
end
|
||||
|
||||
if type(base) == 'table' then
|
||||
-- our new class is a shallow copy of the base class!
|
||||
-- but be careful not to wipe out any methods we have been given at this point!
|
||||
tupdate(c,base,plain)
|
||||
c._base = base
|
||||
-- inherit the 'not found' handler, if present
|
||||
if rawget(c,'_handler') then mt.__index = c._handler end
|
||||
elseif base ~= nil then
|
||||
error("must derive from a table type",3)
|
||||
end
|
||||
|
||||
c.__index = c
|
||||
setmetatable(c,mt)
|
||||
if not plain then
|
||||
if base and rawget(base,'_init') then c._parent_with_init = base end -- For super and inherited init
|
||||
c._init = nil
|
||||
end
|
||||
|
||||
if base and rawget(base,'_class_init') then
|
||||
base._class_init(c,c_arg)
|
||||
end
|
||||
|
||||
-- expose a ctor which can be called by <classname>(<args>)
|
||||
mt.__call = function(class_tbl,...)
|
||||
local obj
|
||||
if rawget(c,'_create') then obj = c._create(...) end
|
||||
if not obj then obj = {} end
|
||||
setmetatable(obj,c)
|
||||
|
||||
if rawget(c,'_init') or rawget(c,'_parent_with_init') then -- constructor exists
|
||||
local res = call_ctor(c,obj,...)
|
||||
if res then -- _if_ a ctor returns a value, it becomes the object...
|
||||
obj = res
|
||||
setmetatable(obj,c)
|
||||
end
|
||||
end
|
||||
|
||||
if base and rawget(base,'_post_init') then
|
||||
base._post_init(obj)
|
||||
end
|
||||
|
||||
return obj
|
||||
end
|
||||
-- Call Class.catch to set a handler for methods/properties not found in the class!
|
||||
c.catch = function(self, handler)
|
||||
if type(self) == "function" then
|
||||
-- called using . instead of :
|
||||
handler = self
|
||||
end
|
||||
c._handler = handler
|
||||
mt.__index = handler
|
||||
end
|
||||
c.is_a = is_a
|
||||
c.class_of = class_of
|
||||
c.cast = cast
|
||||
c._class = c
|
||||
|
||||
if not rawget(c,'__tostring') then
|
||||
c.__tostring = _class_tostring
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
--- create a new class, derived from a given base class.
|
||||
-- Supporting two class creation syntaxes:
|
||||
-- either `Name = class(base)` or `class.Name(base)`.
|
||||
-- The first form returns the class directly and does not set its `_name`.
|
||||
-- The second form creates a variable `Name` in the current environment set
|
||||
-- to the class, and also sets `_name`.
|
||||
-- @function class
|
||||
-- @param base optional base class
|
||||
-- @param c_arg optional parameter to class constructor
|
||||
-- @param c optional table to be used as class
|
||||
local class
|
||||
class = setmetatable({},{
|
||||
__call = function(fun,...)
|
||||
return _class(...)
|
||||
end,
|
||||
__index = function(tbl,key)
|
||||
if key == 'class' then
|
||||
io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n')
|
||||
return class
|
||||
end
|
||||
compat = compat or require 'pl.compat'
|
||||
local env = compat.getfenv(2)
|
||||
return function(...)
|
||||
local c = _class(...)
|
||||
c._name = key
|
||||
rawset(env,key,c)
|
||||
return c
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
class.properties = class()
|
||||
|
||||
function class.properties._class_init(klass)
|
||||
klass.__index = function(t,key)
|
||||
-- normal class lookup!
|
||||
local v = klass[key]
|
||||
if v then return v end
|
||||
-- is it a getter?
|
||||
v = rawget(klass,'get_'..key)
|
||||
if v then
|
||||
return v(t)
|
||||
end
|
||||
-- is it a field?
|
||||
return rawget(t,'_'..key)
|
||||
end
|
||||
klass.__newindex = function (t,key,value)
|
||||
-- if there's a setter, use that, otherwise directly set table
|
||||
local p = 'set_'..key
|
||||
local setter = klass[p]
|
||||
if setter then
|
||||
setter(t,value)
|
||||
else
|
||||
rawset(t,key,value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return class
|
||||
|
252
Documentation/compiler/pl/compat.lua
Normal file
252
Documentation/compiler/pl/compat.lua
Normal file
|
@ -0,0 +1,252 @@
|
|||
----------------
|
||||
--- Lua 5.1/5.2/5.3 compatibility.
|
||||
-- Injects `table.pack`, `table.unpack`, and `package.searchpath` in the global
|
||||
-- environment, to make sure they are available for Lua 5.1 and LuaJIT.
|
||||
--
|
||||
-- All other functions are exported as usual in the returned module table.
|
||||
--
|
||||
-- NOTE: everything in this module is also available in `pl.utils`.
|
||||
-- @module pl.compat
|
||||
local compat = {}
|
||||
|
||||
--- boolean flag this is Lua 5.1 (or LuaJIT).
|
||||
-- @field lua51
|
||||
compat.lua51 = _VERSION == 'Lua 5.1'
|
||||
|
||||
--- boolean flag this is LuaJIT.
|
||||
-- @field jit
|
||||
compat.jit = (tostring(assert):match('builtin') ~= nil)
|
||||
|
||||
--- boolean flag this is LuaJIT with 5.2 compatibility compiled in.
|
||||
-- @field jit52
|
||||
if compat.jit then
|
||||
-- 'goto' is a keyword when 52 compatibility is enabled in LuaJit
|
||||
compat.jit52 = not loadstring("local goto = 1")
|
||||
end
|
||||
|
||||
--- the directory separator character for the current platform.
|
||||
-- @field dir_separator
|
||||
compat.dir_separator = _G.package.config:sub(1,1)
|
||||
|
||||
--- boolean flag this is a Windows platform.
|
||||
-- @field is_windows
|
||||
compat.is_windows = compat.dir_separator == '\\'
|
||||
|
||||
--- execute a shell command, in a compatible and platform independent way.
|
||||
-- This is a compatibility function that returns the same for Lua 5.1 and
|
||||
-- Lua 5.2+.
|
||||
--
|
||||
-- NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems
|
||||
-- only use exitcodes 0-255, anything else is undefined.
|
||||
--
|
||||
-- NOTE2: In Lua 5.2 and 5.3 a Windows exitcode of -1 would not properly be
|
||||
-- returned, this function will return it properly for all versions.
|
||||
-- @param cmd a shell command
|
||||
-- @return true if successful
|
||||
-- @return actual return code
|
||||
function compat.execute(cmd)
|
||||
local res1,res2,res3 = os.execute(cmd)
|
||||
if res2 == "No error" and res3 == 0 and compat.is_windows then
|
||||
-- os.execute bug in Lua 5.2/5.3 not reporting -1 properly on Windows
|
||||
-- this was fixed in 5.4
|
||||
res3 = -1
|
||||
end
|
||||
if compat.lua51 and not compat.jit52 then
|
||||
if compat.is_windows then
|
||||
return res1==0,res1
|
||||
else
|
||||
res1 = res1 > 255 and res1 / 256 or res1
|
||||
return res1==0,res1
|
||||
end
|
||||
else
|
||||
if compat.is_windows then
|
||||
return res3==0,res3
|
||||
else
|
||||
return not not res1,res3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
----------------
|
||||
-- Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way).
|
||||
-- @param ld code string or loader
|
||||
-- @param[opt] source name of chunk for errors
|
||||
-- @param[opt] mode 'b', 't' or 'bt'
|
||||
-- @param[opt] env environment to load the chunk in
|
||||
-- @function compat.load
|
||||
|
||||
---------------
|
||||
-- Get environment of a function (in a Lua 5.1 compatible way).
|
||||
-- Not 100% compatible, so with Lua 5.2 it may return nil for a function with no
|
||||
-- global references!
|
||||
-- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html)
|
||||
-- @param f a function or a call stack reference
|
||||
-- @function compat.getfenv
|
||||
|
||||
---------------
|
||||
-- Set environment of a function (in a Lua 5.1 compatible way).
|
||||
-- @param f a function or a call stack reference
|
||||
-- @param env a table that becomes the new environment of `f`
|
||||
-- @function compat.setfenv
|
||||
|
||||
if compat.lua51 then -- define Lua 5.2 style load()
|
||||
if not compat.jit then -- but LuaJIT's load _is_ compatible
|
||||
local lua51_load = load
|
||||
function compat.load(str,src,mode,env)
|
||||
local chunk,err
|
||||
if type(str) == 'string' then
|
||||
if str:byte(1) == 27 and not (mode or 'bt'):find 'b' then
|
||||
return nil,"attempt to load a binary chunk"
|
||||
end
|
||||
chunk,err = loadstring(str,src)
|
||||
else
|
||||
chunk,err = lua51_load(str,src)
|
||||
end
|
||||
if chunk and env then setfenv(chunk,env) end
|
||||
return chunk,err
|
||||
end
|
||||
else
|
||||
compat.load = load
|
||||
end
|
||||
compat.setfenv, compat.getfenv = setfenv, getfenv
|
||||
else
|
||||
compat.load = load
|
||||
-- setfenv/getfenv replacements for Lua 5.2
|
||||
-- by Sergey Rozhenko
|
||||
-- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
|
||||
-- Roberto Ierusalimschy notes that it is possible for getfenv to return nil
|
||||
-- in the case of a function with no globals:
|
||||
-- http://lua-users.org/lists/lua-l/2010-06/msg00315.html
|
||||
function compat.setfenv(f, t)
|
||||
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
|
||||
local name
|
||||
local up = 0
|
||||
repeat
|
||||
up = up + 1
|
||||
name = debug.getupvalue(f, up)
|
||||
until name == '_ENV' or name == nil
|
||||
if name then
|
||||
debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue
|
||||
debug.setupvalue(f, up, t)
|
||||
end
|
||||
if f ~= 0 then return f end
|
||||
end
|
||||
|
||||
function compat.getfenv(f)
|
||||
local f = f or 0
|
||||
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
|
||||
local name, val
|
||||
local up = 0
|
||||
repeat
|
||||
up = up + 1
|
||||
name, val = debug.getupvalue(f, up)
|
||||
until name == '_ENV' or name == nil
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Global exported functions (for Lua 5.1 & LuaJIT)
|
||||
-- @section lua52
|
||||
|
||||
--- pack an argument list into a table.
|
||||
-- @param ... any arguments
|
||||
-- @return a table with field n set to the length
|
||||
-- @function table.pack
|
||||
if not table.pack then
|
||||
function table.pack (...) -- luacheck: ignore
|
||||
return {n=select('#',...); ...}
|
||||
end
|
||||
end
|
||||
|
||||
--- unpack a table and return the elements.
|
||||
--
|
||||
-- NOTE: this version does NOT honor the n field, and hence it is not nil-safe.
|
||||
-- See `utils.unpack` for a version that is nil-safe.
|
||||
-- @param t table to unpack
|
||||
-- @param[opt] i index from which to start unpacking, defaults to 1
|
||||
-- @param[opt] j index of the last element to unpack, defaults to #t
|
||||
-- @return multiple return values from the table
|
||||
-- @function table.unpack
|
||||
-- @see utils.unpack
|
||||
if not table.unpack then
|
||||
table.unpack = unpack -- luacheck: ignore
|
||||
end
|
||||
|
||||
--- return the full path where a file name would be matched.
|
||||
-- This function was introduced in Lua 5.2, so this compatibility version
|
||||
-- will be injected in Lua 5.1 engines.
|
||||
-- @string name file name, possibly dotted
|
||||
-- @string path a path-template in the same form as package.path or package.cpath
|
||||
-- @string[opt] sep template separate character to be replaced by path separator. Default: "."
|
||||
-- @string[opt] rep the path separator to use, defaults to system separator. Default; "/" on Unixes, "\" on Windows.
|
||||
-- @see path.package_path
|
||||
-- @function package.searchpath
|
||||
-- @return on success: path of the file
|
||||
-- @return on failure: nil, error string listing paths tried
|
||||
if not package.searchpath then
|
||||
function package.searchpath (name,path,sep,rep) -- luacheck: ignore
|
||||
if type(name) ~= "string" then
|
||||
error(("bad argument #1 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
if type(path) ~= "string" then
|
||||
error(("bad argument #2 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
if sep ~= nil and type(sep) ~= "string" then
|
||||
error(("bad argument #3 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
if rep ~= nil and type(rep) ~= "string" then
|
||||
error(("bad argument #4 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
sep = sep or "."
|
||||
rep = rep or compat.dir_separator
|
||||
do
|
||||
local s, e = name:find(sep, nil, true)
|
||||
while s do
|
||||
name = name:sub(1, s-1) .. rep .. name:sub(e+1, -1)
|
||||
s, e = name:find(sep, s + #rep + 1, true)
|
||||
end
|
||||
end
|
||||
local tried = {}
|
||||
for m in path:gmatch('[^;]+') do
|
||||
local nm = m:gsub('?', name)
|
||||
tried[#tried+1] = nm
|
||||
local f = io.open(nm,'r')
|
||||
if f then f:close(); return nm end
|
||||
end
|
||||
return nil, "\tno file '" .. table.concat(tried, "'\n\tno file '") .. "'"
|
||||
end
|
||||
end
|
||||
|
||||
--- Global exported functions (for Lua < 5.4)
|
||||
-- @section lua54
|
||||
|
||||
--- raise a warning message.
|
||||
-- This functions mimics the `warn` function added in Lua 5.4.
|
||||
-- @function warn
|
||||
-- @param ... any arguments
|
||||
if not rawget(_G, "warn") then
|
||||
local enabled = false
|
||||
local function warn(arg1, ...)
|
||||
if type(arg1) == "string" and arg1:sub(1, 1) == "@" then
|
||||
-- control message
|
||||
if arg1 == "@on" then
|
||||
enabled = true
|
||||
return
|
||||
end
|
||||
if arg1 == "@off" then
|
||||
enabled = false
|
||||
return
|
||||
end
|
||||
return -- ignore unknown control messages
|
||||
end
|
||||
if enabled then
|
||||
io.stderr:write("Lua warning: ", arg1, ...)
|
||||
io.stderr:write("\n")
|
||||
end
|
||||
end
|
||||
-- use rawset to bypass OpenResty's protection of global scope
|
||||
rawset(_G, "warn", warn)
|
||||
end
|
||||
|
||||
return compat
|
285
Documentation/compiler/pl/comprehension.lua
Normal file
285
Documentation/compiler/pl/comprehension.lua
Normal file
|
@ -0,0 +1,285 @@
|
|||
--- List comprehensions implemented in Lua.
|
||||
--
|
||||
-- See the [wiki page](http://lua-users.org/wiki/ListComprehensions)
|
||||
--
|
||||
-- local C= require 'pl.comprehension' . new()
|
||||
--
|
||||
-- C ('x for x=1,10') ()
|
||||
-- ==> {1,2,3,4,5,6,7,8,9,10}
|
||||
-- C 'x^2 for x=1,4' ()
|
||||
-- ==> {1,4,9,16}
|
||||
-- C '{x,x^2} for x=1,4' ()
|
||||
-- ==> {{1,1},{2,4},{3,9},{4,16}}
|
||||
-- C '2*x for x' {1,2,3}
|
||||
-- ==> {2,4,6}
|
||||
-- dbl = C '2*x for x'
|
||||
-- dbl {10,20,30}
|
||||
-- ==> {20,40,60}
|
||||
-- C 'x for x if x % 2 == 0' {1,2,3,4,5}
|
||||
-- ==> {2,4}
|
||||
-- C '{x,y} for x = 1,2 for y = 1,2' ()
|
||||
-- ==> {{1,1},{1,2},{2,1},{2,2}}
|
||||
-- C '{x,y} for x for y' ({1,2},{10,20})
|
||||
-- ==> {{1,10},{1,20},{2,10},{2,20}}
|
||||
-- assert(C 'sum(x^2 for x)' {2,3,4} == 2^2+3^2+4^2)
|
||||
--
|
||||
-- (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license).
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.luabalanced`
|
||||
--
|
||||
-- See @{07-functional.md.List_Comprehensions|the Guide}
|
||||
-- @module pl.comprehension
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
|
||||
local status,lb = pcall(require, "pl.luabalanced")
|
||||
if not status then
|
||||
lb = require 'luabalanced'
|
||||
end
|
||||
|
||||
local math_max = math.max
|
||||
local table_concat = table.concat
|
||||
|
||||
-- fold operations
|
||||
-- http://en.wikipedia.org/wiki/Fold_(higher-order_function)
|
||||
local ops = {
|
||||
list = {init=' {} ', accum=' __result[#__result+1] = (%s) '},
|
||||
table = {init=' {} ', accum=' local __k, __v = %s __result[__k] = __v '},
|
||||
sum = {init=' 0 ', accum=' __result = __result + (%s) '},
|
||||
min = {init=' nil ', accum=' local __tmp = %s ' ..
|
||||
' if __result then if __tmp < __result then ' ..
|
||||
'__result = __tmp end else __result = __tmp end '},
|
||||
max = {init=' nil ', accum=' local __tmp = %s ' ..
|
||||
' if __result then if __tmp > __result then ' ..
|
||||
'__result = __tmp end else __result = __tmp end '},
|
||||
}
|
||||
|
||||
|
||||
-- Parses comprehension string expr.
|
||||
-- Returns output expression list <out> string, array of for types
|
||||
-- ('=', 'in' or nil) <fortypes>, array of input variable name
|
||||
-- strings <invarlists>, array of input variable value strings
|
||||
-- <invallists>, array of predicate expression strings <preds>,
|
||||
-- operation name string <opname>, and number of placeholder
|
||||
-- parameters <max_param>.
|
||||
--
|
||||
-- The is equivalent to the mathematical set-builder notation:
|
||||
--
|
||||
-- <opname> { <out> | <invarlist> in <invallist> , <preds> }
|
||||
--
|
||||
-- @usage "x^2 for x" -- array values
|
||||
-- @usage "x^2 for x=1,10,2" -- numeric for
|
||||
-- @usage "k^v for k,v in pairs(_1)" -- iterator for
|
||||
-- @usage "(x+y)^2 for x for y if x > y" -- nested
|
||||
--
|
||||
local function parse_comprehension(expr)
|
||||
local pos = 1
|
||||
|
||||
-- extract opname (if exists)
|
||||
local opname
|
||||
local tok, post = expr:match('^%s*([%a_][%w_]*)%s*%(()', pos)
|
||||
local pose = #expr + 1
|
||||
if tok then
|
||||
local tok2, posb = lb.match_bracketed(expr, post-1)
|
||||
assert(tok2, 'syntax error')
|
||||
if expr:match('^%s*$', posb) then
|
||||
opname = tok
|
||||
pose = posb - 1
|
||||
pos = post
|
||||
end
|
||||
end
|
||||
opname = opname or "list"
|
||||
|
||||
-- extract out expression list
|
||||
local out; out, pos = lb.match_explist(expr, pos)
|
||||
assert(out, "syntax error: missing expression list")
|
||||
out = table_concat(out, ', ')
|
||||
|
||||
-- extract "for" clauses
|
||||
local fortypes = {}
|
||||
local invarlists = {}
|
||||
local invallists = {}
|
||||
while 1 do
|
||||
local post = expr:match('^%s*for%s+()', pos)
|
||||
if not post then break end
|
||||
pos = post
|
||||
|
||||
-- extract input vars
|
||||
local iv; iv, pos = lb.match_namelist(expr, pos)
|
||||
assert(#iv > 0, 'syntax error: zero variables')
|
||||
for _,ident in ipairs(iv) do
|
||||
assert(not ident:match'^__',
|
||||
"identifier " .. ident .. " may not contain __ prefix")
|
||||
end
|
||||
invarlists[#invarlists+1] = iv
|
||||
|
||||
-- extract '=' or 'in' (optional)
|
||||
local fortype, post = expr:match('^(=)%s*()', pos)
|
||||
if not fortype then fortype, post = expr:match('^(in)%s+()', pos) end
|
||||
if fortype then
|
||||
pos = post
|
||||
-- extract input value range
|
||||
local il; il, pos = lb.match_explist(expr, pos)
|
||||
assert(#il > 0, 'syntax error: zero expressions')
|
||||
assert(fortype ~= '=' or #il == 2 or #il == 3,
|
||||
'syntax error: numeric for requires 2 or three expressions')
|
||||
fortypes[#invarlists] = fortype
|
||||
invallists[#invarlists] = il
|
||||
else
|
||||
fortypes[#invarlists] = false
|
||||
invallists[#invarlists] = false
|
||||
end
|
||||
end
|
||||
assert(#invarlists > 0, 'syntax error: missing "for" clause')
|
||||
|
||||
-- extract "if" clauses
|
||||
local preds = {}
|
||||
while 1 do
|
||||
local post = expr:match('^%s*if%s+()', pos)
|
||||
if not post then break end
|
||||
pos = post
|
||||
local pred; pred, pos = lb.match_expression(expr, pos)
|
||||
assert(pred, 'syntax error: predicated expression not found')
|
||||
preds[#preds+1] = pred
|
||||
end
|
||||
|
||||
-- extract number of parameter variables (name matching "_%d+")
|
||||
local stmp = ''; lb.gsub(expr, function(u, sin) -- strip comments/strings
|
||||
if u == 'e' then stmp = stmp .. ' ' .. sin .. ' ' end
|
||||
end)
|
||||
local max_param = 0; stmp:gsub('[%a_][%w_]*', function(s)
|
||||
local s = s:match('^_(%d+)$')
|
||||
if s then max_param = math_max(max_param, tonumber(s)) end
|
||||
end)
|
||||
|
||||
if pos ~= pose then
|
||||
assert(false, "syntax error: unrecognized " .. expr:sub(pos))
|
||||
end
|
||||
|
||||
--DEBUG:
|
||||
--print('----\n', string.format("%q", expr), string.format("%q", out), opname)
|
||||
--for k,v in ipairs(invarlists) do print(k,v, invallists[k]) end
|
||||
--for k,v in ipairs(preds) do print(k,v) end
|
||||
|
||||
return out, fortypes, invarlists, invallists, preds, opname, max_param
|
||||
end
|
||||
|
||||
|
||||
-- Create Lua code string representing comprehension.
|
||||
-- Arguments are in the form returned by parse_comprehension.
|
||||
local function code_comprehension(
|
||||
out, fortypes, invarlists, invallists, preds, opname, max_param
|
||||
)
|
||||
local op = assert(ops[opname])
|
||||
local code = op.accum:gsub('%%s', out)
|
||||
|
||||
for i=#preds,1,-1 do local pred = preds[i]
|
||||
code = ' if ' .. pred .. ' then ' .. code .. ' end '
|
||||
end
|
||||
for i=#invarlists,1,-1 do
|
||||
if not fortypes[i] then
|
||||
local arrayname = '__in' .. i
|
||||
local idx = '__idx' .. i
|
||||
code =
|
||||
' for ' .. idx .. ' = 1, #' .. arrayname .. ' do ' ..
|
||||
' local ' .. invarlists[i][1] .. ' = ' .. arrayname .. '['..idx..'] ' ..
|
||||
code .. ' end '
|
||||
else
|
||||
code =
|
||||
' for ' ..
|
||||
table_concat(invarlists[i], ', ') ..
|
||||
' ' .. fortypes[i] .. ' ' ..
|
||||
table_concat(invallists[i], ', ') ..
|
||||
' do ' .. code .. ' end '
|
||||
end
|
||||
end
|
||||
code = ' local __result = ( ' .. op.init .. ' ) ' .. code
|
||||
return code
|
||||
end
|
||||
|
||||
|
||||
-- Convert code string represented by code_comprehension
|
||||
-- into Lua function. Also must pass ninputs = #invarlists,
|
||||
-- max_param, and invallists (from parse_comprehension).
|
||||
-- Uses environment env.
|
||||
local function wrap_comprehension(code, ninputs, max_param, invallists, env)
|
||||
assert(ninputs > 0)
|
||||
local ts = {}
|
||||
for i=1,max_param do
|
||||
ts[#ts+1] = '_' .. i
|
||||
end
|
||||
for i=1,ninputs do
|
||||
if not invallists[i] then
|
||||
local name = '__in' .. i
|
||||
ts[#ts+1] = name
|
||||
end
|
||||
end
|
||||
if #ts > 0 then
|
||||
code = ' local ' .. table_concat(ts, ', ') .. ' = ... ' .. code
|
||||
end
|
||||
code = code .. ' return __result '
|
||||
--print('DEBUG:', code)
|
||||
local f, err = utils.load(code,'tmp','t',env)
|
||||
if not f then assert(false, err .. ' with generated code ' .. code) end
|
||||
return f
|
||||
end
|
||||
|
||||
|
||||
-- Build Lua function from comprehension string.
|
||||
-- Uses environment env.
|
||||
local function build_comprehension(expr, env)
|
||||
local out, fortypes, invarlists, invallists, preds, opname, max_param
|
||||
= parse_comprehension(expr)
|
||||
local code = code_comprehension(
|
||||
out, fortypes, invarlists, invallists, preds, opname, max_param)
|
||||
local f = wrap_comprehension(code, #invarlists, max_param, invallists, env)
|
||||
return f
|
||||
end
|
||||
|
||||
|
||||
-- Creates new comprehension cache.
|
||||
-- Any list comprehension function created are set to the environment
|
||||
-- env (defaults to caller of new).
|
||||
local function new(env)
|
||||
-- Note: using a single global comprehension cache would have had
|
||||
-- security implications (e.g. retrieving cached functions created
|
||||
-- in other environments).
|
||||
-- The cache lookup function could have instead been written to retrieve
|
||||
-- the caller's environment, lookup up the cache private to that
|
||||
-- environment, and then looked up the function in that cache.
|
||||
-- That would avoid the need for this <new> call to
|
||||
-- explicitly manage caches; however, that might also have an undue
|
||||
-- performance penalty.
|
||||
|
||||
if not env then
|
||||
env = utils.getfenv(2)
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
local cache = setmetatable({}, mt)
|
||||
|
||||
-- Index operator builds, caches, and returns Lua function
|
||||
-- corresponding to comprehension expression string.
|
||||
--
|
||||
-- Example: f = comprehension['x^2 for x']
|
||||
--
|
||||
function mt:__index(expr)
|
||||
local f = build_comprehension(expr, env)
|
||||
self[expr] = f -- cache
|
||||
return f
|
||||
end
|
||||
|
||||
-- Convenience syntax.
|
||||
-- Allows comprehension 'x^2 for x' instead of comprehension['x^2 for x'].
|
||||
mt.__call = mt.__index
|
||||
|
||||
cache.new = new
|
||||
|
||||
return cache
|
||||
end
|
||||
|
||||
|
||||
local comprehension = {}
|
||||
comprehension.new = new
|
||||
|
||||
return comprehension
|
207
Documentation/compiler/pl/config.lua
Normal file
207
Documentation/compiler/pl/config.lua
Normal file
|
@ -0,0 +1,207 @@
|
|||
--- Reads configuration files into a Lua table.
|
||||
-- Understands INI files, classic Unix config files, and simple
|
||||
-- delimited columns of values. See @{06-data.md.Reading_Configuration_Files|the Guide}
|
||||
--
|
||||
-- # test.config
|
||||
-- # Read timeout in seconds
|
||||
-- read.timeout=10
|
||||
-- # Write timeout in seconds
|
||||
-- write.timeout=5
|
||||
-- #acceptable ports
|
||||
-- ports = 1002,1003,1004
|
||||
--
|
||||
-- -- readconfig.lua
|
||||
-- local config = require 'config'
|
||||
-- local t = config.read 'test.config'
|
||||
-- print(pretty.write(t))
|
||||
--
|
||||
-- ### output #####
|
||||
-- {
|
||||
-- ports = {
|
||||
-- 1002,
|
||||
-- 1003,
|
||||
-- 1004
|
||||
-- },
|
||||
-- write_timeout = 5,
|
||||
-- read_timeout = 10
|
||||
-- }
|
||||
--
|
||||
-- @module pl.config
|
||||
|
||||
local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table
|
||||
|
||||
local function split(s,re)
|
||||
local res = {}
|
||||
local t_insert = table.insert
|
||||
re = '[^'..re..']+'
|
||||
for k in s:gmatch(re) do t_insert(res,k) end
|
||||
return res
|
||||
end
|
||||
|
||||
local function strip(s)
|
||||
return s:gsub('^%s+',''):gsub('%s+$','')
|
||||
end
|
||||
|
||||
local function strip_quotes (s)
|
||||
return s:gsub("['\"](.*)['\"]",'%1')
|
||||
end
|
||||
|
||||
local config = {}
|
||||
|
||||
--- like io.lines(), but allows for lines to be continued with '\'.
|
||||
-- @param file a file-like object (anything where read() returns the next line) or a filename.
|
||||
-- Defaults to stardard input.
|
||||
-- @return an iterator over the lines, or nil
|
||||
-- @return error 'not a file-like object' or 'file is nil'
|
||||
function config.lines(file)
|
||||
local f,openf,err
|
||||
local line = ''
|
||||
if type(file) == 'string' then
|
||||
f,err = io.open(file,'r')
|
||||
if not f then return nil,err end
|
||||
openf = true
|
||||
else
|
||||
f = file or io.stdin
|
||||
if not file.read then return nil, 'not a file-like object' end
|
||||
end
|
||||
if not f then return nil, 'file is nil' end
|
||||
return function()
|
||||
local l = f:read()
|
||||
while l do
|
||||
-- only for non-blank lines that don't begin with either ';' or '#'
|
||||
if l:match '%S' and not l:match '^%s*[;#]' then
|
||||
-- does the line end with '\'?
|
||||
local i = l:find '\\%s*$'
|
||||
if i then -- if so,
|
||||
line = line..l:sub(1,i-1)
|
||||
elseif line == '' then
|
||||
return l
|
||||
else
|
||||
l = line..l
|
||||
line = ''
|
||||
return l
|
||||
end
|
||||
end
|
||||
l = f:read()
|
||||
end
|
||||
if openf then f:close() end
|
||||
end
|
||||
end
|
||||
|
||||
--- read a configuration file into a table
|
||||
-- @param file either a file-like object or a string, which must be a filename
|
||||
-- @tab[opt] cnfg a configuration table that may contain these fields:
|
||||
--
|
||||
-- * `smart` try to deduce what kind of config file we have (default false)
|
||||
-- * `variabilize` make names into valid Lua identifiers (default true)
|
||||
-- * `convert_numbers` try to convert values into numbers (default true)
|
||||
-- * `trim_space` ensure that there is no starting or trailing whitespace with values (default true)
|
||||
-- * `trim_quotes` remove quotes from strings (default false)
|
||||
-- * `list_delim` delimiter to use when separating columns (default ',')
|
||||
-- * `keysep` separator between key and value pairs (default '=')
|
||||
--
|
||||
-- @return a table containing items, or `nil`
|
||||
-- @return error message (same as @{config.lines}
|
||||
function config.read(file,cnfg)
|
||||
local auto
|
||||
|
||||
local iter,err = config.lines(file)
|
||||
if not iter then return nil,err end
|
||||
local line = iter()
|
||||
cnfg = cnfg or {}
|
||||
if cnfg.smart then
|
||||
auto = true
|
||||
if line:match '^[^=]+=' then
|
||||
cnfg.keysep = '='
|
||||
elseif line:match '^[^:]+:' then
|
||||
cnfg.keysep = ':'
|
||||
cnfg.list_delim = ':'
|
||||
elseif line:match '^%S+%s+' then
|
||||
cnfg.keysep = ' '
|
||||
-- more than two columns assume that it's a space-delimited list
|
||||
-- cf /etc/fstab with /etc/ssh/ssh_config
|
||||
if line:match '^%S+%s+%S+%s+%S+' then
|
||||
cnfg.list_delim = ' '
|
||||
end
|
||||
cnfg.variabilize = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function check_cnfg (var,def)
|
||||
local val = cnfg[var]
|
||||
if val == nil then return def else return val end
|
||||
end
|
||||
|
||||
local initial_digits = '^[%d%+%-]'
|
||||
local t = {}
|
||||
local top_t = t
|
||||
local variabilize = check_cnfg ('variabilize',true)
|
||||
local list_delim = check_cnfg('list_delim',',')
|
||||
local convert_numbers = check_cnfg('convert_numbers',true)
|
||||
local convert_boolean = check_cnfg('convert_boolean',false)
|
||||
local trim_space = check_cnfg('trim_space',true)
|
||||
local trim_quotes = check_cnfg('trim_quotes',false)
|
||||
local ignore_assign = check_cnfg('ignore_assign',false)
|
||||
local keysep = check_cnfg('keysep','=')
|
||||
local keypat = keysep == ' ' and '%s+' or '%s*'..keysep..'%s*'
|
||||
if list_delim == ' ' then list_delim = '%s+' end
|
||||
|
||||
local function process_name(key)
|
||||
if variabilize then
|
||||
key = key:gsub('[^%w]','_')
|
||||
end
|
||||
return key
|
||||
end
|
||||
|
||||
local function process_value(value)
|
||||
if list_delim and value:find(list_delim) then
|
||||
value = split(value,list_delim)
|
||||
for i,v in ipairs(value) do
|
||||
value[i] = process_value(v)
|
||||
end
|
||||
elseif convert_numbers and value:find(initial_digits) then
|
||||
local val = tonumber(value)
|
||||
if not val and value:match ' kB$' then
|
||||
value = value:gsub(' kB','')
|
||||
val = tonumber(value)
|
||||
end
|
||||
if val then value = val end
|
||||
elseif convert_boolean and value == 'true' then
|
||||
return true
|
||||
elseif convert_boolean and value == 'false' then
|
||||
return false
|
||||
end
|
||||
if type(value) == 'string' then
|
||||
if trim_space then value = strip(value) end
|
||||
if not trim_quotes and auto and value:match '^"' then
|
||||
trim_quotes = true
|
||||
end
|
||||
if trim_quotes then value = strip_quotes(value) end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
while line do
|
||||
if line:find('^%[') then -- section!
|
||||
local section = process_name(line:match('%[([^%]]+)%]'))
|
||||
t = top_t
|
||||
t[section] = {}
|
||||
t = t[section]
|
||||
else
|
||||
line = line:gsub('^%s*','')
|
||||
local i1,i2 = line:find(keypat)
|
||||
if i1 and not ignore_assign then -- key,value assignment
|
||||
local key = process_name(line:sub(1,i1-1))
|
||||
local value = process_value(line:sub(i2+1))
|
||||
t[key] = value
|
||||
else -- a plain list of values...
|
||||
t[#t+1] = process_value(line)
|
||||
end
|
||||
end
|
||||
line = iter()
|
||||
end
|
||||
return top_t
|
||||
end
|
||||
|
||||
return config
|
654
Documentation/compiler/pl/data.lua
Normal file
654
Documentation/compiler/pl/data.lua
Normal file
|
@ -0,0 +1,654 @@
|
|||
--- Reading and querying simple tabular data.
|
||||
--
|
||||
-- data.read 'test.txt'
|
||||
-- ==> {{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','}
|
||||
--
|
||||
-- Provides a way of creating basic SQL-like queries.
|
||||
--
|
||||
-- require 'pl'
|
||||
-- local d = data.read('xyz.txt')
|
||||
-- local q = d:select('x,y,z where x > 3 and z < 2 sort by y')
|
||||
-- for x,y,z in q do
|
||||
-- print(x,y,z)
|
||||
-- end
|
||||
--
|
||||
-- See @{06-data.md.Reading_Columnar_Data|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.array2d` (fallback methods)
|
||||
-- @module pl.data
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local _DEBUG = rawget(_G,'_DEBUG')
|
||||
|
||||
local patterns,function_arg,usplit,array_tostring = utils.patterns,utils.function_arg,utils.split,utils.array_tostring
|
||||
local append,concat = table.insert,table.concat
|
||||
local gsub = string.gsub
|
||||
local io = io
|
||||
local _G,print,type,tonumber,ipairs,setmetatable = _G,print,type,tonumber,ipairs,setmetatable
|
||||
|
||||
|
||||
local data = {}
|
||||
|
||||
local parse_select
|
||||
|
||||
local function rstrip(s)
|
||||
return (s:gsub('%s+$',''))
|
||||
end
|
||||
|
||||
local function strip (s)
|
||||
return (rstrip(s):gsub('^%s*',''))
|
||||
end
|
||||
|
||||
-- This gives `l` the standard List metatable,
|
||||
-- pulling in the List module.
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local function map(fun,t)
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
res[i] = fun(t[i])
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function split(line,delim,csv,n)
|
||||
local massage
|
||||
-- CSV fields may be double-quoted and may contain commas!
|
||||
if csv and line:match '"' then
|
||||
line = line:gsub('"([^"]+)"',function(str)
|
||||
local s,cnt = str:gsub(',','\001')
|
||||
if cnt > 0 then massage = true end
|
||||
return s
|
||||
end)
|
||||
if massage then
|
||||
massage = function(s) return (s:gsub('\001',',')) end
|
||||
end
|
||||
end
|
||||
local res = (usplit(line,delim,false,n))
|
||||
if csv then
|
||||
-- restore CSV commas-in-fields
|
||||
if massage then res = map(massage,res) end
|
||||
-- in CSV mode trailiing commas are significant!
|
||||
if line:match ',$' then append(res,'') end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
local function find(t,v)
|
||||
for i = 1,#t do
|
||||
if v == t[i] then return i end
|
||||
end
|
||||
end
|
||||
|
||||
local DataMT = {
|
||||
column_by_name = function(self,name)
|
||||
if type(name) == 'number' then
|
||||
name = '$'..name
|
||||
end
|
||||
local arr = {}
|
||||
for res in data.query(self,name) do
|
||||
append(arr,res)
|
||||
end
|
||||
return makelist(arr)
|
||||
end,
|
||||
|
||||
copy_select = function(self,condn)
|
||||
condn = parse_select(condn,self)
|
||||
local iter = data.query(self,condn)
|
||||
local res = {}
|
||||
local row = makelist{iter()}
|
||||
while #row > 0 do
|
||||
append(res,row)
|
||||
row = makelist{iter()}
|
||||
end
|
||||
res.delim = self.delim
|
||||
return data.new(res,split(condn.fields,','))
|
||||
end,
|
||||
|
||||
column_names = function(self)
|
||||
return self.fieldnames
|
||||
end,
|
||||
}
|
||||
|
||||
local array2d
|
||||
|
||||
DataMT.__index = function(self,name)
|
||||
local f = DataMT[name]
|
||||
if f then return f end
|
||||
if not array2d then
|
||||
array2d = require 'pl.array2d'
|
||||
end
|
||||
return array2d[name]
|
||||
end
|
||||
|
||||
--- return a particular column as a list of values (method).
|
||||
-- @param name either name of column, or numerical index.
|
||||
-- @function Data.column_by_name
|
||||
|
||||
--- return a query iterator on this data (method).
|
||||
-- @string condn the query expression
|
||||
-- @function Data.select
|
||||
-- @see data.query
|
||||
|
||||
--- return a row iterator on this data (method).
|
||||
-- @string condn the query expression
|
||||
-- @function Data.select_row
|
||||
|
||||
--- return a new data object based on this query (method).
|
||||
-- @string condn the query expression
|
||||
-- @function Data.copy_select
|
||||
|
||||
--- return the field names of this data object (method).
|
||||
-- @function Data.column_names
|
||||
|
||||
--- write out a row (method).
|
||||
-- @param f file-like object
|
||||
-- @function Data.write_row
|
||||
|
||||
--- write data out to file (method).
|
||||
-- @param f file-like object
|
||||
-- @function Data.write
|
||||
|
||||
|
||||
-- [guessing delimiter] We check for comma, tab and spaces in that order.
|
||||
-- [issue] any other delimiters to be checked?
|
||||
local delims = {',', '\t', ' ', ';'}
|
||||
|
||||
local function guess_delim (line)
|
||||
if line=='' then return ' ' end
|
||||
for _,delim in ipairs(delims) do
|
||||
if line:find(delim) then
|
||||
return delim == ' ' and '%s+' or delim
|
||||
end
|
||||
end
|
||||
return ' '
|
||||
end
|
||||
|
||||
-- [file parameter] If it's a string, we try open as a filename. If nil, then
|
||||
-- either stdin or stdout depending on the mode. Otherwise, check if this is
|
||||
-- a file-like object (implements read or write depending)
|
||||
local function open_file (f,mode)
|
||||
local opened, err
|
||||
local reading = mode == 'r'
|
||||
if type(f) == 'string' then
|
||||
if f == 'stdin' then
|
||||
f = io.stdin
|
||||
elseif f == 'stdout' then
|
||||
f = io.stdout
|
||||
else
|
||||
f,err = io.open(f,mode)
|
||||
if not f then return nil,err end
|
||||
opened = true
|
||||
end
|
||||
end
|
||||
if f and ((reading and not f.read) or (not reading and not f.write)) then
|
||||
return nil, "not a file-like object"
|
||||
end
|
||||
return f,nil,opened
|
||||
end
|
||||
|
||||
--- read a delimited file in a Lua table.
|
||||
-- By default, attempts to treat first line as separated list of fieldnames.
|
||||
-- @param file a filename or a file-like object
|
||||
-- @tab cnfg parsing options
|
||||
-- @string cnfg.delim a string pattern to split fields
|
||||
-- @array cnfg.fieldnames (i.e. don't read from first line)
|
||||
-- @bool cnfg.no_convert (default is to try conversion on first data line)
|
||||
-- @tab cnfg.convert table of custom conversion functions with column keys
|
||||
-- @int cnfg.numfields indices of columns known to be numbers
|
||||
-- @bool cnfg.last_field_collect only split as many fields as fieldnames.
|
||||
-- @int cnfg.thousands_dot thousands separator in Excel CSV is '.'
|
||||
-- @bool cnfg.csv fields may be double-quoted and contain commas;
|
||||
-- Also, empty fields are considered to be equivalent to zero.
|
||||
-- @return `data` object, or `nil`
|
||||
-- @return error message. May be a file error, 'not a file-like object'
|
||||
-- or a conversion error
|
||||
function data.read(file,cnfg)
|
||||
local count,line
|
||||
local D = {}
|
||||
if not cnfg then cnfg = {} end
|
||||
local f,err,opened = open_file(file,'r')
|
||||
if not f then return nil, err end
|
||||
local thousands_dot = cnfg.thousands_dot
|
||||
local csv = cnfg.csv
|
||||
if csv then cnfg.delim = ',' end
|
||||
|
||||
-- note that using dot as the thousands separator (@thousands_dot)
|
||||
-- requires a special conversion function! For CSV, _empty fields_ are
|
||||
-- considered to default to numerial zeroes.
|
||||
local tonumber = tonumber
|
||||
local function try_number(x)
|
||||
if thousands_dot then x = x:gsub('%.(...)','%1') end
|
||||
if csv and x == '' then x = '0' end
|
||||
local v = tonumber(x)
|
||||
if v == nil then return nil,"not a number" end
|
||||
return v
|
||||
end
|
||||
|
||||
count = 1
|
||||
line = f:read()
|
||||
if not line then return nil, "empty file" end
|
||||
|
||||
-- first question: what is the delimiter?
|
||||
D.delim = cnfg.delim and cnfg.delim or guess_delim(line)
|
||||
local delim = D.delim
|
||||
|
||||
local conversion
|
||||
local numfields = {}
|
||||
local function append_conversion (idx,conv)
|
||||
conversion = conversion or {}
|
||||
append(numfields,idx)
|
||||
append(conversion,conv)
|
||||
end
|
||||
if cnfg.numfields then
|
||||
for _,n in ipairs(cnfg.numfields) do append_conversion(n,try_number) end
|
||||
end
|
||||
|
||||
-- some space-delimited data starts with a space. This should not be a column,
|
||||
-- although it certainly would be for comma-separated, etc.
|
||||
local stripper
|
||||
if delim == '%s+' and line:find(delim) == 1 then
|
||||
stripper = function(s) return s:gsub('^%s+','') end
|
||||
line = stripper(line)
|
||||
end
|
||||
-- first line will usually be field names. Unless fieldnames are specified,
|
||||
-- we check if it contains purely numerical values for the case of reading
|
||||
-- plain data files.
|
||||
if not cnfg.fieldnames then
|
||||
local fields,nums
|
||||
fields = split(line,delim,csv)
|
||||
if not cnfg.convert then
|
||||
nums = map(tonumber,fields)
|
||||
if #nums == #fields then -- they're ALL numbers!
|
||||
append(D,nums) -- add the first converted row
|
||||
-- and specify conversions for subsequent rows
|
||||
for i = 1,#nums do append_conversion(i,try_number) end
|
||||
else -- we'll try to check numbers just now..
|
||||
nums = nil
|
||||
end
|
||||
else -- [explicit column conversions] (any deduced number conversions will be added)
|
||||
for idx,conv in pairs(cnfg.convert) do append_conversion(idx,conv) end
|
||||
end
|
||||
if nums == nil then
|
||||
cnfg.fieldnames = fields
|
||||
end
|
||||
line = f:read()
|
||||
count = count + 1
|
||||
if stripper then line = stripper(line) end
|
||||
elseif type(cnfg.fieldnames) == 'string' then
|
||||
cnfg.fieldnames = split(cnfg.fieldnames,delim,csv)
|
||||
end
|
||||
local nfields
|
||||
-- at this point, the column headers have been read in. If the first
|
||||
-- row consisted of numbers, it has already been added to the dataset.
|
||||
if cnfg.fieldnames then
|
||||
D.fieldnames = cnfg.fieldnames
|
||||
-- [collecting end field] If @last_field_collect then we'll
|
||||
-- only split as many fields as there are fieldnames
|
||||
if cnfg.last_field_collect then
|
||||
nfields = #D.fieldnames
|
||||
end
|
||||
-- [implicit column conversion] unless @no_convert, we need the numerical field indices
|
||||
-- of the first data row. These can also be specified explicitly by @numfields.
|
||||
if not cnfg.no_convert then
|
||||
local fields = split(line,D.delim,csv,nfields)
|
||||
for i = 1,#fields do
|
||||
if not find(numfields,i) and try_number(fields[i]) then
|
||||
append_conversion(i,try_number)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- keep going until finished
|
||||
while line do
|
||||
if not line:find ('^%s*$') then -- [blank lines] ignore them!
|
||||
if stripper then line = stripper(line) end
|
||||
local fields = split(line,delim,csv,nfields)
|
||||
if conversion then -- there were field conversions...
|
||||
for k = 1,#numfields do
|
||||
local i,conv = numfields[k],conversion[k]
|
||||
local val,err = conv(fields[i])
|
||||
if val == nil then
|
||||
return nil, err..": "..fields[i].." at line "..count
|
||||
else
|
||||
fields[i] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
append(D,fields)
|
||||
end
|
||||
line = f:read()
|
||||
count = count + 1
|
||||
end
|
||||
if opened then f:close() end
|
||||
if delim == '%s+' then D.delim = ' ' end
|
||||
if not D.fieldnames then D.fieldnames = {} end
|
||||
return data.new(D)
|
||||
end
|
||||
|
||||
local function write_row (data,f,row,delim)
|
||||
data.temp = array_tostring(row,data.temp)
|
||||
f:write(concat(data.temp,delim),'\n')
|
||||
end
|
||||
|
||||
function DataMT:write_row(f,row)
|
||||
write_row(self,f,row,self.delim)
|
||||
end
|
||||
|
||||
--- write 2D data to a file.
|
||||
-- Does not assume that the data has actually been
|
||||
-- generated with `new` or `read`.
|
||||
-- @param data 2D array
|
||||
-- @param file filename or file-like object
|
||||
-- @tparam[opt] {string} fieldnames list of fields (optional)
|
||||
-- @string[opt='\t'] delim delimiter (default tab)
|
||||
-- @return true or nil, error
|
||||
function data.write (data,file,fieldnames,delim)
|
||||
local f,err,opened = open_file(file,'w')
|
||||
if not f then return nil, err end
|
||||
if not fieldnames then
|
||||
fieldnames = data.fieldnames
|
||||
end
|
||||
delim = delim or '\t'
|
||||
if fieldnames and #fieldnames > 0 then
|
||||
f:write(concat(fieldnames,delim),'\n')
|
||||
end
|
||||
for i = 1,#data do
|
||||
write_row(data,f,data[i],delim)
|
||||
end
|
||||
if opened then f:close() end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function DataMT:write(file)
|
||||
data.write(self,file,self.fieldnames,self.delim)
|
||||
end
|
||||
|
||||
local function massage_fieldnames (fields,copy)
|
||||
-- fieldnames must be valid Lua identifiers; ignore any surrounding padding
|
||||
-- but keep the original fieldnames...
|
||||
for i = 1,#fields do
|
||||
local f = strip(fields[i])
|
||||
copy[i] = f
|
||||
fields[i] = f:gsub('%W','_')
|
||||
end
|
||||
end
|
||||
|
||||
--- create a new dataset from a table of rows.
|
||||
-- Can specify the fieldnames, else the table must have a field called
|
||||
-- 'fieldnames', which is either a string of delimiter-separated names,
|
||||
-- or a table of names. <br>
|
||||
-- If the table does not have a field called 'delim', then an attempt will be
|
||||
-- made to guess it from the fieldnames string, defaults otherwise to tab.
|
||||
-- @param d the table.
|
||||
-- @tparam[opt] {string} fieldnames optional fieldnames
|
||||
-- @return the table.
|
||||
function data.new (d,fieldnames)
|
||||
d.fieldnames = d.fieldnames or fieldnames or ''
|
||||
if not d.delim and type(d.fieldnames) == 'string' then
|
||||
d.delim = guess_delim(d.fieldnames)
|
||||
d.fieldnames = split(d.fieldnames,d.delim)
|
||||
end
|
||||
d.fieldnames = makelist(d.fieldnames)
|
||||
d.original_fieldnames = {}
|
||||
massage_fieldnames(d.fieldnames,d.original_fieldnames)
|
||||
setmetatable(d,DataMT)
|
||||
-- a query with just the fieldname will return a sequence
|
||||
-- of values, which seq.copy turns into a table.
|
||||
return d
|
||||
end
|
||||
|
||||
local sorted_query = [[
|
||||
return function (t)
|
||||
local i = 0
|
||||
local v
|
||||
local ls = {}
|
||||
for i,v in ipairs(t) do
|
||||
if CONDITION then
|
||||
ls[#ls+1] = v
|
||||
end
|
||||
end
|
||||
table.sort(ls,function(v1,v2)
|
||||
return SORT_EXPR
|
||||
end)
|
||||
local n = #ls
|
||||
return function()
|
||||
i = i + 1
|
||||
v = ls[i]
|
||||
if i > n then return end
|
||||
return FIELDLIST
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
-- question: is this optimized case actually worth the extra code?
|
||||
local simple_query = [[
|
||||
return function (t)
|
||||
local n = #t
|
||||
local i = 0
|
||||
local v
|
||||
return function()
|
||||
repeat
|
||||
i = i + 1
|
||||
v = t[i]
|
||||
until i > n or CONDITION
|
||||
if i > n then return end
|
||||
return FIELDLIST
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
local function is_string (s)
|
||||
return type(s) == 'string'
|
||||
end
|
||||
|
||||
local field_error
|
||||
|
||||
local function fieldnames_as_string (data)
|
||||
return concat(data.fieldnames,',')
|
||||
end
|
||||
|
||||
local function massage_fields(data,f)
|
||||
local idx
|
||||
if f:find '^%d+$' then
|
||||
idx = tonumber(f)
|
||||
else
|
||||
idx = find(data.fieldnames,f)
|
||||
end
|
||||
if idx then
|
||||
return 'v['..idx..']'
|
||||
else
|
||||
field_error = f..' not found in '..fieldnames_as_string(data)
|
||||
return f
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function process_select (data,parms)
|
||||
--- preparing fields ----
|
||||
field_error = nil
|
||||
local fields = parms.fields
|
||||
local numfields = fields:find '%$' or #data.fieldnames == 0
|
||||
if fields:find '^%s*%*%s*' then
|
||||
if not numfields then
|
||||
fields = fieldnames_as_string(data)
|
||||
else
|
||||
local ncol = #data[1]
|
||||
fields = {}
|
||||
for i = 1,ncol do append(fields,'$'..i) end
|
||||
fields = concat(fields,',')
|
||||
end
|
||||
end
|
||||
local idpat = patterns.IDEN
|
||||
if numfields then
|
||||
idpat = '%$(%d+)'
|
||||
else
|
||||
-- massage field names to replace non-identifier chars
|
||||
fields = rstrip(fields):gsub('[^,%w]','_')
|
||||
end
|
||||
local massage_fields = utils.bind1(massage_fields,data)
|
||||
local ret = gsub(fields,idpat,massage_fields)
|
||||
if field_error then return nil,field_error end
|
||||
parms.fields = fields
|
||||
parms.proc_fields = ret
|
||||
parms.where = parms.where or 'true'
|
||||
if is_string(parms.where) then
|
||||
parms.where = gsub(parms.where,idpat,massage_fields)
|
||||
field_error = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
parse_select = function(s,data)
|
||||
local endp
|
||||
local parms = {}
|
||||
local w1,w2 = s:find('where ')
|
||||
local s1,s2 = s:find('sort by ')
|
||||
if w1 then -- where clause!
|
||||
endp = (s1 or 0)-1
|
||||
parms.where = s:sub(w2+1,endp)
|
||||
end
|
||||
if s1 then -- sort by clause (must be last!)
|
||||
parms.sort_by = s:sub(s2+1)
|
||||
end
|
||||
endp = (w1 or s1 or 0)-1
|
||||
parms.fields = s:sub(1,endp)
|
||||
local status,err = process_select(data,parms)
|
||||
if not status then return nil,err
|
||||
else return parms end
|
||||
end
|
||||
|
||||
--- create a query iterator from a select string.
|
||||
-- Select string has this format: <br>
|
||||
-- FIELDLIST [ where LUA-CONDN [ sort by FIELD] ]<br>
|
||||
-- FIELDLIST is a comma-separated list of valid fields, or '*'. <br> <br>
|
||||
-- The condition can also be a table, with fields 'fields' (comma-sep string or
|
||||
-- table), 'sort_by' (string) and 'where' (Lua expression string or function)
|
||||
-- @param data table produced by read
|
||||
-- @param condn select string or table
|
||||
-- @param context a list of tables to be searched when resolving functions
|
||||
-- @param return_row if true, wrap the results in a row table
|
||||
-- @return an iterator over the specified fields, or nil
|
||||
-- @return an error message
|
||||
function data.query(data,condn,context,return_row)
|
||||
local err
|
||||
if is_string(condn) then
|
||||
condn,err = parse_select(condn,data)
|
||||
if not condn then return nil,err end
|
||||
elseif type(condn) == 'table' then
|
||||
if type(condn.fields) == 'table' then
|
||||
condn.fields = concat(condn.fields,',')
|
||||
end
|
||||
if not condn.proc_fields then
|
||||
local status,err = process_select(data,condn)
|
||||
if not status then return nil,err end
|
||||
end
|
||||
else
|
||||
return nil, "condition must be a string or a table"
|
||||
end
|
||||
local query
|
||||
if condn.sort_by then -- use sorted_query
|
||||
query = sorted_query
|
||||
else
|
||||
query = simple_query
|
||||
end
|
||||
local fields = condn.proc_fields or condn.fields
|
||||
if return_row then
|
||||
fields = '{'..fields..'}'
|
||||
end
|
||||
query = query:gsub('FIELDLIST',fields)
|
||||
if is_string(condn.where) then
|
||||
query = query:gsub('CONDITION',condn.where)
|
||||
condn.where = nil
|
||||
else
|
||||
query = query:gsub('CONDITION','_condn(v)')
|
||||
condn.where = function_arg(0,condn.where,'condition.where must be callable')
|
||||
end
|
||||
if condn.sort_by then
|
||||
local expr,sort_var,sort_dir
|
||||
local sort_by = condn.sort_by
|
||||
local i1,i2 = sort_by:find('%s+')
|
||||
if i1 then
|
||||
sort_var,sort_dir = sort_by:sub(1,i1-1),sort_by:sub(i2+1)
|
||||
else
|
||||
sort_var = sort_by
|
||||
sort_dir = 'asc'
|
||||
end
|
||||
if sort_var:match '^%$' then sort_var = sort_var:sub(2) end
|
||||
sort_var = massage_fields(data,sort_var)
|
||||
if field_error then return nil,field_error end
|
||||
if sort_dir == 'asc' then
|
||||
sort_dir = '<'
|
||||
else
|
||||
sort_dir = '>'
|
||||
end
|
||||
expr = ('%s %s %s'):format(sort_var:gsub('v','v1'),sort_dir,sort_var:gsub('v','v2'))
|
||||
query = query:gsub('SORT_EXPR',expr)
|
||||
end
|
||||
if condn.where then
|
||||
query = 'return function(_condn) '..query..' end'
|
||||
end
|
||||
if _DEBUG then print(query) end
|
||||
|
||||
local fn,err = utils.load(query,'tmp')
|
||||
if not fn then return nil,err end
|
||||
fn = fn() -- get the function
|
||||
if condn.where then
|
||||
fn = fn(condn.where)
|
||||
end
|
||||
local qfun = fn(data)
|
||||
if context then
|
||||
-- [specifying context for condition] @context is a list of tables which are
|
||||
-- 'injected'into the condition's custom context
|
||||
append(context,_G)
|
||||
local lookup = {}
|
||||
utils.setfenv(qfun,lookup)
|
||||
setmetatable(lookup,{
|
||||
__index = function(tbl,key)
|
||||
-- _G.print(tbl,key)
|
||||
for k,t in ipairs(context) do
|
||||
if t[key] then return t[key] end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
return qfun
|
||||
end
|
||||
|
||||
|
||||
DataMT.select = data.query
|
||||
DataMT.select_row = function(d,condn,context)
|
||||
return data.query(d,condn,context,true)
|
||||
end
|
||||
|
||||
--- Filter input using a query.
|
||||
-- @string Q a query string
|
||||
-- @param infile filename or file-like object
|
||||
-- @param outfile filename or file-like object
|
||||
-- @bool dont_fail true if you want to return an error, not just fail
|
||||
function data.filter (Q,infile,outfile,dont_fail)
|
||||
local d = data.read(infile or 'stdin')
|
||||
local out = open_file(outfile or 'stdout')
|
||||
local iter,err = d:select(Q)
|
||||
local delim = d.delim
|
||||
if not iter then
|
||||
err = 'error: '..err
|
||||
if dont_fail then
|
||||
return nil,err
|
||||
else
|
||||
utils.quit(1,err)
|
||||
end
|
||||
end
|
||||
while true do
|
||||
local res = {iter()}
|
||||
if #res == 0 then break end
|
||||
out:write(concat(res,delim),'\n')
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
|
527
Documentation/compiler/pl/dir.lua
Normal file
527
Documentation/compiler/pl/dir.lua
Normal file
|
@ -0,0 +1,527 @@
|
|||
--- Listing files in directories and creating/removing directory paths.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.path`
|
||||
--
|
||||
-- Soft Dependencies: `alien`, `ffi` (either are used on Windows for copying/moving files)
|
||||
-- @module pl.dir
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local path = require 'pl.path'
|
||||
local is_windows = path.is_windows
|
||||
local ldir = path.dir
|
||||
local mkdir = path.mkdir
|
||||
local rmdir = path.rmdir
|
||||
local sub = string.sub
|
||||
local os,pcall,ipairs,pairs,require,setmetatable = os,pcall,ipairs,pairs,require,setmetatable
|
||||
local remove = os.remove
|
||||
local append = table.insert
|
||||
local assert_arg,assert_string,raise = utils.assert_arg,utils.assert_string,utils.raise
|
||||
|
||||
local exists, isdir = path.exists, path.isdir
|
||||
local sep = path.sep
|
||||
|
||||
local dir = {}
|
||||
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local function assert_dir (n,val)
|
||||
assert_arg(n,val,'string',path.isdir,'not a directory',4)
|
||||
end
|
||||
|
||||
local function filemask(mask)
|
||||
mask = utils.escape(path.normcase(mask))
|
||||
return '^'..mask:gsub('%%%*','.*'):gsub('%%%?','.')..'$'
|
||||
end
|
||||
|
||||
--- Test whether a file name matches a shell pattern.
|
||||
-- Both parameters are case-normalized if operating system is
|
||||
-- case-insensitive.
|
||||
-- @string filename A file name.
|
||||
-- @string pattern A shell pattern. The only special characters are
|
||||
-- `'*'` and `'?'`: `'*'` matches any sequence of characters and
|
||||
-- `'?'` matches any single character.
|
||||
-- @treturn bool
|
||||
-- @raise dir and mask must be strings
|
||||
function dir.fnmatch(filename,pattern)
|
||||
assert_string(1,filename)
|
||||
assert_string(2,pattern)
|
||||
return path.normcase(filename):find(filemask(pattern)) ~= nil
|
||||
end
|
||||
|
||||
--- Return a list of all file names within an array which match a pattern.
|
||||
-- @tab filenames An array containing file names.
|
||||
-- @string pattern A shell pattern (see `fnmatch`).
|
||||
-- @treturn List(string) List of matching file names.
|
||||
-- @raise dir and mask must be strings
|
||||
function dir.filter(filenames,pattern)
|
||||
assert_arg(1,filenames,'table')
|
||||
assert_string(2,pattern)
|
||||
local res = {}
|
||||
local mask = filemask(pattern)
|
||||
for i,f in ipairs(filenames) do
|
||||
if path.normcase(f):find(mask) then append(res,f) end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
local function _listfiles(dirname,filemode,match)
|
||||
local res = {}
|
||||
local check = utils.choose(filemode,path.isfile,path.isdir)
|
||||
if not dirname then dirname = '.' end
|
||||
for f in ldir(dirname) do
|
||||
if f ~= '.' and f ~= '..' then
|
||||
local p = path.join(dirname,f)
|
||||
if check(p) and (not match or match(f)) then
|
||||
append(res,p)
|
||||
end
|
||||
end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- return a list of all files in a directory which match a shell pattern.
|
||||
-- @string[opt='.'] dirname A directory.
|
||||
-- @string[opt] mask A shell pattern (see `fnmatch`). If not given, all files are returned.
|
||||
-- @treturn {string} list of files
|
||||
-- @raise dirname and mask must be strings
|
||||
function dir.getfiles(dirname,mask)
|
||||
dirname = dirname or '.'
|
||||
assert_dir(1,dirname)
|
||||
if mask then assert_string(2,mask) end
|
||||
local match
|
||||
if mask then
|
||||
mask = filemask(mask)
|
||||
match = function(f)
|
||||
return path.normcase(f):find(mask)
|
||||
end
|
||||
end
|
||||
return _listfiles(dirname,true,match)
|
||||
end
|
||||
|
||||
--- return a list of all subdirectories of the directory.
|
||||
-- @string[opt='.'] dirname A directory.
|
||||
-- @treturn {string} a list of directories
|
||||
-- @raise dir must be a valid directory
|
||||
function dir.getdirectories(dirname)
|
||||
dirname = dirname or '.'
|
||||
assert_dir(1,dirname)
|
||||
return _listfiles(dirname,false)
|
||||
end
|
||||
|
||||
local alien,ffi,ffi_checked,CopyFile,MoveFile,GetLastError,win32_errors,cmd_tmpfile
|
||||
|
||||
local function execute_command(cmd,parms)
|
||||
if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end
|
||||
local err = path.is_windows and ' > ' or ' 2> '
|
||||
cmd = cmd..' '..parms..err..utils.quote_arg(cmd_tmpfile)
|
||||
local ret = utils.execute(cmd)
|
||||
if not ret then
|
||||
local err = (utils.readfile(cmd_tmpfile):gsub('\n(.*)',''))
|
||||
remove(cmd_tmpfile)
|
||||
return false,err
|
||||
else
|
||||
remove(cmd_tmpfile)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function find_ffi_copyfile ()
|
||||
if not ffi_checked then
|
||||
ffi_checked = true
|
||||
local res
|
||||
res,alien = pcall(require,'alien')
|
||||
if not res then
|
||||
alien = nil
|
||||
res, ffi = pcall(require,'ffi')
|
||||
end
|
||||
if not res then
|
||||
ffi = nil
|
||||
return
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
if alien then
|
||||
-- register the Win32 CopyFile and MoveFile functions
|
||||
local kernel = alien.load('kernel32.dll')
|
||||
CopyFile = kernel.CopyFileA
|
||||
CopyFile:types{'string','string','int',ret='int',abi='stdcall'}
|
||||
MoveFile = kernel.MoveFileA
|
||||
MoveFile:types{'string','string',ret='int',abi='stdcall'}
|
||||
GetLastError = kernel.GetLastError
|
||||
GetLastError:types{ret ='int', abi='stdcall'}
|
||||
elseif ffi then
|
||||
ffi.cdef [[
|
||||
int CopyFileA(const char *src, const char *dest, int iovr);
|
||||
int MoveFileA(const char *src, const char *dest);
|
||||
int GetLastError();
|
||||
]]
|
||||
CopyFile = ffi.C.CopyFileA
|
||||
MoveFile = ffi.C.MoveFileA
|
||||
GetLastError = ffi.C.GetLastError
|
||||
end
|
||||
win32_errors = {
|
||||
ERROR_FILE_NOT_FOUND = 2,
|
||||
ERROR_PATH_NOT_FOUND = 3,
|
||||
ERROR_ACCESS_DENIED = 5,
|
||||
ERROR_WRITE_PROTECT = 19,
|
||||
ERROR_BAD_UNIT = 20,
|
||||
ERROR_NOT_READY = 21,
|
||||
ERROR_WRITE_FAULT = 29,
|
||||
ERROR_READ_FAULT = 30,
|
||||
ERROR_SHARING_VIOLATION = 32,
|
||||
ERROR_LOCK_VIOLATION = 33,
|
||||
ERROR_HANDLE_DISK_FULL = 39,
|
||||
ERROR_BAD_NETPATH = 53,
|
||||
ERROR_NETWORK_BUSY = 54,
|
||||
ERROR_DEV_NOT_EXIST = 55,
|
||||
ERROR_FILE_EXISTS = 80,
|
||||
ERROR_OPEN_FAILED = 110,
|
||||
ERROR_INVALID_NAME = 123,
|
||||
ERROR_BAD_PATHNAME = 161,
|
||||
ERROR_ALREADY_EXISTS = 183,
|
||||
}
|
||||
end
|
||||
|
||||
local function two_arguments (f1,f2)
|
||||
return utils.quote_arg(f1)..' '..utils.quote_arg(f2)
|
||||
end
|
||||
|
||||
local function file_op (is_copy,src,dest,flag)
|
||||
if flag == 1 and path.exists(dest) then
|
||||
return false,"cannot overwrite destination"
|
||||
end
|
||||
if is_windows then
|
||||
-- if we haven't tried to load Alien/LuaJIT FFI before, then do so
|
||||
find_ffi_copyfile()
|
||||
-- fallback if there's no Alien, just use DOS commands *shudder*
|
||||
-- 'rename' involves a copy and then deleting the source.
|
||||
if not CopyFile then
|
||||
if path.is_windows then
|
||||
src = src:gsub("/","\\")
|
||||
dest = dest:gsub("/","\\")
|
||||
end
|
||||
local res, err = execute_command('copy',two_arguments(src,dest))
|
||||
if not res then return false,err end
|
||||
if not is_copy then
|
||||
return execute_command('del',utils.quote_arg(src))
|
||||
end
|
||||
return true
|
||||
else
|
||||
if path.isdir(dest) then
|
||||
dest = path.join(dest,path.basename(src))
|
||||
end
|
||||
local ret
|
||||
if is_copy then ret = CopyFile(src,dest,flag)
|
||||
else ret = MoveFile(src,dest) end
|
||||
if ret == 0 then
|
||||
local err = GetLastError()
|
||||
for name,value in pairs(win32_errors) do
|
||||
if value == err then return false,name end
|
||||
end
|
||||
return false,"Error #"..err
|
||||
else return true
|
||||
end
|
||||
end
|
||||
else -- for Unix, just use cp for now
|
||||
return execute_command(is_copy and 'cp' or 'mv',
|
||||
two_arguments(src,dest))
|
||||
end
|
||||
end
|
||||
|
||||
--- copy a file.
|
||||
-- @string src source file
|
||||
-- @string dest destination file or directory
|
||||
-- @bool flag true if you want to force the copy (default)
|
||||
-- @treturn bool operation succeeded
|
||||
-- @raise src and dest must be strings
|
||||
function dir.copyfile (src,dest,flag)
|
||||
assert_string(1,src)
|
||||
assert_string(2,dest)
|
||||
flag = flag==nil or flag
|
||||
return file_op(true,src,dest,flag and 0 or 1)
|
||||
end
|
||||
|
||||
--- move a file.
|
||||
-- @string src source file
|
||||
-- @string dest destination file or directory
|
||||
-- @treturn bool operation succeeded
|
||||
-- @raise src and dest must be strings
|
||||
function dir.movefile (src,dest)
|
||||
assert_string(1,src)
|
||||
assert_string(2,dest)
|
||||
return file_op(false,src,dest,0)
|
||||
end
|
||||
|
||||
local function _dirfiles(dirname,attrib)
|
||||
local dirs = {}
|
||||
local files = {}
|
||||
for f in ldir(dirname) do
|
||||
if f ~= '.' and f ~= '..' then
|
||||
local p = path.join(dirname,f)
|
||||
local mode = attrib(p,'mode')
|
||||
if mode=='directory' then
|
||||
append(dirs,f)
|
||||
else
|
||||
append(files,f)
|
||||
end
|
||||
end
|
||||
end
|
||||
return makelist(dirs), makelist(files)
|
||||
end
|
||||
|
||||
|
||||
--- return an iterator which walks through a directory tree starting at root.
|
||||
-- The iterator returns (root,dirs,files)
|
||||
-- Note that dirs and files are lists of names (i.e. you must say path.join(root,d)
|
||||
-- to get the actual full path)
|
||||
-- If bottom_up is false (or not present), then the entries at the current level are returned
|
||||
-- before we go deeper. This means that you can modify the returned list of directories before
|
||||
-- continuing.
|
||||
-- This is a clone of os.walk from the Python libraries.
|
||||
-- @string root A starting directory
|
||||
-- @bool bottom_up False if we start listing entries immediately.
|
||||
-- @bool follow_links follow symbolic links
|
||||
-- @return an iterator returning root,dirs,files
|
||||
-- @raise root must be a directory
|
||||
function dir.walk(root,bottom_up,follow_links)
|
||||
assert_dir(1,root)
|
||||
local attrib
|
||||
if path.is_windows or not follow_links then
|
||||
attrib = path.attrib
|
||||
else
|
||||
attrib = path.link_attrib
|
||||
end
|
||||
|
||||
local to_scan = { root }
|
||||
local to_return = {}
|
||||
local iter = function()
|
||||
while #to_scan > 0 do
|
||||
local current_root = table.remove(to_scan)
|
||||
local dirs,files = _dirfiles(current_root, attrib)
|
||||
for _, d in ipairs(dirs) do
|
||||
table.insert(to_scan, current_root..path.sep..d)
|
||||
end
|
||||
if not bottom_up then
|
||||
return current_root, dirs, files
|
||||
else
|
||||
table.insert(to_return, { current_root, dirs, files })
|
||||
end
|
||||
end
|
||||
if #to_return > 0 then
|
||||
return utils.unpack(table.remove(to_return))
|
||||
end
|
||||
end
|
||||
|
||||
return iter
|
||||
end
|
||||
|
||||
--- remove a whole directory tree.
|
||||
-- Symlinks in the tree will be deleted without following them.
|
||||
-- @string fullpath A directory path (must be an actual directory, not a symlink)
|
||||
-- @return true or nil
|
||||
-- @return error if failed
|
||||
-- @raise fullpath must be a string
|
||||
function dir.rmtree(fullpath)
|
||||
assert_dir(1,fullpath)
|
||||
if path.islink(fullpath) then return false,'will not follow symlink' end
|
||||
for root,dirs,files in dir.walk(fullpath,true) do
|
||||
if path.islink(root) then
|
||||
-- sub dir is a link, remove link, do not follow
|
||||
if is_windows then
|
||||
-- Windows requires using "rmdir". Deleting the link like a file
|
||||
-- will instead delete all files from the target directory!!
|
||||
local res, err = rmdir(root)
|
||||
if not res then return nil,err .. ": " .. root end
|
||||
else
|
||||
local res, err = remove(root)
|
||||
if not res then return nil,err .. ": " .. root end
|
||||
end
|
||||
else
|
||||
for i,f in ipairs(files) do
|
||||
local res, err = remove(path.join(root,f))
|
||||
if not res then return nil,err .. ": " .. path.join(root,f) end
|
||||
end
|
||||
local res, err = rmdir(root)
|
||||
if not res then return nil,err .. ": " .. root end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local dirpat
|
||||
if path.is_windows then
|
||||
dirpat = '(.+)\\[^\\]+$'
|
||||
else
|
||||
dirpat = '(.+)/[^/]+$'
|
||||
end
|
||||
|
||||
local _makepath
|
||||
function _makepath(p)
|
||||
-- windows root drive case
|
||||
if p:find '^%a:[\\]*$' then
|
||||
return true
|
||||
end
|
||||
if not path.isdir(p) then
|
||||
local subp = p:match(dirpat)
|
||||
if subp then
|
||||
local ok, err = _makepath(subp)
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
return mkdir(p)
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- create a directory path.
|
||||
-- This will create subdirectories as necessary!
|
||||
-- @string p A directory path
|
||||
-- @return true on success, nil + errormsg on failure
|
||||
-- @raise failure to create
|
||||
function dir.makepath (p)
|
||||
assert_string(1,p)
|
||||
if path.is_windows then
|
||||
p = p:gsub("/", "\\")
|
||||
end
|
||||
return _makepath(path.abspath(p))
|
||||
end
|
||||
end
|
||||
|
||||
--- clone a directory tree. Will always try to create a new directory structure
|
||||
-- if necessary.
|
||||
-- @string path1 the base path of the source tree
|
||||
-- @string path2 the new base path for the destination
|
||||
-- @func file_fun an optional function to apply on all files
|
||||
-- @bool verbose an optional boolean to control the verbosity of the output.
|
||||
-- It can also be a logging function that behaves like print()
|
||||
-- @return true, or nil
|
||||
-- @return error message, or list of failed directory creations
|
||||
-- @return list of failed file operations
|
||||
-- @raise path1 and path2 must be strings
|
||||
-- @usage clonetree('.','../backup',copyfile)
|
||||
function dir.clonetree (path1,path2,file_fun,verbose)
|
||||
assert_string(1,path1)
|
||||
assert_string(2,path2)
|
||||
if verbose == true then verbose = print end
|
||||
local abspath,normcase,isdir,join = path.abspath,path.normcase,path.isdir,path.join
|
||||
local faildirs,failfiles = {},{}
|
||||
if not isdir(path1) then return raise 'source is not a valid directory' end
|
||||
path1 = abspath(normcase(path1))
|
||||
path2 = abspath(normcase(path2))
|
||||
if verbose then verbose('normalized:',path1,path2) end
|
||||
-- particularly NB that the new path isn't fully contained in the old path
|
||||
if path1 == path2 then return raise "paths are the same" end
|
||||
local _,i2 = path2:find(path1,1,true)
|
||||
if i2 == #path1 and path2:sub(i2+1,i2+1) == path.sep then
|
||||
return raise 'destination is a subdirectory of the source'
|
||||
end
|
||||
local cp = path.common_prefix (path1,path2)
|
||||
local idx = #cp
|
||||
if idx == 0 then -- no common path, but watch out for Windows paths!
|
||||
if path1:sub(2,2) == ':' then idx = 3 end
|
||||
end
|
||||
for root,dirs,files in dir.walk(path1) do
|
||||
local opath = path2..root:sub(idx)
|
||||
if verbose then verbose('paths:',opath,root) end
|
||||
if not isdir(opath) then
|
||||
local ret = dir.makepath(opath)
|
||||
if not ret then append(faildirs,opath) end
|
||||
if verbose then verbose('creating:',opath,ret) end
|
||||
end
|
||||
if file_fun then
|
||||
for i,f in ipairs(files) do
|
||||
local p1 = join(root,f)
|
||||
local p2 = join(opath,f)
|
||||
local ret = file_fun(p1,p2)
|
||||
if not ret then append(failfiles,p2) end
|
||||
if verbose then
|
||||
verbose('files:',p1,p2,ret)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true,faildirs,failfiles
|
||||
end
|
||||
|
||||
|
||||
-- each entry of the stack is an array with three items:
|
||||
-- 1. the name of the directory
|
||||
-- 2. the lfs iterator function
|
||||
-- 3. the lfs iterator userdata
|
||||
local function treeiter(iterstack)
|
||||
local diriter = iterstack[#iterstack]
|
||||
if not diriter then
|
||||
return -- done
|
||||
end
|
||||
|
||||
local dirname = diriter[1]
|
||||
local entry = diriter[2](diriter[3])
|
||||
if not entry then
|
||||
table.remove(iterstack)
|
||||
return treeiter(iterstack) -- tail-call to try next
|
||||
end
|
||||
|
||||
if entry ~= "." and entry ~= ".." then
|
||||
entry = dirname .. sep .. entry
|
||||
if exists(entry) then -- Just in case a symlink is broken.
|
||||
local is_dir = isdir(entry)
|
||||
if is_dir then
|
||||
table.insert(iterstack, { entry, ldir(entry) })
|
||||
end
|
||||
return entry, is_dir
|
||||
end
|
||||
end
|
||||
|
||||
return treeiter(iterstack) -- tail-call to try next
|
||||
end
|
||||
|
||||
|
||||
--- return an iterator over all entries in a directory tree
|
||||
-- @string d a directory
|
||||
-- @return an iterator giving pathname and mode (true for dir, false otherwise)
|
||||
-- @raise d must be a non-empty string
|
||||
function dir.dirtree( d )
|
||||
assert( d and d ~= "", "directory parameter is missing or empty" )
|
||||
|
||||
local last = sub ( d, -1 )
|
||||
if last == sep or last == '/' then
|
||||
d = sub( d, 1, -2 )
|
||||
end
|
||||
|
||||
local iterstack = { {d, ldir(d)} }
|
||||
|
||||
return treeiter, iterstack
|
||||
end
|
||||
|
||||
|
||||
--- Recursively returns all the file starting at 'path'. It can optionally take a shell pattern and
|
||||
-- only returns files that match 'shell_pattern'. If a pattern is given it will do a case insensitive search.
|
||||
-- @string[opt='.'] start_path A directory.
|
||||
-- @string[opt='*'] shell_pattern A shell pattern (see `fnmatch`).
|
||||
-- @treturn List(string) containing all the files found recursively starting at 'path' and filtered by 'shell_pattern'.
|
||||
-- @raise start_path must be a directory
|
||||
function dir.getallfiles( start_path, shell_pattern )
|
||||
start_path = start_path or '.'
|
||||
assert_dir(1,start_path)
|
||||
shell_pattern = shell_pattern or "*"
|
||||
|
||||
local files = {}
|
||||
local normcase = path.normcase
|
||||
for filename, mode in dir.dirtree( start_path ) do
|
||||
if not mode then
|
||||
local mask = filemask( shell_pattern )
|
||||
if normcase(filename):find( mask ) then
|
||||
files[#files + 1] = filename
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return makelist(files)
|
||||
end
|
||||
|
||||
return dir
|
55
Documentation/compiler/pl/file.lua
Normal file
55
Documentation/compiler/pl/file.lua
Normal file
|
@ -0,0 +1,55 @@
|
|||
--- File manipulation functions: reading, writing, moving and copying.
|
||||
--
|
||||
-- This module wraps a number of functions from other modules into a
|
||||
-- file related module for convenience.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.dir`, `pl.path`
|
||||
-- @module pl.file
|
||||
local os = os
|
||||
local utils = require 'pl.utils'
|
||||
local dir = require 'pl.dir'
|
||||
local path = require 'pl.path'
|
||||
|
||||
local file = {}
|
||||
|
||||
--- return the contents of a file as a string.
|
||||
-- This function is a copy of `utils.readfile`.
|
||||
-- @function file.read
|
||||
file.read = utils.readfile
|
||||
|
||||
--- write a string to a file.
|
||||
-- This function is a copy of `utils.writefile`.
|
||||
-- @function file.write
|
||||
file.write = utils.writefile
|
||||
|
||||
--- copy a file.
|
||||
-- This function is a copy of `dir.copyfile`.
|
||||
-- @function file.copy
|
||||
file.copy = dir.copyfile
|
||||
|
||||
--- move a file.
|
||||
-- This function is a copy of `dir.movefile`.
|
||||
-- @function file.move
|
||||
file.move = dir.movefile
|
||||
|
||||
--- Return the time of last access as the number of seconds since the epoch.
|
||||
-- This function is a copy of `path.getatime`.
|
||||
-- @function file.access_time
|
||||
file.access_time = path.getatime
|
||||
|
||||
---Return when the file was created.
|
||||
-- This function is a copy of `path.getctime`.
|
||||
-- @function file.creation_time
|
||||
file.creation_time = path.getctime
|
||||
|
||||
--- Return the time of last modification.
|
||||
-- This function is a copy of `path.getmtime`.
|
||||
-- @function file.modified_time
|
||||
file.modified_time = path.getmtime
|
||||
|
||||
--- Delete a file.
|
||||
-- This function is a copy of `os.remove`.
|
||||
-- @function file.delete
|
||||
file.delete = os.remove
|
||||
|
||||
return file
|
393
Documentation/compiler/pl/func.lua
Normal file
393
Documentation/compiler/pl/func.lua
Normal file
|
@ -0,0 +1,393 @@
|
|||
--- Functional helpers like composition, binding and placeholder expressions.
|
||||
-- Placeholder expressions are useful for short anonymous functions, and were
|
||||
-- inspired by the Boost Lambda library.
|
||||
--
|
||||
-- > utils.import 'pl.func'
|
||||
-- > ls = List{10,20,30}
|
||||
-- > = ls:map(_1+1)
|
||||
-- {11,21,31}
|
||||
--
|
||||
-- They can also be used to _bind_ particular arguments of a function.
|
||||
--
|
||||
-- > p = bind(print,'start>',_0)
|
||||
-- > p(10,20,30)
|
||||
-- > start> 10 20 30
|
||||
--
|
||||
-- See @{07-functional.md.Creating_Functions_from_Functions|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`
|
||||
-- @module pl.func
|
||||
local type,setmetatable,getmetatable,rawset = type,setmetatable,getmetatable,rawset
|
||||
local concat,append = table.concat,table.insert
|
||||
local tostring = tostring
|
||||
local utils = require 'pl.utils'
|
||||
local pairs,rawget,unpack,pack = pairs,rawget,utils.unpack,utils.pack
|
||||
local tablex = require 'pl.tablex'
|
||||
local map = tablex.map
|
||||
local _DEBUG = rawget(_G,'_DEBUG')
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
local func = {}
|
||||
|
||||
-- metatable for Placeholder Expressions (PE)
|
||||
local _PEMT = {}
|
||||
|
||||
local function P (t)
|
||||
setmetatable(t,_PEMT)
|
||||
return t
|
||||
end
|
||||
|
||||
func.PE = P
|
||||
|
||||
local function isPE (obj)
|
||||
return getmetatable(obj) == _PEMT
|
||||
end
|
||||
|
||||
func.isPE = isPE
|
||||
|
||||
-- construct a placeholder variable (e.g _1 and _2)
|
||||
local function PH (idx)
|
||||
return P {op='X',repr='_'..idx, index=idx}
|
||||
end
|
||||
|
||||
-- construct a constant placeholder variable (e.g _C1 and _C2)
|
||||
local function CPH (idx)
|
||||
return P {op='X',repr='_C'..idx, index=idx}
|
||||
end
|
||||
|
||||
func._1,func._2,func._3,func._4,func._5 = PH(1),PH(2),PH(3),PH(4),PH(5)
|
||||
func._0 = P{op='X',repr='...',index=0}
|
||||
|
||||
function func.Var (name)
|
||||
local ls = utils.split(name,'[%s,]+')
|
||||
local res = {}
|
||||
for i = 1, #ls do
|
||||
append(res,P{op='X',repr=ls[i],index=0})
|
||||
end
|
||||
return unpack(res)
|
||||
end
|
||||
|
||||
function func._ (value)
|
||||
return P{op='X',repr=value,index='wrap'}
|
||||
end
|
||||
|
||||
local repr
|
||||
|
||||
func.Nil = func.Var 'nil'
|
||||
|
||||
function _PEMT.__index(obj,key)
|
||||
return P{op='[]',obj,key}
|
||||
end
|
||||
|
||||
function _PEMT.__call(fun,...)
|
||||
return P{op='()',fun,...}
|
||||
end
|
||||
|
||||
function _PEMT.__tostring (e)
|
||||
return repr(e)
|
||||
end
|
||||
|
||||
function _PEMT.__unm(arg)
|
||||
return P{op='unm',arg}
|
||||
end
|
||||
|
||||
function func.Not (arg)
|
||||
return P{op='not',arg}
|
||||
end
|
||||
|
||||
function func.Len (arg)
|
||||
return P{op='#',arg}
|
||||
end
|
||||
|
||||
|
||||
local function binreg(context,t)
|
||||
for name,op in pairs(t) do
|
||||
rawset(context,name,function(x,y)
|
||||
return P{op=op,x,y}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local function import_name (name,fun,context)
|
||||
rawset(context,name,function(...)
|
||||
return P{op='()',fun,...}
|
||||
end)
|
||||
end
|
||||
|
||||
local imported_functions = {}
|
||||
|
||||
local function is_global_table (n)
|
||||
return type(_G[n]) == 'table'
|
||||
end
|
||||
|
||||
--- wrap a table of functions. This makes them available for use in
|
||||
-- placeholder expressions.
|
||||
-- @string tname a table name
|
||||
-- @tab context context to put results, defaults to environment of caller
|
||||
function func.import(tname,context)
|
||||
assert_arg(1,tname,'string',is_global_table,'arg# 1: not a name of a global table')
|
||||
local t = _G[tname]
|
||||
context = context or _G
|
||||
for name,fun in pairs(t) do
|
||||
import_name(name,fun,context)
|
||||
imported_functions[fun] = name
|
||||
end
|
||||
end
|
||||
|
||||
--- register a function for use in placeholder expressions.
|
||||
-- @func fun a function
|
||||
-- @string[opt] name an optional name
|
||||
-- @return a placeholder functiond
|
||||
function func.register (fun,name)
|
||||
assert_arg(1,fun,'function')
|
||||
if name then
|
||||
assert_arg(2,name,'string')
|
||||
imported_functions[fun] = name
|
||||
end
|
||||
return function(...)
|
||||
return P{op='()',fun,...}
|
||||
end
|
||||
end
|
||||
|
||||
function func.lookup_imported_name (fun)
|
||||
return imported_functions[fun]
|
||||
end
|
||||
|
||||
local function _arg(...) return ... end
|
||||
|
||||
function func.Args (...)
|
||||
return P{op='()',_arg,...}
|
||||
end
|
||||
|
||||
-- binary operators with their precedences (see Lua manual)
|
||||
-- precedences might be incremented by one before use depending on
|
||||
-- left- or right-associativity, space them out
|
||||
local binary_operators = {
|
||||
['or'] = 0,
|
||||
['and'] = 2,
|
||||
['=='] = 4, ['~='] = 4, ['<'] = 4, ['>'] = 4, ['<='] = 4, ['>='] = 4,
|
||||
['..'] = 6,
|
||||
['+'] = 8, ['-'] = 8,
|
||||
['*'] = 10, ['/'] = 10, ['%'] = 10,
|
||||
['^'] = 14
|
||||
}
|
||||
|
||||
-- unary operators with their precedences
|
||||
local unary_operators = {
|
||||
['not'] = 12, ['#'] = 12, ['unm'] = 12
|
||||
}
|
||||
|
||||
-- comparisons (as prefix functions)
|
||||
binreg (func,{And='and',Or='or',Eq='==',Lt='<',Gt='>',Le='<=',Ge='>='})
|
||||
|
||||
-- standard binary operators (as metamethods)
|
||||
binreg (_PEMT,{__add='+',__sub='-',__mul='*',__div='/',__mod='%',__pow='^',__concat='..'})
|
||||
|
||||
binreg (_PEMT,{__eq='=='})
|
||||
|
||||
--- all elements of a table except the first.
|
||||
-- @tab ls a list-like table.
|
||||
function func.tail (ls)
|
||||
assert_arg(1,ls,'table')
|
||||
local res = {}
|
||||
for i = 2,#ls do
|
||||
append(res,ls[i])
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- create a string representation of a placeholder expression.
|
||||
-- @param e a placeholder expression
|
||||
-- @param lastpred not used
|
||||
function repr (e,lastpred)
|
||||
local tail = func.tail
|
||||
if isPE(e) then
|
||||
local pred = binary_operators[e.op] or unary_operators[e.op]
|
||||
if pred then
|
||||
-- binary or unary operator
|
||||
local s
|
||||
if binary_operators[e.op] then
|
||||
local left_pred = pred
|
||||
local right_pred = pred
|
||||
if e.op == '..' or e.op == '^' then
|
||||
left_pred = left_pred + 1
|
||||
else
|
||||
right_pred = right_pred + 1
|
||||
end
|
||||
local left_arg = repr(e[1], left_pred)
|
||||
local right_arg = repr(e[2], right_pred)
|
||||
s = left_arg..' '..e.op..' '..right_arg
|
||||
else
|
||||
local op = e.op == 'unm' and '-' or e.op
|
||||
s = op..' '..repr(e[1], pred)
|
||||
end
|
||||
if lastpred and lastpred > pred then
|
||||
s = '('..s..')'
|
||||
end
|
||||
return s
|
||||
else -- either postfix, or a placeholder
|
||||
local ls = map(repr,e)
|
||||
if e.op == '[]' then
|
||||
return ls[1]..'['..ls[2]..']'
|
||||
elseif e.op == '()' then
|
||||
local fn
|
||||
if ls[1] ~= nil then -- was _args, undeclared!
|
||||
fn = ls[1]
|
||||
else
|
||||
fn = ''
|
||||
end
|
||||
return fn..'('..concat(tail(ls),',')..')'
|
||||
else
|
||||
return e.repr
|
||||
end
|
||||
end
|
||||
elseif type(e) == 'string' then
|
||||
return '"'..e..'"'
|
||||
elseif type(e) == 'function' then
|
||||
local name = func.lookup_imported_name(e)
|
||||
if name then return name else return tostring(e) end
|
||||
else
|
||||
return tostring(e) --should not really get here!
|
||||
end
|
||||
end
|
||||
func.repr = repr
|
||||
|
||||
-- collect all the non-PE values in this PE into vlist, and replace each occurence
|
||||
-- with a constant PH (_C1, etc). Return the maximum placeholder index found.
|
||||
local collect_values
|
||||
function collect_values (e,vlist)
|
||||
if isPE(e) then
|
||||
if e.op ~= 'X' then
|
||||
local m = 0
|
||||
for i = 1,#e do
|
||||
local subx = e[i]
|
||||
local pe = isPE(subx)
|
||||
if pe then
|
||||
if subx.op == 'X' and subx.index == 'wrap' then
|
||||
subx = subx.repr
|
||||
pe = false
|
||||
else
|
||||
m = math.max(m,collect_values(subx,vlist))
|
||||
end
|
||||
end
|
||||
if not pe then
|
||||
append(vlist,subx)
|
||||
e[i] = CPH(#vlist)
|
||||
end
|
||||
end
|
||||
return m
|
||||
else -- was a placeholder, it has an index...
|
||||
return e.index
|
||||
end
|
||||
else -- plain value has no placeholder dependence
|
||||
return 0
|
||||
end
|
||||
end
|
||||
func.collect_values = collect_values
|
||||
|
||||
--- instantiate a PE into an actual function. First we find the largest placeholder used,
|
||||
-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace
|
||||
-- any non-PE values from the PE, and build up a constant binding list.
|
||||
-- Finally, the expression can be compiled, and e.__PE_function is set.
|
||||
-- @param e a placeholder expression
|
||||
-- @return a function
|
||||
function func.instantiate (e)
|
||||
local consts,values,parms = {},{},{}
|
||||
local rep, err, fun
|
||||
local n = func.collect_values(e,values)
|
||||
for i = 1,#values do
|
||||
append(consts,'_C'..i)
|
||||
if _DEBUG then print(i,values[i]) end
|
||||
end
|
||||
for i =1,n do
|
||||
append(parms,'_'..i)
|
||||
end
|
||||
consts = concat(consts,',')
|
||||
parms = concat(parms,',')
|
||||
rep = repr(e)
|
||||
local fstr = ('return function(%s) return function(%s) return %s end end'):format(consts,parms,rep)
|
||||
if _DEBUG then print(fstr) end
|
||||
fun,err = utils.load(fstr,'fun')
|
||||
if not fun then return nil,err end
|
||||
fun = fun() -- get wrapper
|
||||
fun = fun(unpack(values)) -- call wrapper (values could be empty)
|
||||
e.__PE_function = fun
|
||||
return fun
|
||||
end
|
||||
|
||||
--- instantiate a PE unless it has already been done.
|
||||
-- @param e a placeholder expression
|
||||
-- @return the function
|
||||
function func.I(e)
|
||||
if rawget(e,'__PE_function') then
|
||||
return e.__PE_function
|
||||
else return func.instantiate(e)
|
||||
end
|
||||
end
|
||||
|
||||
utils.add_function_factory(_PEMT,func.I)
|
||||
|
||||
--- bind the first parameter of the function to a value.
|
||||
-- @function func.bind1
|
||||
-- @func fn a function of one or more arguments
|
||||
-- @param p a value
|
||||
-- @return a function of one less argument
|
||||
-- @usage (bind1(math.max,10))(20) == math.max(10,20)
|
||||
func.bind1 = utils.bind1
|
||||
func.curry = func.bind1
|
||||
|
||||
--- create a function which chains two functions.
|
||||
-- @func f a function of at least one argument
|
||||
-- @func g a function of at least one argument
|
||||
-- @return a function
|
||||
-- @usage printf = compose(io.write,string.format)
|
||||
function func.compose (f,g)
|
||||
return function(...) return f(g(...)) end
|
||||
end
|
||||
|
||||
--- bind the arguments of a function to given values.
|
||||
-- `bind(fn,v,_2)` is equivalent to `bind1(fn,v)`.
|
||||
-- @func fn a function of at least one argument
|
||||
-- @param ... values or placeholder variables
|
||||
-- @return a function
|
||||
-- @usage (bind(f,_1,a))(b) == f(a,b)
|
||||
-- @usage (bind(f,_2,_1))(a,b) == f(b,a)
|
||||
function func.bind(fn,...)
|
||||
local args = pack(...)
|
||||
local holders,parms,bvalues,values = {},{},{'fn'},{}
|
||||
local nv,maxplace,varargs = 1,0,false
|
||||
for i = 1,args.n do
|
||||
local a = args[i]
|
||||
if isPE(a) and a.op == 'X' then
|
||||
append(holders,a.repr)
|
||||
maxplace = math.max(maxplace,a.index)
|
||||
if a.index == 0 then varargs = true end
|
||||
else
|
||||
local v = '_v'..nv
|
||||
append(bvalues,v)
|
||||
append(holders,v)
|
||||
append(values,a)
|
||||
nv = nv + 1
|
||||
end
|
||||
end
|
||||
for np = 1,maxplace do
|
||||
append(parms,'_'..np)
|
||||
end
|
||||
if varargs then append(parms,'...') end
|
||||
bvalues = concat(bvalues,',')
|
||||
parms = concat(parms,',')
|
||||
holders = concat(holders,',')
|
||||
local fstr = ([[
|
||||
return function (%s)
|
||||
return function(%s) return fn(%s) end
|
||||
end
|
||||
]]):format(bvalues,parms,holders)
|
||||
if _DEBUG then print(fstr) end
|
||||
local res = utils.load(fstr)
|
||||
res = res()
|
||||
return res(fn,unpack(values))
|
||||
end
|
||||
|
||||
return func
|
||||
|
||||
|
91
Documentation/compiler/pl/import_into.lua
Normal file
91
Documentation/compiler/pl/import_into.lua
Normal file
|
@ -0,0 +1,91 @@
|
|||
--------------
|
||||
-- PL loader, for loading all PL libraries, only on demand.
|
||||
-- Whenever a module is implicitly accesssed, the table will have the module automatically injected.
|
||||
-- (e.g. `_ENV.tablex`)
|
||||
-- then that module is dynamically loaded. The submodules are all brought into
|
||||
-- the table that is provided as the argument, or returned in a new table.
|
||||
-- If a table is provided, that table's metatable is clobbered, but the values are not.
|
||||
-- This module returns a single function, which is passed the environment.
|
||||
-- If this is `true`, then return a 'shadow table' as the module
|
||||
-- See @{01-introduction.md.To_Inject_or_not_to_Inject_|the Guide}
|
||||
|
||||
-- @module pl.import_into
|
||||
|
||||
return function(env)
|
||||
local mod
|
||||
if env == true then
|
||||
mod = {}
|
||||
env = {}
|
||||
end
|
||||
local env = env or {}
|
||||
|
||||
local modules = {
|
||||
utils = true,path=true,dir=true,tablex=true,stringio=true,sip=true,
|
||||
input=true,seq=true,lexer=true,stringx=true,
|
||||
config=true,pretty=true,data=true,func=true,text=true,
|
||||
operator=true,lapp=true,array2d=true,
|
||||
comprehension=true,xml=true,types=true,
|
||||
test = true, app = true, file = true, class = true,
|
||||
luabalanced = true, permute = true, template = true,
|
||||
url = true, compat = true,
|
||||
-- classes --
|
||||
List = true, Map = true, Set = true,
|
||||
OrderedMap = true, MultiMap = true, Date = true,
|
||||
}
|
||||
rawset(env,'utils',require 'pl.utils')
|
||||
|
||||
for name,klass in pairs(env.utils.stdmt) do
|
||||
klass.__index = function(t,key)
|
||||
return require ('pl.'..name)[key]
|
||||
end;
|
||||
end
|
||||
|
||||
-- ensure that we play nice with libraries that also attach a metatable
|
||||
-- to the global table; always forward to a custom __index if we don't
|
||||
-- match
|
||||
|
||||
local _hook,_prev_index
|
||||
local gmt = {}
|
||||
local prevenvmt = getmetatable(env)
|
||||
if prevenvmt then
|
||||
_prev_index = prevenvmt.__index
|
||||
if prevenvmt.__newindex then
|
||||
gmt.__newindex = prevenvmt.__newindex
|
||||
end
|
||||
end
|
||||
|
||||
function gmt.hook(handler)
|
||||
_hook = handler
|
||||
end
|
||||
|
||||
function gmt.__index(t,name)
|
||||
local found = modules[name]
|
||||
-- either true, or the name of the module containing this class.
|
||||
-- either way, we load the required module and make it globally available.
|
||||
if found then
|
||||
-- e..g pretty.dump causes pl.pretty to become available as 'pretty'
|
||||
rawset(env,name,require('pl.'..name))
|
||||
return env[name]
|
||||
else
|
||||
local res
|
||||
if _hook then
|
||||
res = _hook(t,name)
|
||||
if res then return res end
|
||||
end
|
||||
if _prev_index then
|
||||
return _prev_index(t,name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if mod then
|
||||
function gmt.__newindex(t,name,value)
|
||||
mod[name] = value
|
||||
rawset(t,name,value)
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(env,gmt)
|
||||
|
||||
return env,mod or env
|
||||
end
|
11
Documentation/compiler/pl/init.lua
Normal file
11
Documentation/compiler/pl/init.lua
Normal file
|
@ -0,0 +1,11 @@
|
|||
--------------
|
||||
-- Entry point for loading all PL libraries only on demand, into the global space.
|
||||
-- Requiring 'pl' means that whenever a module is implicitly accesssed
|
||||
-- (e.g. `utils.split`)
|
||||
-- then that module is dynamically loaded. The submodules are all brought into
|
||||
-- the global space.
|
||||
--Updated to use @{pl.import_into}
|
||||
-- @module pl
|
||||
require'pl.import_into'(_G)
|
||||
|
||||
if rawget(_G,'PENLIGHT_STRICT') then require 'pl.strict' end
|
171
Documentation/compiler/pl/input.lua
Normal file
171
Documentation/compiler/pl/input.lua
Normal file
|
@ -0,0 +1,171 @@
|
|||
--- Iterators for extracting words or numbers from an input source.
|
||||
--
|
||||
-- require 'pl'
|
||||
-- local total,n = seq.sum(input.numbers())
|
||||
-- print('average',total/n)
|
||||
--
|
||||
-- _source_ is defined as a string or a file-like object (i.e. has a read() method which returns the next line)
|
||||
--
|
||||
-- See @{06-data.md.Reading_Unstructured_Text_Data|here}
|
||||
--
|
||||
-- Dependencies: `pl.utils`
|
||||
-- @module pl.input
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local strmatch = string.match
|
||||
local utils = require 'pl.utils'
|
||||
local unpack = utils.unpack
|
||||
local pairs,type,tonumber = pairs,type,tonumber
|
||||
local patterns = utils.patterns
|
||||
local io = io
|
||||
|
||||
local input = {}
|
||||
|
||||
--- create an iterator over all tokens.
|
||||
-- based on allwords from PiL, 7.1
|
||||
-- @func getter any function that returns a line of text
|
||||
-- @string pattern
|
||||
-- @string[opt] fn Optionally can pass a function to process each token as it's found.
|
||||
-- @return an iterator
|
||||
function input.alltokens (getter,pattern,fn)
|
||||
local line = getter() -- current line
|
||||
local pos = 1 -- current position in the line
|
||||
return function () -- iterator function
|
||||
while line do -- repeat while there are lines
|
||||
local s, e = strfind(line, pattern, pos)
|
||||
if s then -- found a word?
|
||||
pos = e + 1 -- next position is after this token
|
||||
local res = strsub(line, s, e) -- return the token
|
||||
if fn then res = fn(res) end
|
||||
return res
|
||||
else
|
||||
line = getter() -- token not found; try next line
|
||||
pos = 1 -- restart from first position
|
||||
end
|
||||
end
|
||||
return nil -- no more lines: end of traversal
|
||||
end
|
||||
end
|
||||
local alltokens = input.alltokens
|
||||
|
||||
-- question: shd this _split_ a string containing line feeds?
|
||||
|
||||
--- create a function which grabs the next value from a source. If the source is a string, then the getter
|
||||
-- will return the string and thereafter return nil. If not specified then the source is assumed to be stdin.
|
||||
-- @param f a string or a file-like object (i.e. has a read() method which returns the next line)
|
||||
-- @return a getter function
|
||||
function input.create_getter(f)
|
||||
if f then
|
||||
if type(f) == 'string' then
|
||||
local ls = utils.split(f,'\n')
|
||||
local i,n = 0,#ls
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > n then return nil end
|
||||
return ls[i]
|
||||
end
|
||||
else
|
||||
-- anything that supports the read() method!
|
||||
if not f.read then error('not a file-like object') end
|
||||
return function() return f:read() end
|
||||
end
|
||||
else
|
||||
return io.read -- i.e. just read from stdin
|
||||
end
|
||||
end
|
||||
|
||||
--- generate a sequence of numbers from a source.
|
||||
-- @param f A source
|
||||
-- @return An iterator
|
||||
function input.numbers(f)
|
||||
return alltokens(input.create_getter(f),
|
||||
'('..patterns.FLOAT..')',tonumber)
|
||||
end
|
||||
|
||||
--- generate a sequence of words from a source.
|
||||
-- @param f A source
|
||||
-- @return An iterator
|
||||
function input.words(f)
|
||||
return alltokens(input.create_getter(f),"%w+")
|
||||
end
|
||||
|
||||
local function apply_tonumber (no_fail,...)
|
||||
local args = {...}
|
||||
for i = 1,#args do
|
||||
local n = tonumber(args[i])
|
||||
if n == nil then
|
||||
if not no_fail then return nil,args[i] end
|
||||
else
|
||||
args[i] = n
|
||||
end
|
||||
end
|
||||
return args
|
||||
end
|
||||
|
||||
--- parse an input source into fields.
|
||||
-- By default, will fail if it cannot convert a field to a number.
|
||||
-- @param ids a list of field indices, or a maximum field index
|
||||
-- @string delim delimiter to parse fields (default space)
|
||||
-- @param f a source @see create_getter
|
||||
-- @tab opts option table, `{no_fail=true}`
|
||||
-- @return an iterator with the field values
|
||||
-- @usage for x,y in fields {2,3} do print(x,y) end -- 2nd and 3rd fields from stdin
|
||||
function input.fields (ids,delim,f,opts)
|
||||
local sep
|
||||
local s
|
||||
local getter = input.create_getter(f)
|
||||
local no_fail = opts and opts.no_fail
|
||||
local no_convert = opts and opts.no_convert
|
||||
if not delim or delim == ' ' then
|
||||
delim = '%s'
|
||||
sep = '%s+'
|
||||
s = '%s*'
|
||||
else
|
||||
sep = delim
|
||||
s = ''
|
||||
end
|
||||
local max_id = 0
|
||||
if type(ids) == 'table' then
|
||||
for i,id in pairs(ids) do
|
||||
if id > max_id then max_id = id end
|
||||
end
|
||||
else
|
||||
max_id = ids
|
||||
ids = {}
|
||||
for i = 1,max_id do ids[#ids+1] = i end
|
||||
end
|
||||
local pat = '[^'..delim..']*'
|
||||
local k = 1
|
||||
for i = 1,max_id do
|
||||
if ids[k] == i then
|
||||
k = k + 1
|
||||
s = s..'('..pat..')'
|
||||
else
|
||||
s = s..pat
|
||||
end
|
||||
if i < max_id then
|
||||
s = s..sep
|
||||
end
|
||||
end
|
||||
local linecount = 1
|
||||
return function()
|
||||
local line,results,err
|
||||
repeat
|
||||
line = getter()
|
||||
linecount = linecount + 1
|
||||
if not line then return nil end
|
||||
if no_convert then
|
||||
results = {strmatch(line,s)}
|
||||
else
|
||||
results,err = apply_tonumber(no_fail,strmatch(line,s))
|
||||
if not results then
|
||||
utils.quit("line "..(linecount-1)..": cannot convert '"..err.."' to number")
|
||||
end
|
||||
end
|
||||
until #results > 0
|
||||
return unpack(results)
|
||||
end
|
||||
end
|
||||
|
||||
return input
|
||||
|
458
Documentation/compiler/pl/lapp.lua
Normal file
458
Documentation/compiler/pl/lapp.lua
Normal file
|
@ -0,0 +1,458 @@
|
|||
--- Simple command-line parsing using human-readable specification.
|
||||
-- Supports GNU-style parameters.
|
||||
--
|
||||
-- lapp = require 'pl.lapp'
|
||||
-- local args = lapp [[
|
||||
-- Does some calculations
|
||||
-- -o,--offset (default 0.0) Offset to add to scaled number
|
||||
-- -s,--scale (number) Scaling factor
|
||||
-- <number> (number) Number to be scaled
|
||||
-- ]]
|
||||
--
|
||||
-- print(args.offset + args.scale * args.number)
|
||||
--
|
||||
-- Lines beginning with `'-'` are flags; there may be a short and a long name;
|
||||
-- lines beginning with `'<var>'` are arguments. Anything in parens after
|
||||
-- the flag/argument is either a default, a type name or a range constraint.
|
||||
--
|
||||
-- See @{08-additional.md.Command_line_Programs_with_Lapp|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.sip`
|
||||
-- @module pl.lapp
|
||||
|
||||
local status,sip = pcall(require,'pl.sip')
|
||||
if not status then
|
||||
sip = require 'sip'
|
||||
end
|
||||
local match = sip.match_at_start
|
||||
local append,tinsert = table.insert,table.insert
|
||||
|
||||
sip.custom_pattern('X','(%a[%w_%-]*)')
|
||||
|
||||
local function lines(s) return s:gmatch('([^\n]*)\n') end
|
||||
local function lstrip(str) return str:gsub('^%s+','') end
|
||||
local function strip(str) return lstrip(str):gsub('%s+$','') end
|
||||
local function at(s,k) return s:sub(k,k) end
|
||||
|
||||
local lapp = {}
|
||||
|
||||
local open_files,parms,aliases,parmlist,usage,script
|
||||
|
||||
lapp.callback = false -- keep Strict happy
|
||||
|
||||
local filetypes = {
|
||||
stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'},
|
||||
stderr = {io.stderr,'file-out'}
|
||||
}
|
||||
|
||||
--- controls whether to dump usage on error.
|
||||
-- Defaults to true
|
||||
lapp.show_usage_error = true
|
||||
|
||||
--- quit this script immediately.
|
||||
-- @string msg optional message
|
||||
-- @bool no_usage suppress 'usage' display
|
||||
function lapp.quit(msg,no_usage)
|
||||
if no_usage == 'throw' then
|
||||
error(msg)
|
||||
end
|
||||
if msg then
|
||||
io.stderr:write(msg..'\n\n')
|
||||
end
|
||||
if not no_usage then
|
||||
io.stderr:write(usage)
|
||||
end
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
--- print an error to stderr and quit.
|
||||
-- @string msg a message
|
||||
-- @bool no_usage suppress 'usage' display
|
||||
function lapp.error(msg,no_usage)
|
||||
if not lapp.show_usage_error then
|
||||
no_usage = true
|
||||
elseif lapp.show_usage_error == 'throw' then
|
||||
no_usage = 'throw'
|
||||
end
|
||||
lapp.quit(script..': '..msg,no_usage)
|
||||
end
|
||||
|
||||
--- open a file.
|
||||
-- This will quit on error, and keep a list of file objects for later cleanup.
|
||||
-- @string file filename
|
||||
-- @string[opt] opt same as second parameter of `io.open`
|
||||
function lapp.open (file,opt)
|
||||
local val,err = io.open(file,opt)
|
||||
if not val then lapp.error(err,true) end
|
||||
append(open_files,val)
|
||||
return val
|
||||
end
|
||||
|
||||
--- quit if the condition is false.
|
||||
-- @bool condn a condition
|
||||
-- @string msg message text
|
||||
function lapp.assert(condn,msg)
|
||||
if not condn then
|
||||
lapp.error(msg)
|
||||
end
|
||||
end
|
||||
|
||||
local function range_check(x,min,max,parm)
|
||||
lapp.assert(min <= x and max >= x,parm..' out of range')
|
||||
end
|
||||
|
||||
local function xtonumber(s)
|
||||
local val = tonumber(s)
|
||||
if not val then lapp.error("unable to convert to number: "..s) end
|
||||
return val
|
||||
end
|
||||
|
||||
local types = {}
|
||||
|
||||
local builtin_types = {string=true,number=true,['file-in']='file',['file-out']='file',boolean=true}
|
||||
|
||||
local function convert_parameter(ps,val)
|
||||
if ps.converter then
|
||||
val = ps.converter(val)
|
||||
end
|
||||
if ps.type == 'number' then
|
||||
val = xtonumber(val)
|
||||
elseif builtin_types[ps.type] == 'file' then
|
||||
val = lapp.open(val,(ps.type == 'file-in' and 'r') or 'w' )
|
||||
elseif ps.type == 'boolean' then
|
||||
return val
|
||||
end
|
||||
if ps.constraint then
|
||||
ps.constraint(val)
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
--- add a new type to Lapp. These appear in parens after the value like
|
||||
-- a range constraint, e.g. '<ival> (integer) Process PID'
|
||||
-- @string name name of type
|
||||
-- @param converter either a function to convert values, or a Lua type name.
|
||||
-- @func[opt] constraint optional function to verify values, should use lapp.error
|
||||
-- if failed.
|
||||
function lapp.add_type (name,converter,constraint)
|
||||
types[name] = {converter=converter,constraint=constraint}
|
||||
end
|
||||
|
||||
local function force_short(short)
|
||||
lapp.assert(#short==1,short..": short parameters should be one character")
|
||||
end
|
||||
|
||||
-- deducing type of variable from default value;
|
||||
local function process_default (sval,vtype)
|
||||
local val, success
|
||||
if not vtype or vtype == 'number' then
|
||||
val = tonumber(sval)
|
||||
end
|
||||
if val then -- we have a number!
|
||||
return val,'number'
|
||||
elseif filetypes[sval] then
|
||||
local ft = filetypes[sval]
|
||||
return ft[1],ft[2]
|
||||
else
|
||||
if sval == 'true' and not vtype then
|
||||
return true, 'boolean'
|
||||
end
|
||||
if sval:match '^["\']' then sval = sval:sub(2,-2) end
|
||||
|
||||
local ps = types[vtype] or {}
|
||||
ps.type = vtype
|
||||
|
||||
local show_usage_error = lapp.show_usage_error
|
||||
lapp.show_usage_error = "throw"
|
||||
success, val = pcall(convert_parameter, ps, sval)
|
||||
lapp.show_usage_error = show_usage_error
|
||||
if success then
|
||||
return val, vtype or 'string'
|
||||
end
|
||||
|
||||
return sval,vtype or 'string'
|
||||
end
|
||||
end
|
||||
|
||||
--- process a Lapp options string.
|
||||
-- Usually called as `lapp()`.
|
||||
-- @string str the options text
|
||||
-- @tparam {string} args a table of arguments (default is `_G.arg`)
|
||||
-- @return a table with parameter-value pairs
|
||||
function lapp.process_options_string(str,args)
|
||||
local results = {}
|
||||
local varargs
|
||||
local arg = args or _G.arg
|
||||
open_files = {}
|
||||
parms = {}
|
||||
aliases = {}
|
||||
parmlist = {}
|
||||
|
||||
local function check_varargs(s)
|
||||
local res,cnt = s:gsub('^%.%.%.%s*','')
|
||||
return res, (cnt > 0)
|
||||
end
|
||||
|
||||
local function set_result(ps,parm,val)
|
||||
parm = type(parm) == "string" and parm:gsub("%W", "_") or parm -- so foo-bar becomes foo_bar in Lua
|
||||
if not ps.varargs then
|
||||
results[parm] = val
|
||||
else
|
||||
if not results[parm] then
|
||||
results[parm] = { val }
|
||||
else
|
||||
append(results[parm],val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
usage = str
|
||||
|
||||
for _,a in ipairs(arg) do
|
||||
if a == "-h" or a == "--help" then
|
||||
return lapp.quit()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for line in lines(str) do
|
||||
local res = {}
|
||||
local optparm,defval,vtype,constraint,rest
|
||||
line = lstrip(line)
|
||||
local function check(str)
|
||||
return match(str,line,res)
|
||||
end
|
||||
|
||||
-- flags: either '-<short>', '-<short>,--<long>' or '--<long>'
|
||||
if check '-$v{short}, --$o{long} $' or check '-$v{short} $' or check '--$o{long} $' then
|
||||
if res.long then
|
||||
optparm = res.long:gsub('[^%w%-]','_') -- I'm not sure the $o pattern will let anything else through?
|
||||
if #res.rest == 1 then optparm = optparm .. res.rest end
|
||||
if res.short then aliases[res.short] = optparm end
|
||||
else
|
||||
optparm = res.short
|
||||
end
|
||||
if res.short and not lapp.slack then force_short(res.short) end
|
||||
res.rest, varargs = check_varargs(res.rest)
|
||||
elseif check '$<{name} $' then -- is it <parameter_name>?
|
||||
-- so <input file...> becomes input_file ...
|
||||
optparm,rest = res.name:match '([^%.]+)(.*)'
|
||||
-- follow lua legal variable names
|
||||
optparm = optparm:sub(1,1):gsub('%A','_') .. optparm:sub(2):gsub('%W', '_')
|
||||
varargs = rest == '...'
|
||||
append(parmlist,optparm)
|
||||
end
|
||||
-- this is not a pure doc line and specifies the flag/parameter type
|
||||
if res.rest then
|
||||
line = res.rest
|
||||
res = {}
|
||||
local optional
|
||||
local defval_str
|
||||
-- do we have ([optional] [<type>] [default <val>])?
|
||||
if match('$({def} $',line,res) or match('$({def}',line,res) then
|
||||
local typespec = strip(res.def)
|
||||
local ftype, rest = typespec:match('^(%S+)(.*)$')
|
||||
rest = strip(rest)
|
||||
if ftype == 'optional' then
|
||||
ftype, rest = rest:match('^(%S+)(.*)$')
|
||||
rest = strip(rest)
|
||||
optional = true
|
||||
end
|
||||
local default
|
||||
if ftype == 'default' then
|
||||
default = true
|
||||
if rest == '' then lapp.error("value must follow default") end
|
||||
else -- a type specification
|
||||
if match('$f{min}..$f{max}',ftype,res) then
|
||||
-- a numerical range like 1..10
|
||||
local min,max = res.min,res.max
|
||||
vtype = 'number'
|
||||
constraint = function(x)
|
||||
range_check(x,min,max,optparm)
|
||||
end
|
||||
elseif not ftype:match '|' then -- plain type
|
||||
vtype = ftype
|
||||
else
|
||||
-- 'enum' type is a string which must belong to
|
||||
-- one of several distinct values
|
||||
local enums = ftype
|
||||
local enump = '|' .. enums .. '|'
|
||||
vtype = 'string'
|
||||
constraint = function(s)
|
||||
lapp.assert(enump:find('|'..s..'|', 1, true),
|
||||
"value '"..s.."' not in "..enums
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
res.rest = rest
|
||||
typespec = res.rest
|
||||
-- optional 'default value' clause. Type is inferred as
|
||||
-- 'string' or 'number' if there's no explicit type
|
||||
if default or match('default $r{rest}',typespec,res) then
|
||||
defval_str = res.rest
|
||||
defval,vtype = process_default(res.rest,vtype)
|
||||
end
|
||||
else -- must be a plain flag, no extra parameter required
|
||||
defval = false
|
||||
vtype = 'boolean'
|
||||
end
|
||||
local ps = {
|
||||
type = vtype,
|
||||
defval = defval,
|
||||
defval_str = defval_str,
|
||||
required = defval == nil and not optional,
|
||||
comment = res.rest or optparm,
|
||||
constraint = constraint,
|
||||
varargs = varargs
|
||||
}
|
||||
varargs = nil
|
||||
if types[vtype] then
|
||||
local converter = types[vtype].converter
|
||||
if type(converter) == 'string' then
|
||||
ps.type = converter
|
||||
else
|
||||
ps.converter = converter
|
||||
end
|
||||
ps.constraint = types[vtype].constraint
|
||||
elseif not builtin_types[vtype] and vtype then
|
||||
lapp.error(vtype.." is unknown type")
|
||||
end
|
||||
parms[optparm] = ps
|
||||
end
|
||||
end
|
||||
-- cool, we have our parms, let's parse the command line args
|
||||
local iparm = 1
|
||||
local iextra = 1
|
||||
local i = 1
|
||||
local parm,ps,val
|
||||
local end_of_flags = false
|
||||
|
||||
local function check_parm (parm)
|
||||
local eqi = parm:find '[=:]'
|
||||
if eqi then
|
||||
tinsert(arg,i+1,parm:sub(eqi+1))
|
||||
parm = parm:sub(1,eqi-1)
|
||||
end
|
||||
return parm,eqi
|
||||
end
|
||||
|
||||
local function is_flag (parm)
|
||||
return parms[aliases[parm] or parm]
|
||||
end
|
||||
|
||||
while i <= #arg do
|
||||
local theArg = arg[i]
|
||||
local res = {}
|
||||
-- after '--' we don't parse args and they end up in
|
||||
-- the array part of the result (args[1] etc)
|
||||
if theArg == '--' then
|
||||
end_of_flags = true
|
||||
iparm = #parmlist + 1
|
||||
i = i + 1
|
||||
theArg = arg[i]
|
||||
if not theArg then
|
||||
break
|
||||
end
|
||||
end
|
||||
-- look for a flag, -<short flags> or --<long flag>
|
||||
if not end_of_flags and (match('--$S{long}',theArg,res) or match('-$S{short}',theArg,res)) then
|
||||
if res.long then -- long option
|
||||
parm = check_parm(res.long)
|
||||
elseif #res.short == 1 or is_flag(res.short) then
|
||||
parm = res.short
|
||||
else
|
||||
local parmstr,eq = check_parm(res.short)
|
||||
if not eq then
|
||||
parm = at(parmstr,1)
|
||||
local flag = is_flag(parm)
|
||||
if flag and flag.type ~= 'boolean' then
|
||||
--if isdigit(at(parmstr,2)) then
|
||||
-- a short option followed by a digit is an exception (for AW;))
|
||||
-- push ahead into the arg array
|
||||
tinsert(arg,i+1,parmstr:sub(2))
|
||||
else
|
||||
-- push multiple flags into the arg array!
|
||||
for k = 2,#parmstr do
|
||||
tinsert(arg,i+k-1,'-'..at(parmstr,k))
|
||||
end
|
||||
end
|
||||
else
|
||||
parm = parmstr
|
||||
end
|
||||
end
|
||||
if aliases[parm] then parm = aliases[parm] end
|
||||
if not parms[parm] and (parm == 'h' or parm == 'help') then
|
||||
lapp.quit()
|
||||
end
|
||||
else -- a parameter
|
||||
parm = parmlist[iparm]
|
||||
if not parm then
|
||||
-- extra unnamed parameters are indexed starting at 1
|
||||
parm = iextra
|
||||
ps = { type = 'string' }
|
||||
parms[parm] = ps
|
||||
iextra = iextra + 1
|
||||
else
|
||||
ps = parms[parm]
|
||||
end
|
||||
if not ps.varargs then
|
||||
iparm = iparm + 1
|
||||
end
|
||||
val = theArg
|
||||
end
|
||||
ps = parms[parm]
|
||||
if not ps then lapp.error("unrecognized parameter: "..parm) end
|
||||
if ps.type ~= 'boolean' then -- we need a value! This should follow
|
||||
if not val then
|
||||
i = i + 1
|
||||
val = arg[i]
|
||||
theArg = val
|
||||
end
|
||||
lapp.assert(val,parm.." was expecting a value")
|
||||
else -- toggle boolean flags (usually false -> true)
|
||||
val = not ps.defval
|
||||
end
|
||||
ps.used = true
|
||||
val = convert_parameter(ps,val)
|
||||
set_result(ps,parm,val)
|
||||
if builtin_types[ps.type] == 'file' then
|
||||
set_result(ps,parm..'_name',theArg)
|
||||
end
|
||||
if lapp.callback then
|
||||
lapp.callback(parm,theArg,res)
|
||||
end
|
||||
i = i + 1
|
||||
val = nil
|
||||
end
|
||||
-- check unused parms, set defaults and check if any required parameters were missed
|
||||
for parm,ps in pairs(parms) do
|
||||
if not ps.used then
|
||||
if ps.required then lapp.error("missing required parameter: "..parm) end
|
||||
set_result(ps,parm,ps.defval)
|
||||
if builtin_types[ps.type] == "file" then
|
||||
set_result(ps, parm .. "_name", ps.defval_str)
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
if arg then
|
||||
script = arg[0]
|
||||
script = script or rawget(_G,"LAPP_SCRIPT") or "unknown"
|
||||
-- strip dir and extension to get current script name
|
||||
script = script:gsub('.+[\\/]',''):gsub('%.%a+$','')
|
||||
else
|
||||
script = "inter"
|
||||
end
|
||||
|
||||
|
||||
setmetatable(lapp, {
|
||||
__call = function(tbl,str,args) return lapp.process_options_string(str,args) end,
|
||||
})
|
||||
|
||||
|
||||
return lapp
|
||||
|
||||
|
515
Documentation/compiler/pl/lexer.lua
Normal file
515
Documentation/compiler/pl/lexer.lua
Normal file
|
@ -0,0 +1,515 @@
|
|||
--- 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
|
||||
--
|
||||
-- See the Guide for further @{06-data.md.Lexical_Scanning|discussion}
|
||||
-- @module pl.lexer
|
||||
|
||||
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 NUMBER1a = '^[%+%-]?%d*%.%d+[eE][%+%-]?%d+'
|
||||
local NUMBER2 = '^[%+%-]?%d+%.?%d*'
|
||||
local NUMBER2a = '^[%+%-]?%d*%.%d+'
|
||||
local NUMBER3 = '^0x[%da-fA-F]+'
|
||||
local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+'
|
||||
local NUMBER4a = '^%d*%.%d+[eE][%+%-]?%d+'
|
||||
local NUMBER5 = '^%d+%.?%d*'
|
||||
local NUMBER5a = '^%d*%.%d+'
|
||||
local IDEN = '^[%a_][%w_]*'
|
||||
local WSPACE = '^%s+'
|
||||
local STRING1 = "^(['\"])%1" -- empty string
|
||||
local STRING2 = [[^(['"])(\*)%2%1]]
|
||||
local STRING3 = [[^(['"]).-[^\](\*)%2%1]]
|
||||
local CHAR1 = "^''"
|
||||
local CHAR2 = [[^'(\*)%1']]
|
||||
local CHAR3 = [[^'.-[^\](\*)%1']]
|
||||
local PREPRO = '^#.-[^\\]\n'
|
||||
|
||||
local plain_matches,lua_matches,cpp_matches,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
|
||||
|
||||
-- long Lua strings need extra work to get rid of the quotes
|
||||
local function sdump_l(tok,options,findres)
|
||||
if options and options.string then
|
||||
local quotelen = 3
|
||||
if findres[3] then
|
||||
quotelen = quotelen + findres[3]:len()
|
||||
end
|
||||
tok = tok:sub(quotelen, -quotelen)
|
||||
if tok:sub(1, 1) == "\n" then
|
||||
tok = tok:sub(2)
|
||||
end
|
||||
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
|
||||
|
||||
--- create a plain token iterator from a string or file-like object.
|
||||
-- @tparam string|file s a string or a file-like object with `:read()` method returning lines.
|
||||
-- @tab matches an optional match table - array of token descriptions.
|
||||
-- A token is described by a `{pattern, action}` pair, where `pattern` should match
|
||||
-- token body and `action` is a function called when a token of described type is found.
|
||||
-- @tab[opt] filter a table of token types to exclude, by default `{space=true}`
|
||||
-- @tab[opt] 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)
|
||||
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},
|
||||
{NUMBER1a,ndump},
|
||||
{NUMBER2,ndump},
|
||||
{NUMBER2a,ndump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{STRING3,sdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
matches = plain_matches
|
||||
end
|
||||
|
||||
local line_nr = 0
|
||||
local next_line = file and file:read()
|
||||
local sz = file and 0 or #s
|
||||
local idx = 1
|
||||
|
||||
local tlist_i
|
||||
local tlist
|
||||
|
||||
local first_hit = true
|
||||
|
||||
local function iter(res)
|
||||
local tp = type(res)
|
||||
|
||||
if tlist then -- returning the inserted token list
|
||||
local cur = tlist[tlist_i]
|
||||
if cur then
|
||||
tlist_i = tlist_i + 1
|
||||
return cur[1], cur[2]
|
||||
else
|
||||
tlist = nil
|
||||
end
|
||||
end
|
||||
|
||||
if tp == 'string' then -- search up to some special pattern
|
||||
local i1,i2 = strfind(s,res,idx)
|
||||
if i1 then
|
||||
local tok = strsub(s,i1,i2)
|
||||
idx = i2 + 1
|
||||
return '', tok
|
||||
else
|
||||
idx = sz + 1
|
||||
return '', ''
|
||||
end
|
||||
|
||||
elseif tp == 'table' then -- insert a token list
|
||||
tlist_i = 1
|
||||
tlist = res
|
||||
return '', ''
|
||||
|
||||
elseif tp ~= 'nil' then -- return position
|
||||
return line_nr, idx
|
||||
|
||||
else -- look for next token
|
||||
if first_hit then
|
||||
if not file then line_nr = 1 end
|
||||
first_hit = false
|
||||
end
|
||||
|
||||
if idx > sz then
|
||||
if file then
|
||||
if not next_line then
|
||||
return -- past the end of file, done
|
||||
end
|
||||
s = next_line
|
||||
line_nr = line_nr + 1
|
||||
next_line = file:read()
|
||||
if next_line then
|
||||
s = s .. '\n'
|
||||
end
|
||||
idx, sz = 1, #s
|
||||
else
|
||||
return -- past the end of input, done
|
||||
end
|
||||
end
|
||||
|
||||
for _,m in ipairs(matches) do
|
||||
local pat = m[1]
|
||||
local fun = m[2]
|
||||
local findres = {strfind(s,pat,idx)}
|
||||
local i1, i2 = findres[1], findres[2]
|
||||
if i1 then
|
||||
local tok = strsub(s,i1,i2)
|
||||
idx = i2 + 1
|
||||
local ret1, ret2
|
||||
if not (filter and filter[fun]) then
|
||||
lexer.finished = idx > sz
|
||||
ret1, ret2 = fun(tok, options, findres)
|
||||
end
|
||||
if not file and tok:find("\n") then
|
||||
-- Update line number.
|
||||
local _, newlines = tok:gsub("\n", {})
|
||||
line_nr = line_nr + newlines
|
||||
end
|
||||
if ret1 then
|
||||
return ret1, ret2 -- found a match
|
||||
else
|
||||
return iter() -- tail-call to try again
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return iter
|
||||
end
|
||||
|
||||
local function isstring (s)
|
||||
return type(s) == 'string'
|
||||
end
|
||||
|
||||
--- insert tokens into a stream.
|
||||
-- @param tok a token stream
|
||||
-- @param a1 a string is the type, a table is a token list and
|
||||
-- a function is assumed to be a token-like iterator (returns type & value)
|
||||
-- @string a2 a string is the value
|
||||
function lexer.insert (tok,a1,a2)
|
||||
if not a1 then return end
|
||||
local ts
|
||||
if isstring(a1) and isstring(a2) then
|
||||
ts = {{a1,a2}}
|
||||
elseif type(a1) == 'function' then
|
||||
ts = {}
|
||||
for t,v in a1() do
|
||||
append(ts,{t,v})
|
||||
end
|
||||
else
|
||||
ts = a1
|
||||
end
|
||||
tok(ts)
|
||||
end
|
||||
|
||||
--- get everything in a stream upto a newline.
|
||||
-- @param tok a token stream
|
||||
-- @return a string
|
||||
function lexer.getline (tok)
|
||||
local _,v = tok('.-\n')
|
||||
return v
|
||||
end
|
||||
|
||||
--- get current line number.
|
||||
-- @param tok a token stream
|
||||
-- @return the line number.
|
||||
-- if the input source is a file-like object,
|
||||
-- also return the column.
|
||||
function lexer.lineno (tok)
|
||||
return tok(0)
|
||||
end
|
||||
|
||||
--- get the rest of the stream.
|
||||
-- @param tok a token stream
|
||||
-- @return a string
|
||||
function lexer.getrest (tok)
|
||||
local _,v = tok('.+')
|
||||
return v
|
||||
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.
|
||||
-- @string s the string
|
||||
-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}`
|
||||
-- @tab[opt] 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},
|
||||
{NUMBER4a,ndump},
|
||||
{NUMBER5,ndump},
|
||||
{NUMBER5a,ndump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{STRING3,sdump},
|
||||
{'^%-%-%[(=*)%[.-%]%1%]',cdump},
|
||||
{'^%-%-.-\n',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.
|
||||
-- @string s the string
|
||||
-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}`
|
||||
-- @tab[opt] 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)
|
||||
filter = filter or {space=true,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},
|
||||
{NUMBER4a,ndump},
|
||||
{NUMBER5,ndump},
|
||||
{NUMBER5a,ndump},
|
||||
{CHAR1,chdump},
|
||||
{CHAR2,chdump},
|
||||
{CHAR3,chdump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{STRING3,sdump},
|
||||
{'^//.-\n',cdump},
|
||||
{'^/%*.-%*/',cdump},
|
||||
{'^==',tdump},
|
||||
{'^!=',tdump},
|
||||
{'^<=',tdump},
|
||||
{'^>=',tdump},
|
||||
{'^->',tdump},
|
||||
{'^&&',tdump},
|
||||
{'^||',tdump},
|
||||
{'^%+%+',tdump},
|
||||
{'^%-%-',tdump},
|
||||
{'^%+=',tdump},
|
||||
{'^%-=',tdump},
|
||||
{'^%*=',tdump},
|
||||
{'^/=',tdump},
|
||||
{'^|=',tdump},
|
||||
{'^%^=',tdump},
|
||||
{'^::',tdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
return lexer.scan(s,cpp_matches,filter,options)
|
||||
end
|
||||
|
||||
--- get a list of parameters separated by a delimiter from a stream.
|
||||
-- @param tok the token stream
|
||||
-- @string[opt=')'] endtoken end of list. Can be '\n'
|
||||
-- @string[opt=','] delim separator
|
||||
-- @return a list of token lists.
|
||||
function lexer.get_separated_list(tok,endtoken,delim)
|
||||
endtoken = endtoken or ')'
|
||||
delim = delim or ','
|
||||
local parm_values = {}
|
||||
local level = 1 -- used to count ( and )
|
||||
local tl = {}
|
||||
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 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
|
||||
append(parm_values,tl)
|
||||
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 token == delim and level == 1 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)
|
||||
local t,v = tok()
|
||||
while t == 'space' do
|
||||
t,v = tok()
|
||||
end
|
||||
return t,v
|
||||
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
|
||||
-- @string expected_type the token type
|
||||
-- @bool 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
|
264
Documentation/compiler/pl/luabalanced.lua
Normal file
264
Documentation/compiler/pl/luabalanced.lua
Normal file
|
@ -0,0 +1,264 @@
|
|||
--- Extract delimited Lua sequences from strings.
|
||||
-- Inspired by Damian Conway's Text::Balanced in Perl. <br/>
|
||||
-- <ul>
|
||||
-- <li>[1] <a href="http://lua-users.org/wiki/LuaBalanced">Lua Wiki Page</a></li>
|
||||
-- <li>[2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm</li>
|
||||
-- </ul> <br/>
|
||||
-- <pre class=example>
|
||||
-- local lb = require "pl.luabalanced"
|
||||
-- --Extract Lua expression starting at position 4.
|
||||
-- print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4))
|
||||
-- --> x^2 + x > 5 16
|
||||
-- --Extract Lua string starting at (default) position 1.
|
||||
-- print(lb.match_string([["test\"123" .. "more"]]))
|
||||
-- --> "test\"123" 12
|
||||
-- </pre>
|
||||
-- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license).
|
||||
-- @class module
|
||||
-- @name pl.luabalanced
|
||||
|
||||
local M = {}
|
||||
|
||||
local assert = assert
|
||||
|
||||
-- map opening brace <-> closing brace.
|
||||
local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' }
|
||||
local begins = {}; for k,v in pairs(ends) do begins[v] = k end
|
||||
|
||||
|
||||
-- Match Lua string in string <s> starting at position <pos>.
|
||||
-- Returns <string>, <posnew>, where <string> is the matched
|
||||
-- string (or nil on no match) and <posnew> is the character
|
||||
-- following the match (or <pos> on no match).
|
||||
-- Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc.
|
||||
local function match_string(s, pos)
|
||||
pos = pos or 1
|
||||
local posa = pos
|
||||
local c = s:sub(pos,pos)
|
||||
if c == '"' or c == "'" then
|
||||
pos = pos + 1
|
||||
while 1 do
|
||||
pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error')
|
||||
if s:sub(pos,pos) == c then
|
||||
local part = s:sub(posa, pos)
|
||||
return part, pos + 1
|
||||
else
|
||||
pos = pos + 2
|
||||
end
|
||||
end
|
||||
else
|
||||
local sc = s:match("^%[(=*)%[", pos)
|
||||
if sc then
|
||||
local _; _, pos = s:find("%]" .. sc .. "%]", pos)
|
||||
assert(pos)
|
||||
local part = s:sub(posa, pos)
|
||||
return part, pos + 1
|
||||
else
|
||||
return nil, pos
|
||||
end
|
||||
end
|
||||
end
|
||||
M.match_string = match_string
|
||||
|
||||
|
||||
-- Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]",
|
||||
-- [=[...]=], etc.
|
||||
-- Function interface is similar to match_string.
|
||||
local function match_bracketed(s, pos)
|
||||
pos = pos or 1
|
||||
local posa = pos
|
||||
local ca = s:sub(pos,pos)
|
||||
if not ends[ca] then
|
||||
return nil, pos
|
||||
end
|
||||
local stack = {}
|
||||
while 1 do
|
||||
pos = s:find('[%(%{%[%)%}%]\"\']', pos)
|
||||
assert(pos, 'syntax error: unbalanced')
|
||||
local c = s:sub(pos,pos)
|
||||
if c == '"' or c == "'" then
|
||||
local part; part, pos = match_string(s, pos)
|
||||
assert(part)
|
||||
elseif ends[c] then -- open
|
||||
local mid, posb
|
||||
if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end
|
||||
if mid then
|
||||
pos = s:match('%]' .. mid .. '%]()', posb)
|
||||
assert(pos, 'syntax error: long string not terminated')
|
||||
if #stack == 0 then
|
||||
local part = s:sub(posa, pos-1)
|
||||
return part, pos
|
||||
end
|
||||
else
|
||||
stack[#stack+1] = c
|
||||
pos = pos + 1
|
||||
end
|
||||
else -- close
|
||||
assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced')
|
||||
stack[#stack] = nil
|
||||
if #stack == 0 then
|
||||
local part = s:sub(posa, pos)
|
||||
return part, pos+1
|
||||
end
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
M.match_bracketed = match_bracketed
|
||||
|
||||
|
||||
-- Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc.
|
||||
-- Function interface is similar to match_string.
|
||||
local function match_comment(s, pos)
|
||||
pos = pos or 1
|
||||
if s:sub(pos, pos+1) ~= '--' then
|
||||
return nil, pos
|
||||
end
|
||||
pos = pos + 2
|
||||
local partt, post = match_string(s, pos)
|
||||
if partt then
|
||||
return '--' .. partt, post
|
||||
end
|
||||
local part; part, pos = s:match('^([^\n]*\n?)()', pos)
|
||||
return '--' .. part, pos
|
||||
end
|
||||
|
||||
|
||||
-- Match Lua expression, e.g. "a + b * c[e]".
|
||||
-- Function interface is similar to match_string.
|
||||
local wordop = {['and']=true, ['or']=true, ['not']=true}
|
||||
local is_compare = {['>']=true, ['<']=true, ['~']=true}
|
||||
local function match_expression(s, pos)
|
||||
pos = pos or 1
|
||||
local _
|
||||
local posa = pos
|
||||
local lastident
|
||||
local poscs, posce
|
||||
while pos do
|
||||
local c = s:sub(pos,pos)
|
||||
if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then
|
||||
local part; part, pos = match_string(s, pos)
|
||||
assert(part, 'syntax error')
|
||||
elseif c == '-' and s:sub(pos+1,pos+1) == '-' then
|
||||
-- note: handle adjacent comments in loop to properly support
|
||||
-- backtracing (poscs/posce).
|
||||
poscs = pos
|
||||
while s:sub(pos,pos+1) == '--' do
|
||||
local part; part, pos = match_comment(s, pos)
|
||||
assert(part)
|
||||
pos = s:match('^%s*()', pos)
|
||||
posce = pos
|
||||
end
|
||||
elseif c == '(' or c == '{' or c == '[' then
|
||||
_, pos = match_bracketed(s, pos)
|
||||
elseif c == '=' and s:sub(pos+1,pos+1) == '=' then
|
||||
pos = pos + 2 -- skip over two-char op containing '='
|
||||
elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then
|
||||
pos = pos + 1 -- skip over two-char op containing '='
|
||||
elseif c:match'^[%)%}%];,=]' then
|
||||
local part = s:sub(posa, pos-1)
|
||||
return part, pos
|
||||
elseif c:match'^[%w_]' then
|
||||
local newident,newpos = s:match('^([%w_]+)()', pos)
|
||||
if pos ~= posa and not wordop[newident] then -- non-first ident
|
||||
local pose = ((posce == pos) and poscs or pos) - 1
|
||||
while s:match('^%s', pose) do pose = pose - 1 end
|
||||
local ce = s:sub(pose,pose)
|
||||
if ce:match'[%)%}\'\"%]]' or
|
||||
ce:match'[%w_]' and not wordop[lastident]
|
||||
then
|
||||
local part = s:sub(posa, pos-1)
|
||||
return part, pos
|
||||
end
|
||||
end
|
||||
lastident, pos = newident, newpos
|
||||
else
|
||||
pos = pos + 1
|
||||
end
|
||||
pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos)
|
||||
end
|
||||
local part = s:sub(posa, #s)
|
||||
return part, #s+1
|
||||
end
|
||||
M.match_expression = match_expression
|
||||
|
||||
|
||||
-- Match name list (zero or more names). E.g. "a,b,c"
|
||||
-- Function interface is similar to match_string,
|
||||
-- but returns array as match.
|
||||
local function match_namelist(s, pos)
|
||||
pos = pos or 1
|
||||
local list = {}
|
||||
while 1 do
|
||||
local c = #list == 0 and '^' or '^%s*,%s*'
|
||||
local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos)
|
||||
if item then pos = post else break end
|
||||
list[#list+1] = item
|
||||
end
|
||||
return list, pos
|
||||
end
|
||||
M.match_namelist = match_namelist
|
||||
|
||||
|
||||
-- Match expression list (zero or more expressions). E.g. "a+b,b*c".
|
||||
-- Function interface is similar to match_string,
|
||||
-- but returns array as match.
|
||||
local function match_explist(s, pos)
|
||||
pos = pos or 1
|
||||
local list = {}
|
||||
while 1 do
|
||||
if #list ~= 0 then
|
||||
local post = s:match('^%s*,%s*()', pos)
|
||||
if post then pos = post else break end
|
||||
end
|
||||
local item; item, pos = match_expression(s, pos)
|
||||
assert(item, 'syntax error')
|
||||
list[#list+1] = item
|
||||
end
|
||||
return list, pos
|
||||
end
|
||||
M.match_explist = match_explist
|
||||
|
||||
|
||||
-- Replace snippets of code in Lua code string <s>
|
||||
-- using replacement function f(u,sin) --> sout.
|
||||
-- <u> is the type of snippet ('c' = comment, 's' = string,
|
||||
-- 'e' = any other code).
|
||||
-- Snippet is replaced with <sout> (unless <sout> is nil or false, in
|
||||
-- which case the original snippet is kept)
|
||||
-- This is somewhat analogous to string.gsub .
|
||||
local function gsub(s, f)
|
||||
local pos = 1
|
||||
local posa = 1
|
||||
local sret = ''
|
||||
while 1 do
|
||||
pos = s:find('[%-\'\"%[]', pos)
|
||||
if not pos then break end
|
||||
if s:match('^%-%-', pos) then
|
||||
local exp = s:sub(posa, pos-1)
|
||||
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
|
||||
local comment; comment, pos = match_comment(s, pos)
|
||||
sret = sret .. (f('c', assert(comment)) or comment)
|
||||
posa = pos
|
||||
else
|
||||
local posb = s:find('^[\'\"%[]', pos)
|
||||
local str
|
||||
if posb then str, pos = match_string(s, posb) end
|
||||
if str then
|
||||
local exp = s:sub(posa, posb-1)
|
||||
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
|
||||
sret = sret .. (f('s', str) or str)
|
||||
posa = pos
|
||||
else
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
local exp = s:sub(posa)
|
||||
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
|
||||
return sret
|
||||
end
|
||||
M.gsub = gsub
|
||||
|
||||
|
||||
return M
|
209
Documentation/compiler/pl/operator.lua
Normal file
209
Documentation/compiler/pl/operator.lua
Normal file
|
@ -0,0 +1,209 @@
|
|||
--- Lua operators available as functions.
|
||||
--
|
||||
-- (similar to the Python module of the same name)
|
||||
--
|
||||
-- There is a module field `optable` which maps the operator strings
|
||||
-- onto these functions, e.g. `operator.optable['()']==operator.call`
|
||||
--
|
||||
-- Operator strings like '>' and '{}' can be passed to most Penlight functions
|
||||
-- expecting a function argument.
|
||||
--
|
||||
-- @module pl.operator
|
||||
|
||||
local strfind = string.find
|
||||
|
||||
local operator = {}
|
||||
|
||||
--- apply function to some arguments **()**
|
||||
-- @param fn a function or callable object
|
||||
-- @param ... arguments
|
||||
function operator.call(fn,...)
|
||||
return fn(...)
|
||||
end
|
||||
|
||||
--- get the indexed value from a table **[]**
|
||||
-- @param t a table or any indexable object
|
||||
-- @param k the key
|
||||
function operator.index(t,k)
|
||||
return t[k]
|
||||
end
|
||||
|
||||
--- returns true if arguments are equal **==**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.eq(a,b)
|
||||
return a==b
|
||||
end
|
||||
|
||||
--- returns true if arguments are not equal **~=**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.neq(a,b)
|
||||
return a~=b
|
||||
end
|
||||
|
||||
--- returns true if a is less than b **<**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.lt(a,b)
|
||||
return a < b
|
||||
end
|
||||
|
||||
--- returns true if a is less or equal to b **<=**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.le(a,b)
|
||||
return a <= b
|
||||
end
|
||||
|
||||
--- returns true if a is greater than b **>**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.gt(a,b)
|
||||
return a > b
|
||||
end
|
||||
|
||||
--- returns true if a is greater or equal to b **>=**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.ge(a,b)
|
||||
return a >= b
|
||||
end
|
||||
|
||||
--- returns length of string or table **#**
|
||||
-- @param a a string or a table
|
||||
function operator.len(a)
|
||||
return #a
|
||||
end
|
||||
|
||||
--- add two values **+**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.add(a,b)
|
||||
return a+b
|
||||
end
|
||||
|
||||
--- subtract b from a **-**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.sub(a,b)
|
||||
return a-b
|
||||
end
|
||||
|
||||
--- multiply two values __*__
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.mul(a,b)
|
||||
return a*b
|
||||
end
|
||||
|
||||
--- divide first value by second **/**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.div(a,b)
|
||||
return a/b
|
||||
end
|
||||
|
||||
--- raise first to the power of second **^**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.pow(a,b)
|
||||
return a^b
|
||||
end
|
||||
|
||||
--- modulo; remainder of a divided by b **%**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.mod(a,b)
|
||||
return a%b
|
||||
end
|
||||
|
||||
--- concatenate two values (either strings or `__concat` defined) **..**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.concat(a,b)
|
||||
return a..b
|
||||
end
|
||||
|
||||
--- return the negative of a value **-**
|
||||
-- @param a value
|
||||
function operator.unm(a)
|
||||
return -a
|
||||
end
|
||||
|
||||
--- false if value evaluates as true **not**
|
||||
-- @param a value
|
||||
function operator.lnot(a)
|
||||
return not a
|
||||
end
|
||||
|
||||
--- true if both values evaluate as true **and**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.land(a,b)
|
||||
return a and b
|
||||
end
|
||||
|
||||
--- true if either value evaluate as true **or**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.lor(a,b)
|
||||
return a or b
|
||||
end
|
||||
|
||||
--- make a table from the arguments **{}**
|
||||
-- @param ... non-nil arguments
|
||||
-- @return a table
|
||||
function operator.table (...)
|
||||
return {...}
|
||||
end
|
||||
|
||||
--- match two strings **~**.
|
||||
-- uses @{string.find}
|
||||
function operator.match (a,b)
|
||||
return strfind(a,b)~=nil
|
||||
end
|
||||
|
||||
--- the null operation.
|
||||
-- @param ... arguments
|
||||
-- @return the arguments
|
||||
function operator.nop (...)
|
||||
return ...
|
||||
end
|
||||
|
||||
---- Map from operator symbol to function.
|
||||
-- Most of these map directly from operators;
|
||||
-- But note these extras
|
||||
--
|
||||
-- * __'()'__ `call`
|
||||
-- * __'[]'__ `index`
|
||||
-- * __'{}'__ `table`
|
||||
-- * __'~'__ `match`
|
||||
--
|
||||
-- @table optable
|
||||
-- @field operator
|
||||
operator.optable = {
|
||||
['+']=operator.add,
|
||||
['-']=operator.sub,
|
||||
['*']=operator.mul,
|
||||
['/']=operator.div,
|
||||
['%']=operator.mod,
|
||||
['^']=operator.pow,
|
||||
['..']=operator.concat,
|
||||
['()']=operator.call,
|
||||
['[]']=operator.index,
|
||||
['<']=operator.lt,
|
||||
['<=']=operator.le,
|
||||
['>']=operator.gt,
|
||||
['>=']=operator.ge,
|
||||
['==']=operator.eq,
|
||||
['~=']=operator.neq,
|
||||
['#']=operator.len,
|
||||
['and']=operator.land,
|
||||
['or']=operator.lor,
|
||||
['{}']=operator.table,
|
||||
['~']=operator.match,
|
||||
['']=operator.nop,
|
||||
}
|
||||
|
||||
return operator
|
572
Documentation/compiler/pl/path.lua
Normal file
572
Documentation/compiler/pl/path.lua
Normal file
|
@ -0,0 +1,572 @@
|
|||
--- Path manipulation and file queries.
|
||||
--
|
||||
-- This is modelled after Python's os.path library (10.1); see @{04-paths.md|the Guide}.
|
||||
--
|
||||
-- NOTE: the functions assume the paths being dealt with to originate
|
||||
-- from the OS the application is running on. Windows drive letters are not
|
||||
-- to be used when running on a Unix system for example. The one exception
|
||||
-- is Windows paths to allow both forward and backward slashes (since Lua
|
||||
-- also accepts those)
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `lfs`
|
||||
-- @module pl.path
|
||||
|
||||
-- imports and locals
|
||||
local _G = _G
|
||||
local sub = string.sub
|
||||
local getenv = os.getenv
|
||||
local tmpnam = os.tmpname
|
||||
local package = package
|
||||
local append, concat, remove = table.insert, table.concat, table.remove
|
||||
local utils = require 'pl.utils'
|
||||
local assert_string,raise = utils.assert_string,utils.raise
|
||||
|
||||
local res,lfs = _G.pcall(_G.require,'lfs')
|
||||
if not res then
|
||||
error("pl.path requires LuaFileSystem")
|
||||
end
|
||||
|
||||
local attrib = lfs.attributes
|
||||
local currentdir = lfs.currentdir
|
||||
local link_attrib = lfs.symlinkattributes
|
||||
|
||||
local path = {}
|
||||
|
||||
local function err_func(name, param, err, code)
|
||||
local ret = ("%s failed"):format(tostring(name))
|
||||
if param ~= nil then
|
||||
ret = ret .. (" for '%s'"):format(tostring(param))
|
||||
end
|
||||
ret = ret .. (": %s"):format(tostring(err))
|
||||
if code ~= nil then
|
||||
ret = ret .. (" (code %s)"):format(tostring(code))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Lua iterator over the entries of a given directory.
|
||||
-- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function dir
|
||||
path.dir = lfs.dir
|
||||
|
||||
--- Creates a directory.
|
||||
-- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function mkdir
|
||||
path.mkdir = function(d)
|
||||
local ok, err, code = lfs.mkdir(d)
|
||||
if not ok then
|
||||
return ok, err_func("mkdir", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Removes a directory.
|
||||
-- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function rmdir
|
||||
path.rmdir = function(d)
|
||||
local ok, err, code = lfs.rmdir(d)
|
||||
if not ok then
|
||||
return ok, err_func("rmdir", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Gets attributes.
|
||||
-- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function attrib
|
||||
path.attrib = function(d, r)
|
||||
local ok, err, code = attrib(d, r)
|
||||
if not ok then
|
||||
return ok, err_func("attrib", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Get the working directory.
|
||||
-- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function currentdir
|
||||
path.currentdir = function()
|
||||
local ok, err, code = currentdir()
|
||||
if not ok then
|
||||
return ok, err_func("currentdir", nil, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Gets symlink attributes.
|
||||
-- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function link_attrib
|
||||
path.link_attrib = function(d, r)
|
||||
local ok, err, code = link_attrib(d, r)
|
||||
if not ok then
|
||||
return ok, err_func("link_attrib", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Changes the working directory.
|
||||
-- On Windows, if a drive is specified, it also changes the current drive. If
|
||||
-- only specifying the drive, it will only switch drive, but not modify the path.
|
||||
-- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function chdir
|
||||
path.chdir = function(d)
|
||||
local ok, err, code = lfs.chdir(d)
|
||||
if not ok then
|
||||
return ok, err_func("chdir", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- is this a directory?
|
||||
-- @string P A file path
|
||||
function path.isdir(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'mode') == 'directory'
|
||||
end
|
||||
|
||||
--- is this a file?
|
||||
-- @string P A file path
|
||||
function path.isfile(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'mode') == 'file'
|
||||
end
|
||||
|
||||
-- is this a symbolic link?
|
||||
-- @string P A file path
|
||||
function path.islink(P)
|
||||
assert_string(1,P)
|
||||
if link_attrib then
|
||||
return link_attrib(P,'mode')=='link'
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- return size of a file.
|
||||
-- @string P A file path
|
||||
function path.getsize(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'size')
|
||||
end
|
||||
|
||||
--- does a path exist?
|
||||
-- @string P A file path
|
||||
-- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise
|
||||
function path.exists(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'mode') ~= nil and P
|
||||
end
|
||||
|
||||
--- Return the time of last access as the number of seconds since the epoch.
|
||||
-- @string P A file path
|
||||
function path.getatime(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'access')
|
||||
end
|
||||
|
||||
--- Return the time of last modification as the number of seconds since the epoch.
|
||||
-- @string P A file path
|
||||
function path.getmtime(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'modification')
|
||||
end
|
||||
|
||||
---Return the system's ctime as the number of seconds since the epoch.
|
||||
-- @string P A file path
|
||||
function path.getctime(P)
|
||||
assert_string(1,P)
|
||||
return path.attrib(P,'change')
|
||||
end
|
||||
|
||||
|
||||
local function at(s,i)
|
||||
return sub(s,i,i)
|
||||
end
|
||||
|
||||
path.is_windows = utils.is_windows
|
||||
|
||||
local sep, other_sep, seps
|
||||
-- constant sep is the directory separator for this platform.
|
||||
-- constant dirsep is the separator in the PATH environment variable
|
||||
if path.is_windows then
|
||||
path.sep = '\\'; other_sep = '/'
|
||||
path.dirsep = ';'
|
||||
seps = { ['/'] = true, ['\\'] = true }
|
||||
else
|
||||
path.sep = '/'
|
||||
path.dirsep = ':'
|
||||
seps = { ['/'] = true }
|
||||
end
|
||||
sep = path.sep
|
||||
|
||||
--- are we running Windows?
|
||||
-- @class field
|
||||
-- @name path.is_windows
|
||||
|
||||
--- path separator for this platform.
|
||||
-- @class field
|
||||
-- @name path.sep
|
||||
|
||||
--- separator for PATH for this platform
|
||||
-- @class field
|
||||
-- @name path.dirsep
|
||||
|
||||
--- given a path, return the directory part and a file part.
|
||||
-- if there's no directory part, the first value will be empty
|
||||
-- @string P A file path
|
||||
-- @return directory part
|
||||
-- @return file part
|
||||
-- @usage
|
||||
-- local dir, file = path.splitpath("some/dir/myfile.txt")
|
||||
-- assert(dir == "some/dir")
|
||||
-- assert(file == "myfile.txt")
|
||||
--
|
||||
-- local dir, file = path.splitpath("some/dir/")
|
||||
-- assert(dir == "some/dir")
|
||||
-- assert(file == "")
|
||||
--
|
||||
-- local dir, file = path.splitpath("some_dir")
|
||||
-- assert(dir == "")
|
||||
-- assert(file == "some_dir")
|
||||
function path.splitpath(P)
|
||||
assert_string(1,P)
|
||||
local i = #P
|
||||
local ch = at(P,i)
|
||||
while i > 0 and ch ~= sep and ch ~= other_sep do
|
||||
i = i - 1
|
||||
ch = at(P,i)
|
||||
end
|
||||
if i == 0 then
|
||||
return '',P
|
||||
else
|
||||
return sub(P,1,i-1), sub(P,i+1)
|
||||
end
|
||||
end
|
||||
|
||||
--- return an absolute path.
|
||||
-- @string P A file path
|
||||
-- @string[opt] pwd optional start path to use (default is current dir)
|
||||
function path.abspath(P,pwd)
|
||||
assert_string(1,P)
|
||||
if pwd then assert_string(2,pwd) end
|
||||
local use_pwd = pwd ~= nil
|
||||
if not use_pwd and not currentdir() then return P end
|
||||
P = P:gsub('[\\/]$','')
|
||||
pwd = pwd or currentdir()
|
||||
if not path.isabs(P) then
|
||||
P = path.join(pwd,P)
|
||||
elseif path.is_windows and not use_pwd and at(P,2) ~= ':' and at(P,2) ~= '\\' then
|
||||
P = pwd:sub(1,2)..P -- attach current drive to path like '\\fred.txt'
|
||||
end
|
||||
return path.normpath(P)
|
||||
end
|
||||
|
||||
--- given a path, return the root part and the extension part.
|
||||
-- if there's no extension part, the second value will be empty
|
||||
-- @string P A file path
|
||||
-- @treturn string root part (everything upto the "."", maybe empty)
|
||||
-- @treturn string extension part (including the ".", maybe empty)
|
||||
-- @usage
|
||||
-- local file_path, ext = path.splitext("/bonzo/dog_stuff/cat.txt")
|
||||
-- assert(file_path == "/bonzo/dog_stuff/cat")
|
||||
-- assert(ext == ".txt")
|
||||
--
|
||||
-- local file_path, ext = path.splitext("")
|
||||
-- assert(file_path == "")
|
||||
-- assert(ext == "")
|
||||
function path.splitext(P)
|
||||
assert_string(1,P)
|
||||
local i = #P
|
||||
local ch = at(P,i)
|
||||
while i > 0 and ch ~= '.' do
|
||||
if seps[ch] then
|
||||
return P,''
|
||||
end
|
||||
i = i - 1
|
||||
ch = at(P,i)
|
||||
end
|
||||
if i == 0 then
|
||||
return P,''
|
||||
else
|
||||
return sub(P,1,i-1),sub(P,i)
|
||||
end
|
||||
end
|
||||
|
||||
--- return the directory part of a path
|
||||
-- @string P A file path
|
||||
-- @treturn string everything before the last dir-separator
|
||||
-- @see splitpath
|
||||
-- @usage
|
||||
-- path.dirname("/some/path/file.txt") -- "/some/path"
|
||||
-- path.dirname("file.txt") -- "" (empty string)
|
||||
function path.dirname(P)
|
||||
assert_string(1,P)
|
||||
local p1 = path.splitpath(P)
|
||||
return p1
|
||||
end
|
||||
|
||||
--- return the file part of a path
|
||||
-- @string P A file path
|
||||
-- @treturn string
|
||||
-- @see splitpath
|
||||
-- @usage
|
||||
-- path.basename("/some/path/file.txt") -- "file.txt"
|
||||
-- path.basename("/some/path/file/") -- "" (empty string)
|
||||
function path.basename(P)
|
||||
assert_string(1,P)
|
||||
local _,p2 = path.splitpath(P)
|
||||
return p2
|
||||
end
|
||||
|
||||
--- get the extension part of a path.
|
||||
-- @string P A file path
|
||||
-- @treturn string
|
||||
-- @see splitext
|
||||
-- @usage
|
||||
-- path.extension("/some/path/file.txt") -- ".txt"
|
||||
-- path.extension("/some/path/file_txt") -- "" (empty string)
|
||||
function path.extension(P)
|
||||
assert_string(1,P)
|
||||
local _,p2 = path.splitext(P)
|
||||
return p2
|
||||
end
|
||||
|
||||
--- is this an absolute path?
|
||||
-- @string P A file path
|
||||
-- @usage
|
||||
-- path.isabs("hello/path") -- false
|
||||
-- path.isabs("/hello/path") -- true
|
||||
-- -- Windows;
|
||||
-- path.isabs("hello\path") -- false
|
||||
-- path.isabs("\hello\path") -- true
|
||||
-- path.isabs("C:\hello\path") -- true
|
||||
-- path.isabs("C:hello\path") -- false
|
||||
function path.isabs(P)
|
||||
assert_string(1,P)
|
||||
if path.is_windows and at(P,2) == ":" then
|
||||
return seps[at(P,3)] ~= nil
|
||||
end
|
||||
return seps[at(P,1)] ~= nil
|
||||
end
|
||||
|
||||
--- return the path resulting from combining the individual paths.
|
||||
-- if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following).
|
||||
-- empty elements (except the last) will be ignored.
|
||||
-- @string p1 A file path
|
||||
-- @string p2 A file path
|
||||
-- @string ... more file paths
|
||||
-- @treturn string the combined path
|
||||
-- @usage
|
||||
-- path.join("/first","second","third") -- "/first/second/third"
|
||||
-- path.join("first","second/third") -- "first/second/third"
|
||||
-- path.join("/first","/second","third") -- "/second/third"
|
||||
function path.join(p1,p2,...)
|
||||
assert_string(1,p1)
|
||||
assert_string(2,p2)
|
||||
if select('#',...) > 0 then
|
||||
local p = path.join(p1,p2)
|
||||
local args = {...}
|
||||
for i = 1,#args do
|
||||
assert_string(i,args[i])
|
||||
p = path.join(p,args[i])
|
||||
end
|
||||
return p
|
||||
end
|
||||
if path.isabs(p2) then return p2 end
|
||||
local endc = at(p1,#p1)
|
||||
if endc ~= path.sep and endc ~= other_sep and endc ~= "" then
|
||||
p1 = p1..path.sep
|
||||
end
|
||||
return p1..p2
|
||||
end
|
||||
|
||||
--- normalize the case of a pathname. On Unix, this returns the path unchanged,
|
||||
-- for Windows it converts;
|
||||
--
|
||||
-- * the path to lowercase
|
||||
-- * forward slashes to backward slashes
|
||||
-- @string P A file path
|
||||
-- @usage path.normcase("/Some/Path/File.txt")
|
||||
-- -- Windows: "\some\path\file.txt"
|
||||
-- -- Others : "/Some/Path/File.txt"
|
||||
function path.normcase(P)
|
||||
assert_string(1,P)
|
||||
if path.is_windows then
|
||||
return P:gsub('/','\\'):lower()
|
||||
else
|
||||
return P
|
||||
end
|
||||
end
|
||||
|
||||
--- normalize a path name.
|
||||
-- `A//B`, `A/./B`, and `A/foo/../B` all become `A/B`.
|
||||
--
|
||||
-- An empty path results in '.'.
|
||||
-- @string P a file path
|
||||
function path.normpath(P)
|
||||
assert_string(1,P)
|
||||
-- Split path into anchor and relative path.
|
||||
local anchor = ''
|
||||
if path.is_windows then
|
||||
if P:match '^\\\\' then -- UNC
|
||||
anchor = '\\\\'
|
||||
P = P:sub(3)
|
||||
elseif seps[at(P, 1)] then
|
||||
anchor = '\\'
|
||||
P = P:sub(2)
|
||||
elseif at(P, 2) == ':' then
|
||||
anchor = P:sub(1, 2)
|
||||
P = P:sub(3)
|
||||
if seps[at(P, 1)] then
|
||||
anchor = anchor..'\\'
|
||||
P = P:sub(2)
|
||||
end
|
||||
end
|
||||
P = P:gsub('/','\\')
|
||||
else
|
||||
-- According to POSIX, in path start '//' and '/' are distinct,
|
||||
-- but '///+' is equivalent to '/'.
|
||||
if P:match '^//' and at(P, 3) ~= '/' then
|
||||
anchor = '//'
|
||||
P = P:sub(3)
|
||||
elseif at(P, 1) == '/' then
|
||||
anchor = '/'
|
||||
P = P:match '^/*(.*)$'
|
||||
end
|
||||
end
|
||||
local parts = {}
|
||||
for part in P:gmatch('[^'..sep..']+') do
|
||||
if part == '..' then
|
||||
if #parts ~= 0 and parts[#parts] ~= '..' then
|
||||
remove(parts)
|
||||
else
|
||||
append(parts, part)
|
||||
end
|
||||
elseif part ~= '.' then
|
||||
append(parts, part)
|
||||
end
|
||||
end
|
||||
P = anchor..concat(parts, sep)
|
||||
if P == '' then P = '.' end
|
||||
return P
|
||||
end
|
||||
|
||||
--- relative path from current directory or optional start point
|
||||
-- @string P a path
|
||||
-- @string[opt] start optional start point (default current directory)
|
||||
function path.relpath (P,start)
|
||||
assert_string(1,P)
|
||||
if start then assert_string(2,start) end
|
||||
local split,min,append = utils.split, math.min, table.insert
|
||||
P = path.abspath(P,start)
|
||||
start = start or currentdir()
|
||||
local compare
|
||||
if path.is_windows then
|
||||
P = P:gsub("/","\\")
|
||||
start = start:gsub("/","\\")
|
||||
compare = function(v) return v:lower() end
|
||||
else
|
||||
compare = function(v) return v end
|
||||
end
|
||||
local startl, Pl = split(start,sep), split(P,sep)
|
||||
local n = min(#startl,#Pl)
|
||||
if path.is_windows and n > 0 and at(Pl[1],2) == ':' and Pl[1] ~= startl[1] then
|
||||
return P
|
||||
end
|
||||
local k = n+1 -- default value if this loop doesn't bail out!
|
||||
for i = 1,n do
|
||||
if compare(startl[i]) ~= compare(Pl[i]) then
|
||||
k = i
|
||||
break
|
||||
end
|
||||
end
|
||||
local rell = {}
|
||||
for i = 1, #startl-k+1 do rell[i] = '..' end
|
||||
if k <= #Pl then
|
||||
for i = k,#Pl do append(rell,Pl[i]) end
|
||||
end
|
||||
return table.concat(rell,sep)
|
||||
end
|
||||
|
||||
|
||||
--- Replace a starting '~' with the user's home directory.
|
||||
-- In windows, if HOME isn't set, then USERPROFILE is used in preference to
|
||||
-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows.
|
||||
-- @string P A file path
|
||||
function path.expanduser(P)
|
||||
assert_string(1,P)
|
||||
if at(P,1) == '~' then
|
||||
local home = getenv('HOME')
|
||||
if not home then -- has to be Windows
|
||||
home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH')
|
||||
end
|
||||
return home..sub(P,2)
|
||||
else
|
||||
return P
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Return a suitable full path to a new temporary file name.
|
||||
-- unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows)
|
||||
function path.tmpname ()
|
||||
local res = tmpnam()
|
||||
-- On Windows if Lua is compiled using MSVC14 os.tmpname
|
||||
-- already returns an absolute path within TEMP env variable directory,
|
||||
-- no need to prepend it.
|
||||
if path.is_windows and not res:find(':') then
|
||||
res = getenv('TEMP')..res
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- return the largest common prefix path of two paths.
|
||||
-- @string path1 a file path
|
||||
-- @string path2 a file path
|
||||
-- @return the common prefix (Windows: separators will be normalized, casing will be original)
|
||||
function path.common_prefix (path1,path2)
|
||||
assert_string(1,path1)
|
||||
assert_string(2,path2)
|
||||
-- get them in order!
|
||||
if #path1 > #path2 then path2,path1 = path1,path2 end
|
||||
local compare
|
||||
if path.is_windows then
|
||||
path1 = path1:gsub("/", "\\")
|
||||
path2 = path2:gsub("/", "\\")
|
||||
compare = function(v) return v:lower() end
|
||||
else
|
||||
compare = function(v) return v end
|
||||
end
|
||||
for i = 1,#path1 do
|
||||
if compare(at(path1,i)) ~= compare(at(path2,i)) then
|
||||
local cp = path1:sub(1,i-1)
|
||||
if at(path1,i-1) ~= sep then
|
||||
cp = path.dirname(cp)
|
||||
end
|
||||
return cp
|
||||
end
|
||||
end
|
||||
if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end
|
||||
return path1
|
||||
--return ''
|
||||
end
|
||||
|
||||
--- return the full path where a particular Lua module would be found.
|
||||
-- Both package.path and package.cpath is searched, so the result may
|
||||
-- either be a Lua file or a shared library.
|
||||
-- @string mod name of the module
|
||||
-- @return on success: path of module, lua or binary
|
||||
-- @return on error: nil, error string listing paths tried
|
||||
function path.package_path(mod)
|
||||
assert_string(1,mod)
|
||||
local res, err1, err2
|
||||
res, err1 = package.searchpath(mod,package.path)
|
||||
if res then return res,true end
|
||||
res, err2 = package.searchpath(mod,package.cpath)
|
||||
if res then return res,false end
|
||||
return raise ('cannot find module on path\n' .. err1 .. "\n" .. err2)
|
||||
end
|
||||
|
||||
|
||||
---- finis -----
|
||||
return path
|
196
Documentation/compiler/pl/permute.lua
Normal file
196
Documentation/compiler/pl/permute.lua
Normal file
|
@ -0,0 +1,196 @@
|
|||
--- Permutation operations.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`
|
||||
-- @module pl.permute
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local copy = tablex.deepcopy
|
||||
local append = table.insert
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
|
||||
local permute = {}
|
||||
|
||||
|
||||
--- an iterator over all order-permutations of the elements of a list.
|
||||
-- Please note that the same list is returned each time, so do not keep references!
|
||||
-- @param a list-like table
|
||||
-- @return an iterator which provides the next permutation as a list
|
||||
function permute.order_iter(a)
|
||||
assert_arg(1,a,'table')
|
||||
|
||||
local t = #a
|
||||
local stack = { 1 }
|
||||
local function iter()
|
||||
local h = #stack
|
||||
local n = t - h + 1
|
||||
|
||||
local i = stack[h]
|
||||
if i > t then
|
||||
return
|
||||
end
|
||||
|
||||
if n == 0 then
|
||||
table.remove(stack)
|
||||
h = h - 1
|
||||
|
||||
stack[h] = stack[h] + 1
|
||||
return a
|
||||
|
||||
elseif i <= n then
|
||||
|
||||
-- put i-th element as the last one
|
||||
a[n], a[i] = a[i], a[n]
|
||||
|
||||
-- generate all permutations of the other elements
|
||||
table.insert(stack, 1)
|
||||
|
||||
else
|
||||
|
||||
table.remove(stack)
|
||||
h = h - 1
|
||||
|
||||
n = n + 1
|
||||
i = stack[h]
|
||||
|
||||
-- restore i-th element
|
||||
a[n], a[i] = a[i], a[n]
|
||||
|
||||
stack[h] = stack[h] + 1
|
||||
end
|
||||
return iter() -- tail-call
|
||||
end
|
||||
|
||||
return iter
|
||||
end
|
||||
|
||||
|
||||
--- construct a table containing all the order-permutations of a list.
|
||||
-- @param a list-like table
|
||||
-- @return a table of tables
|
||||
-- @usage permute.order_table {1,2,3} --> {{2,3,1},{3,2,1},{3,1,2},{1,3,2},{2,1,3},{1,2,3}}
|
||||
function permute.order_table (a)
|
||||
assert_arg(1,a,'table')
|
||||
local res = {}
|
||||
for t in permute.iter(a) do
|
||||
append(res,copy(t))
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- an iterator over all permutations of the elements of the given lists.
|
||||
-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided (see `utils.pack`)
|
||||
-- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index
|
||||
-- @usage
|
||||
-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety
|
||||
-- local bools = utils.pack(true, false)
|
||||
-- local iter = permute.list_iter(strs, bools)
|
||||
--
|
||||
-- print(iter()) --> 1, one, true
|
||||
-- print(iter()) --> 2, nil, true
|
||||
-- print(iter()) --> 3, three, true
|
||||
-- print(iter()) --> 4, one, false
|
||||
-- print(iter()) --> 5, nil, false
|
||||
-- print(iter()) --> 6, three, false
|
||||
function permute.list_iter(...)
|
||||
local elements = {...}
|
||||
local pointers = {}
|
||||
local sizes = {}
|
||||
local size = #elements
|
||||
for i, list in ipairs(elements) do
|
||||
assert_arg(i,list,'table')
|
||||
pointers[i] = 1
|
||||
sizes[i] = list.n or #list
|
||||
end
|
||||
local count = 0
|
||||
|
||||
return function()
|
||||
if pointers[size] > sizes[size] then return end -- we're done
|
||||
count = count + 1
|
||||
local r = { n = #elements }
|
||||
local cascade_up = true
|
||||
for i = 1, size do
|
||||
r[i] = elements[i][pointers[i]]
|
||||
if cascade_up then
|
||||
pointers[i] = pointers[i] + 1
|
||||
if pointers[i] <= sizes[i] then
|
||||
-- this list is not done yet, stop cascade
|
||||
cascade_up = false
|
||||
else
|
||||
-- this list is done
|
||||
if i ~= size then
|
||||
-- reset pointer
|
||||
pointers[i] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return count, utils.unpack(r)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- construct a table containing all the permutations of a set of lists.
|
||||
-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided
|
||||
-- @return a list of lists, the sub-lists have an 'n' field for nil-safety
|
||||
-- @usage
|
||||
-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety
|
||||
-- local bools = utils.pack(true, false)
|
||||
-- local results = permute.list_table(strs, bools)
|
||||
-- -- results = {
|
||||
-- -- { "one, true, n = 2 }
|
||||
-- -- { nil, true, n = 2 },
|
||||
-- -- { "three, true, n = 2 },
|
||||
-- -- { "one, false, n = 2 },
|
||||
-- -- { nil, false, n = 2 },
|
||||
-- -- { "three", false, n = 2 },
|
||||
-- -- }
|
||||
function permute.list_table(...)
|
||||
local iter = permute.list_iter(...)
|
||||
local results = {}
|
||||
local i = 1
|
||||
while true do
|
||||
local values = utils.pack(iter())
|
||||
if values[1] == nil then return results end
|
||||
for i = 1, values.n do values[i] = values[i+1] end
|
||||
values.n = values.n - 1
|
||||
results[i] = values
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- backward compat, to be deprecated
|
||||
|
||||
--- deprecated.
|
||||
-- @param ...
|
||||
-- @see permute.order_iter
|
||||
function permute.iter(...)
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "function 'iter' was renamed to 'order_iter'",
|
||||
version_removed = "2.0.0",
|
||||
deprecated_after = "1.9.2",
|
||||
}
|
||||
|
||||
return permute.order_iter(...)
|
||||
end
|
||||
|
||||
--- deprecated.
|
||||
-- @param ...
|
||||
-- @see permute.order_iter
|
||||
function permute.table(...)
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "function 'table' was renamed to 'order_table'",
|
||||
version_removed = "2.0.0",
|
||||
deprecated_after = "1.9.2",
|
||||
}
|
||||
|
||||
return permute.order_table(...)
|
||||
end
|
||||
|
||||
return permute
|
438
Documentation/compiler/pl/pretty.lua
Normal file
438
Documentation/compiler/pl/pretty.lua
Normal file
|
@ -0,0 +1,438 @@
|
|||
--- Pretty-printing Lua tables.
|
||||
-- Also provides a sandboxed Lua table reader and
|
||||
-- a function to present large numbers in human-friendly format.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.lexer`, `pl.stringx`, `debug`
|
||||
-- @module pl.pretty
|
||||
|
||||
local append = table.insert
|
||||
local concat = table.concat
|
||||
local mfloor, mhuge = math.floor, math.huge
|
||||
local mtype = math.type
|
||||
local utils = require 'pl.utils'
|
||||
local lexer = require 'pl.lexer'
|
||||
local debug = require 'debug'
|
||||
local quote_string = require'pl.stringx'.quote_string
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
local original_tostring = tostring
|
||||
|
||||
-- Patch tostring to format numbers with better precision
|
||||
-- and to produce cross-platform results for
|
||||
-- infinite values and NaN.
|
||||
local function tostring(value)
|
||||
if type(value) ~= "number" then
|
||||
return original_tostring(value)
|
||||
elseif value ~= value then
|
||||
return "NaN"
|
||||
elseif value == mhuge then
|
||||
return "Inf"
|
||||
elseif value == -mhuge then
|
||||
return "-Inf"
|
||||
elseif (_VERSION ~= "Lua 5.3" or mtype(value) == "integer") and mfloor(value) == value then
|
||||
return ("%d"):format(value)
|
||||
else
|
||||
local res = ("%.14g"):format(value)
|
||||
if _VERSION == "Lua 5.3" and mtype(value) == "float" and not res:find("%.") then
|
||||
-- Number is internally a float but looks like an integer.
|
||||
-- Insert ".0" after first run of digits.
|
||||
res = res:gsub("%d+", "%0.0", 1)
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
local pretty = {}
|
||||
|
||||
local function save_global_env()
|
||||
local env = {}
|
||||
env.hook, env.mask, env.count = debug.gethook()
|
||||
|
||||
-- env.hook is "external hook" if is a C hook function
|
||||
if env.hook~="external hook" then
|
||||
debug.sethook()
|
||||
end
|
||||
|
||||
env.string_mt = getmetatable("")
|
||||
debug.setmetatable("", nil)
|
||||
return env
|
||||
end
|
||||
|
||||
local function restore_global_env(env)
|
||||
if env then
|
||||
debug.setmetatable("", env.string_mt)
|
||||
if env.hook~="external hook" then
|
||||
debug.sethook(env.hook, env.mask, env.count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Read a string representation of a Lua table.
|
||||
-- This function loads and runs the string as Lua code, but bails out
|
||||
-- if it contains a function definition.
|
||||
-- Loaded string is executed in an empty environment.
|
||||
-- @string s string to read in `{...}` format, possibly with some whitespace
|
||||
-- before or after the curly braces. A single line comment may be present
|
||||
-- at the beginning.
|
||||
-- @return a table in case of success.
|
||||
-- If loading the string failed, return `nil` and error message.
|
||||
-- If executing loaded string failed, return `nil` and the error it raised.
|
||||
function pretty.read(s)
|
||||
assert_arg(1,s,'string')
|
||||
if s:find '^%s*%-%-' then -- may start with a comment..
|
||||
s = s:gsub('%-%-.-\n','')
|
||||
end
|
||||
if not s:find '^%s*{' then return nil,"not a Lua table" end
|
||||
if s:find '[^\'"%w_]function[^\'"%w_]' then
|
||||
local tok = lexer.lua(s)
|
||||
for t,v in tok do
|
||||
if t == 'keyword' and v == 'function' then
|
||||
return nil,"cannot have functions in table definition"
|
||||
end
|
||||
end
|
||||
end
|
||||
s = 'return '..s
|
||||
local chunk,err = utils.load(s,'tbl','t',{})
|
||||
if not chunk then return nil,err end
|
||||
local global_env = save_global_env()
|
||||
local ok,ret = pcall(chunk)
|
||||
restore_global_env(global_env)
|
||||
if ok then return ret
|
||||
else
|
||||
return nil,ret
|
||||
end
|
||||
end
|
||||
|
||||
--- Read a Lua chunk.
|
||||
-- @string s Lua code.
|
||||
-- @tab[opt] env environment used to run the code, empty by default.
|
||||
-- @bool[opt] paranoid abort loading if any looping constructs a found in the code
|
||||
-- and disable string methods.
|
||||
-- @return the environment in case of success or `nil` and syntax or runtime error
|
||||
-- if something went wrong.
|
||||
function pretty.load (s, env, paranoid)
|
||||
env = env or {}
|
||||
if paranoid then
|
||||
local tok = lexer.lua(s)
|
||||
for t,v in tok do
|
||||
if t == 'keyword'
|
||||
and (v == 'for' or v == 'repeat' or v == 'function' or v == 'goto')
|
||||
then
|
||||
return nil,"looping not allowed"
|
||||
end
|
||||
end
|
||||
end
|
||||
local chunk,err = utils.load(s,'tbl','t',env)
|
||||
if not chunk then return nil,err end
|
||||
local global_env = paranoid and save_global_env()
|
||||
local ok,err = pcall(chunk)
|
||||
restore_global_env(global_env)
|
||||
if not ok then return nil,err end
|
||||
return env
|
||||
end
|
||||
|
||||
local function quote_if_necessary (v)
|
||||
if not v then return ''
|
||||
else
|
||||
--AAS
|
||||
if v:find ' ' then v = quote_string(v) end
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local keywords
|
||||
|
||||
local function is_identifier (s)
|
||||
return type(s) == 'string' and s:find('^[%a_][%w_]*$') and not keywords[s]
|
||||
end
|
||||
|
||||
local function quote (s)
|
||||
if type(s) == 'table' then
|
||||
return pretty.write(s,'')
|
||||
else
|
||||
--AAS
|
||||
return quote_string(s)-- ('%q'):format(tostring(s))
|
||||
end
|
||||
end
|
||||
|
||||
local function index (numkey,key)
|
||||
--AAS
|
||||
if not numkey then
|
||||
key = quote(key)
|
||||
key = key:find("^%[") and (" " .. key .. " ") or key
|
||||
end
|
||||
return '['..key..']'
|
||||
end
|
||||
|
||||
|
||||
--- Create a string representation of a Lua table.
|
||||
-- This function never fails, but may complain by returning an
|
||||
-- extra value. Normally puts out one item per line, using
|
||||
-- the provided indent; set the second parameter to an empty string
|
||||
-- if you want output on one line.
|
||||
--
|
||||
-- *NOTE:* this is NOT a serialization function, not a full blown
|
||||
-- debug function. Checkout out respectively the
|
||||
-- [serpent](https://github.com/pkulchenko/serpent)
|
||||
-- or [inspect](https://github.com/kikito/inspect.lua)
|
||||
-- Lua modules for that if you need them.
|
||||
-- @tab tbl Table to serialize to a string.
|
||||
-- @string[opt] space The indent to use.
|
||||
-- Defaults to two spaces; pass an empty string for no indentation.
|
||||
-- @bool[opt] not_clever Pass `true` for plain output, e.g `{['key']=1}`.
|
||||
-- Defaults to `false`.
|
||||
-- @return a string
|
||||
-- @return an optional error message
|
||||
function pretty.write (tbl,space,not_clever)
|
||||
if type(tbl) ~= 'table' then
|
||||
local res = tostring(tbl)
|
||||
if type(tbl) == 'string' then return quote(tbl) end
|
||||
return res, 'not a table'
|
||||
end
|
||||
if not keywords then
|
||||
keywords = lexer.get_keywords()
|
||||
end
|
||||
local set = ' = '
|
||||
if space == '' then set = '=' end
|
||||
space = space or ' '
|
||||
local lines = {}
|
||||
local line = ''
|
||||
local tables = {}
|
||||
|
||||
|
||||
local function put(s)
|
||||
if #s > 0 then
|
||||
line = line..s
|
||||
end
|
||||
end
|
||||
|
||||
local function putln (s)
|
||||
if #line > 0 then
|
||||
line = line..s
|
||||
append(lines,line)
|
||||
line = ''
|
||||
else
|
||||
append(lines,s)
|
||||
end
|
||||
end
|
||||
|
||||
local function eat_last_comma ()
|
||||
local n = #lines
|
||||
local lastch = lines[n]:sub(-1,-1)
|
||||
if lastch == ',' then
|
||||
lines[n] = lines[n]:sub(1,-2)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- safe versions for iterators since 5.3+ honors metamethods that can throw
|
||||
-- errors
|
||||
local ipairs = function(t)
|
||||
local i = 0
|
||||
local ok, v
|
||||
local getter = function() return t[i] end
|
||||
return function()
|
||||
i = i + 1
|
||||
ok, v = pcall(getter)
|
||||
if v == nil or not ok then return end
|
||||
return i, t[i]
|
||||
end
|
||||
end
|
||||
local pairs = function(t)
|
||||
local k, v, ok
|
||||
local getter = function() return next(t, k) end
|
||||
return function()
|
||||
ok, k, v = pcall(getter)
|
||||
if not ok then return end
|
||||
return k, v
|
||||
end
|
||||
end
|
||||
|
||||
local writeit
|
||||
writeit = function (t,oldindent,indent)
|
||||
local tp = type(t)
|
||||
if tp ~= 'string' and tp ~= 'table' then
|
||||
putln(quote_if_necessary(tostring(t))..',')
|
||||
elseif tp == 'string' then
|
||||
-- if t:find('\n') then
|
||||
-- putln('[[\n'..t..']],')
|
||||
-- else
|
||||
-- putln(quote(t)..',')
|
||||
-- end
|
||||
--AAS
|
||||
putln(quote_string(t) ..",")
|
||||
elseif tp == 'table' then
|
||||
if tables[t] then
|
||||
putln('<cycle>,')
|
||||
return
|
||||
end
|
||||
tables[t] = true
|
||||
local newindent = indent..space
|
||||
putln('{')
|
||||
local used = {}
|
||||
if not not_clever then
|
||||
for i,val in ipairs(t) do
|
||||
put(indent)
|
||||
writeit(val,indent,newindent)
|
||||
used[i] = true
|
||||
end
|
||||
end
|
||||
local ordered_keys = {}
|
||||
for k,v in pairs(t) do
|
||||
if type(k) ~= 'number' then
|
||||
ordered_keys[#ordered_keys + 1] = k
|
||||
end
|
||||
end
|
||||
table.sort(ordered_keys, function (a, b)
|
||||
if type(a) == type(b) then
|
||||
return tostring(a) < tostring(b)
|
||||
else
|
||||
return type(a) < type(b)
|
||||
end
|
||||
end)
|
||||
local function write_entry (key, val)
|
||||
local tkey = type(key)
|
||||
local numkey = tkey == 'number'
|
||||
if not_clever then
|
||||
key = tostring(key)
|
||||
put(indent..index(numkey,key)..set)
|
||||
writeit(val,indent,newindent)
|
||||
else
|
||||
if not numkey or not used[key] then -- non-array indices
|
||||
if tkey ~= 'string' then
|
||||
key = tostring(key)
|
||||
end
|
||||
if numkey or not is_identifier(key) then
|
||||
key = index(numkey,key)
|
||||
end
|
||||
put(indent..key..set)
|
||||
writeit(val,indent,newindent)
|
||||
end
|
||||
end
|
||||
end
|
||||
for i = 1, #ordered_keys do
|
||||
local key = ordered_keys[i]
|
||||
local val = t[key]
|
||||
write_entry(key, val)
|
||||
end
|
||||
for key,val in pairs(t) do
|
||||
if type(key) == 'number' then
|
||||
write_entry(key, val)
|
||||
end
|
||||
end
|
||||
tables[t] = nil
|
||||
eat_last_comma()
|
||||
putln(oldindent..'},')
|
||||
else
|
||||
putln(tostring(t)..',')
|
||||
end
|
||||
end
|
||||
writeit(tbl,'',space)
|
||||
eat_last_comma()
|
||||
return concat(lines,#space > 0 and '\n' or '')
|
||||
end
|
||||
|
||||
--- Dump a Lua table out to a file or stdout.
|
||||
-- @tab t The table to write to a file or stdout.
|
||||
-- @string[opt] filename File name to write too. Defaults to writing
|
||||
-- to stdout.
|
||||
function pretty.dump (t, filename)
|
||||
if not filename then
|
||||
print(pretty.write(t))
|
||||
return true
|
||||
else
|
||||
return utils.writefile(filename, pretty.write(t))
|
||||
end
|
||||
end
|
||||
|
||||
--- Dump a series of arguments to stdout for debug purposes.
|
||||
-- This function is attached to the module table `__call` method, to make it
|
||||
-- extra easy to access. So the full:
|
||||
--
|
||||
-- print(require("pl.pretty").write({...}))
|
||||
--
|
||||
-- Can be shortened to:
|
||||
--
|
||||
-- require"pl.pretty" (...)
|
||||
--
|
||||
-- Any `nil` entries will be printed as `"<nil>"` to make them explicit.
|
||||
-- @param ... the parameters to dump to stdout.
|
||||
-- @usage
|
||||
-- -- example debug output
|
||||
-- require"pl.pretty" ("hello", nil, "world", { bye = "world", true} )
|
||||
--
|
||||
-- -- output:
|
||||
-- {
|
||||
-- ["arg 1"] = "hello",
|
||||
-- ["arg 2"] = "<nil>",
|
||||
-- ["arg 3"] = "world",
|
||||
-- ["arg 4"] = {
|
||||
-- true,
|
||||
-- bye = "world"
|
||||
-- }
|
||||
-- }
|
||||
function pretty.debug(...)
|
||||
local n = select("#", ...)
|
||||
local t = { ... }
|
||||
for i = 1, n do
|
||||
local value = t[i]
|
||||
if value == nil then
|
||||
value = "<nil>"
|
||||
end
|
||||
t[i] = nil
|
||||
t["arg " .. i] = value
|
||||
end
|
||||
|
||||
print(pretty.write(t))
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local memp,nump = {'B','KiB','MiB','GiB'},{'','K','M','B'}
|
||||
|
||||
local function comma (val)
|
||||
local thou = math.floor(val/1000)
|
||||
if thou > 0 then return comma(thou)..','.. tostring(val % 1000)
|
||||
else return tostring(val) end
|
||||
end
|
||||
|
||||
--- Format large numbers nicely for human consumption.
|
||||
-- @number num a number.
|
||||
-- @string[opt] kind one of `'M'` (memory in `KiB`, `MiB`, etc.),
|
||||
-- `'N'` (postfixes are `'K'`, `'M'` and `'B'`),
|
||||
-- or `'T'` (use commas as thousands separator), `'N'` by default.
|
||||
-- @int[opt] prec number of digits to use for `'M'` and `'N'`, `1` by default.
|
||||
function pretty.number (num,kind,prec)
|
||||
local fmt = '%.'..(prec or 1)..'f%s'
|
||||
if kind == 'T' then
|
||||
return comma(num)
|
||||
else
|
||||
local postfixes, fact
|
||||
if kind == 'M' then
|
||||
fact = 1024
|
||||
postfixes = memp
|
||||
else
|
||||
fact = 1000
|
||||
postfixes = nump
|
||||
end
|
||||
local div = fact
|
||||
local k = 1
|
||||
while num >= div and k <= #postfixes do
|
||||
div = div * fact
|
||||
k = k + 1
|
||||
end
|
||||
div = div / fact
|
||||
if k > #postfixes then k = k - 1; div = div/fact end
|
||||
if k > 1 then
|
||||
return fmt:format(num/div,postfixes[k] or 'duh')
|
||||
else
|
||||
return num..postfixes[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(pretty, {
|
||||
__call = function(self, ...)
|
||||
return self.debug(...)
|
||||
end
|
||||
})
|
544
Documentation/compiler/pl/seq.lua
Normal file
544
Documentation/compiler/pl/seq.lua
Normal file
|
@ -0,0 +1,544 @@
|
|||
--- Manipulating iterators as sequences.
|
||||
-- See @{07-functional.md.Sequences|The Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.types`, `debug`
|
||||
-- @module pl.seq
|
||||
|
||||
local next,assert,pairs,tonumber,type,setmetatable = next,assert,pairs,tonumber,type,setmetatable
|
||||
local strfind,format = string.find,string.format
|
||||
local mrandom = math.random
|
||||
local tsort,tappend = table.sort,table.insert
|
||||
local io = io
|
||||
local utils = require 'pl.utils'
|
||||
local callable = require 'pl.types'.is_callable
|
||||
local function_arg = utils.function_arg
|
||||
local assert_arg = utils.assert_arg
|
||||
local debug = require 'debug'
|
||||
|
||||
local seq = {}
|
||||
|
||||
-- given a number, return a function(y) which returns true if y > x
|
||||
-- @param x a number
|
||||
function seq.greater_than(x)
|
||||
return function(v)
|
||||
return tonumber(v) > x
|
||||
end
|
||||
end
|
||||
|
||||
-- given a number, returns a function(y) which returns true if y < x
|
||||
-- @param x a number
|
||||
function seq.less_than(x)
|
||||
return function(v)
|
||||
return tonumber(v) < x
|
||||
end
|
||||
end
|
||||
|
||||
-- given any value, return a function(y) which returns true if y == x
|
||||
-- @param x a value
|
||||
function seq.equal_to(x)
|
||||
if type(x) == "number" then
|
||||
return function(v)
|
||||
return tonumber(v) == x
|
||||
end
|
||||
else
|
||||
return function(v)
|
||||
return v == x
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- given a string, return a function(y) which matches y against the string.
|
||||
-- @param s a string
|
||||
function seq.matching(s)
|
||||
return function(v)
|
||||
return strfind(v,s)
|
||||
end
|
||||
end
|
||||
|
||||
local nexti
|
||||
|
||||
--- sequence adaptor for a table. Note that if any generic function is
|
||||
-- passed a table, it will automatically use seq.list()
|
||||
-- @param t a list-like table
|
||||
-- @usage sum(list(t)) is the sum of all elements of t
|
||||
-- @usage for x in list(t) do...end
|
||||
function seq.list(t)
|
||||
assert_arg(1,t,'table')
|
||||
if not nexti then
|
||||
nexti = ipairs{}
|
||||
end
|
||||
local key,value = 0
|
||||
return function()
|
||||
key,value = nexti(t,key)
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
--- return the keys of the table.
|
||||
-- @param t an arbitrary table
|
||||
-- @return iterator over keys
|
||||
function seq.keys(t)
|
||||
assert_arg(1,t,'table')
|
||||
local key
|
||||
return function()
|
||||
key = next(t,key)
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
local list = seq.list
|
||||
local function default_iter(iter)
|
||||
if type(iter) == 'table' then return list(iter)
|
||||
else return iter end
|
||||
end
|
||||
|
||||
seq.iter = default_iter
|
||||
|
||||
--- create an iterator over a numerical range. Like the standard Python function xrange.
|
||||
-- @param start a number
|
||||
-- @param finish a number greater than start
|
||||
function seq.range(start,finish)
|
||||
local i = start - 1
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > finish then return nil
|
||||
else return i end
|
||||
end
|
||||
end
|
||||
|
||||
-- count the number of elements in the sequence which satisfy the predicate
|
||||
-- @param iter a sequence
|
||||
-- @param condn a predicate function (must return either true or false)
|
||||
-- @param optional argument to be passed to predicate as second argument.
|
||||
-- @return count
|
||||
function seq.count(iter,condn,arg)
|
||||
local i = 0
|
||||
seq.foreach(iter,function(val)
|
||||
if condn(val,arg) then i = i + 1 end
|
||||
end)
|
||||
return i
|
||||
end
|
||||
|
||||
--- return the minimum and the maximum value of the sequence.
|
||||
-- @param iter a sequence
|
||||
-- @return minimum value
|
||||
-- @return maximum value
|
||||
function seq.minmax(iter)
|
||||
local vmin,vmax = 1e70,-1e70
|
||||
for v in default_iter(iter) do
|
||||
v = tonumber(v)
|
||||
if v < vmin then vmin = v end
|
||||
if v > vmax then vmax = v end
|
||||
end
|
||||
return vmin,vmax
|
||||
end
|
||||
|
||||
--- return the sum and element count of the sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param fn an optional function to apply to the values
|
||||
function seq.sum(iter,fn)
|
||||
local s = 0
|
||||
local i = 0
|
||||
for v in default_iter(iter) do
|
||||
if fn then v = fn(v) end
|
||||
s = s + v
|
||||
i = i + 1
|
||||
end
|
||||
return s,i
|
||||
end
|
||||
|
||||
--- create a table from the sequence. (This will make the result a List.)
|
||||
-- @param iter a sequence
|
||||
-- @return a List
|
||||
-- @usage copy(list(ls)) is equal to ls
|
||||
-- @usage copy(list {1,2,3}) == List{1,2,3}
|
||||
function seq.copy(iter)
|
||||
local res,k = {},1
|
||||
for v in default_iter(iter) do
|
||||
res[k] = v
|
||||
k = k + 1
|
||||
end
|
||||
setmetatable(res, require('pl.List'))
|
||||
return res
|
||||
end
|
||||
|
||||
--- create a table of pairs from the double-valued sequence.
|
||||
-- @param iter a double-valued sequence
|
||||
-- @param i1 used to capture extra iterator values
|
||||
-- @param i2 as with pairs & ipairs
|
||||
-- @usage copy2(ipairs{10,20,30}) == {{1,10},{2,20},{3,30}}
|
||||
-- @return a list-like table
|
||||
function seq.copy2 (iter,i1,i2)
|
||||
local res,k = {},1
|
||||
for v1,v2 in iter,i1,i2 do
|
||||
res[k] = {v1,v2}
|
||||
k = k + 1
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- create a table of 'tuples' from a multi-valued sequence.
|
||||
-- A generalization of copy2 above
|
||||
-- @param iter a multiple-valued sequence
|
||||
-- @return a list-like table
|
||||
function seq.copy_tuples (iter)
|
||||
iter = default_iter(iter)
|
||||
local res = {}
|
||||
local row = {iter()}
|
||||
while #row > 0 do
|
||||
tappend(res,row)
|
||||
row = {iter()}
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- return an iterator of random numbers.
|
||||
-- @param n the length of the sequence
|
||||
-- @param l same as the first optional argument to math.random
|
||||
-- @param u same as the second optional argument to math.random
|
||||
-- @return a sequence
|
||||
function seq.random(n,l,u)
|
||||
local rand
|
||||
assert(type(n) == 'number')
|
||||
if u then
|
||||
rand = function() return mrandom(l,u) end
|
||||
elseif l then
|
||||
rand = function() return mrandom(l) end
|
||||
else
|
||||
rand = mrandom
|
||||
end
|
||||
|
||||
return function()
|
||||
if n == 0 then return nil
|
||||
else
|
||||
n = n - 1
|
||||
return rand()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- return an iterator to the sorted elements of a sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param comp an optional comparison function (comp(x,y) is true if x < y)
|
||||
function seq.sort(iter,comp)
|
||||
local t = seq.copy(iter)
|
||||
tsort(t,comp)
|
||||
return list(t)
|
||||
end
|
||||
|
||||
--- return an iterator which returns elements of two sequences.
|
||||
-- @param iter1 a sequence
|
||||
-- @param iter2 a sequence
|
||||
-- @usage for x,y in seq.zip(ls1,ls2) do....end
|
||||
function seq.zip(iter1,iter2)
|
||||
iter1 = default_iter(iter1)
|
||||
iter2 = default_iter(iter2)
|
||||
return function()
|
||||
return iter1(),iter2()
|
||||
end
|
||||
end
|
||||
|
||||
--- Makes a table where the key/values are the values and value counts of the sequence.
|
||||
-- This version works with 'hashable' values like strings and numbers.
|
||||
-- `pl.tablex.count_map` is more general.
|
||||
-- @param iter a sequence
|
||||
-- @return a map-like table
|
||||
-- @return a table
|
||||
-- @see pl.tablex.count_map
|
||||
function seq.count_map(iter)
|
||||
local t = {}
|
||||
local v
|
||||
for s in default_iter(iter) do
|
||||
v = t[s]
|
||||
if v then t[s] = v + 1
|
||||
else t[s] = 1 end
|
||||
end
|
||||
return setmetatable(t, require('pl.Map'))
|
||||
end
|
||||
|
||||
-- given a sequence, return all the unique values in that sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param returns_table true if we return a table, not a sequence
|
||||
-- @return a sequence or a table; defaults to a sequence.
|
||||
function seq.unique(iter,returns_table)
|
||||
local t = seq.count_map(iter)
|
||||
local res,k = {},1
|
||||
for key in pairs(t) do res[k] = key; k = k + 1 end
|
||||
table.sort(res)
|
||||
if returns_table then
|
||||
return res
|
||||
else
|
||||
return list(res)
|
||||
end
|
||||
end
|
||||
|
||||
--- print out a sequence iter with a separator.
|
||||
-- @param iter a sequence
|
||||
-- @param sep the separator (default space)
|
||||
-- @param nfields maximum number of values per line (default 7)
|
||||
-- @param fmt optional format function for each value
|
||||
function seq.printall(iter,sep,nfields,fmt)
|
||||
local write = io.write
|
||||
if not sep then sep = ' ' end
|
||||
if not nfields then
|
||||
if sep == '\n' then nfields = 1e30
|
||||
else nfields = 7 end
|
||||
end
|
||||
if fmt then
|
||||
local fstr = fmt
|
||||
fmt = function(v) return format(fstr,v) end
|
||||
end
|
||||
local k = 1
|
||||
for v in default_iter(iter) do
|
||||
if fmt then v = fmt(v) end
|
||||
if k < nfields then
|
||||
write(v,sep)
|
||||
k = k + 1
|
||||
else
|
||||
write(v,'\n')
|
||||
k = 1
|
||||
end
|
||||
end
|
||||
write '\n'
|
||||
end
|
||||
|
||||
-- return an iterator running over every element of two sequences (concatenation).
|
||||
-- @param iter1 a sequence
|
||||
-- @param iter2 a sequence
|
||||
function seq.splice(iter1,iter2)
|
||||
iter1 = default_iter(iter1)
|
||||
iter2 = default_iter(iter2)
|
||||
local iter = iter1
|
||||
return function()
|
||||
local ret = iter()
|
||||
if ret == nil then
|
||||
if iter == iter1 then
|
||||
iter = iter2
|
||||
return iter()
|
||||
else return nil end
|
||||
else
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- return a sequence where every element of a sequence has been transformed
|
||||
-- by a function. If you don't supply an argument, then the function will
|
||||
-- receive both values of a double-valued sequence, otherwise behaves rather like
|
||||
-- tablex.map.
|
||||
-- @param fn a function to apply to elements; may take two arguments
|
||||
-- @param iter a sequence of one or two values
|
||||
-- @param arg optional argument to pass to function.
|
||||
function seq.map(fn,iter,arg)
|
||||
fn = function_arg(1,fn)
|
||||
iter = default_iter(iter)
|
||||
return function()
|
||||
local v1,v2 = iter()
|
||||
if v1 == nil then return nil end
|
||||
return fn(v1,arg or v2) or false
|
||||
end
|
||||
end
|
||||
|
||||
--- filter a sequence using a predicate function.
|
||||
-- @param iter a sequence of one or two values
|
||||
-- @param pred a boolean function; may take two arguments
|
||||
-- @param arg optional argument to pass to function.
|
||||
function seq.filter (iter,pred,arg)
|
||||
pred = function_arg(2,pred)
|
||||
return function ()
|
||||
local v1,v2
|
||||
while true do
|
||||
v1,v2 = iter()
|
||||
if v1 == nil then return nil end
|
||||
if pred(v1,arg or v2) then return v1,v2 end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 'reduce' a sequence using a binary function.
|
||||
-- @func fn a function of two arguments
|
||||
-- @param iter a sequence
|
||||
-- @param initval optional initial value
|
||||
-- @usage seq.reduce(operator.add,seq.list{1,2,3,4}) == 10
|
||||
-- @usage seq.reduce('-',{1,2,3,4,5}) == -13
|
||||
function seq.reduce (fn,iter,initval)
|
||||
fn = function_arg(1,fn)
|
||||
iter = default_iter(iter)
|
||||
local val = initval or iter()
|
||||
if val == nil then return nil end
|
||||
for v in iter do
|
||||
val = fn(val,v)
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
--- take the first n values from the sequence.
|
||||
-- @param iter a sequence of one or two values
|
||||
-- @param n number of items to take
|
||||
-- @return a sequence of at most n items
|
||||
function seq.take (iter,n)
|
||||
iter = default_iter(iter)
|
||||
return function()
|
||||
if n < 1 then return end
|
||||
local val1,val2 = iter()
|
||||
if not val1 then return end
|
||||
n = n - 1
|
||||
return val1,val2
|
||||
end
|
||||
end
|
||||
|
||||
--- skip the first n values of a sequence
|
||||
-- @param iter a sequence of one or more values
|
||||
-- @param n number of items to skip
|
||||
function seq.skip (iter,n)
|
||||
n = n or 1
|
||||
for i = 1,n do
|
||||
if iter() == nil then return list{} end
|
||||
end
|
||||
return iter
|
||||
end
|
||||
|
||||
--- a sequence with a sequence count and the original value.
|
||||
-- enum(copy(ls)) is a roundabout way of saying ipairs(ls).
|
||||
-- @param iter a single or double valued sequence
|
||||
-- @return sequence of (i,v), i = 1..n and v is from iter.
|
||||
function seq.enum (iter)
|
||||
local i = 0
|
||||
iter = default_iter(iter)
|
||||
return function ()
|
||||
local val1,val2 = iter()
|
||||
if not val1 then return end
|
||||
i = i + 1
|
||||
return i,val1,val2
|
||||
end
|
||||
end
|
||||
|
||||
--- map using a named method over a sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param name the method name
|
||||
-- @param arg1 optional first extra argument
|
||||
-- @param arg2 optional second extra argument
|
||||
function seq.mapmethod (iter,name,arg1,arg2)
|
||||
iter = default_iter(iter)
|
||||
return function()
|
||||
local val = iter()
|
||||
if not val then return end
|
||||
local fn = val[name]
|
||||
if not fn then error(type(val).." does not have method "..name) end
|
||||
return fn(val,arg1,arg2)
|
||||
end
|
||||
end
|
||||
|
||||
--- a sequence of (last,current) values from another sequence.
|
||||
-- This will return S(i-1),S(i) if given S(i)
|
||||
-- @param iter a sequence
|
||||
function seq.last (iter)
|
||||
iter = default_iter(iter)
|
||||
local val, l = iter(), nil
|
||||
if val == nil then return list{} end
|
||||
return function ()
|
||||
val,l = iter(),val
|
||||
if val == nil then return nil end
|
||||
return val,l
|
||||
end
|
||||
end
|
||||
|
||||
--- call the function on each element of the sequence.
|
||||
-- @param iter a sequence with up to 3 values
|
||||
-- @param fn a function
|
||||
function seq.foreach(iter,fn)
|
||||
fn = function_arg(2,fn)
|
||||
for i1,i2,i3 in default_iter(iter) do fn(i1,i2,i3) end
|
||||
end
|
||||
|
||||
---------------------- Sequence Adapters ---------------------
|
||||
|
||||
local SMT
|
||||
|
||||
local function SW (iter,...)
|
||||
if callable(iter) then
|
||||
return setmetatable({iter=iter},SMT)
|
||||
else
|
||||
return iter,...
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- can't directly look these up in seq because of the wrong argument order...
|
||||
local map,reduce,mapmethod = seq.map, seq.reduce, seq.mapmethod
|
||||
local overrides = {
|
||||
map = function(self,fun,arg)
|
||||
return map(fun,self,arg)
|
||||
end,
|
||||
reduce = function(self,fun,initval)
|
||||
return reduce(fun,self,initval)
|
||||
end
|
||||
}
|
||||
|
||||
SMT = {
|
||||
__index = function (tbl,key)
|
||||
local fn = overrides[key] or seq[key]
|
||||
if fn then
|
||||
return function(sw,...) return SW(fn(sw.iter,...)) end
|
||||
else
|
||||
return function(sw,...) return SW(mapmethod(sw.iter,key,...)) end
|
||||
end
|
||||
end,
|
||||
__call = function (sw)
|
||||
return sw.iter()
|
||||
end,
|
||||
}
|
||||
|
||||
setmetatable(seq,{
|
||||
__call = function(tbl,iter,extra)
|
||||
if not callable(iter) then
|
||||
if type(iter) == 'table' then iter = seq.list(iter)
|
||||
else return iter
|
||||
end
|
||||
end
|
||||
if extra then
|
||||
return setmetatable({iter=function()
|
||||
return iter(extra)
|
||||
end},SMT)
|
||||
else
|
||||
return setmetatable({iter=iter},SMT)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
--- create a wrapped iterator over all lines in the file.
|
||||
-- @param f either a filename, file-like object, or 'STDIN' (for standard input)
|
||||
-- @param ... for Lua 5.2 only, optional format specifiers, as in `io.read`.
|
||||
-- @return a sequence wrapper
|
||||
function seq.lines (f,...)
|
||||
local iter,obj
|
||||
if f == 'STDIN' then
|
||||
f = io.stdin
|
||||
elseif type(f) == 'string' then
|
||||
iter,obj = io.lines(f,...)
|
||||
elseif not f.read then
|
||||
error("Pass either a string or a file-like object",2)
|
||||
end
|
||||
if not iter then
|
||||
iter,obj = f:lines(...)
|
||||
end
|
||||
if obj then -- LuaJIT version returns a function operating on a file
|
||||
local lines,file = iter,obj
|
||||
iter = function() return lines(file) end
|
||||
end
|
||||
return SW(iter)
|
||||
end
|
||||
|
||||
function seq.import ()
|
||||
debug.setmetatable(function() end,{
|
||||
__index = function(tbl,key)
|
||||
local s = overrides[key] or seq[key]
|
||||
if s then return s
|
||||
else
|
||||
return function(s,...) return seq.mapmethod(s,key,...) end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
return seq
|
337
Documentation/compiler/pl/sip.lua
Normal file
337
Documentation/compiler/pl/sip.lua
Normal file
|
@ -0,0 +1,337 @@
|
|||
--- Simple Input Patterns (SIP).
|
||||
-- SIP patterns start with '$', then a
|
||||
-- one-letter type, and then an optional variable in curly braces.
|
||||
--
|
||||
-- sip.match('$v=$q','name="dolly"',res)
|
||||
-- ==> res=={'name','dolly'}
|
||||
-- sip.match('($q{first},$q{second})','("john","smith")',res)
|
||||
-- ==> res=={second='smith',first='john'}
|
||||
--
|
||||
-- Type names:
|
||||
--
|
||||
-- v identifier
|
||||
-- i integer
|
||||
-- f floating-point
|
||||
-- q quoted string
|
||||
-- ([{< match up to closing bracket
|
||||
--
|
||||
-- See @{08-additional.md.Simple_Input_Patterns|the Guide}
|
||||
--
|
||||
-- @module pl.sip
|
||||
|
||||
local loadstring = rawget(_G,'loadstring') or load
|
||||
local unpack = rawget(_G,'unpack') or rawget(table,'unpack')
|
||||
|
||||
local append,concat = table.insert,table.concat
|
||||
local ipairs,type = ipairs,type
|
||||
local io,_G = io,_G
|
||||
local print,rawget = print,rawget
|
||||
|
||||
local patterns = {
|
||||
FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*',
|
||||
INTEGER = '[+%-%d]%d*',
|
||||
IDEN = '[%a_][%w_]*',
|
||||
OPTION = '[%a_][%w_%-]*',
|
||||
}
|
||||
|
||||
local function assert_arg(idx,val,tp)
|
||||
if type(val) ~= tp then
|
||||
error("argument "..idx.." must be "..tp, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local sip = {}
|
||||
|
||||
local brackets = {['<'] = '>', ['('] = ')', ['{'] = '}', ['['] = ']' }
|
||||
local stdclasses = {a=1,c=0,d=1,l=1,p=0,u=1,w=1,x=1,s=0}
|
||||
|
||||
local function group(s)
|
||||
return '('..s..')'
|
||||
end
|
||||
|
||||
-- escape all magic characters except $, which has special meaning
|
||||
-- Also, un-escape any characters after $, so $( and $[ passes through as is.
|
||||
local function escape (spec)
|
||||
return (spec:gsub('[%-%.%+%[%]%(%)%^%%%?%*]','%%%0'):gsub('%$%%(%S)','$%1'))
|
||||
end
|
||||
|
||||
-- Most spaces within patterns can match zero or more spaces.
|
||||
-- Spaces between alphanumeric characters or underscores or between
|
||||
-- patterns that can match these characters, however, must match at least
|
||||
-- one space. Otherwise '$v $v' would match 'abcd' as {'abc', 'd'}.
|
||||
-- This function replaces continuous spaces within a pattern with either
|
||||
-- '%s*' or '%s+' according to this rule. The pattern has already
|
||||
-- been stripped of pattern names by now.
|
||||
local function compress_spaces(patt)
|
||||
return (patt:gsub("()%s+()", function(i1, i2)
|
||||
local before = patt:sub(i1 - 2, i1 - 1)
|
||||
if before:match('%$[vifadxlu]') or before:match('^[^%$]?[%w_]$') then
|
||||
local after = patt:sub(i2, i2 + 1)
|
||||
if after:match('%$[vifadxlu]') or after:match('^[%w_]') then
|
||||
return '%s+'
|
||||
end
|
||||
end
|
||||
return '%s*'
|
||||
end))
|
||||
end
|
||||
|
||||
local pattern_map = {
|
||||
v = group(patterns.IDEN),
|
||||
i = group(patterns.INTEGER),
|
||||
f = group(patterns.FLOAT),
|
||||
o = group(patterns.OPTION),
|
||||
r = '(%S.*)',
|
||||
p = '([%a]?[:]?[\\/%.%w_]+)'
|
||||
}
|
||||
|
||||
function sip.custom_pattern(flag,patt)
|
||||
pattern_map[flag] = patt
|
||||
end
|
||||
|
||||
--- convert a SIP pattern into the equivalent Lua string pattern.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param options a table; only the <code>at_start</code> field is
|
||||
-- currently meaningful and ensures that the pattern is anchored
|
||||
-- at the start of the string.
|
||||
-- @return a Lua string pattern.
|
||||
function sip.create_pattern (spec,options)
|
||||
assert_arg(1,spec,'string')
|
||||
local fieldnames,fieldtypes = {},{}
|
||||
|
||||
if type(spec) == 'string' then
|
||||
spec = escape(spec)
|
||||
else
|
||||
local res = {}
|
||||
for i,s in ipairs(spec) do
|
||||
res[i] = escape(s)
|
||||
end
|
||||
spec = concat(res,'.-')
|
||||
end
|
||||
|
||||
local kount = 1
|
||||
|
||||
local function addfield (name,type)
|
||||
name = name or kount
|
||||
append(fieldnames,name)
|
||||
fieldtypes[name] = type
|
||||
kount = kount + 1
|
||||
end
|
||||
|
||||
local named_vars = spec:find('{%a+}')
|
||||
|
||||
if options and options.at_start then
|
||||
spec = '^'..spec
|
||||
end
|
||||
if spec:sub(-1,-1) == '$' then
|
||||
spec = spec:sub(1,-2)..'$r'
|
||||
if named_vars then spec = spec..'{rest}' end
|
||||
end
|
||||
|
||||
local names
|
||||
|
||||
if named_vars then
|
||||
names = {}
|
||||
spec = spec:gsub('{(%a+)}',function(name)
|
||||
append(names,name)
|
||||
return ''
|
||||
end)
|
||||
end
|
||||
spec = compress_spaces(spec)
|
||||
|
||||
local k = 1
|
||||
local err
|
||||
local r = (spec:gsub('%$%S',function(s)
|
||||
local type,name
|
||||
type = s:sub(2,2)
|
||||
if names then name = names[k]; k=k+1 end
|
||||
-- this kludge is necessary because %q generates two matches, and
|
||||
-- we want to ignore the first. Not a problem for named captures.
|
||||
if not names and type == 'q' then
|
||||
addfield(nil,'Q')
|
||||
else
|
||||
addfield(name,type)
|
||||
end
|
||||
local res
|
||||
if pattern_map[type] then
|
||||
res = pattern_map[type]
|
||||
elseif type == 'q' then
|
||||
-- some Lua pattern matching voodoo; we want to match '...' as
|
||||
-- well as "...", and can use the fact that %n will match a
|
||||
-- previous capture. Adding the extra field above comes from needing
|
||||
-- to accommodate the extra spurious match (which is either ' or ")
|
||||
addfield(name,type)
|
||||
res = '(["\'])(.-)%'..(kount-2)
|
||||
else
|
||||
local endbracket = brackets[type]
|
||||
if endbracket then
|
||||
res = '(%b'..type..endbracket..')'
|
||||
elseif stdclasses[type] or stdclasses[type:lower()] then
|
||||
res = '(%'..type..'+)'
|
||||
else
|
||||
err = "unknown format type or character class"
|
||||
end
|
||||
end
|
||||
return res
|
||||
end))
|
||||
|
||||
if err then
|
||||
return nil,err
|
||||
else
|
||||
return r,fieldnames,fieldtypes
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function tnumber (s)
|
||||
return s == 'd' or s == 'i' or s == 'f'
|
||||
end
|
||||
|
||||
function sip.create_spec_fun(spec,options)
|
||||
local fieldtypes,fieldnames
|
||||
local ls = {}
|
||||
spec,fieldnames,fieldtypes = sip.create_pattern(spec,options)
|
||||
if not spec then return spec,fieldnames end
|
||||
local named_vars = type(fieldnames[1]) == 'string'
|
||||
for i = 1,#fieldnames do
|
||||
append(ls,'mm'..i)
|
||||
end
|
||||
ls[1] = ls[1] or "mm1" -- behave correctly if there are no patterns
|
||||
local fun = ('return (function(s,res)\n\tlocal %s = s:match(%q)\n'):format(concat(ls,','),spec)
|
||||
fun = fun..'\tif not mm1 then return false end\n'
|
||||
local k=1
|
||||
for i,f in ipairs(fieldnames) do
|
||||
if f ~= '_' then
|
||||
local var = 'mm'..i
|
||||
if tnumber(fieldtypes[f]) then
|
||||
var = 'tonumber('..var..')'
|
||||
elseif brackets[fieldtypes[f]] then
|
||||
var = var..':sub(2,-2)'
|
||||
end
|
||||
if named_vars then
|
||||
fun = ('%s\tres.%s = %s\n'):format(fun,f,var)
|
||||
else
|
||||
if fieldtypes[f] ~= 'Q' then -- we skip the string-delim capture
|
||||
fun = ('%s\tres[%d] = %s\n'):format(fun,k,var)
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return fun..'\treturn true\nend)\n', named_vars
|
||||
end
|
||||
|
||||
--- convert a SIP pattern into a matching function.
|
||||
-- The returned function takes two arguments, the line and an empty table.
|
||||
-- If the line matched the pattern, then this function returns true
|
||||
-- and the table is filled with field-value pairs.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param options optional table; {at_start=true} ensures that the pattern
|
||||
-- is anchored at the start of the string.
|
||||
-- @return a function if successful, or nil,error
|
||||
function sip.compile(spec,options)
|
||||
assert_arg(1,spec,'string')
|
||||
local fun,names = sip.create_spec_fun(spec,options)
|
||||
if not fun then return nil,names end
|
||||
if rawget(_G,'_DEBUG') then print(fun) end
|
||||
local chunk,err = loadstring(fun,'tmp')
|
||||
if err then return nil,err end
|
||||
return chunk(),names
|
||||
end
|
||||
|
||||
local cache = {}
|
||||
|
||||
--- match a SIP pattern against a string.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param line a string
|
||||
-- @param res a table to receive values
|
||||
-- @param options (optional) option table
|
||||
-- @return true or false
|
||||
function sip.match (spec,line,res,options)
|
||||
assert_arg(1,spec,'string')
|
||||
assert_arg(2,line,'string')
|
||||
assert_arg(3,res,'table')
|
||||
if not cache[spec] then
|
||||
cache[spec] = sip.compile(spec,options)
|
||||
end
|
||||
return cache[spec](line,res)
|
||||
end
|
||||
|
||||
--- match a SIP pattern against the start of a string.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param line a string
|
||||
-- @param res a table to receive values
|
||||
-- @return true or false
|
||||
function sip.match_at_start (spec,line,res)
|
||||
return sip.match(spec,line,res,{at_start=true})
|
||||
end
|
||||
|
||||
--- given a pattern and a file object, return an iterator over the results
|
||||
-- @param spec a SIP pattern
|
||||
-- @param f a file-like object.
|
||||
function sip.fields (spec,f)
|
||||
assert_arg(1,spec,'string')
|
||||
if not f then return nil,"no file object" end
|
||||
local fun,err = sip.compile(spec)
|
||||
if not fun then return nil,err end
|
||||
local res = {}
|
||||
return function()
|
||||
while true do
|
||||
local line = f:read()
|
||||
if not line then return end
|
||||
if fun(line,res) then
|
||||
local values = res
|
||||
res = {}
|
||||
return unpack(values)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local read_patterns = {}
|
||||
|
||||
--- register a match which will be used in the read function.
|
||||
-- @string spec a SIP pattern
|
||||
-- @func fun a function to be called with the results of the match
|
||||
-- @see read
|
||||
function sip.pattern (spec,fun)
|
||||
assert_arg(1,spec,'string')
|
||||
local pat,named = sip.compile(spec)
|
||||
append(read_patterns,{pat=pat,named=named,callback=fun})
|
||||
end
|
||||
|
||||
--- enter a loop which applies all registered matches to the input file.
|
||||
-- @param f a file-like object
|
||||
-- @array matches optional list of `{spec,fun}` pairs, as for `pattern` above.
|
||||
function sip.read (f,matches)
|
||||
local owned,err
|
||||
if not f then return nil,"no file object" end
|
||||
if type(f) == 'string' then
|
||||
f,err = io.open(f)
|
||||
if not f then return nil,err end
|
||||
owned = true
|
||||
end
|
||||
if matches then
|
||||
for _,p in ipairs(matches) do
|
||||
sip.pattern(p[1],p[2])
|
||||
end
|
||||
end
|
||||
local res = {}
|
||||
for line in f:lines() do
|
||||
for _,item in ipairs(read_patterns) do
|
||||
if item.pat(line,res) then
|
||||
if item.callback then
|
||||
if item.named then
|
||||
item.callback(res)
|
||||
else
|
||||
item.callback(unpack(res))
|
||||
end
|
||||
end
|
||||
res = {}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if owned then f:close() end
|
||||
end
|
||||
|
||||
return sip
|
138
Documentation/compiler/pl/strict.lua
Normal file
138
Documentation/compiler/pl/strict.lua
Normal file
|
@ -0,0 +1,138 @@
|
|||
--- Checks uses of undeclared global variables.
|
||||
-- All global variables must be 'declared' through a regular assignment
|
||||
-- (even assigning `nil` will do) in a main chunk before being used
|
||||
-- anywhere or assigned to inside a function. Existing metatables `__newindex` and `__index`
|
||||
-- metamethods are respected.
|
||||
--
|
||||
-- You can set any table to have strict behaviour using `strict.module`. Creating a new
|
||||
-- module with `strict.closed_module` makes the module immune to monkey-patching, if
|
||||
-- you don't wish to encourage monkey business.
|
||||
--
|
||||
-- If the global `PENLIGHT_NO_GLOBAL_STRICT` is defined, then this module won't make the
|
||||
-- global environment strict - if you just want to explicitly set table strictness.
|
||||
--
|
||||
-- @module pl.strict
|
||||
|
||||
require 'debug' -- for Lua 5.2
|
||||
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
|
||||
local strict = {}
|
||||
|
||||
local function what ()
|
||||
local d = getinfo(3, "S")
|
||||
return d and d.what or "C"
|
||||
end
|
||||
|
||||
--- make an existing table strict.
|
||||
-- @string[opt] name name of table
|
||||
-- @tab[opt] mod the table to protect - if `nil` then we'll return a new table
|
||||
-- @tab[opt] predeclared - table of variables that are to be considered predeclared.
|
||||
-- @return the given table, or a new table
|
||||
-- @usage
|
||||
-- local M = { hello = "world" }
|
||||
-- strict.module ("Awesome_Module", M, {
|
||||
-- Lua = true, -- defines allowed keys
|
||||
-- })
|
||||
--
|
||||
-- assert(M.hello == "world")
|
||||
-- assert(M.Lua == nil) -- access allowed, but has no value yet
|
||||
-- M.Lua = "Rocks"
|
||||
-- assert(M.Lua == "Rocks")
|
||||
-- M.not_allowed = "bad boy" -- throws an error
|
||||
function strict.module (name,mod,predeclared)
|
||||
local mt, old_newindex, old_index, old_index_type, global
|
||||
if predeclared then
|
||||
global = predeclared.__global
|
||||
end
|
||||
if type(mod) == 'table' then
|
||||
mt = getmetatable(mod)
|
||||
if mt and rawget(mt,'__declared') then return end -- already patched...
|
||||
else
|
||||
mod = {}
|
||||
end
|
||||
if mt == nil then
|
||||
mt = {}
|
||||
setmetatable(mod, mt)
|
||||
else
|
||||
old_newindex = mt.__newindex
|
||||
old_index = mt.__index
|
||||
old_index_type = type(old_index)
|
||||
end
|
||||
mt.__declared = predeclared or {}
|
||||
mt.__newindex = function(t, n, v)
|
||||
if old_newindex then
|
||||
old_newindex(t, n, v)
|
||||
if rawget(t,n)~=nil then return end
|
||||
end
|
||||
if not mt.__declared[n] then
|
||||
if global then
|
||||
local w = what()
|
||||
if w ~= "main" and w ~= "C" then
|
||||
error("assign to undeclared global '"..n.."'", 2)
|
||||
end
|
||||
end
|
||||
mt.__declared[n] = true
|
||||
end
|
||||
rawset(t, n, v)
|
||||
end
|
||||
mt.__index = function(t,n)
|
||||
if not mt.__declared[n] and what() ~= "C" then
|
||||
if old_index then
|
||||
if old_index_type == "table" then
|
||||
local fallback = old_index[n]
|
||||
if fallback ~= nil then
|
||||
return fallback
|
||||
end
|
||||
else
|
||||
local res = old_index(t, n)
|
||||
if res ~= nil then
|
||||
return res
|
||||
end
|
||||
end
|
||||
end
|
||||
local msg = "variable '"..n.."' is not declared"
|
||||
if name then
|
||||
msg = msg .. " in '"..tostring(name).."'"
|
||||
end
|
||||
error(msg, 2)
|
||||
end
|
||||
return rawget(t, n)
|
||||
end
|
||||
return mod
|
||||
end
|
||||
|
||||
--- make all tables in a table strict.
|
||||
-- So `strict.make_all_strict(_G)` prevents monkey-patching
|
||||
-- of any global table
|
||||
-- @tab T the table containing the tables to protect. Table `T` itself will NOT be protected.
|
||||
function strict.make_all_strict (T)
|
||||
for k,v in pairs(T) do
|
||||
if type(v) == 'table' and v ~= T then
|
||||
strict.module(k,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- make a new module table which is closed to further changes.
|
||||
-- @tab mod module table
|
||||
-- @string name module name
|
||||
function strict.closed_module (mod,name)
|
||||
-- No clue to what this is useful for? see tests
|
||||
-- Deprecate this and remove???
|
||||
local M = {}
|
||||
mod = mod or {}
|
||||
local mt = getmetatable(mod)
|
||||
if not mt then
|
||||
mt = {}
|
||||
setmetatable(mod,mt)
|
||||
end
|
||||
mt.__newindex = function(t,k,v)
|
||||
M[k] = v
|
||||
end
|
||||
return strict.module(name,M)
|
||||
end
|
||||
|
||||
if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then
|
||||
strict.module(nil,_G,{_PROMPT=true,_PROMPT2=true,__global=true})
|
||||
end
|
||||
|
||||
return strict
|
158
Documentation/compiler/pl/stringio.lua
Normal file
158
Documentation/compiler/pl/stringio.lua
Normal file
|
@ -0,0 +1,158 @@
|
|||
--- Reading and writing strings using file-like objects. <br>
|
||||
--
|
||||
-- f = stringio.open(text)
|
||||
-- l1 = f:read() -- read first line
|
||||
-- n,m = f:read ('*n','*n') -- read two numbers
|
||||
-- for line in f:lines() do print(line) end -- iterate over all lines
|
||||
-- f = stringio.create()
|
||||
-- f:write('hello')
|
||||
-- f:write('dolly')
|
||||
-- assert(f:value(),'hellodolly')
|
||||
--
|
||||
-- See @{03-strings.md.File_style_I_O_on_Strings|the Guide}.
|
||||
-- @module pl.stringio
|
||||
|
||||
local unpack = rawget(_G,'unpack') or rawget(table,'unpack')
|
||||
local tonumber = tonumber
|
||||
local concat,append = table.concat,table.insert
|
||||
|
||||
local stringio = {}
|
||||
|
||||
-- Writer class
|
||||
local SW = {}
|
||||
SW.__index = SW
|
||||
|
||||
local function xwrite(self,...)
|
||||
local args = {...} --arguments may not be nil!
|
||||
for i = 1, #args do
|
||||
append(self.tbl,args[i])
|
||||
end
|
||||
end
|
||||
|
||||
function SW:write(arg1,arg2,...)
|
||||
if arg2 then
|
||||
xwrite(self,arg1,arg2,...)
|
||||
else
|
||||
append(self.tbl,arg1)
|
||||
end
|
||||
end
|
||||
|
||||
function SW:writef(fmt,...)
|
||||
self:write(fmt:format(...))
|
||||
end
|
||||
|
||||
function SW:value()
|
||||
return concat(self.tbl)
|
||||
end
|
||||
|
||||
function SW:__tostring()
|
||||
return self:value()
|
||||
end
|
||||
|
||||
function SW:close() -- for compatibility only
|
||||
end
|
||||
|
||||
function SW:seek()
|
||||
end
|
||||
|
||||
-- Reader class
|
||||
local SR = {}
|
||||
SR.__index = SR
|
||||
|
||||
function SR:_read(fmt)
|
||||
local i,str = self.i,self.str
|
||||
local sz = #str
|
||||
if i > sz then return nil end
|
||||
local res
|
||||
if fmt == '*l' or fmt == '*L' then
|
||||
local idx = str:find('\n',i) or (sz+1)
|
||||
res = str:sub(i,fmt == '*l' and idx-1 or idx)
|
||||
self.i = idx+1
|
||||
elseif fmt == '*a' then
|
||||
res = str:sub(i)
|
||||
self.i = sz
|
||||
elseif fmt == '*n' then
|
||||
local _,i2,idx
|
||||
_,idx = str:find ('%s*%d+',i)
|
||||
_,i2 = str:find ('^%.%d+',idx+1)
|
||||
if i2 then idx = i2 end
|
||||
_,i2 = str:find ('^[eE][%+%-]*%d+',idx+1)
|
||||
if i2 then idx = i2 end
|
||||
local val = str:sub(i,idx)
|
||||
res = tonumber(val)
|
||||
self.i = idx+1
|
||||
elseif type(fmt) == 'number' then
|
||||
res = str:sub(i,i+fmt-1)
|
||||
self.i = i + fmt
|
||||
else
|
||||
error("bad read format",2)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function SR:read(...)
|
||||
if select('#',...) == 0 then
|
||||
return self:_read('*l')
|
||||
else
|
||||
local res, fmts = {},{...}
|
||||
for i = 1, #fmts do
|
||||
res[i] = self:_read(fmts[i])
|
||||
end
|
||||
return unpack(res)
|
||||
end
|
||||
end
|
||||
|
||||
function SR:seek(whence,offset)
|
||||
local base
|
||||
whence = whence or 'cur'
|
||||
offset = offset or 0
|
||||
if whence == 'set' then
|
||||
base = 1
|
||||
elseif whence == 'cur' then
|
||||
base = self.i
|
||||
elseif whence == 'end' then
|
||||
base = #self.str
|
||||
end
|
||||
self.i = base + offset
|
||||
return self.i
|
||||
end
|
||||
|
||||
function SR:lines(...)
|
||||
local n, args = select('#',...)
|
||||
if n > 0 then
|
||||
args = {...}
|
||||
end
|
||||
return function()
|
||||
if n == 0 then
|
||||
return self:_read '*l'
|
||||
else
|
||||
return self:read(unpack(args))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SR:close() -- for compatibility only
|
||||
end
|
||||
|
||||
--- create a file-like object which can be used to construct a string.
|
||||
-- The resulting object has an extra `value()` method for
|
||||
-- retrieving the string value. Implements `file:write`, `file:seek`, `file:lines`,
|
||||
-- plus an extra `writef` method which works like `utils.printf`.
|
||||
-- @usage f = create(); f:write('hello, dolly\n'); print(f:value())
|
||||
function stringio.create()
|
||||
return setmetatable({tbl={}},SW)
|
||||
end
|
||||
|
||||
--- create a file-like object for reading from a given string.
|
||||
-- Implements `file:read`.
|
||||
-- @string s The input string.
|
||||
-- @usage fs = open '20 10'; x,y = f:read ('*n','*n'); assert(x == 20 and y == 10)
|
||||
function stringio.open(s)
|
||||
return setmetatable({str=s,i=1},SR)
|
||||
end
|
||||
|
||||
function stringio.lines(s,...)
|
||||
return stringio.open(s):lines(...)
|
||||
end
|
||||
|
||||
return stringio
|
917
Documentation/compiler/pl/stringx.lua
Normal file
917
Documentation/compiler/pl/stringx.lua
Normal file
|
@ -0,0 +1,917 @@
|
|||
--- Python-style extended string library.
|
||||
--
|
||||
-- see 3.6.1 of the Python reference.
|
||||
-- If you want to make these available as string methods, then say
|
||||
-- `stringx.import()` to bring them into the standard `string` table.
|
||||
--
|
||||
-- See @{03-strings.md|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.types`
|
||||
-- @module pl.stringx
|
||||
local utils = require 'pl.utils'
|
||||
local is_callable = require 'pl.types'.is_callable
|
||||
local string = string
|
||||
local find = string.find
|
||||
local type,setmetatable,ipairs = type,setmetatable,ipairs
|
||||
local error = error
|
||||
local gsub = string.gsub
|
||||
local rep = string.rep
|
||||
local sub = string.sub
|
||||
local reverse = string.reverse
|
||||
local concat = table.concat
|
||||
local append = table.insert
|
||||
local remove = table.remove
|
||||
local escape = utils.escape
|
||||
local ceil, max = math.ceil, math.max
|
||||
local assert_arg,usplit = utils.assert_arg,utils.split
|
||||
local lstrip
|
||||
local unpack = utils.unpack
|
||||
local pack = utils.pack
|
||||
|
||||
local function assert_string (n,s)
|
||||
assert_arg(n,s,'string')
|
||||
end
|
||||
|
||||
local function non_empty(s)
|
||||
return #s > 0
|
||||
end
|
||||
|
||||
local function assert_nonempty_string(n,s)
|
||||
assert_arg(n,s,'string',non_empty,'must be a non-empty string')
|
||||
end
|
||||
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local stringx = {}
|
||||
|
||||
------------------
|
||||
-- String Predicates
|
||||
-- @section predicates
|
||||
|
||||
--- does s only contain alphabetic characters?
|
||||
-- @string s a string
|
||||
function stringx.isalpha(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%a+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain digits?
|
||||
-- @string s a string
|
||||
function stringx.isdigit(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%d+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain alphanumeric characters?
|
||||
-- @string s a string
|
||||
function stringx.isalnum(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%w+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain whitespace?
|
||||
-- Matches on pattern '%s' so matches space, newline, tabs, etc.
|
||||
-- @string s a string
|
||||
function stringx.isspace(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%s+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain lower case characters?
|
||||
-- @string s a string
|
||||
function stringx.islower(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^[%l%s]+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain upper case characters?
|
||||
-- @string s a string
|
||||
function stringx.isupper(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^[%u%s]+$') == 1
|
||||
end
|
||||
|
||||
local function raw_startswith(s, prefix)
|
||||
return find(s,prefix,1,true) == 1
|
||||
end
|
||||
|
||||
local function raw_endswith(s, suffix)
|
||||
return #s >= #suffix and find(s, suffix, #s-#suffix+1, true) and true or false
|
||||
end
|
||||
|
||||
local function test_affixes(s, affixes, fn)
|
||||
if type(affixes) == 'string' then
|
||||
return fn(s,affixes)
|
||||
elseif type(affixes) == 'table' then
|
||||
for _,affix in ipairs(affixes) do
|
||||
if fn(s,affix) then return true end
|
||||
end
|
||||
return false
|
||||
else
|
||||
error(("argument #2 expected a 'string' or a 'table', got a '%s'"):format(type(affixes)))
|
||||
end
|
||||
end
|
||||
|
||||
--- does s start with prefix or one of prefixes?
|
||||
-- @string s a string
|
||||
-- @param prefix a string or an array of strings
|
||||
function stringx.startswith(s,prefix)
|
||||
assert_string(1,s)
|
||||
return test_affixes(s,prefix,raw_startswith)
|
||||
end
|
||||
|
||||
--- does s end with suffix or one of suffixes?
|
||||
-- @string s a string
|
||||
-- @param suffix a string or an array of strings
|
||||
function stringx.endswith(s,suffix)
|
||||
assert_string(1,s)
|
||||
return test_affixes(s,suffix,raw_endswith)
|
||||
end
|
||||
|
||||
--- Strings and Lists
|
||||
-- @section lists
|
||||
|
||||
--- concatenate the strings using this string as a delimiter.
|
||||
-- Note that the arguments are reversed from `string.concat`.
|
||||
-- @string s the string
|
||||
-- @param seq a table of strings or numbers
|
||||
-- @usage stringx.join(' ', {1,2,3}) == '1 2 3'
|
||||
function stringx.join(s,seq)
|
||||
assert_string(1,s)
|
||||
return concat(seq,s)
|
||||
end
|
||||
|
||||
--- Split a string into a list of lines.
|
||||
-- `"\r"`, `"\n"`, and `"\r\n"` are considered line ends.
|
||||
-- They are not included in the lines unless `keepends` is passed.
|
||||
-- Terminal line end does not produce an extra line.
|
||||
-- Splitting an empty string results in an empty list.
|
||||
-- @string s the string.
|
||||
-- @bool[opt] keep_ends include line ends.
|
||||
-- @return List of lines
|
||||
function stringx.splitlines(s, keep_ends)
|
||||
assert_string(1, s)
|
||||
local res = {}
|
||||
local pos = 1
|
||||
while true do
|
||||
local line_end_pos = find(s, '[\r\n]', pos)
|
||||
if not line_end_pos then
|
||||
break
|
||||
end
|
||||
|
||||
local line_end = sub(s, line_end_pos, line_end_pos)
|
||||
if line_end == '\r' and sub(s, line_end_pos + 1, line_end_pos + 1) == '\n' then
|
||||
line_end = '\r\n'
|
||||
end
|
||||
|
||||
local line = sub(s, pos, line_end_pos - 1)
|
||||
if keep_ends then
|
||||
line = line .. line_end
|
||||
end
|
||||
append(res, line)
|
||||
|
||||
pos = line_end_pos + #line_end
|
||||
end
|
||||
|
||||
if pos <= #s then
|
||||
append(res, sub(s, pos))
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- split a string into a list of strings using a delimiter.
|
||||
-- @function split
|
||||
-- @string s the string
|
||||
-- @string[opt] re a delimiter (defaults to whitespace)
|
||||
-- @int[opt] n maximum number of results
|
||||
-- @return List
|
||||
-- @usage #(stringx.split('one two')) == 2
|
||||
-- @usage stringx.split('one,two,three', ',') == List{'one','two','three'}
|
||||
-- @usage stringx.split('one,two,three', ',', 2) == List{'one','two,three'}
|
||||
function stringx.split(s,re,n)
|
||||
assert_string(1,s)
|
||||
local plain = true
|
||||
if not re then -- default spaces
|
||||
s = lstrip(s)
|
||||
plain = false
|
||||
end
|
||||
local res = usplit(s,re,plain,n)
|
||||
if re and re ~= '' and
|
||||
find(s,re,-#re,true) and
|
||||
(n or math.huge) > #res then
|
||||
res[#res+1] = ""
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- replace all tabs in s with tabsize spaces. If not specified, tabsize defaults to 8.
|
||||
-- Tab stops will be honored.
|
||||
-- @string s the string
|
||||
-- @int tabsize[opt=8] number of spaces to expand each tab
|
||||
-- @return expanded string
|
||||
-- @usage stringx.expandtabs('\tone,two,three', 4) == ' one,two,three'
|
||||
-- @usage stringx.expandtabs(' \tone,two,three', 4) == ' one,two,three'
|
||||
function stringx.expandtabs(s,tabsize)
|
||||
assert_string(1,s)
|
||||
tabsize = tabsize or 8
|
||||
return (s:gsub("([^\t\r\n]*)\t", function(before_tab)
|
||||
if tabsize == 0 then
|
||||
return before_tab
|
||||
else
|
||||
return before_tab .. (" "):rep(tabsize - #before_tab % tabsize)
|
||||
end
|
||||
end))
|
||||
end
|
||||
|
||||
--- Finding and Replacing
|
||||
-- @section find
|
||||
|
||||
local function _find_all(s,sub,first,last,allow_overlap)
|
||||
first = first or 1
|
||||
last = last or #s
|
||||
if sub == '' then return last+1,last-first+1 end
|
||||
local i1,i2 = find(s,sub,first,true)
|
||||
local res
|
||||
local k = 0
|
||||
while i1 do
|
||||
if last and i2 > last then break end
|
||||
res = i1
|
||||
k = k + 1
|
||||
if allow_overlap then
|
||||
i1,i2 = find(s,sub,i1+1,true)
|
||||
else
|
||||
i1,i2 = find(s,sub,i2+1,true)
|
||||
end
|
||||
end
|
||||
return res,k
|
||||
end
|
||||
|
||||
--- find index of first instance of sub in s from the left.
|
||||
-- @string s the string
|
||||
-- @string sub substring
|
||||
-- @int[opt] first first index
|
||||
-- @int[opt] last last index
|
||||
-- @return start index, or nil if not found
|
||||
function stringx.lfind(s,sub,first,last)
|
||||
assert_string(1,s)
|
||||
assert_string(2,sub)
|
||||
local i1, i2 = find(s,sub,first,true)
|
||||
|
||||
if i1 and (not last or i2 <= last) then
|
||||
return i1
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- find index of first instance of sub in s from the right.
|
||||
-- @string s the string
|
||||
-- @string sub substring
|
||||
-- @int[opt] first first index
|
||||
-- @int[opt] last last index
|
||||
-- @return start index, or nil if not found
|
||||
function stringx.rfind(s,sub,first,last)
|
||||
assert_string(1,s)
|
||||
assert_string(2,sub)
|
||||
return (_find_all(s,sub,first,last,true))
|
||||
end
|
||||
|
||||
--- replace up to n instances of old by new in the string s.
|
||||
-- If n is not present, replace all instances.
|
||||
-- @string s the string
|
||||
-- @string old the target substring
|
||||
-- @string new the substitution
|
||||
-- @int[opt] n optional maximum number of substitutions
|
||||
-- @return result string
|
||||
function stringx.replace(s,old,new,n)
|
||||
assert_string(1,s)
|
||||
assert_string(2,old)
|
||||
assert_string(3,new)
|
||||
return (gsub(s,escape(old),new:gsub('%%','%%%%'),n))
|
||||
end
|
||||
|
||||
--- count all instances of substring in string.
|
||||
-- @string s the string
|
||||
-- @string sub substring
|
||||
-- @bool[opt] allow_overlap allow matches to overlap
|
||||
-- @usage
|
||||
-- assert(stringx.count('banana', 'ana') == 1)
|
||||
-- assert(stringx.count('banana', 'ana', true) == 2)
|
||||
function stringx.count(s,sub,allow_overlap)
|
||||
assert_string(1,s)
|
||||
local _,k = _find_all(s,sub,1,false,allow_overlap)
|
||||
return k
|
||||
end
|
||||
|
||||
--- Stripping and Justifying
|
||||
-- @section strip
|
||||
|
||||
local function _just(s,w,ch,left,right)
|
||||
local n = #s
|
||||
if w > n then
|
||||
if not ch then ch = ' ' end
|
||||
local f1,f2
|
||||
if left and right then
|
||||
local rn = ceil((w-n)/2)
|
||||
local ln = w - n - rn
|
||||
f1 = rep(ch,ln)
|
||||
f2 = rep(ch,rn)
|
||||
elseif right then
|
||||
f1 = rep(ch,w-n)
|
||||
f2 = ''
|
||||
else
|
||||
f2 = rep(ch,w-n)
|
||||
f1 = ''
|
||||
end
|
||||
return f1..s..f2
|
||||
else
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
--- left-justify s with width w.
|
||||
-- @string s the string
|
||||
-- @int w width of justification
|
||||
-- @string[opt=' '] ch padding character
|
||||
-- @usage stringx.ljust('hello', 10, '*') == '*****hello'
|
||||
function stringx.ljust(s,w,ch)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,w,'number')
|
||||
return _just(s,w,ch,true,false)
|
||||
end
|
||||
|
||||
--- right-justify s with width w.
|
||||
-- @string s the string
|
||||
-- @int w width of justification
|
||||
-- @string[opt=' '] ch padding character
|
||||
-- @usage stringx.rjust('hello', 10, '*') == 'hello*****'
|
||||
function stringx.rjust(s,w,ch)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,w,'number')
|
||||
return _just(s,w,ch,false,true)
|
||||
end
|
||||
|
||||
--- center-justify s with width w.
|
||||
-- @string s the string
|
||||
-- @int w width of justification
|
||||
-- @string[opt=' '] ch padding character
|
||||
-- @usage stringx.center('hello', 10, '*') == '**hello***'
|
||||
function stringx.center(s,w,ch)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,w,'number')
|
||||
return _just(s,w,ch,true,true)
|
||||
end
|
||||
|
||||
local function _strip(s,left,right,chrs)
|
||||
if not chrs then
|
||||
chrs = '%s'
|
||||
else
|
||||
chrs = '['..escape(chrs)..']'
|
||||
end
|
||||
local f = 1
|
||||
local t
|
||||
if left then
|
||||
local i1,i2 = find(s,'^'..chrs..'*')
|
||||
if i2 >= i1 then
|
||||
f = i2+1
|
||||
end
|
||||
end
|
||||
if right then
|
||||
if #s < 200 then
|
||||
local i1,i2 = find(s,chrs..'*$',f)
|
||||
if i2 >= i1 then
|
||||
t = i1-1
|
||||
end
|
||||
else
|
||||
local rs = reverse(s)
|
||||
local i1,i2 = find(rs, '^'..chrs..'*')
|
||||
if i2 >= i1 then
|
||||
t = -i2-1
|
||||
end
|
||||
end
|
||||
end
|
||||
return sub(s,f,t)
|
||||
end
|
||||
|
||||
--- trim any characters on the left of s.
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] chrs default any whitespace character,
|
||||
-- but can be a string of characters to be trimmed
|
||||
function stringx.lstrip(s,chrs)
|
||||
assert_string(1,s)
|
||||
return _strip(s,true,false,chrs)
|
||||
end
|
||||
lstrip = stringx.lstrip
|
||||
|
||||
--- trim any characters on the right of s.
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] chrs default any whitespace character,
|
||||
-- but can be a string of characters to be trimmed
|
||||
function stringx.rstrip(s,chrs)
|
||||
assert_string(1,s)
|
||||
return _strip(s,false,true,chrs)
|
||||
end
|
||||
|
||||
--- trim any characters on both left and right of s.
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] chrs default any whitespace character,
|
||||
-- but can be a string of characters to be trimmed
|
||||
-- @usage stringx.strip(' --== Hello ==-- ', "- =") --> 'Hello'
|
||||
function stringx.strip(s,chrs)
|
||||
assert_string(1,s)
|
||||
return _strip(s,true,true,chrs)
|
||||
end
|
||||
|
||||
--- Partitioning Strings
|
||||
-- @section partitioning
|
||||
|
||||
--- split a string using a pattern. Note that at least one value will be returned!
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] re a Lua string pattern (defaults to whitespace)
|
||||
-- @return the parts of the string
|
||||
-- @usage a,b = line:splitv('=')
|
||||
-- @see utils.splitv
|
||||
function stringx.splitv(s,re)
|
||||
assert_string(1,s)
|
||||
return utils.splitv(s,re)
|
||||
end
|
||||
|
||||
-- The partition functions split a string using a delimiter into three parts:
|
||||
-- the part before, the delimiter itself, and the part afterwards
|
||||
local function _partition(p,delim,fn)
|
||||
local i1,i2 = fn(p,delim)
|
||||
if not i1 or i1 == -1 then
|
||||
return p,'',''
|
||||
else
|
||||
if not i2 then i2 = i1 end
|
||||
return sub(p,1,i1-1),sub(p,i1,i2),sub(p,i2+1)
|
||||
end
|
||||
end
|
||||
|
||||
--- partition the string using first occurance of a delimiter
|
||||
-- @string s the string
|
||||
-- @string ch delimiter (match as plain string, no patterns)
|
||||
-- @return part before ch
|
||||
-- @return ch
|
||||
-- @return part after ch
|
||||
-- @usage {stringx.partition('a,b,c', ','))} == {'a', ',', 'b,c'}
|
||||
-- @usage {stringx.partition('abc', 'x'))} == {'abc', '', ''}
|
||||
function stringx.partition(s,ch)
|
||||
assert_string(1,s)
|
||||
assert_nonempty_string(2,ch)
|
||||
return _partition(s,ch,stringx.lfind)
|
||||
end
|
||||
|
||||
--- partition the string p using last occurance of a delimiter
|
||||
-- @string s the string
|
||||
-- @string ch delimiter (match as plain string, no patterns)
|
||||
-- @return part before ch
|
||||
-- @return ch
|
||||
-- @return part after ch
|
||||
-- @usage {stringx.rpartition('a,b,c', ','))} == {'a,b', ',', 'c'}
|
||||
-- @usage {stringx.rpartition('abc', 'x'))} == {'', '', 'abc'}
|
||||
function stringx.rpartition(s,ch)
|
||||
assert_string(1,s)
|
||||
assert_nonempty_string(2,ch)
|
||||
local a,b,c = _partition(s,ch,stringx.rfind)
|
||||
if a == s then -- no match found
|
||||
return c,b,a
|
||||
end
|
||||
return a,b,c
|
||||
end
|
||||
|
||||
--- return the 'character' at the index.
|
||||
-- @string s the string
|
||||
-- @int idx an index (can be negative)
|
||||
-- @return a substring of length 1 if successful, empty string otherwise.
|
||||
function stringx.at(s,idx)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,idx,'number')
|
||||
return sub(s,idx,idx)
|
||||
end
|
||||
|
||||
|
||||
--- Text handling
|
||||
-- @section text
|
||||
|
||||
|
||||
--- indent a multiline string.
|
||||
-- @tparam string s the (multiline) string
|
||||
-- @tparam integer n the size of the indent
|
||||
-- @tparam[opt=' '] string ch the character to use when indenting
|
||||
-- @return indented string
|
||||
function stringx.indent (s,n,ch)
|
||||
assert_arg(1,s,'string')
|
||||
assert_arg(2,n,'number')
|
||||
local lines = usplit(s ,'\n')
|
||||
local prefix = string.rep(ch or ' ',n)
|
||||
for i, line in ipairs(lines) do
|
||||
lines[i] = prefix..line
|
||||
end
|
||||
return concat(lines,'\n')..'\n'
|
||||
end
|
||||
|
||||
|
||||
--- dedent a multiline string by removing any initial indent.
|
||||
-- useful when working with [[..]] strings.
|
||||
-- Empty lines are ignored.
|
||||
-- @tparam string s the (multiline) string
|
||||
-- @return a string with initial indent zero.
|
||||
-- @usage
|
||||
-- local s = dedent [[
|
||||
-- One
|
||||
--
|
||||
-- Two
|
||||
--
|
||||
-- Three
|
||||
-- ]]
|
||||
-- assert(s == [[
|
||||
-- One
|
||||
--
|
||||
-- Two
|
||||
--
|
||||
-- Three
|
||||
-- ]])
|
||||
function stringx.dedent (s)
|
||||
assert_arg(1,s,'string')
|
||||
local lst = usplit(s,'\n')
|
||||
if #lst>0 then
|
||||
local ind_size = math.huge
|
||||
for i, line in ipairs(lst) do
|
||||
local i1, i2 = lst[i]:find('^%s*[^%s]')
|
||||
if i1 and i2 < ind_size then
|
||||
ind_size = i2
|
||||
end
|
||||
end
|
||||
for i, line in ipairs(lst) do
|
||||
lst[i] = lst[i]:sub(ind_size, -1)
|
||||
end
|
||||
end
|
||||
return concat(lst,'\n')..'\n'
|
||||
end
|
||||
|
||||
|
||||
|
||||
do
|
||||
local buildline = function(words, size, breaklong)
|
||||
-- if overflow is set, a word longer than size, will overflow the size
|
||||
-- otherwise it will be chopped in line-length pieces
|
||||
local line = {}
|
||||
if #words[1] > size then
|
||||
-- word longer than line
|
||||
if not breaklong then
|
||||
line[1] = words[1]
|
||||
remove(words, 1)
|
||||
else
|
||||
line[1] = words[1]:sub(1, size)
|
||||
words[1] = words[1]:sub(size + 1, -1)
|
||||
end
|
||||
else
|
||||
local len = 0
|
||||
while words[1] and (len + #words[1] <= size) or
|
||||
(len == 0 and #words[1] == size) do
|
||||
if words[1] ~= "" then
|
||||
line[#line+1] = words[1]
|
||||
len = len + #words[1] + 1
|
||||
end
|
||||
remove(words, 1)
|
||||
end
|
||||
end
|
||||
return stringx.strip(concat(line, " ")), words
|
||||
end
|
||||
|
||||
--- format a paragraph into lines so that they fit into a line width.
|
||||
-- It will not break long words by default, so lines can be over the length
|
||||
-- to that extent.
|
||||
-- @tparam string s the string to format
|
||||
-- @tparam[opt=70] integer width the margin width
|
||||
-- @tparam[opt=false] boolean breaklong if truthy, words longer than the width given will be forced split.
|
||||
-- @return a list of lines (List object), use `fill` to return a string instead of a `List`.
|
||||
-- @see pl.List
|
||||
-- @see fill
|
||||
stringx.wrap = function(s, width, breaklong)
|
||||
s = s:gsub('\n',' ') -- remove line breaks
|
||||
s = stringx.strip(s) -- remove leading/trailing whitespace
|
||||
if s == "" then
|
||||
return { "" }
|
||||
end
|
||||
width = width or 70
|
||||
local out = {}
|
||||
local words = usplit(s, "%s")
|
||||
while words[1] do
|
||||
out[#out+1], words = buildline(words, width, breaklong)
|
||||
end
|
||||
return makelist(out)
|
||||
end
|
||||
end
|
||||
|
||||
--- format a paragraph so that it fits into a line width.
|
||||
-- @tparam string s the string to format
|
||||
-- @tparam[opt=70] integer width the margin width
|
||||
-- @tparam[opt=false] boolean breaklong if truthy, words longer than the width given will be forced split.
|
||||
-- @return a string, use `wrap` to return a list of lines instead of a string.
|
||||
-- @see wrap
|
||||
function stringx.fill (s,width,breaklong)
|
||||
return concat(stringx.wrap(s,width,breaklong),'\n') .. '\n'
|
||||
end
|
||||
|
||||
--- Template
|
||||
-- @section Template
|
||||
|
||||
|
||||
local function _substitute(s,tbl,safe)
|
||||
local subst
|
||||
if is_callable(tbl) then
|
||||
subst = tbl
|
||||
else
|
||||
function subst(f)
|
||||
local s = tbl[f]
|
||||
if not s then
|
||||
if safe then
|
||||
return f
|
||||
else
|
||||
error("not present in table "..f)
|
||||
end
|
||||
else
|
||||
return s
|
||||
end
|
||||
end
|
||||
end
|
||||
local res = gsub(s,'%${([%w_]+)}',subst)
|
||||
return (gsub(res,'%$([%w_]+)',subst))
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Template = {}
|
||||
stringx.Template = Template
|
||||
Template.__index = Template
|
||||
setmetatable(Template, {
|
||||
__call = function(obj,tmpl)
|
||||
return Template.new(tmpl)
|
||||
end
|
||||
})
|
||||
|
||||
--- Creates a new Template class.
|
||||
-- This is a shortcut to `Template.new(tmpl)`.
|
||||
-- @tparam string tmpl the template string
|
||||
-- @function Template
|
||||
-- @treturn Template
|
||||
function Template.new(tmpl)
|
||||
assert_arg(1,tmpl,'string')
|
||||
local res = {}
|
||||
res.tmpl = tmpl
|
||||
setmetatable(res,Template)
|
||||
return res
|
||||
end
|
||||
|
||||
--- substitute values into a template, throwing an error.
|
||||
-- This will throw an error if no name is found.
|
||||
-- @tparam table tbl a table of name-value pairs.
|
||||
-- @return string with place holders substituted
|
||||
function Template:substitute(tbl)
|
||||
assert_arg(1,tbl,'table')
|
||||
return _substitute(self.tmpl,tbl,false)
|
||||
end
|
||||
|
||||
--- substitute values into a template.
|
||||
-- This version just passes unknown names through.
|
||||
-- @tparam table tbl a table of name-value pairs.
|
||||
-- @return string with place holders substituted
|
||||
function Template:safe_substitute(tbl)
|
||||
assert_arg(1,tbl,'table')
|
||||
return _substitute(self.tmpl,tbl,true)
|
||||
end
|
||||
|
||||
--- substitute values into a template, preserving indentation. <br>
|
||||
-- If the value is a multiline string _or_ a template, it will insert
|
||||
-- the lines at the correct indentation. <br>
|
||||
-- Furthermore, if a template, then that template will be substituted
|
||||
-- using the same table.
|
||||
-- @tparam table tbl a table of name-value pairs.
|
||||
-- @return string with place holders substituted
|
||||
function Template:indent_substitute(tbl)
|
||||
assert_arg(1,tbl,'table')
|
||||
if not self.strings then
|
||||
self.strings = usplit(self.tmpl,'\n')
|
||||
end
|
||||
|
||||
-- the idea is to substitute line by line, grabbing any spaces as
|
||||
-- well as the $var. If the value to be substituted contains newlines,
|
||||
-- then we split that into lines and adjust the indent before inserting.
|
||||
local function subst(line)
|
||||
return line:gsub('(%s*)%$([%w_]+)',function(sp,f)
|
||||
local subtmpl
|
||||
local s = tbl[f]
|
||||
if not s then error("not present in table "..f) end
|
||||
if getmetatable(s) == Template then
|
||||
subtmpl = s
|
||||
s = s.tmpl
|
||||
else
|
||||
s = tostring(s)
|
||||
end
|
||||
if s:find '\n' then
|
||||
local lines = usplit(s, '\n')
|
||||
for i, line in ipairs(lines) do
|
||||
lines[i] = sp..line
|
||||
end
|
||||
s = concat(lines, '\n') .. '\n'
|
||||
end
|
||||
if subtmpl then
|
||||
return _substitute(s, tbl)
|
||||
else
|
||||
return s
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
for i, line in ipairs(self.strings) do
|
||||
lines[i] = subst(line)
|
||||
end
|
||||
return concat(lines,'\n')..'\n'
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Miscelaneous
|
||||
-- @section misc
|
||||
|
||||
--- return an iterator over all lines in a string
|
||||
-- @string s the string
|
||||
-- @return an iterator
|
||||
-- @usage
|
||||
-- local line_no = 1
|
||||
-- for line in stringx.lines(some_text) do
|
||||
-- print(line_no, line)
|
||||
-- line_no = line_no + 1
|
||||
-- end
|
||||
function stringx.lines(s)
|
||||
assert_string(1,s)
|
||||
if not s:find '\n$' then s = s..'\n' end
|
||||
return s:gmatch('([^\n]*)\n')
|
||||
end
|
||||
|
||||
--- inital word letters uppercase ('title case').
|
||||
-- Here 'words' mean chunks of non-space characters.
|
||||
-- @string s the string
|
||||
-- @return a string with each word's first letter uppercase
|
||||
-- @usage stringx.title("hello world") == "Hello World")
|
||||
function stringx.title(s)
|
||||
assert_string(1,s)
|
||||
return (s:gsub('(%S)(%S*)',function(f,r)
|
||||
return f:upper()..r:lower()
|
||||
end))
|
||||
end
|
||||
|
||||
stringx.capitalize = stringx.title
|
||||
|
||||
do
|
||||
local ellipsis = '...'
|
||||
local n_ellipsis = #ellipsis
|
||||
|
||||
--- Return a shortened version of a string.
|
||||
-- Fits string within w characters. Removed characters are marked with ellipsis.
|
||||
-- @string s the string
|
||||
-- @int w the maxinum size allowed
|
||||
-- @bool tail true if we want to show the end of the string (head otherwise)
|
||||
-- @usage ('1234567890'):shorten(8) == '12345...'
|
||||
-- @usage ('1234567890'):shorten(8, true) == '...67890'
|
||||
-- @usage ('1234567890'):shorten(20) == '1234567890'
|
||||
function stringx.shorten(s,w,tail)
|
||||
assert_string(1,s)
|
||||
if #s > w then
|
||||
if w < n_ellipsis then return ellipsis:sub(1,w) end
|
||||
if tail then
|
||||
local i = #s - w + 1 + n_ellipsis
|
||||
return ellipsis .. s:sub(i)
|
||||
else
|
||||
return s:sub(1,w-n_ellipsis) .. ellipsis
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- Utility function that finds any patterns that match a long string's an open or close.
|
||||
-- Note that having this function use the least number of equal signs that is possible is a harder algorithm to come up with.
|
||||
-- Right now, it simply returns the greatest number of them found.
|
||||
-- @param s The string
|
||||
-- @return 'nil' if not found. If found, the maximum number of equal signs found within all matches.
|
||||
local function has_lquote(s)
|
||||
local lstring_pat = '([%[%]])(=*)%1'
|
||||
local equals, new_equals, _
|
||||
local finish = 1
|
||||
repeat
|
||||
_, finish, _, new_equals = s:find(lstring_pat, finish)
|
||||
if new_equals then
|
||||
equals = max(equals or 0, #new_equals)
|
||||
end
|
||||
until not new_equals
|
||||
|
||||
return equals
|
||||
end
|
||||
|
||||
--- Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result.
|
||||
-- @param s The string to be quoted.
|
||||
-- @return The quoted string.
|
||||
function stringx.quote_string(s)
|
||||
assert_string(1,s)
|
||||
-- Find out if there are any embedded long-quote sequences that may cause issues.
|
||||
-- This is important when strings are embedded within strings, like when serializing.
|
||||
-- Append a closing bracket to catch unfinished long-quote sequences at the end of the string.
|
||||
local equal_signs = has_lquote(s .. "]")
|
||||
|
||||
-- Note that strings containing "\r" can't be quoted using long brackets
|
||||
-- as Lua lexer converts all newlines to "\n" within long strings.
|
||||
if (s:find("\n") or equal_signs) and not s:find("\r") then
|
||||
-- If there is an embedded sequence that matches a long quote, then
|
||||
-- find the one with the maximum number of = signs and add one to that number.
|
||||
equal_signs = ("="):rep((equal_signs or -1) + 1)
|
||||
-- Long strings strip out leading newline. We want to retain that, when quoting.
|
||||
if s:find("^\n") then s = "\n" .. s end
|
||||
local lbracket, rbracket =
|
||||
"[" .. equal_signs .. "[",
|
||||
"]" .. equal_signs .. "]"
|
||||
s = lbracket .. s .. rbracket
|
||||
else
|
||||
-- Escape funny stuff. Lua 5.1 does not handle "\r" correctly.
|
||||
s = ("%q"):format(s):gsub("\r", "\\r")
|
||||
end
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Python-style formatting operator.
|
||||
-- Calling `text.format_operator()` overloads the % operator for strings to give
|
||||
-- Python/Ruby style formated output.
|
||||
-- This is extended to also do template-like substitution for map-like data.
|
||||
--
|
||||
-- Note this goes further than the original, and will allow these cases:
|
||||
--
|
||||
-- 1. a single value
|
||||
-- 2. a list of values
|
||||
-- 3. a map of var=value pairs
|
||||
-- 4. a function, as in gsub
|
||||
--
|
||||
-- For the second two cases, it uses $-variable substituion.
|
||||
--
|
||||
-- When called, this function will monkey-patch the global `string` metatable by
|
||||
-- adding a `__mod` method.
|
||||
--
|
||||
-- See <a href="http://lua-users.org/wiki/StringInterpolation">the lua-users wiki</a>
|
||||
--
|
||||
-- @usage
|
||||
-- require 'pl.text'.format_operator()
|
||||
-- local out1 = '%s = %5.3f' % {'PI',math.pi} --> 'PI = 3.142'
|
||||
-- local out2 = '$name = $value' % {name='dog',value='Pluto'} --> 'dog = Pluto'
|
||||
function stringx.format_operator()
|
||||
|
||||
local format = string.format
|
||||
|
||||
-- a more forgiving version of string.format, which applies
|
||||
-- tostring() to any value with a %s format.
|
||||
local function formatx (fmt,...)
|
||||
local args = pack(...)
|
||||
local i = 1
|
||||
for p in fmt:gmatch('%%.') do
|
||||
if p == '%s' and type(args[i]) ~= 'string' then
|
||||
args[i] = tostring(args[i])
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return format(fmt,unpack(args))
|
||||
end
|
||||
|
||||
local function basic_subst(s,t)
|
||||
return (s:gsub('%$([%w_]+)',t))
|
||||
end
|
||||
|
||||
getmetatable("").__mod = function(a, b)
|
||||
if b == nil then
|
||||
return a
|
||||
elseif type(b) == "table" and getmetatable(b) == nil then
|
||||
if #b == 0 then -- assume a map-like table
|
||||
return _substitute(a,b,true)
|
||||
else
|
||||
return formatx(a,unpack(b))
|
||||
end
|
||||
elseif type(b) == 'function' then
|
||||
return basic_subst(a,b)
|
||||
else
|
||||
return formatx(a,b)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- import the stringx functions into the global string (meta)table
|
||||
function stringx.import()
|
||||
utils.import(stringx,string)
|
||||
end
|
||||
|
||||
return stringx
|
999
Documentation/compiler/pl/tablex.lua
Normal file
999
Documentation/compiler/pl/tablex.lua
Normal file
|
@ -0,0 +1,999 @@
|
|||
--- Extended operations on Lua tables.
|
||||
--
|
||||
-- See @{02-arrays.md.Useful_Operations_on_Tables|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.types`
|
||||
-- @module pl.tablex
|
||||
local utils = require ('pl.utils')
|
||||
local types = require ('pl.types')
|
||||
local getmetatable,setmetatable,require = getmetatable,setmetatable,require
|
||||
local tsort,append,remove = table.sort,table.insert,table.remove
|
||||
local min = math.min
|
||||
local pairs,type,unpack,select,tostring = pairs,type,utils.unpack,select,tostring
|
||||
local function_arg = utils.function_arg
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
local tablex = {}
|
||||
|
||||
-- generally, functions that make copies of tables try to preserve the metatable.
|
||||
-- However, when the source has no obvious type, then we attach appropriate metatables
|
||||
-- like List, Map, etc to the result.
|
||||
local function setmeta (res,tbl,pl_class)
|
||||
local mt = getmetatable(tbl) or pl_class and require('pl.' .. pl_class)
|
||||
return mt and setmetatable(res, mt) or res
|
||||
end
|
||||
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local function makemap(m)
|
||||
return setmetatable(m, require('pl.Map'))
|
||||
end
|
||||
|
||||
local function complain (idx,msg)
|
||||
error(('argument %d is not %s'):format(idx,msg),3)
|
||||
end
|
||||
|
||||
local function assert_arg_indexable (idx,val)
|
||||
if not types.is_indexable(val) then
|
||||
complain(idx,"indexable")
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_arg_iterable (idx,val)
|
||||
if not types.is_iterable(val) then
|
||||
complain(idx,"iterable")
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_arg_writeable (idx,val)
|
||||
if not types.is_writeable(val) then
|
||||
complain(idx,"writeable")
|
||||
end
|
||||
end
|
||||
|
||||
--- copy a table into another, in-place.
|
||||
-- @within Copying
|
||||
-- @tab t1 destination table
|
||||
-- @tab t2 source (actually any iterable object)
|
||||
-- @return first table
|
||||
function tablex.update (t1,t2)
|
||||
assert_arg_writeable(1,t1)
|
||||
assert_arg_iterable(2,t2)
|
||||
for k,v in pairs(t2) do
|
||||
t1[k] = v
|
||||
end
|
||||
return t1
|
||||
end
|
||||
|
||||
--- total number of elements in this table.
|
||||
-- Note that this is distinct from `#t`, which is the number
|
||||
-- of values in the array part; this value will always
|
||||
-- be greater or equal. The difference gives the size of
|
||||
-- the hash part, for practical purposes. Works for any
|
||||
-- object with a __pairs metamethod.
|
||||
-- @tab t a table
|
||||
-- @return the size
|
||||
function tablex.size (t)
|
||||
assert_arg_iterable(1,t)
|
||||
local i = 0
|
||||
for k in pairs(t) do i = i + 1 end
|
||||
return i
|
||||
end
|
||||
|
||||
--- make a shallow copy of a table
|
||||
-- @within Copying
|
||||
-- @tab t an iterable source
|
||||
-- @return new table
|
||||
function tablex.copy (t)
|
||||
assert_arg_iterable(1,t)
|
||||
local res = {}
|
||||
for k,v in pairs(t) do
|
||||
res[k] = v
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function cycle_aware_copy(t, cache)
|
||||
if type(t) ~= 'table' then return t end
|
||||
if cache[t] then return cache[t] end
|
||||
assert_arg_iterable(1,t)
|
||||
local res = {}
|
||||
cache[t] = res
|
||||
local mt = getmetatable(t)
|
||||
for k,v in pairs(t) do
|
||||
k = cycle_aware_copy(k, cache)
|
||||
v = cycle_aware_copy(v, cache)
|
||||
res[k] = v
|
||||
end
|
||||
setmetatable(res,mt)
|
||||
return res
|
||||
end
|
||||
|
||||
--- make a deep copy of a table, recursively copying all the keys and fields.
|
||||
-- This supports cycles in tables; cycles will be reproduced in the copy.
|
||||
-- This will also set the copied table's metatable to that of the original.
|
||||
-- @within Copying
|
||||
-- @tab t A table
|
||||
-- @return new table
|
||||
function tablex.deepcopy(t)
|
||||
return cycle_aware_copy(t,{})
|
||||
end
|
||||
|
||||
local abs = math.abs
|
||||
|
||||
local function cycle_aware_compare(t1,t2,ignore_mt,eps,cache)
|
||||
if cache[t1] and cache[t1][t2] then return true end
|
||||
local ty1 = type(t1)
|
||||
local ty2 = type(t2)
|
||||
if ty1 ~= ty2 then return false end
|
||||
-- non-table types can be directly compared
|
||||
if ty1 ~= 'table' then
|
||||
if ty1 == 'number' and eps then return abs(t1-t2) < eps end
|
||||
return t1 == t2
|
||||
end
|
||||
-- as well as tables which have the metamethod __eq
|
||||
local mt = getmetatable(t1)
|
||||
if not ignore_mt and mt and mt.__eq then return t1 == t2 end
|
||||
for k1 in pairs(t1) do
|
||||
if t2[k1]==nil then return false end
|
||||
end
|
||||
for k2 in pairs(t2) do
|
||||
if t1[k2]==nil then return false end
|
||||
end
|
||||
cache[t1] = cache[t1] or {}
|
||||
cache[t1][t2] = true
|
||||
for k1,v1 in pairs(t1) do
|
||||
local v2 = t2[k1]
|
||||
if not cycle_aware_compare(v1,v2,ignore_mt,eps,cache) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- compare two values.
|
||||
-- if they are tables, then compare their keys and fields recursively.
|
||||
-- @within Comparing
|
||||
-- @param t1 A value
|
||||
-- @param t2 A value
|
||||
-- @bool[opt] ignore_mt if true, ignore __eq metamethod (default false)
|
||||
-- @number[opt] eps if defined, then used for any number comparisons
|
||||
-- @return true or false
|
||||
function tablex.deepcompare(t1,t2,ignore_mt,eps)
|
||||
return cycle_aware_compare(t1,t2,ignore_mt,eps,{})
|
||||
end
|
||||
|
||||
--- compare two arrays using a predicate.
|
||||
-- @within Comparing
|
||||
-- @array t1 an array
|
||||
-- @array t2 an array
|
||||
-- @func cmp A comparison function; `bool = cmp(t1_value, t2_value)`
|
||||
-- @return true or false
|
||||
-- @usage
|
||||
-- assert(tablex.compare({ 1, 2, 3 }, { 1, 2, 3 }, "=="))
|
||||
--
|
||||
-- assert(tablex.compare(
|
||||
-- {1,2,3, hello = "world"}, -- fields are not compared!
|
||||
-- {1,2,3}, function(v1, v2) return v1 == v2 end)
|
||||
function tablex.compare (t1,t2,cmp)
|
||||
assert_arg_indexable(1,t1)
|
||||
assert_arg_indexable(2,t2)
|
||||
if #t1 ~= #t2 then return false end
|
||||
cmp = function_arg(3,cmp)
|
||||
for k = 1,#t1 do
|
||||
if not cmp(t1[k],t2[k]) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- compare two list-like tables using an optional predicate, without regard for element order.
|
||||
-- @within Comparing
|
||||
-- @array t1 a list-like table
|
||||
-- @array t2 a list-like table
|
||||
-- @param cmp A comparison function (may be nil)
|
||||
function tablex.compare_no_order (t1,t2,cmp)
|
||||
assert_arg_indexable(1,t1)
|
||||
assert_arg_indexable(2,t2)
|
||||
if cmp then cmp = function_arg(3,cmp) end
|
||||
if #t1 ~= #t2 then return false end
|
||||
local visited = {}
|
||||
for i = 1,#t1 do
|
||||
local val = t1[i]
|
||||
local gotcha
|
||||
for j = 1,#t2 do
|
||||
if not visited[j] then
|
||||
local match
|
||||
if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end
|
||||
if match then
|
||||
gotcha = j
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not gotcha then return false end
|
||||
visited[gotcha] = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--- return the index of a value in a list.
|
||||
-- Like string.find, there is an optional index to start searching,
|
||||
-- which can be negative.
|
||||
-- @within Finding
|
||||
-- @array t A list-like table
|
||||
-- @param val A value
|
||||
-- @int idx index to start; -1 means last element,etc (default 1)
|
||||
-- @return index of value or nil if not found
|
||||
-- @usage find({10,20,30},20) == 2
|
||||
-- @usage find({'a','b','a','c'},'a',2) == 3
|
||||
function tablex.find(t,val,idx)
|
||||
assert_arg_indexable(1,t)
|
||||
idx = idx or 1
|
||||
if idx < 0 then idx = #t + idx + 1 end
|
||||
for i = idx,#t do
|
||||
if t[i] == val then return i end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- return the index of a value in a list, searching from the end.
|
||||
-- Like string.find, there is an optional index to start searching,
|
||||
-- which can be negative.
|
||||
-- @within Finding
|
||||
-- @array t A list-like table
|
||||
-- @param val A value
|
||||
-- @param idx index to start; -1 means last element,etc (default `#t`)
|
||||
-- @return index of value or nil if not found
|
||||
-- @usage rfind({10,10,10},10) == 3
|
||||
function tablex.rfind(t,val,idx)
|
||||
assert_arg_indexable(1,t)
|
||||
idx = idx or #t
|
||||
if idx < 0 then idx = #t + idx + 1 end
|
||||
for i = idx,1,-1 do
|
||||
if t[i] == val then return i end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- return the index (or key) of a value in a table using a comparison function.
|
||||
--
|
||||
-- *NOTE*: the 2nd return value of this function, the value returned
|
||||
-- by the comparison function, has a limitation that it cannot be `false`.
|
||||
-- Because if it is, then it indicates the comparison failed, and the
|
||||
-- function will continue the search. See examples.
|
||||
-- @within Finding
|
||||
-- @tab t A table
|
||||
-- @func cmp A comparison function
|
||||
-- @param arg an optional second argument to the function
|
||||
-- @return index of value, or nil if not found
|
||||
-- @return value returned by comparison function (cannot be `false`!)
|
||||
-- @usage
|
||||
-- -- using an operator
|
||||
-- local lst = { "Rudolph", true, false, 15 }
|
||||
-- local idx, cmp_result = tablex.rfind(lst, "==", "Rudolph")
|
||||
-- assert(idx == 1)
|
||||
-- assert(cmp_result == true)
|
||||
--
|
||||
-- local idx, cmp_result = tablex.rfind(lst, "==", false)
|
||||
-- assert(idx == 3)
|
||||
-- assert(cmp_result == true) -- looking up 'false' works!
|
||||
--
|
||||
-- -- using a function returning the value looked up
|
||||
-- local cmp = function(v1, v2) return v1 == v2 and v2 end
|
||||
-- local idx, cmp_result = tablex.rfind(lst, cmp, "Rudolph")
|
||||
-- assert(idx == 1)
|
||||
-- assert(cmp_result == "Rudolph") -- the value is returned
|
||||
--
|
||||
-- -- NOTE: this fails, since 'false' cannot be returned!
|
||||
-- local idx, cmp_result = tablex.rfind(lst, cmp, false)
|
||||
-- assert(idx == nil) -- looking up 'false' failed!
|
||||
-- assert(cmp_result == nil)
|
||||
function tablex.find_if(t,cmp,arg)
|
||||
assert_arg_iterable(1,t)
|
||||
cmp = function_arg(2,cmp)
|
||||
for k,v in pairs(t) do
|
||||
local c = cmp(v,arg)
|
||||
if c then return k,c end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- return a list of all values in a table indexed by another list.
|
||||
-- @tab tbl a table
|
||||
-- @array idx an index table (a list of keys)
|
||||
-- @return a list-like table
|
||||
-- @usage index_by({10,20,30,40},{2,4}) == {20,40}
|
||||
-- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3}
|
||||
function tablex.index_by(tbl,idx)
|
||||
assert_arg_indexable(1,tbl)
|
||||
assert_arg_indexable(2,idx)
|
||||
local res = {}
|
||||
for i = 1,#idx do
|
||||
res[i] = tbl[idx[i]]
|
||||
end
|
||||
return setmeta(res,tbl,'List')
|
||||
end
|
||||
|
||||
--- apply a function to all values of a table.
|
||||
-- This returns a table of the results.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @tab t A table
|
||||
-- @param ... optional arguments
|
||||
-- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4}
|
||||
function tablex.map(fun,t,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for k,v in pairs(t) do
|
||||
res[k] = fun(v,...)
|
||||
end
|
||||
return setmeta(res,t)
|
||||
end
|
||||
|
||||
--- apply a function to all values of a list.
|
||||
-- This returns a table of the results.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @array t a table (applies to array part)
|
||||
-- @param ... optional arguments
|
||||
-- @return a list-like table
|
||||
-- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900}
|
||||
function tablex.imap(fun,t,...)
|
||||
assert_arg_indexable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
res[i] = fun(t[i],...) or false
|
||||
end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- apply a named method to values from a table.
|
||||
-- @within MappingAndFiltering
|
||||
-- @string name the method name
|
||||
-- @array t a list-like table
|
||||
-- @param ... any extra arguments to the method
|
||||
-- @return a `List` with the results of the method (1st result only)
|
||||
-- @usage
|
||||
-- local Car = {}
|
||||
-- Car.__index = Car
|
||||
-- function Car.new(car)
|
||||
-- return setmetatable(car or {}, Car)
|
||||
-- end
|
||||
-- Car.speed = 0
|
||||
-- function Car:faster(increase)
|
||||
-- self.speed = self.speed + increase
|
||||
-- return self.speed
|
||||
-- end
|
||||
--
|
||||
-- local ferrari = Car.new{ name = "Ferrari" }
|
||||
-- local lamborghini = Car.new{ name = "Lamborghini", speed = 50 }
|
||||
-- local cars = { ferrari, lamborghini }
|
||||
--
|
||||
-- assert(ferrari.speed == 0)
|
||||
-- assert(lamborghini.speed == 50)
|
||||
-- tablex.map_named_method("faster", cars, 10)
|
||||
-- assert(ferrari.speed == 10)
|
||||
-- assert(lamborghini.speed == 60)
|
||||
function tablex.map_named_method (name,t,...)
|
||||
utils.assert_string(1,name)
|
||||
assert_arg_indexable(2,t)
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
local val = t[i]
|
||||
local fun = val[name]
|
||||
res[i] = fun(val,...)
|
||||
end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- apply a function to all values of a table, in-place.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @tab t a table
|
||||
-- @param ... extra arguments passed to `fun`
|
||||
-- @see tablex.foreach
|
||||
function tablex.transform (fun,t,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
for k,v in pairs(t) do
|
||||
t[k] = fun(v,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- generate a table of all numbers in a range.
|
||||
-- This is consistent with a numerical for loop.
|
||||
-- @int start number
|
||||
-- @int finish number
|
||||
-- @int[opt=1] step make this negative for start < finish
|
||||
function tablex.range (start,finish,step)
|
||||
local res
|
||||
step = step or 1
|
||||
if start == finish then
|
||||
res = {start}
|
||||
elseif (start > finish and step > 0) or (finish > start and step < 0) then
|
||||
res = {}
|
||||
else
|
||||
local k = 1
|
||||
res = {}
|
||||
for i=start,finish,step do res[k]=i; k=k+1 end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- apply a function to values from two tables.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun a function of at least two arguments
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @param ... extra arguments
|
||||
-- @return a table
|
||||
-- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44}
|
||||
function tablex.map2 (fun,t1,t2,...)
|
||||
assert_arg_iterable(1,t1)
|
||||
assert_arg_iterable(2,t2)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for k,v in pairs(t1) do
|
||||
res[k] = fun(v,t2[k],...)
|
||||
end
|
||||
return setmeta(res,t1,'List')
|
||||
end
|
||||
|
||||
--- apply a function to values from two arrays.
|
||||
-- The result will be the length of the shortest array.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun a function of at least two arguments
|
||||
-- @array t1 a list-like table
|
||||
-- @array t2 a list-like table
|
||||
-- @param ... extra arguments
|
||||
-- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23}
|
||||
function tablex.imap2 (fun,t1,t2,...)
|
||||
assert_arg_indexable(2,t1)
|
||||
assert_arg_indexable(3,t2)
|
||||
fun = function_arg(1,fun)
|
||||
local res,n = {},math.min(#t1,#t2)
|
||||
for i = 1,n do
|
||||
res[i] = fun(t1[i],t2[i],...)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- 'reduce' a list using a binary function.
|
||||
-- @func fun a function of two arguments
|
||||
-- @array t a list-like table
|
||||
-- @array memo optional initial memo value. Defaults to first value in table.
|
||||
-- @return the result of the function
|
||||
-- @usage reduce('+',{1,2,3,4}) == 10
|
||||
function tablex.reduce (fun,t,memo)
|
||||
assert_arg_indexable(2,t)
|
||||
fun = function_arg(1,fun)
|
||||
local n = #t
|
||||
if n == 0 then
|
||||
return memo
|
||||
end
|
||||
local res = memo and fun(memo, t[1]) or t[1]
|
||||
for i = 2,n do
|
||||
res = fun(res,t[i])
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- apply a function to all elements of a table.
|
||||
-- The arguments to the function will be the value,
|
||||
-- the key and _finally_ any extra arguments passed to this function.
|
||||
-- Note that the Lua 5.0 function table.foreach passed the _key_ first.
|
||||
-- @within Iterating
|
||||
-- @tab t a table
|
||||
-- @func fun a function on the elements; `function(value, key, ...)`
|
||||
-- @param ... extra arguments passed to `fun`
|
||||
-- @see tablex.transform
|
||||
function tablex.foreach(t,fun,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(2,fun)
|
||||
for k,v in pairs(t) do
|
||||
fun(v,k,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- apply a function to all elements of a list-like table in order.
|
||||
-- The arguments to the function will be the value,
|
||||
-- the index and _finally_ any extra arguments passed to this function
|
||||
-- @within Iterating
|
||||
-- @array t a table
|
||||
-- @func fun a function with at least one argument
|
||||
-- @param ... optional arguments
|
||||
function tablex.foreachi(t,fun,...)
|
||||
assert_arg_indexable(1,t)
|
||||
fun = function_arg(2,fun)
|
||||
for i = 1,#t do
|
||||
fun(t[i],i,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- Apply a function to a number of tables.
|
||||
-- A more general version of map
|
||||
-- The result is a table containing the result of applying that function to the
|
||||
-- ith value of each table. Length of output list is the minimum length of all the lists
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun a function of n arguments
|
||||
-- @tab ... n tables
|
||||
-- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333}
|
||||
-- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300}
|
||||
-- @param fun A function that takes as many arguments as there are tables
|
||||
function tablex.mapn(fun,...)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
local lists = {...}
|
||||
local minn = 1e40
|
||||
for i = 1,#lists do
|
||||
minn = min(minn,#(lists[i]))
|
||||
end
|
||||
for i = 1,minn do
|
||||
local args,k = {},1
|
||||
for j = 1,#lists do
|
||||
args[k] = lists[j][i]
|
||||
k = k + 1
|
||||
end
|
||||
res[#res+1] = fun(unpack(args))
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- call the function with the key and value pairs from a table.
|
||||
-- The function can return a value and a key (note the order!). If both
|
||||
-- are not nil, then this pair is inserted into the result: if the key already exists, we convert the value for that
|
||||
-- key into a table and append into it. If only value is not nil, then it is appended to the result.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap.
|
||||
-- @tab t A table
|
||||
-- @param ... optional arguments
|
||||
-- @usage pairmap(function(k,v) return v end,{fred=10,bonzo=20}) is {10,20} _or_ {20,10}
|
||||
-- @usage pairmap(function(k,v) return {k,v},k end,{one=1,two=2}) is {one={'one',1},two={'two',2}}
|
||||
function tablex.pairmap(fun,t,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for k,v in pairs(t) do
|
||||
local rv,rk = fun(k,v,...)
|
||||
if rk then
|
||||
if res[rk] then
|
||||
if type(res[rk]) == 'table' then
|
||||
table.insert(res[rk],rv)
|
||||
else
|
||||
res[rk] = {res[rk], rv}
|
||||
end
|
||||
else
|
||||
res[rk] = rv
|
||||
end
|
||||
else
|
||||
res[#res+1] = rv
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function keys_op(i,v) return i end
|
||||
|
||||
--- return all the keys of a table in arbitrary order.
|
||||
-- @within Extraction
|
||||
-- @tab t A list-like table where the values are the keys of the input table
|
||||
function tablex.keys(t)
|
||||
assert_arg_iterable(1,t)
|
||||
return makelist(tablex.pairmap(keys_op,t))
|
||||
end
|
||||
|
||||
local function values_op(i,v) return v end
|
||||
|
||||
--- return all the values of the table in arbitrary order
|
||||
-- @within Extraction
|
||||
-- @tab t A list-like table where the values are the values of the input table
|
||||
function tablex.values(t)
|
||||
assert_arg_iterable(1,t)
|
||||
return makelist(tablex.pairmap(values_op,t))
|
||||
end
|
||||
|
||||
local function index_map_op (i,v) return i,v end
|
||||
|
||||
--- create an index map from a list-like table. The original values become keys,
|
||||
-- and the associated values are the indices into the original list.
|
||||
-- @array t a list-like table
|
||||
-- @return a map-like table
|
||||
function tablex.index_map (t)
|
||||
assert_arg_indexable(1,t)
|
||||
return makemap(tablex.pairmap(index_map_op,t))
|
||||
end
|
||||
|
||||
local function set_op(i,v) return true,v end
|
||||
|
||||
--- create a set from a list-like table. A set is a table where the original values
|
||||
-- become keys, and the associated values are all true.
|
||||
-- @array t a list-like table
|
||||
-- @return a set (a map-like table)
|
||||
function tablex.makeset (t)
|
||||
assert_arg_indexable(1,t)
|
||||
return setmetatable(tablex.pairmap(set_op,t),require('pl.Set'))
|
||||
end
|
||||
|
||||
--- combine two tables, either as union or intersection. Corresponds to
|
||||
-- set operations for sets () but more general. Not particularly
|
||||
-- useful for list-like tables.
|
||||
-- @within Merging
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @bool dup true for a union, false for an intersection.
|
||||
-- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34}
|
||||
-- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23}
|
||||
-- @see tablex.index_map
|
||||
function tablex.merge (t1,t2,dup)
|
||||
assert_arg_iterable(1,t1)
|
||||
assert_arg_iterable(2,t2)
|
||||
local res = {}
|
||||
for k,v in pairs(t1) do
|
||||
if dup or t2[k] then res[k] = v end
|
||||
end
|
||||
if dup then
|
||||
for k,v in pairs(t2) do
|
||||
res[k] = v
|
||||
end
|
||||
end
|
||||
return setmeta(res,t1,'Map')
|
||||
end
|
||||
|
||||
--- the union of two map-like tables.
|
||||
-- If there are duplicate keys, the second table wins.
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @treturn tab
|
||||
-- @see tablex.merge
|
||||
function tablex.union(t1, t2)
|
||||
return tablex.merge(t1, t2, true)
|
||||
end
|
||||
|
||||
--- the intersection of two map-like tables.
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @treturn tab
|
||||
-- @see tablex.merge
|
||||
function tablex.intersection(t1, t2)
|
||||
return tablex.merge(t1, t2, false)
|
||||
end
|
||||
|
||||
--- a new table which is the difference of two tables.
|
||||
-- With sets (where the values are all true) this is set difference and
|
||||
-- symmetric difference depending on the third parameter.
|
||||
-- @within Merging
|
||||
-- @tab s1 a map-like table or set
|
||||
-- @tab s2 a map-like table or set
|
||||
-- @bool symm symmetric difference (default false)
|
||||
-- @return a map-like table or set
|
||||
function tablex.difference (s1,s2,symm)
|
||||
assert_arg_iterable(1,s1)
|
||||
assert_arg_iterable(2,s2)
|
||||
local res = {}
|
||||
for k,v in pairs(s1) do
|
||||
if s2[k] == nil then res[k] = v end
|
||||
end
|
||||
if symm then
|
||||
for k,v in pairs(s2) do
|
||||
if s1[k] == nil then res[k] = v end
|
||||
end
|
||||
end
|
||||
return setmeta(res,s1,'Map')
|
||||
end
|
||||
|
||||
--- A table where the key/values are the values and value counts of the table.
|
||||
-- @array t a list-like table
|
||||
-- @func cmp a function that defines equality (otherwise uses ==)
|
||||
-- @return a map-like table
|
||||
-- @see seq.count_map
|
||||
function tablex.count_map (t,cmp)
|
||||
assert_arg_indexable(1,t)
|
||||
local res,mask = {},{}
|
||||
cmp = function_arg(2,cmp or '==')
|
||||
local n = #t
|
||||
for i = 1,#t do
|
||||
local v = t[i]
|
||||
if not mask[v] then
|
||||
mask[v] = true
|
||||
-- check this value against all other values
|
||||
res[v] = 1 -- there's at least one instance
|
||||
for j = i+1,n do
|
||||
local w = t[j]
|
||||
local ok = cmp(v,w)
|
||||
if ok then
|
||||
res[v] = res[v] + 1
|
||||
mask[w] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return makemap(res)
|
||||
end
|
||||
|
||||
--- filter an array's values using a predicate function
|
||||
-- @within MappingAndFiltering
|
||||
-- @array t a list-like table
|
||||
-- @func pred a boolean function
|
||||
-- @param arg optional argument to be passed as second argument of the predicate
|
||||
function tablex.filter (t,pred,arg)
|
||||
assert_arg_indexable(1,t)
|
||||
pred = function_arg(2,pred)
|
||||
local res,k = {},1
|
||||
for i = 1,#t do
|
||||
local v = t[i]
|
||||
if pred(v,arg) then
|
||||
res[k] = v
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- return a table where each element is a table of the ith values of an arbitrary
|
||||
-- number of tables. It is equivalent to a matrix transpose.
|
||||
-- @within Merging
|
||||
-- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}}
|
||||
-- @array ... arrays to be zipped
|
||||
function tablex.zip(...)
|
||||
return tablex.mapn(function(...) return {...} end,...)
|
||||
end
|
||||
|
||||
local _copy
|
||||
function _copy (dest,src,idest,isrc,nsrc,clean_tail)
|
||||
idest = idest or 1
|
||||
isrc = isrc or 1
|
||||
local iend
|
||||
if not nsrc then
|
||||
nsrc = #src
|
||||
iend = #src
|
||||
else
|
||||
iend = isrc + min(nsrc-1,#src-isrc)
|
||||
end
|
||||
if dest == src then -- special case
|
||||
if idest > isrc and iend >= idest then -- overlapping ranges
|
||||
src = tablex.sub(src,isrc,nsrc)
|
||||
isrc = 1; iend = #src
|
||||
end
|
||||
end
|
||||
for i = isrc,iend do
|
||||
dest[idest] = src[i]
|
||||
idest = idest + 1
|
||||
end
|
||||
if clean_tail then
|
||||
tablex.clear(dest,idest)
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
--- copy an array into another one, clearing `dest` after `idest+nsrc`, if necessary.
|
||||
-- @within Copying
|
||||
-- @array dest a list-like table
|
||||
-- @array src a list-like table
|
||||
-- @int[opt=1] idest where to start copying values into destination
|
||||
-- @int[opt=1] isrc where to start copying values from source
|
||||
-- @int[opt=#src] nsrc number of elements to copy from source
|
||||
function tablex.icopy (dest,src,idest,isrc,nsrc)
|
||||
assert_arg_indexable(1,dest)
|
||||
assert_arg_indexable(2,src)
|
||||
return _copy(dest,src,idest,isrc,nsrc,true)
|
||||
end
|
||||
|
||||
--- copy an array into another one.
|
||||
-- @within Copying
|
||||
-- @array dest a list-like table
|
||||
-- @array src a list-like table
|
||||
-- @int[opt=1] idest where to start copying values into destination
|
||||
-- @int[opt=1] isrc where to start copying values from source
|
||||
-- @int[opt=#src] nsrc number of elements to copy from source
|
||||
function tablex.move (dest,src,idest,isrc,nsrc)
|
||||
assert_arg_indexable(1,dest)
|
||||
assert_arg_indexable(2,src)
|
||||
return _copy(dest,src,idest,isrc,nsrc,false)
|
||||
end
|
||||
|
||||
function tablex._normalize_slice(self,first,last)
|
||||
local sz = #self
|
||||
if not first then first=1 end
|
||||
if first<0 then first=sz+first+1 end
|
||||
-- make the range _inclusive_!
|
||||
if not last then last=sz end
|
||||
if last < 0 then last=sz+1+last end
|
||||
return first,last
|
||||
end
|
||||
|
||||
--- Extract a range from a table, like 'string.sub'.
|
||||
-- If first or last are negative then they are relative to the end of the list
|
||||
-- eg. sub(t,-2) gives last 2 entries in a list, and
|
||||
-- sub(t,-4,-2) gives from -4th to -2nd
|
||||
-- @within Extraction
|
||||
-- @array t a list-like table
|
||||
-- @int first An index
|
||||
-- @int last An index
|
||||
-- @return a new List
|
||||
function tablex.sub(t,first,last)
|
||||
assert_arg_indexable(1,t)
|
||||
first,last = tablex._normalize_slice(t,first,last)
|
||||
local res={}
|
||||
for i=first,last do append(res,t[i]) end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- set an array range to a value. If it's a function we use the result
|
||||
-- of applying it to the indices.
|
||||
-- @array t a list-like table
|
||||
-- @param val a value
|
||||
-- @int[opt=1] i1 start range
|
||||
-- @int[opt=#t] i2 end range
|
||||
function tablex.set (t,val,i1,i2)
|
||||
assert_arg_indexable(1,t)
|
||||
i1,i2 = i1 or 1,i2 or #t
|
||||
if types.is_callable(val) then
|
||||
for i = i1,i2 do
|
||||
t[i] = val(i)
|
||||
end
|
||||
else
|
||||
for i = i1,i2 do
|
||||
t[i] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- create a new array of specified size with initial value.
|
||||
-- @int n size
|
||||
-- @param val initial value (can be `nil`, but don't expect `#` to work!)
|
||||
-- @return the table
|
||||
function tablex.new (n,val)
|
||||
local res = {}
|
||||
tablex.set(res,val,1,n)
|
||||
return res
|
||||
end
|
||||
|
||||
--- clear out the contents of a table.
|
||||
-- @array t a list
|
||||
-- @param istart optional start position
|
||||
function tablex.clear(t,istart)
|
||||
istart = istart or 1
|
||||
for i = istart,#t do remove(t) end
|
||||
end
|
||||
|
||||
--- insert values into a table.
|
||||
-- similar to `table.insert` but inserts values from given table `values`,
|
||||
-- not the object itself, into table `t` at position `pos`.
|
||||
-- @within Copying
|
||||
-- @array t the list
|
||||
-- @int[opt] position (default is at end)
|
||||
-- @array values
|
||||
function tablex.insertvalues(t, ...)
|
||||
assert_arg(1,t,'table')
|
||||
local pos, values
|
||||
if select('#', ...) == 1 then
|
||||
pos,values = #t+1, ...
|
||||
else
|
||||
pos,values = ...
|
||||
end
|
||||
if #values > 0 then
|
||||
for i=#t,pos,-1 do
|
||||
t[i+#values] = t[i]
|
||||
end
|
||||
local offset = 1 - pos
|
||||
for i=pos,pos+#values-1 do
|
||||
t[i] = values[i + offset]
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
--- remove a range of values from a table.
|
||||
-- End of range may be negative.
|
||||
-- @array t a list-like table
|
||||
-- @int i1 start index
|
||||
-- @int i2 end index
|
||||
-- @return the table
|
||||
function tablex.removevalues (t,i1,i2)
|
||||
assert_arg(1,t,'table')
|
||||
i1,i2 = tablex._normalize_slice(t,i1,i2)
|
||||
for i = i1,i2 do
|
||||
remove(t,i1)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local _find
|
||||
_find = function (t,value,tables)
|
||||
for k,v in pairs(t) do
|
||||
if v == value then return k end
|
||||
end
|
||||
for k,v in pairs(t) do
|
||||
if not tables[v] and type(v) == 'table' then
|
||||
tables[v] = true
|
||||
local res = _find(v,value,tables)
|
||||
if res then
|
||||
res = tostring(res)
|
||||
if type(k) ~= 'string' then
|
||||
return '['..k..']'..res
|
||||
else
|
||||
return k..'.'..res
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- find a value in a table by recursive search.
|
||||
-- @within Finding
|
||||
-- @tab t the table
|
||||
-- @param value the value
|
||||
-- @array[opt] exclude any tables to avoid searching
|
||||
-- @return a fieldspec, e.g. 'a.b' or 'math.sin'
|
||||
-- @usage search(_G,math.sin,{package.path}) == 'math.sin'
|
||||
function tablex.search (t,value,exclude)
|
||||
assert_arg_iterable(1,t)
|
||||
local tables = {[t]=true}
|
||||
if exclude then
|
||||
for _,v in pairs(exclude) do tables[v] = true end
|
||||
end
|
||||
return _find(t,value,tables)
|
||||
end
|
||||
|
||||
--- return an iterator to a table sorted by its keys
|
||||
-- @within Iterating
|
||||
-- @tab t the table
|
||||
-- @func f an optional comparison function (f(x,y) is true if x < y)
|
||||
-- @usage for k,v in tablex.sort(t) do print(k,v) end
|
||||
-- @return an iterator to traverse elements sorted by the keys
|
||||
function tablex.sort(t,f)
|
||||
local keys = {}
|
||||
for k in pairs(t) do keys[#keys + 1] = k end
|
||||
tsort(keys,f)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
return keys[i], t[keys[i]]
|
||||
end
|
||||
end
|
||||
|
||||
--- return an iterator to a table sorted by its values
|
||||
-- @within Iterating
|
||||
-- @tab t the table
|
||||
-- @func f an optional comparison function (f(x,y) is true if x < y)
|
||||
-- @usage for k,v in tablex.sortv(t) do print(k,v) end
|
||||
-- @return an iterator to traverse elements sorted by the values
|
||||
function tablex.sortv(t,f)
|
||||
f = function_arg(2, f or '<')
|
||||
local keys = {}
|
||||
for k in pairs(t) do keys[#keys + 1] = k end
|
||||
tsort(keys,function(x, y) return f(t[x], t[y]) end)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
return keys[i], t[keys[i]]
|
||||
end
|
||||
end
|
||||
|
||||
--- modifies a table to be read only.
|
||||
-- This only offers weak protection. Tables can still be modified with
|
||||
-- `table.insert` and `rawset`.
|
||||
--
|
||||
-- *NOTE*: for Lua 5.1 length, pairs and ipairs will not work, since the
|
||||
-- equivalent metamethods are only available in Lua 5.2 and newer.
|
||||
-- @tab t the table
|
||||
-- @return the table read only (a proxy).
|
||||
function tablex.readonly(t)
|
||||
local mt = {
|
||||
__index=t,
|
||||
__newindex=function(t, k, v) error("Attempt to modify read-only table", 2) end,
|
||||
__pairs=function() return pairs(t) end,
|
||||
__ipairs=function() return ipairs(t) end,
|
||||
__len=function() return #t end,
|
||||
__metatable=false
|
||||
}
|
||||
return setmetatable({}, mt)
|
||||
end
|
||||
|
||||
return tablex
|
202
Documentation/compiler/pl/template.lua
Normal file
202
Documentation/compiler/pl/template.lua
Normal file
|
@ -0,0 +1,202 @@
|
|||
--- A template preprocessor.
|
||||
-- Originally by [Ricki Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor)
|
||||
--
|
||||
-- There are two rules:
|
||||
--
|
||||
-- * lines starting with # are Lua
|
||||
-- * otherwise, `$(expr)` is the result of evaluating `expr`
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
-- # for i = 1,3 do
|
||||
-- $(i) Hello, Word!
|
||||
-- # end
|
||||
-- ===>
|
||||
-- 1 Hello, Word!
|
||||
-- 2 Hello, Word!
|
||||
-- 3 Hello, Word!
|
||||
--
|
||||
-- Other escape characters can be used, when the defaults conflict
|
||||
-- with the output language.
|
||||
--
|
||||
-- > for _,n in pairs{'one','two','three'} do
|
||||
-- static int l_${n} (luaState *state);
|
||||
-- > end
|
||||
--
|
||||
-- See @{03-strings.md.Another_Style_of_Template|the Guide}.
|
||||
--
|
||||
-- Dependencies: `pl.utils`
|
||||
-- @module pl.template
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
|
||||
local append,format,strsub,strfind,strgsub = table.insert,string.format,string.sub,string.find,string.gsub
|
||||
|
||||
local APPENDER = "\n__R_size = __R_size + 1; __R_table[__R_size] = "
|
||||
|
||||
local function parseDollarParen(pieces, chunk, exec_pat, newline)
|
||||
local s = 1
|
||||
for term, executed, e in chunk:gmatch(exec_pat) do
|
||||
executed = '('..strsub(executed,2,-2)..')'
|
||||
append(pieces, APPENDER..format("%q", strsub(chunk,s, term - 1)))
|
||||
append(pieces, APPENDER..format("__tostring(%s or '')", executed))
|
||||
s = e
|
||||
end
|
||||
local r
|
||||
if newline then
|
||||
r = format("%q", strgsub(strsub(chunk,s),"\n",""))
|
||||
else
|
||||
r = format("%q", strsub(chunk,s))
|
||||
end
|
||||
if r ~= '""' then
|
||||
append(pieces, APPENDER..r)
|
||||
end
|
||||
end
|
||||
|
||||
local function parseHashLines(chunk,inline_escape,brackets,esc,newline)
|
||||
local exec_pat = "()"..inline_escape.."(%b"..brackets..")()"
|
||||
|
||||
local esc_pat = esc.."+([^\n]*\n?)"
|
||||
local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat
|
||||
local pieces, s = {"return function()\nlocal __R_size, __R_table, __tostring = 0, {}, __tostring", n = 1}, 1
|
||||
while true do
|
||||
local _, e, lua = strfind(chunk,esc_pat1, s)
|
||||
if not e then
|
||||
local ss
|
||||
ss, e, lua = strfind(chunk,esc_pat2, s)
|
||||
parseDollarParen(pieces, strsub(chunk,s, ss), exec_pat, newline)
|
||||
if not e then break end
|
||||
end
|
||||
if strsub(lua, -1, -1) == "\n" then lua = strsub(lua, 1, -2) end
|
||||
append(pieces, "\n"..lua)
|
||||
s = e + 1
|
||||
end
|
||||
append(pieces, "\nreturn __R_table\nend")
|
||||
|
||||
-- let's check for a special case where there is nothing to template, but it's
|
||||
-- just a single static string
|
||||
local short = false
|
||||
if (#pieces == 3) and (pieces[2]:find(APPENDER, 1, true) == 1) then
|
||||
pieces = { "return " .. pieces[2]:sub(#APPENDER+1,-1) }
|
||||
short = true
|
||||
end
|
||||
-- if short == true, the generated function will not return a table of strings,
|
||||
-- but a single string
|
||||
return table.concat(pieces), short
|
||||
end
|
||||
|
||||
local template = {}
|
||||
|
||||
--- expand the template using the specified environment.
|
||||
-- This function will compile and render the template. For more performant
|
||||
-- recurring usage use the two step approach by using `compile` and `ct:render`.
|
||||
-- There are six special fields in the environment table `env`
|
||||
--
|
||||
-- * `_parent`: continue looking up in this table (e.g. `_parent=_G`).
|
||||
-- * `_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
|
||||
-- * `_escape`: character marking Lua lines, default is '#'
|
||||
-- * `_inline_escape`: character marking inline Lua expression, default is '$'.
|
||||
-- * `_chunk_name`: chunk name for loaded templates, used if there
|
||||
-- is an error in Lua code. Default is 'TMP'.
|
||||
-- * `_debug`: if truthy, the generated code will be printed upon a render error
|
||||
--
|
||||
-- @string str the template string
|
||||
-- @tab[opt] env the environment
|
||||
-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last
|
||||
-- return value (`source_code`) is only returned if the debug option is used.
|
||||
function template.substitute(str,env)
|
||||
env = env or {}
|
||||
local t, err = template.compile(str, {
|
||||
chunk_name = rawget(env,"_chunk_name"),
|
||||
escape = rawget(env,"_escape"),
|
||||
inline_escape = rawget(env,"_inline_escape"),
|
||||
inline_brackets = rawget(env,"_brackets"),
|
||||
newline = nil,
|
||||
debug = rawget(env,"_debug")
|
||||
})
|
||||
if not t then return t, err end
|
||||
|
||||
return t:render(env, rawget(env,"_parent"), rawget(env,"_debug"))
|
||||
end
|
||||
|
||||
--- executes the previously compiled template and renders it.
|
||||
-- @function ct:render
|
||||
-- @tab[opt] env the environment.
|
||||
-- @tab[opt] parent continue looking up in this table (e.g. `parent=_G`).
|
||||
-- @bool[opt] db if thruthy, it will print the code upon a render error
|
||||
-- (provided the template was compiled with the debug option).
|
||||
-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last return value
|
||||
-- (`source_code`) is only returned if the template was compiled with the debug option.
|
||||
-- @usage
|
||||
-- local ct, err = template.compile(my_template)
|
||||
-- local rendered , err = ct:render(my_env, parent)
|
||||
local render = function(self, env, parent, db)
|
||||
env = env or {}
|
||||
if parent then -- parent is a bit silly, but for backward compatibility retained
|
||||
setmetatable(env, {__index = parent})
|
||||
end
|
||||
setmetatable(self.env, {__index = env})
|
||||
|
||||
local res, out = xpcall(self.fn, debug.traceback)
|
||||
if not res then
|
||||
if self.code and db then print(self.code) end
|
||||
return nil, out, self.code
|
||||
end
|
||||
return table.concat(out), nil, self.code
|
||||
end
|
||||
|
||||
--- compiles the template.
|
||||
-- Returns an object that can repeatedly be rendered without parsing/compiling
|
||||
-- the template again.
|
||||
-- The options passed in the `opts` table support the following options:
|
||||
--
|
||||
-- * `chunk_name`: chunk name for loaded templates, used if there
|
||||
-- is an error in Lua code. Default is 'TMP'.
|
||||
-- * `escape`: character marking Lua lines, default is '#'
|
||||
-- * `inline_escape`: character marking inline Lua expression, default is '$'.
|
||||
-- * `inline_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
|
||||
-- * `newline`: string to replace newline characters, default is `nil` (not replacing newlines).
|
||||
-- * `debug`: if truthy, the generated source code will be retained within the compiled template object, default is `nil`.
|
||||
--
|
||||
-- @string str the template string
|
||||
-- @tab[opt] opts the compilation options to use
|
||||
-- @return template object, or `nil + error + source_code`
|
||||
-- @usage
|
||||
-- local ct, err = template.compile(my_template)
|
||||
-- local rendered , err = ct:render(my_env, parent)
|
||||
function template.compile(str, opts)
|
||||
opts = opts or {}
|
||||
local chunk_name = opts.chunk_name or 'TMP'
|
||||
local escape = opts.escape or '#'
|
||||
local inline_escape = opts.inline_escape or '$'
|
||||
local inline_brackets = opts.inline_brackets or '()'
|
||||
|
||||
local code, short = parseHashLines(str,inline_escape,inline_brackets,escape,opts.newline)
|
||||
local env = { __tostring = tostring }
|
||||
local fn, err = utils.load(code, chunk_name,'t',env)
|
||||
if not fn then return nil, err, code end
|
||||
|
||||
if short then
|
||||
-- the template returns a single constant string, let's optimize for that
|
||||
local constant_string = fn()
|
||||
return {
|
||||
fn = fn(),
|
||||
env = env,
|
||||
render = function(self) -- additional params can be ignored
|
||||
-- skip the metatable magic and error handling in the render
|
||||
-- function above for this special case
|
||||
return constant_string, nil, self.code
|
||||
end,
|
||||
code = opts.debug and code or nil,
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
fn = fn(),
|
||||
env = env,
|
||||
render = render,
|
||||
code = opts.debug and code or nil,
|
||||
}
|
||||
end
|
||||
|
||||
return template
|
164
Documentation/compiler/pl/test.lua
Normal file
164
Documentation/compiler/pl/test.lua
Normal file
|
@ -0,0 +1,164 @@
|
|||
--- Useful test utilities.
|
||||
--
|
||||
-- test.asserteq({1,2},{1,2}) -- can compare tables
|
||||
-- test.asserteq(1.2,1.19,0.02) -- compare FP numbers within precision
|
||||
-- T = test.tuple -- used for comparing multiple results
|
||||
-- test.asserteq(T(string.find(" me","me")),T(2,3))
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.pretty`, `pl.path`, `debug`
|
||||
-- @module pl.test
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local pretty = require 'pl.pretty'
|
||||
local path = require 'pl.path'
|
||||
local type,unpack,pack = type,utils.unpack,utils.pack
|
||||
local clock = os.clock
|
||||
local debug = require 'debug'
|
||||
local io = io
|
||||
|
||||
local function dump(x)
|
||||
if type(x) == 'table' and not (getmetatable(x) and getmetatable(x).__tostring) then
|
||||
return pretty.write(x,' ',true)
|
||||
elseif type(x) == 'string' then
|
||||
return '"'..x..'"'
|
||||
else
|
||||
return tostring(x)
|
||||
end
|
||||
end
|
||||
|
||||
local test = {}
|
||||
|
||||
---- error handling for test results.
|
||||
-- By default, this writes to stderr and exits the program.
|
||||
-- Re-define this function to raise an error and/or redirect output
|
||||
function test.error_handler(file,line,got_text, needed_text,msg)
|
||||
local err = io.stderr
|
||||
err:write(path.basename(file)..':'..line..': assertion failed\n')
|
||||
err:write("got:\t",got_text,'\n')
|
||||
err:write("needed:\t",needed_text,'\n')
|
||||
utils.quit(1,msg or "these values were not equal")
|
||||
end
|
||||
|
||||
local function complain (x,y,msg,where)
|
||||
local i = debug.getinfo(3 + (where or 0))
|
||||
test.error_handler(i.short_src,i.currentline,dump(x),dump(y),msg)
|
||||
end
|
||||
|
||||
--- general test complain message.
|
||||
-- Useful for composing new test functions (see tests/tablex.lua for an example)
|
||||
-- @param x a value
|
||||
-- @param y value to compare first value against
|
||||
-- @param msg message
|
||||
-- @param where extra level offset for errors
|
||||
-- @function complain
|
||||
test.complain = complain
|
||||
|
||||
--- like assert, except takes two arguments that must be equal and can be tables.
|
||||
-- If they are plain tables, it will use tablex.deepcompare.
|
||||
-- @param x any value
|
||||
-- @param y a value equal to x
|
||||
-- @param eps an optional tolerance for numerical comparisons
|
||||
-- @param where extra level offset
|
||||
function test.asserteq (x,y,eps,where)
|
||||
local res = x == y
|
||||
if not res then
|
||||
res = tablex.deepcompare(x,y,true,eps)
|
||||
end
|
||||
if not res then
|
||||
complain(x,y,nil,where)
|
||||
end
|
||||
end
|
||||
|
||||
--- assert that the first string matches the second.
|
||||
-- @param s1 a string
|
||||
-- @param s2 a string
|
||||
-- @param where extra level offset
|
||||
function test.assertmatch (s1,s2,where)
|
||||
if not s1:match(s2) then
|
||||
complain (s1,s2,"these strings did not match",where)
|
||||
end
|
||||
end
|
||||
|
||||
--- assert that the function raises a particular error.
|
||||
-- @param fn a function or a table of the form {function,arg1,...}
|
||||
-- @param e a string to match the error against
|
||||
-- @param where extra level offset
|
||||
function test.assertraise(fn,e,where)
|
||||
local ok, err
|
||||
if type(fn) == 'table' then
|
||||
ok, err = pcall(unpack(fn))
|
||||
else
|
||||
ok, err = pcall(fn)
|
||||
end
|
||||
if ok or err:match(e)==nil then
|
||||
complain (err,e,"these errors did not match",where)
|
||||
end
|
||||
end
|
||||
|
||||
--- a version of asserteq that takes two pairs of values.
|
||||
-- <code>x1==y1 and x2==y2</code> must be true. Useful for functions that naturally
|
||||
-- return two values.
|
||||
-- @param x1 any value
|
||||
-- @param x2 any value
|
||||
-- @param y1 any value
|
||||
-- @param y2 any value
|
||||
-- @param where extra level offset
|
||||
function test.asserteq2 (x1,x2,y1,y2,where)
|
||||
if x1 ~= y1 then complain(x1,y1,nil,where) end
|
||||
if x2 ~= y2 then complain(x2,y2,nil,where) end
|
||||
end
|
||||
|
||||
-- tuple type --
|
||||
|
||||
local tuple_mt = {
|
||||
unpack = unpack
|
||||
}
|
||||
tuple_mt.__index = tuple_mt
|
||||
|
||||
function tuple_mt.__tostring(self)
|
||||
local ts = {}
|
||||
for i=1, self.n do
|
||||
local s = self[i]
|
||||
ts[i] = type(s) == 'string' and ('%q'):format(s) or tostring(s)
|
||||
end
|
||||
return 'tuple(' .. table.concat(ts, ', ') .. ')'
|
||||
end
|
||||
|
||||
function tuple_mt.__eq(a, b)
|
||||
if a.n ~= b.n then return false end
|
||||
for i=1, a.n do
|
||||
if a[i] ~= b[i] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function tuple_mt.__len(self)
|
||||
return self.n
|
||||
end
|
||||
|
||||
--- encode an arbitrary argument list as a tuple.
|
||||
-- This can be used to compare to other argument lists, which is
|
||||
-- very useful for testing functions which return a number of values.
|
||||
-- Unlike regular array-like tables ('sequences') they may contain nils.
|
||||
-- Tuples understand equality and know how to print themselves out.
|
||||
-- The # operator is defined to be the size, irrespecive of any nils,
|
||||
-- and there is an `unpack` method.
|
||||
-- @usage asserteq(tuple( ('ab'):find 'a'), tuple(1,1))
|
||||
function test.tuple(...)
|
||||
return setmetatable(pack(...), tuple_mt)
|
||||
end
|
||||
|
||||
--- Time a function. Call the function a given number of times, and report the number of seconds taken,
|
||||
-- together with a message. Any extra arguments will be passed to the function.
|
||||
-- @string msg a descriptive message
|
||||
-- @int n number of times to call the function
|
||||
-- @func fun the function
|
||||
-- @param ... optional arguments to fun
|
||||
function test.timer(msg,n,fun,...)
|
||||
local start = clock()
|
||||
for i = 1,n do fun(...) end
|
||||
utils.printf("%s: took %7.2f sec\n",msg,clock()-start)
|
||||
end
|
||||
|
||||
return test
|
26
Documentation/compiler/pl/text.lua
Normal file
26
Documentation/compiler/pl/text.lua
Normal file
|
@ -0,0 +1,26 @@
|
|||
--- Text processing utilities.
|
||||
--
|
||||
-- This provides a Template class (modeled after the same from the Python
|
||||
-- libraries, see string.Template). It also provides similar functions to those
|
||||
-- found in the textwrap module.
|
||||
--
|
||||
-- IMPORTANT: this module has been deprecated and will be removed in a future
|
||||
-- version (2.0). The contents of this module have moved to the `pl.stringx`
|
||||
-- module.
|
||||
--
|
||||
-- See @{03-strings.md.String_Templates|the Guide}.
|
||||
--
|
||||
-- Dependencies: `pl.stringx`, `pl.utils`
|
||||
-- @module pl.text
|
||||
|
||||
local utils = require("pl.utils")
|
||||
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "the contents of module 'pl.text' has moved into 'pl.stringx'",
|
||||
version_removed = "2.0.0",
|
||||
deprecated_after = "1.11.0",
|
||||
no_trace = true,
|
||||
}
|
||||
|
||||
return require "pl.stringx"
|
183
Documentation/compiler/pl/types.lua
Normal file
183
Documentation/compiler/pl/types.lua
Normal file
|
@ -0,0 +1,183 @@
|
|||
---- Dealing with Detailed Type Information
|
||||
|
||||
-- Dependencies `pl.utils`
|
||||
-- @module pl.types
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local math_ceil = math.ceil
|
||||
local assert_arg = utils.assert_arg
|
||||
local types = {}
|
||||
|
||||
--- is the object either a function or a callable object?.
|
||||
-- @param obj Object to check.
|
||||
function types.is_callable (obj)
|
||||
return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call and true
|
||||
end
|
||||
|
||||
--- is the object of the specified type?.
|
||||
-- If the type is a string, then use type, otherwise compare with metatable.
|
||||
--
|
||||
-- NOTE: this function is imported from `utils.is_type`.
|
||||
-- @param obj An object to check
|
||||
-- @param tp The expected type
|
||||
-- @function is_type
|
||||
-- @see utils.is_type
|
||||
types.is_type = utils.is_type
|
||||
|
||||
local fileMT = getmetatable(io.stdout)
|
||||
|
||||
--- a string representation of a type.
|
||||
-- For tables and userdata with metatables, we assume that the metatable has a `_name`
|
||||
-- field. If the field is not present it will return 'unknown table' or
|
||||
-- 'unknown userdata'.
|
||||
-- Lua file objects return the type 'file'.
|
||||
-- @param obj an object
|
||||
-- @return a string like 'number', 'table', 'file' or 'List'
|
||||
function types.type (obj)
|
||||
local t = type(obj)
|
||||
if t == 'table' or t == 'userdata' then
|
||||
local mt = getmetatable(obj)
|
||||
if mt == fileMT then
|
||||
return 'file'
|
||||
elseif mt == nil then
|
||||
return t
|
||||
else
|
||||
-- TODO: the "unknown" is weird, it should just return the type
|
||||
return mt._name or "unknown "..t
|
||||
end
|
||||
else
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
--- is this number an integer?
|
||||
-- @param x a number
|
||||
-- @raise error if x is not a number
|
||||
-- @return boolean
|
||||
function types.is_integer (x)
|
||||
return math_ceil(x)==x
|
||||
end
|
||||
|
||||
--- Check if the object is "empty".
|
||||
-- An object is considered empty if it is:
|
||||
--
|
||||
-- - `nil`
|
||||
-- - a table without any items (key-value pairs or indexes)
|
||||
-- - a string with no content ("")
|
||||
-- - not a nil/table/string
|
||||
-- @param o The object to check if it is empty.
|
||||
-- @param ignore_spaces If the object is a string and this is true the string is
|
||||
-- considered empty if it only contains spaces.
|
||||
-- @return `true` if the object is empty, otherwise a falsy value.
|
||||
function types.is_empty(o, ignore_spaces)
|
||||
if o == nil then
|
||||
return true
|
||||
elseif type(o) == "table" then
|
||||
return next(o) == nil
|
||||
elseif type(o) == "string" then
|
||||
return o == "" or (not not ignore_spaces and (not not o:find("^%s+$")))
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function check_meta (val)
|
||||
if type(val) == 'table' then return true end
|
||||
return getmetatable(val)
|
||||
end
|
||||
|
||||
--- is an object 'array-like'?
|
||||
-- An object is array like if:
|
||||
--
|
||||
-- - it is a table, or
|
||||
-- - it has a metatable with `__len` and `__index` methods
|
||||
--
|
||||
-- NOTE: since `__len` is 5.2+, on 5.1 is usually returns `false` for userdata
|
||||
-- @param val any value.
|
||||
-- @return `true` if the object is array-like, otherwise a falsy value.
|
||||
function types.is_indexable (val)
|
||||
local mt = check_meta(val)
|
||||
if mt == true then return true end
|
||||
return mt and mt.__len and mt.__index and true
|
||||
end
|
||||
|
||||
--- can an object be iterated over with `pairs`?
|
||||
-- An object is iterable if:
|
||||
--
|
||||
-- - it is a table, or
|
||||
-- - it has a metatable with a `__pairs` meta method
|
||||
--
|
||||
-- NOTE: since `__pairs` is 5.2+, on 5.1 is usually returns `false` for userdata
|
||||
-- @param val any value.
|
||||
-- @return `true` if the object is iterable, otherwise a falsy value.
|
||||
function types.is_iterable (val)
|
||||
local mt = check_meta(val)
|
||||
if mt == true then return true end
|
||||
return mt and mt.__pairs and true
|
||||
end
|
||||
|
||||
--- can an object accept new key/pair values?
|
||||
-- An object is iterable if:
|
||||
--
|
||||
-- - it is a table, or
|
||||
-- - it has a metatable with a `__newindex` meta method
|
||||
--
|
||||
-- @param val any value.
|
||||
-- @return `true` if the object is writeable, otherwise a falsy value.
|
||||
function types.is_writeable (val)
|
||||
local mt = check_meta(val)
|
||||
if mt == true then return true end
|
||||
return mt and mt.__newindex and true
|
||||
end
|
||||
|
||||
-- Strings that should evaluate to true. -- TODO: add on/off ???
|
||||
local trues = { yes=true, y=true, ["true"]=true, t=true, ["1"]=true }
|
||||
-- Conditions types should evaluate to true.
|
||||
local true_types = {
|
||||
boolean=function(o, true_strs, check_objs) return o end,
|
||||
string=function(o, true_strs, check_objs)
|
||||
o = o:lower()
|
||||
if trues[o] then
|
||||
return true
|
||||
end
|
||||
-- Check alternative user provided strings.
|
||||
for _,v in ipairs(true_strs or {}) do
|
||||
if type(v) == "string" and o == v:lower() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
number=function(o, true_strs, check_objs) return o ~= 0 end,
|
||||
table=function(o, true_strs, check_objs) if check_objs and next(o) ~= nil then return true end return false end
|
||||
}
|
||||
--- Convert to a boolean value.
|
||||
-- True values are:
|
||||
--
|
||||
-- * boolean: true.
|
||||
-- * string: 'yes', 'y', 'true', 't', '1' or additional strings specified by `true_strs`.
|
||||
-- * number: Any non-zero value.
|
||||
-- * table: Is not empty and `check_objs` is true.
|
||||
-- * everything else: Is not `nil` and `check_objs` is true.
|
||||
--
|
||||
-- @param o The object to evaluate.
|
||||
-- @param[opt] true_strs optional Additional strings that when matched should evaluate to true. Comparison is case insensitive.
|
||||
-- This should be a List of strings. E.g. "ja" to support German.
|
||||
-- @param[opt] check_objs True if objects should be evaluated.
|
||||
-- @return true if the input evaluates to true, otherwise false.
|
||||
function types.to_bool(o, true_strs, check_objs)
|
||||
local true_func
|
||||
if true_strs then
|
||||
assert_arg(2, true_strs, "table")
|
||||
end
|
||||
true_func = true_types[type(o)]
|
||||
if true_func then
|
||||
return true_func(o, true_strs, check_objs)
|
||||
elseif check_objs and o ~= nil then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
return types
|
51
Documentation/compiler/pl/url.lua
Normal file
51
Documentation/compiler/pl/url.lua
Normal file
|
@ -0,0 +1,51 @@
|
|||
--- Python-style URL quoting library.
|
||||
--
|
||||
-- @module pl.url
|
||||
|
||||
local url = {}
|
||||
|
||||
local function quote_char(c)
|
||||
return string.format("%%%02X", string.byte(c))
|
||||
end
|
||||
|
||||
--- Quote the url, replacing special characters using the '%xx' escape.
|
||||
-- @string s the string
|
||||
-- @bool quote_plus Also escape slashes and replace spaces by plus signs.
|
||||
-- @return The quoted string, or if `s` wasn't a string, just plain unaltered `s`.
|
||||
function url.quote(s, quote_plus)
|
||||
if type(s) ~= "string" then
|
||||
return s
|
||||
end
|
||||
|
||||
s = s:gsub("\n", "\r\n")
|
||||
s = s:gsub("([^A-Za-z0-9 %-_%./])", quote_char)
|
||||
if quote_plus then
|
||||
s = s:gsub(" ", "+")
|
||||
s = s:gsub("/", quote_char)
|
||||
else
|
||||
s = s:gsub(" ", "%%20")
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
local function unquote_char(h)
|
||||
return string.char(tonumber(h, 16))
|
||||
end
|
||||
|
||||
--- Unquote the url, replacing '%xx' escapes and plus signs.
|
||||
-- @string s the string
|
||||
-- @return The unquoted string, or if `s` wasn't a string, just plain unaltered `s`.
|
||||
function url.unquote(s)
|
||||
if type(s) ~= "string" then
|
||||
return s
|
||||
end
|
||||
|
||||
s = s:gsub("+", " ")
|
||||
s = s:gsub("%%(%x%x)", unquote_char)
|
||||
s = s:gsub("\r\n", "\n")
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
return url
|
947
Documentation/compiler/pl/utils.lua
Normal file
947
Documentation/compiler/pl/utils.lua
Normal file
|
@ -0,0 +1,947 @@
|
|||
--- Generally useful routines.
|
||||
-- See @{01-introduction.md.Generally_useful_functions|the Guide}.
|
||||
--
|
||||
-- Dependencies: `pl.compat`, all exported fields and functions from
|
||||
-- `pl.compat` are also available in this module.
|
||||
--
|
||||
-- @module pl.utils
|
||||
local format = string.format
|
||||
local compat = require 'pl.compat'
|
||||
local stdout = io.stdout
|
||||
local append = table.insert
|
||||
local concat = table.concat
|
||||
local _unpack = table.unpack -- always injected by 'compat'
|
||||
local find = string.find
|
||||
local sub = string.sub
|
||||
local next = next
|
||||
local floor = math.floor
|
||||
|
||||
local is_windows = compat.is_windows
|
||||
local err_mode = 'default'
|
||||
local raise
|
||||
local operators
|
||||
local _function_factories = {}
|
||||
|
||||
|
||||
local utils = { _VERSION = "1.13.1" }
|
||||
for k, v in pairs(compat) do utils[k] = v end
|
||||
|
||||
--- Some standard patterns
|
||||
-- @table patterns
|
||||
utils.patterns = {
|
||||
FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', -- floating point number
|
||||
INTEGER = '[+%-%d]%d*', -- integer number
|
||||
IDEN = '[%a_][%w_]*', -- identifier
|
||||
FILE = '[%a%.\\][:%][%w%._%-\\]*', -- file
|
||||
}
|
||||
|
||||
|
||||
--- Standard meta-tables as used by other Penlight modules
|
||||
-- @table stdmt
|
||||
-- @field List the List metatable
|
||||
-- @field Map the Map metatable
|
||||
-- @field Set the Set metatable
|
||||
-- @field MultiMap the MultiMap metatable
|
||||
utils.stdmt = {
|
||||
List = {_name='List'},
|
||||
Map = {_name='Map'},
|
||||
Set = {_name='Set'},
|
||||
MultiMap = {_name='MultiMap'},
|
||||
}
|
||||
|
||||
|
||||
--- pack an argument list into a table.
|
||||
-- @param ... any arguments
|
||||
-- @return a table with field `n` set to the length
|
||||
-- @function utils.pack
|
||||
-- @see compat.pack
|
||||
-- @see utils.npairs
|
||||
-- @see utils.unpack
|
||||
utils.pack = table.pack -- added here to be symmetrical with unpack
|
||||
|
||||
--- unpack a table and return its contents.
|
||||
--
|
||||
-- NOTE: this implementation differs from the Lua implementation in the way
|
||||
-- that this one DOES honor the `n` field in the table `t`, such that it is 'nil-safe'.
|
||||
-- @param t table to unpack
|
||||
-- @param[opt] i index from which to start unpacking, defaults to 1
|
||||
-- @param[opt] j index of the last element to unpack, defaults to `t.n` or else `#t`
|
||||
-- @return multiple return values from the table
|
||||
-- @function utils.unpack
|
||||
-- @see compat.unpack
|
||||
-- @see utils.pack
|
||||
-- @see utils.npairs
|
||||
-- @usage
|
||||
-- local t = table.pack(nil, nil, nil, 4)
|
||||
-- local a, b, c, d = table.unpack(t) -- this `unpack` is NOT nil-safe, so d == nil
|
||||
--
|
||||
-- local a, b, c, d = utils.unpack(t) -- this is nil-safe, so d == 4
|
||||
function utils.unpack(t, i, j)
|
||||
return _unpack(t, i or 1, j or t.n or #t)
|
||||
end
|
||||
|
||||
--- print an arbitrary number of arguments using a format.
|
||||
-- Output will be sent to `stdout`.
|
||||
-- @param fmt The format (see `string.format`)
|
||||
-- @param ... Extra arguments for format
|
||||
function utils.printf(fmt, ...)
|
||||
utils.assert_string(1, fmt)
|
||||
utils.fprintf(stdout, fmt, ...)
|
||||
end
|
||||
|
||||
--- write an arbitrary number of arguments to a file using a format.
|
||||
-- @param f File handle to write to.
|
||||
-- @param fmt The format (see `string.format`).
|
||||
-- @param ... Extra arguments for format
|
||||
function utils.fprintf(f,fmt,...)
|
||||
utils.assert_string(2,fmt)
|
||||
f:write(format(fmt,...))
|
||||
end
|
||||
|
||||
do
|
||||
local function import_symbol(T,k,v,libname)
|
||||
local key = rawget(T,k)
|
||||
-- warn about collisions!
|
||||
if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then
|
||||
utils.fprintf(io.stderr,"warning: '%s.%s' will not override existing symbol\n",libname,k)
|
||||
return
|
||||
end
|
||||
rawset(T,k,v)
|
||||
end
|
||||
|
||||
local function lookup_lib(T,t)
|
||||
for k,v in pairs(T) do
|
||||
if v == t then return k end
|
||||
end
|
||||
return '?'
|
||||
end
|
||||
|
||||
local already_imported = {}
|
||||
|
||||
--- take a table and 'inject' it into the local namespace.
|
||||
-- @param t The table (table), or module name (string), defaults to this `utils` module table
|
||||
-- @param T An optional destination table (defaults to callers environment)
|
||||
function utils.import(t,T)
|
||||
T = T or _G
|
||||
t = t or utils
|
||||
if type(t) == 'string' then
|
||||
t = require (t)
|
||||
end
|
||||
local libname = lookup_lib(T,t)
|
||||
if already_imported[t] then return end
|
||||
already_imported[t] = libname
|
||||
for k,v in pairs(t) do
|
||||
import_symbol(T,k,v,libname)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- return either of two values, depending on a condition.
|
||||
-- @param cond A condition
|
||||
-- @param value1 Value returned if cond is truthy
|
||||
-- @param value2 Value returned if cond is falsy
|
||||
function utils.choose(cond, value1, value2)
|
||||
return cond and value1 or value2
|
||||
end
|
||||
|
||||
--- convert an array of values to strings.
|
||||
-- @param t a list-like table
|
||||
-- @param[opt] temp (table) buffer to use, otherwise allocate
|
||||
-- @param[opt] tostr custom tostring function, called with (value,index). Defaults to `tostring`.
|
||||
-- @return the converted buffer
|
||||
function utils.array_tostring (t,temp,tostr)
|
||||
temp, tostr = temp or {}, tostr or tostring
|
||||
for i = 1,#t do
|
||||
temp[i] = tostr(t[i],i)
|
||||
end
|
||||
return temp
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- is the object of the specified type?
|
||||
-- If the type is a string, then use type, otherwise compare with metatable
|
||||
-- @param obj An object to check
|
||||
-- @param tp String of what type it should be
|
||||
-- @return boolean
|
||||
-- @usage utils.is_type("hello world", "string") --> true
|
||||
-- -- or check metatable
|
||||
-- local my_mt = {}
|
||||
-- local my_obj = setmetatable(my_obj, my_mt)
|
||||
-- utils.is_type(my_obj, my_mt) --> true
|
||||
function utils.is_type (obj,tp)
|
||||
if type(tp) == 'string' then return type(obj) == tp end
|
||||
local mt = getmetatable(obj)
|
||||
return tp == mt
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- an iterator with indices, similar to `ipairs`, but with a range.
|
||||
-- This is a nil-safe index based iterator that will return `nil` when there
|
||||
-- is a hole in a list. To be safe ensure that table `t.n` contains the length.
|
||||
-- @tparam table t the table to iterate over
|
||||
-- @tparam[opt=1] integer i_start start index
|
||||
-- @tparam[opt=t.n or #t] integer i_end end index
|
||||
-- @tparam[opt=1] integer step step size
|
||||
-- @treturn integer index
|
||||
-- @treturn any value at index (which can be `nil`!)
|
||||
-- @see utils.pack
|
||||
-- @see utils.unpack
|
||||
-- @usage
|
||||
-- local t = utils.pack(nil, 123, nil) -- adds an `n` field when packing
|
||||
--
|
||||
-- for i, v in utils.npairs(t, 2) do -- start at index 2
|
||||
-- t[i] = tostring(t[i])
|
||||
-- end
|
||||
--
|
||||
-- -- t = { n = 3, [2] = "123", [3] = "nil" }
|
||||
function utils.npairs(t, i_start, i_end, step)
|
||||
step = step or 1
|
||||
if step == 0 then
|
||||
error("iterator step-size cannot be 0", 2)
|
||||
end
|
||||
local i = (i_start or 1) - step
|
||||
i_end = i_end or t.n or #t
|
||||
if step < 0 then
|
||||
return function()
|
||||
i = i + step
|
||||
if i < i_end then
|
||||
return nil
|
||||
end
|
||||
return i, t[i]
|
||||
end
|
||||
|
||||
else
|
||||
return function()
|
||||
i = i + step
|
||||
if i > i_end then
|
||||
return nil
|
||||
end
|
||||
return i, t[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- an iterator over all non-integer keys (inverse of `ipairs`).
|
||||
-- It will skip any key that is an integer number, so negative indices or an
|
||||
-- array with holes will not return those either (so it returns slightly less than
|
||||
-- 'the inverse of `ipairs`').
|
||||
--
|
||||
-- This uses `pairs` under the hood, so any value that is iterable using `pairs`
|
||||
-- will work with this function.
|
||||
-- @tparam table t the table to iterate over
|
||||
-- @treturn key
|
||||
-- @treturn value
|
||||
-- @usage
|
||||
-- local t = {
|
||||
-- "hello",
|
||||
-- "world",
|
||||
-- hello = "hallo",
|
||||
-- world = "Welt",
|
||||
-- }
|
||||
--
|
||||
-- for k, v in utils.kpairs(t) do
|
||||
-- print("German: ", v)
|
||||
-- end
|
||||
--
|
||||
-- -- output;
|
||||
-- -- German: hallo
|
||||
-- -- German: Welt
|
||||
function utils.kpairs(t)
|
||||
local index
|
||||
return function()
|
||||
local value
|
||||
while true do
|
||||
index, value = next(t, index)
|
||||
if type(index) ~= "number" or floor(index) ~= index then
|
||||
break
|
||||
end
|
||||
end
|
||||
return index, value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Error handling
|
||||
-- @section Error-handling
|
||||
|
||||
--- assert that the given argument is in fact of the correct type.
|
||||
-- @param n argument index
|
||||
-- @param val the value
|
||||
-- @param tp the type
|
||||
-- @param verify an optional verification function
|
||||
-- @param msg an optional custom message
|
||||
-- @param lev optional stack position for trace, default 2
|
||||
-- @return the validated value
|
||||
-- @raise if `val` is not the correct type
|
||||
-- @usage
|
||||
-- local param1 = assert_arg(1,"hello",'table') --> error: argument 1 expected a 'table', got a 'string'
|
||||
-- local param4 = assert_arg(4,'!@#$%^&*','string',path.isdir,'not a directory')
|
||||
-- --> error: argument 4: '!@#$%^&*' not a directory
|
||||
function utils.assert_arg (n,val,tp,verify,msg,lev)
|
||||
if type(val) ~= tp then
|
||||
error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2)
|
||||
end
|
||||
if verify and not verify(val) then
|
||||
error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2)
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
--- creates an Enum or constants lookup table for improved error handling.
|
||||
-- This helps prevent magic strings in code by throwing errors for accessing
|
||||
-- non-existing values, and/or converting strings/identifiers to other values.
|
||||
--
|
||||
-- Calling on the object does the same, but returns a soft error; `nil + err`, if
|
||||
-- the call is succesful (the key exists), it will return the value.
|
||||
--
|
||||
-- When calling with varargs or an array the values will be equal to the keys.
|
||||
-- The enum object is read-only.
|
||||
-- @tparam table|vararg ... the input for the Enum. If varargs or an array then the
|
||||
-- values in the Enum will be equal to the names (must be strings), if a hash-table
|
||||
-- then values remain (any type), and the keys must be strings.
|
||||
-- @return Enum object (read-only table/object)
|
||||
-- @usage -- Enum access at runtime
|
||||
-- local obj = {}
|
||||
-- obj.MOVEMENT = utils.enum("FORWARD", "REVERSE", "LEFT", "RIGHT")
|
||||
--
|
||||
-- if current_movement == obj.MOVEMENT.FORWARD then
|
||||
-- -- do something
|
||||
--
|
||||
-- elseif current_movement == obj.MOVEMENT.REVERES then
|
||||
-- -- throws error due to typo 'REVERES', so a silent mistake becomes a hard error
|
||||
-- -- "'REVERES' is not a valid value (expected one of: 'FORWARD', 'REVERSE', 'LEFT', 'RIGHT')"
|
||||
--
|
||||
-- end
|
||||
-- @usage -- standardized error codes
|
||||
-- local obj = {
|
||||
-- ERR = utils.enum {
|
||||
-- NOT_FOUND = "the item was not found",
|
||||
-- OUT_OF_BOUNDS = "the index is outside the allowed range"
|
||||
-- },
|
||||
--
|
||||
-- some_method = function(self)
|
||||
-- return self.ERR.OUT_OF_BOUNDS
|
||||
-- end,
|
||||
-- }
|
||||
--
|
||||
-- local result, err = obj:some_method()
|
||||
-- if not result then
|
||||
-- if err == obj.ERR.NOT_FOUND then
|
||||
-- -- check on error code, not magic strings
|
||||
--
|
||||
-- else
|
||||
-- -- return the error description, contained in the constant
|
||||
-- return nil, "error: "..err -- "error: the index is outside the allowed range"
|
||||
-- end
|
||||
-- end
|
||||
-- @usage -- validating/converting user-input
|
||||
-- local color = "purple"
|
||||
-- local ansi_colors = utils.enum {
|
||||
-- black = 30,
|
||||
-- red = 31,
|
||||
-- green = 32,
|
||||
-- }
|
||||
-- local color_code, err = ansi_colors(color) -- calling on the object, returns the value from the enum
|
||||
-- if not color_code then
|
||||
-- print("bad 'color', " .. err)
|
||||
-- -- "bad 'color', 'purple' is not a valid value (expected one of: 'black', 'red', 'green')"
|
||||
-- os.exit(1)
|
||||
-- end
|
||||
function utils.enum(...)
|
||||
local first = select(1, ...)
|
||||
local enum = {}
|
||||
local lst
|
||||
|
||||
if type(first) ~= "table" then
|
||||
-- vararg with strings
|
||||
lst = utils.pack(...)
|
||||
for i, value in utils.npairs(lst) do
|
||||
utils.assert_arg(i, value, "string")
|
||||
enum[value] = value
|
||||
end
|
||||
|
||||
else
|
||||
-- table/array with values
|
||||
utils.assert_arg(1, first, "table")
|
||||
lst = {}
|
||||
-- first add array part
|
||||
for i, value in ipairs(first) do
|
||||
if type(value) ~= "string" then
|
||||
error(("expected 'string' but got '%s' at index %d"):format(type(value), i), 2)
|
||||
end
|
||||
lst[i] = value
|
||||
enum[value] = value
|
||||
end
|
||||
-- add key-ed part
|
||||
for key, value in utils.kpairs(first) do
|
||||
if type(key) ~= "string" then
|
||||
error(("expected key to be 'string' but got '%s'"):format(type(key)), 2)
|
||||
end
|
||||
if enum[key] then
|
||||
error(("duplicate entry in array and hash part: '%s'"):format(key), 2)
|
||||
end
|
||||
enum[key] = value
|
||||
lst[#lst+1] = key
|
||||
end
|
||||
end
|
||||
|
||||
if not lst[1] then
|
||||
error("expected at least 1 entry", 2)
|
||||
end
|
||||
|
||||
local valid = "(expected one of: '" .. concat(lst, "', '") .. "')"
|
||||
setmetatable(enum, {
|
||||
__index = function(self, key)
|
||||
error(("'%s' is not a valid value %s"):format(tostring(key), valid), 2)
|
||||
end,
|
||||
__newindex = function(self, key, value)
|
||||
error("the Enum object is read-only", 2)
|
||||
end,
|
||||
__call = function(self, key)
|
||||
if type(key) == "string" then
|
||||
local v = rawget(self, key)
|
||||
if v ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return nil, ("'%s' is not a valid value %s"):format(tostring(key), valid)
|
||||
end
|
||||
})
|
||||
|
||||
return enum
|
||||
end
|
||||
|
||||
|
||||
--- process a function argument.
|
||||
-- This is used throughout Penlight and defines what is meant by a function:
|
||||
-- Something that is callable, or an operator string as defined by <code>pl.operator</code>,
|
||||
-- such as '>' or '#'. If a function factory has been registered for the type, it will
|
||||
-- be called to get the function.
|
||||
-- @param idx argument index
|
||||
-- @param f a function, operator string, or callable object
|
||||
-- @param msg optional error message
|
||||
-- @return a callable
|
||||
-- @raise if idx is not a number or if f is not callable
|
||||
function utils.function_arg (idx,f,msg)
|
||||
utils.assert_arg(1,idx,'number')
|
||||
local tp = type(f)
|
||||
if tp == 'function' then return f end -- no worries!
|
||||
-- ok, a string can correspond to an operator (like '==')
|
||||
if tp == 'string' then
|
||||
if not operators then operators = require 'pl.operator'.optable end
|
||||
local fn = operators[f]
|
||||
if fn then return fn end
|
||||
local fn, err = utils.string_lambda(f)
|
||||
if not fn then error(err..': '..f) end
|
||||
return fn
|
||||
elseif tp == 'table' or tp == 'userdata' then
|
||||
local mt = getmetatable(f)
|
||||
if not mt then error('not a callable object',2) end
|
||||
local ff = _function_factories[mt]
|
||||
if not ff then
|
||||
if not mt.__call then error('not a callable object',2) end
|
||||
return f
|
||||
else
|
||||
return ff(f) -- we have a function factory for this type!
|
||||
end
|
||||
end
|
||||
if not msg then msg = " must be callable" end
|
||||
if idx > 0 then
|
||||
error("argument "..idx..": "..msg,2)
|
||||
else
|
||||
error(msg,2)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- assert the common case that the argument is a string.
|
||||
-- @param n argument index
|
||||
-- @param val a value that must be a string
|
||||
-- @return the validated value
|
||||
-- @raise val must be a string
|
||||
-- @usage
|
||||
-- local val = 42
|
||||
-- local param2 = utils.assert_string(2, val) --> error: argument 2 expected a 'string', got a 'number'
|
||||
function utils.assert_string (n, val)
|
||||
return utils.assert_arg(n,val,'string',nil,nil,3)
|
||||
end
|
||||
|
||||
--- control the error strategy used by Penlight.
|
||||
-- This is a global setting that controls how `utils.raise` behaves:
|
||||
--
|
||||
-- - 'default': return `nil + error` (this is the default)
|
||||
-- - 'error': throw a Lua error
|
||||
-- - 'quit': exit the program
|
||||
--
|
||||
-- @param mode either 'default', 'quit' or 'error'
|
||||
-- @see utils.raise
|
||||
function utils.on_error (mode)
|
||||
mode = tostring(mode)
|
||||
if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then
|
||||
err_mode = mode
|
||||
else
|
||||
-- fail loudly
|
||||
local err = "Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'"
|
||||
if err_mode == 'default' then
|
||||
error(err, 2) -- even in 'default' mode fail loud in this case
|
||||
end
|
||||
raise(err)
|
||||
end
|
||||
end
|
||||
|
||||
--- used by Penlight functions to return errors. Its global behaviour is controlled
|
||||
-- by `utils.on_error`.
|
||||
-- To use this function you MUST use it in conjunction with `return`, since it might
|
||||
-- return `nil + error`.
|
||||
-- @param err the error string.
|
||||
-- @see utils.on_error
|
||||
-- @usage
|
||||
-- if some_condition then
|
||||
-- return utils.raise("some condition was not met") -- MUST use 'return'!
|
||||
-- end
|
||||
function utils.raise (err)
|
||||
if err_mode == 'default' then
|
||||
return nil, err
|
||||
elseif err_mode == 'quit' then
|
||||
return utils.quit(err)
|
||||
else
|
||||
error(err, 2)
|
||||
end
|
||||
end
|
||||
raise = utils.raise
|
||||
|
||||
|
||||
|
||||
--- File handling
|
||||
-- @section files
|
||||
|
||||
--- return the contents of a file as a string
|
||||
-- @param filename The file path
|
||||
-- @param is_bin open in binary mode
|
||||
-- @return file contents
|
||||
function utils.readfile(filename,is_bin)
|
||||
local mode = is_bin and 'b' or ''
|
||||
utils.assert_string(1,filename)
|
||||
local f,open_err = io.open(filename,'r'..mode)
|
||||
if not f then return raise (open_err) end
|
||||
local res,read_err = f:read('*a')
|
||||
f:close()
|
||||
if not res then
|
||||
-- Errors in io.open have "filename: " prefix,
|
||||
-- error in file:read don't, add it.
|
||||
return raise (filename..": "..read_err)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- write a string to a file
|
||||
-- @param filename The file path
|
||||
-- @param str The string
|
||||
-- @param is_bin open in binary mode
|
||||
-- @return true or nil
|
||||
-- @return error message
|
||||
-- @raise error if filename or str aren't strings
|
||||
function utils.writefile(filename,str,is_bin)
|
||||
local mode = is_bin and 'b' or ''
|
||||
utils.assert_string(1,filename)
|
||||
utils.assert_string(2,str)
|
||||
local f,err = io.open(filename,'w'..mode)
|
||||
if not f then return raise(err) end
|
||||
local ok, write_err = f:write(str)
|
||||
f:close()
|
||||
if not ok then
|
||||
-- Errors in io.open have "filename: " prefix,
|
||||
-- error in file:write don't, add it.
|
||||
return raise (filename..": "..write_err)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- return the contents of a file as a list of lines
|
||||
-- @param filename The file path
|
||||
-- @return file contents as a table
|
||||
-- @raise error if filename is not a string
|
||||
function utils.readlines(filename)
|
||||
utils.assert_string(1,filename)
|
||||
local f,err = io.open(filename,'r')
|
||||
if not f then return raise(err) end
|
||||
local res = {}
|
||||
for line in f:lines() do
|
||||
append(res,line)
|
||||
end
|
||||
f:close()
|
||||
return res
|
||||
end
|
||||
|
||||
--- OS functions
|
||||
-- @section OS-functions
|
||||
|
||||
--- execute a shell command and return the output.
|
||||
-- This function redirects the output to tempfiles and returns the content of those files.
|
||||
-- @param cmd a shell command
|
||||
-- @param bin boolean, if true, read output as binary file
|
||||
-- @return true if successful
|
||||
-- @return actual return code
|
||||
-- @return stdout output (string)
|
||||
-- @return errout output (string)
|
||||
function utils.executeex(cmd, bin)
|
||||
local outfile = os.tmpname()
|
||||
local errfile = os.tmpname()
|
||||
|
||||
if is_windows and not outfile:find(':') then
|
||||
outfile = os.getenv('TEMP')..outfile
|
||||
errfile = os.getenv('TEMP')..errfile
|
||||
end
|
||||
cmd = cmd .. " > " .. utils.quote_arg(outfile) .. " 2> " .. utils.quote_arg(errfile)
|
||||
|
||||
local success, retcode = utils.execute(cmd)
|
||||
local outcontent = utils.readfile(outfile, bin)
|
||||
local errcontent = utils.readfile(errfile, bin)
|
||||
os.remove(outfile)
|
||||
os.remove(errfile)
|
||||
return success, retcode, (outcontent or ""), (errcontent or "")
|
||||
end
|
||||
|
||||
--- Quote and escape an argument of a command.
|
||||
-- Quotes a single (or list of) argument(s) of a command to be passed
|
||||
-- to `os.execute`, `pl.utils.execute` or `pl.utils.executeex`.
|
||||
-- @param argument (string or table/list) the argument to quote. If a list then
|
||||
-- all arguments in the list will be returned as a single string quoted.
|
||||
-- @return quoted and escaped argument.
|
||||
-- @usage
|
||||
-- local options = utils.quote_arg {
|
||||
-- "-lluacov",
|
||||
-- "-e",
|
||||
-- "utils = print(require('pl.utils')._VERSION",
|
||||
-- }
|
||||
-- -- returns: -lluacov -e 'utils = print(require('\''pl.utils'\'')._VERSION'
|
||||
function utils.quote_arg(argument)
|
||||
if type(argument) == "table" then
|
||||
-- encode an entire table
|
||||
local r = {}
|
||||
for i, arg in ipairs(argument) do
|
||||
r[i] = utils.quote_arg(arg)
|
||||
end
|
||||
|
||||
return concat(r, " ")
|
||||
end
|
||||
-- only a single argument
|
||||
if is_windows then
|
||||
if argument == "" or argument:find('[ \f\t\v]') then
|
||||
-- Need to quote the argument.
|
||||
-- Quotes need to be escaped with backslashes;
|
||||
-- additionally, backslashes before a quote, escaped or not,
|
||||
-- need to be doubled.
|
||||
-- See documentation for CommandLineToArgvW Windows function.
|
||||
argument = '"' .. argument:gsub([[(\*)"]], [[%1%1\"]]):gsub([[\+$]], "%0%0") .. '"'
|
||||
end
|
||||
|
||||
-- os.execute() uses system() C function, which on Windows passes command
|
||||
-- to cmd.exe. Escape its special characters.
|
||||
return (argument:gsub('["^<>!|&%%]', "^%0"))
|
||||
else
|
||||
if argument == "" or argument:find('[^a-zA-Z0-9_@%+=:,./-]') then
|
||||
-- To quote arguments on posix-like systems use single quotes.
|
||||
-- To represent an embedded single quote close quoted string ('),
|
||||
-- add escaped quote (\'), open quoted string again (').
|
||||
argument = "'" .. argument:gsub("'", [['\'']]) .. "'"
|
||||
end
|
||||
|
||||
return argument
|
||||
end
|
||||
end
|
||||
|
||||
--- error out of this program gracefully.
|
||||
-- @param[opt] code The exit code, defaults to -`1` if omitted
|
||||
-- @param msg The exit message will be sent to `stderr` (will be formatted with the extra parameters)
|
||||
-- @param ... extra arguments for message's format'
|
||||
-- @see utils.fprintf
|
||||
-- @usage utils.quit(-1, "Error '%s' happened", "42")
|
||||
-- -- is equivalent to
|
||||
-- utils.quit("Error '%s' happened", "42") --> Error '42' happened
|
||||
function utils.quit(code, msg, ...)
|
||||
if type(code) == 'string' then
|
||||
utils.fprintf(io.stderr, code, msg, ...)
|
||||
io.stderr:write('\n')
|
||||
code = -1 -- TODO: this is odd, see the test. Which returns 255 as exit code
|
||||
elseif msg then
|
||||
utils.fprintf(io.stderr, msg, ...)
|
||||
io.stderr:write('\n')
|
||||
end
|
||||
os.exit(code, true)
|
||||
end
|
||||
|
||||
|
||||
--- String functions
|
||||
-- @section string-functions
|
||||
|
||||
--- escape any Lua 'magic' characters in a string
|
||||
-- @param s The input string
|
||||
function utils.escape(s)
|
||||
utils.assert_string(1,s)
|
||||
return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
|
||||
end
|
||||
|
||||
--- split a string into a list of strings separated by a delimiter.
|
||||
-- @param s The input string
|
||||
-- @param re optional A Lua string pattern; defaults to '%s+'
|
||||
-- @param plain optional If truthy don't use Lua patterns
|
||||
-- @param n optional maximum number of elements (if there are more, the last will remian un-split)
|
||||
-- @return a list-like table
|
||||
-- @raise error if s is not a string
|
||||
-- @see splitv
|
||||
function utils.split(s,re,plain,n)
|
||||
utils.assert_string(1,s)
|
||||
local i1,ls = 1,{}
|
||||
if not re then re = '%s+' end
|
||||
if re == '' then return {s} end
|
||||
while true do
|
||||
local i2,i3 = find(s,re,i1,plain)
|
||||
if not i2 then
|
||||
local last = sub(s,i1)
|
||||
if last ~= '' then append(ls,last) end
|
||||
if #ls == 1 and ls[1] == '' then
|
||||
return {}
|
||||
else
|
||||
return ls
|
||||
end
|
||||
end
|
||||
append(ls,sub(s,i1,i2-1))
|
||||
if n and #ls == n then
|
||||
ls[#ls] = sub(s,i1)
|
||||
return ls
|
||||
end
|
||||
i1 = i3+1
|
||||
end
|
||||
end
|
||||
|
||||
--- split a string into a number of return values.
|
||||
-- Identical to `split` but returns multiple sub-strings instead of
|
||||
-- a single list of sub-strings.
|
||||
-- @param s the string
|
||||
-- @param re A Lua string pattern; defaults to '%s+'
|
||||
-- @param plain don't use Lua patterns
|
||||
-- @param n optional maximum number of splits
|
||||
-- @return n values
|
||||
-- @usage first,next = splitv('user=jane=doe','=', false, 2)
|
||||
-- assert(first == "user")
|
||||
-- assert(next == "jane=doe")
|
||||
-- @see split
|
||||
function utils.splitv (s,re, plain, n)
|
||||
return _unpack(utils.split(s,re, plain, n))
|
||||
end
|
||||
|
||||
|
||||
--- Functional
|
||||
-- @section functional
|
||||
|
||||
|
||||
--- 'memoize' a function (cache returned value for next call).
|
||||
-- This is useful if you have a function which is relatively expensive,
|
||||
-- but you don't know in advance what values will be required, so
|
||||
-- building a table upfront is wasteful/impossible.
|
||||
-- @param func a function of at least one argument
|
||||
-- @return a function with at least one argument, which is used as the key.
|
||||
function utils.memoize(func)
|
||||
local cache = {}
|
||||
return function(k)
|
||||
local res = cache[k]
|
||||
if res == nil then
|
||||
res = func(k)
|
||||
cache[k] = res
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- associate a function factory with a type.
|
||||
-- A function factory takes an object of the given type and
|
||||
-- returns a function for evaluating it
|
||||
-- @tab mt metatable
|
||||
-- @func fun a callable that returns a function
|
||||
function utils.add_function_factory (mt,fun)
|
||||
_function_factories[mt] = fun
|
||||
end
|
||||
|
||||
local function _string_lambda(f)
|
||||
if f:find '^|' or f:find '_' then
|
||||
local args,body = f:match '|([^|]*)|(.+)'
|
||||
if f:find '_' then
|
||||
args = '_'
|
||||
body = f
|
||||
else
|
||||
if not args then return raise 'bad string lambda' end
|
||||
end
|
||||
local fstr = 'return function('..args..') return '..body..' end'
|
||||
local fn,err = utils.load(fstr)
|
||||
if not fn then return raise(err) end
|
||||
fn = fn()
|
||||
return fn
|
||||
else
|
||||
return raise 'not a string lambda'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- an anonymous function as a string. This string is either of the form
|
||||
-- '|args| expression' or is a function of one argument, '_'
|
||||
-- @param lf function as a string
|
||||
-- @return a function
|
||||
-- @function utils.string_lambda
|
||||
-- @usage
|
||||
-- string_lambda '|x|x+1' (2) == 3
|
||||
-- string_lambda '_+1' (2) == 3
|
||||
utils.string_lambda = utils.memoize(_string_lambda)
|
||||
|
||||
|
||||
--- bind the first argument of the function to a value.
|
||||
-- @param fn a function of at least two values (may be an operator string)
|
||||
-- @param p a value
|
||||
-- @return a function such that f(x) is fn(p,x)
|
||||
-- @raise same as @{function_arg}
|
||||
-- @see func.bind1
|
||||
-- @usage local function f(msg, name)
|
||||
-- print(msg .. " " .. name)
|
||||
-- end
|
||||
--
|
||||
-- local hello = utils.bind1(f, "Hello")
|
||||
--
|
||||
-- print(hello("world")) --> "Hello world"
|
||||
-- print(hello("sunshine")) --> "Hello sunshine"
|
||||
function utils.bind1 (fn,p)
|
||||
fn = utils.function_arg(1,fn)
|
||||
return function(...) return fn(p,...) end
|
||||
end
|
||||
|
||||
|
||||
--- bind the second argument of the function to a value.
|
||||
-- @param fn a function of at least two values (may be an operator string)
|
||||
-- @param p a value
|
||||
-- @return a function such that f(x) is fn(x,p)
|
||||
-- @raise same as @{function_arg}
|
||||
-- @usage local function f(a, b, c)
|
||||
-- print(a .. " " .. b .. " " .. c)
|
||||
-- end
|
||||
--
|
||||
-- local hello = utils.bind1(f, "world")
|
||||
--
|
||||
-- print(hello("Hello", "!")) --> "Hello world !"
|
||||
-- print(hello("Bye", "?")) --> "Bye world ?"
|
||||
function utils.bind2 (fn,p)
|
||||
fn = utils.function_arg(1,fn)
|
||||
return function(x,...) return fn(x,p,...) end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Deprecation
|
||||
-- @section deprecation
|
||||
|
||||
do
|
||||
-- the default implementation
|
||||
local deprecation_func = function(msg, trace)
|
||||
if trace then
|
||||
warn(msg, "\n", trace) -- luacheck: ignore
|
||||
else
|
||||
warn(msg) -- luacheck: ignore
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets a deprecation warning function.
|
||||
-- An application can override this function to support proper output of
|
||||
-- deprecation warnings. The warnings can be generated from libraries or
|
||||
-- functions by calling `utils.raise_deprecation`. The default function
|
||||
-- will write to the 'warn' system (introduced in Lua 5.4, or the compatibility
|
||||
-- function from the `compat` module for earlier versions).
|
||||
--
|
||||
-- Note: only applications should set/change this function, libraries should not.
|
||||
-- @param func a callback with signature: `function(msg, trace)` both arguments are strings, the latter being optional.
|
||||
-- @see utils.raise_deprecation
|
||||
-- @usage
|
||||
-- -- write to the Nginx logs with OpenResty
|
||||
-- utils.set_deprecation_func(function(msg, trace)
|
||||
-- ngx.log(ngx.WARN, msg, (trace and (" " .. trace) or nil))
|
||||
-- end)
|
||||
--
|
||||
-- -- disable deprecation warnings
|
||||
-- utils.set_deprecation_func()
|
||||
function utils.set_deprecation_func(func)
|
||||
if func == nil then
|
||||
deprecation_func = function() end
|
||||
else
|
||||
utils.assert_arg(1, func, "function")
|
||||
deprecation_func = func
|
||||
end
|
||||
end
|
||||
|
||||
--- raises a deprecation warning.
|
||||
-- For options see the usage example below.
|
||||
--
|
||||
-- Note: the `opts.deprecated_after` field is the last version in which
|
||||
-- a feature or option was NOT YET deprecated! Because when writing the code it
|
||||
-- is quite often not known in what version the code will land. But the last
|
||||
-- released version is usually known.
|
||||
-- @param opts options table
|
||||
-- @see utils.set_deprecation_func
|
||||
-- @usage
|
||||
-- warn("@on") -- enable Lua warnings, they are usually off by default
|
||||
--
|
||||
-- function stringx.islower(str)
|
||||
-- raise_deprecation {
|
||||
-- source = "Penlight " .. utils._VERSION, -- optional
|
||||
-- message = "function 'islower' was renamed to 'is_lower'", -- required
|
||||
-- version_removed = "2.0.0", -- optional
|
||||
-- deprecated_after = "1.2.3", -- optional
|
||||
-- no_trace = true, -- optional
|
||||
-- }
|
||||
-- return stringx.is_lower(str)
|
||||
-- end
|
||||
-- -- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)"
|
||||
function utils.raise_deprecation(opts)
|
||||
utils.assert_arg(1, opts, "table")
|
||||
if type(opts.message) ~= "string" then
|
||||
error("field 'message' of the options table must be a string", 2)
|
||||
end
|
||||
local trace
|
||||
if not opts.no_trace then
|
||||
trace = debug.traceback("", 2):match("[\n%s]*(.-)$")
|
||||
end
|
||||
local msg
|
||||
if opts.deprecated_after and opts.version_removed then
|
||||
msg = (" (deprecated after %s, scheduled for removal in %s)"):format(
|
||||
tostring(opts.deprecated_after), tostring(opts.version_removed))
|
||||
elseif opts.deprecated_after then
|
||||
msg = (" (deprecated after %s)"):format(tostring(opts.deprecated_after))
|
||||
elseif opts.version_removed then
|
||||
msg = (" (scheduled for removal in %s)"):format(tostring(opts.version_removed))
|
||||
else
|
||||
msg = ""
|
||||
end
|
||||
|
||||
msg = opts.message .. msg
|
||||
|
||||
if opts.source then
|
||||
msg = "[" .. opts.source .."] " .. msg
|
||||
else
|
||||
if msg:sub(1,1) == "@" then
|
||||
-- in Lua 5.4 "@" prefixed messages are control messages to the warn system
|
||||
error("message cannot start with '@'", 2)
|
||||
end
|
||||
end
|
||||
|
||||
deprecation_func(msg, trace)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
return utils
|
||||
|
||||
|
1165
Documentation/compiler/pl/xml.lua
Normal file
1165
Documentation/compiler/pl/xml.lua
Normal file
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,7 @@ new_type("luautil", "5 Lua utility modules", true)
|
|||
|
||||
not_luadoc = true
|
||||
|
||||
local version = "1.1.0"
|
||||
local version = "1.3"
|
||||
project = "TombEngine"
|
||||
title = "TombEngine " .. version .. " Lua API"
|
||||
description = "TombEngine " .. version .. " scripting interface"
|
||||
|
@ -51,4 +51,4 @@ custom_display_name_handler = function(item, default_handler)
|
|||
end
|
||||
local hand = default_handler(item)
|
||||
return hand
|
||||
end
|
||||
end
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <here>Effects</here></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -131,10 +140,6 @@
|
|||
<td class="name" ><a href="#MakeEarthquake">MakeEarthquake(strength)</a></td>
|
||||
<td class="summary">Make an earthquake</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#FlashScreen">FlashScreen(color, speed)</a></td>
|
||||
<td class="summary">Flash screen.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
@ -490,32 +495,6 @@
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "FlashScreen"></a>
|
||||
<strong>FlashScreen(color, speed)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Flash screen.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">color</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
(default Color(255, 255, 255))
|
||||
</li>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). Speed in "amount" per second. Value of 1 will make flash take one second. Clamped to [0.005, 1.0].
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
@ -524,7 +503,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <here>Flow</here></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -147,8 +156,12 @@ scripts too.</p>
|
|||
<td class="summary">Returns the level that the game control is running in that moment.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EndLevel">EndLevel([index])</a></td>
|
||||
<td class="summary">Finishes the current level, with optional level index provided.</td>
|
||||
<td class="name" ><a href="#EndLevel">EndLevel([index][, startPos])</a></td>
|
||||
<td class="summary">Finishes the current level, with optional level index and start position index provided.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetGameStatus">GetGameStatus()</a></td>
|
||||
<td class="summary">Get current game status, such as normal game loop, exiting to title, etc.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SaveGame">SaveGame(slotID)</a></td>
|
||||
|
@ -163,6 +176,10 @@ scripts too.</p>
|
|||
<td class="summary">Delete a savegame.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DoesSaveGameExist">DoesSaveGameExist(slotID)</a></td>
|
||||
<td class="summary">Check if a savegame exists.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetSecretCount">GetSecretCount()</a></td>
|
||||
<td class="summary">Returns the player's current per-game secret count.</td>
|
||||
</tr>
|
||||
|
@ -178,6 +195,14 @@ scripts too.</p>
|
|||
<td class="name" ><a href="#SetTotalSecretCount">SetTotalSecretCount(total)</a></td>
|
||||
<td class="summary">Total number of secrets in game.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#FlipMap">FlipMap(flipmap)</a></td>
|
||||
<td class="summary">Do FlipMap with specific group ID.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFlipMapStatus">GetFlipMapStatus([index])</a></td>
|
||||
<td class="summary">Get current FlipMap status for specific group ID.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#settings_lua">settings.lua </a></h2>
|
||||
<table class="function_list">
|
||||
|
@ -202,7 +227,7 @@ scripts too.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetString">GetString(string)</a></td>
|
||||
<td class="summary">Get translated string</td>
|
||||
<td class="summary">Get translated string.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetLanguageNames">SetLanguageNames(table)</a></td>
|
||||
|
@ -485,12 +510,13 @@ have an ID of 0, the second an ID of 1, and so on.
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "EndLevel"></a>
|
||||
<strong>EndLevel([index])</strong>
|
||||
<strong>EndLevel([index][, startPos])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Finishes the current level, with optional level index provided. If level index
|
||||
is not provided or is zero, jumps to next level. If level index is more than
|
||||
level count, jumps to title.
|
||||
Finishes the current level, with optional level index and start position index provided.
|
||||
If level index is not provided or is zero, jumps to next level. If level index is more than
|
||||
level count, jumps to title. If LARA_START_POS objects are present in level, player will be
|
||||
teleported to such object with OCB similar to provided second argument.
|
||||
|
||||
|
||||
|
||||
|
@ -501,12 +527,38 @@ level count, jumps to title.
|
|||
level index (default 0)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">startPos</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
player start position (default 0)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetGameStatus"></a>
|
||||
<strong>GetGameStatus()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current game status, such as normal game loop, exiting to title, etc.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Flow.GameStatus.html#">GameStatus</a></span>
|
||||
the current game status
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SaveGame"></a>
|
||||
|
@ -573,6 +625,34 @@ level count, jumps to title.
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DoesSaveGameExist"></a>
|
||||
<strong>DoesSaveGameExist(slotID)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if a savegame exists.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">slotID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID of the savegame slot to check.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
true if the savegame exists, false if not.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetSecretCount"></a>
|
||||
|
@ -662,6 +742,57 @@ Must be an integer value (0 means no secrets).
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "FlipMap"></a>
|
||||
<strong>FlipMap(flipmap)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Do FlipMap with specific group ID.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">flipmap</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
(ID of flipmap group to actuvate / deactivate)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFlipMapStatus"></a>
|
||||
<strong>GetFlipMapStatus([index])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current FlipMap status for specific group ID.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">index</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Flipmap group ID to check. If no group specified or group is -1, function returns overall flipmap status (on or off).
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Status of the flipmap group (true means on, false means off).
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 class="section-header has-description"><a name="settings_lua"></a>settings.lua </h2>
|
||||
|
@ -755,7 +886,7 @@ You will not need to call them manually.
|
|||
<strong>GetString(string)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get translated string
|
||||
Get translated string.
|
||||
|
||||
|
||||
|
||||
|
@ -802,7 +933,6 @@ Specify which translations in the strings table correspond to which languages.
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
289
Documentation/doc/1 modules/Input.html
Normal file
289
Documentation/doc/1 modules/Input.html
Normal file
|
@ -0,0 +1,289 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>1 Modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <here>Input</here></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
|
||||
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
|
||||
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
|
||||
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
|
||||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Table <code>Input</code></h1>
|
||||
<p>Functions for input management.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#Vibrate">Vibrate(strength, time)</a></td>
|
||||
<td class="summary">Vibrate the game controller if the function is available and the setting is on.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#KeyIsHeld">KeyIsHeld(action)</a></td>
|
||||
<td class="summary">Check if an action key is being held.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#KeyIsHit">KeyIsHit(action)</a></td>
|
||||
<td class="summary">Check if an action key is being hit or clicked.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#KeyPush">KeyPush(action)</a></td>
|
||||
<td class="summary">Simulate an action key push.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#KeyClear">KeyClear(action)</a></td>
|
||||
<td class="summary">Clear an action key.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetMouseDisplayPosition">GetMouseDisplayPosition()</a></td>
|
||||
<td class="summary">Get the display position of the cursor in percent.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "Vibrate"></a>
|
||||
<strong>Vibrate(strength, time)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Vibrate the game controller if the function is available and the setting is on.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">strength</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Vibration strength.
|
||||
</li>
|
||||
<li><span class="parameter">time</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
<strong>(default 0.3)</strong> Vibration time in seconds.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "KeyIsHeld"></a>
|
||||
<strong>KeyIsHeld(action)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if an action key is being held.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">action</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Input.ActionID.html#">ActionID</a></span>
|
||||
Action ID to check.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "KeyIsHit"></a>
|
||||
<strong>KeyIsHit(action)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if an action key is being hit or clicked.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">action</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Input.ActionID.html#">ActionID</a></span>
|
||||
Action ID to check.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "KeyPush"></a>
|
||||
<strong>KeyPush(action)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Simulate an action key push.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">action</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Input.ActionID.html#">ActionID</a></span>
|
||||
Action ID to push.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "KeyClear"></a>
|
||||
<strong>KeyClear(action)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Clear an action key.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">action</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Input.ActionID.html#">ActionID</a></span>
|
||||
Action ID to clear.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetMouseDisplayPosition"></a>
|
||||
<strong>GetMouseDisplayPosition()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the display position of the cursor in percent. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Cursor display position in percent.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <here>Inventory</here></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -244,7 +253,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <here>Logic</here></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -100,16 +109,24 @@
|
|||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#AddCallback">AddCallback(CallbackPoint, func)</a></td>
|
||||
<td class="name" ><a href="#AddCallback">AddCallback(point, func)</a></td>
|
||||
<td class="summary">Register a function as a callback.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#RemoveCallback">RemoveCallback(CallbackPoint, LevelFunc)</a></td>
|
||||
<td class="name" ><a href="#RemoveCallback">RemoveCallback(point, func)</a></td>
|
||||
<td class="summary">Deregister a function as a callback.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#HandleEvent">HandleEvent(string, EventType, Moveable)</a></td>
|
||||
<td class="summary">Attempt to find an event set and exectute a particular event from it.</td>
|
||||
<td class="name" ><a href="#HandleEvent">HandleEvent(name, type, activator)</a></td>
|
||||
<td class="summary">Attempt to find an event set and execute a particular event from it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EnableEvent">EnableEvent(name, type)</a></td>
|
||||
<td class="summary">Attempt to find an event set and enable specified event in it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisableEvent">DisableEvent(name, type)</a></td>
|
||||
<td class="summary">Attempt to find an event set and disable specified event in it.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#Special_objects">Special objects </a></h2>
|
||||
|
@ -144,7 +161,7 @@
|
|||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "AddCallback"></a>
|
||||
<strong>AddCallback(CallbackPoint, func)</strong>
|
||||
<strong>AddCallback(point, func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Register a function as a callback.
|
||||
|
@ -153,7 +170,7 @@
|
|||
stuff during level start/load/end/save/control phase, but don't want the level
|
||||
designer to add calls to <code>OnStart</code>, <code>OnLoad</code>, etc. in their level script.</p>
|
||||
|
||||
<p>Possible values for CallbackPoint:</p>
|
||||
<p>Possible values for <code>point</code>:</p>
|
||||
<pre class="example"><span class="comment">-- These take functions which accept no arguments
|
||||
</span>PRESTART <span class="comment">-- will be called immediately before OnStart
|
||||
</span>POSTSTART <span class="comment">-- will be called immediately after OnStart
|
||||
|
@ -169,23 +186,23 @@ PRELOAD <span class="comment">-- will be called immediately before OnLoad
|
|||
</span>POSTEND <span class="comment">-- will be called immediately after OnEnd
|
||||
</span>
|
||||
<span class="comment">-- These take functions which accepts a deltaTime argument
|
||||
</span>PRECONTROLPHASE <span class="comment">-- will be called immediately before OnControlPhase
|
||||
</span>POSTCONTROLPHASE <span class="comment">-- will be called immediately after OnControlPhase
|
||||
</span>PRELOOP <span class="comment">-- will be called in the beginning of game loop
|
||||
</span>POSTLOOP <span class="comment">-- will be called at the end of game loop
|
||||
</span></pre>
|
||||
<p>The order in which two functions with the same CallbackPoint are called is undefined.
|
||||
i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRECONTROLPHASE</code>, both will be called before <code>OnControlPhase</code>, but there is no guarantee that <code>MyFunc</code> will be called before <code>MyFunc2</code>, or vice-versa.</p>
|
||||
i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRELOOP</code>, both will be called in the beginning of game loop, but there is no guarantee that <code>MyFunc</code> will be called before <code>MyFunc2</code>, or vice-versa.</p>
|
||||
|
||||
<p>Any returned value will be discarded.</p>
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">CallbackPoint</span>
|
||||
<span class="types"><span class="type">point</span></span>
|
||||
<li><span class="parameter">point</span>
|
||||
<span class="types"><span class="type">CallbackPoint</span></span>
|
||||
When should the callback be called?
|
||||
</li>
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
<span class="types"><span class="type">LevelFunc</span></span>
|
||||
The function to be called (must be in the <a href="../1 modules/Logic.html#LevelFuncs">LevelFuncs</a> hierarchy). Will receive, as an argument, the time in seconds since the last frame.
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -196,13 +213,13 @@ i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRE
|
|||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.MyFunc = <span class="keyword">function</span>(dt) <span class="global">print</span>(dt) <span class="keyword">end</span>
|
||||
TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc)</pre>
|
||||
TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRELOOP, LevelFuncs.MyFunc)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "RemoveCallback"></a>
|
||||
<strong>RemoveCallback(CallbackPoint, LevelFunc)</strong>
|
||||
<strong>RemoveCallback(point, func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Deregister a function as a callback.
|
||||
|
@ -212,13 +229,13 @@ Will have no effect if the function was not registered as a callback
|
|||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">CallbackPoint</span>
|
||||
<span class="types"><span class="type">point</span></span>
|
||||
<li><span class="parameter">point</span>
|
||||
<span class="types"><span class="type">CallbackPoint</span></span>
|
||||
The callback point the function was registered with. See <a href="../1 modules/Logic.html#AddCallback">AddCallback</a>
|
||||
</li>
|
||||
<li><span class="parameter">LevelFunc</span>
|
||||
<span class="types"><span class="type">func</span></span>
|
||||
the function to remove; must be in the LevelFuncs hierarchy.
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">LevelFunc</span></span>
|
||||
The function to remove; must be in the LevelFuncs hierarchy.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -227,32 +244,93 @@ Will have no effect if the function was not registered as a callback
|
|||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc)</pre>
|
||||
<pre class="example">TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRELOOP, LevelFuncs.MyFunc)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "HandleEvent"></a>
|
||||
<strong>HandleEvent(string, EventType, Moveable)</strong>
|
||||
<strong>HandleEvent(name, type, activator)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Attempt to find an event set and exectute a particular event from it.
|
||||
Attempt to find an event set and execute a particular event from it.
|
||||
|
||||
<p>Possible event type values:</p>
|
||||
<pre class="example">ENTER
|
||||
INSIDE
|
||||
LEAVE
|
||||
LOAD
|
||||
SAVE
|
||||
START
|
||||
END
|
||||
LOOP</pre>
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
Name of the event set to find.
|
||||
</li>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><span class="type">EventType</span></span>
|
||||
Event to execute.
|
||||
</li>
|
||||
<li><span class="parameter">activator</span>
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Moveable.html#">Moveable</a></span>
|
||||
Optional activator. Default is the player object.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EnableEvent"></a>
|
||||
<strong>EnableEvent(name, type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Attempt to find an event set and enable specified event in it.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">string</span>
|
||||
<span class="types"><span class="type">name</span></span>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
Name of the event set to find.
|
||||
</li>
|
||||
<li><span class="parameter">EventType</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#pdf-type">type</a></span>
|
||||
Event to execute.
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><span class="type">EventType</span></span>
|
||||
Event to enable.
|
||||
</li>
|
||||
<li><span class="parameter">Moveable</span>
|
||||
<span class="types"><span class="type">activator</span></span>
|
||||
Optional activator. Default is the player object.
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisableEvent"></a>
|
||||
<strong>DisableEvent(name, type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Attempt to find an event set and disable specified event in it.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
Name of the event set to find.
|
||||
</li>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><span class="type">EventType</span></span>
|
||||
Event to disable.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -384,7 +462,7 @@ you can just leave out <code>LevelFuncs.OnStart</code>.</p>
|
|||
<li>The level script itself is run (i.e. any code you put outside the <a href="../1 modules/Logic.html#LevelFuncs">LevelFuncs</a> callbacks is executed).</li>
|
||||
<li>Save data is loaded, if saving from a saved game (will empty <a href="../1 modules/Logic.html#LevelVars">LevelVars</a> and <a href="../1 modules/Logic.html#GameVars">GameVars</a> and repopulate them with what they contained when the game was saved).</li>
|
||||
<li>If loading from a save, <code>OnLoaded</code> will be called. Otherwise, <code>OnStart</code> will be called.</li>
|
||||
<li>The control loop, in which <code>OnControlPhase</code> will be called once per frame, begins.</li>
|
||||
<li>The control loop, in which <code>OnLoop</code> will be called once per frame, begins.</li>
|
||||
</ol>
|
||||
|
||||
</p>
|
||||
|
@ -400,7 +478,7 @@ you can just leave out <code>LevelFuncs.OnStart</code>.</p>
|
|||
<span class="types"><span class="type">function</span></span>
|
||||
Will be called when a saved game is loaded, just <em>after</em> data is loaded
|
||||
</li>
|
||||
<li><span class="parameter">OnControlPhase</span>
|
||||
<li><span class="parameter">OnLoop</span>
|
||||
<span class="types"><span class="type">function(float)</span></span>
|
||||
Will be called during the game's update loop,
|
||||
and provides the delta time (a float representing game time since last call) via its argument.
|
||||
|
@ -444,7 +522,6 @@ end
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <here>Objects</here></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -467,7 +476,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
399
Documentation/doc/1 modules/Sound.html
Normal file
399
Documentation/doc/1 modules/Sound.html
Normal file
|
@ -0,0 +1,399 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>1 Modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <here>Sound</here></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
|
||||
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
|
||||
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
|
||||
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
|
||||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Table <code>Sound</code></h1>
|
||||
<p>Functions for sound management.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#PlayAudioTrack">PlayAudioTrack(name, type)</a></td>
|
||||
<td class="summary">Play an audio track</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetAmbientTrack">SetAmbientTrack(name)</a></td>
|
||||
<td class="summary">Set and play an ambient track</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#StopAudioTracks">StopAudioTracks()</a></td>
|
||||
<td class="summary">Stop any audio tracks currently playing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#StopAudioTrack">StopAudioTrack(type)</a></td>
|
||||
<td class="summary">Stop audio track that is currently playing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetAudioTrackLoudness">GetAudioTrackLoudness(type)</a></td>
|
||||
<td class="summary">Get current loudness level for specified track type</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PlaySound">PlaySound(sound[, position])</a></td>
|
||||
<td class="summary">Play sound effect</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#StopSound">StopSound(sound)</a></td>
|
||||
<td class="summary">Stop sound effect</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsSoundPlaying">IsSoundPlaying(Sound)</a></td>
|
||||
<td class="summary">Check if the sound effect is playing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsAudioTrackPlaying">IsAudioTrackPlaying(Track)</a></td>
|
||||
<td class="summary">Check if the audio track is playing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCurrentSubtitle">GetCurrentSubtitle()</a></td>
|
||||
<td class="summary">Get current subtitle string for a voice track currently playing.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "PlayAudioTrack"></a>
|
||||
<strong>PlayAudioTrack(name, type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Play an audio track
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
of track (without file extension) to play
|
||||
</li>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
|
||||
of the audio track to play
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetAmbientTrack"></a>
|
||||
<strong>SetAmbientTrack(name)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set and play an ambient track
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
of track (without file extension) to play
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "StopAudioTracks"></a>
|
||||
<strong>StopAudioTracks()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Stop any audio tracks currently playing
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "StopAudioTrack"></a>
|
||||
<strong>StopAudioTrack(type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Stop audio track that is currently playing
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
|
||||
of the audio track
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetAudioTrackLoudness"></a>
|
||||
<strong>GetAudioTrackLoudness(type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current loudness level for specified track type
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
|
||||
of the audio track
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
current loudness of a specified audio track
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PlaySound"></a>
|
||||
<strong>PlaySound(sound[, position])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Play sound effect
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">sound</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
</li>
|
||||
<li><span class="parameter">position</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
The 3D position of the sound, i.e. where the sound "comes from". If not given, the sound will not be positional.
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "StopSound"></a>
|
||||
<strong>StopSound(sound)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Stop sound effect
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">sound</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsSoundPlaying"></a>
|
||||
<strong>IsSoundPlaying(Sound)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if the sound effect is playing
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">Sound</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID to check. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsAudioTrackPlaying"></a>
|
||||
<strong>IsAudioTrackPlaying(Track)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if the audio track is playing
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">Track</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
filename to check. Should be without extension and without full directory path.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCurrentSubtitle"></a>
|
||||
<strong>GetCurrentSubtitle()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current subtitle string for a voice track currently playing.
|
||||
Subtitle file must be in .srt format, have same filename as voice track, and be placed in same directory as voice track.
|
||||
Returns nil if no voice track is playing or no subtitle present.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
current subtitle string
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <here>Strings</here></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -91,7 +100,7 @@
|
|||
<div id="content">
|
||||
|
||||
<h1>Table <code>Strings</code></h1>
|
||||
<p>On-screen strings.</p>
|
||||
<p>Display strings.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
@ -207,7 +216,6 @@ with a call to <a href="../1 modules/Strings.html#ShowString">ShowString</a>, or
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
490
Documentation/doc/1 modules/Util.html
Normal file
490
Documentation/doc/1 modules/Util.html
Normal file
|
@ -0,0 +1,490 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>1 Modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <here>Util</here></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
|
||||
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
|
||||
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
|
||||
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
|
||||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Table <code>Util</code></h1>
|
||||
<p>Utility functions for various calculations.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#HasLineOfSight">HasLineOfSight(roomID, posA, posB)</a></td>
|
||||
<td class="summary">Determine if there is a clear line of sight between two positions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#CalculateDistance">CalculateDistance(posA, posB)</a></td>
|
||||
<td class="summary">Calculate the distance between two positions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#CalculateHorizontalDistance">CalculateHorizontalDistance(posA, posB)</a></td>
|
||||
<td class="summary">Calculate the horizontal distance between two positions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetDisplayPosition">GetDisplayPosition(worldPos)</a></td>
|
||||
<td class="summary">Get the projected display space position of a 3D world position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PercentToScreen">PercentToScreen(x, y)</a></td>
|
||||
<td class="summary">Translate a pair display position coordinates to pixel coordinates.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#ScreenToPercent">ScreenToPercent(x, y)</a></td>
|
||||
<td class="summary">Translate a pair of pixel coordinates to display position coordinates.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PickMoveableByDisplayPosition">PickMoveableByDisplayPosition(Display)</a></td>
|
||||
<td class="summary">Pick a moveable by the given display position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PickStaticByDisplayPosition">PickStaticByDisplayPosition(Display)</a></td>
|
||||
<td class="summary">Pick a static mesh by the given display position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PrintLog">PrintLog(message, logLevel[, allowSpam])</a></td>
|
||||
<td class="summary">Write messages within the Log file</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "HasLineOfSight"></a>
|
||||
<strong>HasLineOfSight(roomID, posA, posB)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Determine if there is a clear line of sight between two positions.
|
||||
NOTE: Limited to room geometry. Objects are ignored.()
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">roomID</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Room ID of the first position's room.
|
||||
</li>
|
||||
<li><span class="parameter">posA</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
First position.
|
||||
</li>
|
||||
<li><span class="parameter">posB</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Second position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
<strong>true</strong> if there is a line of sight, <strong>false</strong> if not.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example"><span class="keyword">local</span> flamePlinthPos = flamePlinth:GetPosition() + Vec3(<span class="number">0</span>, flamePlinthHeight, <span class="number">0</span>);
|
||||
<span class="global">print</span>(Misc.HasLineOfSight(enemyHead:GetRoomNumber(), enemyHead:GetPosition(), flamePlinthPos))</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "CalculateDistance"></a>
|
||||
<strong>CalculateDistance(posA, posB)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Calculate the distance between two positions.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">posA</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
First position.
|
||||
</li>
|
||||
<li><span class="parameter">posB</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Second position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Distance between two positions.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "CalculateHorizontalDistance"></a>
|
||||
<strong>CalculateHorizontalDistance(posA, posB)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Calculate the horizontal distance between two positions.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">posA</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
First position.
|
||||
</li>
|
||||
<li><span class="parameter">posB</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Second position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Horizontal distance between the two positions.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetDisplayPosition"></a>
|
||||
<strong>GetDisplayPosition(worldPos)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the projected display space position of a 3D world position. Returns nil if the world position is behind the camera view.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">worldPos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
3D world position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Projected display space position in percent.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">Example: Display a <span class="global">string</span> at the player<span class="string">'s position.
|
||||
local string = DisplayString('</span>Example', <span class="number">0</span>, <span class="number">0</span>, Color(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>), <span class="keyword">false</span>)
|
||||
<span class="keyword">local</span> displayPos = GetDisplayPosition(Lara:GetPosition())
|
||||
<span class="global">string</span>:SetPosition(PercentToScreen(displayPos.x, displayPos.y))</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PercentToScreen"></a>
|
||||
<strong>PercentToScreen(x, y)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Translate a pair display position coordinates to pixel coordinates.
|
||||
To be used with <a href="../2 classes/Strings.DisplayString.html#DisplayString:SetPosition">Strings.DisplayString:SetPosition</a> and <a href="../2 classes/Strings.DisplayString.html#">Strings.DisplayString</a>.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">x</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
X component of the display position.
|
||||
</li>
|
||||
<li><span class="parameter">y</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Y component of the display position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
<li>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
x X coordinate in pixels.</li>
|
||||
<li>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
y Y coordinate in pixels.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example"><span class="keyword">local</span> halfwayX, halfwayY = PercentToScreen(<span class="number">50</span>, <span class="number">50</span>)
|
||||
<span class="keyword">local</span> baddy
|
||||
<span class="keyword">local</span> spawnLocationNullmesh = GetMoveableByName(<span class="string">"position_behind_left_pillar"</span>)
|
||||
<span class="keyword">local</span> str1 = DisplayString(<span class="string">"You spawned an enemy!"</span>, halfwayX, halfwayY, Color(<span class="number">255</span>, <span class="number">100</span>, <span class="number">100</span>), <span class="keyword">false</span>, { DisplayStringOption.SHADOW, DisplayStringOption.CENTER })
|
||||
|
||||
LevelFuncs.triggerOne = <span class="keyword">function</span>(obj)
|
||||
ShowString(str1, <span class="number">4</span>)
|
||||
<span class="keyword">end</span></pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "ScreenToPercent"></a>
|
||||
<strong>ScreenToPercent(x, y)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Translate a pair of pixel coordinates to display position coordinates.
|
||||
To be used with <a href="../2 classes/Strings.DisplayString.html#DisplayString:GetPosition">Strings.DisplayString:GetPosition</a>.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">x</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
X pixel coordinate to translate to display position.
|
||||
</li>
|
||||
<li><span class="parameter">y</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Y pixel coordinate to translate to display position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
<li>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
x X component of display position.</li>
|
||||
<li>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
y Y component of display position.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PickMoveableByDisplayPosition"></a>
|
||||
<strong>PickMoveableByDisplayPosition(Display)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Pick a moveable by the given display position.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">Display</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
space position in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Moveable.html#">Moveable</a></span>
|
||||
Picked moveable (nil if no moveable was found under the cursor).
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PickStaticByDisplayPosition"></a>
|
||||
<strong>PickStaticByDisplayPosition(Display)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Pick a static mesh by the given display position.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">Display</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
space position in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Static.html#">Static</a></span>
|
||||
Picked static mesh (nil if no static mesh was found under the cursor).
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PrintLog"></a>
|
||||
<strong>PrintLog(message, logLevel[, allowSpam])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Write messages within the Log file
|
||||
|
||||
<p>For native Lua handling of errors, see the official Lua website:</p>
|
||||
|
||||
<p><a href="https://www.lua.org/pil/8.3.html">Error management</a></p>
|
||||
|
||||
<p><a href="https://www.lua.org/manual/5.4/manual.html#pdf-debug.traceback">debug.traceback</a></p>
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">message</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
to be displayed within the Log
|
||||
</li>
|
||||
<li><span class="parameter">logLevel</span>
|
||||
<span class="types"><span class="type">Misc.LogLevel</span></span>
|
||||
log level to be displayed
|
||||
</li>
|
||||
<li><span class="parameter">allowSpam</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
true allows spamming of the message
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">PrintLog(<span class="string">'test info log'</span>, LogLevel.INFO)
|
||||
PrintLog(<span class="string">'test warning log'</span>, LogLevel.WARNING)
|
||||
PrintLog(<span class="string">'test error log'</span>, LogLevel.ERROR)
|
||||
<span class="comment">-- spammed message
|
||||
</span>PrintLog(<span class="string">'test spam log'</span>, LogLevel.INFO, <span class="keyword">true</span>) </pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
549
Documentation/doc/1 modules/View.html
Normal file
549
Documentation/doc/1 modules/View.html
Normal file
|
@ -0,0 +1,549 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>1 Modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <here>View</here></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
|
||||
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
|
||||
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
|
||||
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
|
||||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Table <code>View</code></h1>
|
||||
<p>Functions to manage camera and game view.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#FadeIn">FadeIn(speed)</a></td>
|
||||
<td class="summary">Do a full-screen fade-in from black.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#FadeOut">FadeOut(speed)</a></td>
|
||||
<td class="summary">Do a full-screen fade-to-black.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetCineBars">SetCineBars(height, speed)</a></td>
|
||||
<td class="summary">Move black cinematic bars in from the top and bottom of the game window.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetFOV">SetFOV(angle)</a></td>
|
||||
<td class="summary">Set field of view.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFOV">GetFOV()</a></td>
|
||||
<td class="summary">Get field of view.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCameraType">GetCameraType()</a></td>
|
||||
<td class="summary">Shows the mode of the game camera.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCameraRoom">GetCameraRoom()</a></td>
|
||||
<td class="summary">Gets current room where camera is positioned.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetPostProcessMode">SetPostProcessMode(effect)</a></td>
|
||||
<td class="summary">Sets the post-process effect mode, like negative or monochrome.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetPostProcessStrength">SetPostProcessStrength(strength)</a></td>
|
||||
<td class="summary">Sets the post-process effect strength.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetPostProcessTint">SetPostProcessTint(tint)</a></td>
|
||||
<td class="summary">Sets the post-process tint.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCameraPosition">GetCameraPosition()</a></td>
|
||||
<td class="summary">Gets current camera position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCameraTarget">GetCameraTarget()</a></td>
|
||||
<td class="summary">Gets current camera target.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PlayFlyBy">PlayFlyBy(flyby)</a></td>
|
||||
<td class="summary">Enable FlyBy with specific ID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#ResetObjCamera">ResetObjCamera()</a></td>
|
||||
<td class="summary">Reset object camera back to Lara and deactivate object camera.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#FlashScreen">FlashScreen(color, speed)</a></td>
|
||||
<td class="summary">Flash screen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetAspectRatio">GetAspectRatio()</a></td>
|
||||
<td class="summary">Get the display resolution's aspect ratio.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "FadeIn"></a>
|
||||
<strong>FadeIn(speed)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Do a full-screen fade-in from black.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "FadeOut"></a>
|
||||
<strong>FadeOut(speed)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Do a full-screen fade-to-black. The screen will remain black until a call to FadeIn.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetCineBars"></a>
|
||||
<strong>SetCineBars(height, speed)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Move black cinematic bars in from the top and bottom of the game window.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">height</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
<strong>(default 30)</strong> Percentage of the screen to be covered
|
||||
</li>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
<strong>(default 30)</strong> Coverage percent per second
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetFOV"></a>
|
||||
<strong>SetFOV(angle)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set field of view.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">angle</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
in degrees (clamped to [10, 170])
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFOV"></a>
|
||||
<strong>GetFOV()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get field of view.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
current FOV angle in degrees
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCameraType"></a>
|
||||
<strong>GetCameraType()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Shows the mode of the game camera.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/View.CameraType.html#">CameraType</a></span>
|
||||
value used by the Main Camera.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.OnLoop = <span class="keyword">function</span>()
|
||||
<span class="keyword">if</span> (View.GetCameraType() == CameraType.Combat) <span class="keyword">then</span>
|
||||
<span class="comment">--Do your Actions here.
|
||||
</span> <span class="keyword">end</span>
|
||||
<span class="keyword">end</span></pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCameraRoom"></a>
|
||||
<strong>GetCameraRoom()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Gets current room where camera is positioned.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Room.html#">Room</a></span>
|
||||
current room of the camera
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetPostProcessMode"></a>
|
||||
<strong>SetPostProcessMode(effect)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Sets the post-process effect mode, like negative or monochrome.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">effect</span>
|
||||
<span class="types"><a class="type" href="../4 enums/View.PostProcessMode.html#">PostProcessMode</a></span>
|
||||
type to set.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetPostProcessStrength"></a>
|
||||
<strong>SetPostProcessStrength(strength)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Sets the post-process effect strength.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">strength</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). How strong the effect is.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetPostProcessTint"></a>
|
||||
<strong>SetPostProcessTint(tint)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Sets the post-process tint.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">tint</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
value to use.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCameraPosition"></a>
|
||||
<strong>GetCameraPosition()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Gets current camera position.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
current camera position
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCameraTarget"></a>
|
||||
<strong>GetCameraTarget()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Gets current camera target.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
current camera target
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PlayFlyBy"></a>
|
||||
<strong>PlayFlyBy(flyby)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Enable FlyBy with specific ID
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">flyby</span>
|
||||
<span class="types"><span class="type">short</span></span>
|
||||
(ID of flyby)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "ResetObjCamera"></a>
|
||||
<strong>ResetObjCamera()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Reset object camera back to Lara and deactivate object camera.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "FlashScreen"></a>
|
||||
<strong>FlashScreen(color, speed)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Flash screen.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">color</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
(default Color(255, 255, 255))
|
||||
</li>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). Speed in "amount" per second. Value of 1 will make flash take one second. Clamped to [0.005, 1.0].
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetAspectRatio"></a>
|
||||
<strong>GetAspectRatio()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the display resolution's aspect ratio.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Display resolution's aspect ratio.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
529
Documentation/doc/2 classes/DisplaySprite.html
Normal file
529
Documentation/doc/2 classes/DisplaySprite.html
Normal file
|
@ -0,0 +1,529 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>1 Modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <here>DisplaySprite</here></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
|
||||
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
|
||||
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
|
||||
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
|
||||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/DisplaySprite.AlignMode.html">DisplaySprite.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/DisplaySprite.ScaleMode.html">DisplaySprite.ScaleMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Class <code>DisplaySprite</code></h1>
|
||||
<p>Represents a screen-space display sprite.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite">DisplaySprite(ID, int, pos, rot, scale[, color])</a></td>
|
||||
<td class="summary">Create a DisplaySprite object.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetObjectID">DisplaySprite:GetObjectID()</a></td>
|
||||
<td class="summary">Get the object ID of the sprite sequence object used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetSpriteID">DisplaySprite:GetSpriteID()</a></td>
|
||||
<td class="summary">Get the sprite ID in the sprite sequence object used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetPosition">DisplaySprite:GetPosition()</a></td>
|
||||
<td class="summary">Get the display position of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetRotation">DisplaySprite:GetRotation()</a></td>
|
||||
<td class="summary">Get the rotation of the display sprite in degrees.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetScale">DisplaySprite:GetScale()</a></td>
|
||||
<td class="summary">Get the horizontal and vertical scale of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetColor">DisplaySprite:GetColor()</a></td>
|
||||
<td class="summary">Get the color of the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetObjectID">DisplaySprite:SetObjectID(New)</a></td>
|
||||
<td class="summary">Set the sprite sequence object ID used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetSpriteID">DisplaySprite:SetSpriteID(New)</a></td>
|
||||
<td class="summary">Set the sprite ID in the sprite sequence object used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetPosition">DisplaySprite:SetPosition(New)</a></td>
|
||||
<td class="summary">Set the display position of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetRotation">DisplaySprite:SetRotation(New)</a></td>
|
||||
<td class="summary">Set the rotation of the display sprite in degrees.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetScale">DisplaySprite:SetScale(New)</a></td>
|
||||
<td class="summary">Set the horizontal and vertical scale of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetColor">DisplaySprite:SetColor(New)</a></td>
|
||||
<td class="summary">Set the color of the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:Draw">DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])</a></td>
|
||||
<td class="summary">Draw the display sprite in display space for the current frame.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "DisplaySprite"></a>
|
||||
<strong>DisplaySprite(ID, int, pos, rot, scale[, color])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Create a DisplaySprite object.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">ID</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
of the sprite sequence object.
|
||||
</li>
|
||||
<li><span class="parameter">int</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
spriteID ID of the sprite in the sequence.
|
||||
</li>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Display position in percent.
|
||||
</li>
|
||||
<li><span class="parameter">rot</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotation in degrees.
|
||||
</li>
|
||||
<li><span class="parameter">scale</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Horizontal and vertical scale in percent. Scaling is interpreted by the DisplaySpriteEnum.ScaleMode passed to the Draw() function call.
|
||||
</li>
|
||||
<li><span class="parameter">color</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color. <strong>Default: Color(255, 255, 255, 255)</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/DisplaySprite.html#">DisplaySprite</a></span>
|
||||
A new DisplaySprite object.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetObjectID"></a>
|
||||
<strong>DisplaySprite:GetObjectID()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the object ID of the sprite sequence object used by the display sprite. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
Sprite sequence object ID.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetSpriteID"></a>
|
||||
<strong>DisplaySprite:GetSpriteID()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the sprite ID in the sprite sequence object used by the display sprite. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Sprite ID in the sprite sequence object.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetPosition"></a>
|
||||
<strong>DisplaySprite:GetPosition()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the display position of the display sprite in percent. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Display position in percent.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetRotation"></a>
|
||||
<strong>DisplaySprite:GetRotation()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the rotation of the display sprite in degrees. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotation in degrees.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetScale"></a>
|
||||
<strong>DisplaySprite:GetScale()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the horizontal and vertical scale of the display sprite in percent. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Horizontal and vertical scale in percent.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetColor"></a>
|
||||
<strong>DisplaySprite:GetColor()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the color of the display sprite. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetObjectID"></a>
|
||||
<strong>DisplaySprite:SetObjectID(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the sprite sequence object ID used by the display sprite. (Objects.ObjID)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
sprite sequence object ID.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetSpriteID"></a>
|
||||
<strong>DisplaySprite:SetSpriteID(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the sprite ID in the sprite sequence object used by the display sprite. (int)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
sprite ID in the sprite sequence object.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetPosition"></a>
|
||||
<strong>DisplaySprite:SetPosition(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the display position of the display sprite in percent. (Vec2)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
display position in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetRotation"></a>
|
||||
<strong>DisplaySprite:SetRotation(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the rotation of the display sprite in degrees. (float)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
rotation in degrees.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetScale"></a>
|
||||
<strong>DisplaySprite:SetScale(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the horizontal and vertical scale of the display sprite in percent. (Vec2)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
horizontal and vertical scale in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetColor"></a>
|
||||
<strong>DisplaySprite:SetColor(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the color of the display sprite. (Color)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
color.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:Draw"></a>
|
||||
<strong>DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Draw the display sprite in display space for the current frame.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">priority</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
Draw priority. Can be thought of as a layer, with higher values having precedence. <strong>Default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">alignMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/DisplaySprite.AlignMode.html#">AlignMode</a></span>
|
||||
Align mode interpreting an offset from the sprite's position. <strong>Default: DisplaySprite.AlignMode.CENTER</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">scaleMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/DisplaySprite.ScaleMode.html#">ScaleMode</a></span>
|
||||
Scale mode interpreting the display sprite's horizontal and vertical scale. <strong>Default: DisplaySprite.ScaleMode.FIT</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">blendMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
|
||||
Blend mode. <strong>Default: Effects.BlendID.ALPHABLEND</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-09 18:25:22 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <here>Flow.Animations</here></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -108,7 +117,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <here>Flow.Fog</here></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -235,7 +244,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <here>Flow.InventoryItem</here></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -193,7 +202,6 @@ EXAMINE
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -158,8 +167,8 @@
|
|||
<td class="summary">(<a href="../2 classes/Flow.Mirror.html#">Flow.Mirror</a>) Location and size of the level's mirror, if present.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#unlimitedAir">unlimitedAir</a></td>
|
||||
<td class="summary">(bool) Enable unlimited oxygen supply when in water.</td>
|
||||
<td class="name" ><a href="#resetHub">resetHub</a></td>
|
||||
<td class="summary">(bool) Reset hub data.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#objects">objects</a></td>
|
||||
|
@ -441,13 +450,12 @@ Invisible
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "unlimitedAir"></a>
|
||||
<strong>unlimitedAir</strong>
|
||||
<a name = "resetHub"></a>
|
||||
<strong>resetHub</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(bool) Enable unlimited oxygen supply when in water. </p>
|
||||
|
||||
<p> <strong>(not yet implemented)</strong>
|
||||
(bool) Reset hub data.
|
||||
Resets the state for all previous levels, including items, flipmaps and statistics.
|
||||
|
||||
|
||||
|
||||
|
@ -538,7 +546,6 @@ Must be at least 4.</p>
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -108,7 +117,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -148,7 +157,6 @@ has an unrecoverable error, the game will close.
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -206,7 +215,6 @@ Less is more. City of The Dead, for example, uses a speed value of 16.
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -409,7 +418,6 @@ aiObj:SetObjectID(TEN.Objects.ObjID.AI_PATROL1)</pre>
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<title>TombEngine 1.3 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -34,14 +34,18 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Misc.html">Misc</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
|
@ -71,14 +75,19 @@
|
|||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
|
||||
<li> <a href="../4 enums/Misc.LogLevel.html">Misc.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/Misc.SoundTrackType.html">Misc.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
|
@ -116,7 +125,7 @@
|
|||
<td class="summary">Set the camera's name (its unique string identifier)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#CameraObject:GetRoom">CameraObject:GetRoom()</a></td>
|
||||
<td class="name" ><a href="#Camera:GetRoom">Camera:GetRoom()</a></td>
|
||||
<td class="summary">Get the current room of the camera</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -228,8 +237,8 @@
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "CameraObject:GetRoom"></a>
|
||||
<strong>CameraObject:GetRoom()</strong>
|
||||
<a name = "Camera:GetRoom"></a>
|
||||
<strong>Camera:GetRoom()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the current room of the camera
|
||||
|
@ -322,7 +331,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-09-30 13:48:57 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue