mirror of
https://github.com/araxiaonline/ets-module-collection.git
synced 2026-06-13 02:52:20 -04:00
Added AIO
This commit is contained in:
1287
AIO_Server/AIO.lua
Normal file
1287
AIO_Server/AIO.lua
Normal file
File diff suppressed because it is too large
Load Diff
38
AIO_Server/Dep_LuaSrcDiet/COPYRIGHT
Normal file
38
AIO_Server/Dep_LuaSrcDiet/COPYRIGHT
Normal file
@@ -0,0 +1,38 @@
|
||||
LuaSrcDiet License
|
||||
------------------
|
||||
|
||||
LuaSrcDiet is licensed under the terms of the MIT license reproduced
|
||||
below. This means that LuaSrcDiet is free software and can be used for
|
||||
both academic and commercial purposes at absolutely no cost.
|
||||
|
||||
Parts of LuaSrcDiet is based on Lua 5 code. See COPYRIGHT_Lua51
|
||||
(Lua 5.1.3) for Lua 5 license information.
|
||||
|
||||
For details and rationale, see http://www.lua.org/license.html .
|
||||
|
||||
===============================================================================
|
||||
|
||||
Copyright (C) 2005-2008 Kein-Hong Man <khman@users.sf.net>
|
||||
Lua 5.1.3 Copyright (C) 1994-2008 Lua.org, PUC-Rio.
|
||||
|
||||
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.
|
||||
|
||||
===============================================================================
|
||||
|
||||
(end of COPYRIGHT)
|
||||
34
AIO_Server/Dep_LuaSrcDiet/COPYRIGHT_Lua51
Normal file
34
AIO_Server/Dep_LuaSrcDiet/COPYRIGHT_Lua51
Normal file
@@ -0,0 +1,34 @@
|
||||
Lua License
|
||||
-----------
|
||||
|
||||
Lua is licensed under the terms of the MIT license reproduced below.
|
||||
This means that Lua is free software and can be used for both academic
|
||||
and commercial purposes at absolutely no cost.
|
||||
|
||||
For details and rationale, see http://www.lua.org/license.html .
|
||||
|
||||
===============================================================================
|
||||
|
||||
Copyright (C) 1994-2008 Lua.org, PUC-Rio.
|
||||
|
||||
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.
|
||||
|
||||
===============================================================================
|
||||
|
||||
(end of COPYRIGHT)
|
||||
809
AIO_Server/Dep_LuaSrcDiet/LuaSrcDiet.lua
Normal file
809
AIO_Server/Dep_LuaSrcDiet/LuaSrcDiet.lua
Normal file
@@ -0,0 +1,809 @@
|
||||
#!/usr/bin/env lua
|
||||
--[[--------------------------------------------------------------------
|
||||
|
||||
LuaSrcDiet
|
||||
Compresses Lua source code by removing unnecessary characters.
|
||||
For Lua 5.1.x source code.
|
||||
|
||||
Copyright (c) 2008 Kein-Hong Man <khman@users.sf.net>
|
||||
The COPYRIGHT file describes the conditions
|
||||
under which this software may be distributed.
|
||||
|
||||
See the ChangeLog for more information.
|
||||
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- NOTES:
|
||||
-- * Remember to update version and date information below (MSG_TITLE)
|
||||
-- * TODO: to implement pcall() to properly handle lexer etc. errors
|
||||
-- * TODO: verify token stream or double-check binary chunk?
|
||||
-- * TODO: need some automatic testing for a semblance of sanity
|
||||
-- * TODO: the plugin module is highly experimental and unstable
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
-- standard libraries, functions
|
||||
local string = string
|
||||
local math = math
|
||||
local table = table
|
||||
local require = require
|
||||
local print = print
|
||||
local sub = string.sub
|
||||
local gmatch = string.gmatch
|
||||
|
||||
-- support modules
|
||||
local llex = require "llex"
|
||||
local lparser = require "lparser"
|
||||
local optlex = require "optlex"
|
||||
local optparser = require "optparser"
|
||||
local plugin
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- messages and textual data
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
local MSG_TITLE = [[
|
||||
LuaSrcDiet: Puts your Lua 5.1 source code on a diet
|
||||
Version 0.11.2 (20080608) Copyright (c) 2005-2008 Kein-Hong Man
|
||||
The COPYRIGHT file describes the conditions under which this
|
||||
software may be distributed.
|
||||
]]
|
||||
|
||||
local MSG_USAGE = [[
|
||||
usage: LuaSrcDiet [options] [filenames]
|
||||
|
||||
example:
|
||||
>LuaSrcDiet myscript.lua -o myscript_.lua
|
||||
|
||||
options:
|
||||
-v, --version prints version information
|
||||
-h, --help prints usage information
|
||||
-o <file> specify file name to write output
|
||||
-s <suffix> suffix for output files (default '_')
|
||||
--keep <msg> keep block comment with <msg> inside
|
||||
--plugin <module> run <module> in plugin/ directory
|
||||
- stop handling arguments
|
||||
|
||||
(optimization levels)
|
||||
--none all optimizations off (normalizes EOLs only)
|
||||
--basic lexer-based optimizations only
|
||||
--maximum maximize reduction of source
|
||||
|
||||
(informational)
|
||||
--quiet process files quietly
|
||||
--read-only read file and print token stats only
|
||||
--dump-lexer dump raw tokens from lexer to stdout
|
||||
--dump-parser dump variable tracking tables from parser
|
||||
--details extra info (strings, numbers, locals)
|
||||
|
||||
features (to disable, insert 'no' prefix like --noopt-comments):
|
||||
%s
|
||||
default settings:
|
||||
%s]]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- optimization options, for ease of switching on and off
|
||||
-- * positive to enable optimization, negative (no) to disable
|
||||
-- * these options should follow --opt-* and --noopt-* style for now
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local OPTION = [[
|
||||
--opt-comments,'remove comments and block comments'
|
||||
--opt-whitespace,'remove whitespace excluding EOLs'
|
||||
--opt-emptylines,'remove empty lines'
|
||||
--opt-eols,'all above, plus remove unnecessary EOLs'
|
||||
--opt-strings,'optimize strings and long strings'
|
||||
--opt-numbers,'optimize numbers'
|
||||
--opt-locals,'optimize local variable names'
|
||||
--opt-entropy,'tries to reduce symbol entropy of locals'
|
||||
]]
|
||||
|
||||
-- preset configuration
|
||||
local DEFAULT_CONFIG = [[
|
||||
--opt-comments --opt-whitespace --opt-emptylines
|
||||
--opt-numbers --opt-locals
|
||||
]]
|
||||
-- override configurations: MUST explicitly enable/disable everything
|
||||
local BASIC_CONFIG = [[
|
||||
--opt-comments --opt-whitespace --opt-emptylines
|
||||
--noopt-eols --noopt-strings --noopt-numbers
|
||||
--noopt-locals
|
||||
]]
|
||||
local MAXIMUM_CONFIG = [[
|
||||
--opt-comments --opt-whitespace --opt-emptylines
|
||||
--opt-eols --opt-strings --opt-numbers
|
||||
--opt-locals --opt-entropy
|
||||
]]
|
||||
local NONE_CONFIG = [[
|
||||
--noopt-comments --noopt-whitespace --noopt-emptylines
|
||||
--noopt-eols --noopt-strings --noopt-numbers
|
||||
--noopt-locals
|
||||
]]
|
||||
|
||||
local DEFAULT_SUFFIX = "_" -- default suffix for file renaming
|
||||
local PLUGIN_SUFFIX = "plugin/" -- relative location of plugins
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- startup and initialize option list handling
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
-- simple error message handler; change to error if traceback wanted
|
||||
local function die(msg)
|
||||
print("LuaSrcDiet: "..msg); os.exit()
|
||||
end
|
||||
--die = error--DEBUG
|
||||
|
||||
--if not string.match(_VERSION, "5.1", 1, 1) then -- sanity check
|
||||
-- die("requires Lua 5.1 to run")
|
||||
--end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- prepares text for list of optimizations, prepare lookup table
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local MSG_OPTIONS = ""
|
||||
do
|
||||
local WIDTH = 24
|
||||
local o = {}
|
||||
for op, desc in gmatch(OPTION, "%s*([^,]+),'([^']+)'") do
|
||||
local msg = " "..op
|
||||
msg = msg..string.rep(" ", WIDTH - #msg)..desc.."\n"
|
||||
MSG_OPTIONS = MSG_OPTIONS..msg
|
||||
o[op] = true
|
||||
o["--no"..sub(op, 3)] = true
|
||||
end
|
||||
OPTION = o -- replace OPTION with lookup table
|
||||
end
|
||||
|
||||
MSG_USAGE = string.format(MSG_USAGE, MSG_OPTIONS, DEFAULT_CONFIG)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- global variable initialization, option set handling
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local suffix = DEFAULT_SUFFIX -- file suffix
|
||||
local option = {} -- program options
|
||||
local stat_c, stat_l -- statistics tables
|
||||
|
||||
-- function to set option lookup table based on a text list of options
|
||||
-- note: additional forced settings for --opt-eols is done in optlex.lua
|
||||
local function set_options(CONFIG)
|
||||
for op in gmatch(CONFIG, "(%-%-%S+)") do
|
||||
if sub(op, 3, 4) == "no" and -- handle negative options
|
||||
OPTION["--"..sub(op, 5)] then
|
||||
option[sub(op, 5)] = false
|
||||
else
|
||||
option[sub(op, 3)] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- support functions
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
-- list of token types, parser-significant types are up to TTYPE_GRAMMAR
|
||||
-- while the rest are not used by parsers; arranged for stats display
|
||||
local TTYPES = {
|
||||
"TK_KEYWORD", "TK_NAME", "TK_NUMBER", -- grammar
|
||||
"TK_STRING", "TK_LSTRING", "TK_OP",
|
||||
"TK_EOS",
|
||||
"TK_COMMENT", "TK_LCOMMENT", -- non-grammar
|
||||
"TK_EOL", "TK_SPACE",
|
||||
}
|
||||
local TTYPE_GRAMMAR = 7
|
||||
|
||||
local EOLTYPES = { -- EOL names for token dump
|
||||
["\n"] = "LF", ["\r"] = "CR",
|
||||
["\n\r"] = "LFCR", ["\r\n"] = "CRLF",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- read source code from file
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function load_file(fname)
|
||||
local INF = io.open(fname, "rb")
|
||||
if not INF then die("cannot open \""..fname.."\" for reading") end
|
||||
local dat = INF:read("*a")
|
||||
if not dat then die("cannot read from \""..fname.."\"") end
|
||||
INF:close()
|
||||
print("loadf ", type(dat))
|
||||
return dat
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- save source code to file
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function save_file(fname, dat)
|
||||
local OUTF = io.open(fname, "wb")
|
||||
if not OUTF then die("cannot open \""..fname.."\" for writing") end
|
||||
local status = OUTF:write(dat)
|
||||
if not status then die("cannot write to \""..fname.."\"") end
|
||||
OUTF:close()
|
||||
print("loadf ", type(dat))
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- functions to deal with statistics
|
||||
------------------------------------------------------------------------
|
||||
|
||||
-- initialize statistics table
|
||||
local function stat_init()
|
||||
stat_c, stat_l = {}, {}
|
||||
for i = 1, #TTYPES do
|
||||
local ttype = TTYPES[i]
|
||||
stat_c[ttype], stat_l[ttype] = 0, 0
|
||||
end
|
||||
end
|
||||
|
||||
-- add a token to statistics table
|
||||
local function stat_add(tok, seminfo)
|
||||
stat_c[tok] = stat_c[tok] + 1
|
||||
stat_l[tok] = stat_l[tok] + #seminfo
|
||||
end
|
||||
|
||||
-- do totals for statistics table, return average table
|
||||
local function stat_calc()
|
||||
local function avg(c, l) -- safe average function
|
||||
if c == 0 then return 0 end
|
||||
return l / c
|
||||
end
|
||||
local stat_a = {}
|
||||
local c, l = 0, 0
|
||||
for i = 1, TTYPE_GRAMMAR do -- total grammar tokens
|
||||
local ttype = TTYPES[i]
|
||||
c = c + stat_c[ttype]; l = l + stat_l[ttype]
|
||||
end
|
||||
stat_c.TOTAL_TOK, stat_l.TOTAL_TOK = c, l
|
||||
stat_a.TOTAL_TOK = avg(c, l)
|
||||
c, l = 0, 0
|
||||
for i = 1, #TTYPES do -- total all tokens
|
||||
local ttype = TTYPES[i]
|
||||
c = c + stat_c[ttype]; l = l + stat_l[ttype]
|
||||
stat_a[ttype] = avg(stat_c[ttype], stat_l[ttype])
|
||||
end
|
||||
stat_c.TOTAL_ALL, stat_l.TOTAL_ALL = c, l
|
||||
stat_a.TOTAL_ALL = avg(c, l)
|
||||
return stat_a
|
||||
end
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- main tasks
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- a simple token dumper, minimal translation of seminfo data
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function dump_tokens(srcfl)
|
||||
--------------------------------------------------------------------
|
||||
-- load file and process source input into tokens
|
||||
--------------------------------------------------------------------
|
||||
local z = load_file(srcfl)
|
||||
llex.init(z)
|
||||
llex.llex()
|
||||
local toklist, seminfolist = llex.tok, llex.seminfo
|
||||
--------------------------------------------------------------------
|
||||
-- display output
|
||||
--------------------------------------------------------------------
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
if tok == "TK_OP" and string.byte(seminfo) < 32 then
|
||||
seminfo = "(".. string.byte(seminfo)..")"
|
||||
elseif tok == "TK_EOL" then
|
||||
seminfo = EOLTYPES[seminfo]
|
||||
else
|
||||
seminfo = "'"..seminfo.."'"
|
||||
end
|
||||
print(tok.." "..seminfo)
|
||||
end--for
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- parser dump; dump globalinfo and localinfo tables
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function dump_parser(srcfl)
|
||||
local print = print
|
||||
--------------------------------------------------------------------
|
||||
-- load file and process source input into tokens
|
||||
--------------------------------------------------------------------
|
||||
local z = load_file(srcfl)
|
||||
llex.init(z)
|
||||
llex.llex()
|
||||
local toklist, seminfolist, toklnlist
|
||||
= llex.tok, llex.seminfo, llex.tokln
|
||||
--------------------------------------------------------------------
|
||||
-- do parser optimization here
|
||||
--------------------------------------------------------------------
|
||||
lparser.init(toklist, seminfolist, toklnlist)
|
||||
local globalinfo, localinfo = lparser.parser()
|
||||
--------------------------------------------------------------------
|
||||
-- display output
|
||||
--------------------------------------------------------------------
|
||||
local hl = string.rep("-", 72)
|
||||
print("*** Local/Global Variable Tracker Tables ***")
|
||||
print(hl.."\n GLOBALS\n"..hl)
|
||||
-- global tables have a list of xref numbers only
|
||||
for i = 1, #globalinfo do
|
||||
local obj = globalinfo[i]
|
||||
local msg = "("..i..") '"..obj.name.."' -> "
|
||||
local xref = obj.xref
|
||||
for j = 1, #xref do msg = msg..xref[j].." " end
|
||||
print(msg)
|
||||
end
|
||||
-- local tables have xref numbers and a few other special
|
||||
-- numbers that are specially named: decl (declaration xref),
|
||||
-- act (activation xref), rem (removal xref)
|
||||
print(hl.."\n LOCALS (decl=declared act=activated rem=removed)\n"..hl)
|
||||
for i = 1, #localinfo do
|
||||
local obj = localinfo[i]
|
||||
local msg = "("..i..") '"..obj.name.."' decl:"..obj.decl..
|
||||
" act:"..obj.act.." rem:"..obj.rem
|
||||
if obj.isself then
|
||||
msg = msg.." isself"
|
||||
end
|
||||
msg = msg.." -> "
|
||||
local xref = obj.xref
|
||||
for j = 1, #xref do msg = msg..xref[j].." " end
|
||||
print(msg)
|
||||
end
|
||||
print(hl.."\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- reads source file(s) and reports some statistics
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function read_only(srcfl)
|
||||
local print = print
|
||||
--------------------------------------------------------------------
|
||||
-- load file and process source input into tokens
|
||||
--------------------------------------------------------------------
|
||||
local z = load_file(srcfl)
|
||||
llex.init(z)
|
||||
llex.llex()
|
||||
local toklist, seminfolist = llex.tok, llex.seminfo
|
||||
print(MSG_TITLE)
|
||||
print("Statistics for: "..srcfl.."\n")
|
||||
--------------------------------------------------------------------
|
||||
-- collect statistics
|
||||
--------------------------------------------------------------------
|
||||
stat_init()
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
stat_add(tok, seminfo)
|
||||
end--for
|
||||
local stat_a = stat_calc()
|
||||
--------------------------------------------------------------------
|
||||
-- display output
|
||||
--------------------------------------------------------------------
|
||||
local fmt = string.format
|
||||
local function figures(tt)
|
||||
return stat_c[tt], stat_l[tt], stat_a[tt]
|
||||
end
|
||||
local tabf1, tabf2 = "%-16s%8s%8s%10s", "%-16s%8d%8d%10.2f"
|
||||
local hl = string.rep("-", 42)
|
||||
print(fmt(tabf1, "Lexical", "Input", "Input", "Input"))
|
||||
print(fmt(tabf1, "Elements", "Count", "Bytes", "Average"))
|
||||
print(hl)
|
||||
for i = 1, #TTYPES do
|
||||
local ttype = TTYPES[i]
|
||||
print(fmt(tabf2, ttype, figures(ttype)))
|
||||
if ttype == "TK_EOS" then print(hl) end
|
||||
end
|
||||
print(hl)
|
||||
print(fmt(tabf2, "Total Elements", figures("TOTAL_ALL")))
|
||||
print(hl)
|
||||
print(fmt(tabf2, "Total Tokens", figures("TOTAL_TOK")))
|
||||
print(hl.."\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- process source file(s), write output and reports some statistics
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function process_file(srcfl, destfl)
|
||||
local function print(...) -- handle quiet option
|
||||
if option.QUIET then return end
|
||||
_G.print(...)
|
||||
end
|
||||
if plugin and plugin.init then -- plugin init
|
||||
option.EXIT = false
|
||||
plugin.init(option, srcfl, destfl)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
print(MSG_TITLE) -- title message
|
||||
--------------------------------------------------------------------
|
||||
-- load file and process source input into tokens
|
||||
--------------------------------------------------------------------
|
||||
local z = load_file(srcfl)
|
||||
if plugin and plugin.post_load then -- plugin post-load
|
||||
z = plugin.post_load(z) or z
|
||||
if option.EXIT then return end
|
||||
end
|
||||
llex.init(z)
|
||||
llex.llex()
|
||||
local toklist, seminfolist, toklnlist
|
||||
= llex.tok, llex.seminfo, llex.tokln
|
||||
if plugin and plugin.post_lex then -- plugin post-lex
|
||||
plugin.post_lex(toklist, seminfolist, toklnlist)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- collect 'before' statistics
|
||||
--------------------------------------------------------------------
|
||||
stat_init()
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
stat_add(tok, seminfo)
|
||||
end--for
|
||||
local stat1_a = stat_calc()
|
||||
local stat1_c, stat1_l = stat_c, stat_l
|
||||
--------------------------------------------------------------------
|
||||
-- do parser optimization here
|
||||
--------------------------------------------------------------------
|
||||
if option["opt-locals"] then
|
||||
optparser.print = print -- hack
|
||||
lparser.init(toklist, seminfolist, toklnlist)
|
||||
local globalinfo, localinfo = lparser.parser()
|
||||
if plugin and plugin.post_parse then -- plugin post-parse
|
||||
plugin.post_parse(globalinfo, localinfo)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
optparser.optimize(option, toklist, seminfolist, globalinfo, localinfo)
|
||||
if plugin and plugin.post_optparse then -- plugin post-optparse
|
||||
plugin.post_optparse()
|
||||
if option.EXIT then return end
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- do lexer optimization here, save output file
|
||||
--------------------------------------------------------------------
|
||||
optlex.print = print -- hack
|
||||
toklist, seminfolist, toklnlist
|
||||
= optlex.optimize(option, toklist, seminfolist, toklnlist)
|
||||
if plugin and plugin.post_optlex then -- plugin post-optlex
|
||||
plugin.post_optlex(toklist, seminfolist, toklnlist)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
local dat = table.concat(seminfolist)
|
||||
-- depending on options selected, embedded EOLs in long strings and
|
||||
-- long comments may not have been translated to \n, tack a warning
|
||||
if string.find(dat, "\r\n", 1, 1) or
|
||||
string.find(dat, "\n\r", 1, 1) then
|
||||
optlex.warn.mixedeol = true
|
||||
end
|
||||
-- save optimized source stream to output file
|
||||
save_file(destfl, dat)
|
||||
--------------------------------------------------------------------
|
||||
-- collect 'after' statistics
|
||||
--------------------------------------------------------------------
|
||||
stat_init()
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
stat_add(tok, seminfo)
|
||||
end--for
|
||||
local stat_a = stat_calc()
|
||||
--------------------------------------------------------------------
|
||||
-- display output
|
||||
--------------------------------------------------------------------
|
||||
-- print("Statistics for: "..srcfl.." -> "..destfl.."\n")
|
||||
-- local fmt = string.format
|
||||
-- local function figures(tt)
|
||||
-- return stat1_c[tt], stat1_l[tt], stat1_a[tt],
|
||||
-- stat_c[tt], stat_l[tt], stat_a[tt]
|
||||
-- end
|
||||
-- local tabf1, tabf2 = "%-16s%8s%8s%10s%8s%8s%10s",
|
||||
-- "%-16s%8d%8d%10.2f%8d%8d%10.2f"
|
||||
-- local hl = string.rep("-", 68)
|
||||
-- print("*** lexer-based optimizations summary ***\n"..hl)
|
||||
-- print(fmt(tabf1, "Lexical",
|
||||
-- "Input", "Input", "Input",
|
||||
-- "Output", "Output", "Output"))
|
||||
-- print(fmt(tabf1, "Elements",
|
||||
-- "Count", "Bytes", "Average",
|
||||
-- "Count", "Bytes", "Average"))
|
||||
-- print(hl)
|
||||
-- for i = 1, #TTYPES do
|
||||
-- local ttype = TTYPES[i]
|
||||
-- print(fmt(tabf2, ttype, figures(ttype)))
|
||||
-- if ttype == "TK_EOS" then print(hl) end
|
||||
-- end
|
||||
-- print(hl)
|
||||
-- print(fmt(tabf2, "Total Elements", figures("TOTAL_ALL")))
|
||||
-- print(hl)
|
||||
-- print(fmt(tabf2, "Total Tokens", figures("TOTAL_TOK")))
|
||||
-- print(hl)
|
||||
--------------------------------------------------------------------
|
||||
-- report warning flags from optimizing process
|
||||
--------------------------------------------------------------------
|
||||
if optlex.warn.lstring then
|
||||
print("* WARNING: "..optlex.warn.lstring)
|
||||
elseif optlex.warn.mixedeol then
|
||||
print("* WARNING: ".."output still contains some CRLF or LFCR line endings")
|
||||
end
|
||||
print()
|
||||
end
|
||||
|
||||
local function process_code(code, config)
|
||||
option.QUIET = true
|
||||
if config == 1 then
|
||||
set_options(DEFAULT_CONFIG)
|
||||
elseif config == 2 then
|
||||
set_options(BASIC_CONFIG)
|
||||
elseif config == 3 then
|
||||
set_options(MAXIMUM_CONFIG)
|
||||
else
|
||||
set_options(NONE_CONFIG)
|
||||
end
|
||||
|
||||
local function print(...) -- handle quiet option
|
||||
if option.QUIET then return end
|
||||
_G.print(...)
|
||||
end
|
||||
-- if plugin and plugin.init then -- plugin init
|
||||
-- option.EXIT = false
|
||||
-- plugin.init(option, srcfl, destfl)
|
||||
-- if option.EXIT then return end
|
||||
-- end
|
||||
print(MSG_TITLE) -- title message
|
||||
--------------------------------------------------------------------
|
||||
-- load file and process source input into tokens
|
||||
--------------------------------------------------------------------
|
||||
local z = code -- load_file(srcfl)
|
||||
if plugin and plugin.post_load then -- plugin post-load
|
||||
z = plugin.post_load(z) or z
|
||||
if option.EXIT then return end
|
||||
end
|
||||
llex.init(z)
|
||||
llex.llex()
|
||||
local toklist, seminfolist, toklnlist
|
||||
= llex.tok, llex.seminfo, llex.tokln
|
||||
if plugin and plugin.post_lex then -- plugin post-lex
|
||||
plugin.post_lex(toklist, seminfolist, toklnlist)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- collect 'before' statistics
|
||||
--------------------------------------------------------------------
|
||||
stat_init()
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
stat_add(tok, seminfo)
|
||||
end--for
|
||||
local stat1_a = stat_calc()
|
||||
local stat1_c, stat1_l = stat_c, stat_l
|
||||
--------------------------------------------------------------------
|
||||
-- do parser optimization here
|
||||
--------------------------------------------------------------------
|
||||
if option["opt-locals"] then
|
||||
optparser.print = print -- hack
|
||||
lparser.init(toklist, seminfolist, toklnlist)
|
||||
local globalinfo, localinfo = lparser.parser()
|
||||
if plugin and plugin.post_parse then -- plugin post-parse
|
||||
plugin.post_parse(globalinfo, localinfo)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
optparser.optimize(option, toklist, seminfolist, globalinfo, localinfo)
|
||||
if plugin and plugin.post_optparse then -- plugin post-optparse
|
||||
plugin.post_optparse()
|
||||
if option.EXIT then return end
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- do lexer optimization here, save output file
|
||||
--------------------------------------------------------------------
|
||||
optlex.print = print -- hack
|
||||
toklist, seminfolist, toklnlist
|
||||
= optlex.optimize(option, toklist, seminfolist, toklnlist)
|
||||
if plugin and plugin.post_optlex then -- plugin post-optlex
|
||||
plugin.post_optlex(toklist, seminfolist, toklnlist)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
local dat = table.concat(seminfolist)
|
||||
-- depending on options selected, embedded EOLs in long strings and
|
||||
-- long comments may not have been translated to \n, tack a warning
|
||||
if string.find(dat, "\r\n", 1, 1) or
|
||||
string.find(dat, "\n\r", 1, 1) then
|
||||
optlex.warn.mixedeol = true
|
||||
end
|
||||
-- save optimized source stream to output file
|
||||
-- save_file(destfl, dat)
|
||||
--------------------------------------------------------------------
|
||||
-- collect 'after' statistics
|
||||
--------------------------------------------------------------------
|
||||
-- stat_init()
|
||||
-- for i = 1, #toklist do
|
||||
-- local tok, seminfo = toklist[i], seminfolist[i]
|
||||
-- stat_add(tok, seminfo)
|
||||
-- end--for
|
||||
-- local stat_a = stat_calc()
|
||||
--------------------------------------------------------------------
|
||||
-- display output
|
||||
--------------------------------------------------------------------
|
||||
-- print("Statistics for: "..srcfl.." -> "..destfl.."\n")
|
||||
-- local fmt = string.format
|
||||
-- local function figures(tt)
|
||||
-- return stat1_c[tt], stat1_l[tt], stat1_a[tt],
|
||||
-- stat_c[tt], stat_l[tt], stat_a[tt]
|
||||
-- end
|
||||
-- local tabf1, tabf2 = "%-16s%8s%8s%10s%8s%8s%10s",
|
||||
-- "%-16s%8d%8d%10.2f%8d%8d%10.2f"
|
||||
-- local hl = string.rep("-", 68)
|
||||
-- print("*** lexer-based optimizations summary ***\n"..hl)
|
||||
-- print(fmt(tabf1, "Lexical",
|
||||
-- "Input", "Input", "Input",
|
||||
-- "Output", "Output", "Output"))
|
||||
-- print(fmt(tabf1, "Elements",
|
||||
-- "Count", "Bytes", "Average",
|
||||
-- "Count", "Bytes", "Average"))
|
||||
-- print(hl)
|
||||
-- for i = 1, #TTYPES do
|
||||
-- local ttype = TTYPES[i]
|
||||
-- print(fmt(tabf2, ttype, figures(ttype)))
|
||||
-- if ttype == "TK_EOS" then print(hl) end
|
||||
-- end
|
||||
-- print(hl)
|
||||
-- print(fmt(tabf2, "Total Elements", figures("TOTAL_ALL")))
|
||||
-- print(hl)
|
||||
-- print(fmt(tabf2, "Total Tokens", figures("TOTAL_TOK")))
|
||||
-- print(hl)
|
||||
--------------------------------------------------------------------
|
||||
-- report warning flags from optimizing process
|
||||
--------------------------------------------------------------------
|
||||
if optlex.warn.lstring then
|
||||
print("* WARNING: "..optlex.warn.lstring)
|
||||
elseif optlex.warn.mixedeol then
|
||||
print("* WARNING: ".."output still contains some CRLF or LFCR line endings")
|
||||
end
|
||||
print()
|
||||
return dat
|
||||
end
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- main functions
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
local arg = {...} -- program arguments
|
||||
local fspec = {}
|
||||
set_options(DEFAULT_CONFIG) -- set to default options at beginning
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- per-file handling, ship off to tasks
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function do_files(fspec)
|
||||
for _, srcfl in ipairs(fspec) do
|
||||
local destfl
|
||||
------------------------------------------------------------------
|
||||
-- find and replace extension for filenames
|
||||
------------------------------------------------------------------
|
||||
local extb, exte = string.find(srcfl, "%.[^%.%\\%/]*$")
|
||||
local basename, extension = srcfl, ""
|
||||
if extb and extb > 1 then
|
||||
basename = sub(srcfl, 1, extb - 1)
|
||||
extension = sub(srcfl, extb, exte)
|
||||
end
|
||||
destfl = basename..suffix..extension
|
||||
if #fspec == 1 and option.OUTPUT_FILE then
|
||||
destfl = option.OUTPUT_FILE
|
||||
end
|
||||
if srcfl == destfl then
|
||||
die("output filename identical to input filename")
|
||||
end
|
||||
------------------------------------------------------------------
|
||||
-- perform requested operations
|
||||
------------------------------------------------------------------
|
||||
if option.DUMP_LEXER then
|
||||
dump_tokens(srcfl)
|
||||
elseif option.DUMP_PARSER then
|
||||
dump_parser(srcfl)
|
||||
elseif option.READ_ONLY then
|
||||
read_only(srcfl)
|
||||
else
|
||||
process_file(srcfl, destfl)
|
||||
end
|
||||
end--for
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- main function (entry point is after this definition)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function main()
|
||||
local argn, i = #arg, 1
|
||||
if argn == 0 then
|
||||
option.HELP = true
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- handle arguments
|
||||
--------------------------------------------------------------------
|
||||
while i <= argn do
|
||||
local o, p = arg[i], arg[i + 1]
|
||||
local dash = string.match(o, "^%-%-?")
|
||||
if dash == "-" then -- single-dash options
|
||||
if o == "-h" then
|
||||
option.HELP = true; break
|
||||
elseif o == "-v" then
|
||||
option.VERSION = true; break
|
||||
elseif o == "-s" then
|
||||
if not p then die("-s option needs suffix specification") end
|
||||
suffix = p
|
||||
i = i + 1
|
||||
elseif o == "-o" then
|
||||
if not p then die("-o option needs a file name") end
|
||||
option.OUTPUT_FILE = p
|
||||
i = i + 1
|
||||
elseif o == "-" then
|
||||
break -- ignore rest of args
|
||||
else
|
||||
die("unrecognized option "..o)
|
||||
end
|
||||
elseif dash == "--" then -- double-dash options
|
||||
if o == "--help" then
|
||||
option.HELP = true; break
|
||||
elseif o == "--version" then
|
||||
option.VERSION = true; break
|
||||
elseif o == "--keep" then
|
||||
if not p then die("--keep option needs a string to match for") end
|
||||
option.KEEP = p
|
||||
i = i + 1
|
||||
elseif o == "--plugin" then
|
||||
if not p then die("--plugin option needs a module name") end
|
||||
if option.PLUGIN then die("only one plugin can be specified") end
|
||||
option.PLUGIN = p
|
||||
plugin = require(PLUGIN_SUFFIX..p)
|
||||
i = i + 1
|
||||
elseif o == "--quiet" then
|
||||
option.QUIET = true
|
||||
elseif o == "--read-only" then
|
||||
option.READ_ONLY = true
|
||||
elseif o == "--basic" then
|
||||
set_options(BASIC_CONFIG)
|
||||
elseif o == "--maximum" then
|
||||
set_options(MAXIMUM_CONFIG)
|
||||
elseif o == "--none" then
|
||||
set_options(NONE_CONFIG)
|
||||
elseif o == "--dump-lexer" then
|
||||
option.DUMP_LEXER = true
|
||||
elseif o == "--dump-parser" then
|
||||
option.DUMP_PARSER = true
|
||||
elseif o == "--details" then
|
||||
option.DETAILS = true
|
||||
elseif OPTION[o] then -- lookup optimization options
|
||||
set_options(o)
|
||||
else
|
||||
die("unrecognized option "..o)
|
||||
end
|
||||
else
|
||||
fspec[#fspec + 1] = o -- potential filename
|
||||
end
|
||||
i = i + 1
|
||||
end--while
|
||||
if option.HELP then
|
||||
print(MSG_TITLE..MSG_USAGE); return true
|
||||
elseif option.VERSION then
|
||||
print(MSG_TITLE); return true
|
||||
end
|
||||
if #fspec > 0 then
|
||||
if #fspec > 1 and option.OUTPUT_FILE then
|
||||
die("with -o, only one source file can be specified")
|
||||
end
|
||||
do_files(fspec)
|
||||
return true
|
||||
else
|
||||
die("nothing to do!")
|
||||
end
|
||||
end
|
||||
|
||||
-- entry point -> main() -> do_files()
|
||||
-- if not main() then
|
||||
-- die("Please run with option -h or --help for usage information")
|
||||
-- end
|
||||
|
||||
-- end of script
|
||||
|
||||
return process_code
|
||||
193
AIO_Server/Dep_LuaSrcDiet/README
Normal file
193
AIO_Server/Dep_LuaSrcDiet/README
Normal file
@@ -0,0 +1,193 @@
|
||||
|
||||
LuaSrcDiet
|
||||
Compresses Lua source code by removing unnecessary characters.
|
||||
|
||||
Copyright (c) 2005-2008 Kein-Hong Man <khman@users.sf.net>
|
||||
The COPYRIGHT file describes the conditions
|
||||
under which this software may be distributed.
|
||||
|
||||
http://luaforge.net/projects/luasrcdiet/
|
||||
http://luasrcdiet.luaforge.net/
|
||||
|
||||
--
|
||||
|
||||
For the older unmaintained version of LuaSrcDiet for Lua 5.0.2 sources,
|
||||
please see the 5.0/README file.
|
||||
|
||||
--
|
||||
|
||||
PREVIEW NOTES
|
||||
|
||||
See also: http://luasrcdiet.luaforge.net/
|
||||
|
||||
The 0.11.0 release of LuaSrcDiet has a local variable name optimizer.
|
||||
Local variable names are renamed into the shortest possible names. In
|
||||
addition, variable names are reused whenever possible, reducing the
|
||||
number of unique variable names. Several hundred local variable names
|
||||
can be reduced into 53 or less unique names, which allows all locals
|
||||
to be single-character in length.
|
||||
|
||||
The local variable name optimizer uses a full parser of Lua 5.1 source
|
||||
code, thus it can rename all local variables, including function
|
||||
parameters. It should handle the implicit "self" parameter gracefully.
|
||||
The optimizer needs more testing, but is already able to optimize the
|
||||
LuaSrcDiet sources itself and generate correct Lua output.
|
||||
|
||||
String and number token optimizations are also performed, apart from the
|
||||
usual whitespace, line ending and comment removal. Numbers can switch
|
||||
between different formats. Strings can be simplified and can switch
|
||||
delimiters between " or ' characters.
|
||||
|
||||
Most options can also be enabled or disabled separately, for maximum
|
||||
flexibility. If you need to keep a copyright message in the optimized
|
||||
output, the --keep option can keep block comments that contain a certain
|
||||
string.
|
||||
|
||||
For samples, see the sample/ directory. Performance statistics can be
|
||||
found in the sample/statistics.txt file. Preliminary test samples for
|
||||
strings and numbers can also be found in the sample/ directory.
|
||||
|
||||
Priority for future work:
|
||||
(a) automatic tests for lexer/parser optimizations
|
||||
(b) integrity checking, token stream check or binary chunk check
|
||||
|
||||
--
|
||||
|
||||
INTRODUCTION
|
||||
|
||||
...
|
||||
|
||||
WARNING! Locals optimization does NOT have support for 'arg' vararg
|
||||
functions (LUA_COMPAT_VARARG).
|
||||
|
||||
--
|
||||
|
||||
WHAT'S NEW
|
||||
|
||||
Major changes for version 0.11.2 (see the ChangeLog as well):
|
||||
* improved local variable name allocation, more efficient now
|
||||
* added experimental --plugin option with an example plugin script
|
||||
* added a SLOC plugin to count SLOC for Lua 5.1 source files
|
||||
* added a HTML plugin to see globals and locals marked
|
||||
|
||||
Major changes for version 0.11.1 (see the ChangeLog as well):
|
||||
* --detail option for more string, number and local variable info
|
||||
* fixed a local rename bug that generates names that are keywords
|
||||
* added explanatory notes on local variable optimization
|
||||
* added --opt-entropy option for locals to reduce symbol entropy
|
||||
|
||||
Major changes for version 0.11.0 (see the ChangeLog as well):
|
||||
* Local variable name optimization.
|
||||
* Many options and sample output added.
|
||||
|
||||
Major changes for version 0.10.2 (see the ChangeLog as well):
|
||||
* Aggressive optimizations for string and number tokens.
|
||||
* Minor bug fixes.
|
||||
|
||||
Major changes for version 0.10.1 (see the ChangeLog as well):
|
||||
* Totally rewritten for Lua 5.1.x.
|
||||
|
||||
--
|
||||
|
||||
USAGE OPTIONS
|
||||
|
||||
...
|
||||
|
||||
Example of summary data display:
|
||||
|
||||
Statistics for: LuaSrcDiet.lua -> sample/LuaSrcDiet.lua
|
||||
|
||||
*** local variable optimization summary ***
|
||||
----------------------------------------------------------
|
||||
Variable Unique Decl. Token Size Average
|
||||
Types Names Count Count Bytes Bytes
|
||||
----------------------------------------------------------
|
||||
Global 10 0 19 95 5.00
|
||||
----------------------------------------------------------
|
||||
Local (in) 88 153 683 3340 4.89
|
||||
TOTAL (in) 98 153 702 3435 4.89
|
||||
----------------------------------------------------------
|
||||
Local (out) 32 153 683 683 1.00
|
||||
TOTAL (out) 42 153 702 778 1.11
|
||||
----------------------------------------------------------
|
||||
|
||||
*** lexer-based optimizations summary ***
|
||||
--------------------------------------------------------------------
|
||||
Lexical Input Input Input Output Output Output
|
||||
Elements Count Bytes Average Count Bytes Average
|
||||
--------------------------------------------------------------------
|
||||
TK_KEYWORD 374 1531 4.09 374 1531 4.09
|
||||
TK_NAME 795 3963 4.98 795 1306 1.64
|
||||
TK_NUMBER 54 59 1.09 54 59 1.09
|
||||
TK_STRING 152 1725 11.35 152 1717 11.30
|
||||
TK_LSTRING 7 1976 282.29 7 1976 282.29
|
||||
TK_OP 997 1092 1.10 997 1092 1.10
|
||||
TK_EOS 1 0 0.00 1 0 0.00
|
||||
--------------------------------------------------------------------
|
||||
TK_COMMENT 140 6884 49.17 1 18 18.00
|
||||
TK_LCOMMENT 7 1723 246.14 0 0 0.00
|
||||
TK_EOL 543 543 1.00 197 197 1.00
|
||||
TK_SPACE 1270 2465 1.94 263 263 1.00
|
||||
--------------------------------------------------------------------
|
||||
Total Elements 4340 21961 5.06 2841 8159 2.87
|
||||
--------------------------------------------------------------------
|
||||
Total Tokens 2380 10346 4.35 2380 7681 3.23
|
||||
--------------------------------------------------------------------
|
||||
|
||||
--
|
||||
|
||||
USING LUASRCDIET
|
||||
|
||||
...
|
||||
|
||||
Please see the command line help or see sample/Makefile for examples.
|
||||
|
||||
This is experimental software and nothing has been done yet on a proper
|
||||
installation scheme for use with normal work. A thousand apologies...
|
||||
|
||||
--
|
||||
|
||||
CODE SIZE REDUCTION
|
||||
|
||||
...
|
||||
|
||||
--
|
||||
|
||||
OTHER OPTIONS
|
||||
|
||||
...
|
||||
|
||||
--
|
||||
|
||||
BEHAVIOUR NOTES
|
||||
|
||||
* embedded line endings in strings and long strings always
|
||||
normalized to LF
|
||||
* will not optimize trailing spaces in long strings, only warns
|
||||
* scientific notation generated in number optimzation is not in
|
||||
canonical form, this may or may not be a bad thing, so feedback
|
||||
is welcome
|
||||
|
||||
--
|
||||
|
||||
ACKNOWLEDGEMENTS
|
||||
|
||||
Thanks to the LuaForge people for hosting this.
|
||||
Developed on SciTE http://www.scintilla.org/. Two thumbs up.
|
||||
|
||||
--
|
||||
|
||||
FEEDBACK
|
||||
|
||||
Feedback and contributions are welcome. Your name will be acknowledged,
|
||||
as long as you are willing to comply with COPYRIGHT. If your material is
|
||||
self-contained, you can retain a copyright notice for those material in
|
||||
your own name, as long as you use the same Lua 5/MIT-style copyright.
|
||||
|
||||
My alternative e-mail address is: keinhong AT gmail DOT com
|
||||
|
||||
Enjoy!!
|
||||
|
||||
Kein-Hong Man (esq.)
|
||||
Kuala Lumpur
|
||||
Malaysia 20080603
|
||||
355
AIO_Server/Dep_LuaSrcDiet/llex.lua
Normal file
355
AIO_Server/Dep_LuaSrcDiet/llex.lua
Normal file
@@ -0,0 +1,355 @@
|
||||
--[[--------------------------------------------------------------------
|
||||
|
||||
base.llex.lua: Lua 5.1 lexical analyzer in Lua
|
||||
This file is part of LuaSrcDiet, based on Yueliang material.
|
||||
|
||||
Copyright (c) 2008 Kein-Hong Man <khman@users.sf.net>
|
||||
The COPYRIGHT file describes the conditions
|
||||
under which this software may be distributed.
|
||||
|
||||
See the ChangeLog for more information.
|
||||
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- NOTES:
|
||||
-- * This is a version of the native 5.1.x lexer from Yueliang 0.4.0,
|
||||
-- with significant modifications to handle LuaSrcDiet's needs:
|
||||
-- (1) base.llex.error is an optional error function handler
|
||||
-- (2) base.seminfo for strings include their delimiters and no
|
||||
-- translation operations are performed on them
|
||||
-- * ADDED shbang handling has been added to support executable scripts
|
||||
-- * NO localized decimal point replacement magic
|
||||
-- * NO limit to number of lines
|
||||
-- * NO support for compatible long strings (LUA_COMPAT_LSTR)
|
||||
-- * Please read technotes.txt for more technical details.
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
local base = {}
|
||||
-- local base = _G
|
||||
-- local string = require "string"
|
||||
-- module "base.llex"
|
||||
|
||||
local find = string.find
|
||||
local match = string.match
|
||||
local sub = string.sub
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- initialize keyword list, variables
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local kw = {}
|
||||
for v in string.gmatch([[
|
||||
and break do else elseif end false for function if in
|
||||
local nil not or repeat return then true until while]], "%S+") do
|
||||
kw[v] = true
|
||||
end
|
||||
|
||||
-- NOTE: see init() for module variables (externally visible):
|
||||
-- base.tok, base.seminfo, base.tokln
|
||||
|
||||
local z, -- source stream
|
||||
sourceid, -- name of source
|
||||
I, -- position of lexer
|
||||
buff, -- buffer for strings
|
||||
ln -- line number
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- add information to token listing
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function addtoken(token, info)
|
||||
local i = #base.tok + 1
|
||||
base.tok[i] = token
|
||||
base.seminfo[i] = info
|
||||
base.tokln[i] = ln
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- handles line number incrementation and end-of-line characters
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function inclinenumber(i, is_tok)
|
||||
local sub = sub
|
||||
local old = sub(z, i, i)
|
||||
i = i + 1 -- skip '\n' or '\r'
|
||||
local c = sub(z, i, i)
|
||||
if (c == "\n" or c == "\r") and (c ~= old) then
|
||||
i = i + 1 -- skip '\n\r' or '\r\n'
|
||||
old = old..c
|
||||
end
|
||||
if is_tok then addtoken("TK_EOL", old) end
|
||||
ln = ln + 1
|
||||
I = i
|
||||
return i
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- initialize lexer for given source _z and source name _sourceid
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function base.init(_z, _sourceid)
|
||||
z = _z -- source
|
||||
sourceid = _sourceid -- name of source
|
||||
I = 1 -- lexer's position in source
|
||||
ln = 1 -- line number
|
||||
base.tok = {} -- lexed token list*
|
||||
base.seminfo = {} -- lexed semantic information list*
|
||||
base.tokln = {} -- line numbers for messages*
|
||||
-- (*) externally visible thru' module
|
||||
--------------------------------------------------------------------
|
||||
-- initial processing (shbang handling)
|
||||
--------------------------------------------------------------------
|
||||
local p, _, q, r = find(z, "^(#[^\r\n]*)(\r?\n?)")
|
||||
if p then -- skip first line
|
||||
I = I + #q
|
||||
addtoken("TK_COMMENT", q)
|
||||
if #r > 0 then inclinenumber(I, true) end
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- returns a chunk name or id, no truncation for long names
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function base.chunkid()
|
||||
if sourceid and match(sourceid, "^[=@]") then
|
||||
return sub(sourceid, 2) -- remove first char
|
||||
end
|
||||
return "[string]"
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- formats error message and throws error
|
||||
-- * a simplified version, does not report what token was responsible
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function base.errorline(s, line)
|
||||
local e = error or base.error
|
||||
e(string.format("%s:%d: %s", base.chunkid(), line or ln, s))
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- count separators ("=") in a long string delimiter
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function skip_sep(i)
|
||||
local sub = sub
|
||||
local s = sub(z, i, i)
|
||||
i = i + 1
|
||||
local count = #match(z, "=*", i) -- note, take the length
|
||||
i = i + count
|
||||
I = i
|
||||
return (sub(z, i, i) == s) and count or (-count) - 1
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- reads a long string or long comment
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function read_long_string(is_str, sep)
|
||||
local i = I + 1 -- skip 2nd '['
|
||||
local sub = sub
|
||||
local c = sub(z, i, i)
|
||||
if c == "\r" or c == "\n" then -- string starts with a newline?
|
||||
i = inclinenumber(i) -- skip it
|
||||
end
|
||||
local j = i
|
||||
while true do
|
||||
local p, q, r = find(z, "([\r\n%]])", i) -- (long range)
|
||||
if not p then
|
||||
base.errorline(is_str and "unfinished long string" or
|
||||
"unfinished long comment")
|
||||
end
|
||||
i = p
|
||||
if r == "]" then -- delimiter test
|
||||
if skip_sep(i) == sep then
|
||||
buff = sub(z, buff, I)
|
||||
I = I + 1 -- skip 2nd ']'
|
||||
return buff
|
||||
end
|
||||
i = I
|
||||
else -- newline
|
||||
buff = buff.."\n"
|
||||
i = inclinenumber(i)
|
||||
end
|
||||
end--while
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- reads a string
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function read_string(del)
|
||||
local i = I
|
||||
local find = find
|
||||
local sub = sub
|
||||
while true do
|
||||
local p, q, r = find(z, "([\n\r\\\"\'])", i) -- (long range)
|
||||
if p then
|
||||
if r == "\n" or r == "\r" then
|
||||
base.errorline("unfinished string")
|
||||
end
|
||||
i = p
|
||||
if r == "\\" then -- handle escapes
|
||||
i = i + 1
|
||||
r = sub(z, i, i)
|
||||
if r == "" then break end -- (EOZ error)
|
||||
p = find("abfnrtv\n\r", r, 1, true)
|
||||
------------------------------------------------------
|
||||
if p then -- special escapes
|
||||
if p > 7 then
|
||||
i = inclinenumber(i)
|
||||
else
|
||||
i = i + 1
|
||||
end
|
||||
------------------------------------------------------
|
||||
elseif find(r, "%D") then -- other non-digits
|
||||
i = i + 1
|
||||
------------------------------------------------------
|
||||
else -- \xxx sequence
|
||||
local p, q, s = find(z, "^(%d%d?%d?)", i)
|
||||
i = q + 1
|
||||
if s + 1 > 256 then -- UCHAR_MAX
|
||||
base.errorline("escape sequence too large")
|
||||
end
|
||||
------------------------------------------------------
|
||||
end--if p
|
||||
else
|
||||
i = i + 1
|
||||
if r == del then -- ending delimiter
|
||||
I = i
|
||||
return sub(z, buff, i - 1) -- return string
|
||||
end
|
||||
end--if r
|
||||
else
|
||||
break -- (error)
|
||||
end--if p
|
||||
end--while
|
||||
base.errorline("unfinished string")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- main lexer function
|
||||
------------------------------------------------------------------------
|
||||
|
||||
function base.llex()
|
||||
local find = find
|
||||
local match = match
|
||||
while true do--outer
|
||||
local i = I
|
||||
-- inner loop allows break to be used to nicely section tests
|
||||
while true do--inner
|
||||
----------------------------------------------------------------
|
||||
local p, _, r = find(z, "^([_%a][_%w]*)", i)
|
||||
if p then
|
||||
I = i + #r
|
||||
if kw[r] then
|
||||
addtoken("TK_KEYWORD", r) -- reserved word (keyword)
|
||||
else
|
||||
addtoken("TK_NAME", r) -- identifier
|
||||
end
|
||||
break -- (continue)
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
local p, _, r = find(z, "^(%.?)%d", i)
|
||||
if p then -- numeral
|
||||
if r == "." then i = i + 1 end
|
||||
local _, q, r = find(z, "^%d*[%.%d]*([eE]?)", i)
|
||||
i = q + 1
|
||||
if #r == 1 then -- optional exponent
|
||||
if match(z, "^[%+%-]", i) then -- optional sign
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
local _, q = find(z, "^[_%w]*", i)
|
||||
I = q + 1
|
||||
local v = sub(z, p, q) -- string equivalent
|
||||
if not tonumber(v) then -- handles hex test also
|
||||
base.errorline("malformed number")
|
||||
end
|
||||
addtoken("TK_NUMBER", v)
|
||||
break -- (continue)
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
local p, q, r, t = find(z, "^((%s)[ \t\v\f]*)", i)
|
||||
if p then
|
||||
if t == "\n" or t == "\r" then -- newline
|
||||
inclinenumber(i, true)
|
||||
else
|
||||
I = q + 1 -- whitespace
|
||||
addtoken("TK_SPACE", r)
|
||||
end
|
||||
break -- (continue)
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
local r = match(z, "^%p", i)
|
||||
if r then
|
||||
buff = i
|
||||
local p = find("-[\"\'.=<>~", r, 1, true)
|
||||
if p then
|
||||
-- two-level if block for punctuation/symbols
|
||||
--------------------------------------------------------
|
||||
if p <= 2 then
|
||||
if p == 1 then -- minus
|
||||
local c = match(z, "^%-%-(%[?)", i)
|
||||
if c then
|
||||
i = i + 2
|
||||
local sep = -1
|
||||
if c == "[" then
|
||||
sep = skip_sep(i)
|
||||
end
|
||||
if sep >= 0 then -- long comment
|
||||
addtoken("TK_LCOMMENT", read_long_string(false, sep))
|
||||
else -- short comment
|
||||
I = find(z, "[\n\r]", i) or (#z + 1)
|
||||
addtoken("TK_COMMENT", sub(z, buff, I - 1))
|
||||
end
|
||||
break -- (continue)
|
||||
end
|
||||
-- (fall through for "-")
|
||||
else -- [ or long string
|
||||
local sep = skip_sep(i)
|
||||
if sep >= 0 then
|
||||
addtoken("TK_LSTRING", read_long_string(true, sep))
|
||||
elseif sep == -1 then
|
||||
addtoken("TK_OP", "[")
|
||||
else
|
||||
base.errorline("invalid long string delimiter")
|
||||
end
|
||||
break -- (continue)
|
||||
end
|
||||
--------------------------------------------------------
|
||||
elseif p <= 5 then
|
||||
if p < 5 then -- strings
|
||||
I = i + 1
|
||||
addtoken("TK_STRING", read_string(r))
|
||||
break -- (continue)
|
||||
end
|
||||
r = match(z, "^%.%.?%.?", i) -- .|..|... dots
|
||||
-- (fall through)
|
||||
--------------------------------------------------------
|
||||
else -- relational
|
||||
r = match(z, "^%p=?", i)
|
||||
-- (fall through)
|
||||
end
|
||||
end
|
||||
I = i + #r
|
||||
addtoken("TK_OP", r) -- for other symbols, fall through
|
||||
break -- (continue)
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
local r = sub(z, i, i)
|
||||
if r ~= "" then
|
||||
I = i + 1
|
||||
addtoken("TK_OP", r) -- other single-char tokens
|
||||
break
|
||||
end
|
||||
addtoken("TK_EOS", "") -- end of stream,
|
||||
return base -- exit here
|
||||
----------------------------------------------------------------
|
||||
end--while inner
|
||||
end--while outer
|
||||
end
|
||||
|
||||
return base
|
||||
1296
AIO_Server/Dep_LuaSrcDiet/lparser.lua
Normal file
1296
AIO_Server/Dep_LuaSrcDiet/lparser.lua
Normal file
File diff suppressed because it is too large
Load Diff
837
AIO_Server/Dep_LuaSrcDiet/optlex.lua
Normal file
837
AIO_Server/Dep_LuaSrcDiet/optlex.lua
Normal file
@@ -0,0 +1,837 @@
|
||||
--[[--------------------------------------------------------------------
|
||||
|
||||
optlex.lua: does lexer-based optimizations
|
||||
This file is part of LuaSrcDiet.
|
||||
|
||||
Copyright (c) 2008 Kein-Hong Man <khman@users.sf.net>
|
||||
The COPYRIGHT file describes the conditions
|
||||
under which this software may be distributed.
|
||||
|
||||
See the ChangeLog for more information.
|
||||
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- NOTES:
|
||||
-- * For more lexer-based optimization ideas, see the TODO items or
|
||||
-- look at technotes.txt.
|
||||
-- * TODO: general string delimiter conversion optimizer
|
||||
-- * TODO: (numbers) warn if overly significant digit
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
local base = {}
|
||||
-- local base = _G
|
||||
-- local string = require "string"
|
||||
-- module "optlex"
|
||||
local match = string.match
|
||||
local sub = string.sub
|
||||
local find = string.find
|
||||
local rep = string.rep
|
||||
local print = print
|
||||
local tostring = tostring
|
||||
local tonumber = tonumber
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- variables and data structures
|
||||
------------------------------------------------------------------------
|
||||
|
||||
-- error function, can override by setting own function into module
|
||||
local error = error
|
||||
|
||||
base.warn = {} -- table for warning flags
|
||||
|
||||
local stoks, sinfos, stoklns -- source lists
|
||||
|
||||
local is_realtoken = { -- significant (grammar) tokens
|
||||
TK_KEYWORD = true,
|
||||
TK_NAME = true,
|
||||
TK_NUMBER = true,
|
||||
TK_STRING = true,
|
||||
TK_LSTRING = true,
|
||||
TK_OP = true,
|
||||
TK_EOS = true,
|
||||
}
|
||||
local is_faketoken = { -- whitespace (non-grammar) tokens
|
||||
TK_COMMENT = true,
|
||||
TK_LCOMMENT = true,
|
||||
TK_EOL = true,
|
||||
TK_SPACE = true,
|
||||
}
|
||||
|
||||
local opt_details -- for extra information
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- true if current token is at the start of a line
|
||||
-- * skips over deleted tokens via recursion
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function atlinestart(i)
|
||||
local tok = stoks[i - 1]
|
||||
if i <= 1 or tok == "TK_EOL" then
|
||||
return true
|
||||
elseif tok == "" then
|
||||
return atlinestart(i - 1)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- true if current token is at the end of a line
|
||||
-- * skips over deleted tokens via recursion
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function atlineend(i)
|
||||
local tok = stoks[i + 1]
|
||||
if i >= #stoks or tok == "TK_EOL" or tok == "TK_EOS" then
|
||||
return true
|
||||
elseif tok == "" then
|
||||
return atlineend(i + 1)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- counts comment EOLs inside a long comment
|
||||
-- * in order to keep line numbering, EOLs need to be reinserted
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function commenteols(lcomment)
|
||||
local sep = #match(lcomment, "^%-%-%[=*%[")
|
||||
local z = sub(lcomment, sep + 1, -(sep - 1)) -- remove delims
|
||||
local i, c = 1, 0
|
||||
while true do
|
||||
local p, q, r, s = find(z, "([\r\n])([\r\n]?)", i)
|
||||
if not p then break end -- if no matches, done
|
||||
i = p + 1
|
||||
c = c + 1
|
||||
if #s > 0 and r ~= s then -- skip CRLF or LFCR
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- compares two tokens (i, j) and returns the whitespace required
|
||||
-- * important! see technotes.txt for more information
|
||||
-- * only two grammar/real tokens are being considered
|
||||
-- * if "", no separation is needed
|
||||
-- * if " ", then at least one whitespace (or EOL) is required
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function checkpair(i, j)
|
||||
local match = match
|
||||
local t1, t2 = stoks[i], stoks[j]
|
||||
--------------------------------------------------------------------
|
||||
if t1 == "TK_STRING" or t1 == "TK_LSTRING" or
|
||||
t2 == "TK_STRING" or t2 == "TK_LSTRING" then
|
||||
return ""
|
||||
--------------------------------------------------------------------
|
||||
elseif t1 == "TK_OP" or t2 == "TK_OP" then
|
||||
if (t1 == "TK_OP" and (t2 == "TK_KEYWORD" or t2 == "TK_NAME")) or
|
||||
(t2 == "TK_OP" and (t1 == "TK_KEYWORD" or t1 == "TK_NAME")) then
|
||||
return ""
|
||||
end
|
||||
if t1 == "TK_OP" and t2 == "TK_OP" then
|
||||
-- for TK_OP/TK_OP pairs, see notes in technotes.txt
|
||||
local op, op2 = sinfos[i], sinfos[j]
|
||||
if (match(op, "^%.%.?$") and match(op2, "^%.")) or
|
||||
(match(op, "^[~=<>]$") and op2 == "=") or
|
||||
(op == "[" and (op2 == "[" or op2 == "=")) then
|
||||
return " "
|
||||
end
|
||||
return ""
|
||||
end
|
||||
-- "TK_OP" + "TK_NUMBER" case
|
||||
local op = sinfos[i]
|
||||
if t2 == "TK_OP" then op = sinfos[j] end
|
||||
if match(op, "^%.%.?%.?$") then
|
||||
return " "
|
||||
end
|
||||
return ""
|
||||
--------------------------------------------------------------------
|
||||
else-- "TK_KEYWORD" | "TK_NAME" | "TK_NUMBER" then
|
||||
return " "
|
||||
--------------------------------------------------------------------
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- repack tokens, removing deletions caused by optimization process
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function repack_tokens()
|
||||
local dtoks, dinfos, dtoklns = {}, {}, {}
|
||||
local j = 1
|
||||
for i = 1, #stoks do
|
||||
local tok = stoks[i]
|
||||
if tok ~= "" then
|
||||
dtoks[j], dinfos[j], dtoklns[j] = tok, sinfos[i], stoklns[i]
|
||||
j = j + 1
|
||||
end
|
||||
end
|
||||
stoks, sinfos, stoklns = dtoks, dinfos, dtoklns
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- number optimization
|
||||
-- * optimization using string formatting functions is one way of doing
|
||||
-- this, but here, we consider all cases and handle them separately
|
||||
-- (possibly an idiotic approach...)
|
||||
-- * scientific notation being generated is not in canonical form, this
|
||||
-- may or may not be a bad thing, feedback welcome
|
||||
-- * note: intermediate portions need to fit into a normal number range
|
||||
-- * optimizations can be divided based on number patterns:
|
||||
-- * hexadecimal:
|
||||
-- (1) no need to remove leading zeros, just skip to (2)
|
||||
-- (2) convert to integer if size equal or smaller
|
||||
-- * change if equal size -> lose the 'x' to reduce entropy
|
||||
-- (3) number is then processed as an integer
|
||||
-- (4) note: does not make 0[xX] consistent
|
||||
-- * integer:
|
||||
-- (1) note: includes anything with trailing ".", ".0", ...
|
||||
-- (2) remove useless fractional part, if present, e.g. 123.000
|
||||
-- (3) remove leading zeros, e.g. 000123
|
||||
-- (4) switch to scientific if shorter, e.g. 123000 -> 123e3
|
||||
-- * with fraction:
|
||||
-- (1) split into digits dot digits
|
||||
-- (2) if no integer portion, take as zero (can omit later)
|
||||
-- (3) handle degenerate .000 case, after which the fractional part
|
||||
-- must be non-zero (if zero, it's matched as an integer)
|
||||
-- (4) remove trailing zeros for fractional portion
|
||||
-- (5) p.q where p > 0 and q > 0 cannot be shortened any more
|
||||
-- (6) otherwise p == 0 and the form is .q, e.g. .000123
|
||||
-- (7) if scientific shorter, convert, e.g. .000123 -> 123e-6
|
||||
-- * scientific:
|
||||
-- (1) split into (digits dot digits) [eE] ([+-] digits)
|
||||
-- (2) if significand has ".", shift it out so it becomes an integer
|
||||
-- (3) if significand is zero, just use zero
|
||||
-- (4) remove leading zeros for significand
|
||||
-- (5) shift out trailing zeros for significand
|
||||
-- (6) examine exponent and determine which format is best:
|
||||
-- integer, with fraction, scientific
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function do_number(i)
|
||||
local before = sinfos[i] -- 'before'
|
||||
local z = before -- working representation
|
||||
local y -- 'after', if better
|
||||
--------------------------------------------------------------------
|
||||
if match(z, "^0[xX]") then -- hexadecimal number
|
||||
local v = tostring(tonumber(z))
|
||||
if #v <= #z then
|
||||
z = v -- change to integer, AND continue
|
||||
else
|
||||
return -- no change; stick to hex
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
if match(z, "^%d+%.?0*$") then -- integer or has useless frac
|
||||
z = match(z, "^(%d+)%.?0*$") -- int portion only
|
||||
if z + 0 > 0 then
|
||||
z = match(z, "^0*([1-9]%d*)$") -- remove leading zeros
|
||||
local v = #match(z, "0*$")
|
||||
local nv = tostring(v)
|
||||
if v > #nv + 1 then -- scientific is shorter
|
||||
z = sub(z, 1, #z - v).."e"..nv
|
||||
end
|
||||
y = z
|
||||
else
|
||||
y = "0" -- basic zero
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
elseif not match(z, "[eE]") then -- number with fraction part
|
||||
local p, q = match(z, "^(%d*)%.(%d+)$") -- split
|
||||
if p == "" then p = 0 end -- int part zero
|
||||
if q + 0 == 0 and p == 0 then
|
||||
y = "0" -- degenerate .000 case
|
||||
else
|
||||
-- now, q > 0 holds and p is a number
|
||||
local v = #match(q, "0*$") -- remove trailing zeros
|
||||
if v > 0 then
|
||||
q = sub(q, 1, #q - v)
|
||||
end
|
||||
-- if p > 0, nothing else we can do to simplify p.q case
|
||||
if p + 0 > 0 then
|
||||
y = p.."."..q
|
||||
else
|
||||
y = "."..q -- tentative, e.g. .000123
|
||||
local v = #match(q, "^0*") -- # leading spaces
|
||||
local w = #q - v -- # significant digits
|
||||
local nv = tostring(#q)
|
||||
-- e.g. compare 123e-6 versus .000123
|
||||
if w + 2 + #nv < 1 + #q then
|
||||
y = sub(q, -w).."e-"..nv
|
||||
end
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
else -- scientific number
|
||||
local sig, ex = match(z, "^([^eE]+)[eE]([%+%-]?%d+)$")
|
||||
ex = tonumber(ex)
|
||||
-- if got ".", shift out fractional portion of significand
|
||||
local p, q = match(sig, "^(%d*)%.(%d*)$")
|
||||
if p then
|
||||
ex = ex - #q
|
||||
sig = p..q
|
||||
end
|
||||
if sig + 0 == 0 then
|
||||
y = "0" -- basic zero
|
||||
else
|
||||
local v = #match(sig, "^0*") -- remove leading zeros
|
||||
sig = sub(sig, v + 1)
|
||||
v = #match(sig, "0*$") -- shift out trailing zeros
|
||||
if v > 0 then
|
||||
sig = sub(sig, 1, #sig - v)
|
||||
ex = ex + v
|
||||
end
|
||||
-- examine exponent and determine which format is best
|
||||
local nex = tostring(ex)
|
||||
if ex == 0 then -- it's just an integer
|
||||
y = sig
|
||||
elseif ex > 0 and (ex <= 1 + #nex) then -- a number
|
||||
y = sig..rep("0", ex)
|
||||
elseif ex < 0 and (ex >= -#sig) then -- fraction, e.g. .123
|
||||
v = #sig + ex
|
||||
y = sub(sig, 1, v).."."..sub(sig, v + 1)
|
||||
elseif ex < 0 and (#nex >= -ex - #sig) then
|
||||
-- e.g. compare 1234e-5 versus .01234
|
||||
-- gives: #sig + 1 + #nex >= 1 + (-ex - #sig) + #sig
|
||||
-- -> #nex >= -ex - #sig
|
||||
v = -ex - #sig
|
||||
y = "."..rep("0", v)..sig
|
||||
else -- non-canonical scientific representation
|
||||
y = sig.."e"..ex
|
||||
end
|
||||
end--if sig
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
if y and y ~= sinfos[i] then
|
||||
if opt_details then
|
||||
print("<number> (line "..stoklns[i]..") "..sinfos[i].." -> "..y)
|
||||
opt_details = opt_details + 1
|
||||
end
|
||||
sinfos[i] = y
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- string optimization
|
||||
-- * note: works on well-formed strings only!
|
||||
-- * optimizations on characters can be summarized as follows:
|
||||
-- \a\b\f\n\r\t\v -- no change
|
||||
-- \\ -- no change
|
||||
-- \"\' -- depends on delim, other can remove \
|
||||
-- \[\] -- remove \
|
||||
-- \<char> -- general escape, remove \
|
||||
-- \<eol> -- normalize the EOL only
|
||||
-- \ddd -- if \a\b\f\n\r\t\v, change to latter
|
||||
-- if other < ascii 32, keep ddd but zap leading zeros
|
||||
-- if >= ascii 32, translate it into the literal, then also
|
||||
-- do escapes for \\,\",\' cases
|
||||
-- <other> -- no change
|
||||
-- * switch delimiters if string becomes shorter
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function do_string(I)
|
||||
local info = sinfos[I]
|
||||
local delim = sub(info, 1, 1) -- delimiter used
|
||||
local ndelim = (delim == "'") and '"' or "'" -- opposite " <-> '
|
||||
local z = sub(info, 2, -2) -- actual string
|
||||
local i = 1
|
||||
local c_delim, c_ndelim = 0, 0 -- "/' counts
|
||||
--------------------------------------------------------------------
|
||||
while i <= #z do
|
||||
local c = sub(z, i, i)
|
||||
----------------------------------------------------------------
|
||||
if c == "\\" then -- escaped stuff
|
||||
local j = i + 1
|
||||
local d = sub(z, j, j)
|
||||
local p = find("abfnrtv\\\n\r\"\'0123456789", d, 1, true)
|
||||
------------------------------------------------------------
|
||||
if not p then -- \<char> -- remove \
|
||||
z = sub(z, 1, i - 1)..sub(z, j)
|
||||
i = i + 1
|
||||
------------------------------------------------------------
|
||||
elseif p <= 8 then -- \a\b\f\n\r\t\v\\
|
||||
i = i + 2 -- no change
|
||||
------------------------------------------------------------
|
||||
elseif p <= 10 then -- \<eol> -- normalize EOL
|
||||
local eol = sub(z, j, j + 1)
|
||||
if eol == "\r\n" or eol == "\n\r" then
|
||||
z = sub(z, 1, i).."\n"..sub(z, j + 2)
|
||||
elseif p == 10 then -- \r case
|
||||
z = sub(z, 1, i).."\n"..sub(z, j + 1)
|
||||
end
|
||||
i = i + 2
|
||||
------------------------------------------------------------
|
||||
elseif p <= 12 then -- \"\' -- remove \ for ndelim
|
||||
if d == delim then
|
||||
c_delim = c_delim + 1
|
||||
i = i + 2
|
||||
else
|
||||
c_ndelim = c_ndelim + 1
|
||||
z = sub(z, 1, i - 1)..sub(z, j)
|
||||
i = i + 1
|
||||
end
|
||||
------------------------------------------------------------
|
||||
else -- \ddd -- various steps
|
||||
local s = match(z, "^(%d%d?%d?)", j)
|
||||
j = i + 1 + #s -- skip to location
|
||||
local cv = s + 0
|
||||
local cc = string.char(cv)
|
||||
local p = find("\a\b\f\n\r\t\v", cc, 1, true)
|
||||
if p then -- special escapes
|
||||
s = "\\"..sub("abfnrtv", p, p)
|
||||
elseif cv < 32 then -- normalized \ddd
|
||||
s = "\\"..cv
|
||||
elseif cc == delim then -- \<delim>
|
||||
s = "\\"..cc
|
||||
c_delim = c_delim + 1
|
||||
elseif cc == "\\" then -- \\
|
||||
s = "\\\\"
|
||||
else -- literal character
|
||||
s = cc
|
||||
if cc == ndelim then
|
||||
c_ndelim = c_ndelim + 1
|
||||
end
|
||||
end
|
||||
z = sub(z, 1, i - 1)..s..sub(z, j)
|
||||
i = i + #s
|
||||
------------------------------------------------------------
|
||||
end--if p
|
||||
----------------------------------------------------------------
|
||||
else-- c ~= "\\" -- <other> -- no change
|
||||
i = i + 1
|
||||
if c == ndelim then -- count ndelim, for switching delimiters
|
||||
c_ndelim = c_ndelim + 1
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
end--if c
|
||||
end--while
|
||||
--------------------------------------------------------------------
|
||||
-- switching delimiters, a long-winded derivation:
|
||||
-- (1) delim takes 2+2*c_delim bytes, ndelim takes c_ndelim bytes
|
||||
-- (2) delim becomes c_delim bytes, ndelim becomes 2+2*c_ndelim bytes
|
||||
-- simplifying the condition (1)>(2) --> c_delim > c_ndelim
|
||||
if c_delim > c_ndelim then
|
||||
i = 1
|
||||
while i <= #z do
|
||||
local p, q, r = find(z, "([\'\"])", i)
|
||||
if not p then break end
|
||||
if r == delim then -- \<delim> -> <delim>
|
||||
z = sub(z, 1, p - 2)..sub(z, p)
|
||||
i = p
|
||||
else-- r == ndelim -- <ndelim> -> \<ndelim>
|
||||
z = sub(z, 1, p - 1).."\\"..sub(z, p)
|
||||
i = p + 2
|
||||
end
|
||||
end--while
|
||||
delim = ndelim -- actually change delimiters
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
z = delim..z..delim
|
||||
if z ~= sinfos[I] then
|
||||
if opt_details then
|
||||
print("<string> (line "..stoklns[I]..") "..sinfos[I].." -> "..z)
|
||||
opt_details = opt_details + 1
|
||||
end
|
||||
sinfos[I] = z
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- long string optimization
|
||||
-- * note: warning flagged if trailing whitespace found, not trimmed
|
||||
-- * remove first optional newline
|
||||
-- * normalize embedded newlines
|
||||
-- * reduce '=' separators in delimiters if possible
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function do_lstring(I)
|
||||
local info = sinfos[I]
|
||||
local delim1 = match(info, "^%[=*%[") -- cut out delimiters
|
||||
local sep = #delim1
|
||||
local delim2 = sub(info, -sep, -1)
|
||||
local z = sub(info, sep + 1, -(sep + 1)) -- lstring without delims
|
||||
local y = ""
|
||||
local i = 1
|
||||
--------------------------------------------------------------------
|
||||
while true do
|
||||
local p, q, r, s = find(z, "([\r\n])([\r\n]?)", i)
|
||||
-- deal with a single line
|
||||
local ln
|
||||
if not p then
|
||||
ln = sub(z, i)
|
||||
elseif p >= i then
|
||||
ln = sub(z, i, p - 1)
|
||||
end
|
||||
if ln ~= "" then
|
||||
-- flag a warning if there are trailing spaces, won't base.optimize!
|
||||
if match(ln, "%s+$") then
|
||||
warn.lstring = "trailing whitespace in long string near line "..stoklns[I]
|
||||
end
|
||||
y = y..ln
|
||||
end
|
||||
if not p then -- done if no more EOLs
|
||||
break
|
||||
end
|
||||
-- deal with line endings, normalize them
|
||||
i = p + 1
|
||||
if p then
|
||||
if #s > 0 and r ~= s then -- skip CRLF or LFCR
|
||||
i = i + 1
|
||||
end
|
||||
-- skip first newline, which can be safely deleted
|
||||
if not(i == 1 and i == p) then
|
||||
y = y.."\n"
|
||||
end
|
||||
end
|
||||
end--while
|
||||
--------------------------------------------------------------------
|
||||
-- handle possible deletion of one or more '=' separators
|
||||
if sep >= 3 then
|
||||
local chk, okay = sep - 1
|
||||
-- loop to test ending delimiter with less of '=' down to zero
|
||||
while chk >= 2 do
|
||||
local delim = "%]"..rep("=", chk - 2).."%]"
|
||||
if not match(y, delim) then okay = chk end
|
||||
chk = chk - 1
|
||||
end
|
||||
if okay then -- change delimiters
|
||||
sep = rep("=", okay - 2)
|
||||
delim1, delim2 = "["..sep.."[", "]"..sep.."]"
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
sinfos[I] = delim1..y..delim2
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- long comment optimization
|
||||
-- * note: does not remove first optional newline
|
||||
-- * trim trailing whitespace
|
||||
-- * normalize embedded newlines
|
||||
-- * reduce '=' separators in delimiters if possible
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function do_lcomment(I)
|
||||
local info = sinfos[I]
|
||||
local delim1 = match(info, "^%-%-%[=*%[") -- cut out delimiters
|
||||
local sep = #delim1
|
||||
local delim2 = sub(info, -sep, -1)
|
||||
local z = sub(info, sep + 1, -(sep - 1)) -- comment without delims
|
||||
local y = ""
|
||||
local i = 1
|
||||
--------------------------------------------------------------------
|
||||
while true do
|
||||
local p, q, r, s = find(z, "([\r\n])([\r\n]?)", i)
|
||||
-- deal with a single line, extract and check trailing whitespace
|
||||
local ln
|
||||
if not p then
|
||||
ln = sub(z, i)
|
||||
elseif p >= i then
|
||||
ln = sub(z, i, p - 1)
|
||||
end
|
||||
if ln ~= "" then
|
||||
-- trim trailing whitespace if non-empty line
|
||||
local ws = match(ln, "%s*$")
|
||||
if #ws > 0 then ln = sub(ln, 1, -(ws + 1)) end
|
||||
y = y..ln
|
||||
end
|
||||
if not p then -- done if no more EOLs
|
||||
break
|
||||
end
|
||||
-- deal with line endings, normalize them
|
||||
i = p + 1
|
||||
if p then
|
||||
if #s > 0 and r ~= s then -- skip CRLF or LFCR
|
||||
i = i + 1
|
||||
end
|
||||
y = y.."\n"
|
||||
end
|
||||
end--while
|
||||
--------------------------------------------------------------------
|
||||
-- handle possible deletion of one or more '=' separators
|
||||
sep = sep - 2
|
||||
if sep >= 3 then
|
||||
local chk, okay = sep - 1
|
||||
-- loop to test ending delimiter with less of '=' down to zero
|
||||
while chk >= 2 do
|
||||
local delim = "%]"..rep("=", chk - 2).."%]"
|
||||
if not match(y, delim) then okay = chk end
|
||||
chk = chk - 1
|
||||
end
|
||||
if okay then -- change delimiters
|
||||
sep = rep("=", okay - 2)
|
||||
delim1, delim2 = "--["..sep.."[", "]"..sep.."]"
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
sinfos[I] = delim1..y..delim2
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- short comment optimization
|
||||
-- * trim trailing whitespace
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function do_comment(i)
|
||||
local info = sinfos[i]
|
||||
local ws = match(info, "%s*$") -- just look from end of string
|
||||
if #ws > 0 then
|
||||
info = sub(info, 1, -(ws + 1)) -- trim trailing whitespace
|
||||
end
|
||||
sinfos[i] = info
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- returns true if string found in long comment
|
||||
-- * this is a feature to keep copyright or license texts
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function keep_lcomment(opt_keep, info)
|
||||
if not opt_keep then return false end -- option not set
|
||||
local delim1 = match(info, "^%-%-%[=*%[") -- cut out delimiters
|
||||
local sep = #delim1
|
||||
local delim2 = sub(info, -sep, -1)
|
||||
local z = sub(info, sep + 1, -(sep - 1)) -- comment without delims
|
||||
if find(z, opt_keep, 1, true) then -- try to match
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- main entry point
|
||||
-- * currently, lexer processing has 2 passes
|
||||
-- * processing is done on a line-oriented basis, which is easier to
|
||||
-- grok due to the next point...
|
||||
-- * since there are various options that can be enabled or disabled,
|
||||
-- processing is a little messy or convoluted
|
||||
------------------------------------------------------------------------
|
||||
|
||||
function base.optimize(option, toklist, semlist, toklnlist)
|
||||
--------------------------------------------------------------------
|
||||
-- set option flags
|
||||
--------------------------------------------------------------------
|
||||
local opt_comments = option["opt-comments"]
|
||||
local opt_whitespace = option["opt-whitespace"]
|
||||
local opt_emptylines = option["opt-emptylines"]
|
||||
local opt_eols = option["opt-eols"]
|
||||
local opt_strings = option["opt-strings"]
|
||||
local opt_numbers = option["opt-numbers"]
|
||||
local opt_keep = option.KEEP
|
||||
opt_details = option.DETAILS and 0 -- upvalues for details display
|
||||
print = print or base.print
|
||||
if opt_eols then -- forced settings, otherwise won't work properly
|
||||
opt_comments = true
|
||||
opt_whitespace = true
|
||||
opt_emptylines = true
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- variable initialization
|
||||
--------------------------------------------------------------------
|
||||
stoks, sinfos, stoklns -- set source lists
|
||||
= toklist, semlist, toklnlist
|
||||
local i = 1 -- token position
|
||||
local tok, info -- current token
|
||||
local prev -- position of last grammar token
|
||||
-- on same line (for TK_SPACE stuff)
|
||||
--------------------------------------------------------------------
|
||||
-- changes a token, info pair
|
||||
--------------------------------------------------------------------
|
||||
local function settoken(tok, info, I)
|
||||
I = I or i
|
||||
stoks[I] = tok or ""
|
||||
sinfos[I] = info or ""
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- processing loop (PASS 1)
|
||||
--------------------------------------------------------------------
|
||||
while true do
|
||||
tok, info = stoks[i], sinfos[i]
|
||||
----------------------------------------------------------------
|
||||
local atstart = atlinestart(i) -- set line begin flag
|
||||
if atstart then prev = nil end
|
||||
----------------------------------------------------------------
|
||||
if tok == "TK_EOS" then -- end of stream/pass
|
||||
break
|
||||
----------------------------------------------------------------
|
||||
elseif tok == "TK_KEYWORD" or -- keywords, identifiers,
|
||||
tok == "TK_NAME" or -- operators
|
||||
tok == "TK_OP" then
|
||||
-- TK_KEYWORD and TK_OP can't be optimized without a big
|
||||
-- optimization framework; it would be more of an optimizing
|
||||
-- compiler, not a source code compressor
|
||||
-- TK_NAME that are locals needs parser to analyze/base.optimize
|
||||
prev = i
|
||||
----------------------------------------------------------------
|
||||
elseif tok == "TK_NUMBER" then -- numbers
|
||||
if opt_numbers then
|
||||
do_number(i) -- base.optimize
|
||||
end
|
||||
prev = i
|
||||
----------------------------------------------------------------
|
||||
elseif tok == "TK_STRING" or -- strings, long strings
|
||||
tok == "TK_LSTRING" then
|
||||
if opt_strings then
|
||||
if tok == "TK_STRING" then
|
||||
do_string(i) -- base.optimize
|
||||
else
|
||||
do_lstring(i) -- base.optimize
|
||||
end
|
||||
end
|
||||
prev = i
|
||||
----------------------------------------------------------------
|
||||
elseif tok == "TK_COMMENT" then -- short comments
|
||||
if opt_comments then
|
||||
if i == 1 and sub(info, 1, 1) == "#" then
|
||||
-- keep shbang comment, trim whitespace
|
||||
do_comment(i)
|
||||
else
|
||||
-- safe to delete, as a TK_EOL (or TK_EOS) always follows
|
||||
settoken() -- remove entirely
|
||||
end
|
||||
elseif opt_whitespace then -- trim whitespace only
|
||||
do_comment(i)
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
elseif tok == "TK_LCOMMENT" then -- long comments
|
||||
if keep_lcomment(opt_keep, info) then
|
||||
------------------------------------------------------------
|
||||
-- if --keep, we keep a long comment if <msg> is found;
|
||||
-- this is a feature to keep copyright or license texts
|
||||
if opt_whitespace then -- trim whitespace only
|
||||
do_lcomment(i)
|
||||
end
|
||||
prev = i
|
||||
elseif opt_comments then
|
||||
local eols = commenteols(info)
|
||||
------------------------------------------------------------
|
||||
-- prepare opt_emptylines case first, if a disposable token
|
||||
-- follows, current one is safe to dump, else keep a space;
|
||||
-- it is implied that the operation is safe for '-', because
|
||||
-- current is a TK_LCOMMENT, and must be separate from a '-'
|
||||
if is_faketoken[stoks[i + 1]] then
|
||||
settoken() -- remove entirely
|
||||
tok = ""
|
||||
else
|
||||
settoken("TK_SPACE", " ")
|
||||
end
|
||||
------------------------------------------------------------
|
||||
-- if there are embedded EOLs to keep and opt_emptylines is
|
||||
-- disabled, then switch the token into one or more EOLs
|
||||
if not opt_emptylines and eols > 0 then
|
||||
settoken("TK_EOL", rep("\n", eols))
|
||||
end
|
||||
------------------------------------------------------------
|
||||
-- if optimizing whitespaces, force reinterpretation of the
|
||||
-- token to give a chance for the space to be optimized away
|
||||
if opt_whitespace and tok ~= "" then
|
||||
i = i - 1 -- to reinterpret
|
||||
end
|
||||
------------------------------------------------------------
|
||||
else -- disabled case
|
||||
if opt_whitespace then -- trim whitespace only
|
||||
do_lcomment(i)
|
||||
end
|
||||
prev = i
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
elseif tok == "TK_EOL" then -- line endings
|
||||
if atstart and opt_emptylines then
|
||||
settoken() -- remove entirely
|
||||
elseif info == "\r\n" or info == "\n\r" then
|
||||
-- normalize the rest of the EOLs for CRLF/LFCR only
|
||||
-- (note that TK_LCOMMENT can change into several EOLs)
|
||||
settoken("TK_EOL", "\n")
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
elseif tok == "TK_SPACE" then -- whitespace
|
||||
if opt_whitespace then
|
||||
if atstart or atlineend(i) then
|
||||
-- delete leading and trailing whitespace
|
||||
settoken() -- remove entirely
|
||||
else
|
||||
------------------------------------------------------------
|
||||
-- at this point, since leading whitespace have been removed,
|
||||
-- there should be a either a real token or a TK_LCOMMENT
|
||||
-- prior to hitting this whitespace; the TK_LCOMMENT case
|
||||
-- only happens if opt_comments is disabled; so prev ~= nil
|
||||
local ptok = stoks[prev]
|
||||
if ptok == "TK_LCOMMENT" then
|
||||
-- previous TK_LCOMMENT can abut with anything
|
||||
settoken() -- remove entirely
|
||||
else
|
||||
-- prev must be a grammar token; consecutive TK_SPACE
|
||||
-- tokens is impossible when optimizing whitespace
|
||||
local ntok = stoks[i + 1]
|
||||
if is_faketoken[ntok] then
|
||||
-- handle special case where a '-' cannot abut with
|
||||
-- either a short comment or a long comment
|
||||
if (ntok == "TK_COMMENT" or ntok == "TK_LCOMMENT") and
|
||||
ptok == "TK_OP" and sinfos[prev] == "-" then
|
||||
-- keep token
|
||||
else
|
||||
settoken() -- remove entirely
|
||||
end
|
||||
else--is_realtoken
|
||||
-- check a pair of grammar tokens, if can abut, then
|
||||
-- delete space token entirely, otherwise keep one space
|
||||
local s = checkpair(prev, i + 1)
|
||||
if s == "" then
|
||||
settoken() -- remove entirely
|
||||
else
|
||||
settoken("TK_SPACE", " ")
|
||||
end
|
||||
end
|
||||
end
|
||||
------------------------------------------------------------
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
else
|
||||
error("unidentified token encountered")
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
i = i + 1
|
||||
end--while
|
||||
repack_tokens()
|
||||
--------------------------------------------------------------------
|
||||
-- processing loop (PASS 2)
|
||||
--------------------------------------------------------------------
|
||||
if opt_eols then
|
||||
i = 1
|
||||
-- aggressive EOL removal only works with most non-grammar tokens
|
||||
-- optimized away because it is a rather simple scheme -- basically
|
||||
-- it just checks 'real' token pairs around EOLs
|
||||
if stoks[1] == "TK_COMMENT" then
|
||||
-- first comment still existing must be shbang, skip whole line
|
||||
i = 3
|
||||
end
|
||||
while true do
|
||||
tok, info = stoks[i], sinfos[i]
|
||||
--------------------------------------------------------------
|
||||
if tok == "TK_EOS" then -- end of stream/pass
|
||||
break
|
||||
--------------------------------------------------------------
|
||||
elseif tok == "TK_EOL" then -- consider each TK_EOL
|
||||
local t1, t2 = stoks[i - 1], stoks[i + 1]
|
||||
if is_realtoken[t1] and is_realtoken[t2] then -- sanity check
|
||||
local s = checkpair(i - 1, i + 1)
|
||||
if s == "" then
|
||||
settoken() -- remove entirely
|
||||
end
|
||||
end
|
||||
end--if tok
|
||||
--------------------------------------------------------------
|
||||
i = i + 1
|
||||
end--while
|
||||
repack_tokens()
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
if opt_details and opt_details > 0 then print() end -- spacing
|
||||
return stoks, sinfos, stoklns
|
||||
end
|
||||
|
||||
return base
|
||||
566
AIO_Server/Dep_LuaSrcDiet/optparser.lua
Normal file
566
AIO_Server/Dep_LuaSrcDiet/optparser.lua
Normal file
@@ -0,0 +1,566 @@
|
||||
--[[--------------------------------------------------------------------
|
||||
|
||||
optparser.lua: does parser-based optimizations
|
||||
This file is part of LuaSrcDiet.
|
||||
|
||||
Copyright (c) 2008 Kein-Hong Man <khman@users.sf.net>
|
||||
The COPYRIGHT file describes the conditions
|
||||
under which this software may be distributed.
|
||||
|
||||
See the ChangeLog for more information.
|
||||
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- NOTES:
|
||||
-- * For more parser-based optimization ideas, see the TODO items or
|
||||
-- look at technotes.txt.
|
||||
-- * The processing load is quite significant, but since this is an
|
||||
-- off-line text processor, I believe we can wait a few seconds.
|
||||
-- * TODO: might process "local a,a,a" wrongly... need tests!
|
||||
-- * TODO: remove position handling if overlapped locals (rem < 0)
|
||||
-- needs more study, to check behaviour
|
||||
-- * TODO: there are probably better ways to do allocation, e.g. by
|
||||
-- choosing better methods to sort and pick locals...
|
||||
-- * TODO: we don't need 53*63 two-letter identifiers; we can make
|
||||
-- do with significantly less depending on how many that are really
|
||||
-- needed and improve entropy; e.g. 13 needed -> choose 4*4 instead
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
local base = {}
|
||||
-- local base = _G
|
||||
-- local string = require "string"
|
||||
-- local table = require "table"
|
||||
-- module "optparser"
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Letter frequencies for reducing symbol entropy (fixed version)
|
||||
-- * Might help a wee bit when the output file is compressed
|
||||
-- * See Wikipedia: http://en.wikipedia.org/wiki/Letter_frequencies
|
||||
-- * We use letter frequencies according to a Linotype keyboard, plus
|
||||
-- the underscore, and both lower case and upper case letters.
|
||||
-- * The arrangement below (LC, underscore, %d, UC) is arbitrary.
|
||||
-- * This is certainly not optimal, but is quick-and-dirty and the
|
||||
-- process has no significant overhead
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local LETTERS = "etaoinshrdlucmfwypvbgkqjxz_ETAOINSHRDLUCMFWYPVBGKQJXZ"
|
||||
local ALPHANUM = "etaoinshrdlucmfwypvbgkqjxz_0123456789ETAOINSHRDLUCMFWYPVBGKQJXZ"
|
||||
|
||||
-- names or identifiers that must be skipped
|
||||
-- * the first two lines are for keywords
|
||||
local SKIP_NAME = {}
|
||||
for v in string.gmatch([[
|
||||
and break do else elseif end false for function if in
|
||||
local nil not or repeat return then true until while
|
||||
self]], "%S+") do
|
||||
SKIP_NAME[v] = true
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- variables and data structures
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local toklist, seminfolist, -- token lists
|
||||
globalinfo, localinfo, -- variable information tables
|
||||
globaluniq, localuniq, -- unique name tables
|
||||
var_new, -- index of new variable names
|
||||
varlist -- list of output variables
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- preprocess information table to get lists of unique names
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function preprocess(infotable)
|
||||
local uniqtable = {}
|
||||
for i = 1, #infotable do -- enumerate info table
|
||||
local obj = infotable[i]
|
||||
local name = obj.name
|
||||
--------------------------------------------------------------------
|
||||
if not uniqtable[name] then -- not found, start an entry
|
||||
uniqtable[name] = {
|
||||
decl = 0, token = 0, size = 0,
|
||||
}
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
local uniq = uniqtable[name] -- count declarations, tokens, size
|
||||
uniq.decl = uniq.decl + 1
|
||||
local xref = obj.xref
|
||||
local xcount = #xref
|
||||
uniq.token = uniq.token + xcount
|
||||
uniq.size = uniq.size + xcount * #name
|
||||
--------------------------------------------------------------------
|
||||
if obj.decl then -- if local table, create first,last pairs
|
||||
obj.id = i
|
||||
obj.xcount = xcount
|
||||
if xcount > 1 then -- if ==1, means local never accessed
|
||||
obj.first = xref[2]
|
||||
obj.last = xref[xcount]
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
else -- if global table, add a back ref
|
||||
uniq.id = i
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
end--for
|
||||
return uniqtable
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- calculate actual symbol frequencies, in order to reduce entropy
|
||||
-- * this may help further reduce the size of compressed sources
|
||||
-- * note that since parsing optimizations is put before lexing
|
||||
-- optimizations, the frequency table is not exact!
|
||||
-- * yes, this will miss --keep block comments too...
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function recalc_for_entropy(option)
|
||||
local byte = string.byte
|
||||
local char = string.char
|
||||
-- table of token classes to accept in calculating symbol frequency
|
||||
local ACCEPT = {
|
||||
TK_KEYWORD = true, TK_NAME = true, TK_NUMBER = true,
|
||||
TK_STRING = true, TK_LSTRING = true,
|
||||
}
|
||||
if not option["opt-comments"] then
|
||||
ACCEPT.TK_COMMENT = true
|
||||
ACCEPT.TK_LCOMMENT = true
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- create a new table and remove any original locals by filtering
|
||||
--------------------------------------------------------------------
|
||||
local filtered = {}
|
||||
for i = 1, #toklist do
|
||||
filtered[i] = seminfolist[i]
|
||||
end
|
||||
for i = 1, #localinfo do -- enumerate local info table
|
||||
local obj = localinfo[i]
|
||||
local xref = obj.xref
|
||||
for j = 1, obj.xcount do
|
||||
local p = xref[j]
|
||||
filtered[p] = "" -- remove locals
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
local freq = {} -- reset symbol frequency table
|
||||
for i = 0, 255 do freq[i] = 0 end
|
||||
for i = 1, #toklist do -- gather symbol frequency
|
||||
local tok, info = toklist[i], filtered[i]
|
||||
if ACCEPT[tok] then
|
||||
for j = 1, #info do
|
||||
local c = byte(info, j)
|
||||
freq[c] = freq[c] + 1
|
||||
end
|
||||
end--if
|
||||
end--for
|
||||
--------------------------------------------------------------------
|
||||
-- function to re-sort symbols according to actual frequencies
|
||||
--------------------------------------------------------------------
|
||||
local function resort(symbols)
|
||||
local symlist = {}
|
||||
for i = 1, #symbols do -- prepare table to sort
|
||||
local c = byte(symbols, i)
|
||||
symlist[i] = { c = c, freq = freq[c], }
|
||||
end
|
||||
table.sort(symlist, -- sort selected symbols
|
||||
function(v1, v2)
|
||||
return v1.freq > v2.freq
|
||||
end
|
||||
)
|
||||
local charlist = {} -- reconstitute the string
|
||||
for i = 1, #symlist do
|
||||
charlist[i] = char(symlist[i].c)
|
||||
end
|
||||
return table.concat(charlist)
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
LETTERS = resort(LETTERS) -- change letter arrangement
|
||||
ALPHANUM = resort(ALPHANUM)
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- returns a string containing a new local variable name to use, and
|
||||
-- a flag indicating whether it collides with a global variable
|
||||
-- * trapping keywords and other names like 'self' is done elsewhere
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function new_var_name()
|
||||
local var
|
||||
local cletters, calphanum = #LETTERS, #ALPHANUM
|
||||
local v = var_new
|
||||
if v < cletters then -- single char
|
||||
v = v + 1
|
||||
var = string.sub(LETTERS, v, v)
|
||||
else -- longer names
|
||||
local range, sz = cletters, 1 -- calculate # chars fit
|
||||
repeat
|
||||
v = v - range
|
||||
range = range * calphanum
|
||||
sz = sz + 1
|
||||
until range > v
|
||||
local n = v % cletters -- left side cycles faster
|
||||
v = (v - n) / cletters -- do first char first
|
||||
n = n + 1
|
||||
var = string.sub(LETTERS, n, n)
|
||||
while sz > 1 do
|
||||
local m = v % calphanum
|
||||
v = (v - m) / calphanum
|
||||
m = m + 1
|
||||
var = var..string.sub(ALPHANUM, m, m)
|
||||
sz = sz - 1
|
||||
end
|
||||
end
|
||||
var_new = var_new + 1
|
||||
return var, globaluniq[var] ~= nil
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- calculate and print some statistics
|
||||
-- * probably better in main source, put here for now
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function stats_summary(globaluniq, localuniq, afteruniq, option)
|
||||
local fmt = string.format
|
||||
local opt_details = option.DETAILS
|
||||
local uniq_g , uniq_li, uniq_lo, uniq_ti, uniq_to, -- stats needed
|
||||
decl_g, decl_li, decl_lo, decl_ti, decl_to,
|
||||
token_g, token_li, token_lo, token_ti, token_to,
|
||||
size_g, size_li, size_lo, size_ti, size_to
|
||||
= 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
local function avg(c, l) -- safe average function
|
||||
if c == 0 then return 0 end
|
||||
return l / c
|
||||
end
|
||||
--------------------------------------------------------------------
|
||||
-- collect statistics (note: globals do not have declarations!)
|
||||
--------------------------------------------------------------------
|
||||
for name, uniq in pairs(globaluniq) do
|
||||
uniq_g = uniq_g + 1
|
||||
token_g = token_g + uniq.token
|
||||
size_g = size_g + uniq.size
|
||||
end
|
||||
for name, uniq in pairs(localuniq) do
|
||||
uniq_li = uniq_li + 1
|
||||
decl_li = decl_li + uniq.decl
|
||||
token_li = token_li + uniq.token
|
||||
size_li = size_li + uniq.size
|
||||
end
|
||||
for name, uniq in pairs(afteruniq) do
|
||||
uniq_lo = uniq_lo + 1
|
||||
decl_lo = decl_lo + uniq.decl
|
||||
token_lo = token_lo + uniq.token
|
||||
size_lo = size_lo + uniq.size
|
||||
end
|
||||
uniq_ti = uniq_g + uniq_li
|
||||
decl_ti = decl_g + decl_li
|
||||
token_ti = token_g + token_li
|
||||
size_ti = size_g + size_li
|
||||
uniq_to = uniq_g + uniq_lo
|
||||
decl_to = decl_g + decl_lo
|
||||
token_to = token_g + token_lo
|
||||
size_to = size_g + size_lo
|
||||
--------------------------------------------------------------------
|
||||
-- detailed stats: global list
|
||||
--------------------------------------------------------------------
|
||||
if opt_details then
|
||||
local sorted = {} -- sort table of unique global names by size
|
||||
for name, uniq in pairs(globaluniq) do
|
||||
uniq.name = name
|
||||
sorted[#sorted + 1] = uniq
|
||||
end
|
||||
table.sort(sorted,
|
||||
function(v1, v2)
|
||||
return v1.size > v2.size
|
||||
end
|
||||
)
|
||||
local tabf1, tabf2 = "%8s%8s%10s %s", "%8d%8d%10.2f %s"
|
||||
local hl = string.rep("-", 44)
|
||||
print("*** global variable list (sorted by size) ***\n"..hl)
|
||||
print(fmt(tabf1, "Token", "Input", "Input", "Global"))
|
||||
print(fmt(tabf1, "Count", "Bytes", "Average", "Name"))
|
||||
print(hl)
|
||||
for i = 1, #sorted do
|
||||
local uniq = sorted[i]
|
||||
print(fmt(tabf2, uniq.token, uniq.size, avg(uniq.token, uniq.size), uniq.name))
|
||||
end
|
||||
print(hl)
|
||||
print(fmt(tabf2, token_g, size_g, avg(token_g, size_g), "TOTAL"))
|
||||
print(hl.."\n")
|
||||
--------------------------------------------------------------------
|
||||
-- detailed stats: local list
|
||||
--------------------------------------------------------------------
|
||||
local tabf1, tabf2 = "%8s%8s%8s%10s%8s%10s %s", "%8d%8d%8d%10.2f%8d%10.2f %s"
|
||||
local hl = string.rep("-", 70)
|
||||
print("*** local variable list (sorted by allocation order) ***\n"..hl)
|
||||
print(fmt(tabf1, "Decl.", "Token", "Input", "Input", "Output", "Output", "Global"))
|
||||
print(fmt(tabf1, "Count", "Count", "Bytes", "Average", "Bytes", "Average", "Name"))
|
||||
print(hl)
|
||||
for i = 1, #varlist do -- iterate according to order assigned
|
||||
local name = varlist[i]
|
||||
local uniq = afteruniq[name]
|
||||
local old_t, old_s = 0, 0
|
||||
for j = 1, #localinfo do -- find corresponding old names and calculate
|
||||
local obj = localinfo[j]
|
||||
if obj.name == name then
|
||||
old_t = old_t + obj.xcount
|
||||
old_s = old_s + obj.xcount * #obj.oldname
|
||||
end
|
||||
end
|
||||
print(fmt(tabf2, uniq.decl, uniq.token, old_s, avg(old_t, old_s),
|
||||
uniq.size, avg(uniq.token, uniq.size), name))
|
||||
end
|
||||
print(hl)
|
||||
print(fmt(tabf2, decl_lo, token_lo, size_li, avg(token_li, size_li),
|
||||
size_lo, avg(token_lo, size_lo), "TOTAL"))
|
||||
print(hl.."\n")
|
||||
end--if opt_details
|
||||
--------------------------------------------------------------------
|
||||
-- display output
|
||||
--------------------------------------------------------------------
|
||||
-- local tabf1, tabf2 = "%-16s%8s%8s%8s%8s%10s", "%-16s%8d%8d%8d%8d%10.2f"
|
||||
-- local hl = string.rep("-", 58)
|
||||
-- print("*** local variable optimization summary ***\n"..hl)
|
||||
-- print(fmt(tabf1, "Variable", "Unique", "Decl.", "Token", "Size", "Average"))
|
||||
-- print(fmt(tabf1, "Types", "Names", "Count", "Count", "Bytes", "Bytes"))
|
||||
-- print(hl)
|
||||
-- print(fmt(tabf2, "Global", uniq_g, decl_g, token_g, size_g, avg(token_g, size_g)))
|
||||
-- print(hl)
|
||||
-- print(fmt(tabf2, "Local (in)", uniq_li, decl_li, token_li, size_li, avg(token_li, size_li)))
|
||||
-- print(fmt(tabf2, "TOTAL (in)", uniq_ti, decl_ti, token_ti, size_ti, avg(token_ti, size_ti)))
|
||||
-- print(hl)
|
||||
-- print(fmt(tabf2, "Local (out)", uniq_lo, decl_lo, token_lo, size_lo, avg(token_lo, size_lo)))
|
||||
-- print(fmt(tabf2, "TOTAL (out)", uniq_to, decl_to, token_to, size_to, avg(token_to, size_to)))
|
||||
-- print(hl.."\n")
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- main entry point
|
||||
-- * does only local variable optimization for now
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function base.optimize(option, _toklist, _seminfolist, _globalinfo, _localinfo)
|
||||
-- set tables
|
||||
toklist, seminfolist, globalinfo, localinfo
|
||||
= _toklist, _seminfolist, _globalinfo, _localinfo
|
||||
var_new = 0 -- reset variable name allocator
|
||||
varlist = {}
|
||||
------------------------------------------------------------------
|
||||
-- preprocess global/local tables, handle entropy reduction
|
||||
------------------------------------------------------------------
|
||||
globaluniq = preprocess(globalinfo)
|
||||
localuniq = preprocess(localinfo)
|
||||
if option["opt-entropy"] then -- for entropy improvement
|
||||
recalc_for_entropy(option)
|
||||
end
|
||||
------------------------------------------------------------------
|
||||
-- build initial declared object table, then sort according to
|
||||
-- token count, this might help assign more tokens to more common
|
||||
-- variable names such as 'e' thus possibly reducing entropy
|
||||
-- * an object knows its localinfo index via its 'id' field
|
||||
-- * special handling for "self" special local (parameter) here
|
||||
------------------------------------------------------------------
|
||||
local object = {}
|
||||
for i = 1, #localinfo do
|
||||
object[i] = localinfo[i]
|
||||
end
|
||||
table.sort(object, -- sort largest first
|
||||
function(v1, v2)
|
||||
return v1.xcount > v2.xcount
|
||||
end
|
||||
)
|
||||
------------------------------------------------------------------
|
||||
-- the special "self" function parameters must be preserved
|
||||
-- * the allocator below will never use "self", so it is safe to
|
||||
-- keep those implicit declarations as-is
|
||||
------------------------------------------------------------------
|
||||
local temp, j, gotself = {}, 1, false
|
||||
for i = 1, #object do
|
||||
local obj = object[i]
|
||||
if not obj.isself then
|
||||
temp[j] = obj
|
||||
j = j + 1
|
||||
else
|
||||
gotself = true
|
||||
end
|
||||
end
|
||||
object = temp
|
||||
------------------------------------------------------------------
|
||||
-- a simple first-come first-served heuristic name allocator,
|
||||
-- note that this is in no way optimal...
|
||||
-- * each object is a local variable declaration plus existence
|
||||
-- * the aim is to assign short names to as many tokens as possible,
|
||||
-- so the following tries to maximize name reuse
|
||||
-- * note that we preserve sort order
|
||||
------------------------------------------------------------------
|
||||
local nobject = #object
|
||||
while nobject > 0 do
|
||||
local varname, gcollide
|
||||
repeat
|
||||
varname, gcollide = new_var_name() -- collect a variable name
|
||||
until not SKIP_NAME[varname] -- skip all special names
|
||||
varlist[#varlist + 1] = varname -- keep a list
|
||||
local oleft = nobject
|
||||
------------------------------------------------------------------
|
||||
-- if variable name collides with an existing global, the name
|
||||
-- cannot be used by a local when the name is accessed as a global
|
||||
-- during which the local is alive (between 'act' to 'rem'), so
|
||||
-- we drop objects that collides with the corresponding global
|
||||
------------------------------------------------------------------
|
||||
if gcollide then
|
||||
-- find the xref table of the global
|
||||
local gref = globalinfo[globaluniq[varname].id].xref
|
||||
local ngref = #gref
|
||||
-- enumerate for all current objects; all are valid at this point
|
||||
for i = 1, nobject do
|
||||
local obj = object[i]
|
||||
local act, rem = obj.act, obj.rem -- 'live' range of local
|
||||
-- if rem < 0, it is a -id to a local that had the same name
|
||||
-- so follow rem to extend it; does this make sense?
|
||||
while rem < 0 do
|
||||
rem = localinfo[-rem].rem
|
||||
end
|
||||
local drop
|
||||
for j = 1, ngref do
|
||||
local p = gref[j]
|
||||
if p >= act and p <= rem then drop = true end -- in range?
|
||||
end
|
||||
if drop then
|
||||
obj.skip = true
|
||||
oleft = oleft - 1
|
||||
end
|
||||
end--for
|
||||
end--if gcollide
|
||||
------------------------------------------------------------------
|
||||
-- now the first unassigned local (since it's sorted) will be the
|
||||
-- one with the most tokens to rename, so we set this one and then
|
||||
-- eliminate all others that collides, then any locals that left
|
||||
-- can then reuse the same variable name; this is repeated until
|
||||
-- all local declaration that can use this name is assigned
|
||||
-- * the criteria for local-local reuse/collision is:
|
||||
-- A is the local with a name already assigned
|
||||
-- B is the unassigned local under consideration
|
||||
-- => anytime A is accessed, it cannot be when B is 'live'
|
||||
-- => to speed up things, we have first/last accesses noted
|
||||
------------------------------------------------------------------
|
||||
while oleft > 0 do
|
||||
local i = 1
|
||||
while object[i].skip do -- scan for first object
|
||||
i = i + 1
|
||||
end
|
||||
------------------------------------------------------------------
|
||||
-- first object is free for assignment of the variable name
|
||||
-- [first,last] gives the access range for collision checking
|
||||
------------------------------------------------------------------
|
||||
oleft = oleft - 1
|
||||
local obja = object[i]
|
||||
i = i + 1
|
||||
obja.newname = varname
|
||||
obja.skip = true
|
||||
obja.done = true
|
||||
local first, last = obja.first, obja.last
|
||||
local xref = obja.xref
|
||||
------------------------------------------------------------------
|
||||
-- then, scan all the rest and drop those colliding
|
||||
-- if A was never accessed then it'll never collide with anything
|
||||
-- otherwise trivial skip if:
|
||||
-- * B was activated after A's last access (last < act)
|
||||
-- * B was removed before A's first access (first > rem)
|
||||
-- if not, see detailed skip below...
|
||||
------------------------------------------------------------------
|
||||
if first and oleft > 0 then -- must have at least 1 access
|
||||
local scanleft = oleft
|
||||
while scanleft > 0 do
|
||||
while object[i].skip do -- next valid object
|
||||
i = i + 1
|
||||
end
|
||||
scanleft = scanleft - 1
|
||||
local objb = object[i]
|
||||
i = i + 1
|
||||
local act, rem = objb.act, objb.rem -- live range of B
|
||||
-- if rem < 0, extend range of rem thru' following local
|
||||
while rem < 0 do
|
||||
rem = localinfo[-rem].rem
|
||||
end
|
||||
--------------------------------------------------------
|
||||
if not(last < act or first > rem) then -- possible collision
|
||||
--------------------------------------------------------
|
||||
-- B is activated later than A or at the same statement,
|
||||
-- this means for no collision, A cannot be accessed when B
|
||||
-- is alive, since B overrides A (or is a peer)
|
||||
--------------------------------------------------------
|
||||
if act >= obja.act then
|
||||
for j = 1, obja.xcount do -- ... then check every access
|
||||
local p = xref[j]
|
||||
if p >= act and p <= rem then -- A accessed when B live!
|
||||
oleft = oleft - 1
|
||||
objb.skip = true
|
||||
break
|
||||
end
|
||||
end--for
|
||||
--------------------------------------------------------
|
||||
-- A is activated later than B, this means for no collision,
|
||||
-- A's access is okay since it overrides B, but B's last
|
||||
-- access need to be earlier than A's activation time
|
||||
--------------------------------------------------------
|
||||
else
|
||||
if objb.last and objb.last >= obja.act then
|
||||
oleft = oleft - 1
|
||||
objb.skip = true
|
||||
end
|
||||
end
|
||||
end
|
||||
--------------------------------------------------------
|
||||
if oleft == 0 then break end
|
||||
end
|
||||
end--if first
|
||||
------------------------------------------------------------------
|
||||
end--while
|
||||
------------------------------------------------------------------
|
||||
-- after assigning all possible locals to one variable name, the
|
||||
-- unassigned locals/objects have the skip field reset and the table
|
||||
-- is compacted, to hopefully reduce iteration time
|
||||
------------------------------------------------------------------
|
||||
local temp, j = {}, 1
|
||||
for i = 1, nobject do
|
||||
local obj = object[i]
|
||||
if not obj.done then
|
||||
obj.skip = false
|
||||
temp[j] = obj
|
||||
j = j + 1
|
||||
end
|
||||
end
|
||||
object = temp -- new compacted object table
|
||||
nobject = #object -- objects left to process
|
||||
------------------------------------------------------------------
|
||||
end--while
|
||||
------------------------------------------------------------------
|
||||
-- after assigning all locals with new variable names, we can
|
||||
-- patch in the new names, and reprocess to get 'after' stats
|
||||
------------------------------------------------------------------
|
||||
for i = 1, #localinfo do -- enumerate all locals
|
||||
local obj = localinfo[i]
|
||||
local xref = obj.xref
|
||||
if obj.newname then -- if got new name, patch it in
|
||||
for j = 1, obj.xcount do
|
||||
local p = xref[j] -- xrefs indexes the token list
|
||||
seminfolist[p] = obj.newname
|
||||
end
|
||||
obj.name, obj.oldname -- adjust names
|
||||
= obj.newname, obj.name
|
||||
else
|
||||
obj.oldname = obj.name -- for cases like 'self'
|
||||
end
|
||||
end
|
||||
------------------------------------------------------------------
|
||||
-- deal with statistics output
|
||||
------------------------------------------------------------------
|
||||
if gotself then -- add 'self' to end of list
|
||||
varlist[#varlist + 1] = "self"
|
||||
end
|
||||
local afteruniq = preprocess(localinfo)
|
||||
stats_summary(globaluniq, localuniq, afteruniq, option)
|
||||
------------------------------------------------------------------
|
||||
end
|
||||
|
||||
return base
|
||||
361
AIO_Server/Dep_LuaSrcDiet/technotes.txt
Normal file
361
AIO_Server/Dep_LuaSrcDiet/technotes.txt
Normal file
@@ -0,0 +1,361 @@
|
||||
Tech Notes for LuaSrcDiet
|
||||
=========================
|
||||
|
||||
The following are notes on the optimization process for easy reference.
|
||||
|
||||
|
||||
Miscellaneous Ideas, TODO Stuff
|
||||
===============================
|
||||
|
||||
Other Ideas:
|
||||
(a) remove unused locals that can be removed in the source
|
||||
(b) remove declarations using nil
|
||||
(c) remove table constructor elements using nil
|
||||
(d) extra optional semicolon removal
|
||||
(e) extra comma or semicolon removal in table constructors
|
||||
(f) special number forms: using ^ and * to shorten constants: 1^16
|
||||
(g) simple declaration of locals that can be merged: local a,b,c,d
|
||||
(h) warn of opportunity for using a local to zap a bunch of globals
|
||||
(i) warn of trailing whitespace in strings or long strings
|
||||
(j) spaces to tabs in comments/long comments/long strings
|
||||
(k) convert long strings to normal strings, vice versa
|
||||
(l) simplify logical or relational operator expression
|
||||
|
||||
|
||||
Modified Lexer Output
|
||||
=====================
|
||||
|
||||
The lexer is works almost exactly like llex.c in 'normal' Lua. Instead
|
||||
of returning one token on each call, the lexer processes the entire
|
||||
string (from an entire file) and returns. Two lists (tokens and semantic
|
||||
information items) are set up in the module for use by the caller.
|
||||
|
||||
For maximum flexibility during processing, the lexer returns non-grammar
|
||||
lexical elements as tokens too. Non-grammar elements, such as comments,
|
||||
whitespace, line endings, are classified along with 'normal' tokens. The
|
||||
lexer classifies 7 kinds of grammar tokens and 4 kinds of non-grammar
|
||||
tokens:
|
||||
|
||||
---------------------------------------------------------------------
|
||||
TOKEN CLASS DESCRIPTION
|
||||
---------------------------------------------------------------------
|
||||
"TK_KEYWORD" keywords
|
||||
"TK_NAME" identifiers
|
||||
"TK_NUMBER" numbers (unconverted, kept in original form)
|
||||
"TK_STRING" strings (no translation is done, includes delimiters)
|
||||
"TK_LSTRING" long strings (no translation is done, includes delimiters)
|
||||
"TK_OP" operators and punctuation (most single-char, some double)
|
||||
"TK_EOS" end-of-stream (there is only one for each file/stream)
|
||||
---------------------------------------------------------------------
|
||||
"TK_SPACE" whitespace (generally, spaces, \t, \v and \f)
|
||||
"TK_COMMENT" comments (includes delimiters, also includes special
|
||||
first line shbang, which is handled specially in the
|
||||
optimizer)
|
||||
"TK_LCOMMENT" block comments (includes delimiters)
|
||||
"TK_EOL" end-of-lines (excludes those embedded in strings)
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
Table for Lexer-Based Optimizations
|
||||
===================================
|
||||
|
||||
We aim to keep lexer-based optimizations free of parser considerations,
|
||||
i.e. we allow for generalized optimization of token sequences. The table
|
||||
below considers the requirements for all combinations of significant
|
||||
tokens. Other tokens are whitespace-like. Comments can be considered to
|
||||
be a special kind of whitespace, e.g. a short comment needs to have a
|
||||
following EOL token, if we do not want to optimize away short comments.
|
||||
|
||||
FIRST SECOND TOKEN
|
||||
TOKEN |
|
||||
| V
|
||||
V Keyword Name Number String LString Oper
|
||||
--------------------------------------------------------
|
||||
Keyword [S] [S] [S] 0 0 0
|
||||
Name [S] [S] [S] 0 0 0
|
||||
Number [S] [S] [S] 0 0 [1]
|
||||
String 0 0 0 0 0 0
|
||||
LString 0 0 0 0 0 0
|
||||
Oper 0 0 [1] 0 0 [2]
|
||||
--------------------------------------------------------
|
||||
|
||||
[S] = need at least one whitespace (set as either a space or keep EOL)
|
||||
|
||||
[1] = need a space if operator is a '.', all others okay; a '+' or '-'
|
||||
is used as part of a floating-point spec, but there does not
|
||||
appear to be any way of creating a float by joining with number
|
||||
with a a '+' or '-' plus another number, because an 'e' has to
|
||||
be somewhere in the first token, this can't be done
|
||||
|
||||
[2] = normally there cannot be consecutive operators, but we plan to
|
||||
allow for generalized optimization of token sequences, i.e. even
|
||||
sequences that are grammatically illegal; so disallow adjacent
|
||||
operators if:
|
||||
(a) the first is in [=<>] and the second is '='
|
||||
(b) disallow dot sequences to be adjacent, but "..." first okay
|
||||
(c) disallow '[' followed by '=' or '[' (not optimal)
|
||||
|
||||
Also, a minus '-' cannot preceed a Comment or LComment, because comments
|
||||
start with a '--' prefix. Apart from that, all Comment or LComment
|
||||
tokens can be set abut with a real token.
|
||||
|
||||
|
||||
Sequence of Lexer-Based Optimization Process
|
||||
============================================
|
||||
|
||||
*** TODO ***
|
||||
|
||||
|
||||
Description of Locals Optimization
|
||||
==================================
|
||||
|
||||
VARIABLE TRACKING AND LOCAL VARIABLE RENAMING
|
||||
|
||||
(A) TK_NAME token class considerations
|
||||
--------------------------------------
|
||||
|
||||
A TK_NAME token means a number of things, and some of these cannot be
|
||||
renamed. We are interested in the use of TK_NAME in the following:
|
||||
|
||||
(a) global variable access
|
||||
(b) local variable declaration, includes local statements, local
|
||||
functions, function parameters, implicit "self" local, etc.
|
||||
(c) local variable access, including upvalue access
|
||||
|
||||
TK_NAME is also used in parts of grammar as constant strings -- these
|
||||
tokens cannot be optimized without user assistance. These include:
|
||||
|
||||
(d) as the key in key=value pairs in table construction
|
||||
(e) as field or method names in a:b or a.b syntax forms
|
||||
|
||||
For local variable name optimization, we do not need to consider (d) and
|
||||
(e), and while global variables cannot be renamed (since we do not have
|
||||
support for user assistance), they need to be considered as part of
|
||||
Lua's variable access scheme.
|
||||
|
||||
(B) Lifetime of a local variable
|
||||
--------------------------------
|
||||
|
||||
Take the following example:
|
||||
|
||||
local string, table = string, table
|
||||
|
||||
In the example, the two locals are assigned the values of the globals
|
||||
with the same names. Therefore, when Lua encounters the declaration
|
||||
portion:
|
||||
|
||||
local string, table
|
||||
|
||||
The parser cannot immediately make the two local variable available to
|
||||
following code. In the parser and code generator, locals are inactive
|
||||
when its entry is created. They are activated only when the function
|
||||
adjustlocalvars() is called to activate the appropriate local variables.
|
||||
|
||||
In the example, the two local variables are activated only after the
|
||||
whole statement has been parsed, that is, after the last "table" token.
|
||||
Hence, the statement works as expected. Also, once the two local
|
||||
variables goes out of scope, removevars() is called to deactivate them,
|
||||
allowing other variables of the same name to become visible again.
|
||||
|
||||
Another example worth mentioning is:
|
||||
|
||||
local a, a, a, = 1, 2, 3
|
||||
|
||||
The above will assign 3 to 'a'.
|
||||
|
||||
Thus, when optimizing local variable names, (1) we need to consider
|
||||
accesses of global variable names affecting the namespace, (2) for the
|
||||
local variable names themselves, we need to consider when they are
|
||||
declared, activated and removed, and (3) within the 'live' time of
|
||||
locals, we need to know when they are accessed (since locals that are
|
||||
never accessed don't really matter.)
|
||||
|
||||
(C) Local variable tracking
|
||||
---------------------------
|
||||
|
||||
Every local variable declaration is considered an object to be renamed.
|
||||
|
||||
From the parser, we have the original name of the local variable, the
|
||||
token positions for declaration, activation and removal, and the token
|
||||
position for all the TK_NAME tokens which references this local. All
|
||||
instances of the implicit "self" local variable are flagged as such.
|
||||
|
||||
In addition to local variable information, all global variable accesses
|
||||
are tabled, one object entry for one name, and each object has a
|
||||
corresponding list of token positions for the TK_NAME tokens, which is
|
||||
where the global variables were accessed.
|
||||
|
||||
The key criteria is: Our act of renaming cannot change the visibility of
|
||||
any of these locals and globals at the time they are accessed.
|
||||
|
||||
Of course, if every variable has a unique name, then there is no need
|
||||
for a name allocation algorithm, as there will be no conflict. But, in
|
||||
order to maximize utilization of short identifier names, we want to
|
||||
reuse the names as much as possible. In addition, fewer names will
|
||||
likely reduce symbol entropy and may slightly improve compressibility of
|
||||
the source code.
|
||||
|
||||
(D) Name allocation theory
|
||||
--------------------------
|
||||
|
||||
To understand the renaming algorithm, first we need to establish how
|
||||
different local and global variables can operate happily without
|
||||
interfering with each other.
|
||||
|
||||
Consider three objects, local object A, local object B and global object
|
||||
G. A and B involve declaration, activation and removal, and within the
|
||||
period it is active, there may be zero or more accesses of the local.
|
||||
For G, there are only global variable accesses to look into.
|
||||
|
||||
Assume that we have assigned a new name to A and we wish to consider its
|
||||
effects on other locals and globals, for which we choose B and G as
|
||||
examples. We assume local B has not been assigned a new name as we
|
||||
expect our algorithm to take care of collisions.
|
||||
|
||||
A's lifetime is something like this:
|
||||
|
||||
Decl Act Rem
|
||||
+ +-------------------------------+
|
||||
-------------------------------------------------
|
||||
|
||||
where 'Decl' is the time of declaration, 'Act' is the time of
|
||||
activation, and 'Rem' is the time of removal. Between 'Act' and 'Rem',
|
||||
the local is alive or 'live' and Lua can see it if its corresponding
|
||||
TK_NAME identifier comes up.
|
||||
|
||||
Decl Act Rem
|
||||
+ +-------------------------------+
|
||||
-------------------------------------------------
|
||||
* * * *
|
||||
(1) (2) (3) (4)
|
||||
|
||||
Recall that the key criteria is to not change the visibility of globals
|
||||
and locals. Consider local and global accesses at (1), (2), (3) and (4).
|
||||
|
||||
A global G of the same name as A will only collide at (3), where Lua
|
||||
will see A and not G. Since G must be accessed at (3) according to what
|
||||
the parser says, and we cannot modify the positions of 'Decl', 'Act' and
|
||||
'Rem', it follows that A cannot have the same name as G.
|
||||
|
||||
Decl Act Rem
|
||||
+ +-----------------------+
|
||||
---------------------------------
|
||||
(1)+ +---+ (2)+ +---+ (3)+ +---+ (4)+ +---+
|
||||
--------- --------- --------- ---------
|
||||
|
||||
For the case of A and B having the same names and colliding, consider
|
||||
the cases for which B is at (1), (2), (3) or (4) in the above.
|
||||
|
||||
(1) and (4) means that A and B are completely isolated from each other,
|
||||
hence in the two cases, A and B can safely use the same variable names.
|
||||
To be specific, since we have assigned A, B is considered completely
|
||||
isolated from A if B's Activation-to-Removal period is isolated from
|
||||
the time of A's first access to last access, meaning B's active time
|
||||
will never affect any of A's accesses.
|
||||
|
||||
For (2) and (3), we have two cases where we need to consider which one
|
||||
has been activated first. For (2), B is active before A, so A cannot
|
||||
impose on B. But A's accesses are valid while B is active, since A can
|
||||
override B. For no collision in the case of (2), we simply need to
|
||||
ensure that the last access of B occurs before A is activated.
|
||||
|
||||
For (3), B is activated before A, hence B can override A's accesses. For
|
||||
no collision, all of A's accesses cannot happen while B is active. Thus
|
||||
position (3) follows the "A is never accessed when B is active" rule in
|
||||
a general way. Local variables of a child function are in the position
|
||||
of (3). To illustrate, the local B can use the same name as local A and
|
||||
live in a child function or block scope if each time A is accessed, Lua
|
||||
sees A and not B. So we have to check all accesses of A and see whether
|
||||
they collide with the active period of B. Now if A was never accessed,
|
||||
then B can be active anywhere.
|
||||
|
||||
The above appears to resolve all sorts of cases where the active times
|
||||
of A and B overlap. If there is a more simple scheme, do let me know.
|
||||
Note that in the above, the allocator does not need to know how locals
|
||||
are separated according to function prototypes. Perhaps the allocator
|
||||
can be simplified if knowledge of function structure is utilized.
|
||||
|
||||
(E) Name allocation algorithm
|
||||
-----------------------------
|
||||
|
||||
To begin with, the name generator is mostly separate from the name
|
||||
allocation algorithm. The name generator returns the next shortest name
|
||||
for the algorithm to apply to local variables. To attempt to reduce
|
||||
symbol entropy (which benefit compression algorithms), the name
|
||||
generator follows English frequent letter usage. Later, there may be an
|
||||
option to calculate an actual symbol entropy table from the input data.
|
||||
|
||||
Since there are 53 one-character identifiers and (53*63-4) two-character
|
||||
identifiers (minus a few keywords), there isn't a pressing need to
|
||||
optimally maximize name reuse. Sample files show that we can eventually
|
||||
get 20 tokens per identifier name, thus a source file can have over 1000
|
||||
local variable tokens that are all single character in length.
|
||||
|
||||
In theory, we should need no more than 260 local identifiers by default.
|
||||
Why? Since LUAI_MAXVARS is 200 and LUAI_MAXUPVALUES is 60, at any block
|
||||
scope, there can be at most (LUAI_MAXVARS + LUAI_MAXUPVALUES) locals
|
||||
referenced, or 260. Also, those from outer scopes not referenced in
|
||||
inner scopes can reuse identifiers. The net effect of this is that a
|
||||
local variable name allocation method should not allocate more than 260
|
||||
identifier names for locals.
|
||||
|
||||
The current algorithm is a simple first-come first-served scheme:
|
||||
|
||||
(a) One local object that use the most tokens is named first.
|
||||
(b) Any other non-conflicting locals with respect to the first
|
||||
object are assigned the same name.
|
||||
(c) Assigned locals are removed and the procedure is repeated for
|
||||
objects that have not been assigned new names. (a) to (c)
|
||||
repeats until no local objects are left.
|
||||
|
||||
In addition, there are a few extra issues to take care of:
|
||||
|
||||
(d) Implicit "self" locals that have been flagged as such are
|
||||
already "assigned to" and so they are left unmodified.
|
||||
(e) The name generator skips "self" to avoid conflicts. This
|
||||
is not optimal but it is unlikely a script will use so many
|
||||
local variables as to reach "self".
|
||||
(f) Keywords are also skipped for the name generator.
|
||||
(g) Global name conflict resolution.
|
||||
|
||||
For (g), global name conflict resolution is handled just after the new
|
||||
name is generated. The name can still be used for some locals even if it
|
||||
conflicts with other locals. To remove conflicts, global variable
|
||||
accesses for the particular identifier name is checked. Any local
|
||||
variables that are active when a global access is made is marked to be
|
||||
skipped. The rest of the local objects can then use that name.
|
||||
|
||||
The algorithm has special handling for locals that use the same name in
|
||||
the same scope. For example:
|
||||
|
||||
local foo = 10 -- (1)
|
||||
...
|
||||
local foo = 20 -- (2)
|
||||
...
|
||||
print(e)
|
||||
|
||||
Since we are considering name visibility, the first 'foo' does not
|
||||
really cease to exist when the second 'foo' is declared, because if we
|
||||
were to make that assumption, and the first 'foo' is removed before (2),
|
||||
then I should be able to use 'e' as the name for the first 'foo' and
|
||||
after (2), it should not conflict with variables in the outer scope
|
||||
with the same name. To illustrate:
|
||||
|
||||
local e = 10 -- 'foo ' renamed to 'e'
|
||||
...
|
||||
local t = 20 -- error if we assumed 'e' removed here
|
||||
...
|
||||
print(e)
|
||||
|
||||
Since 'e' is a global in the example, we now have an error as the
|
||||
name as been taken over by a local. Thus, the first 'foo' local must
|
||||
have its active time extend to the end of the current scope. If there
|
||||
is no conflict between the first and second 'foo', the algorithm may
|
||||
still assign the same names to them.
|
||||
|
||||
The current fix to deal with the above chains local objects in order to
|
||||
find the removal position. Practically, the parser can be modified to
|
||||
simplify this.
|
||||
|
||||
|
||||
END.
|
||||
20
AIO_Server/Dep_Smallfolk/LICENSE.md
Normal file
20
AIO_Server/Dep_Smallfolk/LICENSE.md
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014 Robin Wellner
|
||||
|
||||
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.
|
||||
|
||||
97
AIO_Server/Dep_Smallfolk/README.md
Normal file
97
AIO_Server/Dep_Smallfolk/README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
Smallfolk
|
||||
=========
|
||||
|
||||
Smallfolk is a reasonably fast, robust, richtly-featured table serialization
|
||||
library for Lua. It was specifically written to allow complex data structures
|
||||
to be loaded from unsafe sources for [LÖVE](http://love2d.org/) games, but can
|
||||
be used anywhere.
|
||||
|
||||
You use, distribute and extend Smallfolk under the terms of the MIT license.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Smallfolk is very simple and easy to use:
|
||||
|
||||
```lua
|
||||
local smallfolk = require 'smallfolk'
|
||||
|
||||
print(smallfolk.dumps({"Hello", world = true}))
|
||||
print(smallfolk.loads('{"foo":"bar"}').foo)
|
||||
-- prints:
|
||||
-- {"Hello","world":t}
|
||||
-- bar
|
||||
```
|
||||
|
||||
Fast
|
||||
----
|
||||
|
||||
Using Serpent's benchmark code, Smallfolk's serialization speed is comparable
|
||||
to that of Ser (and Ser is 33% faster than Serpent).
|
||||
|
||||
It should be noted that deserialization is much slower in Smallfolk than in
|
||||
most other serialization libraries, because it parses the input itself instead
|
||||
of handing it over to Lua. However, if you use LuaJIT this difference is much
|
||||
less, and it is not noticable for small outputs. By default, Smallfolk rejects
|
||||
inputs that are too large, to prevent DOS attacks.
|
||||
|
||||
Robust
|
||||
------
|
||||
|
||||
Sometimes you have strange, non-euclidean geometries in your table
|
||||
constructions. It happens, I don't judge. Smallfolk can deal with that, where
|
||||
some other serialization libraries (or anything that produces JSON) cry "Iä!
|
||||
Iä! Cthulhu fhtagn!" and give up — or worse, silently produce incorrect
|
||||
data.
|
||||
|
||||
```lua
|
||||
local smallfolk = require 'smallfolk'
|
||||
|
||||
local cthulhu = {{}, {}, {}}
|
||||
cthulhu.fhtagn = cthulhu
|
||||
cthulhu[1][cthulhu[2]] = cthulhu[3]
|
||||
cthulhu[2][cthulhu[1]] = cthulhu[2]
|
||||
cthulhu[3][cthulhu[3]] = cthulhu
|
||||
print(smallfolk.dumps(cthulhu))
|
||||
-- prints:
|
||||
-- {{{@2:@3}:{@4:@1}},@3,@4,"fhtagn":@1}
|
||||
```
|
||||
|
||||
Secure
|
||||
------
|
||||
|
||||
Smallfolk doesn't run arbitrary Lua code, so you can safely use it when you
|
||||
want to read data from an untrusted source.
|
||||
|
||||
Compact
|
||||
-------
|
||||
|
||||
Smallfolk creates really small output files compared to something like Ser when
|
||||
it encounters a lot of non-tree-like data, by using numbered references rather
|
||||
than item assignment.
|
||||
|
||||
Tested
|
||||
------
|
||||
|
||||
Check out `tests.lua` to see how Smallfolk behaves with all kinds of inputs.
|
||||
|
||||
Reference
|
||||
---------
|
||||
|
||||
###`smallfolk.dumps(object)`
|
||||
|
||||
Returns an 8-bit string representation of `object`. Throws an error if `object`
|
||||
contains any types that cannot be serialised (userdata, functions and threads).
|
||||
|
||||
###`smallfolk.loads(string[, maxsize=10000])`
|
||||
|
||||
Returns an object whose representation would be `string`. If the length of
|
||||
`string` is larger than `maxsize`, no deserialization is attempted and instead
|
||||
an error is thrown. If `string` is not a valid representation of any object,
|
||||
an error is thrown.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
* [Ser](https://github.com/gvx/Ser): for trusted-source serialization
|
||||
* [Lady](https://github.com/gvx/Lady): for trusted-source savegames
|
||||
218
AIO_Server/Dep_Smallfolk/smallfolk.lua
Normal file
218
AIO_Server/Dep_Smallfolk/smallfolk.lua
Normal file
@@ -0,0 +1,218 @@
|
||||
local M = {}
|
||||
Smallfolk = M
|
||||
local expect_object, dump_object
|
||||
local error, tostring, pairs, type, floor, huge, concat = error, tostring, pairs, type, math.floor, math.huge, table.concat
|
||||
|
||||
local dump_type = {}
|
||||
|
||||
function dump_type:string(nmemo, memo, acc)
|
||||
local nacc = #acc
|
||||
acc[nacc + 1] = '"'
|
||||
acc[nacc + 2] = self:gsub('"', '""')
|
||||
acc[nacc + 3] = '"'
|
||||
return nmemo
|
||||
end
|
||||
|
||||
function dump_type:number(nmemo, memo, acc)
|
||||
acc[#acc + 1] = ("%.17g"):format(self)
|
||||
return nmemo
|
||||
end
|
||||
|
||||
function dump_type:table(nmemo, memo, acc)
|
||||
--[[
|
||||
if memo[self] then
|
||||
acc[#acc + 1] = '@'
|
||||
acc[#acc + 1] = tostring(memo[self])
|
||||
return nmemo
|
||||
end
|
||||
nmemo = nmemo + 1
|
||||
]]
|
||||
memo[self] = nmemo
|
||||
acc[#acc + 1] = '{'
|
||||
local nself = #self
|
||||
for i = 1, nself do -- don't use ipairs here, we need the gaps
|
||||
nmemo = dump_object(self[i], nmemo, memo, acc)
|
||||
acc[#acc + 1] = ','
|
||||
end
|
||||
for k, v in pairs(self) do
|
||||
if type(k) ~= 'number' or floor(k) ~= k or k < 1 or k > nself then
|
||||
nmemo = dump_object(k, nmemo, memo, acc)
|
||||
acc[#acc + 1] = ':'
|
||||
nmemo = dump_object(v, nmemo, memo, acc)
|
||||
acc[#acc + 1] = ','
|
||||
end
|
||||
end
|
||||
acc[#acc] = acc[#acc] == '{' and '{}' or '}'
|
||||
return nmemo
|
||||
end
|
||||
|
||||
function dump_object(object, nmemo, memo, acc)
|
||||
if object == true then
|
||||
acc[#acc + 1] = 't'
|
||||
elseif object == false then
|
||||
acc[#acc + 1] = 'f'
|
||||
elseif object == nil then
|
||||
acc[#acc + 1] = 'n'
|
||||
elseif object ~= object then
|
||||
if (''..object):sub(1,1) == '-' then
|
||||
acc[#acc + 1] = 'N'
|
||||
else
|
||||
acc[#acc + 1] = 'Q'
|
||||
end
|
||||
elseif object == huge then
|
||||
acc[#acc + 1] = 'I'
|
||||
elseif object == -huge then
|
||||
acc[#acc + 1] = 'i'
|
||||
else
|
||||
local t = type(object)
|
||||
if not dump_type[t] then
|
||||
error('cannot dump type ' .. t)
|
||||
end
|
||||
return dump_type[t](object, nmemo, memo, acc)
|
||||
end
|
||||
return nmemo
|
||||
end
|
||||
|
||||
function M.dumps(object)
|
||||
local nmemo = 0
|
||||
local memo = {}
|
||||
local acc = {}
|
||||
dump_object(object, nmemo, memo, acc)
|
||||
return concat(acc)
|
||||
end
|
||||
|
||||
local function invalid(i)
|
||||
error('invalid input at position ' .. i)
|
||||
end
|
||||
|
||||
local nonzero_digit = {['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true, ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true}
|
||||
local is_digit = {['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true, ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true}
|
||||
local function expect_number(string, start)
|
||||
local i = start
|
||||
local head = string:sub(i, i)
|
||||
if head == '-' then
|
||||
i = i + 1
|
||||
head = string:sub(i, i)
|
||||
end
|
||||
if nonzero_digit[head] then
|
||||
repeat
|
||||
i = i + 1
|
||||
head = string:sub(i, i)
|
||||
until not is_digit[head]
|
||||
elseif head == '0' then
|
||||
i = i + 1
|
||||
head = string:sub(i, i)
|
||||
else
|
||||
invalid(i)
|
||||
end
|
||||
if head == '.' then
|
||||
local oldi = i
|
||||
repeat
|
||||
i = i + 1
|
||||
head = string:sub(i, i)
|
||||
until not is_digit[head]
|
||||
if i == oldi + 1 then
|
||||
invalid(i)
|
||||
end
|
||||
end
|
||||
if head == 'e' or head == 'E' then
|
||||
i = i + 1
|
||||
head = string:sub(i, i)
|
||||
if head == '+' or head == '-' then
|
||||
i = i + 1
|
||||
head = string:sub(i, i)
|
||||
end
|
||||
if not is_digit[head] then
|
||||
invalid(i)
|
||||
end
|
||||
repeat
|
||||
i = i + 1
|
||||
head = string:sub(i, i)
|
||||
until not is_digit[head]
|
||||
end
|
||||
return tonumber(string:sub(start, i - 1)), i
|
||||
end
|
||||
|
||||
local expect_object_head = {
|
||||
t = function(string, i) return true, i end,
|
||||
f = function(string, i) return false, i end,
|
||||
n = function(string, i) return nil, i end,
|
||||
Q = function(string, i) return -(0/0), i end,
|
||||
N = function(string, i) return 0/0, i end,
|
||||
I = function(string, i) return 1/0, i end,
|
||||
i = function(string, i) return -1/0, i end,
|
||||
['"'] = function(string, i)
|
||||
local nexti = i - 1
|
||||
repeat
|
||||
nexti = string:find('"', nexti + 1, true) + 1
|
||||
until string:sub(nexti, nexti) ~= '"'
|
||||
return string:sub(i, nexti - 2):gsub('""', '"'), nexti
|
||||
end,
|
||||
['0'] = function(string, i)
|
||||
return expect_number(string, i - 1)
|
||||
end,
|
||||
['{'] = function(string, i, tables)
|
||||
local nt, k, v = {}
|
||||
local j = 1
|
||||
tables[#tables + 1] = nt
|
||||
if string:sub(i, i) == '}' then
|
||||
return nt, i + 1
|
||||
end
|
||||
while true do
|
||||
k, i = expect_object(string, i, tables)
|
||||
if string:sub(i, i) == ':' then
|
||||
v, i = expect_object(string, i + 1, tables)
|
||||
nt[k] = v
|
||||
else
|
||||
nt[j] = k
|
||||
j = j + 1
|
||||
end
|
||||
local head = string:sub(i, i)
|
||||
if head == ',' then
|
||||
i = i + 1
|
||||
elseif head == '}' then
|
||||
return nt, i + 1
|
||||
else
|
||||
invalid(i)
|
||||
end
|
||||
end
|
||||
end,
|
||||
--[[
|
||||
['@'] = function(string, i, tables)
|
||||
local match = string:match('^%d+', i)
|
||||
local ref = tonumber(match)
|
||||
if tables[ref] then
|
||||
return tables[ref], i + #match
|
||||
end
|
||||
invalid(i)
|
||||
end,
|
||||
]]
|
||||
}
|
||||
expect_object_head['1'] = expect_object_head['0']
|
||||
expect_object_head['2'] = expect_object_head['0']
|
||||
expect_object_head['3'] = expect_object_head['0']
|
||||
expect_object_head['4'] = expect_object_head['0']
|
||||
expect_object_head['5'] = expect_object_head['0']
|
||||
expect_object_head['6'] = expect_object_head['0']
|
||||
expect_object_head['7'] = expect_object_head['0']
|
||||
expect_object_head['8'] = expect_object_head['0']
|
||||
expect_object_head['9'] = expect_object_head['0']
|
||||
expect_object_head['-'] = expect_object_head['0']
|
||||
expect_object_head['.'] = expect_object_head['0']
|
||||
|
||||
expect_object = function(string, i, tables)
|
||||
local head = string:sub(i, i)
|
||||
if expect_object_head[head] then
|
||||
return expect_object_head[head](string, i + 1, tables)
|
||||
end
|
||||
invalid(i)
|
||||
end
|
||||
|
||||
function M.loads(string, maxsize)
|
||||
if #string > (maxsize or 10000) then
|
||||
error 'input too large'
|
||||
end
|
||||
return (expect_object(string, 1, {}))
|
||||
end
|
||||
|
||||
return M
|
||||
25
AIO_Server/Dep_crc32lua/COPYRIGHT
Normal file
25
AIO_Server/Dep_crc32lua/COPYRIGHT
Normal file
@@ -0,0 +1,25 @@
|
||||
lua-digest-crc32lua License
|
||||
|
||||
===============================================================================
|
||||
|
||||
Copyright (C) 2008, David Manura.
|
||||
|
||||
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.
|
||||
|
||||
===============================================================================
|
||||
207
AIO_Server/Dep_crc32lua/crc32lua.lua
Normal file
207
AIO_Server/Dep_crc32lua/crc32lua.lua
Normal file
@@ -0,0 +1,207 @@
|
||||
--[[
|
||||
|
||||
LUA MODULE
|
||||
|
||||
digest.crc32 - CRC-32 checksum implemented entirely in Lua.
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
local CRC = require 'digest.crc32lua'
|
||||
print(CRC.crc32 'test') --> 0xD87F7E0C or -662733300
|
||||
|
||||
assert(CRC.crc32('st', CRC.crc32('te')) == CRC.crc32 'test')
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This can be used to compute CRC-32 checksums on strings.
|
||||
This is similar to [1-2].
|
||||
|
||||
API
|
||||
|
||||
Note: in the functions below, checksums are 32-bit integers stored in
|
||||
numbers. The number format currently depends on the bit
|
||||
implementation--see DESIGN NOTES below.
|
||||
|
||||
CRC.crc32_byte(byte [, crc]) --> rcrc
|
||||
|
||||
Returns CRC-32 checksum `rcrc` of byte `byte` (number 0..255) appended to
|
||||
a string with CRC-32 checksum `crc`. `crc` defaults to 0 (empty string)
|
||||
if omitted.
|
||||
|
||||
CRC.crc32_string(s, crc) --> bcrc
|
||||
|
||||
Returns CRC-32 checksum `rcrc` of string `s` appended to
|
||||
a string with CRC-32 checksum `crc`. `crc` defaults to 0 (empty string)
|
||||
if omitted.
|
||||
|
||||
CRC.crc32(o, crc) --> bcrc
|
||||
|
||||
This invokes `crc32_byte` if `o` is a byte or `crc32_string` if `o`
|
||||
is a string.
|
||||
|
||||
CRC.bit
|
||||
|
||||
This contains the underlying bit library used by the module. It
|
||||
should be considered a read-only copy.
|
||||
|
||||
DESIGN NOTES
|
||||
|
||||
Currently, this module exposes the underlying bit array implementation in CRC
|
||||
checksums returned. In BitOp, bit arrays are 32-bit signed integer numbers
|
||||
(may be negative). In Lua 5.2 'bit32' and 'bit.numberlua', bit arrays are
|
||||
32-bit unsigned integer numbers (non-negative). This is subject to change
|
||||
in the future but is currently done due to (unconfirmed) performance
|
||||
implications.
|
||||
|
||||
On platforms with 64-bit numbers, one way to normalize CRC
|
||||
checksums to be unsigned is to do `crcvalue % 2^32`,
|
||||
|
||||
The name of this module is inspired by Perl `Digest::CRC*`.
|
||||
|
||||
DEPENDENCIES
|
||||
|
||||
Requires one of the following bit libraries:
|
||||
|
||||
BitOp "bit" -- bitop.luajit.org -- This is included in LuaJIT and also available
|
||||
for Lua 5.1/5.2. This provides the fastest performance in LuaJIT.
|
||||
Lua 5.2 "bit32" -- www.lua.org/manual/5.2 -- This is provided in Lua 5.2
|
||||
and is preferred in 5.2 (unless "bit" also happens to be installed).
|
||||
"bit.numberlua" (>=000.003) -- https://github.com/davidm/lua-bit-numberlua
|
||||
This is slowest and used as a last resort.
|
||||
It is only a few times slower than "bit32" though.
|
||||
|
||||
DOWNLOAD/INSTALLATION
|
||||
|
||||
If using LuaRocks:
|
||||
luarocks install lua-digest-crc32lua
|
||||
|
||||
Otherwise, download <https://github.com/davidm/lua-digest-crc32lua/zipball/master>.
|
||||
Alternately, if using git:
|
||||
git clone git://github.com/davidm/lua-digest-crc32lua.git
|
||||
cd lua-digest-crc32lua
|
||||
Optionally unpack:
|
||||
./util.mk
|
||||
or unpack and install in LuaRocks:
|
||||
./util.mk install
|
||||
|
||||
REFERENCES
|
||||
|
||||
[1] http://www.axlradius.com/freestuff/CRC32.java
|
||||
[2] http://www.gamedev.net/reference/articles/article1941.asp
|
||||
[3] http://java.sun.com/j2se/1.5.0/docs/api/java/util/zip/CRC32.html
|
||||
[4] http://www.dsource.org/projects/tango/docs/current/tango.io.digest.Crc32.html
|
||||
[5] http://pydoc.org/1.5.2/zlib.html#-crc32
|
||||
[6] http://www.python.org/doc/2.5.2/lib/module-binascii.html
|
||||
|
||||
LICENSE
|
||||
|
||||
(c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT).
|
||||
|
||||
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.
|
||||
(end license)
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
local M = {_TYPE='module', _NAME='digest.crc32', _VERSION='0.3.20111128'}
|
||||
|
||||
local type = type
|
||||
local require = require
|
||||
local setmetatable = setmetatable
|
||||
|
||||
--[[
|
||||
Requires the first module listed that exists, else raises like `require`.
|
||||
If a non-string is encountered, it is returned.
|
||||
Second return value is module name loaded (or '').
|
||||
--]]
|
||||
local function requireany(...)
|
||||
local errs = {}
|
||||
for _,name in ipairs{...} do
|
||||
if type(name) ~= 'string' then return name, '' end
|
||||
local ok, mod = pcall(require, name)
|
||||
if ok then return mod, name end
|
||||
errs[#errs+1] = mod
|
||||
end
|
||||
error(table.concat(errs, '\n'), 2)
|
||||
end
|
||||
|
||||
local bit, name_ = requireany('bit', 'bit32', 'bit.numberlua', 'bit53')
|
||||
local bxor = bit.bxor
|
||||
local bnot = bit.bnot
|
||||
local band = bit.band
|
||||
local rshift = bit.rshift
|
||||
|
||||
-- CRC-32-IEEE 802.3 (V.42)
|
||||
local POLY = 0xEDB88320
|
||||
|
||||
-- Memoize function pattern (like http://lua-users.org/wiki/FuncTables ).
|
||||
local function memoize(f)
|
||||
local mt = {}
|
||||
local t = setmetatable({}, mt)
|
||||
function mt:__index(k)
|
||||
local v = f(k); t[k] = v
|
||||
return v
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- CRC table.
|
||||
local crc_table = memoize(function(i)
|
||||
local crc = i
|
||||
for _=1,8 do
|
||||
local b = band(crc, 1)
|
||||
crc = rshift(crc, 1)
|
||||
if b == 1 then crc = bxor(crc, POLY) end
|
||||
end
|
||||
return crc
|
||||
end)
|
||||
|
||||
|
||||
function M.crc32_byte(byte, crc)
|
||||
crc = bnot(crc or 0)
|
||||
local v1 = rshift(crc, 8)
|
||||
local v2 = crc_table[bxor(crc % 256, byte)]
|
||||
return bnot(bxor(v1, v2))
|
||||
end
|
||||
local M_crc32_byte = M.crc32_byte
|
||||
|
||||
|
||||
function M.crc32_string(s, crc)
|
||||
crc = crc or 0
|
||||
for i=1,#s do
|
||||
crc = M_crc32_byte(s:byte(i), crc)
|
||||
end
|
||||
return crc
|
||||
end
|
||||
local M_crc32_string = M.crc32_string
|
||||
|
||||
|
||||
function M.crc32(s, crc)
|
||||
if type(s) == 'string' then
|
||||
return M_crc32_string(s, crc)
|
||||
else
|
||||
return M_crc32_byte(s, crc)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
M.bit = bit -- bit library used
|
||||
|
||||
|
||||
return M
|
||||
127
AIO_Server/LibCompress.lua
Normal file
127
AIO_Server/LibCompress.lua
Normal file
@@ -0,0 +1,127 @@
|
||||
-- LibCompress.lua
|
||||
--
|
||||
-- Authors: jjsheets and Galmok of European Stormrage (Horde)
|
||||
-- Email: sheets.jeff@gmail.com and galmok@gmail.com
|
||||
-- Licence: GPL version 2 (General Public License)
|
||||
--
|
||||
-- Hacked severely by Taehl (SelfMadeSpirit@gmail.com)
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
assert(not TLibCompress, "LibCompress already loaded. Possibly loading different versions of LibCompress")
|
||||
|
||||
TLibCompress = {}
|
||||
|
||||
local assert = assert
|
||||
local type = type
|
||||
local unpack = unpack or table.unpack
|
||||
local tconcat = table.concat
|
||||
local schar = string.char
|
||||
local ssub = string.sub
|
||||
local sbyte = string.byte
|
||||
local mmodf = math.modf
|
||||
local floor = floor or math.floor
|
||||
|
||||
local function encode(x)
|
||||
local bytes = {}
|
||||
local xmod
|
||||
repeat
|
||||
x, xmod = mmodf(x/255)
|
||||
xmod = xmod * 255
|
||||
bytes[#bytes + 1] = xmod
|
||||
until x <= 0
|
||||
if #bytes == 1 and bytes[1] > 0 and bytes[1] < 250 then
|
||||
return schar(bytes[1])
|
||||
else
|
||||
for i = 1, #bytes do bytes[i] = bytes[i] + 1 end
|
||||
return schar(256 - #bytes, unpack(bytes))
|
||||
end
|
||||
end
|
||||
|
||||
local function decode(ss,i)
|
||||
i = i or 1
|
||||
local a = sbyte(ss,i,i)
|
||||
if a > 249 then
|
||||
local r = 0
|
||||
a = 256 - a
|
||||
for n = i+a, i+1, -1 do
|
||||
r = r * 255 + sbyte(ss,n,n) - 1
|
||||
end
|
||||
return r, a + 1
|
||||
else
|
||||
return a, 1
|
||||
end
|
||||
end
|
||||
|
||||
function TLibCompress.CompressLZW(uncompressed)
|
||||
assert(type(uncompressed) == 'string')
|
||||
local result = {'\222'}
|
||||
local ressize = 1
|
||||
local w = ''
|
||||
local dict = {}
|
||||
local dict_size = 256
|
||||
for i = 0, 255 do
|
||||
dict[schar(i)] = i
|
||||
end
|
||||
for i = 1, #uncompressed do
|
||||
local c = ssub(uncompressed,i,i)
|
||||
local wc = w..c
|
||||
if dict[wc] then
|
||||
w = wc
|
||||
else
|
||||
dict[wc] = dict_size
|
||||
dict_size = dict_size +1
|
||||
local r = encode(dict[w])
|
||||
ressize = ressize + #r
|
||||
result[#result + 1] = r
|
||||
w = c
|
||||
end
|
||||
end
|
||||
if w then
|
||||
local r = encode(dict[w])
|
||||
ressize = ressize + #r
|
||||
result[#result + 1] = r
|
||||
end
|
||||
if (#uncompressed+1) > ressize then
|
||||
return tconcat(result)
|
||||
else
|
||||
return '\1'..uncompressed
|
||||
end
|
||||
end
|
||||
|
||||
function TLibCompress.DecompressLZW(compressed)
|
||||
assert(type(compressed) == 'string')
|
||||
local UC
|
||||
UC, compressed = ssub(compressed,1,1), ssub(compressed, 2)
|
||||
if UC == '\1' then
|
||||
return compressed
|
||||
end
|
||||
if UC ~= "\222" then
|
||||
return nil, "Can only decompress LZW compressed data ("..tostring(UC)..")"
|
||||
end
|
||||
local dict_size = 256
|
||||
local dict = {}
|
||||
for i = 0, 255 do
|
||||
dict[i] = schar(i)
|
||||
end
|
||||
local result = {}
|
||||
local t = 1
|
||||
local delta, k
|
||||
k, delta = decode(compressed,t)
|
||||
t = t + delta
|
||||
result[#result+1] = dict[k]
|
||||
local w = dict[k]
|
||||
local entry
|
||||
local csize = #compressed
|
||||
while t <= csize do
|
||||
k, delta = decode(compressed,t)
|
||||
t = t + delta
|
||||
entry = dict[k] or (w..ssub(w,1,1))
|
||||
result[#result+1] = entry
|
||||
dict[dict_size] = w..ssub(entry,1,1)
|
||||
dict_size = dict_size + 1
|
||||
w = entry
|
||||
end
|
||||
return tconcat(result)
|
||||
end
|
||||
|
||||
return TLibCompress
|
||||
29
AIO_Server/bit53.lua
Normal file
29
AIO_Server/bit53.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Provides compatibility for scripts using bit libs for lua versions < 5.3
|
||||
-- Using load to avoid errors when having this file in earlier lua sources than 5.3
|
||||
|
||||
-- check that lua version is higher or equal to 5.3
|
||||
local MIN_LUA_VER = 5.3
|
||||
if tonumber(_VERSION:match("%d+%.?%d*")) >= MIN_LUA_VER then
|
||||
return assert(assert(load( [[
|
||||
local bit53 = {}
|
||||
function bit53.band(a,b)
|
||||
return a&b
|
||||
end
|
||||
function bit53.bor(a,b)
|
||||
return a|b
|
||||
end
|
||||
function bit53.bxor(a,b)
|
||||
return a~b
|
||||
end
|
||||
function bit53.bnot(a)
|
||||
return ~a
|
||||
end
|
||||
function bit53.lshift(a, b)
|
||||
return a<<b
|
||||
end
|
||||
function bit53.rshift(a, b)
|
||||
return a>>b
|
||||
end
|
||||
return bit53
|
||||
]], "bit53" ))())
|
||||
end
|
||||
41
AIO_Server/lualzw-zeros/.gitignore
vendored
Normal file
41
AIO_Server/lualzw-zeros/.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# Compiled Lua sources
|
||||
luac.out
|
||||
|
||||
# luarocks build files
|
||||
*.src.rock
|
||||
*.zip
|
||||
*.tar.gz
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.os
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
*.def
|
||||
*.exp
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
21
AIO_Server/lualzw-zeros/LICENSE
Normal file
21
AIO_Server/lualzw-zeros/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016
|
||||
|
||||
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.
|
||||
72
AIO_Server/lualzw-zeros/README.md
Normal file
72
AIO_Server/lualzw-zeros/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# lualzw
|
||||
A relatively fast LZW compression algorithm in pure lua
|
||||
|
||||
# encoding and decoding
|
||||
Lossless compression for any text. The more repetition in the text, the better.
|
||||
|
||||
16 bit encoding is used. So each 8 bit character is encoded as 16 bit.
|
||||
This means that the dictionary size is 65280.
|
||||
|
||||
Any special characters like `äöå` that are represented with multiple characters are supported. The special characters are split up into single characters that are then encoded and decoded.
|
||||
|
||||
While compressing, the algorithm checks if the result size gets over the input. If it does, then the input is not compressed and the algorithm returns the input prematurely as the compressed result.
|
||||
|
||||
The `zeros` branch contains a version that does not add additional null `\0` characters to the input when encoding. Any existing null characters in input string are preserved as nulls however so make sure your input does not contain nulls.
|
||||
|
||||
# usage
|
||||
```lua
|
||||
local lualzw = require("lualzw")
|
||||
|
||||
local input = "foofoofoofoofoofoofoofoofoo"
|
||||
local compressed = assert(lualzw.compress(input))
|
||||
local decompressed = assert(lualzw.decompress(compressed))
|
||||
assert(input == decompressed)
|
||||
```
|
||||
|
||||
# errors
|
||||
Returns nil and an error message when the algorithm fails to compress or decompress.
|
||||
|
||||
# speed
|
||||
Times are in seconds.
|
||||
Both have the same generated input.
|
||||
The values are an average of 10 tries.
|
||||
|
||||
Note that compressing random generated inputs results usually in bigger result than original. In these cases the algorithms do not compress and return input instead and thus compression result is 100% of input.
|
||||
|
||||
lualzw is at an advantage in cases where compression cannot be done as it stops prematurely and LibCompress does not.
|
||||
Also lualzw is at an advantage in cases where compression can be done as it has a larger dictionary in use.
|
||||
|
||||
Input: 1000000 random generated bytes converted into string
|
||||
|
||||
algorithm|compress|decompress|result % of input
|
||||
---------|--------|----------|-------------
|
||||
lualzw|0.6622|0.0003|100
|
||||
LibCompress|2.1983|0.0024|100
|
||||
|
||||
Input: 1000000 random generated bytes in ASCII range converted into string
|
||||
|
||||
algorithm|compress|decompress|result % of input
|
||||
---------|--------|----------|-------------
|
||||
lualzw|0.812|0.0022|100
|
||||
LibCompress|1.782|0.0007|100
|
||||
|
||||
Input: 1000000 random generated repeating bytes converted into string
|
||||
|
||||
algorithm|compress|decompress|result % of input
|
||||
---------|--------|----------|-------------
|
||||
lualzw|0.3975|0.0262|4.5001
|
||||
LibCompress|0.3907|0.0264|6.6997
|
||||
|
||||
Input: 1000000 of same character
|
||||
|
||||
algorithm|compress|decompress|result % of input
|
||||
---------|--------|----------|-------------
|
||||
lualzw|0.7045|0.0026|0.2829
|
||||
LibCompress|0.6418|0.0038|0.4241
|
||||
|
||||
Input: "ymn32h8hm8ekrwjkrn9f" repeated 50000 times. In total 1000000 bytes
|
||||
|
||||
algorithm|compress|decompress|result % of input
|
||||
---------|--------|----------|-------------
|
||||
lualzw|0.4788|0.0088|1.2629
|
||||
LibCompress|0.4426|0.0093|1.8905
|
||||
166
AIO_Server/lualzw-zeros/lualzw.lua
Normal file
166
AIO_Server/lualzw-zeros/lualzw.lua
Normal file
@@ -0,0 +1,166 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Rochet2
|
||||
|
||||
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.
|
||||
]]
|
||||
|
||||
local char = string.char
|
||||
local type = type
|
||||
local select = select
|
||||
local sub = string.sub
|
||||
local tconcat = table.concat
|
||||
|
||||
local basedictcompress = {}
|
||||
local basedictdecompress = {}
|
||||
for i = 0, 255 do
|
||||
local ic, iic = char(i), char(i, 1)
|
||||
basedictcompress[ic] = iic
|
||||
basedictdecompress[iic] = ic
|
||||
end
|
||||
|
||||
local function dictAddA(str, dict, a, b)
|
||||
if a >= 256 then
|
||||
a, b = 1, b+1
|
||||
if b >= 256 then
|
||||
dict = {}
|
||||
b = 2
|
||||
end
|
||||
end
|
||||
dict[str] = char(a,b)
|
||||
a = a+1
|
||||
return dict, a, b
|
||||
end
|
||||
|
||||
local function compress(input)
|
||||
if type(input) ~= "string" then
|
||||
return nil, "string expected, got "..type(input)
|
||||
end
|
||||
local len = #input
|
||||
if len <= 1 then
|
||||
return "u"..input
|
||||
end
|
||||
|
||||
local dict = {}
|
||||
local a, b = 1, 2
|
||||
|
||||
local result = {"c"}
|
||||
local resultlen = 1
|
||||
local n = 2
|
||||
local word = ""
|
||||
for i = 1, len do
|
||||
local c = sub(input, i, i)
|
||||
local wc = word..c
|
||||
if not (basedictcompress[wc] or dict[wc]) then
|
||||
local write = basedictcompress[word] or dict[word]
|
||||
if not write then
|
||||
return nil, "algorithm error, could not fetch word"
|
||||
end
|
||||
result[n] = write
|
||||
resultlen = resultlen + #write
|
||||
n = n+1
|
||||
if len <= resultlen then
|
||||
return "u"..input
|
||||
end
|
||||
dict, a, b = dictAddA(wc, dict, a, b)
|
||||
word = c
|
||||
else
|
||||
word = wc
|
||||
end
|
||||
end
|
||||
result[n] = basedictcompress[word] or dict[word]
|
||||
resultlen = resultlen+#result[n]
|
||||
n = n+1
|
||||
if len <= resultlen then
|
||||
return "u"..input
|
||||
end
|
||||
return tconcat(result)
|
||||
end
|
||||
|
||||
local function dictAddB(str, dict, a, b)
|
||||
if a >= 256 then
|
||||
a, b = 1, b+1
|
||||
if b >= 256 then
|
||||
dict = {}
|
||||
b = 2
|
||||
end
|
||||
end
|
||||
dict[char(a,b)] = str
|
||||
a = a+1
|
||||
return dict, a, b
|
||||
end
|
||||
|
||||
local function decompress(input)
|
||||
if type(input) ~= "string" then
|
||||
return nil, "string expected, got "..type(input)
|
||||
end
|
||||
|
||||
if #input < 1 then
|
||||
return nil, "invalid input - not a compressed string"
|
||||
end
|
||||
|
||||
local control = sub(input, 1, 1)
|
||||
if control == "u" then
|
||||
return sub(input, 2)
|
||||
elseif control ~= "c" then
|
||||
return nil, "invalid input - not a compressed string"
|
||||
end
|
||||
input = sub(input, 2)
|
||||
local len = #input
|
||||
|
||||
if len < 2 then
|
||||
return nil, "invalid input - not a compressed string"
|
||||
end
|
||||
|
||||
local dict = {}
|
||||
local a, b = 1, 2
|
||||
|
||||
local result = {}
|
||||
local n = 1
|
||||
local last = sub(input, 1, 2)
|
||||
result[n] = basedictdecompress[last] or dict[last]
|
||||
n = n+1
|
||||
for i = 3, len, 2 do
|
||||
local code = sub(input, i, i+1)
|
||||
local lastStr = basedictdecompress[last] or dict[last]
|
||||
if not lastStr then
|
||||
return nil, "could not find last from dict. Invalid input?"
|
||||
end
|
||||
local toAdd = basedictdecompress[code] or dict[code]
|
||||
if toAdd then
|
||||
result[n] = toAdd
|
||||
n = n+1
|
||||
dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b)
|
||||
else
|
||||
local tmp = lastStr..sub(lastStr, 1, 1)
|
||||
result[n] = tmp
|
||||
n = n+1
|
||||
dict, a, b = dictAddB(tmp, dict, a, b)
|
||||
end
|
||||
last = code
|
||||
end
|
||||
return tconcat(result)
|
||||
end
|
||||
|
||||
lualzw = {
|
||||
compress = compress,
|
||||
decompress = decompress,
|
||||
}
|
||||
return lualzw
|
||||
83
AIO_Server/queue.lua
Normal file
83
AIO_Server/queue.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
local Queue = {}
|
||||
function Queue.__index(que, key)
|
||||
return Queue[key]
|
||||
end
|
||||
|
||||
function NewQueue()
|
||||
local t = {first = 0, last = -1}
|
||||
setmetatable(t, Queue)
|
||||
return t
|
||||
end
|
||||
|
||||
function Queue.pushleft(que, value)
|
||||
local first = que.first - 1
|
||||
que.first = first
|
||||
que[first] = value
|
||||
return first
|
||||
end
|
||||
|
||||
function Queue.pushright(que, value)
|
||||
local last = que.last + 1
|
||||
que.last = last
|
||||
que[last] = value
|
||||
return last
|
||||
end
|
||||
|
||||
function Queue.popleft(que)
|
||||
local first = que.first
|
||||
if first > que.last then error("que is empty") end
|
||||
local value = que[first]
|
||||
que[first] = nil -- to allow garbage collection
|
||||
que.first = first + 1
|
||||
return value
|
||||
end
|
||||
|
||||
function Queue.popright(que)
|
||||
local last = que.last
|
||||
if que.first > last then error("que is empty") end
|
||||
local value = que[last]
|
||||
que[last] = nil -- to allow garbage collection
|
||||
que.last = last - 1
|
||||
return value
|
||||
end
|
||||
|
||||
function Queue.peekleft(que)
|
||||
return que[que.first]
|
||||
end
|
||||
|
||||
function Queue.peekright(que)
|
||||
return que[que.last]
|
||||
end
|
||||
|
||||
function Queue.empty(que)
|
||||
return que.last < que.first
|
||||
end
|
||||
|
||||
function Queue.size(que)
|
||||
return que.last - que.first + 1
|
||||
end
|
||||
|
||||
function Queue.clear(que)
|
||||
local l, r = self:getrange()
|
||||
for i = l, r do
|
||||
que[idx] = nil
|
||||
end
|
||||
que.first, que.last = 0, -1
|
||||
end
|
||||
|
||||
function Queue.get(que, idx)
|
||||
if idx < que.first or idx > que.last then
|
||||
return
|
||||
end
|
||||
return que[idx]
|
||||
end
|
||||
|
||||
function Queue.getrange(que)
|
||||
return que.first, que.last
|
||||
end
|
||||
|
||||
function Queue.gettable(que)
|
||||
return que
|
||||
end
|
||||
|
||||
return NewQueue
|
||||
35
development/rift-teleport.ts
Normal file
35
development/rift-teleport.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
const bonusAuraId = 600000;
|
||||
const bonusAreaId = 3817;
|
||||
|
||||
const teleportData = {
|
||||
map: 571,
|
||||
x: 5496.157227,
|
||||
y: 4725.473633,
|
||||
z: -194.177444,
|
||||
o: 2.009775,
|
||||
};
|
||||
|
||||
function exitPlayer(player): boolean {
|
||||
if (player.GetAreaId() === bonusAreaId && !player.HasAura(bonusAuraId)) {
|
||||
player.Teleport(teleportData.map, teleportData.x, teleportData.y, teleportData.z, teleportData.o);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const checkAura = (eventId: number, delay: number, repeats: number, player: Player): void => {
|
||||
if(exitPlayer(player)){
|
||||
player.RemoveEventById(eventId);
|
||||
}
|
||||
}
|
||||
|
||||
const openRift: player_event_on_update_zone = (event: number, player: Player) => {
|
||||
// Exit player should not be here
|
||||
if(!exitPlayer(player)){
|
||||
player.RegisterEvent(checkAura, 60000, 11);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RegisterPlayerEvent(PlayerEvents.PLAYER_EVENT_ON_UPDATE_ZONE, (...args) => openRift(...args));
|
||||
|
||||
Reference in New Issue
Block a user