diff options
Diffstat (limited to 'contrib/luasrcdiet/lua/LuaSrcDiet.lua')
-rwxr-xr-x | contrib/luasrcdiet/lua/LuaSrcDiet.lua | 670 |
1 files changed, 0 insertions, 670 deletions
diff --git a/contrib/luasrcdiet/lua/LuaSrcDiet.lua b/contrib/luasrcdiet/lua/LuaSrcDiet.lua deleted file mode 100755 index d9a41f24b..000000000 --- a/contrib/luasrcdiet/lua/LuaSrcDiet.lua +++ /dev/null @@ -1,670 +0,0 @@ -#!/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() - 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() -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 - ---[[-------------------------------------------------------------------- --- 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 |