LuaSrcDiet.lua 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. #!/usr/bin/env lua
  2. --[[--------------------------------------------------------------------
  3. LuaSrcDiet
  4. Compresses Lua source code by removing unnecessary characters.
  5. For Lua 5.1.x source code.
  6. Copyright (c) 2008 Kein-Hong Man <khman@users.sf.net>
  7. The COPYRIGHT file describes the conditions
  8. under which this software may be distributed.
  9. See the ChangeLog for more information.
  10. ----------------------------------------------------------------------]]
  11. --[[--------------------------------------------------------------------
  12. -- NOTES:
  13. -- * Remember to update version and date information below (MSG_TITLE)
  14. -- * TODO: to implement pcall() to properly handle lexer etc. errors
  15. -- * TODO: verify token stream or double-check binary chunk?
  16. -- * TODO: need some automatic testing for a semblance of sanity
  17. -- * TODO: the plugin module is highly experimental and unstable
  18. ----------------------------------------------------------------------]]
  19. -- standard libraries, functions
  20. local string = string
  21. local math = math
  22. local table = table
  23. local require = require
  24. local print = print
  25. local sub = string.sub
  26. local gmatch = string.gmatch
  27. -- support modules
  28. local llex = require "llex"
  29. local lparser = require "lparser"
  30. local optlex = require "optlex"
  31. local optparser = require "optparser"
  32. local plugin
  33. --[[--------------------------------------------------------------------
  34. -- messages and textual data
  35. ----------------------------------------------------------------------]]
  36. local MSG_TITLE = [[
  37. LuaSrcDiet: Puts your Lua 5.1 source code on a diet
  38. Version 0.11.2 (20080608) Copyright (c) 2005-2008 Kein-Hong Man
  39. The COPYRIGHT file describes the conditions under which this
  40. software may be distributed.
  41. ]]
  42. local MSG_USAGE = [[
  43. usage: LuaSrcDiet [options] [filenames]
  44. example:
  45. >LuaSrcDiet myscript.lua -o myscript_.lua
  46. options:
  47. -v, --version prints version information
  48. -h, --help prints usage information
  49. -o <file> specify file name to write output
  50. -s <suffix> suffix for output files (default '_')
  51. --keep <msg> keep block comment with <msg> inside
  52. --plugin <module> run <module> in plugin/ directory
  53. - stop handling arguments
  54. (optimization levels)
  55. --none all optimizations off (normalizes EOLs only)
  56. --basic lexer-based optimizations only
  57. --maximum maximize reduction of source
  58. (informational)
  59. --quiet process files quietly
  60. --read-only read file and print token stats only
  61. --dump-lexer dump raw tokens from lexer to stdout
  62. --dump-parser dump variable tracking tables from parser
  63. --details extra info (strings, numbers, locals)
  64. features (to disable, insert 'no' prefix like --noopt-comments):
  65. %s
  66. default settings:
  67. %s]]
  68. ------------------------------------------------------------------------
  69. -- optimization options, for ease of switching on and off
  70. -- * positive to enable optimization, negative (no) to disable
  71. -- * these options should follow --opt-* and --noopt-* style for now
  72. ------------------------------------------------------------------------
  73. local OPTION = [[
  74. --opt-comments,'remove comments and block comments'
  75. --opt-whitespace,'remove whitespace excluding EOLs'
  76. --opt-emptylines,'remove empty lines'
  77. --opt-eols,'all above, plus remove unnecessary EOLs'
  78. --opt-strings,'optimize strings and long strings'
  79. --opt-numbers,'optimize numbers'
  80. --opt-locals,'optimize local variable names'
  81. --opt-entropy,'tries to reduce symbol entropy of locals'
  82. ]]
  83. -- preset configuration
  84. local DEFAULT_CONFIG = [[
  85. --opt-comments --opt-whitespace --opt-emptylines
  86. --opt-numbers --opt-locals
  87. ]]
  88. -- override configurations: MUST explicitly enable/disable everything
  89. local BASIC_CONFIG = [[
  90. --opt-comments --opt-whitespace --opt-emptylines
  91. --noopt-eols --noopt-strings --noopt-numbers
  92. --noopt-locals
  93. ]]
  94. local MAXIMUM_CONFIG = [[
  95. --opt-comments --opt-whitespace --opt-emptylines
  96. --opt-eols --opt-strings --opt-numbers
  97. --opt-locals --opt-entropy
  98. ]]
  99. local NONE_CONFIG = [[
  100. --noopt-comments --noopt-whitespace --noopt-emptylines
  101. --noopt-eols --noopt-strings --noopt-numbers
  102. --noopt-locals
  103. ]]
  104. local DEFAULT_SUFFIX = "_" -- default suffix for file renaming
  105. local PLUGIN_SUFFIX = "plugin/" -- relative location of plugins
  106. --[[--------------------------------------------------------------------
  107. -- startup and initialize option list handling
  108. ----------------------------------------------------------------------]]
  109. -- simple error message handler; change to error if traceback wanted
  110. local function die(msg)
  111. print("LuaSrcDiet: "..msg); os.exit()
  112. end
  113. --die = error--DEBUG
  114. if not string.match(_VERSION, "5.1", 1, 1) then -- sanity check
  115. die("requires Lua 5.1 to run")
  116. end
  117. ------------------------------------------------------------------------
  118. -- prepares text for list of optimizations, prepare lookup table
  119. ------------------------------------------------------------------------
  120. local MSG_OPTIONS = ""
  121. do
  122. local WIDTH = 24
  123. local o = {}
  124. for op, desc in gmatch(OPTION, "%s*([^,]+),'([^']+)'") do
  125. local msg = " "..op
  126. msg = msg..string.rep(" ", WIDTH - #msg)..desc.."\n"
  127. MSG_OPTIONS = MSG_OPTIONS..msg
  128. o[op] = true
  129. o["--no"..sub(op, 3)] = true
  130. end
  131. OPTION = o -- replace OPTION with lookup table
  132. end
  133. MSG_USAGE = string.format(MSG_USAGE, MSG_OPTIONS, DEFAULT_CONFIG)
  134. ------------------------------------------------------------------------
  135. -- global variable initialization, option set handling
  136. ------------------------------------------------------------------------
  137. local suffix = DEFAULT_SUFFIX -- file suffix
  138. local option = {} -- program options
  139. local stat_c, stat_l -- statistics tables
  140. -- function to set option lookup table based on a text list of options
  141. -- note: additional forced settings for --opt-eols is done in optlex.lua
  142. local function set_options(CONFIG)
  143. for op in gmatch(CONFIG, "(%-%-%S+)") do
  144. if sub(op, 3, 4) == "no" and -- handle negative options
  145. OPTION["--"..sub(op, 5)] then
  146. option[sub(op, 5)] = false
  147. else
  148. option[sub(op, 3)] = true
  149. end
  150. end
  151. end
  152. --[[--------------------------------------------------------------------
  153. -- support functions
  154. ----------------------------------------------------------------------]]
  155. -- list of token types, parser-significant types are up to TTYPE_GRAMMAR
  156. -- while the rest are not used by parsers; arranged for stats display
  157. local TTYPES = {
  158. "TK_KEYWORD", "TK_NAME", "TK_NUMBER", -- grammar
  159. "TK_STRING", "TK_LSTRING", "TK_OP",
  160. "TK_EOS",
  161. "TK_COMMENT", "TK_LCOMMENT", -- non-grammar
  162. "TK_EOL", "TK_SPACE",
  163. }
  164. local TTYPE_GRAMMAR = 7
  165. local EOLTYPES = { -- EOL names for token dump
  166. ["\n"] = "LF", ["\r"] = "CR",
  167. ["\n\r"] = "LFCR", ["\r\n"] = "CRLF",
  168. }
  169. ------------------------------------------------------------------------
  170. -- read source code from file
  171. ------------------------------------------------------------------------
  172. local function load_file(fname)
  173. local INF = io.open(fname, "rb")
  174. if not INF then die("cannot open \""..fname.."\" for reading") end
  175. local dat = INF:read("*a")
  176. if not dat then die("cannot read from \""..fname.."\"") end
  177. INF:close()
  178. return dat
  179. end
  180. ------------------------------------------------------------------------
  181. -- save source code to file
  182. ------------------------------------------------------------------------
  183. local function save_file(fname, dat)
  184. local OUTF = io.open(fname, "wb")
  185. if not OUTF then die("cannot open \""..fname.."\" for writing") end
  186. local status = OUTF:write(dat)
  187. if not status then die("cannot write to \""..fname.."\"") end
  188. OUTF:close()
  189. end
  190. ------------------------------------------------------------------------
  191. -- functions to deal with statistics
  192. ------------------------------------------------------------------------
  193. -- initialize statistics table
  194. local function stat_init()
  195. stat_c, stat_l = {}, {}
  196. for i = 1, #TTYPES do
  197. local ttype = TTYPES[i]
  198. stat_c[ttype], stat_l[ttype] = 0, 0
  199. end
  200. end
  201. -- add a token to statistics table
  202. local function stat_add(tok, seminfo)
  203. stat_c[tok] = stat_c[tok] + 1
  204. stat_l[tok] = stat_l[tok] + #seminfo
  205. end
  206. -- do totals for statistics table, return average table
  207. local function stat_calc()
  208. local function avg(c, l) -- safe average function
  209. if c == 0 then return 0 end
  210. return l / c
  211. end
  212. local stat_a = {}
  213. local c, l = 0, 0
  214. for i = 1, TTYPE_GRAMMAR do -- total grammar tokens
  215. local ttype = TTYPES[i]
  216. c = c + stat_c[ttype]; l = l + stat_l[ttype]
  217. end
  218. stat_c.TOTAL_TOK, stat_l.TOTAL_TOK = c, l
  219. stat_a.TOTAL_TOK = avg(c, l)
  220. c, l = 0, 0
  221. for i = 1, #TTYPES do -- total all tokens
  222. local ttype = TTYPES[i]
  223. c = c + stat_c[ttype]; l = l + stat_l[ttype]
  224. stat_a[ttype] = avg(stat_c[ttype], stat_l[ttype])
  225. end
  226. stat_c.TOTAL_ALL, stat_l.TOTAL_ALL = c, l
  227. stat_a.TOTAL_ALL = avg(c, l)
  228. return stat_a
  229. end
  230. --[[--------------------------------------------------------------------
  231. -- main tasks
  232. ----------------------------------------------------------------------]]
  233. ------------------------------------------------------------------------
  234. -- a simple token dumper, minimal translation of seminfo data
  235. ------------------------------------------------------------------------
  236. local function dump_tokens(srcfl)
  237. --------------------------------------------------------------------
  238. -- load file and process source input into tokens
  239. --------------------------------------------------------------------
  240. local z = load_file(srcfl)
  241. llex.init(z)
  242. llex.llex()
  243. local toklist, seminfolist = llex.tok, llex.seminfo
  244. --------------------------------------------------------------------
  245. -- display output
  246. --------------------------------------------------------------------
  247. for i = 1, #toklist do
  248. local tok, seminfo = toklist[i], seminfolist[i]
  249. if tok == "TK_OP" and string.byte(seminfo) < 32 then
  250. seminfo = "(".. string.byte(seminfo)..")"
  251. elseif tok == "TK_EOL" then
  252. seminfo = EOLTYPES[seminfo]
  253. else
  254. seminfo = "'"..seminfo.."'"
  255. end
  256. print(tok.." "..seminfo)
  257. end--for
  258. end
  259. ----------------------------------------------------------------------
  260. -- parser dump; dump globalinfo and localinfo tables
  261. ----------------------------------------------------------------------
  262. local function dump_parser(srcfl)
  263. local print = print
  264. --------------------------------------------------------------------
  265. -- load file and process source input into tokens
  266. --------------------------------------------------------------------
  267. local z = load_file(srcfl)
  268. llex.init(z)
  269. llex.llex()
  270. local toklist, seminfolist, toklnlist
  271. = llex.tok, llex.seminfo, llex.tokln
  272. --------------------------------------------------------------------
  273. -- do parser optimization here
  274. --------------------------------------------------------------------
  275. lparser.init(toklist, seminfolist, toklnlist)
  276. local globalinfo, localinfo = lparser.parser()
  277. --------------------------------------------------------------------
  278. -- display output
  279. --------------------------------------------------------------------
  280. local hl = string.rep("-", 72)
  281. print("*** Local/Global Variable Tracker Tables ***")
  282. print(hl.."\n GLOBALS\n"..hl)
  283. -- global tables have a list of xref numbers only
  284. for i = 1, #globalinfo do
  285. local obj = globalinfo[i]
  286. local msg = "("..i..") '"..obj.name.."' -> "
  287. local xref = obj.xref
  288. for j = 1, #xref do msg = msg..xref[j].." " end
  289. print(msg)
  290. end
  291. -- local tables have xref numbers and a few other special
  292. -- numbers that are specially named: decl (declaration xref),
  293. -- act (activation xref), rem (removal xref)
  294. print(hl.."\n LOCALS (decl=declared act=activated rem=removed)\n"..hl)
  295. for i = 1, #localinfo do
  296. local obj = localinfo[i]
  297. local msg = "("..i..") '"..obj.name.."' decl:"..obj.decl..
  298. " act:"..obj.act.." rem:"..obj.rem
  299. if obj.isself then
  300. msg = msg.." isself"
  301. end
  302. msg = msg.." -> "
  303. local xref = obj.xref
  304. for j = 1, #xref do msg = msg..xref[j].." " end
  305. print(msg)
  306. end
  307. print(hl.."\n")
  308. end
  309. ------------------------------------------------------------------------
  310. -- reads source file(s) and reports some statistics
  311. ------------------------------------------------------------------------
  312. local function read_only(srcfl)
  313. local print = print
  314. --------------------------------------------------------------------
  315. -- load file and process source input into tokens
  316. --------------------------------------------------------------------
  317. local z = load_file(srcfl)
  318. llex.init(z)
  319. llex.llex()
  320. local toklist, seminfolist = llex.tok, llex.seminfo
  321. print(MSG_TITLE)
  322. print("Statistics for: "..srcfl.."\n")
  323. --------------------------------------------------------------------
  324. -- collect statistics
  325. --------------------------------------------------------------------
  326. stat_init()
  327. for i = 1, #toklist do
  328. local tok, seminfo = toklist[i], seminfolist[i]
  329. stat_add(tok, seminfo)
  330. end--for
  331. local stat_a = stat_calc()
  332. --------------------------------------------------------------------
  333. -- display output
  334. --------------------------------------------------------------------
  335. local fmt = string.format
  336. local function figures(tt)
  337. return stat_c[tt], stat_l[tt], stat_a[tt]
  338. end
  339. local tabf1, tabf2 = "%-16s%8s%8s%10s", "%-16s%8d%8d%10.2f"
  340. local hl = string.rep("-", 42)
  341. print(fmt(tabf1, "Lexical", "Input", "Input", "Input"))
  342. print(fmt(tabf1, "Elements", "Count", "Bytes", "Average"))
  343. print(hl)
  344. for i = 1, #TTYPES do
  345. local ttype = TTYPES[i]
  346. print(fmt(tabf2, ttype, figures(ttype)))
  347. if ttype == "TK_EOS" then print(hl) end
  348. end
  349. print(hl)
  350. print(fmt(tabf2, "Total Elements", figures("TOTAL_ALL")))
  351. print(hl)
  352. print(fmt(tabf2, "Total Tokens", figures("TOTAL_TOK")))
  353. print(hl.."\n")
  354. end
  355. ------------------------------------------------------------------------
  356. -- process source file(s), write output and reports some statistics
  357. ------------------------------------------------------------------------
  358. local function process_file(srcfl, destfl)
  359. local function print(...) -- handle quiet option
  360. if option.QUIET then return end
  361. _G.print(...)
  362. end
  363. if plugin and plugin.init then -- plugin init
  364. option.EXIT = false
  365. plugin.init(option, srcfl, destfl)
  366. if option.EXIT then return end
  367. end
  368. print(MSG_TITLE) -- title message
  369. --------------------------------------------------------------------
  370. -- load file and process source input into tokens
  371. --------------------------------------------------------------------
  372. local z = load_file(srcfl)
  373. if plugin and plugin.post_load then -- plugin post-load
  374. z = plugin.post_load(z) or z
  375. if option.EXIT then return end
  376. end
  377. llex.init(z)
  378. llex.llex()
  379. local toklist, seminfolist, toklnlist
  380. = llex.tok, llex.seminfo, llex.tokln
  381. if plugin and plugin.post_lex then -- plugin post-lex
  382. plugin.post_lex(toklist, seminfolist, toklnlist)
  383. if option.EXIT then return end
  384. end
  385. --------------------------------------------------------------------
  386. -- collect 'before' statistics
  387. --------------------------------------------------------------------
  388. stat_init()
  389. for i = 1, #toklist do
  390. local tok, seminfo = toklist[i], seminfolist[i]
  391. stat_add(tok, seminfo)
  392. end--for
  393. local stat1_a = stat_calc()
  394. local stat1_c, stat1_l = stat_c, stat_l
  395. --------------------------------------------------------------------
  396. -- do parser optimization here
  397. --------------------------------------------------------------------
  398. if option["opt-locals"] then
  399. optparser.print = print -- hack
  400. lparser.init(toklist, seminfolist, toklnlist)
  401. local globalinfo, localinfo = lparser.parser()
  402. if plugin and plugin.post_parse then -- plugin post-parse
  403. plugin.post_parse(globalinfo, localinfo)
  404. if option.EXIT then return end
  405. end
  406. optparser.optimize(option, toklist, seminfolist, globalinfo, localinfo)
  407. if plugin and plugin.post_optparse then -- plugin post-optparse
  408. plugin.post_optparse()
  409. if option.EXIT then return end
  410. end
  411. end
  412. --------------------------------------------------------------------
  413. -- do lexer optimization here, save output file
  414. --------------------------------------------------------------------
  415. optlex.print = print -- hack
  416. toklist, seminfolist, toklnlist
  417. = optlex.optimize(option, toklist, seminfolist, toklnlist)
  418. if plugin and plugin.post_optlex then -- plugin post-optlex
  419. plugin.post_optlex(toklist, seminfolist, toklnlist)
  420. if option.EXIT then return end
  421. end
  422. local dat = table.concat(seminfolist)
  423. -- depending on options selected, embedded EOLs in long strings and
  424. -- long comments may not have been translated to \n, tack a warning
  425. if string.find(dat, "\r\n", 1, 1) or
  426. string.find(dat, "\n\r", 1, 1) then
  427. optlex.warn.mixedeol = true
  428. end
  429. -- save optimized source stream to output file
  430. save_file(destfl, dat)
  431. --------------------------------------------------------------------
  432. -- collect 'after' statistics
  433. --------------------------------------------------------------------
  434. stat_init()
  435. for i = 1, #toklist do
  436. local tok, seminfo = toklist[i], seminfolist[i]
  437. stat_add(tok, seminfo)
  438. end--for
  439. local stat_a = stat_calc()
  440. --------------------------------------------------------------------
  441. -- display output
  442. --------------------------------------------------------------------
  443. print("Statistics for: "..srcfl.." -> "..destfl.."\n")
  444. local fmt = string.format
  445. local function figures(tt)
  446. return stat1_c[tt], stat1_l[tt], stat1_a[tt],
  447. stat_c[tt], stat_l[tt], stat_a[tt]
  448. end
  449. local tabf1, tabf2 = "%-16s%8s%8s%10s%8s%8s%10s",
  450. "%-16s%8d%8d%10.2f%8d%8d%10.2f"
  451. local hl = string.rep("-", 68)
  452. print("*** lexer-based optimizations summary ***\n"..hl)
  453. print(fmt(tabf1, "Lexical",
  454. "Input", "Input", "Input",
  455. "Output", "Output", "Output"))
  456. print(fmt(tabf1, "Elements",
  457. "Count", "Bytes", "Average",
  458. "Count", "Bytes", "Average"))
  459. print(hl)
  460. for i = 1, #TTYPES do
  461. local ttype = TTYPES[i]
  462. print(fmt(tabf2, ttype, figures(ttype)))
  463. if ttype == "TK_EOS" then print(hl) end
  464. end
  465. print(hl)
  466. print(fmt(tabf2, "Total Elements", figures("TOTAL_ALL")))
  467. print(hl)
  468. print(fmt(tabf2, "Total Tokens", figures("TOTAL_TOK")))
  469. print(hl)
  470. --------------------------------------------------------------------
  471. -- report warning flags from optimizing process
  472. --------------------------------------------------------------------
  473. if optlex.warn.lstring then
  474. print("* WARNING: "..optlex.warn.lstring)
  475. elseif optlex.warn.mixedeol then
  476. print("* WARNING: ".."output still contains some CRLF or LFCR line endings")
  477. end
  478. print()
  479. end
  480. --[[--------------------------------------------------------------------
  481. -- main functions
  482. ----------------------------------------------------------------------]]
  483. local arg = {...} -- program arguments
  484. local fspec = {}
  485. set_options(DEFAULT_CONFIG) -- set to default options at beginning
  486. ------------------------------------------------------------------------
  487. -- per-file handling, ship off to tasks
  488. ------------------------------------------------------------------------
  489. local function do_files(fspec)
  490. for _, srcfl in ipairs(fspec) do
  491. local destfl
  492. ------------------------------------------------------------------
  493. -- find and replace extension for filenames
  494. ------------------------------------------------------------------
  495. local extb, exte = string.find(srcfl, "%.[^%.%\\%/]*$")
  496. local basename, extension = srcfl, ""
  497. if extb and extb > 1 then
  498. basename = sub(srcfl, 1, extb - 1)
  499. extension = sub(srcfl, extb, exte)
  500. end
  501. destfl = basename..suffix..extension
  502. if #fspec == 1 and option.OUTPUT_FILE then
  503. destfl = option.OUTPUT_FILE
  504. end
  505. if srcfl == destfl then
  506. die("output filename identical to input filename")
  507. end
  508. ------------------------------------------------------------------
  509. -- perform requested operations
  510. ------------------------------------------------------------------
  511. if option.DUMP_LEXER then
  512. dump_tokens(srcfl)
  513. elseif option.DUMP_PARSER then
  514. dump_parser(srcfl)
  515. elseif option.READ_ONLY then
  516. read_only(srcfl)
  517. else
  518. process_file(srcfl, destfl)
  519. end
  520. end--for
  521. end
  522. ------------------------------------------------------------------------
  523. -- main function (entry point is after this definition)
  524. ------------------------------------------------------------------------
  525. local function main()
  526. local argn, i = #arg, 1
  527. if argn == 0 then
  528. option.HELP = true
  529. end
  530. --------------------------------------------------------------------
  531. -- handle arguments
  532. --------------------------------------------------------------------
  533. while i <= argn do
  534. local o, p = arg[i], arg[i + 1]
  535. local dash = string.match(o, "^%-%-?")
  536. if dash == "-" then -- single-dash options
  537. if o == "-h" then
  538. option.HELP = true; break
  539. elseif o == "-v" then
  540. option.VERSION = true; break
  541. elseif o == "-s" then
  542. if not p then die("-s option needs suffix specification") end
  543. suffix = p
  544. i = i + 1
  545. elseif o == "-o" then
  546. if not p then die("-o option needs a file name") end
  547. option.OUTPUT_FILE = p
  548. i = i + 1
  549. elseif o == "-" then
  550. break -- ignore rest of args
  551. else
  552. die("unrecognized option "..o)
  553. end
  554. elseif dash == "--" then -- double-dash options
  555. if o == "--help" then
  556. option.HELP = true; break
  557. elseif o == "--version" then
  558. option.VERSION = true; break
  559. elseif o == "--keep" then
  560. if not p then die("--keep option needs a string to match for") end
  561. option.KEEP = p
  562. i = i + 1
  563. elseif o == "--plugin" then
  564. if not p then die("--plugin option needs a module name") end
  565. if option.PLUGIN then die("only one plugin can be specified") end
  566. option.PLUGIN = p
  567. plugin = require(PLUGIN_SUFFIX..p)
  568. i = i + 1
  569. elseif o == "--quiet" then
  570. option.QUIET = true
  571. elseif o == "--read-only" then
  572. option.READ_ONLY = true
  573. elseif o == "--basic" then
  574. set_options(BASIC_CONFIG)
  575. elseif o == "--maximum" then
  576. set_options(MAXIMUM_CONFIG)
  577. elseif o == "--none" then
  578. set_options(NONE_CONFIG)
  579. elseif o == "--dump-lexer" then
  580. option.DUMP_LEXER = true
  581. elseif o == "--dump-parser" then
  582. option.DUMP_PARSER = true
  583. elseif o == "--details" then
  584. option.DETAILS = true
  585. elseif OPTION[o] then -- lookup optimization options
  586. set_options(o)
  587. else
  588. die("unrecognized option "..o)
  589. end
  590. else
  591. fspec[#fspec + 1] = o -- potential filename
  592. end
  593. i = i + 1
  594. end--while
  595. if option.HELP then
  596. print(MSG_TITLE..MSG_USAGE); return true
  597. elseif option.VERSION then
  598. print(MSG_TITLE); return true
  599. end
  600. if #fspec > 0 then
  601. if #fspec > 1 and option.OUTPUT_FILE then
  602. die("with -o, only one source file can be specified")
  603. end
  604. do_files(fspec)
  605. return true
  606. else
  607. die("nothing to do!")
  608. end
  609. end
  610. -- entry point -> main() -> do_files()
  611. if not main() then
  612. die("Please run with option -h or --help for usage information")
  613. end
  614. -- end of script