├── test ├── test_llex.lua ├── test_optlex.lua ├── test_optparser.lua ├── test_benchmark1.lua └── LuaSrcDiet_fixed_.lua ├── sample ├── strings_on_diet.lua ├── numbers_on_diet.lua ├── numbers_original.lua ├── strings_original.lua ├── llex.lua ├── llex_maximum.lua ├── llex_default.lua ├── llex_basic.lua ├── Makefile ├── optparser.lua ├── dump-parser_llex.dat ├── optlex.lua ├── lparser.lua ├── LuaSrcDiet.lua ├── llex_whitespace.lua ├── llex_locals.lua └── llex_emptylines.lua ├── dist.info ├── CMakeLists.txt ├── Manifest ├── .travis.yml ├── COPYRIGHT_Lua51 ├── COPYRIGHT ├── 5.0 ├── LSDTest.lua └── README ├── cmake ├── FindLua.cmake ├── lua.cmake └── dist.cmake ├── plugin ├── sloc.lua ├── example.lua └── html.lua ├── README ├── llex.lua └── Changelog /test/test_llex.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuaDist/luasrcdiet/HEAD/test/test_llex.lua -------------------------------------------------------------------------------- /sample/strings_on_diet.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuaDist/luasrcdiet/HEAD/sample/strings_on_diet.lua -------------------------------------------------------------------------------- /dist.info: -------------------------------------------------------------------------------- 1 | --- This file is part of LuaDist project 2 | 3 | name = "luasrcdiet" 4 | version = "0.11.2" 5 | 6 | desc = "LuaSrcDiet - Compresses Lua source code by removing unnecessary characters." 7 | author = "Kein-Hong Man" 8 | license = "MIT" 9 | url = "http://luasrcdiet.luaforge.net/" 10 | maintainer = "Peter Kapec" 11 | 12 | depends = { 13 | "lua ~> 5.1", 14 | } 15 | -------------------------------------------------------------------------------- /test/test_optlex.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | test_optlex.lua: Tests for optlex.lua 4 | This file is part of LuaSrcDiet. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- * To test, run it like this: 17 | -- lua5.1 test_optlex.lua 18 | ----------------------------------------------------------------------]] 19 | 20 | ------------------------------------------------------------------------ 21 | -- 22 | ------------------------------------------------------------------------ 23 | -------------------------------------------------------------------------------- /test/test_optparser.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | test_optparser.lua: Tests for optparser.lua 4 | This file is part of LuaSrcDiet. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- * To test, run it like this: 17 | -- lua5.1 test_optparser.lua 18 | ----------------------------------------------------------------------]] 19 | 20 | ------------------------------------------------------------------------ 21 | -- 22 | ------------------------------------------------------------------------ 23 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2007-2012 LuaDist. 2 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 3 | # For details see the COPYRIGHT file distributed with LuaDist. 4 | # Please note that the package source code is licensed under its own license. 5 | 6 | project ( luasrcdiet NONE ) 7 | cmake_minimum_required ( VERSION 2.8 ) 8 | include ( cmake/dist.cmake ) 9 | include ( lua ) 10 | 11 | install_lua_module ( llex llex.lua ) 12 | install_lua_module ( lparser lparser.lua ) 13 | install_lua_module ( optlex optlex.lua ) 14 | install_lua_module ( optparser optparser.lua ) 15 | install_lua_module ( plugin.example plugin/example.lua ) 16 | install_lua_module ( plugin.sloc plugin/sloc.lua ) 17 | install_lua_module ( plugin.html plugin/html.lua ) 18 | install_lua_executable ( luasrcdiet LuaSrcDiet.lua ) 19 | install_data ( README technotes.txt COPYRIGHT Changelog ) 20 | install_example ( sample/ ) 21 | install_test ( test/ ) 22 | -------------------------------------------------------------------------------- /sample/numbers_on_diet.lua: -------------------------------------------------------------------------------- 1 | -- number samples in lieu of automatic testing PUBLIC DOMAIN 2008 2 | -- note: not comprehensive! 3 | -- note: negative numbers are really two tokens, but are included 4 | -- here for convenience 5 | -- 6 | return{ 7 | -- simple numbers 8 | 0, 9 | 1, 10 | -1, 11 | -- hexadecimal 12 | 0, 13 | 16, 14 | 65536, 15 | 3735928559, 16 | 4294967295, 17 | 68719476735, 18 | 16, 19 | 4095, 20 | -- integers 21 | 12345, 22 | -12345, 23 | 123, 24 | 12300, 25 | 123, 26 | 123, 27 | 123, 28 | 123e3, 29 | 12345e5, 30 | -- fractions 31 | 0, 32 | 123.45, 33 | .12345, 34 | .12345, 35 | 1.2345, 36 | 12.345, 37 | .012345, 38 | 12345e-8, 39 | -- scientific 40 | 0, 41 | 0, 42 | 0, 43 | 0, 44 | 12345e96, 45 | 12345e-104, 46 | 12345e96, 47 | 12345, 48 | 12340, 49 | 123400, 50 | 1234e3, 51 | 1234, 52 | 123.4, 53 | 123.4, 54 | 1.234, 55 | .1234, 56 | .001234, 57 | 1234e-7, 58 | 12345e95, 59 | 12345e95, 60 | 12345e96, 61 | 12345e100, 62 | 12345e100, 63 | 12345e100, 64 | } 65 | -------------------------------------------------------------------------------- /Manifest: -------------------------------------------------------------------------------- 1 | Changelog 2 | Manifest 3 | README 4 | technotes.txt 5 | COPYRIGHT 6 | COPYRIGHT_Lua51 7 | LuaSrcDiet.lua 8 | llex.lua 9 | lparser.lua 10 | optlex.lua 11 | optparser.lua 12 | plugin/example.lua 13 | plugin/sloc.lua 14 | plugin/html.lua 15 | sample/Makefile 16 | sample/statistics.txt 17 | sample/LuaSrcDiet.lua 18 | sample/llex.lua 19 | sample/optlex.lua 20 | sample/lparser.lua 21 | sample/optparser.lua 22 | sample/strings_original.lua 23 | sample/strings_on_diet.lua 24 | sample/numbers_original.lua 25 | sample/numbers_on_diet.lua 26 | sample/dump-parser_llex.dat 27 | sample/dump-lexer_llex.dat 28 | sample/llex_basic.lua 29 | sample/llex_maximum.lua 30 | sample/llex_default.lua 31 | sample/llex_locals.lua 32 | sample/llex_whitespace.lua 33 | sample/llex_emptylines.lua 34 | sample/html_sample.html 35 | 5.0/README 36 | 5.0/LuaSrcDiet.lua 37 | 5.0/LuaSrcDiet_.lua 38 | 5.0/LSDTest.lua 39 | test/test_llex.lua 40 | test/test_optlex.lua 41 | test/test_optparser.lua 42 | test/test_benchmark1.lua 43 | test/LuaSrcDiet_fixed.lua 44 | test/LuaSrcDiet_fixed_.lua 45 | -------------------------------------------------------------------------------- /sample/numbers_original.lua: -------------------------------------------------------------------------------- 1 | -- number samples in lieu of automatic testing PUBLIC DOMAIN 2008 2 | -- note: not comprehensive! 3 | -- note: negative numbers are really two tokens, but are included 4 | -- here for convenience 5 | -- 6 | return{ 7 | -- simple numbers 8 | 0, 9 | 1, 10 | -1, 11 | -- hexadecimal 12 | 0x0, 13 | 0x10, 14 | 0x10000, 15 | 0xDEADBEEF, 16 | 0xFFFFFFFF, 17 | 0xFFFFFFFFF, 18 | 0x0010, 19 | 0X0FFF, 20 | -- integers 21 | 12345, 22 | -12345, 23 | 00123, 24 | 12300, 25 | 123., 26 | 123.0, 27 | 123.00000, 28 | 123000, 29 | 1234500000, 30 | -- fractions 31 | .000, 32 | 123.45, 33 | .12345, 34 | 0.12345, 35 | 1.23450000, 36 | 12.345, 37 | .012345, 38 | .00012345, 39 | -- scientific 40 | 0e0, 41 | 0.0e0, 42 | 0.000e1, 43 | .000e-1, 44 | 1.2345e+100, 45 | 0001.2345e-100, 46 | 0001.2345000e100, 47 | 1.234500e+4, 48 | 1234e1, 49 | 1234e2, 50 | 1234e3, 51 | 12.34e2, 52 | 12.34e1, 53 | 1234e-1, 54 | 1234e-3, 55 | 1234e-4, 56 | 1234e-6, 57 | 1234e-7, 58 | 0.12345e+100, 59 | .12345e+100, 60 | 1.2345e+100, 61 | 12345.e+100, 62 | 12345.0e+100, 63 | 12345.000e+100, 64 | } 65 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | # LuaDist Travis-CI Hook 3 | # 4 | 5 | # We assume C build environments 6 | language: C 7 | 8 | # Try using multiple Lua Implementations 9 | env: 10 | - TOOL="gcc" # Use native compiler (GCC usually) 11 | - TOOL="clang" # Use clang 12 | - TOOL="i686-w64-mingw32" # 32bit MinGW 13 | - TOOL="x86_64-w64-mingw32" # 64bit MinGW 14 | - TOOL="arm-linux-gnueabihf" # ARM hard-float (hf), linux 15 | 16 | # Crosscompile builds may fail 17 | matrix: 18 | allow_failures: 19 | - env: TOOL="i686-w64-mingw32" 20 | - env: TOOL="x86_64-w64-mingw32" 21 | - env: TOOL="arm-linux-gnueabihf" 22 | 23 | # Install dependencies 24 | install: 25 | - git clone git://github.com/LuaDist/Tools.git ~/_tools 26 | - ~/_tools/travis/travis install 27 | 28 | # Bootstap 29 | before_script: 30 | - ~/_tools/travis/travis bootstrap 31 | 32 | # Build the module 33 | script: 34 | - ~/_tools/travis/travis build 35 | 36 | # Execute additional tests or commands 37 | after_script: 38 | - ~/_tools/travis/travis test 39 | 40 | # Only watch the master branch 41 | branches: 42 | only: 43 | - master 44 | 45 | # Notify the LuaDist Dev group if needed 46 | notifications: 47 | recipients: 48 | - luadist-dev@googlegroups.com 49 | email: 50 | on_success: change 51 | on_failure: always 52 | -------------------------------------------------------------------------------- /sample/strings_original.lua: -------------------------------------------------------------------------------- 1 | -- string samples in lieu of automatic testing PUBLIC DOMAIN 2008 2 | -- note: does not test newlines other than LF 3 | -- note: does not comprehensively test delimiter switching 4 | -- 5 | -- empty strings 6 | local a="" 7 | local a='' 8 | -- simple string 9 | local a="foo bar BAZ 123" 10 | -- alphanumeric characters 11 | local a="abcdefghijklmnopqrstuvxyz0123456789" 12 | -- escaped 'normal' alphabets 13 | local a="\c\d\e\g\h\i\j\k\l\m\o\p\q\s\u\x\y\z" 14 | -- unescaped punctuation 15 | local a="~`!@#$%^&*()-_=+|[]{};:,.<>/?" 16 | -- escaped punctuation 17 | local a="\~\`\!\@\#\$\%\^\&\*\(\)\-\_\=\+\|\[\]\{\}\;\:\,\.\<\>\/\?" 18 | -- \a\b\f\n\r\t\v -- no change 19 | local a="\a\b\f\n\r\t\v" 20 | -- \\ -- no change 21 | local a="\\" 22 | local a='\\' 23 | -- \"\' -- depends on delim, other can remove \ 24 | local a="\'\"" 25 | local a='\'\"' 26 | -- \ -- normalize the EOL only 27 | local a="\ 28 | " 29 | local a="foo\ 30 | bar" 31 | local a="foo\ 32 | \ 33 | bar" 34 | -- \ddd -- if \a\b\f\n\r\t\v, change to latter 35 | local a="\7\8\9\10\11\12\13" 36 | -- \ddd -- if other < ascii 32, keep ddd but zap leading zeros 37 | local a="\0\1\2\3\4\5\6\14\15" 38 | local a="\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31" 39 | local a="\000\01\004\015" 40 | -- \ddd -- if >= ascii 32, translate into literal, check \\,\",\' cases 41 | local a="\32\065\097\164" 42 | local a="\92" 43 | local a="\39\34" 44 | local a='\39\34' 45 | -- switch delimiters if string becomes shorter 46 | local a="'\"\"\"\"\"\"'" 47 | local a='"\'\'\'\'\'\'"' 48 | -------------------------------------------------------------------------------- /COPYRIGHT_Lua51: -------------------------------------------------------------------------------- 1 | Lua License 2 | ----------- 3 | 4 | Lua is licensed under the terms of the MIT license reproduced below. 5 | This means that Lua is free software and can be used for both academic 6 | and commercial purposes at absolutely no cost. 7 | 8 | For details and rationale, see http://www.lua.org/license.html . 9 | 10 | =============================================================================== 11 | 12 | Copyright (C) 1994-2008 Lua.org, PUC-Rio. 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | 32 | =============================================================================== 33 | 34 | (end of COPYRIGHT) 35 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | LuaSrcDiet License 2 | ------------------ 3 | 4 | LuaSrcDiet is licensed under the terms of the MIT license reproduced 5 | below. This means that LuaSrcDiet is free software and can be used for 6 | both academic and commercial purposes at absolutely no cost. 7 | 8 | Parts of LuaSrcDiet is based on Lua 5 code. See COPYRIGHT_Lua51 9 | (Lua 5.1.3) for Lua 5 license information. 10 | 11 | For details and rationale, see http://www.lua.org/license.html . 12 | 13 | =============================================================================== 14 | 15 | Copyright (C) 2005-2008 Kein-Hong Man 16 | Lua 5.1.3 Copyright (C) 1994-2008 Lua.org, PUC-Rio. 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in 26 | all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 34 | THE SOFTWARE. 35 | 36 | =============================================================================== 37 | 38 | (end of COPYRIGHT) 39 | -------------------------------------------------------------------------------- /5.0/LSDTest.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------- 2 | 3 | LSDTest.lua 4 | Test functions for LuaSrcDiet.lua, including automatic testing. 5 | 6 | Copyright (c) 2005 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions under which this 8 | software may be distributed (basically a Lua 5-style license.) 9 | 10 | http://luaforge.net/projects/luasrcdiet/ 11 | (TODO) http://www.geocities.com/keinhong/luasrcdiet.html 12 | See the ChangeLog for more information. 13 | 14 | ----------------------------------------------------------------------- 15 | --]] 16 | 17 | --[[------------------------------------------------------------------- 18 | -- Notes: 19 | -- * To run: lua LSDTest.lua 20 | -- * TODO include test suite for lexer (currently elsewhere) 21 | -- * Look near the end of the script for testing options. 22 | -- * Setting TEST disables main() in LuaSrcDiet.lua. 23 | ----------------------------------------------------------------------- 24 | --]] 25 | 26 | ----------------------------------------------------------------------- 27 | TEST = true -- always true to skip LuaSrcDiet's main() 28 | require("LuaSrcDiet.lua") 29 | 30 | ----------------------------------------------------------------------- 31 | -- tests adjacent pairs to see whether lexer accepts it 32 | -- * most pairs would not be valid Lua code, just valid lexer input 33 | ----------------------------------------------------------------------- 34 | function TestTokenPairs() 35 | local items = { 36 | "while'foo'", "foo'bar'", "12then", "12.34foo", 37 | "'foo'end", "'foo'bar", "'foo''bar'", "'foo'123", 38 | "12.3'foo'", 39 | } 40 | for _, chunk in ipairs(items) do 41 | llex:setstring(chunk, "(test)") 42 | local ltok, lorig, lval 43 | while ltok ~= "TK_EOS" do 44 | lline = llex.line 45 | ltok, lorig, lval = llex:lex() 46 | lorig = lorig or "" 47 | if ltok ~= "TK_EOS" then 48 | print(lline, ltok, "'"..lorig.."'") 49 | end 50 | end 51 | print() 52 | end 53 | end 54 | 55 | ----------------------------------------------------------------------- 56 | -- simple token dumper for visual inspection 57 | ----------------------------------------------------------------------- 58 | function DumpTokens(filename) 59 | if not filename and type(filename) ~= "string" then 60 | error("invalid filename specified for DumpTokens") 61 | end 62 | local INF = io.open(filename, "rb") 63 | if not INF then 64 | error("cannot open \""..filename.."\" for reading") 65 | end 66 | llex:setinput(INF, filename) 67 | local ltok, lorig, lval 68 | while ltok ~= "TK_EOS" do 69 | lline = llex.line 70 | ltok, lorig, lval = llex:lex() 71 | print(lline, ltok) 72 | lorig = lorig or "" 73 | print("'"..lorig.."'") 74 | lval = lval or "" 75 | print(lval) 76 | end 77 | -- INF closed by llex 78 | end 79 | 80 | ----------------------------------------------------------------------- 81 | -- program entry point 82 | ----------------------------------------------------------------------- 83 | 84 | -- token dump for visual inspection 85 | --[[ 86 | DumpTokens("LuaSrcDiet.lua") 87 | --]] 88 | -- try out no-whitespace pairs 89 | --[[ 90 | TestTokenPairs() 91 | --]] 92 | 93 | -- end of script 94 | -------------------------------------------------------------------------------- /sample/llex.lua: -------------------------------------------------------------------------------- 1 | local u=_G 2 | local d=require"string"module"llex"local c=d.find 3 | local s=d.match 4 | local r=d.sub 5 | local k={}for e in d.gmatch([[ 6 | and break do else elseif end false for function if in 7 | local nil not or repeat return then true until while]],"%S+")do 8 | k[e]=true 9 | end 10 | local e,a,l,t,i 11 | local function o(n,l)local e=#tok+1 12 | tok[e]=n 13 | seminfo[e]=l 14 | tokln[e]=i 15 | end 16 | local function f(n,d)local r=r 17 | local t=r(e,n,n)n=n+1 18 | local e=r(e,n,n)if(e=="\n"or e=="\r")and(e~=t)then 19 | n=n+1 20 | t=t..e 21 | end 22 | if d then o("TK_EOL",t)end 23 | i=i+1 24 | l=n 25 | return n 26 | end 27 | function init(t,n)e=t 28 | a=n 29 | l=1 30 | i=1 31 | tok={}seminfo={}tokln={}local n,r,e,t=c(e,"^(#[^\r\n]*)(\r?\n?)")if n then 32 | l=l+#e 33 | o("TK_COMMENT",e)if#t>0 then f(l,true)end 34 | end 35 | end 36 | function chunkid()if a and s(a,"^[=@]")then 37 | return r(a,2)end 38 | return"[string]"end 39 | function errorline(e,l)local n=error or u.error 40 | n(d.format("%s:%d: %s",chunkid(),l or i,e))end 41 | local a=errorline 42 | local function h(n)local t=r 43 | local r=t(e,n,n)n=n+1 44 | local o=#s(e,"=*",n)n=n+o 45 | l=n 46 | return(t(e,n,n)==r)and o or(-o)-1 47 | end 48 | local function T(d,i)local n=l+1 49 | local r=r 50 | local o=r(e,n,n)if o=="\r"or o=="\n"then 51 | n=f(n)end 52 | local o=n 53 | while true do 54 | local o,u,c=c(e,"([\r\n%]])",n)if not o then 55 | a(d and"unfinished long string"or"unfinished long comment")end 56 | n=o 57 | if c=="]"then 58 | if h(n)==i then 59 | t=r(e,t,l)l=l+1 60 | return t 61 | end 62 | n=l 63 | else 64 | t=t.."\n"n=f(n)end 65 | end 66 | end 67 | local function _(h)local n=l 68 | local i=c 69 | local d=r 70 | while true do 71 | local r,c,o=i(e,"([\n\r\\\"'])",n)if r then 72 | if o=="\n"or o=="\r"then 73 | a("unfinished string")end 74 | n=r 75 | if o=="\\"then 76 | n=n+1 77 | o=d(e,n,n)if o==""then break end 78 | r=i("abfnrtv\n\r",o,1,true)if r then 79 | if r>7 then 80 | n=f(n)else 81 | n=n+1 82 | end 83 | elseif i(o,"%D")then 84 | n=n+1 85 | else 86 | local o,e,l=i(e,"^(%d%d?%d?)",n)n=e+1 87 | if l+1>256 then 88 | a("escape sequence too large")end 89 | end 90 | else 91 | n=n+1 92 | if o==h then 93 | l=n 94 | return d(e,t,n-1)end 95 | end 96 | else 97 | break 98 | end 99 | end 100 | a("unfinished string")end 101 | function llex()local i=c 102 | local c=s 103 | while true do 104 | local n=l 105 | while true do 106 | local s,K,d=i(e,"^([_%a][_%w]*)",n)if s then 107 | l=n+#d 108 | if k[d]then 109 | o("TK_KEYWORD",d)else 110 | o("TK_NAME",d)end 111 | break 112 | end 113 | local d,k,s=i(e,"^(%.?)%d",n)if d then 114 | if s=="."then n=n+1 end 115 | local h,f,t=i(e,"^%d*[%.%d]*([eE]?)",n)n=f+1 116 | if#t==1 then 117 | if c(e,"^[%+%-]",n)then 118 | n=n+1 119 | end 120 | end 121 | local t,n=i(e,"^[_%w]*",n)l=n+1 122 | local e=r(e,d,n)if not u.tonumber(e)then 123 | a("malformed number")end 124 | o("TK_NUMBER",e)break 125 | end 126 | local s,u,k,d=i(e,"^((%s)[ \t\v\f]*)",n)if s then 127 | if d=="\n"or d=="\r"then 128 | f(n,true)else 129 | l=u+1 130 | o("TK_SPACE",k)end 131 | break 132 | end 133 | local d=c(e,"^%p",n)if d then 134 | t=n 135 | local f=i("-[\"'.=<>~",d,1,true)if f then 136 | if f<=2 then 137 | if f==1 then 138 | local a=c(e,"^%-%-(%[?)",n)if a then 139 | n=n+2 140 | local d=-1 141 | if a=="["then 142 | d=h(n)end 143 | if d>=0 then 144 | o("TK_LCOMMENT",T(false,d))else 145 | l=i(e,"[\n\r]",n)or(#e+1)o("TK_COMMENT",r(e,t,l-1))end 146 | break 147 | end 148 | else 149 | local e=h(n)if e>=0 then 150 | o("TK_LSTRING",T(true,e))elseif e==-1 then 151 | o("TK_OP","[")else 152 | a("invalid long string delimiter")end 153 | break 154 | end 155 | elseif f<=5 then 156 | if f<5 then 157 | l=n+1 158 | o("TK_STRING",_(d))break 159 | end 160 | d=c(e,"^%.%.?%.?",n)else 161 | d=c(e,"^%p=?",n)end 162 | end 163 | l=n+#d 164 | o("TK_OP",d)break 165 | end 166 | local e=r(e,n,n)if e~=""then 167 | l=n+1 168 | o("TK_OP",e)break 169 | end 170 | o("TK_EOS","")return 171 | end 172 | end 173 | end 174 | return u.getfenv() -------------------------------------------------------------------------------- /sample/llex_maximum.lua: -------------------------------------------------------------------------------- 1 | local u=_G 2 | local d=require"string"module"llex"local c=d.find 3 | local s=d.match 4 | local r=d.sub 5 | local k={}for e in d.gmatch([[ 6 | and break do else elseif end false for function if in 7 | local nil not or repeat return then true until while]],"%S+")do 8 | k[e]=true 9 | end 10 | local e,a,l,t,i 11 | local function o(n,l)local e=#tok+1 12 | tok[e]=n 13 | seminfo[e]=l 14 | tokln[e]=i 15 | end 16 | local function f(n,d)local r=r 17 | local t=r(e,n,n)n=n+1 18 | local e=r(e,n,n)if(e=="\n"or e=="\r")and(e~=t)then 19 | n=n+1 20 | t=t..e 21 | end 22 | if d then o("TK_EOL",t)end 23 | i=i+1 24 | l=n 25 | return n 26 | end 27 | function init(t,n)e=t 28 | a=n 29 | l=1 30 | i=1 31 | tok={}seminfo={}tokln={}local n,r,e,t=c(e,"^(#[^\r\n]*)(\r?\n?)")if n then 32 | l=l+#e 33 | o("TK_COMMENT",e)if#t>0 then f(l,true)end 34 | end 35 | end 36 | function chunkid()if a and s(a,"^[=@]")then 37 | return r(a,2)end 38 | return"[string]"end 39 | function errorline(e,l)local n=error or u.error 40 | n(d.format("%s:%d: %s",chunkid(),l or i,e))end 41 | local a=errorline 42 | local function h(n)local t=r 43 | local r=t(e,n,n)n=n+1 44 | local o=#s(e,"=*",n)n=n+o 45 | l=n 46 | return(t(e,n,n)==r)and o or(-o)-1 47 | end 48 | local function T(d,i)local n=l+1 49 | local r=r 50 | local o=r(e,n,n)if o=="\r"or o=="\n"then 51 | n=f(n)end 52 | local o=n 53 | while true do 54 | local o,u,c=c(e,"([\r\n%]])",n)if not o then 55 | a(d and"unfinished long string"or"unfinished long comment")end 56 | n=o 57 | if c=="]"then 58 | if h(n)==i then 59 | t=r(e,t,l)l=l+1 60 | return t 61 | end 62 | n=l 63 | else 64 | t=t.."\n"n=f(n)end 65 | end 66 | end 67 | local function _(h)local n=l 68 | local i=c 69 | local d=r 70 | while true do 71 | local r,c,o=i(e,"([\n\r\\\"'])",n)if r then 72 | if o=="\n"or o=="\r"then 73 | a("unfinished string")end 74 | n=r 75 | if o=="\\"then 76 | n=n+1 77 | o=d(e,n,n)if o==""then break end 78 | r=i("abfnrtv\n\r",o,1,true)if r then 79 | if r>7 then 80 | n=f(n)else 81 | n=n+1 82 | end 83 | elseif i(o,"%D")then 84 | n=n+1 85 | else 86 | local o,e,l=i(e,"^(%d%d?%d?)",n)n=e+1 87 | if l+1>256 then 88 | a("escape sequence too large")end 89 | end 90 | else 91 | n=n+1 92 | if o==h then 93 | l=n 94 | return d(e,t,n-1)end 95 | end 96 | else 97 | break 98 | end 99 | end 100 | a("unfinished string")end 101 | function llex()local i=c 102 | local c=s 103 | while true do 104 | local n=l 105 | while true do 106 | local s,K,d=i(e,"^([_%a][_%w]*)",n)if s then 107 | l=n+#d 108 | if k[d]then 109 | o("TK_KEYWORD",d)else 110 | o("TK_NAME",d)end 111 | break 112 | end 113 | local d,k,s=i(e,"^(%.?)%d",n)if d then 114 | if s=="."then n=n+1 end 115 | local h,f,t=i(e,"^%d*[%.%d]*([eE]?)",n)n=f+1 116 | if#t==1 then 117 | if c(e,"^[%+%-]",n)then 118 | n=n+1 119 | end 120 | end 121 | local t,n=i(e,"^[_%w]*",n)l=n+1 122 | local e=r(e,d,n)if not u.tonumber(e)then 123 | a("malformed number")end 124 | o("TK_NUMBER",e)break 125 | end 126 | local s,u,k,d=i(e,"^((%s)[ \t\v\f]*)",n)if s then 127 | if d=="\n"or d=="\r"then 128 | f(n,true)else 129 | l=u+1 130 | o("TK_SPACE",k)end 131 | break 132 | end 133 | local d=c(e,"^%p",n)if d then 134 | t=n 135 | local f=i("-[\"'.=<>~",d,1,true)if f then 136 | if f<=2 then 137 | if f==1 then 138 | local a=c(e,"^%-%-(%[?)",n)if a then 139 | n=n+2 140 | local d=-1 141 | if a=="["then 142 | d=h(n)end 143 | if d>=0 then 144 | o("TK_LCOMMENT",T(false,d))else 145 | l=i(e,"[\n\r]",n)or(#e+1)o("TK_COMMENT",r(e,t,l-1))end 146 | break 147 | end 148 | else 149 | local e=h(n)if e>=0 then 150 | o("TK_LSTRING",T(true,e))elseif e==-1 then 151 | o("TK_OP","[")else 152 | a("invalid long string delimiter")end 153 | break 154 | end 155 | elseif f<=5 then 156 | if f<5 then 157 | l=n+1 158 | o("TK_STRING",_(d))break 159 | end 160 | d=c(e,"^%.%.?%.?",n)else 161 | d=c(e,"^%p=?",n)end 162 | end 163 | l=n+#d 164 | o("TK_OP",d)break 165 | end 166 | local e=r(e,n,n)if e~=""then 167 | l=n+1 168 | o("TK_OP",e)break 169 | end 170 | o("TK_EOS","")return 171 | end 172 | end 173 | end 174 | return u.getfenv() -------------------------------------------------------------------------------- /test/test_benchmark1.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | test_benchmark1.lua: Tests source/binary chunk load performance. 4 | This file is part of LuaSrcDiet. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- * Please have the appropriate files in the appropriate areas before 17 | -- running this script. 18 | -- * Binary chunk files are missing! See sample/Makefile or generate 19 | -- using something like: 20 | -- luac5.1 -s -o llex.out llex.lua 21 | -- * There has to be LuaSrcDiet_fixed.lua and LuaSrcDiet_fixed_.lua 22 | -- because loadstring() can't handle scripts with a shbang line. 23 | -- * To test, run it like this: 24 | -- lua5.1 test_benchmark1.lua 25 | ----------------------------------------------------------------------]] 26 | 27 | ------------------------------------------------------------------------ 28 | -- input datasets 29 | ------------------------------------------------------------------------ 30 | 31 | local FILE_SETS = { 32 | -- uncompressed originals are in the parent directory 33 | [[ 34 | LuaSrcDiet_fixed.lua 35 | ../llex.lua 36 | ../lparser.lua 37 | ../optlex.lua 38 | ../optparser.lua 39 | ]], 40 | -- compressed sources (using --maximum) are in the ../sample directory 41 | [[ 42 | LuaSrcDiet_fixed_.lua 43 | ../sample/llex.lua 44 | ../sample/lparser.lua 45 | ../sample/optlex.lua 46 | ../sample/optparser.lua 47 | ]], 48 | -- stripped binary need to be generated and put somewhere 49 | [[ 50 | ../sample/LuaSrcDiet.out 51 | ../sample/llex.out 52 | ../sample/lparser.out 53 | ../sample/optlex.out 54 | ../sample/optparser.out 55 | ]], 56 | -- dummy to measure call overhead 57 | [[ 58 | foo 59 | foo 60 | foo 61 | foo 62 | foo 63 | ]], 64 | } 65 | 66 | ------------------------------------------------------------------------ 67 | -- read data from file 68 | ------------------------------------------------------------------------ 69 | 70 | local function load_file(fname) 71 | if fname == "foo" then return "" end 72 | local INF = io.open(fname, "rb") 73 | if not INF then error("cannot open \""..fname.."\" for reading") end 74 | local dat = INF:read("*a") 75 | if not dat then error("cannot read from \""..fname.."\"") end 76 | INF:close() 77 | return dat 78 | end 79 | 80 | ------------------------------------------------------------------------ 81 | -- load data 82 | ------------------------------------------------------------------------ 83 | 84 | local DATA_SETS = {} 85 | 86 | for k = 1, #FILE_SETS do 87 | local dset = {} 88 | local fset = {} 89 | for fn in string.gmatch(FILE_SETS[k], "%S+") do 90 | fset[#fset + 1] = fn 91 | end 92 | dset.size = 0 93 | for j = 1, #fset do 94 | local data = load_file(fset[j]) 95 | if not loadstring(data) then 96 | error("error trying to load script \""..fset[j].."\"") 97 | end 98 | dset[j] = data 99 | dset.size = dset.size + #data 100 | end 101 | DATA_SETS[k] = dset 102 | end 103 | 104 | ------------------------------------------------------------------------ 105 | -- benchmark tester 106 | ------------------------------------------------------------------------ 107 | 108 | local DURATION = 1 -- how long the benchmark should run 109 | 110 | local loadstring = loadstring 111 | local time = os.time 112 | 113 | for k = 1, #DATA_SETS do 114 | local dset = DATA_SETS[k] 115 | local tnow, elapsed, c = time(), 0, 0 116 | while time() == tnow do end -- wait for second to click over 117 | tnow = time() 118 | while true do 119 | for i = 1, #dset do 120 | local fn = loadstring(dset[i]) 121 | end 122 | c = c + 1 123 | if time() > tnow then 124 | tnow = time() 125 | elapsed = elapsed + 1 126 | if elapsed == DURATION then break end 127 | end 128 | end 129 | print("Set: ", k) 130 | print("Size: ", dset.size) 131 | print("Iterations: ", c) 132 | print("Duration: ", DURATION) 133 | print() 134 | end 135 | 136 | -- end of script 137 | -------------------------------------------------------------------------------- /sample/llex_default.lua: -------------------------------------------------------------------------------- 1 | local c=_G 2 | local h=require"string" 3 | module"llex" 4 | local l=h.find 5 | local m=h.match 6 | local n=h.sub 7 | local w={} 8 | for e in h.gmatch([[ 9 | and break do else elseif end false for function if in 10 | local nil not or repeat return then true until while]],"%S+")do 11 | w[e]=true 12 | end 13 | local e, 14 | r, 15 | a, 16 | i, 17 | s 18 | local function o(t,a) 19 | local e=#tok+1 20 | tok[e]=t 21 | seminfo[e]=a 22 | tokln[e]=s 23 | end 24 | local function d(t,h) 25 | local n=n 26 | local i=n(e,t,t) 27 | t=t+1 28 | local e=n(e,t,t) 29 | if(e=="\n"or e=="\r")and(e~=i)then 30 | t=t+1 31 | i=i..e 32 | end 33 | if h then o("TK_EOL",i)end 34 | s=s+1 35 | a=t 36 | return t 37 | end 38 | function init(i,t) 39 | e=i 40 | r=t 41 | a=1 42 | s=1 43 | tok={} 44 | seminfo={} 45 | tokln={} 46 | local t,n,e,i=l(e,"^(#[^\r\n]*)(\r?\n?)") 47 | if t then 48 | a=a+#e 49 | o("TK_COMMENT",e) 50 | if#i>0 then d(a,true)end 51 | end 52 | end 53 | function chunkid() 54 | if r and m(r,"^[=@]")then 55 | return n(r,2) 56 | end 57 | return"[string]" 58 | end 59 | function errorline(e,a) 60 | local t=error or c.error 61 | t(h.format("%s:%d: %s",chunkid(),a or s,e)) 62 | end 63 | local r=errorline 64 | local function u(t) 65 | local i=n 66 | local n=i(e,t,t) 67 | t=t+1 68 | local o=#m(e,"=*",t) 69 | t=t+o 70 | a=t 71 | return(i(e,t,t)==n)and o or(-o)-1 72 | end 73 | local function f(h,s) 74 | local t=a+1 75 | local n=n 76 | local o=n(e,t,t) 77 | if o=="\r"or o=="\n"then 78 | t=d(t) 79 | end 80 | local o=t 81 | while true do 82 | local o,c,l=l(e,"([\r\n%]])",t) 83 | if not o then 84 | r(h and"unfinished long string"or 85 | "unfinished long comment") 86 | end 87 | t=o 88 | if l=="]"then 89 | if u(t)==s then 90 | i=n(e,i,a) 91 | a=a+1 92 | return i 93 | end 94 | t=a 95 | else 96 | i=i.."\n" 97 | t=d(t) 98 | end 99 | end 100 | end 101 | local function y(u) 102 | local t=a 103 | local s=l 104 | local h=n 105 | while true do 106 | local n,l,o=s(e,"([\n\r\\\"\'])",t) 107 | if n then 108 | if o=="\n"or o=="\r"then 109 | r("unfinished string") 110 | end 111 | t=n 112 | if o=="\\"then 113 | t=t+1 114 | o=h(e,t,t) 115 | if o==""then break end 116 | n=s("abfnrtv\n\r",o,1,true) 117 | if n then 118 | if n>7 then 119 | t=d(t) 120 | else 121 | t=t+1 122 | end 123 | elseif s(o,"%D")then 124 | t=t+1 125 | else 126 | local o,e,a=s(e,"^(%d%d?%d?)",t) 127 | t=e+1 128 | if a+1>256 then 129 | r("escape sequence too large") 130 | end 131 | end 132 | else 133 | t=t+1 134 | if o==u then 135 | a=t 136 | return h(e,i,t-1) 137 | end 138 | end 139 | else 140 | break 141 | end 142 | end 143 | r("unfinished string") 144 | end 145 | function llex() 146 | local s=l 147 | local l=m 148 | while true do 149 | local t=a 150 | while true do 151 | local m,p,h=s(e,"^([_%a][_%w]*)",t) 152 | if m then 153 | a=t+#h 154 | if w[h]then 155 | o("TK_KEYWORD",h) 156 | else 157 | o("TK_NAME",h) 158 | end 159 | break 160 | end 161 | local h,w,m=s(e,"^(%.?)%d",t) 162 | if h then 163 | if m=="."then t=t+1 end 164 | local u,d,i=s(e,"^%d*[%.%d]*([eE]?)",t) 165 | t=d+1 166 | if#i==1 then 167 | if l(e,"^[%+%-]",t)then 168 | t=t+1 169 | end 170 | end 171 | local i,t=s(e,"^[_%w]*",t) 172 | a=t+1 173 | local e=n(e,h,t) 174 | if not c.tonumber(e)then 175 | r("malformed number") 176 | end 177 | o("TK_NUMBER",e) 178 | break 179 | end 180 | local m,c,w,h=s(e,"^((%s)[ \t\v\f]*)",t) 181 | if m then 182 | if h=="\n"or h=="\r"then 183 | d(t,true) 184 | else 185 | a=c+1 186 | o("TK_SPACE",w) 187 | end 188 | break 189 | end 190 | local h=l(e,"^%p",t) 191 | if h then 192 | i=t 193 | local d=s("-[\"\'.=<>~",h,1,true) 194 | if d then 195 | if d<=2 then 196 | if d==1 then 197 | local r=l(e,"^%-%-(%[?)",t) 198 | if r then 199 | t=t+2 200 | local h=-1 201 | if r=="["then 202 | h=u(t) 203 | end 204 | if h>=0 then 205 | o("TK_LCOMMENT",f(false,h)) 206 | else 207 | a=s(e,"[\n\r]",t)or(#e+1) 208 | o("TK_COMMENT",n(e,i,a-1)) 209 | end 210 | break 211 | end 212 | else 213 | local e=u(t) 214 | if e>=0 then 215 | o("TK_LSTRING",f(true,e)) 216 | elseif e==-1 then 217 | o("TK_OP","[") 218 | else 219 | r("invalid long string delimiter") 220 | end 221 | break 222 | end 223 | elseif d<=5 then 224 | if d<5 then 225 | a=t+1 226 | o("TK_STRING",y(h)) 227 | break 228 | end 229 | h=l(e,"^%.%.?%.?",t) 230 | else 231 | h=l(e,"^%p=?",t) 232 | end 233 | end 234 | a=t+#h 235 | o("TK_OP",h) 236 | break 237 | end 238 | local e=n(e,t,t) 239 | if e~=""then 240 | a=t+1 241 | o("TK_OP",e) 242 | break 243 | end 244 | o("TK_EOS","") 245 | return 246 | end 247 | end 248 | end 249 | return c.getfenv() 250 | -------------------------------------------------------------------------------- /cmake/FindLua.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua library 2 | # This module defines 3 | # LUA_EXECUTABLE, if found 4 | # LUA_FOUND, if false, do not try to link to Lua 5 | # LUA_LIBRARIES 6 | # LUA_INCLUDE_DIR, where to find lua.h 7 | # LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) 8 | # 9 | # Note that the expected include convention is 10 | # #include "lua.h" 11 | # and not 12 | # #include 13 | # This is because, the lua location is not standardized and may exist 14 | # in locations other than lua/ 15 | 16 | #============================================================================= 17 | # Copyright 2007-2009 Kitware, Inc. 18 | # Modified to support Lua 5.2 by LuaDist 2012 19 | # 20 | # Distributed under the OSI-approved BSD License (the "License"); 21 | # see accompanying file Copyright.txt for details. 22 | # 23 | # This software is distributed WITHOUT ANY WARRANTY; without even the 24 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 25 | # See the License for more information. 26 | #============================================================================= 27 | # (To distribute this file outside of CMake, substitute the full 28 | # License text for the above reference.) 29 | # 30 | # The required version of Lua can be specified using the 31 | # standard syntax, e.g. FIND_PACKAGE(Lua 5.1) 32 | # Otherwise the module will search for any available Lua implementation 33 | 34 | # Always search for non-versioned lua first (recommended) 35 | SET(_POSSIBLE_LUA_INCLUDE include include/lua) 36 | SET(_POSSIBLE_LUA_EXECUTABLE lua) 37 | SET(_POSSIBLE_LUA_LIBRARY lua) 38 | 39 | # Determine possible naming suffixes (there is no standard for this) 40 | IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 41 | SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") 42 | ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 43 | SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") 44 | ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 45 | 46 | # Set up possible search names and locations 47 | FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) 48 | LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") 49 | LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") 50 | LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") 51 | ENDFOREACH(_SUFFIX) 52 | 53 | # Find the lua executable 54 | FIND_PROGRAM(LUA_EXECUTABLE 55 | NAMES ${_POSSIBLE_LUA_EXECUTABLE} 56 | ) 57 | 58 | # Find the lua header 59 | FIND_PATH(LUA_INCLUDE_DIR lua.h 60 | HINTS 61 | $ENV{LUA_DIR} 62 | PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} 63 | PATHS 64 | ~/Library/Frameworks 65 | /Library/Frameworks 66 | /usr/local 67 | /usr 68 | /sw # Fink 69 | /opt/local # DarwinPorts 70 | /opt/csw # Blastwave 71 | /opt 72 | ) 73 | 74 | # Find the lua library 75 | FIND_LIBRARY(LUA_LIBRARY 76 | NAMES ${_POSSIBLE_LUA_LIBRARY} 77 | HINTS 78 | $ENV{LUA_DIR} 79 | PATH_SUFFIXES lib64 lib 80 | PATHS 81 | ~/Library/Frameworks 82 | /Library/Frameworks 83 | /usr/local 84 | /usr 85 | /sw 86 | /opt/local 87 | /opt/csw 88 | /opt 89 | ) 90 | 91 | IF(LUA_LIBRARY) 92 | # include the math library for Unix 93 | IF(UNIX AND NOT APPLE) 94 | FIND_LIBRARY(LUA_MATH_LIBRARY m) 95 | SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 96 | # For Windows and Mac, don't need to explicitly include the math library 97 | ELSE(UNIX AND NOT APPLE) 98 | SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 99 | ENDIF(UNIX AND NOT APPLE) 100 | ENDIF(LUA_LIBRARY) 101 | 102 | # Determine Lua version 103 | IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") 104 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") 105 | 106 | STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") 107 | UNSET(lua_version_str) 108 | ENDIF() 109 | 110 | INCLUDE(FindPackageHandleStandardArgs) 111 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 112 | # all listed variables are TRUE 113 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua 114 | REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR 115 | VERSION_VAR LUA_VERSION_STRING) 116 | 117 | MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) 118 | 119 | -------------------------------------------------------------------------------- /plugin/sloc.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | sloc.lua: Calculates SLOC for Lua 5.1 scripts 4 | This file is part of LuaSrcDiet. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- WARNING: highly experimental! interface liable to change 17 | -- * SLOC's behaviour is based on David Wheeler's SLOCCount. 18 | -- * Empty lines and comment don't count as significant. 19 | -- * Empty lines in long strings are also insignificant. This is 20 | -- debatable. In SLOCCount, this allows counting of invalid multi- 21 | -- line strings for C. But an empty line is still an empty line. 22 | -- * Ignores the --quiet option, print own result line. 23 | ----------------------------------------------------------------------]] 24 | 25 | local string = require "string" 26 | local table = require "table" 27 | local base = _G 28 | module "sloc" 29 | 30 | ------------------------------------------------------------------------ 31 | -- initialization 32 | ------------------------------------------------------------------------ 33 | 34 | local option -- local reference to list of options 35 | local srcfl -- source file name 36 | 37 | function init(_option, _srcfl, _destfl) 38 | option = _option 39 | option.QUIET = true 40 | srcfl = _srcfl 41 | end 42 | 43 | ------------------------------------------------------------------------ 44 | -- splits a block into a table of lines (minus EOLs) 45 | ------------------------------------------------------------------------ 46 | 47 | local function split(blk) 48 | local lines = {} 49 | local i, nblk = 1, #blk 50 | while i <= nblk do 51 | local p, q, r, s = string.find(blk, "([\r\n])([\r\n]?)", i) 52 | if not p then 53 | p = nblk + 1 54 | end 55 | lines[#lines + 1] = string.sub(blk, i, p - 1) 56 | i = p + 1 57 | if p < nblk and q > p and r ~= s then -- handle Lua-style CRLF, LFCR 58 | i = i + 1 59 | end 60 | end 61 | return lines 62 | end 63 | 64 | ------------------------------------------------------------------------ 65 | -- post-lexing processing, can work on lexer table output 66 | ------------------------------------------------------------------------ 67 | 68 | function post_lex(toklist, seminfolist, toklnlist) 69 | local lnow, sloc = 0, 0 70 | local function chk(ln) -- if a new line, count it as an SLOC 71 | if ln > lnow then -- new line # must be > old line # 72 | sloc = sloc + 1; lnow = ln 73 | end 74 | end 75 | for i = 1, #toklist do -- enumerate over all tokens 76 | local tok, info, ln 77 | = toklist[i], seminfolist[i], toklnlist[i] 78 | -------------------------------------------------------------------- 79 | if tok == "TK_KEYWORD" or tok == "TK_NAME" or -- significant 80 | tok == "TK_NUMBER" or tok == "TK_OP" then 81 | chk(ln) 82 | -------------------------------------------------------------------- 83 | -- Both TK_STRING and TK_LSTRING may be multi-line, hence, a loop 84 | -- is needed in order to mark off lines one-by-one. Since llex.lua 85 | -- currently returns the line number of the last part of the string, 86 | -- we must subtract in order to get the starting line number. 87 | -------------------------------------------------------------------- 88 | elseif tok == "TK_STRING" then -- possible multi-line 89 | local t = split(info) 90 | ln = ln - #t + 1 91 | for j = 1, #t do 92 | chk(ln); ln = ln + 1 93 | end 94 | -------------------------------------------------------------------- 95 | elseif tok == "TK_LSTRING" then -- possible multi-line 96 | local t = split(info) 97 | ln = ln - #t + 1 98 | for j = 1, #t do 99 | if t[j] ~= "" then chk(ln) end 100 | ln = ln + 1 101 | end 102 | -------------------------------------------------------------------- 103 | -- other tokens are comments or whitespace and are ignored 104 | -------------------------------------------------------------------- 105 | end 106 | end--for 107 | base.print(srcfl..": "..sloc) -- display result 108 | option.EXIT = true 109 | end 110 | 111 | return base.getfenv() 112 | -------------------------------------------------------------------------------- /plugin/example.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | example.lua: Example of a plugin for LuaSrcDiet 4 | This file is part of LuaSrcDiet. 5 | 6 | Kein-Hong Man 2008 PUBLIC DOMAIN 7 | 8 | See the ChangeLog for more information. 9 | 10 | ----------------------------------------------------------------------]] 11 | 12 | --[[-------------------------------------------------------------------- 13 | -- NOTES: 14 | -- WARNING: highly experimental! interface liable to change 15 | -- * Any function can be omitted and LuaSrcDiet won't call it. 16 | -- * The functions are: 17 | -- (1) init(_option, _srcfl, _destfl) 18 | -- (2) post_load(z) can return z 19 | -- (3) post_lex(toklist, seminfolist, toklnlist) 20 | -- (4) post_parse(globalinfo, localinfo) 21 | -- (5) post_optparse() 22 | -- (6) post_optlex(toklist, seminfolist, toklnlist) 23 | -- * Older tables can be copied and kept in the plugin and used later. 24 | -- * If you modify 'option', remember that LuaSrcDiet might be 25 | -- processing more than one file. 26 | -- * Arrangement of the functions is not final! 27 | -- * TODO: not sure about return base.getfenv() if module name different 28 | -- * TODO: can't process additional options from command line yet 29 | ----------------------------------------------------------------------]] 30 | 31 | local string = require "string" 32 | local table = require "table" 33 | local base = _G 34 | module "example" 35 | 36 | ------------------------------------------------------------------------ 37 | -- option handling, plays nice with --quiet option 38 | ------------------------------------------------------------------------ 39 | 40 | local option -- local reference to list of options 41 | local srcfl, destfl -- filenames 42 | local old_quiet 43 | 44 | local function print(...) -- handle quiet option 45 | if option.QUIET then return end 46 | base.print(...) 47 | end 48 | 49 | ------------------------------------------------------------------------ 50 | -- initialization 51 | ------------------------------------------------------------------------ 52 | 53 | function init(_option, _srcfl, _destfl) 54 | option = _option 55 | srcfl, destfl = _srcfl, _destfl 56 | -- plugin can impose its own option starting from here 57 | end 58 | 59 | ------------------------------------------------------------------------ 60 | -- message display, post-load processing, can return z 61 | ------------------------------------------------------------------------ 62 | 63 | function post_load(z) 64 | -- this message will print after the LuaSrcDiet title message 65 | print([[ 66 | Example plugin module for LuaSrcDiet 67 | ]]) 68 | print("Example: source file name is '"..srcfl.."'") 69 | print("Example: destination file name is '"..destfl.."'") 70 | print("Example: the size of the source file is "..#z.." bytes") 71 | -- returning z is optional; this allows optional replacement of 72 | -- the source data prior to lexing 73 | return z 74 | end 75 | 76 | ------------------------------------------------------------------------ 77 | -- post-lexing processing, can work on lexer table output 78 | ------------------------------------------------------------------------ 79 | 80 | function post_lex(toklist, seminfolist, toklnlist) 81 | print("Example: the number of lexed elements is "..#toklist) 82 | end 83 | 84 | ------------------------------------------------------------------------ 85 | -- post-parsing processing, gives globalinfo, localinfo 86 | ------------------------------------------------------------------------ 87 | 88 | function post_parse(globalinfo, localinfo) 89 | print("Example: size of globalinfo is "..#globalinfo) 90 | print("Example: size of localinfo is "..#localinfo) 91 | old_quiet = option.QUIET 92 | option.QUIET = true 93 | end 94 | 95 | ------------------------------------------------------------------------ 96 | -- post-parser optimization processing, can get tables from elsewhere 97 | ------------------------------------------------------------------------ 98 | 99 | function post_optparse() 100 | option.QUIET = old_quiet 101 | print("Example: pretend to do post-optparse") 102 | end 103 | 104 | ------------------------------------------------------------------------ 105 | -- post-lexer optimization processing, can get tables from elsewhere 106 | ------------------------------------------------------------------------ 107 | 108 | function post_optlex(toklist, seminfolist, toklnlist) 109 | print("Example: pretend to do post-optlex") 110 | -- restore old settings, other file might need original settings 111 | option.QUIET = old_quiet 112 | -- option.EXIT can be set at the end of any post_* function to stop 113 | -- further processing and exit for the current file being worked on 114 | -- in this case, final stats printout is disabled and the output will 115 | -- not be written to the destination file 116 | option.EXIT = true 117 | end 118 | 119 | return base.getfenv() 120 | -------------------------------------------------------------------------------- /sample/llex_basic.lua: -------------------------------------------------------------------------------- 1 | local base=_G 2 | local string=require"string" 3 | module"llex" 4 | local find=string.find 5 | local match=string.match 6 | local sub=string.sub 7 | local kw={} 8 | for v in string.gmatch([[ 9 | and break do else elseif end false for function if in 10 | local nil not or repeat return then true until while]],"%S+")do 11 | kw[v]=true 12 | end 13 | local z, 14 | sourceid, 15 | I, 16 | buff, 17 | ln 18 | local function addtoken(token,info) 19 | local i=#tok+1 20 | tok[i]=token 21 | seminfo[i]=info 22 | tokln[i]=ln 23 | end 24 | local function inclinenumber(i,is_tok) 25 | local sub=sub 26 | local old=sub(z,i,i) 27 | i=i+1 28 | local c=sub(z,i,i) 29 | if(c=="\n"or c=="\r")and(c~=old)then 30 | i=i+1 31 | old=old..c 32 | end 33 | if is_tok then addtoken("TK_EOL",old)end 34 | ln=ln+1 35 | I=i 36 | return i 37 | end 38 | function init(_z,_sourceid) 39 | z=_z 40 | sourceid=_sourceid 41 | I=1 42 | ln=1 43 | tok={} 44 | seminfo={} 45 | tokln={} 46 | local p,_,q,r=find(z,"^(#[^\r\n]*)(\r?\n?)") 47 | if p then 48 | I=I+#q 49 | addtoken("TK_COMMENT",q) 50 | if#r>0 then inclinenumber(I,true)end 51 | end 52 | end 53 | function chunkid() 54 | if sourceid and match(sourceid,"^[=@]")then 55 | return sub(sourceid,2) 56 | end 57 | return"[string]" 58 | end 59 | function errorline(s,line) 60 | local e=error or base.error 61 | e(string.format("%s:%d: %s",chunkid(),line or ln,s)) 62 | end 63 | local errorline=errorline 64 | local function skip_sep(i) 65 | local sub=sub 66 | local s=sub(z,i,i) 67 | i=i+1 68 | local count=#match(z,"=*",i) 69 | i=i+count 70 | I=i 71 | return(sub(z,i,i)==s)and count or(-count)-1 72 | end 73 | local function read_long_string(is_str,sep) 74 | local i=I+1 75 | local sub=sub 76 | local c=sub(z,i,i) 77 | if c=="\r"or c=="\n"then 78 | i=inclinenumber(i) 79 | end 80 | local j=i 81 | while true do 82 | local p,q,r=find(z,"([\r\n%]])",i) 83 | if not p then 84 | errorline(is_str and"unfinished long string"or 85 | "unfinished long comment") 86 | end 87 | i=p 88 | if r=="]"then 89 | if skip_sep(i)==sep then 90 | buff=sub(z,buff,I) 91 | I=I+1 92 | return buff 93 | end 94 | i=I 95 | else 96 | buff=buff.."\n" 97 | i=inclinenumber(i) 98 | end 99 | end 100 | end 101 | local function read_string(del) 102 | local i=I 103 | local find=find 104 | local sub=sub 105 | while true do 106 | local p,q,r=find(z,"([\n\r\\\"\'])",i) 107 | if p then 108 | if r=="\n"or r=="\r"then 109 | errorline("unfinished string") 110 | end 111 | i=p 112 | if r=="\\"then 113 | i=i+1 114 | r=sub(z,i,i) 115 | if r==""then break end 116 | p=find("abfnrtv\n\r",r,1,true) 117 | if p then 118 | if p>7 then 119 | i=inclinenumber(i) 120 | else 121 | i=i+1 122 | end 123 | elseif find(r,"%D")then 124 | i=i+1 125 | else 126 | local p,q,s=find(z,"^(%d%d?%d?)",i) 127 | i=q+1 128 | if s+1>256 then 129 | errorline("escape sequence too large") 130 | end 131 | end 132 | else 133 | i=i+1 134 | if r==del then 135 | I=i 136 | return sub(z,buff,i-1) 137 | end 138 | end 139 | else 140 | break 141 | end 142 | end 143 | errorline("unfinished string") 144 | end 145 | function llex() 146 | local find=find 147 | local match=match 148 | while true do 149 | local i=I 150 | while true do 151 | local p,_,r=find(z,"^([_%a][_%w]*)",i) 152 | if p then 153 | I=i+#r 154 | if kw[r]then 155 | addtoken("TK_KEYWORD",r) 156 | else 157 | addtoken("TK_NAME",r) 158 | end 159 | break 160 | end 161 | local p,_,r=find(z,"^(%.?)%d",i) 162 | if p then 163 | if r=="."then i=i+1 end 164 | local _,q,r=find(z,"^%d*[%.%d]*([eE]?)",i) 165 | i=q+1 166 | if#r==1 then 167 | if match(z,"^[%+%-]",i)then 168 | i=i+1 169 | end 170 | end 171 | local _,q=find(z,"^[_%w]*",i) 172 | I=q+1 173 | local v=sub(z,p,q) 174 | if not base.tonumber(v)then 175 | errorline("malformed number") 176 | end 177 | addtoken("TK_NUMBER",v) 178 | break 179 | end 180 | local p,q,r,t=find(z,"^((%s)[ \t\v\f]*)",i) 181 | if p then 182 | if t=="\n"or t=="\r"then 183 | inclinenumber(i,true) 184 | else 185 | I=q+1 186 | addtoken("TK_SPACE",r) 187 | end 188 | break 189 | end 190 | local r=match(z,"^%p",i) 191 | if r then 192 | buff=i 193 | local p=find("-[\"\'.=<>~",r,1,true) 194 | if p then 195 | if p<=2 then 196 | if p==1 then 197 | local c=match(z,"^%-%-(%[?)",i) 198 | if c then 199 | i=i+2 200 | local sep=-1 201 | if c=="["then 202 | sep=skip_sep(i) 203 | end 204 | if sep>=0 then 205 | addtoken("TK_LCOMMENT",read_long_string(false,sep)) 206 | else 207 | I=find(z,"[\n\r]",i)or(#z+1) 208 | addtoken("TK_COMMENT",sub(z,buff,I-1)) 209 | end 210 | break 211 | end 212 | else 213 | local sep=skip_sep(i) 214 | if sep>=0 then 215 | addtoken("TK_LSTRING",read_long_string(true,sep)) 216 | elseif sep==-1 then 217 | addtoken("TK_OP","[") 218 | else 219 | errorline("invalid long string delimiter") 220 | end 221 | break 222 | end 223 | elseif p<=5 then 224 | if p<5 then 225 | I=i+1 226 | addtoken("TK_STRING",read_string(r)) 227 | break 228 | end 229 | r=match(z,"^%.%.?%.?",i) 230 | else 231 | r=match(z,"^%p=?",i) 232 | end 233 | end 234 | I=i+#r 235 | addtoken("TK_OP",r) 236 | break 237 | end 238 | local r=sub(z,i,i) 239 | if r~=""then 240 | I=i+1 241 | addtoken("TK_OP",r) 242 | break 243 | end 244 | addtoken("TK_EOS","") 245 | return 246 | end 247 | end 248 | end 249 | return base.getfenv() 250 | -------------------------------------------------------------------------------- /sample/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Simple Makefile to generate a compressed version of LuaSrcDiet 3 | # This file is part of LuaSrcDiet. 4 | # 5 | # by Kein-Hong Man , PUBLIC DOMAIN 6 | # 7 | 8 | # Lua 5.1 executables, please adjust name to suit 9 | LUA = lua5.1 10 | LUAC = luac5.1 11 | 12 | # LuaSrcDiet, to be in parent directory 13 | LSD = $(LUA) LuaSrcDiet.lua 14 | LSD_MAX = $(LSD) --maximum 15 | 16 | all: 17 | @echo Sample Generator: 18 | @echo ------------------------------------------------------------- 19 | @echo max - generate a basic set of max squeezed sources 20 | @echo test - run Lua on results from 'max' 21 | @echo strings - generate output for string samples 22 | @echo numbers - generate output for number samples 23 | @echo child - attempt to run second generation and diff the output 24 | @echo dump - generate token and parser dump samples 25 | @echo different - generate different llex.lua processed files 26 | @echo binchunks - needed for test_benchmark1.lua testing 27 | @echo html - sample for HTML plugin 28 | @echo ------------------------------------------------------------- 29 | @echo clean - cleanup for 'child' and 'binchunks' 30 | @echo ------------------------------------------------------------- 31 | 32 | # basic test, run LuaSrcDiet files, to generate a compressed, hopefully working, version 33 | max: 34 | cd .. && $(LSD_MAX) --details -o sample/LuaSrcDiet.lua LuaSrcDiet.lua > sample/statistics.txt 35 | cd .. && $(LSD_MAX) --details -o sample/llex.lua llex.lua >> sample/statistics.txt 36 | cd .. && $(LSD_MAX) --details -o sample/optlex.lua optlex.lua >> sample/statistics.txt 37 | cd .. && $(LSD_MAX) --details -o sample/lparser.lua lparser.lua >> sample/statistics.txt 38 | cd .. && $(LSD_MAX) --details -o sample/optparser.lua optparser.lua >> sample/statistics.txt 39 | 40 | # test first generation, generated from above 41 | test: 42 | $(LUA) LuaSrcDiet.lua -v 43 | $(LUA) llex.lua 44 | $(LUA) optlex.lua 45 | $(LUA) lparser.lua 46 | $(LUA) optparser.lua 47 | 48 | # generate different samples 49 | different: 50 | cd .. && $(LSD) llex.lua -o sample/llex_basic.lua --basic 51 | cd .. && $(LSD) llex.lua -o sample/llex_maximum.lua --maximum 52 | cd .. && $(LSD) llex.lua -o sample/llex_default.lua 53 | cd .. && $(LSD) llex.lua -o sample/llex_locals.lua --none --opt-locals 54 | cd .. && $(LSD) llex.lua -o sample/llex_whitespace.lua --none --opt-whitespace 55 | cd .. && $(LSD) llex.lua -o sample/llex_emptylines.lua --none --opt-emptylines 56 | 57 | # disable aggressive optimizations so output can be studied for string optimization 58 | strings: 59 | cd .. && $(LSD) --none --opt-strings \ 60 | -o sample/strings_on_diet.lua \ 61 | sample/strings_original.lua 62 | 63 | # disable aggressive optimizations so output can be studied for number optimization 64 | numbers: 65 | cd .. && $(LSD) --none --opt-numbers \ 66 | -o sample/numbers_on_diet.lua \ 67 | sample/numbers_original.lua 68 | 69 | # dump samples 70 | dump: 71 | cd .. && $(LSD) --dump-lexer llex.lua > sample/dump-lexer_llex.dat 72 | cd .. && $(LSD) --dump-parser llex.lua > sample/dump-parser_llex.dat 73 | 74 | # run compressed version of LuaSrcDiet, compare first generation with second generation 75 | # note that you cannot enable entropy reduction if you wish to compare with diff 76 | CHILD = $(LUA) LuaSrcDiet.lua --maximum --noopt-entropy 77 | child: 78 | cd .. && $(LSD_MAX) --noopt-entropy -o sample/LuaSrcDiet_1.lua LuaSrcDiet.lua > /dev/null 79 | cd .. && $(LSD_MAX) --noopt-entropy -o sample/llex_1.lua llex.lua > /dev/null 80 | cd .. && $(LSD_MAX) --noopt-entropy -o sample/optlex_1.lua optlex.lua > /dev/null 81 | cd .. && $(LSD_MAX) --noopt-entropy -o sample/lparser_1.lua lparser.lua > /dev/null 82 | cd .. && $(LSD_MAX) --noopt-entropy -o sample/optparser_1.lua optparser.lua > /dev/null 83 | @echo ------------------------------------------------------------- 84 | $(CHILD) LuaSrcDiet_1.lua > child.txt 85 | $(CHILD) llex_1.lua >> child.txt 86 | $(CHILD) optlex_1.lua >> child.txt 87 | $(CHILD) lparser_1.lua >> child.txt 88 | $(CHILD) optparser_1.lua >> child.txt 89 | @echo ------------------------------------------------------------- 90 | diff -s -urN LuaSrcDiet_1.lua LuaSrcDiet_1_.lua >> child.txt 91 | diff -s -urN llex_1.lua llex_1_.lua >> child.txt 92 | diff -s -urN optlex_1.lua optlex_1_.lua >> child.txt 93 | diff -s -urN lparser_1.lua lparser_1_.lua >> child.txt 94 | diff -s -urN optparser_1.lua optparser_1_.lua >> child.txt 95 | @echo ------------------------------------------------------------- 96 | tail -n6 child.txt 97 | 98 | binchunks: 99 | $(LUAC) -s -o LuaSrcDiet.out LuaSrcDiet.lua 100 | $(LUAC) -s -o llex.out llex.lua 101 | $(LUAC) -s -o optlex.out optlex.lua 102 | $(LUAC) -s -o lparser.out lparser.lua 103 | $(LUAC) -s -o optparser.out optparser.lua 104 | 105 | html: 106 | cd .. && $(LSD) --plugin html plugin/html.lua -o sample/html_sample.html 107 | 108 | # clean up "child" processing, for testing running of a compressed LuaSrcDiet 109 | clean: 110 | rm -f LuaSrcDiet_1.lua llex_1.lua optlex_1.lua lparser_1.lua optparser_1.lua 111 | rm -f LuaSrcDiet_1_.lua llex_1_.lua optlex_1_.lua lparser_1_.lua optparser_1_.lua 112 | rm -f child.txt 113 | rm -f LuaSrcDiet.out llex.out optlex.out lparser.out optparser.out 114 | 115 | .PHONY: all clean max test strings numbers child dump different binchunks html 116 | -------------------------------------------------------------------------------- /sample/optparser.lua: -------------------------------------------------------------------------------- 1 | local p=_G 2 | local a=require"string"local b=require"table"module"optparser"local t="etaoinshrdlucmfwypvbgkqjxz_ETAOINSHRDLUCMFWYPVBGKQJXZ"local c="etaoinshrdlucmfwypvbgkqjxz_0123456789ETAOINSHRDLUCMFWYPVBGKQJXZ"local _={}for e in a.gmatch([[ 3 | and break do else elseif end false for function if in 4 | local nil not or repeat return then true until while 5 | self]],"%S+")do 6 | _[e]=true 7 | end 8 | local z,N,L,o,w,K,T,i 9 | local function O(e)local a={}for t=1,#e do 10 | local l=e[t]local n=l.name 11 | if not a[n]then 12 | a[n]={decl=0,token=0,size=0,}end 13 | local e=a[n]e.decl=e.decl+1 14 | local a=l.xref 15 | local o=#a 16 | e.token=e.token+o 17 | e.size=e.size+o*#n 18 | if l.decl then 19 | l.id=t 20 | l.xcount=o 21 | if o>1 then 22 | l.first=a[2]l.last=a[o]end 23 | else 24 | e.id=t 25 | end 26 | end 27 | return a 28 | end 29 | local function E(e)local d=a.byte 30 | local a=a.char 31 | local l={TK_KEYWORD=true,TK_NAME=true,TK_NUMBER=true,TK_STRING=true,TK_LSTRING=true,}if not e["opt-comments"]then 32 | l.TK_COMMENT=true 33 | l.TK_LCOMMENT=true 34 | end 35 | local n={}for e=1,#z do 36 | n[e]=N[e]end 37 | for e=1,#o do 38 | local e=o[e]local l=e.xref 39 | for e=1,e.xcount do 40 | local e=l[e]n[e]=""end 41 | end 42 | local e={}for l=0,255 do e[l]=0 end 43 | for o=1,#z do 44 | local n,o=z[o],n[o]if l[n]then 45 | for l=1,#o do 46 | local l=d(o,l)e[l]=e[l]+1 47 | end 48 | end 49 | end 50 | local function o(o)local l={}for n=1,#o do 51 | local o=d(o,n)l[n]={c=o,freq=e[o],}end 52 | b.sort(l,function(l,e)return l.freq>e.freq 53 | end)local o={}for e=1,#l do 54 | o[e]=a(l[e].c)end 55 | return b.concat(o)end 56 | t=o(t)c=o(c)end 57 | local function C()local l 58 | local d,r=#t,#c 59 | local e=T 60 | if ee 69 | local o=e%d 70 | e=(e-o)/d 71 | o=o+1 72 | l=a.sub(t,o,o)while n>1 do 73 | local o=e%r 74 | e=(e-o)/r 75 | o=o+1 76 | l=l..a.sub(c,o,o)n=n-1 77 | end 78 | end 79 | T=T+1 80 | return l,w[l]~=nil 81 | end 82 | local function I(N,v,L,n)local e=print or p.print 83 | local l=a.format 84 | local I=n.DETAILS 85 | local u,k,T,O,K,y,m,h,_,g,d,f,s,A,x,t,r,c,w,z=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 86 | local function n(e,l)if e==0 then return 0 end 87 | return l/e 88 | end 89 | for l,e in p.pairs(N)do 90 | u=u+1 91 | d=d+e.token 92 | t=t+e.size 93 | end 94 | for l,e in p.pairs(v)do 95 | k=k+1 96 | m=m+e.decl 97 | f=f+e.token 98 | r=r+e.size 99 | end 100 | for l,e in p.pairs(L)do 101 | T=T+1 102 | h=h+e.decl 103 | s=s+e.token 104 | c=c+e.size 105 | end 106 | O=u+k 107 | _=y+m 108 | A=d+f 109 | w=t+r 110 | K=u+T 111 | g=y+h 112 | x=d+s 113 | z=t+c 114 | if I then 115 | local u={}for l,e in p.pairs(N)do 116 | e.name=l 117 | u[#u+1]=e 118 | end 119 | b.sort(u,function(l,e)return l.size>e.size 120 | end)local m,T="%8s%8s%10s %s","%8d%8d%10.2f %s"local p=a.rep("-",44)e("*** global variable list (sorted by size) ***\n"..p)e(l(m,"Token","Input","Input","Global"))e(l(m,"Count","Bytes","Average","Name"))e(p)for o=1,#u do 121 | local o=u[o]e(l(T,o.token,o.size,n(o.token,o.size),o.name))end 122 | e(p)e(l(T,d,t,n(d,t),"TOTAL"))e(p.."\n")local t,u="%8s%8s%8s%10s%8s%10s %s","%8d%8d%8d%10.2f%8d%10.2f %s"local d=a.rep("-",70)e("*** local variable list (sorted by allocation order) ***\n"..d)e(l(t,"Decl.","Token","Input","Input","Output","Output","Global"))e(l(t,"Count","Count","Bytes","Average","Bytes","Average","Name"))e(d)for a=1,#i do 123 | local d=i[a]local a=L[d]local c,t=0,0 124 | for l=1,#o do 125 | local e=o[l]if e.name==d then 126 | c=c+e.xcount 127 | t=t+e.xcount*#e.oldname 128 | end 129 | end 130 | e(l(u,a.decl,a.token,t,n(c,t),a.size,n(a.token,a.size),d))end 131 | e(d)e(l(u,h,s,r,n(f,r),c,n(s,c),"TOTAL"))e(d.."\n")end 132 | local i,o="%-16s%8s%8s%8s%8s%10s","%-16s%8d%8d%8d%8d%10.2f"local a=a.rep("-",58)e("*** local variable optimization summary ***\n"..a)e(l(i,"Variable","Unique","Decl.","Token","Size","Average"))e(l(i,"Types","Names","Count","Count","Bytes","Bytes"))e(a)e(l(o,"Global",u,y,d,t,n(d,t)))e(a)e(l(o,"Local (in)",k,m,f,r,n(f,r)))e(l(o,"TOTAL (in)",O,_,A,w,n(A,w)))e(a)e(l(o,"Local (out)",T,h,s,c,n(s,c)))e(l(o,"TOTAL (out)",K,g,x,z,n(x,z)))e(a.."\n")end 133 | function optimize(c,e,l,a,n)z,N,L,o=e,l,a,n 134 | T=0 135 | i={}w=O(L)K=O(o)if c["opt-entropy"]then 136 | E(c)end 137 | local e={}for l=1,#o do 138 | e[l]=o[l]end 139 | b.sort(e,function(e,l)return e.xcount>l.xcount 140 | end)local n,l,r={},1,false 141 | for o=1,#e do 142 | local e=e[o]if not e.isself then 143 | n[l]=e 144 | l=l+1 145 | else 146 | r=true 147 | end 148 | end 149 | e=n 150 | local d=#e 151 | while d>0 do 152 | local t,l 153 | repeat 154 | t,l=C()until not _[t]i[#i+1]=t 155 | local n=d 156 | if l then 157 | local a=L[w[t].id].xref 158 | local c=#a 159 | for l=1,d do 160 | local l=e[l]local t,e=l.act,l.rem 161 | while e<0 do 162 | e=o[-e].rem 163 | end 164 | local o 165 | for l=1,c do 166 | local l=a[l]if l>=t and l<=e then o=true end 167 | end 168 | if o then 169 | l.skip=true 170 | n=n-1 171 | end 172 | end 173 | end 174 | while n>0 do 175 | local l=1 176 | while e[l].skip do 177 | l=l+1 178 | end 179 | n=n-1 180 | local a=e[l]l=l+1 181 | a.newname=t 182 | a.skip=true 183 | a.done=true 184 | local d,c=a.first,a.last 185 | local r=a.xref 186 | if d and n>0 then 187 | local t=n 188 | while t>0 do 189 | while e[l].skip do 190 | l=l+1 191 | end 192 | t=t-1 193 | local e=e[l]l=l+1 194 | local t,l=e.act,e.rem 195 | while l<0 do 196 | l=o[-l].rem 197 | end 198 | if not(cl)then 199 | if t>=a.act then 200 | for o=1,a.xcount do 201 | local o=r[o]if o>=t and o<=l then 202 | n=n-1 203 | e.skip=true 204 | break 205 | end 206 | end 207 | else 208 | if e.last and e.last>=a.act then 209 | n=n-1 210 | e.skip=true 211 | end 212 | end 213 | end 214 | if n==0 then break end 215 | end 216 | end 217 | end 218 | local o,l={},1 219 | for n=1,d do 220 | local e=e[n]if not e.done then 221 | e.skip=false 222 | o[l]=e 223 | l=l+1 224 | end 225 | end 226 | e=o 227 | d=#e 228 | end 229 | for e=1,#o do 230 | local e=o[e]local l=e.xref 231 | if e.newname then 232 | for o=1,e.xcount do 233 | local l=l[o]N[l]=e.newname 234 | end 235 | e.name,e.oldname=e.newname,e.name 236 | else 237 | e.oldname=e.name 238 | end 239 | end 240 | if r then 241 | i[#i+1]="self"end 242 | local e=O(o)I(w,K,e,c)end 243 | -------------------------------------------------------------------------------- /sample/dump-parser_llex.dat: -------------------------------------------------------------------------------- 1 | *** Local/Global Variable Tracker Tables *** 2 | ------------------------------------------------------------------------ 3 | GLOBALS 4 | ------------------------------------------------------------------------ 5 | (1) '_G' -> 13 6 | (2) 'require' -> 21 7 | (3) 'module' -> 25 8 | (4) 'tok' -> 171 178 440 9 | (5) 'seminfo' -> 188 450 10 | (6) 'tokln' -> 198 460 11 | (7) 'init' -> 395 12 | (8) 'chunkid' -> 572 661 13 | (9) 'errorline' -> 628 685 14 | (10) 'error' -> 643 15 | (11) 'llex' -> 1541 16 | ------------------------------------------------------------------------ 17 | LOCALS (decl=declared act=activated rem=removed) 18 | ------------------------------------------------------------------------ 19 | (1) 'base' decl:9 act:15 rem:2607 -> 9 647 1883 2601 20 | (2) 'string' decl:17 act:25 rem:2607 -> 17 36 46 56 83 654 21 | (3) 'find' decl:32 act:40 rem:2607 -> 32 497 945 1129 1552 22 | (4) 'match' decl:42 act:50 rem:2607 -> 42 583 750 1561 23 | (5) 'sub' decl:52 act:68 rem:2607 -> 52 236 596 711 860 1138 1865 2252 2519 24 | (6) 'kw' decl:70 act:77 rem:2607 -> 70 96 1640 25 | (7) 'v' decl:79 act:96 rem:113 -> 79 98 26 | (8) 'z' decl:115 act:151 rem:2607 -> 115 247 278 404 499 722 752 787 871 947 1026 1162 1246 1393 1485 1609 1701 1757 1796 1838 1867 1941 2035 2125 2226 2239 2254 2431 2460 2521 27 | (9) 'sourceid' decl:121 act:151 rem:2607 -> 121 413 579 585 598 28 | (10) 'I' decl:127 act:151 rem:2607 -> 127 372 422 515 519 549 775 845 1032 1036 1040 1061 1120 1474 1578 1626 1848 1991 2220 2260 2394 2479 2542 29 | (11) 'buff' decl:133 act:151 rem:2607 -> 133 1020 1029 1051 1069 1073 1488 2052 2257 30 | (12) 'ln' decl:139 act:151 rem:2607 -> 139 205 361 365 431 670 31 | (13) 'addtoken' decl:155 act:156 rem:2607 -> 155 350 527 1648 1662 1904 2004 2200 2247 2311 2338 2405 2491 2553 2570 32 | (14) 'token' decl:157 act:161 rem:217 -> 157 185 33 | (15) 'info' decl:160 act:161 rem:217 -> 160 195 34 | (16) 'i' decl:166 act:178 rem:217 -> 166 180 190 200 35 | (17) 'inclinenumber' decl:221 act:222 rem:2607 -> 221 547 906 1082 1319 1979 36 | (18) 'i' decl:223 act:227 rem:393 -> 223 250 253 257 261 281 284 319 323 376 381 37 | (19) 'is_tok' decl:226 act:227 rem:393 -> 226 346 38 | (20) 'sub' decl:232 act:239 rem:393 -> 232 245 276 39 | (21) 'old' decl:241 act:257 rem:393 -> 241 313 332 336 355 40 | (22) 'c' decl:272 act:288 rem:393 -> 272 291 299 309 338 41 | (23) '_z' decl:397 act:401 rem:570 -> 397 408 42 | (24) '_sourceid' decl:400 act:401 rem:570 -> 400 417 43 | (25) 'p' decl:484 act:506 rem:570 -> 484 508 44 | (26) '_' decl:487 act:506 rem:570 -> 487 45 | (27) 'q' decl:490 act:506 rem:570 -> 490 524 532 46 | (28) 'r' decl:493 act:506 rem:570 -> 493 539 47 | (29) 's' decl:630 act:634 rem:679 -> 630 673 48 | (30) 'line' decl:633 act:634 rem:679 -> 633 666 49 | (31) 'e' decl:639 act:652 rem:679 -> 639 652 50 | (32) 'errorline' decl:681 act:695 rem:2607 -> 681 968 1200 1431 1524 1895 2350 51 | (33) 'skip_sep' decl:699 act:700 rem:2607 -> 699 1008 2178 2294 52 | (34) 'i' decl:701 act:702 rem:826 -> 701 725 728 732 736 758 764 768 779 790 793 53 | (35) 'sub' decl:707 act:714 rem:826 -> 707 720 785 54 | (36) 's' decl:716 act:732 rem:826 -> 716 798 55 | (37) 'count' decl:745 act:764 rem:826 -> 745 772 803 809 56 | (38) 'read_long_string' decl:830 act:831 rem:2607 -> 830 2205 2316 57 | (39) 'is_str' decl:832 act:836 rem:1104 -> 832 970 58 | (40) 'sep' decl:835 act:836 rem:1104 -> 835 1015 59 | (41) 'i' decl:841 act:854 rem:1104 -> 841 874 877 902 908 923 953 986 1010 1057 1078 1084 60 | (42) 'sub' decl:856 act:863 rem:1104 -> 856 869 1024 61 | (43) 'c' decl:865 act:881 rem:1104 -> 865 883 891 62 | (44) 'j' decl:919 act:926 rem:1104 -> 919 63 | (45) 'p' decl:935 act:959 rem:1091 -> 935 963 990 64 | (46) 'q' decl:938 act:959 rem:1091 -> 938 65 | (47) 'r' decl:941 act:959 rem:1091 -> 941 995 66 | (48) 'read_string' decl:1108 act:1109 rem:2607 -> 1108 2410 67 | (49) 'del' decl:1110 act:1111 rem:1539 -> 1110 1467 68 | (50) 'i' decl:1116 act:1123 rem:1539 -> 1116 1168 1209 1229 1233 1249 1252 1315 1321 1328 1332 1360 1364 1399 1403 1450 1454 1478 1491 69 | (51) 'find' decl:1125 act:1132 rem:1539 -> 1125 1160 1277 1347 1391 70 | (52) 'sub' decl:1134 act:1141 rem:1539 -> 1134 1244 1483 71 | (53) 'p' decl:1150 act:1174 rem:1520 -> 1150 1176 1213 1273 1297 1306 72 | (54) 'q' decl:1153 act:1174 rem:1520 -> 1153 73 | (55) 'r' decl:1156 act:1174 rem:1520 -> 1156 1183 1191 1218 1240 1258 1282 1349 1463 74 | (56) 'p' decl:1381 act:1403 rem:1443 -> 1381 75 | (57) 'q' decl:1384 act:1403 rem:1443 -> 1384 1407 76 | (58) 's' decl:1387 act:1403 rem:1443 -> 1387 1416 77 | (59) 'find' decl:1548 act:1555 rem:2599 -> 1548 1607 1699 1755 1836 1939 2065 2224 78 | (60) 'match' decl:1557 act:1564 rem:2599 -> 1557 1794 2033 2123 2429 2458 79 | (61) 'i' decl:1574 act:1584 rem:2593 -> 1574 1615 1630 1707 1730 1734 1763 1767 1802 1810 1814 1844 1947 1981 2041 2056 2131 2142 2146 2180 2232 2296 2398 2437 2466 2483 2524 2527 2546 80 | (62) 'p' decl:1597 act:1619 rem:-65 -> 1597 1621 81 | (63) '_' decl:1600 act:1619 rem:-66 -> 1600 82 | (64) 'r' decl:1603 act:1619 rem:-67 -> 1603 1635 1642 1653 1667 83 | (65) 'p' decl:1689 act:1711 rem:-74 -> 1689 1713 1870 84 | (66) '_' decl:1692 act:1711 rem:2589 -> 1692 85 | (67) 'r' decl:1695 act:1711 rem:-76 -> 1695 1722 86 | (68) '_' decl:1745 act:1767 rem:-71 -> 1745 87 | (69) 'q' decl:1748 act:1767 rem:-72 -> 1748 1771 88 | (70) 'r' decl:1751 act:1767 rem:1918 -> 1751 1781 89 | (71) '_' decl:1829 act:1848 rem:1918 -> 1829 90 | (72) 'q' decl:1832 act:1848 rem:1918 -> 1832 1852 1873 91 | (73) 'v' decl:1861 act:1879 rem:1918 -> 1861 1887 1909 92 | (74) 'p' decl:1926 act:1951 rem:2589 -> 1926 1953 93 | (75) 'q' decl:1929 act:1951 rem:2589 -> 1929 1995 94 | (76) 'r' decl:1932 act:1951 rem:-78 -> 1932 2009 95 | (77) 't' decl:1935 act:1951 rem:2589 -> 1935 1960 1968 96 | (78) 'r' decl:2029 act:2045 rem:-83 -> 2029 2047 2070 2412 2425 2454 2488 2496 97 | (79) 'p' decl:2061 act:2080 rem:2507 -> 2061 2082 2095 2106 2372 2383 98 | (80) 'c' decl:2119 act:2135 rem:2283 -> 2119 2137 2165 99 | (81) 'sep' decl:2155 act:2163 rem:2277 -> 2155 2174 2189 2210 100 | (82) 'sep' decl:2290 act:2300 rem:2364 -> 2290 2302 2321 2328 101 | (83) 'r' decl:2515 act:2531 rem:2589 -> 2515 2533 2558 102 | ------------------------------------------------------------------------ 103 | 104 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | LuaSrcDiet 3 | Compresses Lua source code by removing unnecessary characters. 4 | 5 | Copyright (c) 2005-2008 Kein-Hong Man 6 | The COPYRIGHT file describes the conditions 7 | under which this software may be distributed. 8 | 9 | http://luaforge.net/projects/luasrcdiet/ 10 | http://luasrcdiet.luaforge.net/ 11 | 12 | -- 13 | 14 | For the older unmaintained version of LuaSrcDiet for Lua 5.0.2 sources, 15 | please see the 5.0/README file. 16 | 17 | -- 18 | 19 | PREVIEW NOTES 20 | 21 | See also: http://luasrcdiet.luaforge.net/ 22 | 23 | The 0.11.0 release of LuaSrcDiet has a local variable name optimizer. 24 | Local variable names are renamed into the shortest possible names. In 25 | addition, variable names are reused whenever possible, reducing the 26 | number of unique variable names. Several hundred local variable names 27 | can be reduced into 53 or less unique names, which allows all locals 28 | to be single-character in length. 29 | 30 | The local variable name optimizer uses a full parser of Lua 5.1 source 31 | code, thus it can rename all local variables, including function 32 | parameters. It should handle the implicit "self" parameter gracefully. 33 | The optimizer needs more testing, but is already able to optimize the 34 | LuaSrcDiet sources itself and generate correct Lua output. 35 | 36 | String and number token optimizations are also performed, apart from the 37 | usual whitespace, line ending and comment removal. Numbers can switch 38 | between different formats. Strings can be simplified and can switch 39 | delimiters between " or ' characters. 40 | 41 | Most options can also be enabled or disabled separately, for maximum 42 | flexibility. If you need to keep a copyright message in the optimized 43 | output, the --keep option can keep block comments that contain a certain 44 | string. 45 | 46 | For samples, see the sample/ directory. Performance statistics can be 47 | found in the sample/statistics.txt file. Preliminary test samples for 48 | strings and numbers can also be found in the sample/ directory. 49 | 50 | Priority for future work: 51 | (a) automatic tests for lexer/parser optimizations 52 | (b) integrity checking, token stream check or binary chunk check 53 | 54 | -- 55 | 56 | INTRODUCTION 57 | 58 | ... 59 | 60 | WARNING! Locals optimization does NOT have support for 'arg' vararg 61 | functions (LUA_COMPAT_VARARG). 62 | 63 | -- 64 | 65 | WHAT'S NEW 66 | 67 | Major changes for version 0.11.2 (see the ChangeLog as well): 68 | * improved local variable name allocation, more efficient now 69 | * added experimental --plugin option with an example plugin script 70 | * added a SLOC plugin to count SLOC for Lua 5.1 source files 71 | * added a HTML plugin to see globals and locals marked 72 | 73 | Major changes for version 0.11.1 (see the ChangeLog as well): 74 | * --detail option for more string, number and local variable info 75 | * fixed a local rename bug that generates names that are keywords 76 | * added explanatory notes on local variable optimization 77 | * added --opt-entropy option for locals to reduce symbol entropy 78 | 79 | Major changes for version 0.11.0 (see the ChangeLog as well): 80 | * Local variable name optimization. 81 | * Many options and sample output added. 82 | 83 | Major changes for version 0.10.2 (see the ChangeLog as well): 84 | * Aggressive optimizations for string and number tokens. 85 | * Minor bug fixes. 86 | 87 | Major changes for version 0.10.1 (see the ChangeLog as well): 88 | * Totally rewritten for Lua 5.1.x. 89 | 90 | -- 91 | 92 | USAGE OPTIONS 93 | 94 | ... 95 | 96 | Example of summary data display: 97 | 98 | Statistics for: LuaSrcDiet.lua -> sample/LuaSrcDiet.lua 99 | 100 | *** local variable optimization summary *** 101 | ---------------------------------------------------------- 102 | Variable Unique Decl. Token Size Average 103 | Types Names Count Count Bytes Bytes 104 | ---------------------------------------------------------- 105 | Global 10 0 19 95 5.00 106 | ---------------------------------------------------------- 107 | Local (in) 88 153 683 3340 4.89 108 | TOTAL (in) 98 153 702 3435 4.89 109 | ---------------------------------------------------------- 110 | Local (out) 32 153 683 683 1.00 111 | TOTAL (out) 42 153 702 778 1.11 112 | ---------------------------------------------------------- 113 | 114 | *** lexer-based optimizations summary *** 115 | -------------------------------------------------------------------- 116 | Lexical Input Input Input Output Output Output 117 | Elements Count Bytes Average Count Bytes Average 118 | -------------------------------------------------------------------- 119 | TK_KEYWORD 374 1531 4.09 374 1531 4.09 120 | TK_NAME 795 3963 4.98 795 1306 1.64 121 | TK_NUMBER 54 59 1.09 54 59 1.09 122 | TK_STRING 152 1725 11.35 152 1717 11.30 123 | TK_LSTRING 7 1976 282.29 7 1976 282.29 124 | TK_OP 997 1092 1.10 997 1092 1.10 125 | TK_EOS 1 0 0.00 1 0 0.00 126 | -------------------------------------------------------------------- 127 | TK_COMMENT 140 6884 49.17 1 18 18.00 128 | TK_LCOMMENT 7 1723 246.14 0 0 0.00 129 | TK_EOL 543 543 1.00 197 197 1.00 130 | TK_SPACE 1270 2465 1.94 263 263 1.00 131 | -------------------------------------------------------------------- 132 | Total Elements 4340 21961 5.06 2841 8159 2.87 133 | -------------------------------------------------------------------- 134 | Total Tokens 2380 10346 4.35 2380 7681 3.23 135 | -------------------------------------------------------------------- 136 | 137 | -- 138 | 139 | USING LUASRCDIET 140 | 141 | ... 142 | 143 | Please see the command line help or see sample/Makefile for examples. 144 | 145 | This is experimental software and nothing has been done yet on a proper 146 | installation scheme for use with normal work. A thousand apologies... 147 | 148 | -- 149 | 150 | CODE SIZE REDUCTION 151 | 152 | ... 153 | 154 | -- 155 | 156 | OTHER OPTIONS 157 | 158 | ... 159 | 160 | -- 161 | 162 | BEHAVIOUR NOTES 163 | 164 | * embedded line endings in strings and long strings always 165 | normalized to LF 166 | * will not optimize trailing spaces in long strings, only warns 167 | * scientific notation generated in number optimzation is not in 168 | canonical form, this may or may not be a bad thing, so feedback 169 | is welcome 170 | 171 | -- 172 | 173 | ACKNOWLEDGEMENTS 174 | 175 | Thanks to the LuaForge people for hosting this. 176 | Developed on SciTE http://www.scintilla.org/. Two thumbs up. 177 | 178 | -- 179 | 180 | FEEDBACK 181 | 182 | Feedback and contributions are welcome. Your name will be acknowledged, 183 | as long as you are willing to comply with COPYRIGHT. If your material is 184 | self-contained, you can retain a copyright notice for those material in 185 | your own name, as long as you use the same Lua 5/MIT-style copyright. 186 | 187 | My alternative e-mail address is: keinhong AT gmail DOT com 188 | 189 | Enjoy!! 190 | 191 | Kein-Hong Man (esq.) 192 | Kuala Lumpur 193 | Malaysia 20080603 194 | -------------------------------------------------------------------------------- /plugin/html.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | html.lua: Turns Lua 5.1 source code into HTML files 4 | This file is part of LuaSrcDiet. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- WARNING: highly experimental! interface liable to change 17 | -- * This HTML highlighter marks globals brightly so that their usage 18 | -- can be manually optimized. 19 | -- * Either uses a .html extension for output files or it follows the 20 | -- -o option. 21 | -- * The HTML style tries to follow that of the Lua wiki. 22 | ----------------------------------------------------------------------]] 23 | 24 | local string = require "string" 25 | local table = require "table" 26 | local io = require "io" 27 | local base = _G 28 | module "html" 29 | 30 | ------------------------------------------------------------------------ 31 | -- constants and configuration 32 | ------------------------------------------------------------------------ 33 | 34 | local HTML_EXT = ".html" 35 | local ENTITIES = { 36 | ["&"] = "&", ["<"] = "<", [">"] = ">", 37 | ["'"] = "'", ["\""] = """, 38 | } 39 | 40 | -- simple headers and footers 41 | local HEADER = [[ 42 | 43 | 44 | 45 | %s 46 | 47 | 49 | 50 | 51 |
 52 | ]]
 53 | local FOOTER = [[
 54 | 
55 | 56 | 57 | ]] 58 | -- for more, please see wikimain.css from the Lua wiki site 59 | local STYLESHEET = [[ 60 | BODY { 61 | background: white; 62 | color: navy; 63 | } 64 | pre.code { color: black; } 65 | span.comment { color: #00a000; } 66 | span.string { color: #009090; } 67 | span.keyword { color: black; font-weight: bold; } 68 | span.number { } 69 | span.operator { } 70 | span.name { } 71 | span.global { color: #ff0000; font-weight: bold; } 72 | span.local { color: #0000ff; font-weight: bold; } 73 | ]] 74 | 75 | ------------------------------------------------------------------------ 76 | -- option handling, plays nice with --quiet option 77 | ------------------------------------------------------------------------ 78 | 79 | local option -- local reference to list of options 80 | local srcfl, destfl -- filenames 81 | local toklist, seminfolist, toklnlist -- token data 82 | 83 | local function print(...) -- handle quiet option 84 | if option.QUIET then return end 85 | base.print(...) 86 | end 87 | 88 | ------------------------------------------------------------------------ 89 | -- initialization 90 | ------------------------------------------------------------------------ 91 | 92 | function init(_option, _srcfl, _destfl) 93 | option = _option 94 | srcfl = _srcfl 95 | local extb, exte = string.find(srcfl, "%.[^%.%\\%/]*$") 96 | local basename, extension = srcfl, "" 97 | if extb and extb > 1 then 98 | basename = string.sub(srcfl, 1, extb - 1) 99 | extension = string.sub(srcfl, extb, exte) 100 | end 101 | destfl = basename..HTML_EXT 102 | if option.OUTPUT_FILE then 103 | destfl = option.OUTPUT_FILE 104 | end 105 | if srcfl == destfl then 106 | base.error("output filename identical to input filename") 107 | end 108 | end 109 | 110 | ------------------------------------------------------------------------ 111 | -- message display, post-load processing 112 | ------------------------------------------------------------------------ 113 | 114 | function post_load(z) 115 | print([[ 116 | HTML plugin module for LuaSrcDiet 117 | ]]) 118 | print("Exporting: "..srcfl.." -> "..destfl.."\n") 119 | end 120 | 121 | ------------------------------------------------------------------------ 122 | -- post-lexing processing, can work on lexer table output 123 | ------------------------------------------------------------------------ 124 | 125 | function post_lex(_toklist, _seminfolist, _toklnlist) 126 | toklist, seminfolist, toklnlist 127 | = _toklist, _seminfolist, _toklnlist 128 | end 129 | 130 | ------------------------------------------------------------------------ 131 | -- escape the usual suspects for HTML/XML 132 | ------------------------------------------------------------------------ 133 | 134 | local function do_entities(z) 135 | local i = 1 136 | while i <= #z do 137 | local c = string.sub(z, i, i) 138 | local d = ENTITIES[c] 139 | if d then 140 | c = d 141 | z = string.sub(z, 1, i - 1)..c..string.sub(z, i + 1) 142 | end 143 | i = i + #c 144 | end--while 145 | return z 146 | end 147 | 148 | ------------------------------------------------------------------------ 149 | -- save source code to file 150 | ------------------------------------------------------------------------ 151 | 152 | local function save_file(fname, dat) 153 | local OUTF = io.open(fname, "wb") 154 | if not OUTF then base.error("cannot open \""..fname.."\" for writing") end 155 | local status = OUTF:write(dat) 156 | if not status then base.error("cannot write to \""..fname.."\"") end 157 | OUTF:close() 158 | end 159 | 160 | ------------------------------------------------------------------------ 161 | -- post-parsing processing, gives globalinfo, localinfo 162 | ------------------------------------------------------------------------ 163 | 164 | function post_parse(globalinfo, localinfo) 165 | local html = {} 166 | local function add(s) -- html helpers 167 | html[#html + 1] = s 168 | end 169 | local function span(class, s) 170 | add(''..s..'') 171 | end 172 | ---------------------------------------------------------------------- 173 | for i = 1, #globalinfo do -- mark global identifiers as TK_GLOBAL 174 | local obj = globalinfo[i] 175 | local xref = obj.xref 176 | for j = 1, #xref do 177 | local p = xref[j] 178 | toklist[p] = "TK_GLOBAL" 179 | end 180 | end--for 181 | ---------------------------------------------------------------------- 182 | for i = 1, #localinfo do -- mark local identifiers as TK_LOCAL 183 | local obj = localinfo[i] 184 | local xref = obj.xref 185 | for j = 1, #xref do 186 | local p = xref[j] 187 | toklist[p] = "TK_LOCAL" 188 | end 189 | end--for 190 | ---------------------------------------------------------------------- 191 | add(string.format(HEADER, -- header and leading stuff 192 | do_entities(srcfl), 193 | STYLESHEET)) 194 | for i = 1, #toklist do -- enumerate token list 195 | local tok, info = toklist[i], seminfolist[i] 196 | if tok == "TK_KEYWORD" then 197 | span("keyword", info) 198 | elseif tok == "TK_STRING" or tok == "TK_LSTRING" then 199 | span("string", do_entities(info)) 200 | elseif tok == "TK_COMMENT" or tok == "TK_LCOMMENT" then 201 | span("comment", do_entities(info)) 202 | elseif tok == "TK_GLOBAL" then 203 | span("global", info) 204 | elseif tok == "TK_LOCAL" then 205 | span("local", info) 206 | elseif tok == "TK_NAME" then 207 | span("name", info) 208 | elseif tok == "TK_NUMBER" then 209 | span("number", info) 210 | elseif tok == "TK_OP" then 211 | span("operator", do_entities(info)) 212 | elseif tok ~= "TK_EOS" then -- TK_EOL, TK_SPACE 213 | add(info) 214 | end 215 | end--for 216 | add(FOOTER) 217 | save_file(destfl, table.concat(html)) 218 | option.EXIT = true 219 | end 220 | 221 | return base.getfenv() 222 | -------------------------------------------------------------------------------- /sample/optlex.lua: -------------------------------------------------------------------------------- 1 | local a=_G 2 | local T=require"string"module"optlex"local o=T.match 3 | local e=T.sub 4 | local f=T.find 5 | local r=T.rep 6 | local s 7 | error=a.error 8 | warn={}local i,t,c 9 | local E={TK_KEYWORD=true,TK_NAME=true,TK_NUMBER=true,TK_STRING=true,TK_LSTRING=true,TK_OP=true,TK_EOS=true,}local S={TK_COMMENT=true,TK_LCOMMENT=true,TK_EOL=true,TK_SPACE=true,}local d 10 | local function L(e)local n=i[e-1]if e<=1 or n=="TK_EOL"then 11 | return true 12 | elseif n==""then 13 | return L(e-1)end 14 | return false 15 | end 16 | local function P(n)local e=i[n+1]if n>=#i or e=="TK_EOL"or e=="TK_EOS"then 17 | return true 18 | elseif e==""then 19 | return P(n+1)end 20 | return false 21 | end 22 | local function g(n)local l=#o(n,"^%-%-%[=*%[")local l=e(n,l+1,-(l-1))local e,n=1,0 23 | while true do 24 | local l,i,o,t=f(l,"([\r\n])([\r\n]?)",e)if not l then break end 25 | e=l+1 26 | n=n+1 27 | if#t>0 and o~=t then 28 | e=e+1 29 | end 30 | end 31 | return n 32 | end 33 | local function M(a,d)local l=o 34 | local n,e=i[a],i[d]if n=="TK_STRING"or n=="TK_LSTRING"or 35 | e=="TK_STRING"or e=="TK_LSTRING"then 36 | return""elseif n=="TK_OP"or e=="TK_OP"then 37 | if(n=="TK_OP"and(e=="TK_KEYWORD"or e=="TK_NAME"))or(e=="TK_OP"and(n=="TK_KEYWORD"or n=="TK_NAME"))then 38 | return""end 39 | if n=="TK_OP"and e=="TK_OP"then 40 | local n,e=t[a],t[d]if(l(n,"^%.%.?$")and l(e,"^%."))or(l(n,"^[~=<>]$")and e=="=")or(n=="["and(e=="["or e=="="))then 41 | return" "end 42 | return""end 43 | local n=t[a]if e=="TK_OP"then n=t[d]end 44 | if l(n,"^%.%.?%.?$")then 45 | return" "end 46 | return""else 47 | return" "end 48 | end 49 | local function N()local l,o,d={},{},{}local e=1 50 | for n=1,#i do 51 | local i=i[n]if i~=""then 52 | l[e],o[e],d[e]=i,t[n],c[n]e=e+1 53 | end 54 | end 55 | i,t,c=l,o,d 56 | end 57 | local function b(f)local n=t[f]local n=n 58 | local i 59 | if o(n,"^0[xX]")then 60 | local e=a.tostring(a.tonumber(n))if#e<=#n then 61 | n=e 62 | else 63 | return 64 | end 65 | end 66 | if o(n,"^%d+%.?0*$")then 67 | n=o(n,"^(%d+)%.?0*$")if n+0>0 then 68 | n=o(n,"^0*([1-9]%d*)$")local l=#o(n,"0*$")local t=a.tostring(l)if l>#t+1 then 69 | n=e(n,1,#n-l).."e"..t 70 | end 71 | i=n 72 | else 73 | i="0"end 74 | elseif not o(n,"[eE]")then 75 | local l,n=o(n,"^(%d*)%.(%d+)$")if l==""then l=0 end 76 | if n+0==0 and l==0 then 77 | i="0"else 78 | local t=#o(n,"0*$")if t>0 then 79 | n=e(n,1,#n-t)end 80 | if l+0>0 then 81 | i=l.."."..n 82 | else 83 | i="."..n 84 | local l=#o(n,"^0*")local t=#n-l 85 | local l=a.tostring(#n)if t+2+#l<1+#n then 86 | i=e(n,-t).."e-"..l 87 | end 88 | end 89 | end 90 | else 91 | local n,l=o(n,"^([^eE]+)[eE]([%+%-]?%d+)$")l=a.tonumber(l)local t,d=o(n,"^(%d*)%.(%d*)$")if t then 92 | l=l-#d 93 | n=t..d 94 | end 95 | if n+0==0 then 96 | i="0"else 97 | local t=#o(n,"^0*")n=e(n,t+1)t=#o(n,"0*$")if t>0 then 98 | n=e(n,1,#n-t)l=l+t 99 | end 100 | local o=a.tostring(l)if l==0 then 101 | i=n 102 | elseif l>0 and(l<=1+#o)then 103 | i=n..r("0",l)elseif l<0 and(l>=-#n)then 104 | t=#n+l 105 | i=e(n,1,t).."."..e(n,t+1)elseif l<0 and(#o>=-l-#n)then 106 | t=-l-#n 107 | i="."..r("0",t)..n 108 | else 109 | i=n.."e"..l 110 | end 111 | end 112 | end 113 | if i and i~=t[f]then 114 | if d then 115 | s(" (line "..c[f]..") "..t[f].." -> "..i)d=d+1 116 | end 117 | t[f]=i 118 | end 119 | end 120 | local function I(r)local n=t[r]local i=e(n,1,1)local u=(i=="'")and'"'or"'"local n=e(n,2,-2)local l=1 121 | local h,a=0,0 122 | while l<=#n do 123 | local s=e(n,l,l)if s=="\\"then 124 | local t=l+1 125 | local r=e(n,t,t)local d=f("abfnrtv\\\n\r\"'0123456789",r,1,true)if not d then 126 | n=e(n,1,l-1)..e(n,t)l=l+1 127 | elseif d<=8 then 128 | l=l+2 129 | elseif d<=10 then 130 | local o=e(n,t,t+1)if o=="\r\n"or o=="\n\r"then 131 | n=e(n,1,l).."\n"..e(n,t+2)elseif d==10 then 132 | n=e(n,1,l).."\n"..e(n,t+1)end 133 | l=l+2 134 | elseif d<=12 then 135 | if r==i then 136 | h=h+1 137 | l=l+2 138 | else 139 | a=a+1 140 | n=e(n,1,l-1)..e(n,t)l=l+1 141 | end 142 | else 143 | local o=o(n,"^(%d%d?%d?)",t)t=l+1+#o 144 | local c=o+0 145 | local d=T.char(c)local r=f("\a\b\f\n\r\t\v",d,1,true)if r then 146 | o="\\"..e("abfnrtv",r,r)elseif c<32 then 147 | o="\\"..c 148 | elseif d==i then 149 | o="\\"..d 150 | h=h+1 151 | elseif d=="\\"then 152 | o="\\\\"else 153 | o=d 154 | if d==u then 155 | a=a+1 156 | end 157 | end 158 | n=e(n,1,l-1)..o..e(n,t)l=l+#o 159 | end 160 | else 161 | l=l+1 162 | if s==u then 163 | a=a+1 164 | end 165 | end 166 | end 167 | if h>a then 168 | l=1 169 | while l<=#n do 170 | local t,d,o=f(n,"(['\"])",l)if not t then break end 171 | if o==i then 172 | n=e(n,1,t-2)..e(n,t)l=t 173 | else 174 | n=e(n,1,t-1).."\\"..e(n,t)l=t+2 175 | end 176 | end 177 | i=u 178 | end 179 | n=i..n..i 180 | if n~=t[r]then 181 | if d then 182 | s(" (line "..c[r]..") "..t[r].." -> "..n)d=d+1 183 | end 184 | t[r]=n 185 | end 186 | end 187 | local function C(d)local n=t[d]local a=o(n,"^%[=*%[")local l=#a 188 | local s=e(n,-l,-1)local h=e(n,l+1,-(l+1))local i=""local n=1 189 | while true do 190 | local l,t,r,a=f(h,"([\r\n])([\r\n]?)",n)local t 191 | if not l then 192 | t=e(h,n)elseif l>=n then 193 | t=e(h,n,l-1)end 194 | if t~=""then 195 | if o(t,"%s+$")then 196 | warn.lstring="trailing whitespace in long string near line "..c[d]end 197 | i=i..t 198 | end 199 | if not l then 200 | break 201 | end 202 | n=l+1 203 | if l then 204 | if#a>0 and r~=a then 205 | n=n+1 206 | end 207 | if not(n==1 and n==l)then 208 | i=i.."\n"end 209 | end 210 | end 211 | if l>=3 then 212 | local e,n=l-1 213 | while e>=2 do 214 | local l="%]"..r("=",e-2).."%]"if not o(i,l)then n=e end 215 | e=e-1 216 | end 217 | if n then 218 | l=r("=",n-2)a,s="["..l.."[","]"..l.."]"end 219 | end 220 | t[d]=a..i..s 221 | end 222 | local function K(c)local l=t[c]local a=o(l,"^%-%-%[=*%[")local n=#a 223 | local h=e(l,-n,-1)local d=e(l,n+1,-(n-1))local i=""local l=1 224 | while true do 225 | local t,n,r,a=f(d,"([\r\n])([\r\n]?)",l)local n 226 | if not t then 227 | n=e(d,l)elseif t>=l then 228 | n=e(d,l,t-1)end 229 | if n~=""then 230 | local l=o(n,"%s*$")if#l>0 then n=e(n,1,-(l+1))end 231 | i=i..n 232 | end 233 | if not t then 234 | break 235 | end 236 | l=t+1 237 | if t then 238 | if#a>0 and r~=a then 239 | l=l+1 240 | end 241 | i=i.."\n"end 242 | end 243 | n=n-2 244 | if n>=3 then 245 | local e,l=n-1 246 | while e>=2 do 247 | local n="%]"..r("=",e-2).."%]"if not o(i,n)then l=e end 248 | e=e-1 249 | end 250 | if l then 251 | n=r("=",l-2)a,h="--["..n.."[","]"..n.."]"end 252 | end 253 | t[c]=a..i..h 254 | end 255 | local function O(i)local n=t[i]local l=o(n,"%s*$")if#l>0 then 256 | n=e(n,1,-(l+1))end 257 | t[i]=n 258 | end 259 | local function m(t,n)if not t then return false end 260 | local l=o(n,"^%-%-%[=*%[")local l=#l 261 | local o=e(n,-l,-1)local e=e(n,l+1,-(l-1))if f(e,t,1,true)then 262 | return true 263 | end 264 | end 265 | function optimize(n,f,l,o)local u=n["opt-comments"]local h=n["opt-whitespace"]local T=n["opt-emptylines"]local _=n["opt-eols"]local R=n["opt-strings"]local w=n["opt-numbers"]local p=n.KEEP 266 | d=n.DETAILS and 0 267 | s=s or a.print 268 | if _ then 269 | u=true 270 | h=true 271 | T=true 272 | end 273 | i,t,c=f,l,o 274 | local n=1 275 | local l,f 276 | local a 277 | local function o(l,o,e)e=e or n 278 | i[e]=l or""t[e]=o or""end 279 | while true do 280 | l,f=i[n],t[n]local d=L(n)if d then a=nil end 281 | if l=="TK_EOS"then 282 | break 283 | elseif l=="TK_KEYWORD"or 284 | l=="TK_NAME"or 285 | l=="TK_OP"then 286 | a=n 287 | elseif l=="TK_NUMBER"then 288 | if w then 289 | b(n)end 290 | a=n 291 | elseif l=="TK_STRING"or 292 | l=="TK_LSTRING"then 293 | if R then 294 | if l=="TK_STRING"then 295 | I(n)else 296 | C(n)end 297 | end 298 | a=n 299 | elseif l=="TK_COMMENT"then 300 | if u then 301 | if n==1 and e(f,1,1)=="#"then 302 | O(n)else 303 | o()end 304 | elseif h then 305 | O(n)end 306 | elseif l=="TK_LCOMMENT"then 307 | if m(p,f)then 308 | if h then 309 | K(n)end 310 | a=n 311 | elseif u then 312 | local e=g(f)if S[i[n+1]]then 313 | o()l=""else 314 | o("TK_SPACE"," ")end 315 | if not T and e>0 then 316 | o("TK_EOL",r("\n",e))end 317 | if h and l~=""then 318 | n=n-1 319 | end 320 | else 321 | if h then 322 | K(n)end 323 | a=n 324 | end 325 | elseif l=="TK_EOL"then 326 | if d and T then 327 | o()elseif f=="\r\n"or f=="\n\r"then 328 | o("TK_EOL","\n")end 329 | elseif l=="TK_SPACE"then 330 | if h then 331 | if d or P(n)then 332 | o()else 333 | local l=i[a]if l=="TK_LCOMMENT"then 334 | o()else 335 | local e=i[n+1]if S[e]then 336 | if(e=="TK_COMMENT"or e=="TK_LCOMMENT")and 337 | l=="TK_OP"and t[a]=="-"then 338 | else 339 | o()end 340 | else 341 | local e=M(a,n+1)if e==""then 342 | o()else 343 | o("TK_SPACE"," ")end 344 | end 345 | end 346 | end 347 | end 348 | else 349 | error("unidentified token encountered")end 350 | n=n+1 351 | end 352 | N()if _ then 353 | n=1 354 | if i[1]=="TK_COMMENT"then 355 | n=3 356 | end 357 | while true do 358 | l,f=i[n],t[n]if l=="TK_EOS"then 359 | break 360 | elseif l=="TK_EOL"then 361 | local e,l=i[n-1],i[n+1]if E[e]and E[l]then 362 | local e=M(n-1,n+1)if e==""then 363 | o()end 364 | end 365 | end 366 | n=n+1 367 | end 368 | N()end 369 | if d and d>0 then s()end 370 | return i,t,c 371 | end 372 | -------------------------------------------------------------------------------- /test/LuaSrcDiet_fixed_.lua: -------------------------------------------------------------------------------- 1 | local o=string 2 | local I=math 3 | local j=table 4 | local r=o.sub 5 | local q=o.gmatch 6 | local a=require"llex"local f=require"lparser"local l=require"optlex"local k=require"optparser"local m=[[ 7 | LuaSrcDiet: Puts your Lua 5.1 source code on a diet 8 | Version 0.11.0 (20080529) Copyright (c) 2005-2008 Kein-Hong Man 9 | The COPYRIGHT file describes the conditions under which this 10 | software may be distributed. 11 | ]]local v=[[ 12 | usage: LuaSrcDiet [options] [filenames] 13 | 14 | example: 15 | >LuaSrcDiet myscript.lua -o myscript_.lua 16 | 17 | options: 18 | -v, --version prints version information 19 | -h, --help prints usage information 20 | -o specify file name to write output 21 | -s suffix for output files (default '_') 22 | --keep keep block comment with inside 23 | - stop handling arguments 24 | 25 | (optimization levels) 26 | --none all optimizations off (normalizes EOLs only) 27 | --basic lexer-based optimizations only 28 | --maximum maximize reduction of source 29 | 30 | (informational) 31 | --quiet process files quietly 32 | --read-only read file and print token stats only 33 | --dump-lexer dump raw tokens from lexer to stdout 34 | --dump-parser dump variable tracking tables from parser 35 | --details gives extra information, e.g. detailed stats 36 | 37 | features (to disable, insert 'no' prefix like --noopt-comments): 38 | %s 39 | default settings: 40 | %s]]local c=[[ 41 | --opt-comments,'remove comments and block comments' 42 | --opt-whitespace,'remove whitespace excluding EOLs' 43 | --opt-emptylines,'remove empty lines' 44 | --opt-eols,'all above, plus remove unnecessary EOLs' 45 | --opt-strings,'optimize strings and long strings' 46 | --opt-numbers,'optimize numbers' 47 | --opt-locals,'optimize local variable names' 48 | ]]local g=[[ 49 | --opt-comments --opt-whitespace --opt-emptylines 50 | --opt-numbers --opt-locals 51 | ]]local x=[[ 52 | --opt-comments --opt-whitespace --opt-emptylines 53 | --noopt-eols --noopt-strings --noopt-numbers 54 | --noopt-locals 55 | ]]local A=[[ 56 | --opt-comments --opt-whitespace --opt-emptylines 57 | --opt-eols --opt-strings --opt-numbers --opt-locals 58 | ]]local T=[[ 59 | --noopt-comments --noopt-whitespace --noopt-emptylines 60 | --noopt-eols --noopt-strings --noopt-numbers 61 | --noopt-locals 62 | ]]local E="_"local function i(e)print("LuaSrcDiet: "..e);os.exit()end 63 | if not o.match(_VERSION,"5.1",1,1)then 64 | i("requires Lua 5.1 to run")end 65 | local p=""do 66 | local n=24 67 | local a={}for t,i in q(c,"%s*([^,]+),'([^']+)'")do 68 | local e=" "..t 69 | e=e..o.rep(" ",n-#e)..i.."\n"p=p..e 70 | a[t]=true 71 | a["--no"..r(t,3)]=true 72 | end 73 | c=a 74 | end 75 | v=o.format(v,p,g)local b=E 76 | local e={}local s,n 77 | local function d(a)for t in q(a,"(%-%-%S+)")do 78 | if r(t,3,4)=="no"and 79 | c["--"..r(t,5)]then 80 | e[r(t,5)]=false 81 | else 82 | e[r(t,3)]=true 83 | end 84 | end 85 | end 86 | local h={"TK_KEYWORD","TK_NAME","TK_NUMBER","TK_STRING","TK_LSTRING","TK_OP","TK_EOS","TK_COMMENT","TK_LCOMMENT","TK_EOL","TK_SPACE",}local _=7 87 | local z={["\n"]="LF",["\r"]="CR",["\n\r"]="LFCR",["\r\n"]="CRLF",}local function u(e)local t=io.open(e,"rb")if not t then i('cannot open "'..e..'" for reading')end 88 | local a=t:read("*a")if not a then i('cannot read from "'..e..'"')end 89 | t:close()return a 90 | end 91 | local function q(e,a)local t=io.open(e,"wb")if not t then i('cannot open "'..e..'" for writing')end 92 | local a=t:write(a)if not a then i('cannot write to "'..e..'"')end 93 | t:close()end 94 | local function p()s,n={},{}for t=1,#h do 95 | local e=h[t]s[e],n[e]=0,0 96 | end 97 | end 98 | local function y(e,t)s[e]=s[e]+1 99 | n[e]=n[e]+#t 100 | end 101 | local function w()local function i(e,t)if e==0 then return 0 end 102 | return t/e 103 | end 104 | local o={}local t,e=0,0 105 | for o=1,_ do 106 | local a=h[o]t=t+s[a];e=e+n[a]end 107 | s.TOTAL_TOK,n.TOTAL_TOK=t,e 108 | o.TOTAL_TOK=i(t,e)t,e=0,0 109 | for r=1,#h do 110 | local a=h[r]t=t+s[a];e=e+n[a]o[a]=i(s[a],n[a])end 111 | s.TOTAL_ALL,n.TOTAL_ALL=t,e 112 | o.TOTAL_ALL=i(t,e)return o 113 | end 114 | local function O(s)local n=u(s)a.init(n)a.llex()local i,n=a.tok,a.seminfo 115 | for a=1,#i do 116 | local t,e=i[a],n[a]if t=="TK_OP"and o.byte(e)<32 then 117 | e="("..o.byte(e)..")"elseif t=="TK_EOL"then 118 | e=z[e]else 119 | e="'"..e.."'"end 120 | print(t.." "..e)end 121 | end 122 | local function z(h)local i=print 123 | local h=u(h)a.init(h)a.llex()local r,d,h=a.tok,a.seminfo,a.tokln 124 | f.init(r,d,h)local n,s=f.parser()local a=o.rep("-",72)i("*** Local/Global Variable Tracker Tables ***")i(a.."\n GLOBALS\n"..a)for o=1,#n do 125 | local a=n[o]local e="("..o..") '"..a.name.."' -> "local t=a.xref 126 | for a=1,#t do e=e..t[a].." "end 127 | i(e)end 128 | i(a.."\n LOCALS (decl=declared act=activated rem=removed)\n"..a)for a=1,#s do 129 | local t=s[a]local e="("..a..") '"..t.name.."' decl:"..t.decl.." act:"..t.act.." rem:"..t.rem 130 | if t.isself then 131 | e=e.." isself"end 132 | e=e.." -> "local t=t.xref 133 | for a=1,#t do e=e..t[a].." "end 134 | i(e)end 135 | i(a.."\n")end 136 | local function _(r)local e=print 137 | local l=u(r)a.init(l)a.llex()local d,l=a.tok,a.seminfo 138 | e(m)e("Statistics for: "..r.."\n")p()for e=1,#d do 139 | local e,t=d[e],l[e]y(e,t)end 140 | local r=w()local a=o.format 141 | local function i(e)return s[e],n[e],r[e]end 142 | local s,n="%-16s%8s%8s%10s","%-16s%8d%8d%10.2f"local t=o.rep("-",42)e(a(s,"Lexical","Input","Input","Input"))e(a(s,"Elements","Count","Bytes","Average"))e(t)for s=1,#h do 143 | local o=h[s]e(a(n,o,i(o)))if o=="TK_EOS"then e(t)end 144 | end 145 | e(t)e(a(n,"Total Elements",i("TOTAL_ALL")))e(t)e(a(n,"Total Tokens",i("TOTAL_TOK")))e(t.."\n")end 146 | local function E(d,c)local function t(...)if e.QUIET then return end 147 | _G.print(...)end 148 | local g=u(d)a.init(g)a.llex()local a,i,u=a.tok,a.seminfo,a.tokln 149 | t(m)t("Statistics for: "..d.." -> "..c.."\n")p()for e=1,#a do 150 | local t,e=a[e],i[e]y(t,e)end 151 | local v=w()local b,m=s,n 152 | if e["opt-locals"]then 153 | k.print=t 154 | f.init(a,i,u)local o,t=f.parser()k.optimize(e,a,i,o,t)end 155 | a,i=l.optimize(e,a,i,u)local r=j.concat(i)if o.find(r,"\r\n",1,1)or 156 | o.find(r,"\n\r",1,1)then 157 | l.warn.mixedeol=true 158 | end 159 | q(c,r)p()for e=1,#a do 160 | local t,e=a[e],i[e]y(t,e)end 161 | local d=w()local a=o.format 162 | local function r(e)return b[e],m[e],v[e],s[e],n[e],d[e]end 163 | local n,i="%-16s%8s%8s%10s%8s%8s%10s","%-16s%8d%8d%10.2f%8d%8d%10.2f"local e=o.rep("-",68)t("*** lexer-based optimizations summary ***\n"..e)t(a(n,"Lexical","Input","Input","Input","Output","Output","Output"))t(a(n,"Elements","Count","Bytes","Average","Count","Bytes","Average"))t(e)for n=1,#h do 164 | local o=h[n]t(a(i,o,r(o)))if o=="TK_EOS"then t(e)end 165 | end 166 | t(e)t(a(i,"Total Elements",r("TOTAL_ALL")))t(e)t(a(i,"Total Tokens",r("TOTAL_TOK")))t(e)if l.warn.lstring then 167 | t("* WARNING: "..l.warn.lstring)elseif l.warn.mixedeol then 168 | t("* WARNING: ".."output still contains some CRLF or LFCR line endings")end 169 | t()end 170 | local h={...}local s={}d(g)local function u(h)for l,t in ipairs(h)do 171 | local a 172 | local o,d=o.find(t,"%.[^%.%\\%/]*$")local n,s=t,""if o and o>1 then 173 | n=r(t,1,o-1)s=r(t,o,d)end 174 | a=n..b..s 175 | if#h==1 and e.OUTPUT_FILE then 176 | a=e.OUTPUT_FILE 177 | end 178 | if t==a then 179 | i("output filename identical to input filename")end 180 | if e.DUMP_LEXER then 181 | O(t)elseif e.DUMP_PARSER then 182 | z(t)elseif e.READ_ONLY then 183 | _(t)else 184 | E(t,a)end 185 | end 186 | end 187 | local function l()local r,a=#h,1 188 | if r==0 then 189 | e.HELP=true 190 | end 191 | while a<=r do 192 | local t,n=h[a],h[a+1]local o=o.match(t,"^%-%-?")if o=="-"then 193 | if t=="-h"then 194 | e.HELP=true;break 195 | elseif t=="-v"then 196 | e.VERSION=true;break 197 | elseif t=="-s"then 198 | if not n then i("-s option needs suffix specification")end 199 | b=n 200 | a=a+1 201 | elseif t=="-o"then 202 | if not n then i("-o option needs a file name")end 203 | e.OUTPUT_FILE=n 204 | a=a+1 205 | elseif t=="-"then 206 | break 207 | else 208 | i("unrecognized option "..t)end 209 | elseif o=="--"then 210 | if t=="--help"then 211 | e.HELP=true;break 212 | elseif t=="--version"then 213 | e.VERSION=true;break 214 | elseif t=="--keep"then 215 | if not n then i("--keep option needs a string to match for")end 216 | e.KEEP=n 217 | a=a+1 218 | elseif t=="--quiet"then 219 | e.QUIET=true 220 | elseif t=="--read-only"then 221 | e.READ_ONLY=true 222 | elseif t=="--basic"then 223 | d(x)elseif t=="--maximum"then 224 | d(A)elseif t=="--none"then 225 | d(T)elseif t=="--dump-lexer"then 226 | e.DUMP_LEXER=true 227 | elseif t=="--dump-parser"then 228 | e.DUMP_PARSER=true 229 | elseif t=="--details"then 230 | e.DETAILS=true 231 | elseif c[t]then 232 | d(t)else 233 | i("unrecognized option "..t)end 234 | else 235 | s[#s+1]=t 236 | end 237 | a=a+1 238 | end 239 | if e.HELP then 240 | print(m..v);return true 241 | elseif e.VERSION then 242 | print(m);return true 243 | end 244 | if#s>0 then 245 | if#s>1 and e.OUTPUT_FILE then 246 | i("with -o, only one source file can be specified")end 247 | u(s)return true 248 | else 249 | i("nothing to do!")end 250 | end 251 | if not l()then 252 | i("Please run with option -h or --help for usage information")end 253 | -------------------------------------------------------------------------------- /sample/lparser.lua: -------------------------------------------------------------------------------- 1 | local k=_G 2 | local D=require"string"module"lparser"local W=k.getfenv()local g,_,E,R,d,f,X,n,m,u,s,L,l,F,O,C,a,v,I 3 | local p,c,b,w,N,A 4 | local e=D.gmatch 5 | local S={}for e in e("else elseif end until ","%S+")do 6 | S[e]=true 7 | end 8 | local Y={}for e in e("if while do for repeat function local return break","%S+")do 9 | Y[e]=e.."_stat"end 10 | local U={}local P={}for e,l,n in e([[ 11 | {+ 6 6}{- 6 6}{* 7 7}{/ 7 7}{% 7 7} 12 | {^ 10 9}{.. 5 4} 13 | {~= 3 3}{== 3 3} 14 | {< 3 3}{<= 3 3}{> 3 3}{>= 3 3} 15 | {and 2 2}{or 1 1} 16 | ]],"{(%S+)%s(%d+)%s(%d+)}")do 17 | U[e]=l+0 18 | P[e]=n+0 19 | end 20 | local ee={["not"]=true,["-"]=true,["#"]=true,}local Z=8 21 | local function o(l,n)local e=error or k.error 22 | e(D.format("(source):%d: %s",n or u,l))end 23 | local function e()X=E[d]n,m,u,s=g[d],_[d],E[d],R[d]d=d+1 24 | end 25 | local function z()return g[d]end 26 | local function r(l)local e=n 27 | if e~=""and e~=""then 28 | if e==""then e=m end 29 | e="'"..e.."'"end 30 | o(l.." near "..e)end 31 | local function h(e)r("'"..e.."' expected")end 32 | local function t(l)if n==l then e();return true end 33 | end 34 | local function G(e)if n~=e then h(e)end 35 | end 36 | local function o(n)G(n);e()end 37 | local function j(n,e)if not n then r(e)end 38 | end 39 | local function i(e,l,n)if not t(e)then 40 | if n==u then 41 | h(e)else 42 | r("'"..e.."' expected (to close '"..l.."' at line "..n..")")end 43 | end 44 | end 45 | local function V()G("")local n=m 46 | L=s 47 | e()return n 48 | end 49 | local function K(e,n)e.k="VK"end 50 | local function B(e)K(e,V())end 51 | local function h(o,t)local e=l.bl 52 | local n 53 | if e then 54 | n=e.locallist 55 | else 56 | n=l.locallist 57 | end 58 | local e=#a+1 59 | a[e]={name=o,xref={L},decl=L,}if t then 60 | a[e].isself=true 61 | end 62 | local l=#v+1 63 | v[l]=e 64 | I[l]=n 65 | end 66 | local function x(e)local n=#v 67 | while e>0 do 68 | e=e-1 69 | local e=n-e 70 | local l=v[e]local n=a[l]local o=n.name 71 | n.act=s 72 | v[e]=nil 73 | local t=I[e]I[e]=nil 74 | local e=t[o]if e then 75 | n=a[e]n.rem=-l 76 | end 77 | t[o]=l 78 | end 79 | end 80 | local function T()local n=l.bl 81 | local e 82 | if n then 83 | e=n.locallist 84 | else 85 | e=l.locallist 86 | end 87 | for n,e in k.pairs(e)do 88 | local e=a[e]e.rem=s 89 | end 90 | end 91 | local function s(e,n)if D.sub(e,1,1)=="("then 92 | return 93 | end 94 | h(e,n)end 95 | local function D(o,l)local n=o.bl 96 | local e 97 | if n then 98 | e=n.locallist 99 | while e do 100 | if e[l]then return e[l]end 101 | n=n.prev 102 | e=n and n.locallist 103 | end 104 | end 105 | e=o.locallist 106 | return e[l]or-1 107 | end 108 | local function k(n,o,e)if n==nil then 109 | e.k="VGLOBAL"return"VGLOBAL"else 110 | local l=D(n,o)if l>=0 then 111 | e.k="VLOCAL"e.id=l 112 | return"VLOCAL"else 113 | if k(n.prev,o,e)=="VGLOBAL"then 114 | return"VGLOBAL"end 115 | e.k="VUPVAL"return"VUPVAL"end 116 | end 117 | end 118 | local function J(o)local n=V()k(l,n,o)if o.k=="VGLOBAL"then 119 | local e=C[n]if not e then 120 | e=#O+1 121 | O[e]={name=n,xref={L},}C[n]=e 122 | else 123 | local e=O[e].xref 124 | e[#e+1]=L 125 | end 126 | else 127 | local e=o.id 128 | local e=a[e].xref 129 | e[#e+1]=L 130 | end 131 | end 132 | local function k(n)local e={}e.isbreakable=n 133 | e.prev=l.bl 134 | e.locallist={}l.bl=e 135 | end 136 | local function L()local e=l.bl 137 | T()l.bl=e.prev 138 | end 139 | local function H()local e 140 | if not l then 141 | e=F 142 | else 143 | e={}end 144 | e.prev=l 145 | e.bl=nil 146 | e.locallist={}l=e 147 | end 148 | local function Q()T()l=l.prev 149 | end 150 | local function D(n)local l={}e()B(l)n.k="VINDEXED"end 151 | local function q(n)e()c(n)o("]")end 152 | local function M(e)local e,l={},{}if n==""then 153 | B(e)else 154 | q(e)end 155 | o("=")c(l)end 156 | local function T(e)if e.v.k=="VVOID"then return end 157 | e.v.k="VVOID"end 158 | local function T(e)c(e.v)end 159 | local function y(l)local c=u 160 | local e={}e.v={}e.t=l 161 | l.k="VRELOCABLE"e.v.k="VVOID"o("{")repeat 162 | if n=="}"then break end 163 | local n=n 164 | if n==""then 165 | if z()~="="then 166 | T(e)else 167 | M(e)end 168 | elseif n=="["then 169 | M(e)else 170 | T(e)end 171 | until not t(",")and not t(";")i("}","{",c)end 172 | local function z()local o=0 173 | if n~=")"then 174 | repeat 175 | local n=n 176 | if n==""then 177 | h(V())o=o+1 178 | elseif n=="..."then 179 | e()l.is_vararg=true 180 | else 181 | r(" or '...' expected")end 182 | until l.is_vararg or not t(",")end 183 | x(o)end 184 | local function M(c)local l={}local t=u 185 | local o=n 186 | if o=="("then 187 | if t~=X then 188 | r("ambiguous syntax (function call x new statement)")end 189 | e()if n==")"then 190 | l.k="VVOID"else 191 | p(l)end 192 | i(")","(",t)elseif o=="{"then 193 | y(l)elseif o==""then 194 | K(l,m)e()else 195 | r("function arguments expected")return 196 | end 197 | c.k="VCALL"end 198 | local function X(l)local n=n 199 | if n=="("then 200 | local n=u 201 | e()c(l)i(")","(",n)elseif n==""then 202 | J(l)else 203 | r("unexpected symbol")end 204 | end 205 | local function T(l)X(l)while true do 206 | local n=n 207 | if n=="."then 208 | D(l)elseif n=="["then 209 | local e={}q(e)elseif n==":"then 210 | local n={}e()B(n)M(l)elseif n=="("or n==""or n=="{"then 211 | M(l)else 212 | return 213 | end 214 | end 215 | end 216 | local function B(o)local n=n 217 | if n==""then 218 | o.k="VKNUM"elseif n==""then 219 | K(o,m)elseif n=="nil"then 220 | o.k="VNIL"elseif n=="true"then 221 | o.k="VTRUE"elseif n=="false"then 222 | o.k="VFALSE"elseif n=="..."then 223 | j(l.is_vararg==true,"cannot use '...' outside a vararg function");o.k="VVARARG"elseif n=="{"then 224 | y(o)return 225 | elseif n=="function"then 226 | e()N(o,false,u)return 227 | else 228 | T(o)return 229 | end 230 | e()end 231 | local function m(o,c)local l=n 232 | local t=ee[l]if t then 233 | e()m(o,Z)else 234 | B(o)end 235 | l=n 236 | local n=U[l]while n and n>c do 237 | local o={}e()local e=m(o,P[l])l=e 238 | n=U[l]end 239 | return l 240 | end 241 | function c(e)m(e,0)end 242 | local function K(e)local n={}local e=e.v.k 243 | j(e=="VLOCAL"or e=="VUPVAL"or e=="VGLOBAL"or e=="VINDEXED","syntax error")if t(",")then 244 | local e={}e.v={}T(e.v)K(e)else 245 | o("=")p(n)return 246 | end 247 | n.k="VNONRELOC"end 248 | local function m(e,n)o("do")k(false)x(e)b()L()end 249 | local function M(e)local n=f 250 | s("(for index)")s("(for limit)")s("(for step)")h(e)o("=")w()o(",")w()if t(",")then 251 | w()else 252 | end 253 | m(1,true)end 254 | local function P(e)local n={}s("(for generator)")s("(for state)")s("(for control)")h(e)local e=1 255 | while t(",")do 256 | h(V())e=e+1 257 | end 258 | o("in")local l=f 259 | p(n)m(e,false)end 260 | local function U(e)local l=false 261 | J(e)while n=="."do 262 | D(e)end 263 | if n==":"then 264 | l=true 265 | D(e)end 266 | return l 267 | end 268 | function w()local e={}c(e)end 269 | local function m()local e={}c(e)end 270 | local function w()e()m()o("then")b()end 271 | local function B()local e,n={}h(V())e.k="VLOCAL"x(1)N(n,false,u)end 272 | local function D()local e=0 273 | local n={}repeat 274 | h(V())e=e+1 275 | until not t(",")if t("=")then 276 | p(n)else 277 | n.k="VVOID"end 278 | x(e)end 279 | function p(e)c(e)while t(",")do 280 | c(e)end 281 | end 282 | function N(l,e,n)H()o("(")if e then 283 | s("self",true)x(1)end 284 | z()o(")")A()i("end","function",n)Q()end 285 | function b()k(false)A()L()end 286 | function for_stat()local o=f 287 | k(true)e()local l=V()local e=n 288 | if e=="="then 289 | M(l)elseif e==","or e=="in"then 290 | P(l)else 291 | r("'=' or 'in' expected")end 292 | i("end","for",o)L()end 293 | function while_stat()local n=f 294 | e()m()k(true)o("do")b()i("end","while",n)L()end 295 | function repeat_stat()local n=f 296 | k(true)k(false)e()A()i("until","repeat",n)m()L()L()end 297 | function if_stat()local l=f 298 | local o={}w()while n=="elseif"do 299 | w()end 300 | if n=="else"then 301 | e()b()end 302 | i("end","if",l)end 303 | function return_stat()local l={}e()local e=n 304 | if S[e]or e==";"then 305 | else 306 | p(l)end 307 | end 308 | function break_stat()local n=l.bl 309 | e()while n and not n.isbreakable do 310 | n=n.prev 311 | end 312 | if not n then 313 | r("no loop to break")end 314 | end 315 | function expr_stat()local e={}e.v={}T(e.v)if e.v.k=="VCALL"then 316 | else 317 | e.prev=nil 318 | K(e)end 319 | end 320 | function function_stat()local l=f 321 | local o,n={},{}e()local e=U(o)N(n,e,l)end 322 | function do_stat()local n=f 323 | e()b()i("end","do",n)end 324 | function local_stat()e()if t("function")then 325 | B()else 326 | D()end 327 | end 328 | local function o()f=u 329 | local e=n 330 | local n=Y[e]if n then 331 | W[n]()if e=="return"or e=="break"then return true end 332 | else 333 | expr_stat()end 334 | return false 335 | end 336 | function A()local e=false 337 | while not e and not S[n]do 338 | e=o()t(";")end 339 | end 340 | function parser()H()l.is_vararg=true 341 | e()A()G("")Q()return O,a 342 | end 343 | function init(e,o,c)d=1 344 | F={}local n=1 345 | g,_,E,R={},{},{},{}for l=1,#e do 346 | local e=e[l]local t=true 347 | if e=="TK_KEYWORD"or e=="TK_OP"then 348 | e=o[l]elseif e=="TK_NAME"then 349 | e=""_[n]=o[l]elseif e=="TK_NUMBER"then 350 | e=""_[n]=0 351 | elseif e=="TK_STRING"or e=="TK_LSTRING"then 352 | e=""_[n]=""elseif e=="TK_EOS"then 353 | e=""else 354 | t=false 355 | end 356 | if t then 357 | g[n]=e 358 | E[n]=c[l]R[n]=l 359 | n=n+1 360 | end 361 | end 362 | O,C,a={},{},{}v,I={},{}end 363 | return W 364 | -------------------------------------------------------------------------------- /sample/LuaSrcDiet.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | local l=string 3 | local e=math 4 | local P=table 5 | local u=require 6 | local d=print 7 | local c=l.sub 8 | local r=l.gmatch 9 | local n=u"llex"local h=u"lparser"local f=u"optlex"local x=u"optparser"local o 10 | local L=[[ 11 | LuaSrcDiet: Puts your Lua 5.1 source code on a diet 12 | Version 0.11.2 (20080608) Copyright (c) 2005-2008 Kein-Hong Man 13 | The COPYRIGHT file describes the conditions under which this 14 | software may be distributed. 15 | ]]local E=[[ 16 | usage: LuaSrcDiet [options] [filenames] 17 | 18 | example: 19 | >LuaSrcDiet myscript.lua -o myscript_.lua 20 | 21 | options: 22 | -v, --version prints version information 23 | -h, --help prints usage information 24 | -o specify file name to write output 25 | -s suffix for output files (default '_') 26 | --keep keep block comment with inside 27 | --plugin run in plugin/ directory 28 | - stop handling arguments 29 | 30 | (optimization levels) 31 | --none all optimizations off (normalizes EOLs only) 32 | --basic lexer-based optimizations only 33 | --maximum maximize reduction of source 34 | 35 | (informational) 36 | --quiet process files quietly 37 | --read-only read file and print token stats only 38 | --dump-lexer dump raw tokens from lexer to stdout 39 | --dump-parser dump variable tracking tables from parser 40 | --details extra info (strings, numbers, locals) 41 | 42 | features (to disable, insert 'no' prefix like --noopt-comments): 43 | %s 44 | default settings: 45 | %s]]local m=[[ 46 | --opt-comments,'remove comments and block comments' 47 | --opt-whitespace,'remove whitespace excluding EOLs' 48 | --opt-emptylines,'remove empty lines' 49 | --opt-eols,'all above, plus remove unnecessary EOLs' 50 | --opt-strings,'optimize strings and long strings' 51 | --opt-numbers,'optimize numbers' 52 | --opt-locals,'optimize local variable names' 53 | --opt-entropy,'tries to reduce symbol entropy of locals' 54 | ]]local b=[[ 55 | --opt-comments --opt-whitespace --opt-emptylines 56 | --opt-numbers --opt-locals 57 | ]]local k=[[ 58 | --opt-comments --opt-whitespace --opt-emptylines 59 | --noopt-eols --noopt-strings --noopt-numbers 60 | --noopt-locals 61 | ]]local K=[[ 62 | --opt-comments --opt-whitespace --opt-emptylines 63 | --opt-eols --opt-strings --opt-numbers 64 | --opt-locals --opt-entropy 65 | ]]local S=[[ 66 | --noopt-comments --noopt-whitespace --noopt-emptylines 67 | --noopt-eols --noopt-strings --noopt-numbers 68 | --noopt-locals 69 | ]]local s="_"local w="plugin/"local function i(e)d("LuaSrcDiet: "..e);os.exit()end 70 | if not l.match(_VERSION,"5.1",1,1)then 71 | i("requires Lua 5.1 to run")end 72 | local t=""do 73 | local a=24 74 | local o={}for n,i in r(m,"%s*([^,]+),'([^']+)'")do 75 | local e=" "..n 76 | e=e..l.rep(" ",a-#e)..i.."\n"t=t..e 77 | o[n]=true 78 | o["--no"..c(n,3)]=true 79 | end 80 | m=o 81 | end 82 | E=l.format(E,t,b)local y=s 83 | local e={}local a,s 84 | local function p(n)for n in r(n,"(%-%-%S+)")do 85 | if c(n,3,4)=="no"and 86 | m["--"..c(n,5)]then 87 | e[c(n,5)]=false 88 | else 89 | e[c(n,3)]=true 90 | end 91 | end 92 | end 93 | local r={"TK_KEYWORD","TK_NAME","TK_NUMBER","TK_STRING","TK_LSTRING","TK_OP","TK_EOS","TK_COMMENT","TK_LCOMMENT","TK_EOL","TK_SPACE",}local I=7 94 | local A={["\n"]="LF",["\r"]="CR",["\n\r"]="LFCR",["\r\n"]="CRLF",}local function T(n)local e=io.open(n,"rb")if not e then i('cannot open "'..n..'" for reading')end 95 | local o=e:read("*a")if not o then i('cannot read from "'..n..'"')end 96 | e:close()return o 97 | end 98 | local function R(e,o)local n=io.open(e,"wb")if not n then i('cannot open "'..e..'" for writing')end 99 | local o=n:write(o)if not o then i('cannot write to "'..e..'"')end 100 | n:close()end 101 | local function g()a,s={},{}for e=1,#r do 102 | local e=r[e]a[e],s[e]=0,0 103 | end 104 | end 105 | local function _(e,n)a[e]=a[e]+1 106 | s[e]=s[e]+#n 107 | end 108 | local function O()local function l(e,n)if e==0 then return 0 end 109 | return n/e 110 | end 111 | local t={}local n,e=0,0 112 | for o=1,I do 113 | local o=r[o]n=n+a[o];e=e+s[o]end 114 | a.TOTAL_TOK,s.TOTAL_TOK=n,e 115 | t.TOTAL_TOK=l(n,e)n,e=0,0 116 | for o=1,#r do 117 | local o=r[o]n=n+a[o];e=e+s[o]t[o]=l(a[o],s[o])end 118 | a.TOTAL_ALL,s.TOTAL_ALL=n,e 119 | t.TOTAL_ALL=l(n,e)return t 120 | end 121 | local function v(e)local e=T(e)n.init(e)n.llex()local n,o=n.tok,n.seminfo 122 | for e=1,#n do 123 | local n,e=n[e],o[e]if n=="TK_OP"and l.byte(e)<32 then 124 | e="("..l.byte(e)..")"elseif n=="TK_EOL"then 125 | e=A[e]else 126 | e="'"..e.."'"end 127 | d(n.." "..e)end 128 | end 129 | local function A(e)local o=d 130 | local e=T(e)n.init(e)n.llex()local e,t,n=n.tok,n.seminfo,n.tokln 131 | h.init(e,t,n)local n,i=h.parser()local t=l.rep("-",72)o("*** Local/Global Variable Tracker Tables ***")o(t.."\n GLOBALS\n"..t)for e=1,#n do 132 | local n=n[e]local e="("..e..") '"..n.name.."' -> "local n=n.xref 133 | for t=1,#n do e=e..n[t].." "end 134 | o(e)end 135 | o(t.."\n LOCALS (decl=declared act=activated rem=removed)\n"..t)for e=1,#i do 136 | local n=i[e]local e="("..e..") '"..n.name.."' decl:"..n.decl.." act:"..n.act.." rem:"..n.rem 137 | if n.isself then 138 | e=e.." isself"end 139 | e=e.." -> "local n=n.xref 140 | for t=1,#n do e=e..n[t].." "end 141 | o(e)end 142 | o(t.."\n")end 143 | local function I(o)local e=d 144 | local t=T(o)n.init(t)n.llex()local n,t=n.tok,n.seminfo 145 | e(L)e("Statistics for: "..o.."\n")g()for e=1,#n do 146 | local e,n=n[e],t[e]_(e,n)end 147 | local o=O()local n=l.format 148 | local function i(e)return a[e],s[e],o[e]end 149 | local t,a="%-16s%8s%8s%10s","%-16s%8d%8d%10.2f"local o=l.rep("-",42)e(n(t,"Lexical","Input","Input","Input"))e(n(t,"Elements","Count","Bytes","Average"))e(o)for t=1,#r do 150 | local t=r[t]e(n(a,t,i(t)))if t=="TK_EOS"then e(o)end 151 | end 152 | e(o)e(n(a,"Total Elements",i("TOTAL_ALL")))e(o)e(n(a,"Total Tokens",i("TOTAL_TOK")))e(o.."\n")end 153 | local function N(d,p)local function t(...)if e.QUIET then return end 154 | _G.print(...)end 155 | if o and o.init then 156 | e.EXIT=false 157 | o.init(e,d,p)if e.EXIT then return end 158 | end 159 | t(L)local i=T(d)if o and o.post_load then 160 | i=o.post_load(i)or i 161 | if e.EXIT then return end 162 | end 163 | n.init(i)n.llex()local n,i,c=n.tok,n.seminfo,n.tokln 164 | if o and o.post_lex then 165 | o.post_lex(n,i,c)if e.EXIT then return end 166 | end 167 | g()for e=1,#n do 168 | local e,n=n[e],i[e]_(e,n)end 169 | local u=O()local m,T=a,s 170 | if e["opt-locals"]then 171 | x.print=t 172 | h.init(n,i,c)local l,t=h.parser()if o and o.post_parse then 173 | o.post_parse(l,t)if e.EXIT then return end 174 | end 175 | x.optimize(e,n,i,l,t)if o and o.post_optparse then 176 | o.post_optparse()if e.EXIT then return end 177 | end 178 | end 179 | f.print=t 180 | n,i,c=f.optimize(e,n,i,c)if o and o.post_optlex then 181 | o.post_optlex(n,i,c)if e.EXIT then return end 182 | end 183 | local e=P.concat(i)if l.find(e,"\r\n",1,1)or 184 | l.find(e,"\n\r",1,1)then 185 | f.warn.mixedeol=true 186 | end 187 | R(p,e)g()for e=1,#n do 188 | local e,n=n[e],i[e]_(e,n)end 189 | local o=O()t("Statistics for: "..d.." -> "..p.."\n")local n=l.format 190 | local function i(e)return m[e],T[e],u[e],a[e],s[e],o[e]end 191 | local a,o="%-16s%8s%8s%10s%8s%8s%10s","%-16s%8d%8d%10.2f%8d%8d%10.2f"local e=l.rep("-",68)t("*** lexer-based optimizations summary ***\n"..e)t(n(a,"Lexical","Input","Input","Input","Output","Output","Output"))t(n(a,"Elements","Count","Bytes","Average","Count","Bytes","Average"))t(e)for l=1,#r do 192 | local l=r[l]t(n(o,l,i(l)))if l=="TK_EOS"then t(e)end 193 | end 194 | t(e)t(n(o,"Total Elements",i("TOTAL_ALL")))t(e)t(n(o,"Total Tokens",i("TOTAL_TOK")))t(e)if f.warn.lstring then 195 | t("* WARNING: "..f.warn.lstring)elseif f.warn.mixedeol then 196 | t("* WARNING: ".."output still contains some CRLF or LFCR line endings")end 197 | t()end 198 | local a={...}local s={}p(b)local function f(s)for o,n in ipairs(s)do 199 | local t 200 | local o,r=l.find(n,"%.[^%.%\\%/]*$")local l,a=n,""if o and o>1 then 201 | l=c(n,1,o-1)a=c(n,o,r)end 202 | t=l..y..a 203 | if#s==1 and e.OUTPUT_FILE then 204 | t=e.OUTPUT_FILE 205 | end 206 | if n==t then 207 | i("output filename identical to input filename")end 208 | if e.DUMP_LEXER then 209 | v(n)elseif e.DUMP_PARSER then 210 | A(n)elseif e.READ_ONLY then 211 | I(n)else 212 | N(n,t)end 213 | end 214 | end 215 | local function r()local n,t=#a,1 216 | if n==0 then 217 | e.HELP=true 218 | end 219 | while t<=n do 220 | local n,a=a[t],a[t+1]local l=l.match(n,"^%-%-?")if l=="-"then 221 | if n=="-h"then 222 | e.HELP=true;break 223 | elseif n=="-v"then 224 | e.VERSION=true;break 225 | elseif n=="-s"then 226 | if not a then i("-s option needs suffix specification")end 227 | y=a 228 | t=t+1 229 | elseif n=="-o"then 230 | if not a then i("-o option needs a file name")end 231 | e.OUTPUT_FILE=a 232 | t=t+1 233 | elseif n=="-"then 234 | break 235 | else 236 | i("unrecognized option "..n)end 237 | elseif l=="--"then 238 | if n=="--help"then 239 | e.HELP=true;break 240 | elseif n=="--version"then 241 | e.VERSION=true;break 242 | elseif n=="--keep"then 243 | if not a then i("--keep option needs a string to match for")end 244 | e.KEEP=a 245 | t=t+1 246 | elseif n=="--plugin"then 247 | if not a then i("--plugin option needs a module name")end 248 | if e.PLUGIN then i("only one plugin can be specified")end 249 | e.PLUGIN=a 250 | o=u(w..a)t=t+1 251 | elseif n=="--quiet"then 252 | e.QUIET=true 253 | elseif n=="--read-only"then 254 | e.READ_ONLY=true 255 | elseif n=="--basic"then 256 | p(k)elseif n=="--maximum"then 257 | p(K)elseif n=="--none"then 258 | p(S)elseif n=="--dump-lexer"then 259 | e.DUMP_LEXER=true 260 | elseif n=="--dump-parser"then 261 | e.DUMP_PARSER=true 262 | elseif n=="--details"then 263 | e.DETAILS=true 264 | elseif m[n]then 265 | p(n)else 266 | i("unrecognized option "..n)end 267 | else 268 | s[#s+1]=n 269 | end 270 | t=t+1 271 | end 272 | if e.HELP then 273 | d(L..E);return true 274 | elseif e.VERSION then 275 | d(L);return true 276 | end 277 | if#s>0 then 278 | if#s>1 and e.OUTPUT_FILE then 279 | i("with -o, only one source file can be specified")end 280 | f(s)return true 281 | else 282 | i("nothing to do!")end 283 | end 284 | if not r()then 285 | i("Please run with option -h or --help for usage information")end 286 | -------------------------------------------------------------------------------- /5.0/README: -------------------------------------------------------------------------------- 1 | Older Notes for LuaSrcDiet < 0.10 for Lua 5.0.2 2 | ----------------------------------------------- 3 | 4 | The following is the older README for the Lua 5.0.2 version of 5 | LuaSrcDiet, which is not maintained anymore. 6 | 7 | -- 8 | 9 | INTRODUCTION 10 | 11 | LuaSrcDiet reduces the size of Lua 5 source files by aggressively 12 | removing all unnecessary whitespace and comments. This is done using a 13 | work-alike version of the Lua 5.0.2 lexical analyzer which produces 14 | additional information on whitespace and comments. Token processing is 15 | then performed and what can be cut, is cut. The script finally verifies 16 | correctness of the generated file by running the result through its 17 | internal lexer again. 18 | 19 | LuaSrcDiet is written as a single self-contained Lua script. 20 | LuaSrcDiet_.lua is a sample output file processed at maximum reduction; 21 | a Lua 5.0.2 command line executable should be able to run it. 22 | 23 | WARNING: Whitespace removal is very aggressive but should be valid for 24 | the Lua 5.0.2 lexer. This is assuming that LuaSrcDiet's lexer behaves 25 | *exactly* like Lua 5.0.2's lexer. I have not analyzed the current Lua 26 | 5.1wk4 lexer in detail, so the output MAY FAIL on Lua 5.1wk4 (Lua 5.1 27 | will be supported in the future.) 28 | 29 | LuaSrcDiet currently optimizes based on tokens only. It does not 30 | currently support optimization methods that require the token stream to 31 | be parsed, e.g. optimizing constant numbers and strings or renaming 32 | locals. Such methods are future TODOs, but unlikely to be implemented 33 | anytime soon. Code contributions are welcome. 34 | 35 | -- 36 | 37 | WHAT'S NEW 38 | 39 | Major changes for version 0.9.1 (see the ChangeLog as well): 40 | * Fixed a bug in the lexer 41 | * Added a --dump option for dumping a raw token list 42 | 43 | Major changes for version 0.9.0 (see the ChangeLog as well): 44 | * Preliminary release, for Lua 5.0.2. Beta quality. 45 | 46 | -- 47 | 48 | USAGE OPTIONS 49 | 50 | usage: lua LuaSrcDiet.lua [options] [filenames] 51 | 52 | options: 53 | -h, --help prints usage information 54 | -o specify file name to write output 55 | --quiet do not display statistics 56 | --read-only read file and print token stats 57 | --keep-lines preserve line numbering 58 | --maximum maximize reduction of source 59 | --dump dump raw tokens from lexer 60 | -- stop handling arguments 61 | 62 | example: 63 | >lua LuaSrcDiet.lua myscript.lua -o myscript_.lua 64 | 65 | -- 66 | 67 | USING LUASRCDIET 68 | 69 | By default, LuaSrcDiet names the output file by adding an underscore to 70 | the basename of the source file. Some statistical information is shown 71 | by default. "Total Tokens" is the number of tokens that will be actually 72 | lexed by the Lua 5.0.2 parser, while "Total Elements" is the total of 73 | tokens plus whitespace and comment elements. 74 | 75 | If you wish to preserve line numbering, use --keep-lines to leave empty 76 | lines in the output. So if you are debugging, an error message will show 77 | the same line number as your original uncompressed sources. 78 | 79 | To see the token statistics of LuaSrcDiet.lua itself ('...' denote the 80 | title information normally displayed by LuaSrcDiet) without doing any 81 | processing or generating an output file: 82 | 83 | >lua LuaSrcDiet.lua LuaSrcDiet.lua --read-only 84 | ... 85 | Statistics for: LuaSrcDiet.lua 86 | 87 | Elements Count Bytes 88 | -------------------------------- 89 | TK_KEYWORD 635 2439 90 | TK_NAME 1282 6004 91 | TK_NUMBER 103 117 92 | TK_STRING 253 3237 93 | TK_OP 1515 1657 94 | TK_EOS 1 0 95 | -------------------------------- 96 | TK_COMMENT 261 11664 97 | TK_LCOMMENT 10 3857 98 | TK_EOL 902 902 99 | TK_SPACE 2172 5845 100 | -------------------------------- 101 | Total Elements 7134 35722 102 | -------------------------------- 103 | Total Tokens 3789 13454 104 | -------------------------------- 105 | 106 | To compress LuaSrcDiet.lua into LuaSrcDiet_.lua: 107 | 108 | >lua LuaSrcDiet.lua LuaSrcDiet.lua 109 | ... 110 | Statistics for: LuaSrcDiet.lua -> LuaSrcDiet_.lua 111 | 112 | Lexical Input Input Output Output 113 | Elements Count Bytes Count Bytes 114 | -------------------------------------------------- 115 | TK_KEYWORD 635 2439 635 2439 116 | TK_NAME 1282 6004 1282 6004 117 | TK_NUMBER 103 117 103 117 118 | TK_STRING 253 3237 253 3237 119 | TK_OP 1515 1657 1515 1657 120 | TK_EOS 1 0 1 0 121 | -------------------------------------------------- 122 | TK_COMMENT 261 11664 1 14 123 | TK_LCOMMENT 10 3857 0 0 124 | TK_EOL 902 902 672 672 125 | TK_SPACE 2172 5845 334 334 126 | -------------------------------------------------- 127 | Total Elements 7134 35722 4796 14474 128 | -------------------------------------------------- 129 | Total Tokens 3789 13454 3789 13454 130 | -------------------------------------------------- 131 | 132 | Note that 'real' tokens that are lexed by Lua 5.0.2 does not change; 133 | LuaSrcDiet does not perform any heavy-duty optimizations at the moment. 134 | The 'before' and 'after' token stream is always compared to ensure that 135 | the reduction or compression process is correct. 136 | 137 | To compress using maximum settings: 138 | 139 | >lua LuaSrcDiet.lua LuaSrcDiet.lua --maximum 140 | ... 141 | Statistics for: LuaSrcDiet.lua -> LuaSrcDiet_.lua 142 | 143 | Lexical Input Input Output Output 144 | Elements Count Bytes Count Bytes 145 | -------------------------------------------------- 146 | TK_KEYWORD 635 2439 635 2439 147 | TK_NAME 1282 6004 1282 6004 148 | TK_NUMBER 103 117 103 117 149 | TK_STRING 253 3237 253 3237 150 | TK_OP 1515 1657 1515 1657 151 | TK_EOS 1 0 1 0 152 | -------------------------------------------------- 153 | TK_COMMENT 261 11664 1 14 154 | TK_LCOMMENT 10 3857 0 0 155 | TK_EOL 902 902 370 370 156 | TK_SPACE 2172 5845 334 334 157 | -------------------------------------------------- 158 | Total Elements 7134 35722 4494 14172 159 | -------------------------------------------------- 160 | Total Tokens 3789 13454 3789 13454 161 | -------------------------------------------------- 162 | 163 | The --maximum option additionally removes unnecessary line endings, at 164 | the cost of reduced readability. 165 | 166 | -- 167 | 168 | CODE SIZE REDUCTION 169 | 170 | There are 3 options for output generation: the default, --keep-lines and 171 | --maximum. The following table details what each option does: 172 | 173 | Reduction Scheme (default) --keep-lines --maximum 174 | ---------------------------------------------------------------- 175 | Removal of comments Y Y Y 176 | Removal of block comments Y Y Y 177 | Basic space and tab removal Y Y Y 178 | Delete empty lines Y Y 179 | Delete all unnecessary EOLs Y 180 | ---------------------------------------------------------------- 181 | *EOL=end-of-line 182 | 183 | The default settings keeps your output a little bit more readable than 184 | the maximum setting while cutting out the majority of the unnecessary 185 | bytes in your source file. Actual savings will depend on your coding 186 | style (amount and type of whitespace and comments used.) 187 | 188 | -- 189 | 190 | OTHER OPTIONS 191 | 192 | The --dump option performs a complete dump of the raw token list 193 | generated by the LuaSrcDiet lexer. This token list is somewhat different 194 | from the token format of the Lua lexer (llex.c); it includes 195 | non-essential tokens such as EOLs, whitespace and comments, which is 196 | needed for code size reduction processing. Useful for debugging and 197 | diagnostic purposes. 198 | 199 | -- 200 | 201 | BEHAVIOUR NOTES 202 | 203 | TK_EOS is always 1 and does not occupy any space. 204 | 205 | The TK_EOL count doesn't include newlines inside strings, so it will 206 | probably not be the same as the number of lines in the source file. 207 | 208 | For scripts with the 'shell bang' line, the line is preserved unchanged 209 | and is reflected as a single remaining TK_COMMENT element. 210 | 211 | The Lua 5.0.2 lexer reads a file line-by-line, so you might find that 212 | LuaSrcDiet handles non-LF line endings incorrectly. It should not affect 213 | the actual token stream, though, so your programs should still work 214 | correctly. If the byte total reported by LuaSrcDiet is different from 215 | the original file, it's probably due to conversion of CRLF line endings 216 | to internal LF line endings. Generated Lua files always have LF line 217 | endings. 218 | 219 | If your editor cannot syntax-highlight a reduced Lua source file 220 | properly, try the SciTE editor or a Scintilla-based editor -- it 221 | highlights perfectly. 222 | 223 | TODO: For the upcoming Lua 5.1, LuaSrcDiet will later be changed to 224 | handle CR, LF and CRLF line endings transparently, in sync with the 225 | lexer changes. 226 | 227 | -- 228 | 229 | ACKNOWLEDGEMENTS 230 | 231 | Thanks to the LuaForge people for hosting this. 232 | Developed on SciTE http://www.scintilla.org/. Two thumbs up. 233 | 234 | -- 235 | 236 | FEEDBACK 237 | 238 | Feedback and contributions are welcome. Your name will be acknowledged, 239 | as long as you are willing to comply with COPYRIGHT. If your material is 240 | self-contained, you can retain a copyright notice for those material in 241 | your own name, as long as you use the same Lua 5/MIT-style copyright. 242 | 243 | I am on dial-up, so I might not be able to reply immediately. My 244 | alternative e-mail address is: mkh AT pl DOT jaring DOT my 245 | 246 | Enjoy!! 247 | 248 | Kein-Hong Man (esq.) 249 | Kuala Lumpur 250 | Malaysia 20050815 251 | -------------------------------------------------------------------------------- /sample/llex_whitespace.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | llex.lua: Lua 5.1 lexical analyzer in Lua 4 | This file is part of LuaSrcDiet, based on Yueliang material. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ------------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- * This is a version of the native 5.1.x lexer from Yueliang 0.4.0, 17 | -- with significant modifications to handle LuaSrcDiet's needs: 18 | -- (1) llex.error is an optional error function handler 19 | -- (2) seminfo for strings include their delimiters and no 20 | -- translation operations are performed on them 21 | -- * ADDED shbang handling has been added to support executable scripts 22 | -- * NO localized decimal point replacement magic 23 | -- * NO limit to number of lines 24 | -- * NO support for compatible long strings (LUA_COMPAT_LSTR) 25 | -- * Please read technotes.txt for more technical details. 26 | ------------------------------------------------------------------------]] 27 | 28 | local base=_G 29 | local string=require"string" 30 | module"llex" 31 | 32 | local find=string.find 33 | local match=string.match 34 | local sub=string.sub 35 | 36 | ---------------------------------------------------------------------- 37 | -- initialize keyword list, variables 38 | ---------------------------------------------------------------------- 39 | 40 | local kw={} 41 | for v in string.gmatch([[ 42 | and break do else elseif end false for function if in 43 | local nil not or repeat return then true until while]],"%S+")do 44 | kw[v]=true 45 | end 46 | 47 | -- NOTE: see init() for module variables (externally visible): 48 | -- tok, seminfo, tokln 49 | 50 | local z,-- source stream 51 | sourceid,-- name of source 52 | I,-- position of lexer 53 | buff,-- buffer for strings 54 | ln-- line number 55 | 56 | ---------------------------------------------------------------------- 57 | -- add information to token listing 58 | ---------------------------------------------------------------------- 59 | 60 | local function addtoken(token,info) 61 | local i=#tok+1 62 | tok[i]=token 63 | seminfo[i]=info 64 | tokln[i]=ln 65 | end 66 | 67 | ---------------------------------------------------------------------- 68 | -- handles line number incrementation and end-of-line characters 69 | ---------------------------------------------------------------------- 70 | 71 | local function inclinenumber(i,is_tok) 72 | local sub=sub 73 | local old=sub(z,i,i) 74 | i=i+1-- skip '\n' or '\r' 75 | local c=sub(z,i,i) 76 | if(c=="\n"or c=="\r")and(c~=old)then 77 | i=i+1-- skip '\n\r' or '\r\n' 78 | old=old..c 79 | end 80 | if is_tok then addtoken("TK_EOL",old)end 81 | ln=ln+1 82 | I=i 83 | return i 84 | end 85 | 86 | ---------------------------------------------------------------------- 87 | -- initialize lexer for given source _z and source name _sourceid 88 | ---------------------------------------------------------------------- 89 | 90 | function init(_z,_sourceid) 91 | z=_z-- source 92 | sourceid=_sourceid-- name of source 93 | I=1-- lexer's position in source 94 | ln=1-- line number 95 | tok={}-- lexed token list* 96 | seminfo={}-- lexed semantic information list* 97 | tokln={}-- line numbers for messages* 98 | -- (*) externally visible thru' module 99 | -------------------------------------------------------------------- 100 | -- initial processing (shbang handling) 101 | -------------------------------------------------------------------- 102 | local p,_,q,r=find(z,"^(#[^\r\n]*)(\r?\n?)") 103 | if p then-- skip first line 104 | I=I+#q 105 | addtoken("TK_COMMENT",q) 106 | if#r>0 then inclinenumber(I,true)end 107 | end 108 | end 109 | 110 | ---------------------------------------------------------------------- 111 | -- returns a chunk name or id, no truncation for long names 112 | ---------------------------------------------------------------------- 113 | 114 | function chunkid() 115 | if sourceid and match(sourceid,"^[=@]")then 116 | return sub(sourceid,2)-- remove first char 117 | end 118 | return"[string]" 119 | end 120 | 121 | ---------------------------------------------------------------------- 122 | -- formats error message and throws error 123 | -- * a simplified version, does not report what token was responsible 124 | ---------------------------------------------------------------------- 125 | 126 | function errorline(s,line) 127 | local e=error or base.error 128 | e(string.format("%s:%d: %s",chunkid(),line or ln,s)) 129 | end 130 | local errorline=errorline 131 | 132 | ------------------------------------------------------------------------ 133 | -- count separators ("=") in a long string delimiter 134 | ------------------------------------------------------------------------ 135 | 136 | local function skip_sep(i) 137 | local sub=sub 138 | local s=sub(z,i,i) 139 | i=i+1 140 | local count=#match(z,"=*",i)-- note, take the length 141 | i=i+count 142 | I=i 143 | return(sub(z,i,i)==s)and count or(-count)-1 144 | end 145 | 146 | ---------------------------------------------------------------------- 147 | -- reads a long string or long comment 148 | ---------------------------------------------------------------------- 149 | 150 | local function read_long_string(is_str,sep) 151 | local i=I+1-- skip 2nd '[' 152 | local sub=sub 153 | local c=sub(z,i,i) 154 | if c=="\r"or c=="\n"then-- string starts with a newline? 155 | i=inclinenumber(i)-- skip it 156 | end 157 | local j=i 158 | while true do 159 | local p,q,r=find(z,"([\r\n%]])",i)-- (long range) 160 | if not p then 161 | errorline(is_str and"unfinished long string"or 162 | "unfinished long comment") 163 | end 164 | i=p 165 | if r=="]"then-- delimiter test 166 | if skip_sep(i)==sep then 167 | buff=sub(z,buff,I) 168 | I=I+1-- skip 2nd ']' 169 | return buff 170 | end 171 | i=I 172 | else-- newline 173 | buff=buff.."\n" 174 | i=inclinenumber(i) 175 | end 176 | end--while 177 | end 178 | 179 | ---------------------------------------------------------------------- 180 | -- reads a string 181 | ---------------------------------------------------------------------- 182 | 183 | local function read_string(del) 184 | local i=I 185 | local find=find 186 | local sub=sub 187 | while true do 188 | local p,q,r=find(z,"([\n\r\\\"\'])",i)-- (long range) 189 | if p then 190 | if r=="\n"or r=="\r"then 191 | errorline("unfinished string") 192 | end 193 | i=p 194 | if r=="\\"then-- handle escapes 195 | i=i+1 196 | r=sub(z,i,i) 197 | if r==""then break end-- (EOZ error) 198 | p=find("abfnrtv\n\r",r,1,true) 199 | ------------------------------------------------------ 200 | if p then-- special escapes 201 | if p>7 then 202 | i=inclinenumber(i) 203 | else 204 | i=i+1 205 | end 206 | ------------------------------------------------------ 207 | elseif find(r,"%D")then-- other non-digits 208 | i=i+1 209 | ------------------------------------------------------ 210 | else-- \xxx sequence 211 | local p,q,s=find(z,"^(%d%d?%d?)",i) 212 | i=q+1 213 | if s+1>256 then-- UCHAR_MAX 214 | errorline("escape sequence too large") 215 | end 216 | ------------------------------------------------------ 217 | end--if p 218 | else 219 | i=i+1 220 | if r==del then-- ending delimiter 221 | I=i 222 | return sub(z,buff,i-1)-- return string 223 | end 224 | end--if r 225 | else 226 | break-- (error) 227 | end--if p 228 | end--while 229 | errorline("unfinished string") 230 | end 231 | 232 | ------------------------------------------------------------------------ 233 | -- main lexer function 234 | ------------------------------------------------------------------------ 235 | 236 | function llex() 237 | local find=find 238 | local match=match 239 | while true do--outer 240 | local i=I 241 | -- inner loop allows break to be used to nicely section tests 242 | while true do--inner 243 | ---------------------------------------------------------------- 244 | local p,_,r=find(z,"^([_%a][_%w]*)",i) 245 | if p then 246 | I=i+#r 247 | if kw[r]then 248 | addtoken("TK_KEYWORD",r)-- reserved word (keyword) 249 | else 250 | addtoken("TK_NAME",r)-- identifier 251 | end 252 | break-- (continue) 253 | end 254 | ---------------------------------------------------------------- 255 | local p,_,r=find(z,"^(%.?)%d",i) 256 | if p then-- numeral 257 | if r=="."then i=i+1 end 258 | local _,q,r=find(z,"^%d*[%.%d]*([eE]?)",i) 259 | i=q+1 260 | if#r==1 then-- optional exponent 261 | if match(z,"^[%+%-]",i)then-- optional sign 262 | i=i+1 263 | end 264 | end 265 | local _,q=find(z,"^[_%w]*",i) 266 | I=q+1 267 | local v=sub(z,p,q)-- string equivalent 268 | if not base.tonumber(v)then-- handles hex test also 269 | errorline("malformed number") 270 | end 271 | addtoken("TK_NUMBER",v) 272 | break-- (continue) 273 | end 274 | ---------------------------------------------------------------- 275 | local p,q,r,t=find(z,"^((%s)[ \t\v\f]*)",i) 276 | if p then 277 | if t=="\n"or t=="\r"then-- newline 278 | inclinenumber(i,true) 279 | else 280 | I=q+1-- whitespace 281 | addtoken("TK_SPACE",r) 282 | end 283 | break-- (continue) 284 | end 285 | ---------------------------------------------------------------- 286 | local r=match(z,"^%p",i) 287 | if r then 288 | buff=i 289 | local p=find("-[\"\'.=<>~",r,1,true) 290 | if p then 291 | -- two-level if block for punctuation/symbols 292 | -------------------------------------------------------- 293 | if p<=2 then 294 | if p==1 then-- minus 295 | local c=match(z,"^%-%-(%[?)",i) 296 | if c then 297 | i=i+2 298 | local sep=-1 299 | if c=="["then 300 | sep=skip_sep(i) 301 | end 302 | if sep>=0 then-- long comment 303 | addtoken("TK_LCOMMENT",read_long_string(false,sep)) 304 | else-- short comment 305 | I=find(z,"[\n\r]",i)or(#z+1) 306 | addtoken("TK_COMMENT",sub(z,buff,I-1)) 307 | end 308 | break-- (continue) 309 | end 310 | -- (fall through for "-") 311 | else-- [ or long string 312 | local sep=skip_sep(i) 313 | if sep>=0 then 314 | addtoken("TK_LSTRING",read_long_string(true,sep)) 315 | elseif sep==-1 then 316 | addtoken("TK_OP","[") 317 | else 318 | errorline("invalid long string delimiter") 319 | end 320 | break-- (continue) 321 | end 322 | -------------------------------------------------------- 323 | elseif p<=5 then 324 | if p<5 then-- strings 325 | I=i+1 326 | addtoken("TK_STRING",read_string(r)) 327 | break-- (continue) 328 | end 329 | r=match(z,"^%.%.?%.?",i)-- .|..|... dots 330 | -- (fall through) 331 | -------------------------------------------------------- 332 | else-- relational 333 | r=match(z,"^%p=?",i) 334 | -- (fall through) 335 | end 336 | end 337 | I=i+#r 338 | addtoken("TK_OP",r)-- for other symbols, fall through 339 | break-- (continue) 340 | end 341 | ---------------------------------------------------------------- 342 | local r=sub(z,i,i) 343 | if r~=""then 344 | I=i+1 345 | addtoken("TK_OP",r)-- other single-char tokens 346 | break 347 | end 348 | addtoken("TK_EOS","")-- end of stream, 349 | return-- exit here 350 | ---------------------------------------------------------------- 351 | end--while inner 352 | end--while outer 353 | end 354 | 355 | return base.getfenv() 356 | -------------------------------------------------------------------------------- /cmake/lua.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library for Lua. 2 | # 3 | # Copyright (C) 2007-2012 LuaDist. 4 | # by David Manura, Peter Drahos 5 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 6 | # For details see the COPYRIGHT file distributed with LuaDist. 7 | # Please note that the package source code is licensed under its own license. 8 | 9 | set ( INSTALL_LMOD ${INSTALL_LIB}/lua 10 | CACHE PATH "Directory to install Lua modules." ) 11 | set ( INSTALL_CMOD ${INSTALL_LIB}/lua 12 | CACHE PATH "Directory to install Lua binary modules." ) 13 | 14 | option ( SKIP_LUA_WRAPPER 15 | "Do not build and install Lua executable wrappers." OFF) 16 | 17 | # List of (Lua module name, file path) pairs. 18 | # Used internally by add_lua_test. Built by add_lua_module. 19 | set ( _lua_modules ) 20 | 21 | # utility function: appends path `path` to path `basepath`, properly 22 | # handling cases when `path` may be relative or absolute. 23 | macro ( _append_path basepath path result ) 24 | if ( IS_ABSOLUTE "${path}" ) 25 | set ( ${result} "${path}" ) 26 | else () 27 | set ( ${result} "${basepath}/${path}" ) 28 | endif () 29 | endmacro () 30 | 31 | # install_lua_executable ( target source ) 32 | # Automatically generate a binary if srlua package is available 33 | # The application or its source will be placed into /bin 34 | # If the application source did not have .lua suffix then it will be added 35 | # USE: lua_executable ( sputnik src/sputnik.lua ) 36 | macro ( install_lua_executable _name _source ) 37 | get_filename_component ( _source_name ${_source} NAME_WE ) 38 | # Find srlua and glue 39 | find_program( SRLUA_EXECUTABLE NAMES srlua ) 40 | find_program( GLUE_EXECUTABLE NAMES glue ) 41 | # Executable output 42 | set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) 43 | if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) 44 | # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems 45 | add_custom_command( 46 | OUTPUT ${_exe} 47 | COMMAND ${GLUE_EXECUTABLE} 48 | ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} 49 | DEPENDS ${_source} 50 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 51 | VERBATIM 52 | ) 53 | # Make sure we have a target associated with the binary 54 | add_custom_target(${_name} ALL 55 | DEPENDS ${_exe} 56 | ) 57 | # Install with run permissions 58 | install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) 59 | # Also install source as optional resurce 60 | install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) 61 | else() 62 | # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic 63 | install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} 64 | RENAME ${_source_name} 65 | COMPONENT Runtime 66 | ) 67 | endif() 68 | endmacro () 69 | 70 | macro ( _lua_module_helper is_install _name ) 71 | parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) 72 | # _target is CMake-compatible target name for module (e.g. socket_core). 73 | # _module is relative path of target (e.g. socket/core), 74 | # without extension (e.g. .lua/.so/.dll). 75 | # _MODULE_SRC is list of module source files (e.g. .lua and .c files). 76 | # _MODULE_NAMES is list of module names (e.g. socket.core). 77 | if ( _MODULE_ALL_IN_ONE ) 78 | string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) 79 | string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) 80 | set ( _target "${_target}_all_in_one") 81 | set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) 82 | set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) 83 | else () 84 | string ( REPLACE "." "_" _target "${_name}" ) 85 | string ( REPLACE "." "/" _module "${_name}" ) 86 | set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) 87 | set ( _MODULE_NAMES ${_name} ) 88 | endif () 89 | if ( NOT _MODULE_SRC ) 90 | message ( FATAL_ERROR "no module sources specified" ) 91 | endif () 92 | list ( GET _MODULE_SRC 0 _first_source ) 93 | 94 | get_filename_component ( _ext ${_first_source} EXT ) 95 | if ( _ext STREQUAL ".lua" ) # Lua source module 96 | list ( LENGTH _MODULE_SRC _len ) 97 | if ( _len GREATER 1 ) 98 | message ( FATAL_ERROR "more than one source file specified" ) 99 | endif () 100 | 101 | set ( _module "${_module}.lua" ) 102 | 103 | get_filename_component ( _module_dir ${_module} PATH ) 104 | get_filename_component ( _module_filename ${_module} NAME ) 105 | _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) 106 | list ( APPEND _lua_modules "${_name}" "${_module_path}" ) 107 | 108 | if ( ${is_install} ) 109 | install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} 110 | RENAME ${_module_filename} 111 | COMPONENT Runtime 112 | ) 113 | endif () 114 | else () # Lua C binary module 115 | enable_language ( C ) 116 | find_package ( Lua REQUIRED ) 117 | include_directories ( ${LUA_INCLUDE_DIR} ) 118 | 119 | set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) 120 | 121 | get_filename_component ( _module_dir ${_module} PATH ) 122 | get_filename_component ( _module_filenamebase ${_module} NAME_WE ) 123 | foreach ( _thisname ${_MODULE_NAMES} ) 124 | list ( APPEND _lua_modules "${_thisname}" 125 | "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) 126 | endforeach () 127 | 128 | add_library( ${_target} MODULE ${_MODULE_SRC}) 129 | target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) 130 | set_target_properties ( ${_target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY 131 | "${_module_dir}" PREFIX "" OUTPUT_NAME "${_module_filenamebase}" ) 132 | if ( ${is_install} ) 133 | install ( TARGETS ${_target} DESTINATION ${INSTALL_CMOD}/${_module_dir} COMPONENT Runtime) 134 | endif () 135 | endif () 136 | endmacro () 137 | 138 | # add_lua_module 139 | # Builds a Lua source module into a destination locatable by Lua 140 | # require syntax. 141 | # Binary modules are also supported where this function takes sources and 142 | # libraries to compile separated by LINK keyword. 143 | # USE: add_lua_module ( socket.http src/http.lua ) 144 | # USE2: add_lua_module ( mime.core src/mime.c ) 145 | # USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 146 | # USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) 147 | # This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing 148 | # both modules ssl.context and ssl.core). The CMake target name will be 149 | # ssl_all_in_one. 150 | # Also sets variable _module_path (relative path where module typically 151 | # would be installed). 152 | macro ( add_lua_module ) 153 | _lua_module_helper ( 0 ${ARGN} ) 154 | endmacro () 155 | 156 | 157 | # install_lua_module 158 | # This is the same as `add_lua_module` but also installs the module. 159 | # USE: install_lua_module ( socket.http src/http.lua ) 160 | # USE2: install_lua_module ( mime.core src/mime.c ) 161 | # USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 162 | macro ( install_lua_module ) 163 | _lua_module_helper ( 1 ${ARGN} ) 164 | endmacro () 165 | 166 | # Builds string representing Lua table mapping Lua modules names to file 167 | # paths. Used internally. 168 | macro ( _make_module_table _outvar ) 169 | set ( ${_outvar} ) 170 | list ( LENGTH _lua_modules _n ) 171 | if ( ${_n} GREATER 0 ) # avoids cmake complaint 172 | foreach ( _i RANGE 1 ${_n} 2 ) 173 | list ( GET _lua_modules ${_i} _path ) 174 | math ( EXPR _ii ${_i}-1 ) 175 | list ( GET _lua_modules ${_ii} _name ) 176 | set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") 177 | endforeach () 178 | endif () 179 | set ( ${_outvar} 180 | "local modules = { 181 | ${_table}}" ) 182 | endmacro () 183 | 184 | # add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) 185 | # Runs Lua script `_testfile` under CTest tester. 186 | # Optional named argument `WORKING_DIRECTORY` is current working directory to 187 | # run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). 188 | # Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. 189 | # Any modules previously defined with install_lua_module are automatically 190 | # preloaded (via package.preload) prior to running the test script. 191 | # Under LuaDist, set test=true in config.lua to enable testing. 192 | # USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) 193 | macro ( add_lua_test _testfile ) 194 | if ( NOT SKIP_TESTING ) 195 | parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) 196 | include ( CTest ) 197 | find_program ( LUA NAMES lua lua.bat ) 198 | get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) 199 | get_filename_component ( TESTFILENAME ${_testfile} NAME ) 200 | get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) 201 | 202 | # Write wrapper script. 203 | # Note: One simple way to allow the script to find modules is 204 | # to just put them in package.preload. 205 | set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) 206 | _make_module_table ( _table ) 207 | set ( TESTWRAPPERSOURCE 208 | "local CMAKE_CFG_INTDIR = ... or '.' 209 | ${_table} 210 | local function preload_modules(modules) 211 | for name, path in pairs(modules) do 212 | if path:match'%.lua' then 213 | package.preload[name] = assert(loadfile(path)) 214 | else 215 | local name = name:gsub('.*%-', '') -- remove any hyphen prefix 216 | local symbol = 'luaopen_' .. name:gsub('%.', '_') 217 | --improve: generalize to support all-in-one loader? 218 | local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) 219 | package.preload[name] = assert(package.loadlib(path, symbol)) 220 | end 221 | end 222 | end 223 | preload_modules(modules) 224 | arg[0] = '${TESTFILEABS}' 225 | table.remove(arg, 1) 226 | return assert(loadfile '${TESTFILEABS}')(unpack(arg)) 227 | " ) 228 | if ( _ARG_WORKING_DIRECTORY ) 229 | get_filename_component ( 230 | TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) 231 | # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. 232 | set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) 233 | endif () 234 | file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) 235 | add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} 236 | ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" 237 | ${_ARG_DEFAULT_ARGS} ) 238 | endif () 239 | # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake 240 | # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper 241 | # expansion by the native build tool. 242 | endmacro () 243 | 244 | 245 | # Converts Lua source file `_source` to binary string embedded in C source 246 | # file `_target`. Optionally compiles Lua source to byte code (not available 247 | # under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua 248 | # versions of bin2c [1] and luac [2] may be passed respectively as additional 249 | # arguments. 250 | # 251 | # [1] http://lua-users.org/wiki/BinToCee 252 | # [2] http://lua-users.org/wiki/LuaCompilerInLua 253 | function ( add_lua_bin2c _target _source ) 254 | find_program ( LUA NAMES lua lua.bat ) 255 | execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" 256 | RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) 257 | if ( NOT ${_LUA_DUMP_RESULT} ) 258 | SET ( HAVE_LUA_DUMP true ) 259 | endif () 260 | message ( "-- string.dump=${HAVE_LUA_DUMP}" ) 261 | 262 | if ( ARGV2 ) 263 | get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) 264 | set ( BIN2C ${LUA} ${BIN2C} ) 265 | else () 266 | find_program ( BIN2C NAMES bin2c bin2c.bat ) 267 | endif () 268 | if ( HAVE_LUA_DUMP ) 269 | if ( ARGV3 ) 270 | get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) 271 | set ( LUAC ${LUA} ${LUAC} ) 272 | else () 273 | find_program ( LUAC NAMES luac luac.bat ) 274 | endif () 275 | endif ( HAVE_LUA_DUMP ) 276 | message ( "-- bin2c=${BIN2C}" ) 277 | message ( "-- luac=${LUAC}" ) 278 | 279 | get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) 280 | if ( HAVE_LUA_DUMP ) 281 | get_filename_component ( SOURCEBASE ${_source} NAME_WE ) 282 | add_custom_command ( 283 | OUTPUT ${_target} DEPENDS ${_source} 284 | COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 285 | ${SOURCEABS} 286 | COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 287 | ">${_target}" ) 288 | else () 289 | add_custom_command ( 290 | OUTPUT ${_target} DEPENDS ${SOURCEABS} 291 | COMMAND ${BIN2C} ${_source} ">${_target}" ) 292 | endif () 293 | endfunction() 294 | -------------------------------------------------------------------------------- /sample/llex_locals.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | llex.lua: Lua 5.1 lexical analyzer in Lua 4 | This file is part of LuaSrcDiet, based on Yueliang material. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- * This is a version of the native 5.1.x lexer from Yueliang 0.4.0, 17 | -- with significant modifications to handle LuaSrcDiet's needs: 18 | -- (1) llex.error is an optional error function handler 19 | -- (2) seminfo for strings include their delimiters and no 20 | -- translation operations are performed on them 21 | -- * ADDED shbang handling has been added to support executable scripts 22 | -- * NO localized decimal point replacement magic 23 | -- * NO limit to number of lines 24 | -- * NO support for compatible long strings (LUA_COMPAT_LSTR) 25 | -- * Please read technotes.txt for more technical details. 26 | ----------------------------------------------------------------------]] 27 | 28 | local c = _G 29 | local h = require "string" 30 | module "llex" 31 | 32 | local l = h.find 33 | local m = h.match 34 | local n = h.sub 35 | 36 | ---------------------------------------------------------------------- 37 | -- initialize keyword list, variables 38 | ---------------------------------------------------------------------- 39 | 40 | local w = {} 41 | for e in h.gmatch([[ 42 | and break do else elseif end false for function if in 43 | local nil not or repeat return then true until while]], "%S+") do 44 | w[e] = true 45 | end 46 | 47 | -- NOTE: see init() for module variables (externally visible): 48 | -- tok, seminfo, tokln 49 | 50 | local e, -- source stream 51 | r, -- name of source 52 | a, -- position of lexer 53 | i, -- buffer for strings 54 | s -- line number 55 | 56 | ---------------------------------------------------------------------- 57 | -- add information to token listing 58 | ---------------------------------------------------------------------- 59 | 60 | local function o(t, a) 61 | local e = #tok + 1 62 | tok[e] = t 63 | seminfo[e] = a 64 | tokln[e] = s 65 | end 66 | 67 | ---------------------------------------------------------------------- 68 | -- handles line number incrementation and end-of-line characters 69 | ---------------------------------------------------------------------- 70 | 71 | local function d(t, h) 72 | local n = n 73 | local i = n(e, t, t) 74 | t = t + 1 -- skip '\n' or '\r' 75 | local e = n(e, t, t) 76 | if (e == "\n" or e == "\r") and (e ~= i) then 77 | t = t + 1 -- skip '\n\r' or '\r\n' 78 | i = i..e 79 | end 80 | if h then o("TK_EOL", i) end 81 | s = s + 1 82 | a = t 83 | return t 84 | end 85 | 86 | ---------------------------------------------------------------------- 87 | -- initialize lexer for given source _z and source name _sourceid 88 | ---------------------------------------------------------------------- 89 | 90 | function init(i, t) 91 | e = i -- source 92 | r = t -- name of source 93 | a = 1 -- lexer's position in source 94 | s = 1 -- line number 95 | tok = {} -- lexed token list* 96 | seminfo = {} -- lexed semantic information list* 97 | tokln = {} -- line numbers for messages* 98 | -- (*) externally visible thru' module 99 | -------------------------------------------------------------------- 100 | -- initial processing (shbang handling) 101 | -------------------------------------------------------------------- 102 | local t, n, e, i = l(e, "^(#[^\r\n]*)(\r?\n?)") 103 | if t then -- skip first line 104 | a = a + #e 105 | o("TK_COMMENT", e) 106 | if #i > 0 then d(a, true) end 107 | end 108 | end 109 | 110 | ---------------------------------------------------------------------- 111 | -- returns a chunk name or id, no truncation for long names 112 | ---------------------------------------------------------------------- 113 | 114 | function chunkid() 115 | if r and m(r, "^[=@]") then 116 | return n(r, 2) -- remove first char 117 | end 118 | return "[string]" 119 | end 120 | 121 | ---------------------------------------------------------------------- 122 | -- formats error message and throws error 123 | -- * a simplified version, does not report what token was responsible 124 | ---------------------------------------------------------------------- 125 | 126 | function errorline(e, a) 127 | local t = error or c.error 128 | t(h.format("%s:%d: %s", chunkid(), a or s, e)) 129 | end 130 | local r = errorline 131 | 132 | ------------------------------------------------------------------------ 133 | -- count separators ("=") in a long string delimiter 134 | ------------------------------------------------------------------------ 135 | 136 | local function u(t) 137 | local i = n 138 | local n = i(e, t, t) 139 | t = t + 1 140 | local o = #m(e, "=*", t) -- note, take the length 141 | t = t + o 142 | a = t 143 | return (i(e, t, t) == n) and o or (-o) - 1 144 | end 145 | 146 | ---------------------------------------------------------------------- 147 | -- reads a long string or long comment 148 | ---------------------------------------------------------------------- 149 | 150 | local function f(h, s) 151 | local t = a + 1 -- skip 2nd '[' 152 | local n = n 153 | local o = n(e, t, t) 154 | if o == "\r" or o == "\n" then -- string starts with a newline? 155 | t = d(t) -- skip it 156 | end 157 | local o = t 158 | while true do 159 | local o, c, l = l(e, "([\r\n%]])", t) -- (long range) 160 | if not o then 161 | r(h and "unfinished long string" or 162 | "unfinished long comment") 163 | end 164 | t = o 165 | if l == "]" then -- delimiter test 166 | if u(t) == s then 167 | i = n(e, i, a) 168 | a = a + 1 -- skip 2nd ']' 169 | return i 170 | end 171 | t = a 172 | else -- newline 173 | i = i.."\n" 174 | t = d(t) 175 | end 176 | end--while 177 | end 178 | 179 | ---------------------------------------------------------------------- 180 | -- reads a string 181 | ---------------------------------------------------------------------- 182 | 183 | local function y(u) 184 | local t = a 185 | local s = l 186 | local h = n 187 | while true do 188 | local n, l, o = s(e, "([\n\r\\\"\'])", t) -- (long range) 189 | if n then 190 | if o == "\n" or o == "\r" then 191 | r("unfinished string") 192 | end 193 | t = n 194 | if o == "\\" then -- handle escapes 195 | t = t + 1 196 | o = h(e, t, t) 197 | if o == "" then break end -- (EOZ error) 198 | n = s("abfnrtv\n\r", o, 1, true) 199 | ------------------------------------------------------ 200 | if n then -- special escapes 201 | if n > 7 then 202 | t = d(t) 203 | else 204 | t = t + 1 205 | end 206 | ------------------------------------------------------ 207 | elseif s(o, "%D") then -- other non-digits 208 | t = t + 1 209 | ------------------------------------------------------ 210 | else -- \xxx sequence 211 | local o, e, a = s(e, "^(%d%d?%d?)", t) 212 | t = e + 1 213 | if a + 1 > 256 then -- UCHAR_MAX 214 | r("escape sequence too large") 215 | end 216 | ------------------------------------------------------ 217 | end--if p 218 | else 219 | t = t + 1 220 | if o == u then -- ending delimiter 221 | a = t 222 | return h(e, i, t - 1) -- return string 223 | end 224 | end--if r 225 | else 226 | break -- (error) 227 | end--if p 228 | end--while 229 | r("unfinished string") 230 | end 231 | 232 | ------------------------------------------------------------------------ 233 | -- main lexer function 234 | ------------------------------------------------------------------------ 235 | 236 | function llex() 237 | local s = l 238 | local l = m 239 | while true do--outer 240 | local t = a 241 | -- inner loop allows break to be used to nicely section tests 242 | while true do--inner 243 | ---------------------------------------------------------------- 244 | local m, p, h = s(e, "^([_%a][_%w]*)", t) 245 | if m then 246 | a = t + #h 247 | if w[h] then 248 | o("TK_KEYWORD", h) -- reserved word (keyword) 249 | else 250 | o("TK_NAME", h) -- identifier 251 | end 252 | break -- (continue) 253 | end 254 | ---------------------------------------------------------------- 255 | local h, w, m = s(e, "^(%.?)%d", t) 256 | if h then -- numeral 257 | if m == "." then t = t + 1 end 258 | local u, d, i = s(e, "^%d*[%.%d]*([eE]?)", t) 259 | t = d + 1 260 | if #i == 1 then -- optional exponent 261 | if l(e, "^[%+%-]", t) then -- optional sign 262 | t = t + 1 263 | end 264 | end 265 | local i, t = s(e, "^[_%w]*", t) 266 | a = t + 1 267 | local e = n(e, h, t) -- string equivalent 268 | if not c.tonumber(e) then -- handles hex test also 269 | r("malformed number") 270 | end 271 | o("TK_NUMBER", e) 272 | break -- (continue) 273 | end 274 | ---------------------------------------------------------------- 275 | local m, c, w, h = s(e, "^((%s)[ \t\v\f]*)", t) 276 | if m then 277 | if h == "\n" or h == "\r" then -- newline 278 | d(t, true) 279 | else 280 | a = c + 1 -- whitespace 281 | o("TK_SPACE", w) 282 | end 283 | break -- (continue) 284 | end 285 | ---------------------------------------------------------------- 286 | local h = l(e, "^%p", t) 287 | if h then 288 | i = t 289 | local d = s("-[\"\'.=<>~", h, 1, true) 290 | if d then 291 | -- two-level if block for punctuation/symbols 292 | -------------------------------------------------------- 293 | if d <= 2 then 294 | if d == 1 then -- minus 295 | local r = l(e, "^%-%-(%[?)", t) 296 | if r then 297 | t = t + 2 298 | local h = -1 299 | if r == "[" then 300 | h = u(t) 301 | end 302 | if h >= 0 then -- long comment 303 | o("TK_LCOMMENT", f(false, h)) 304 | else -- short comment 305 | a = s(e, "[\n\r]", t) or (#e + 1) 306 | o("TK_COMMENT", n(e, i, a - 1)) 307 | end 308 | break -- (continue) 309 | end 310 | -- (fall through for "-") 311 | else -- [ or long string 312 | local e = u(t) 313 | if e >= 0 then 314 | o("TK_LSTRING", f(true, e)) 315 | elseif e == -1 then 316 | o("TK_OP", "[") 317 | else 318 | r("invalid long string delimiter") 319 | end 320 | break -- (continue) 321 | end 322 | -------------------------------------------------------- 323 | elseif d <= 5 then 324 | if d < 5 then -- strings 325 | a = t + 1 326 | o("TK_STRING", y(h)) 327 | break -- (continue) 328 | end 329 | h = l(e, "^%.%.?%.?", t) -- .|..|... dots 330 | -- (fall through) 331 | -------------------------------------------------------- 332 | else -- relational 333 | h = l(e, "^%p=?", t) 334 | -- (fall through) 335 | end 336 | end 337 | a = t + #h 338 | o("TK_OP", h) -- for other symbols, fall through 339 | break -- (continue) 340 | end 341 | ---------------------------------------------------------------- 342 | local e = n(e, t, t) 343 | if e ~= "" then 344 | a = t + 1 345 | o("TK_OP", e) -- other single-char tokens 346 | break 347 | end 348 | o("TK_EOS", "") -- end of stream, 349 | return -- exit here 350 | ---------------------------------------------------------------- 351 | end--while inner 352 | end--while outer 353 | end 354 | 355 | return c.getfenv() 356 | -------------------------------------------------------------------------------- /sample/llex_emptylines.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | llex.lua: Lua 5.1 lexical analyzer in Lua 4 | This file is part of LuaSrcDiet, based on Yueliang material. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | --[[-------------------------------------------------------------------- 14 | -- NOTES: 15 | -- * This is a version of the native 5.1.x lexer from Yueliang 0.4.0, 16 | -- with significant modifications to handle LuaSrcDiet's needs: 17 | -- (1) llex.error is an optional error function handler 18 | -- (2) seminfo for strings include their delimiters and no 19 | -- translation operations are performed on them 20 | -- * ADDED shbang handling has been added to support executable scripts 21 | -- * NO localized decimal point replacement magic 22 | -- * NO limit to number of lines 23 | -- * NO support for compatible long strings (LUA_COMPAT_LSTR) 24 | -- * Please read technotes.txt for more technical details. 25 | ----------------------------------------------------------------------]] 26 | local base = _G 27 | local string = require "string" 28 | module "llex" 29 | local find = string.find 30 | local match = string.match 31 | local sub = string.sub 32 | ---------------------------------------------------------------------- 33 | -- initialize keyword list, variables 34 | ---------------------------------------------------------------------- 35 | local kw = {} 36 | for v in string.gmatch([[ 37 | and break do else elseif end false for function if in 38 | local nil not or repeat return then true until while]], "%S+") do 39 | kw[v] = true 40 | end 41 | -- NOTE: see init() for module variables (externally visible): 42 | -- tok, seminfo, tokln 43 | local z, -- source stream 44 | sourceid, -- name of source 45 | I, -- position of lexer 46 | buff, -- buffer for strings 47 | ln -- line number 48 | ---------------------------------------------------------------------- 49 | -- add information to token listing 50 | ---------------------------------------------------------------------- 51 | local function addtoken(token, info) 52 | local i = #tok + 1 53 | tok[i] = token 54 | seminfo[i] = info 55 | tokln[i] = ln 56 | end 57 | ---------------------------------------------------------------------- 58 | -- handles line number incrementation and end-of-line characters 59 | ---------------------------------------------------------------------- 60 | local function inclinenumber(i, is_tok) 61 | local sub = sub 62 | local old = sub(z, i, i) 63 | i = i + 1 -- skip '\n' or '\r' 64 | local c = sub(z, i, i) 65 | if (c == "\n" or c == "\r") and (c ~= old) then 66 | i = i + 1 -- skip '\n\r' or '\r\n' 67 | old = old..c 68 | end 69 | if is_tok then addtoken("TK_EOL", old) end 70 | ln = ln + 1 71 | I = i 72 | return i 73 | end 74 | ---------------------------------------------------------------------- 75 | -- initialize lexer for given source _z and source name _sourceid 76 | ---------------------------------------------------------------------- 77 | function init(_z, _sourceid) 78 | z = _z -- source 79 | sourceid = _sourceid -- name of source 80 | I = 1 -- lexer's position in source 81 | ln = 1 -- line number 82 | tok = {} -- lexed token list* 83 | seminfo = {} -- lexed semantic information list* 84 | tokln = {} -- line numbers for messages* 85 | -- (*) externally visible thru' module 86 | -------------------------------------------------------------------- 87 | -- initial processing (shbang handling) 88 | -------------------------------------------------------------------- 89 | local p, _, q, r = find(z, "^(#[^\r\n]*)(\r?\n?)") 90 | if p then -- skip first line 91 | I = I + #q 92 | addtoken("TK_COMMENT", q) 93 | if #r > 0 then inclinenumber(I, true) end 94 | end 95 | end 96 | ---------------------------------------------------------------------- 97 | -- returns a chunk name or id, no truncation for long names 98 | ---------------------------------------------------------------------- 99 | function chunkid() 100 | if sourceid and match(sourceid, "^[=@]") then 101 | return sub(sourceid, 2) -- remove first char 102 | end 103 | return "[string]" 104 | end 105 | ---------------------------------------------------------------------- 106 | -- formats error message and throws error 107 | -- * a simplified version, does not report what token was responsible 108 | ---------------------------------------------------------------------- 109 | function errorline(s, line) 110 | local e = error or base.error 111 | e(string.format("%s:%d: %s", chunkid(), line or ln, s)) 112 | end 113 | local errorline = errorline 114 | ------------------------------------------------------------------------ 115 | -- count separators ("=") in a long string delimiter 116 | ------------------------------------------------------------------------ 117 | local function skip_sep(i) 118 | local sub = sub 119 | local s = sub(z, i, i) 120 | i = i + 1 121 | local count = #match(z, "=*", i) -- note, take the length 122 | i = i + count 123 | I = i 124 | return (sub(z, i, i) == s) and count or (-count) - 1 125 | end 126 | ---------------------------------------------------------------------- 127 | -- reads a long string or long comment 128 | ---------------------------------------------------------------------- 129 | local function read_long_string(is_str, sep) 130 | local i = I + 1 -- skip 2nd '[' 131 | local sub = sub 132 | local c = sub(z, i, i) 133 | if c == "\r" or c == "\n" then -- string starts with a newline? 134 | i = inclinenumber(i) -- skip it 135 | end 136 | local j = i 137 | while true do 138 | local p, q, r = find(z, "([\r\n%]])", i) -- (long range) 139 | if not p then 140 | errorline(is_str and "unfinished long string" or 141 | "unfinished long comment") 142 | end 143 | i = p 144 | if r == "]" then -- delimiter test 145 | if skip_sep(i) == sep then 146 | buff = sub(z, buff, I) 147 | I = I + 1 -- skip 2nd ']' 148 | return buff 149 | end 150 | i = I 151 | else -- newline 152 | buff = buff.."\n" 153 | i = inclinenumber(i) 154 | end 155 | end--while 156 | end 157 | ---------------------------------------------------------------------- 158 | -- reads a string 159 | ---------------------------------------------------------------------- 160 | local function read_string(del) 161 | local i = I 162 | local find = find 163 | local sub = sub 164 | while true do 165 | local p, q, r = find(z, "([\n\r\\\"\'])", i) -- (long range) 166 | if p then 167 | if r == "\n" or r == "\r" then 168 | errorline("unfinished string") 169 | end 170 | i = p 171 | if r == "\\" then -- handle escapes 172 | i = i + 1 173 | r = sub(z, i, i) 174 | if r == "" then break end -- (EOZ error) 175 | p = find("abfnrtv\n\r", r, 1, true) 176 | ------------------------------------------------------ 177 | if p then -- special escapes 178 | if p > 7 then 179 | i = inclinenumber(i) 180 | else 181 | i = i + 1 182 | end 183 | ------------------------------------------------------ 184 | elseif find(r, "%D") then -- other non-digits 185 | i = i + 1 186 | ------------------------------------------------------ 187 | else -- \xxx sequence 188 | local p, q, s = find(z, "^(%d%d?%d?)", i) 189 | i = q + 1 190 | if s + 1 > 256 then -- UCHAR_MAX 191 | errorline("escape sequence too large") 192 | end 193 | ------------------------------------------------------ 194 | end--if p 195 | else 196 | i = i + 1 197 | if r == del then -- ending delimiter 198 | I = i 199 | return sub(z, buff, i - 1) -- return string 200 | end 201 | end--if r 202 | else 203 | break -- (error) 204 | end--if p 205 | end--while 206 | errorline("unfinished string") 207 | end 208 | ------------------------------------------------------------------------ 209 | -- main lexer function 210 | ------------------------------------------------------------------------ 211 | function llex() 212 | local find = find 213 | local match = match 214 | while true do--outer 215 | local i = I 216 | -- inner loop allows break to be used to nicely section tests 217 | while true do--inner 218 | ---------------------------------------------------------------- 219 | local p, _, r = find(z, "^([_%a][_%w]*)", i) 220 | if p then 221 | I = i + #r 222 | if kw[r] then 223 | addtoken("TK_KEYWORD", r) -- reserved word (keyword) 224 | else 225 | addtoken("TK_NAME", r) -- identifier 226 | end 227 | break -- (continue) 228 | end 229 | ---------------------------------------------------------------- 230 | local p, _, r = find(z, "^(%.?)%d", i) 231 | if p then -- numeral 232 | if r == "." then i = i + 1 end 233 | local _, q, r = find(z, "^%d*[%.%d]*([eE]?)", i) 234 | i = q + 1 235 | if #r == 1 then -- optional exponent 236 | if match(z, "^[%+%-]", i) then -- optional sign 237 | i = i + 1 238 | end 239 | end 240 | local _, q = find(z, "^[_%w]*", i) 241 | I = q + 1 242 | local v = sub(z, p, q) -- string equivalent 243 | if not base.tonumber(v) then -- handles hex test also 244 | errorline("malformed number") 245 | end 246 | addtoken("TK_NUMBER", v) 247 | break -- (continue) 248 | end 249 | ---------------------------------------------------------------- 250 | local p, q, r, t = find(z, "^((%s)[ \t\v\f]*)", i) 251 | if p then 252 | if t == "\n" or t == "\r" then -- newline 253 | inclinenumber(i, true) 254 | else 255 | I = q + 1 -- whitespace 256 | addtoken("TK_SPACE", r) 257 | end 258 | break -- (continue) 259 | end 260 | ---------------------------------------------------------------- 261 | local r = match(z, "^%p", i) 262 | if r then 263 | buff = i 264 | local p = find("-[\"\'.=<>~", r, 1, true) 265 | if p then 266 | -- two-level if block for punctuation/symbols 267 | -------------------------------------------------------- 268 | if p <= 2 then 269 | if p == 1 then -- minus 270 | local c = match(z, "^%-%-(%[?)", i) 271 | if c then 272 | i = i + 2 273 | local sep = -1 274 | if c == "[" then 275 | sep = skip_sep(i) 276 | end 277 | if sep >= 0 then -- long comment 278 | addtoken("TK_LCOMMENT", read_long_string(false, sep)) 279 | else -- short comment 280 | I = find(z, "[\n\r]", i) or (#z + 1) 281 | addtoken("TK_COMMENT", sub(z, buff, I - 1)) 282 | end 283 | break -- (continue) 284 | end 285 | -- (fall through for "-") 286 | else -- [ or long string 287 | local sep = skip_sep(i) 288 | if sep >= 0 then 289 | addtoken("TK_LSTRING", read_long_string(true, sep)) 290 | elseif sep == -1 then 291 | addtoken("TK_OP", "[") 292 | else 293 | errorline("invalid long string delimiter") 294 | end 295 | break -- (continue) 296 | end 297 | -------------------------------------------------------- 298 | elseif p <= 5 then 299 | if p < 5 then -- strings 300 | I = i + 1 301 | addtoken("TK_STRING", read_string(r)) 302 | break -- (continue) 303 | end 304 | r = match(z, "^%.%.?%.?", i) -- .|..|... dots 305 | -- (fall through) 306 | -------------------------------------------------------- 307 | else -- relational 308 | r = match(z, "^%p=?", i) 309 | -- (fall through) 310 | end 311 | end 312 | I = i + #r 313 | addtoken("TK_OP", r) -- for other symbols, fall through 314 | break -- (continue) 315 | end 316 | ---------------------------------------------------------------- 317 | local r = sub(z, i, i) 318 | if r ~= "" then 319 | I = i + 1 320 | addtoken("TK_OP", r) -- other single-char tokens 321 | break 322 | end 323 | addtoken("TK_EOS", "") -- end of stream, 324 | return -- exit here 325 | ---------------------------------------------------------------- 326 | end--while inner 327 | end--while outer 328 | end 329 | return base.getfenv() 330 | -------------------------------------------------------------------------------- /llex.lua: -------------------------------------------------------------------------------- 1 | --[[-------------------------------------------------------------------- 2 | 3 | llex.lua: Lua 5.1 lexical analyzer in Lua 4 | This file is part of LuaSrcDiet, based on Yueliang material. 5 | 6 | Copyright (c) 2008 Kein-Hong Man 7 | The COPYRIGHT file describes the conditions 8 | under which this software may be distributed. 9 | 10 | See the ChangeLog for more information. 11 | 12 | ----------------------------------------------------------------------]] 13 | 14 | --[[-------------------------------------------------------------------- 15 | -- NOTES: 16 | -- * This is a version of the native 5.1.x lexer from Yueliang 0.4.0, 17 | -- with significant modifications to handle LuaSrcDiet's needs: 18 | -- (1) llex.error is an optional error function handler 19 | -- (2) seminfo for strings include their delimiters and no 20 | -- translation operations are performed on them 21 | -- * ADDED shbang handling has been added to support executable scripts 22 | -- * NO localized decimal point replacement magic 23 | -- * NO limit to number of lines 24 | -- * NO support for compatible long strings (LUA_COMPAT_LSTR) 25 | -- * Please read technotes.txt for more technical details. 26 | ----------------------------------------------------------------------]] 27 | 28 | local base = _G 29 | local string = require "string" 30 | module "llex" 31 | 32 | local find = string.find 33 | local match = string.match 34 | local sub = string.sub 35 | 36 | ---------------------------------------------------------------------- 37 | -- initialize keyword list, variables 38 | ---------------------------------------------------------------------- 39 | 40 | local kw = {} 41 | for v in string.gmatch([[ 42 | and break do else elseif end false for function if in 43 | local nil not or repeat return then true until while]], "%S+") do 44 | kw[v] = true 45 | end 46 | 47 | -- NOTE: see init() for module variables (externally visible): 48 | -- tok, seminfo, tokln 49 | 50 | local z, -- source stream 51 | sourceid, -- name of source 52 | I, -- position of lexer 53 | buff, -- buffer for strings 54 | ln -- line number 55 | 56 | ---------------------------------------------------------------------- 57 | -- add information to token listing 58 | ---------------------------------------------------------------------- 59 | 60 | local function addtoken(token, info) 61 | local i = #tok + 1 62 | tok[i] = token 63 | seminfo[i] = info 64 | tokln[i] = ln 65 | end 66 | 67 | ---------------------------------------------------------------------- 68 | -- handles line number incrementation and end-of-line characters 69 | ---------------------------------------------------------------------- 70 | 71 | local function inclinenumber(i, is_tok) 72 | local sub = sub 73 | local old = sub(z, i, i) 74 | i = i + 1 -- skip '\n' or '\r' 75 | local c = sub(z, i, i) 76 | if (c == "\n" or c == "\r") and (c ~= old) then 77 | i = i + 1 -- skip '\n\r' or '\r\n' 78 | old = old..c 79 | end 80 | if is_tok then addtoken("TK_EOL", old) end 81 | ln = ln + 1 82 | I = i 83 | return i 84 | end 85 | 86 | ---------------------------------------------------------------------- 87 | -- initialize lexer for given source _z and source name _sourceid 88 | ---------------------------------------------------------------------- 89 | 90 | function init(_z, _sourceid) 91 | z = _z -- source 92 | sourceid = _sourceid -- name of source 93 | I = 1 -- lexer's position in source 94 | ln = 1 -- line number 95 | tok = {} -- lexed token list* 96 | seminfo = {} -- lexed semantic information list* 97 | tokln = {} -- line numbers for messages* 98 | -- (*) externally visible thru' module 99 | -------------------------------------------------------------------- 100 | -- initial processing (shbang handling) 101 | -------------------------------------------------------------------- 102 | local p, _, q, r = find(z, "^(#[^\r\n]*)(\r?\n?)") 103 | if p then -- skip first line 104 | I = I + #q 105 | addtoken("TK_COMMENT", q) 106 | if #r > 0 then inclinenumber(I, true) end 107 | end 108 | end 109 | 110 | ---------------------------------------------------------------------- 111 | -- returns a chunk name or id, no truncation for long names 112 | ---------------------------------------------------------------------- 113 | 114 | function chunkid() 115 | if sourceid and match(sourceid, "^[=@]") then 116 | return sub(sourceid, 2) -- remove first char 117 | end 118 | return "[string]" 119 | end 120 | 121 | ---------------------------------------------------------------------- 122 | -- formats error message and throws error 123 | -- * a simplified version, does not report what token was responsible 124 | ---------------------------------------------------------------------- 125 | 126 | function errorline(s, line) 127 | local e = error or base.error 128 | e(string.format("%s:%d: %s", chunkid(), line or ln, s)) 129 | end 130 | local errorline = errorline 131 | 132 | ------------------------------------------------------------------------ 133 | -- count separators ("=") in a long string delimiter 134 | ------------------------------------------------------------------------ 135 | 136 | local function skip_sep(i) 137 | local sub = sub 138 | local s = sub(z, i, i) 139 | i = i + 1 140 | local count = #match(z, "=*", i) -- note, take the length 141 | i = i + count 142 | I = i 143 | return (sub(z, i, i) == s) and count or (-count) - 1 144 | end 145 | 146 | ---------------------------------------------------------------------- 147 | -- reads a long string or long comment 148 | ---------------------------------------------------------------------- 149 | 150 | local function read_long_string(is_str, sep) 151 | local i = I + 1 -- skip 2nd '[' 152 | local sub = sub 153 | local c = sub(z, i, i) 154 | if c == "\r" or c == "\n" then -- string starts with a newline? 155 | i = inclinenumber(i) -- skip it 156 | end 157 | local j = i 158 | while true do 159 | local p, q, r = find(z, "([\r\n%]])", i) -- (long range) 160 | if not p then 161 | errorline(is_str and "unfinished long string" or 162 | "unfinished long comment") 163 | end 164 | i = p 165 | if r == "]" then -- delimiter test 166 | if skip_sep(i) == sep then 167 | buff = sub(z, buff, I) 168 | I = I + 1 -- skip 2nd ']' 169 | return buff 170 | end 171 | i = I 172 | else -- newline 173 | buff = buff.."\n" 174 | i = inclinenumber(i) 175 | end 176 | end--while 177 | end 178 | 179 | ---------------------------------------------------------------------- 180 | -- reads a string 181 | ---------------------------------------------------------------------- 182 | 183 | local function read_string(del) 184 | local i = I 185 | local find = find 186 | local sub = sub 187 | while true do 188 | local p, q, r = find(z, "([\n\r\\\"\'])", i) -- (long range) 189 | if p then 190 | if r == "\n" or r == "\r" then 191 | errorline("unfinished string") 192 | end 193 | i = p 194 | if r == "\\" then -- handle escapes 195 | i = i + 1 196 | r = sub(z, i, i) 197 | if r == "" then break end -- (EOZ error) 198 | p = find("abfnrtv\n\r", r, 1, true) 199 | ------------------------------------------------------ 200 | if p then -- special escapes 201 | if p > 7 then 202 | i = inclinenumber(i) 203 | else 204 | i = i + 1 205 | end 206 | ------------------------------------------------------ 207 | elseif find(r, "%D") then -- other non-digits 208 | i = i + 1 209 | ------------------------------------------------------ 210 | else -- \xxx sequence 211 | local p, q, s = find(z, "^(%d%d?%d?)", i) 212 | i = q + 1 213 | if s + 1 > 256 then -- UCHAR_MAX 214 | errorline("escape sequence too large") 215 | end 216 | ------------------------------------------------------ 217 | end--if p 218 | else 219 | i = i + 1 220 | if r == del then -- ending delimiter 221 | I = i 222 | return sub(z, buff, i - 1) -- return string 223 | end 224 | end--if r 225 | else 226 | break -- (error) 227 | end--if p 228 | end--while 229 | errorline("unfinished string") 230 | end 231 | 232 | ------------------------------------------------------------------------ 233 | -- main lexer function 234 | ------------------------------------------------------------------------ 235 | 236 | function llex() 237 | local find = find 238 | local match = match 239 | while true do--outer 240 | local i = I 241 | -- inner loop allows break to be used to nicely section tests 242 | while true do--inner 243 | ---------------------------------------------------------------- 244 | local p, _, r = find(z, "^([_%a][_%w]*)", i) 245 | if p then 246 | I = i + #r 247 | if kw[r] then 248 | addtoken("TK_KEYWORD", r) -- reserved word (keyword) 249 | else 250 | addtoken("TK_NAME", r) -- identifier 251 | end 252 | break -- (continue) 253 | end 254 | ---------------------------------------------------------------- 255 | local p, _, r = find(z, "^(%.?)%d", i) 256 | if p then -- numeral 257 | if r == "." then i = i + 1 end 258 | local _, q, r = find(z, "^%d*[%.%d]*([eE]?)", i) 259 | i = q + 1 260 | if #r == 1 then -- optional exponent 261 | if match(z, "^[%+%-]", i) then -- optional sign 262 | i = i + 1 263 | end 264 | end 265 | local _, q = find(z, "^[_%w]*", i) 266 | I = q + 1 267 | local v = sub(z, p, q) -- string equivalent 268 | if not base.tonumber(v) then -- handles hex test also 269 | errorline("malformed number") 270 | end 271 | addtoken("TK_NUMBER", v) 272 | break -- (continue) 273 | end 274 | ---------------------------------------------------------------- 275 | local p, q, r, t = find(z, "^((%s)[ \t\v\f]*)", i) 276 | if p then 277 | if t == "\n" or t == "\r" then -- newline 278 | inclinenumber(i, true) 279 | else 280 | I = q + 1 -- whitespace 281 | addtoken("TK_SPACE", r) 282 | end 283 | break -- (continue) 284 | end 285 | ---------------------------------------------------------------- 286 | local r = match(z, "^%p", i) 287 | if r then 288 | buff = i 289 | local p = find("-[\"\'.=<>~", r, 1, true) 290 | if p then 291 | -- two-level if block for punctuation/symbols 292 | -------------------------------------------------------- 293 | if p <= 2 then 294 | if p == 1 then -- minus 295 | local c = match(z, "^%-%-(%[?)", i) 296 | if c then 297 | i = i + 2 298 | local sep = -1 299 | if c == "[" then 300 | sep = skip_sep(i) 301 | end 302 | if sep >= 0 then -- long comment 303 | addtoken("TK_LCOMMENT", read_long_string(false, sep)) 304 | else -- short comment 305 | I = find(z, "[\n\r]", i) or (#z + 1) 306 | addtoken("TK_COMMENT", sub(z, buff, I - 1)) 307 | end 308 | break -- (continue) 309 | end 310 | -- (fall through for "-") 311 | else -- [ or long string 312 | local sep = skip_sep(i) 313 | if sep >= 0 then 314 | addtoken("TK_LSTRING", read_long_string(true, sep)) 315 | elseif sep == -1 then 316 | addtoken("TK_OP", "[") 317 | else 318 | errorline("invalid long string delimiter") 319 | end 320 | break -- (continue) 321 | end 322 | -------------------------------------------------------- 323 | elseif p <= 5 then 324 | if p < 5 then -- strings 325 | I = i + 1 326 | addtoken("TK_STRING", read_string(r)) 327 | break -- (continue) 328 | end 329 | r = match(z, "^%.%.?%.?", i) -- .|..|... dots 330 | -- (fall through) 331 | -------------------------------------------------------- 332 | else -- relational 333 | r = match(z, "^%p=?", i) 334 | -- (fall through) 335 | end 336 | end 337 | I = i + #r 338 | addtoken("TK_OP", r) -- for other symbols, fall through 339 | break -- (continue) 340 | end 341 | ---------------------------------------------------------------- 342 | local r = sub(z, i, i) 343 | if r ~= "" then 344 | I = i + 1 345 | addtoken("TK_OP", r) -- other single-char tokens 346 | break 347 | end 348 | addtoken("TK_EOS", "") -- end of stream, 349 | return -- exit here 350 | ---------------------------------------------------------------- 351 | end--while inner 352 | end--while outer 353 | end 354 | 355 | return base.getfenv() 356 | -------------------------------------------------------------------------------- /cmake/dist.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library. 2 | # Provides sane project defaults and macros common to LuaDist CMake builds. 3 | # 4 | # Copyright (C) 2007-2012 LuaDist. 5 | # by David Manura, Peter Drahoš 6 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 7 | # For details see the COPYRIGHT file distributed with LuaDist. 8 | # Please note that the package source code is licensed under its own license. 9 | 10 | ## Extract information from dist.info 11 | if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) 12 | message ( FATAL_ERROR 13 | "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) 14 | endif () 15 | file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) 16 | if ( "${DIST_INFO}" STREQUAL "" ) 17 | message ( FATAL_ERROR "Failed to load dist.info." ) 18 | endif () 19 | # Reads field `name` from dist.info string `DIST_INFO` into variable `var`. 20 | macro ( _parse_dist_field name var ) 21 | string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 22 | ${var} "${DIST_INFO}" ) 23 | if ( ${var} STREQUAL DIST_INFO ) 24 | message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) 25 | endif () 26 | endmacro () 27 | # 28 | _parse_dist_field ( name DIST_NAME ) 29 | _parse_dist_field ( version DIST_VERSION ) 30 | _parse_dist_field ( license DIST_LICENSE ) 31 | _parse_dist_field ( author DIST_AUTHOR ) 32 | _parse_dist_field ( maintainer DIST_MAINTAINER ) 33 | _parse_dist_field ( url DIST_URL ) 34 | _parse_dist_field ( desc DIST_DESC ) 35 | message ( "DIST_NAME: ${DIST_NAME}") 36 | message ( "DIST_VERSION: ${DIST_VERSION}") 37 | message ( "DIST_LICENSE: ${DIST_LICENSE}") 38 | message ( "DIST_AUTHOR: ${DIST_AUTHOR}") 39 | message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") 40 | message ( "DIST_URL: ${DIST_URL}") 41 | message ( "DIST_DESC: ${DIST_DESC}") 42 | string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 43 | DIST_DEPENDS ${DIST_INFO} ) 44 | if ( DIST_DEPENDS STREQUAL DIST_INFO ) 45 | set ( DIST_DEPENDS "" ) 46 | endif () 47 | message ( "DIST_DEPENDS: ${DIST_DEPENDS}") 48 | ## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add 49 | 50 | 51 | ## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) 52 | # Primary paths 53 | set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) 54 | set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) 55 | set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) 56 | set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) 57 | set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) 58 | 59 | # Secondary paths 60 | option ( INSTALL_VERSION 61 | "Install runtime libraries and executables with version information." OFF) 62 | set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH 63 | "Directory the package can store documentation, tests or other data in.") 64 | set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH 65 | "Recommended directory to install documentation into.") 66 | set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH 67 | "Recommended directory to install examples into.") 68 | set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH 69 | "Recommended directory to install tests into.") 70 | set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH 71 | "Where to install additional files") 72 | 73 | # Tweaks and other defaults 74 | # Setting CMAKE to use loose block and search for find modules in source directory 75 | set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) 76 | set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) 77 | option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) 78 | 79 | # In MSVC, prevent warnings that can occur when using standard libraries. 80 | if ( MSVC ) 81 | add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) 82 | endif () 83 | 84 | # RPath and relative linking 85 | option ( USE_RPATH "Use relative linking." ON) 86 | if ( USE_RPATH ) 87 | string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) 88 | set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) 89 | set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) 90 | set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} 91 | CACHE STRING "" FORCE ) 92 | set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) 93 | set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} 94 | CACHE STRING "" FORCE ) 95 | endif () 96 | 97 | ## MACROS 98 | # Parser macro 99 | macro ( parse_arguments prefix arg_names option_names) 100 | set ( DEFAULT_ARGS ) 101 | foreach ( arg_name ${arg_names} ) 102 | set ( ${prefix}_${arg_name} ) 103 | endforeach () 104 | foreach ( option ${option_names} ) 105 | set ( ${prefix}_${option} FALSE ) 106 | endforeach () 107 | 108 | set ( current_arg_name DEFAULT_ARGS ) 109 | set ( current_arg_list ) 110 | foreach ( arg ${ARGN} ) 111 | set ( larg_names ${arg_names} ) 112 | list ( FIND larg_names "${arg}" is_arg_name ) 113 | if ( is_arg_name GREATER -1 ) 114 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 115 | set ( current_arg_name ${arg} ) 116 | set ( current_arg_list ) 117 | else () 118 | set ( loption_names ${option_names} ) 119 | list ( FIND loption_names "${arg}" is_option ) 120 | if ( is_option GREATER -1 ) 121 | set ( ${prefix}_${arg} TRUE ) 122 | else () 123 | set ( current_arg_list ${current_arg_list} ${arg} ) 124 | endif () 125 | endif () 126 | endforeach () 127 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 128 | endmacro () 129 | 130 | 131 | # install_executable ( executable_targets ) 132 | # Installs any executables generated using "add_executable". 133 | # USE: install_executable ( lua ) 134 | # NOTE: subdirectories are NOT supported 135 | set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) 136 | set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION 137 | "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) 138 | macro ( install_executable ) 139 | foreach ( _file ${ARGN} ) 140 | if ( INSTALL_VERSION ) 141 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 142 | SOVERSION ${DIST_VERSION} ) 143 | endif () 144 | install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} 145 | COMPONENT Runtime ) 146 | endforeach() 147 | endmacro () 148 | 149 | # install_library ( library_targets ) 150 | # Installs any libraries generated using "add_library" into apropriate places. 151 | # USE: install_library ( libexpat ) 152 | # NOTE: subdirectories are NOT supported 153 | set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) 154 | set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION 155 | "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) 156 | macro ( install_library ) 157 | foreach ( _file ${ARGN} ) 158 | if ( INSTALL_VERSION ) 159 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 160 | SOVERSION ${DIST_VERSION} ) 161 | endif () 162 | install ( TARGETS ${_file} 163 | RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime 164 | LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime 165 | ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) 166 | endforeach() 167 | endmacro () 168 | 169 | # helper function for various install_* functions, for PATTERN/REGEX args. 170 | macro ( _complete_install_args ) 171 | if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) 172 | set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) 173 | endif () 174 | if ( NOT("${_ARG_REGEX}" STREQUAL "") ) 175 | set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) 176 | endif () 177 | endmacro () 178 | 179 | # install_header ( files/directories [INTO destination] ) 180 | # Install a directories or files into header destination. 181 | # USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) 182 | # USE: install_header ( mylib.h INTO mylib ) 183 | # For directories, supports optional PATTERN/REGEX arguments like install(). 184 | set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) 185 | set ( CPACK_COMPONENT_HEADER_DESCRIPTION 186 | "Headers needed for development. Installed into ${INSTALL_INC}." ) 187 | macro ( install_header ) 188 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 189 | _complete_install_args() 190 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 191 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 192 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 193 | COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) 194 | else () 195 | install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 196 | COMPONENT Header ) 197 | endif () 198 | endforeach() 199 | endmacro () 200 | 201 | # install_data ( files/directories [INTO destination] ) 202 | # This installs additional data files or directories. 203 | # USE: install_data ( extra data.dat ) 204 | # USE: install_data ( image1.png image2.png INTO images ) 205 | # For directories, supports optional PATTERN/REGEX arguments like install(). 206 | set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) 207 | set ( CPACK_COMPONENT_DATA_DESCRIPTION 208 | "Application data. Installed into ${INSTALL_DATA}." ) 209 | macro ( install_data ) 210 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 211 | _complete_install_args() 212 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 213 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 214 | install ( DIRECTORY ${_file} 215 | DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 216 | COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) 217 | else () 218 | install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 219 | COMPONENT Data ) 220 | endif () 221 | endforeach() 222 | endmacro () 223 | 224 | # INSTALL_DOC ( files/directories [INTO destination] ) 225 | # This installs documentation content 226 | # USE: install_doc ( doc/ doc.pdf ) 227 | # USE: install_doc ( index.html INTO html ) 228 | # For directories, supports optional PATTERN/REGEX arguments like install(). 229 | set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) 230 | set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION 231 | "Application documentation. Installed into ${INSTALL_DOC}." ) 232 | macro ( install_doc ) 233 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 234 | _complete_install_args() 235 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 236 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 237 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 238 | COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) 239 | else () 240 | install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 241 | COMPONENT Documentation ) 242 | endif () 243 | endforeach() 244 | endmacro () 245 | 246 | # install_example ( files/directories [INTO destination] ) 247 | # This installs additional examples 248 | # USE: install_example ( examples/ exampleA ) 249 | # USE: install_example ( super_example super_data INTO super) 250 | # For directories, supports optional PATTERN/REGEX argument like install(). 251 | set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) 252 | set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION 253 | "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) 254 | macro ( install_example ) 255 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 256 | _complete_install_args() 257 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 258 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 259 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 260 | COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) 261 | else () 262 | install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 263 | COMPONENT Example ) 264 | endif () 265 | endforeach() 266 | endmacro () 267 | 268 | # install_test ( files/directories [INTO destination] ) 269 | # This installs tests and test files, DOES NOT EXECUTE TESTS 270 | # USE: install_test ( my_test data.sql ) 271 | # USE: install_test ( feature_x_test INTO x ) 272 | # For directories, supports optional PATTERN/REGEX argument like install(). 273 | set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) 274 | set ( CPACK_COMPONENT_TEST_DESCRIPTION 275 | "Tests and associated data. Installed into ${INSTALL_TEST}." ) 276 | macro ( install_test ) 277 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 278 | _complete_install_args() 279 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 280 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 281 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 282 | COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) 283 | else () 284 | install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 285 | COMPONENT Test ) 286 | endif () 287 | endforeach() 288 | endmacro () 289 | 290 | # install_foo ( files/directories [INTO destination] ) 291 | # This installs optional or otherwise unneeded content 292 | # USE: install_foo ( etc/ example.doc ) 293 | # USE: install_foo ( icon.png logo.png INTO icons) 294 | # For directories, supports optional PATTERN/REGEX argument like install(). 295 | set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) 296 | set ( CPACK_COMPONENT_OTHER_DESCRIPTION 297 | "Other unspecified content. Installed into ${INSTALL_FOO}." ) 298 | macro ( install_foo ) 299 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 300 | _complete_install_args() 301 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 302 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 303 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 304 | COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) 305 | else () 306 | install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 307 | COMPONENT Other ) 308 | endif () 309 | endforeach() 310 | endmacro () 311 | 312 | ## CTest defaults 313 | 314 | ## CPack defaults 315 | set ( CPACK_GENERATOR "ZIP" ) 316 | set ( CPACK_STRIP_FILES TRUE ) 317 | set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) 318 | set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") 319 | set ( CPACK_PACKAGE_VENDOR "LuaDist" ) 320 | set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) 321 | include ( CPack ) 322 | -------------------------------------------------------------------------------- /Changelog: -------------------------------------------------------------------------------- 1 | LuaSrcDiet changelog 2 | -------------------- 3 | 4 | *** Remember to update version and date information in LuaSrcDiet.lua *** 5 | 6 | 2008-06-08 Kein-Hong Man 7 | 8 | * optparser.lua: improved local variable collision discrimination 9 | * technotes.txt: updated notes on local variable collision tests 10 | * optparser.lua: changed a collision test to be more conservative 11 | * Manifest: updated 12 | * README: updated 13 | * Version: 0.11.2 14 | 15 | 2008-06-07 Kein-Hong Man 16 | 17 | * plugin/sloc.lua, plugin/html.lua: added skeletons for two 18 | planned plugins 19 | * LuaSrcDiet.lua: added early exit options for plugins 20 | * plugin/example.lua: added early exit for plugin, filenames 21 | * plugin/sloc.lua: implemented SLOC plugin 22 | * plugin/html.lua: implemented HTML plugin 23 | * numbers_original.lua, numbers_on_diet.lua: fixed missing commas 24 | * LuaSrcDiet.lua: fixed early exit handling for multiple files 25 | * sample/Makefile: added generator entry for HTML plugin 26 | * sample/html_sample.html: added HTML plugin sample (html.lua) 27 | * plugin/example.lua: updated comments 28 | * technotes.txt: added note on maximum local identifiers needed 29 | * optparser.lua: minor formatting tweaks 30 | 31 | 2008-06-06 Kein-Hong Man 32 | 33 | * plugin/: created directory for plugins 34 | * plugin/example.lua: created example plugin with specified calls 35 | * LuaSrcDiet.lua: added --plugin option with plugin handling code 36 | * LuaSrcDiet.lua: tweaked usage text 37 | 38 | 2008-06-03 Kein-Hong Man 39 | 40 | * LuaSrcDiet.lua: added --opt-entropy option handling 41 | * sample/Makefile: improved with an explanation list 42 | * optparser.lua: added implementation for --opt-entropy 43 | * Manifest: updated 44 | * README: updated 45 | * Version: 0.11.1 46 | 47 | 2008-06-03 Kein-Hong Man 48 | 49 | * technotes.txt: added notes on local variable rename algorithm 50 | * optparser.lua: bug fix, avoid keywords when generating names 51 | * test/test_benchmark1.lua: fixed missing die() to error() 52 | * test/test_benchmark1.lua: added verification of scripts after 53 | first loading them using loadstring() 54 | * LuaSrcDiet.lua, optlex.lua: --detail implementation for 55 | strings and numbers, extra info display 56 | * optlex.lua (do_number): fixed --detail handling where the 57 | converted number is no different 58 | * optparser.lua: updated final local renaming handling 59 | * optparser.lua: added --details statistics implementation for 60 | local variable renaming 61 | * sample/Makefile: added --details to standard 'all' build 62 | * LuaSrcDiet.lua, llex.lua, lparser.lua, optlex.lua: fixed some 63 | inadvertent or forgotten or unnecessary global variable accesses 64 | 65 | 2008-05-31 Kein-Hong Man 66 | 67 | * test/test_benchmark1.lua: coded simple test for loader 68 | performance 69 | * test/LuaSrcDiet_fixed.lua, test/LuaSrcDiet_fixed_.lua: files 70 | without shbang first line to satisfy loadstring() 71 | 72 | 2008-05-29 Kein-Hong Man 73 | 74 | * Manifest: updated 75 | * README: updated 76 | * lparser.lua: added isself flag to handle "self" specially 77 | * LuaSrcDiet.lua (dump_parser): added display for 'isself' 78 | * optparser.lua (optimize): added support for preserving implicit 79 | "self" parameter, updated local renaming loop 80 | * Version: 0.11.0 81 | * README: updated 82 | 83 | 2008-05-29 Kein-Hong Man 84 | 85 | * optparser.lua (stats_summary): improved with output stats, clean up 86 | * LuaSrcDiet.lua (process_file): clean up stats display 87 | * LuaSrcDiet.lua (process_file): mistake in assigning optional print, 88 | mistakenly assigned to lparser.print instead of optparser.print 89 | * sample/Makefile: added entries for dumping --dump-* samples 90 | * sample/Makefile: added entries for different optimization options 91 | 92 | 2008-05-29 Kein-Hong Man 93 | 94 | * optparser.lua (optimize): finished coding local variable 95 | optimizer, to test now 96 | * optparser.lua (optimize): syntax error, used 'then' instead of 'do' 97 | * lparser.lua: bug in binopr_*, missing "%" operator, Yueliang bug 98 | * lparser.lua: added nameref, to track local variable declaration's 99 | position properly 100 | * lparser.lua (searchvar): bug, forgot to fix a "return 1" to return 101 | a proper useful id 102 | * optparser.lua (optimize): fix obj/object handling, no need to 103 | compare against nil 104 | * optparser.lua (optimize): rewrite local-local collision loop using 105 | a variable scanleft to track objects left to process 106 | * optparser.lua (optimize): objects assigned to mark properly with 107 | skip and done 108 | * lparser.lua (adjustlocalvars): change 'rem' assignment if local 109 | variables are overlapping 110 | * optparser.lua (optimize): added handling for 'rem' extension if 111 | it is negative 112 | * sample/Makefile: updated, plus second-generation test, auto-diffed 113 | * optparser.lua: local variable optimization seems to work 114 | 115 | 2008-05-28 Kein-Hong Man 116 | 117 | * optparser.lua: added designs for stats tables 118 | * optparser.lua (debug_dump_info): beautify variable names 119 | * optparser.lua: added draft of statistics dump code 120 | * optparser.lua (optimize): added option as parm for future 121 | * optparser.lua (debug_dump_info): removed along with associated 122 | stuff, moved to main program as a dump option 123 | * LuaSrcDiet.lua: change name of --dump to --dump-lexer, added 124 | --dump-parser 125 | * LuaSrcDiet.lua (dump_parser): adapted from debug_dump_info 126 | * optparser.lua: rearranged some code 127 | * optparser.lua: fixed LETTERS, upper-case is valid also 128 | * optparser.lua (new_var_name): implemented variable name allocator 129 | * optparser.lua (preprocess): added preprocess to find first and 130 | last accesses of locals 131 | 132 | 2008-05-28 Kein-Hong Man 133 | 134 | * lparser.lua (removevars): adapted from original parser, 135 | needed for proper local variable activation/deactivation tracking 136 | * optparser.lua: added some debugging display code 137 | * lparser.lua (forlist): bug, nvar set to 0 but should be 1, 138 | mistake in copy-and-paste 139 | * lparser.lua: global/local tables seems okay 140 | * lparser.lua (adjustlocalvars): adjusted activation order 141 | * LuaSrcDiet.lua: added --keep option to leave license or 142 | copyright texts alone 143 | 144 | 2008-05-28 Kein-Hong Man 145 | 146 | * technotes.txt: added a list of possible optimizations 147 | * optparser.lua: updated constant strings to handle name entropy 148 | * LuaSrcDiet.lua: added --none option for zero optimizations 149 | * LuaSrcDiet.lua: added --details option (flag only) for display 150 | of extra or useful optimization output information 151 | 152 | 2008-05-28 Kein-Hong Man 153 | 154 | * LuaSrcDiet.lua: enabled code for --opt-locals 155 | * LuaSrcDiet.lua: added code to call parser, parser optimizer 156 | * lparser.lua: added tables for deferred local variable activation 157 | * lparser.lua (adjustlocalvars): adapted from original parser, 158 | needed for deferred local variable activation, updated various 159 | functions that uses it as well 160 | * lparser.lua (init): off by 1 error for j index, 0 should be 1 161 | * lparser.lua: bug in unopr, missing "#" lookup, Yueliang bug 162 | * optparser.lua (optimize): added debug code 163 | * lparser.lua: working better 164 | 165 | 2008-05-28 Kein-Hong Man 166 | 167 | * lparser.lua: fitted with new token retrieval scheme using tables 168 | * llex.lua: simplified locals declaration 169 | * sample/Makefile: added lparser.lua and optparser.lua for testing 170 | * lparser.lua: restored some earlier line numbering code 171 | * lparser.lua (init): rewrote token retrieval properly to take into 172 | consideration non-grammar tokens and fake constants 173 | * lparser.lua: removed unused token peeking code, added table init 174 | * lparser.lua: add local variable tracking code 175 | * lparser.lua: coded local/global variable tracking code 176 | * lparser.lua (singlevar): bug, tried to local globalinfo[id] 177 | * lparser.lua (init): rename mistake, toklist should be tokorig 178 | * lparser.lua (init): indexing mistake, target, i should be j 179 | * lparser.lua: passes parsing of LuaSrcDiet.lua 180 | 181 | 2008-05-27 Kein-Hong Man 182 | 183 | * README: updated 184 | * Version: 0.10.2 185 | 186 | 2008-05-27 Kein-Hong Man 187 | 188 | * sample/numbers_original.lua: adding number samples 189 | * optlex.lua (do_number): fixed trying to compare string variable 190 | and constant number in if statements 191 | * optlex.lua (do_number): mistake in scientific number regex, 192 | +/- sign must be optional 193 | * optlex.lua (do_number): mistake in taking substring, forgot 194 | first position index parameter 195 | * sample/numbers_original.lua: completed basic samples 196 | * optlex.lua (do_number): mostly works 197 | 198 | 2008-05-27 Kein-Hong Man 199 | 200 | * optlex.lua (do_number): coded number optimizer 201 | * sample/strings_original.lua: adding string samples 202 | * sample/Makefile: added entry to build string samples 203 | * optlex.lua (do_string): bug, used string.byte instead of 204 | string.char in /ddd tests 205 | * LuaSrcDiet.lua: bug, missing handling for --opt*, --noopt* 206 | optimization options 207 | * optlex.lua (do_string): bug, incomplete code for handling 208 | \ddd for \\ and translation to literal char 209 | * sample/strings_original.lua: completed basic samples 210 | * optlex.lua (do_string): mostly works 211 | 212 | 2008-05-27 Kein-Hong Man 213 | 214 | * lparser.lua: added, from Yueliang 0.4.0, removed log() calls 215 | * optparser.lua: added placeholder, parser-based optimizer file 216 | * test/test_optparser.lua: placeholder for optparser.lua testing 217 | 218 | 2008-05-27 Kein-Hong Man 219 | 220 | * optlex.lua (optimize): fixed missing parameter for toklnlist 221 | * optlex.lua (do_string): forgot to initialize c_delim, c_ndelim 222 | to zero 223 | * optlex.lua (do_lstring): bad regex (missing '%' to escape '['), 224 | mistake in editing 225 | * optlex.lua (do_lstring, do_lcomment): attempted to use p from 226 | a string.find when it is nil 227 | * optlex.lua (do_lstring, do_lcomment): rearranged to allow a nil 228 | position variable p to break out of loop 229 | * optlex.lua (do_string): missing i update for \ case 230 | * LuaSrcDiet.lua: enabled relevant command-line options 231 | * optlex.lua (do_string, do_lstring): seem to work 232 | 233 | 2008-05-27 Kein-Hong Man 234 | 235 | * llex.lua: added tokln table for keeping line numbers 236 | * optlex.lua: added management of token line number list 237 | * optlex.lua (do_lstring): changed trailing whitespace warning 238 | message to include approximate line number 239 | * LuaSrcDiet.lua (process_file): adjusted warning handling 240 | * optlex.lua: comment updates, minor improvements 241 | * LuaSrcDiet.lua (process_file): added warning for when settings 242 | cause some CRLF or LFCR line endings to still exist 243 | * optlex.lua (do_string): coded string optimizer 244 | * optlex.lua: updated notes for number optimization 245 | 246 | 2008-05-26 Kein-Hong Man 247 | 248 | * optlex.lua (do_comment): coded short comment optimizer 249 | * optlex.lua (do_lcomment): coded long comment optimizer 250 | * optlex.lua (do_lstring): coded long string optimizer 251 | 252 | 2008-05-25 Kein-Hong Man 253 | 254 | * README: updated 255 | * Version: 0.10.1 256 | 257 | 2008-05-25 Kein-Hong Man 258 | 259 | * LuaSrcDiet.lua (process_file): added code to print statistics 260 | * LuaSrcDiet.lua: set back executable flag, added #! line 261 | * sample/: added directory for samples 262 | * sample/Makefile: braindead Makefile to create samples, 263 | statistics.txt and *.lua files are Makefile-generated 264 | * LuaSrcDiet.lua: formatting adjustments for statistics output 265 | * LuaSrcDiet.lua: added version information option 266 | * optlex.lua (optimize): done pass 2 (opt-eols) and tested 267 | * LuaSrcDiet.lua: removed non-functional options for now 268 | 269 | 2008-05-25 Kein-Hong Man 270 | 271 | * LuaSrcDiet.lua: updated option description for --opt-eols 272 | * LuaSrcDiet.lua: added function to save data 273 | * LuaSrcDiet.lua (process_file): preliminary implementation 274 | * LuaSrcDiet.lua (main): bug, avoid assigning option.OUTPUT_FILE 275 | if it is not set 276 | * technotes.txt: updated TK_OP-TK_OP behaviour, and behaviour 277 | of '-' followed by comments 278 | * optlex.lua (checkpair): fixed TK_OP-TK_OP behaviour 279 | * optlex.lua (optimize): fixed bug, option[] lookup wrong 280 | * optlex.lua (optimize): added option forcing for --opt-eols 281 | * optlex.lua: added support function to repack tokens 282 | * optlex.lua (optimize): fixed bug, don't allow reinterpret if 283 | current token deleted 284 | * optlex.lua: preliminary working version with pass 1 working, 285 | optimization calls to be done, pass 2 to be done 286 | 287 | 2008-05-25 Kein-Hong Man 288 | 289 | * 5.0/README: created for old notes, moved old README to here 290 | * README: mostly deleted, to rewrite 291 | * optlex.lua: support functions, pass 1/2 skeleton 292 | * technotes.txt: updated to reflect optlex.lua work 293 | * optlex.lua: added prototypes for optimization functions 294 | * optlex.lua: completed pass 1 of lexer-based optimization 295 | 296 | 2008-05-24 Kein-Hong Man 297 | 298 | * optlex.lua: create file for lexer-based optimization code 299 | * test/test_optlex.lua: created file for optlex.lua testing 300 | * technotes.txt: updated, cut out some comments from llex.lua 301 | * optlex.lua: coding lexer-based optimization code 302 | 303 | 2008-05-24 Kein-Hong Man 304 | 305 | * LuaSrcDiet.lua (main): fixed a missing return flag 306 | * LuaSrcDiet.lua: coded file loader, token dumper 307 | * LuaSrcDiet.lua: coded statistics dump feature 308 | * technotes.txt: new file detailing optimization tech notes 309 | 310 | 2008-05-23 Kein-Hong Man 311 | 312 | * LuaSrcDiet.lua: coded messages, part of options handling 313 | * LuaSrcDiet.lua: coded argument handling 314 | * llex.lua: converted lexer to LuaSrcDiet needs 315 | * test/test_llex.lua: copied over from Yueliang for testing 316 | * llex.lua (init): reset token, seminfo tables at initialization 317 | * test/test_llex.lua: updated for LuaSrcDiet's lexer, testing 318 | * llex.lua (read_long_string): re-insert is_str parameter, needed 319 | for error message 320 | * test/test_llex.lua: adjusted test cases, all tests passes, 321 | except a test for accented character identifiers, fails for now 322 | * lparser.lua: remove first, work on lexer features first 323 | * LuaSrcDiet.lua: ported over file handling code 324 | 325 | 2008-05-23 Kein-Hong Man 326 | 327 | * LuaSrcDiet.lua: created file for work on 5.1 version 328 | * llex.lua: added 5.1.x lexer from Yueliang, to be worked on 329 | * lparser.lua: added 5.1.x parser skeleton from Yueliang, to 330 | be worked on 331 | * COPYRIGHT: updated copyright 332 | * COPYRIGHT_Lua51: added Lua 5.1.x copyright for completeness 333 | 334 | 2008-05-23 Kein-Hong Man 335 | 336 | * 5.0/: created directory for old 5.0 scripts to make way 337 | for new 5.1 work 338 | * 5.0/LuaSrcDiet.lua, 5.0/LuaSrcDiet_.lua, 5.0/LSDTest.lua: 339 | moved from root directory 340 | 341 | 2005-08-16 Kein-Hong Man 342 | 343 | * Version: 0.9.1 344 | 345 | 2005-08-15 Kein-Hong Man 346 | 347 | * LuaSrcDiet.lua (llex:lex): fixed buff init bug 348 | * LuaSrcDiet.lua (DumpTokens): added --dump option 349 | 350 | 2005-04-14 Kein-Hong Man 351 | 352 | * LuaSrcDiet.lua: adjusted shellbang 353 | * moved project to a Subversion repository 354 | 355 | 2005-02-15 Kein-Hong Man 356 | 357 | * LuaSrcDiet.lua: preliminary functional script 358 | * LuaSrcDiet.lua: preliminary feature-complete 359 | * README: written for preliminary release 360 | * LuaSrcDiet.lua: finalized for preliminary release 361 | * Version: 0.9.0 362 | 363 | 2005-02-14 Kein-Hong Man 364 | 365 | * LuaSrcDiet.lua: done modifying llex.lua for LuaSrcDiet 366 | * LSDTest.lua: created 367 | 368 | 2005-01-14 Kein-Hong Man 369 | 370 | * started project 371 | --------------------------------------------------------------------------------