|
1 local llex = require "llex" |
|
2 |
|
3 local base_char = 128; |
|
4 local keywords = { "and", "break", "do", "else", "elseif", |
|
5 "end", "false", "for", "function", "if", |
|
6 "in", "local", "nil", "not", "or", "repeat", |
|
7 "return", "then", "true", "until", "while" } |
|
8 |
|
9 function uglify_file(infile_fn, outfile_fn) |
|
10 local infile, err = io.open(infile_fn); |
|
11 if not infile then |
|
12 print_err("Can't open input file for reading: "..tostring(err)); |
|
13 return; |
|
14 end |
|
15 |
|
16 local outfile, err = io.open(outfile_fn..".uglified", "w+"); |
|
17 if not outfile then |
|
18 print_err("Can't open output file for writing: "..tostring(err)); |
|
19 return; |
|
20 end |
|
21 |
|
22 local data = infile:read("*a"); |
|
23 infile:close(); |
|
24 |
|
25 local shebang, newdata = data:match("^(#.-\n)(.+)$"); |
|
26 local code = newdata or data; |
|
27 if shebang then |
|
28 outfile:write(shebang) |
|
29 end |
|
30 |
|
31 |
|
32 while base_char + #keywords < 255 and code:find("["..string.char(base_char).."-"..string.char(base_char+#keywords-1).."]") do |
|
33 base_char = base_char + 1; |
|
34 end |
|
35 if base_char == 255 then |
|
36 -- Sorry, can't uglify this file :( |
|
37 -- We /could/ use a multi-byte marker, but that would complicate |
|
38 -- things and lower the compression ratio (there are quite a few |
|
39 -- 2-letter keywords) |
|
40 outfile:write(code); |
|
41 outfile:close(); |
|
42 os.rename(outfile_fn..".uglified", outfile_fn); |
|
43 return; |
|
44 end |
|
45 |
|
46 local keyword_map_to_char = {} |
|
47 for i, keyword in ipairs(keywords) do |
|
48 keyword_map_to_char[keyword] = string.char(base_char + i); |
|
49 end |
|
50 |
|
51 outfile:write("local base_char,keywords=", tostring(base_char), ",{"); |
|
52 for _, keyword in ipairs(keywords) do |
|
53 outfile:write('"', keyword, '",'); |
|
54 end |
|
55 outfile:write[[}; function prettify(code) return code:gsub("["..string.char(base_char).."-"..string.char(base_char+#keywords).."]", |
|
56 function (c) return keywords[c:byte()-base_char]; end) end ]] |
|
57 |
|
58 -- Write loadstring and open string |
|
59 local maxequals = 0; |
|
60 data:gsub("(=+)", function (equals_string) maxequals = math.max(maxequals, #equals_string); end); |
|
61 |
|
62 outfile:write [[assert(loadstring(prettify]] |
|
63 outfile:write("[", string.rep("=", maxequals+1), "["); |
|
64 |
|
65 -- Write code, substituting tokens as we go |
|
66 llex.init(code, "@"..infile_fn); |
|
67 llex.llex() |
|
68 local seminfo = llex.seminfo; |
|
69 for k,v in ipairs(llex.tok) do |
|
70 if v == "TK_KEYWORD" then |
|
71 local keyword_char = keyword_map_to_char[seminfo[k]]; |
|
72 if keyword_char then |
|
73 outfile:write(keyword_char); |
|
74 else -- Those who think Lua shouldn't have 'continue, fix this please :) |
|
75 outfile:write(seminfo[k]); |
|
76 end |
|
77 else |
|
78 outfile:write(seminfo[k]); |
|
79 end |
|
80 end |
|
81 |
|
82 -- Close string/functions |
|
83 outfile:write("]", string.rep("=", maxequals+1), "]"); |
|
84 outfile:write("))()"); |
|
85 outfile:close(); |
|
86 os.rename(outfile_fn..".uglified", outfile_fn); |
|
87 end |
|
88 |
|
89 if opts.uglify then |
|
90 print_info("Uglifying "..out_fn.."..."); |
|
91 uglify_file(out_fn, out_fn); |
|
92 print_info("OK!"); |
|
93 end |
|
94 |