|
1 |
|
2 local keywords = { "function", "for", "if", "elseif", "then", "else", "do", "repeat", "until", "end", "return", "true", "false", "and", "not", "or", "local", "nil", "break" } |
|
3 for _, keyword in ipairs(keywords) do keywords[keyword] = true; end |
|
4 |
|
5 local ops = {}; |
|
6 |
|
7 local bytelisting, err = io.popen("luac -l -p "..arg[1]); |
|
8 if not bytelisting then print(err); return 1; end |
|
9 |
|
10 for line in bytelisting:lines() do |
|
11 local opnum, linenum, opname, comments = line:match("^\t(%d+)%s*%[(%d+)%]%s*(%u+)[^;]*;? ?(.*)$"); |
|
12 if opnum then |
|
13 linenum = tonumber(linenum); |
|
14 -- print(string.format("opnum: %d line: %d op: %s ; %s", opnum, linenum, opname, comments)); |
|
15 if not ops[linenum] then |
|
16 ops[linenum] = { }; |
|
17 if ops[linenum-1] then |
|
18 table.sort(ops[linenum-1], function (a,b) return a.comment and b.comment and #a.comment > #b.comment; end); |
|
19 end |
|
20 end |
|
21 table.insert(ops[linenum], { name = opname, comment = comments }); |
|
22 end |
|
23 end |
|
24 |
|
25 local test_coverage = {}; |
|
26 |
|
27 local test_report, test_report_err = io.open("tests/reports/coverage_"..arg[1]:gsub("^%W", ""):gsub("%W+", "_")..".report"); |
|
28 if test_report then |
|
29 for line in test_report:lines() do |
|
30 local f, linenum, test, success = line:match("^(.-)|(.-)|(.-)|(.-)$"); |
|
31 if linenum and success then |
|
32 test_coverage[tonumber(linenum)] = success; |
|
33 else |
|
34 io.stderr:write("Warning: Can't parse this: [[", line, "]] in test report\n"); |
|
35 end |
|
36 end |
|
37 else |
|
38 test_coverage = nil; |
|
39 end |
|
40 |
|
41 |
|
42 |
|
43 local linenum = 1; |
|
44 local html_spaces = { ["\t"] = " ", [" "] = " " }; |
|
45 function escape_html(s) |
|
46 return (s and s:gsub("[^%w]", { ["<"] = "<", [">"] = ">", ["&"] = "&", ["\""] = """, ["'"] = "'" })) or ""; |
|
47 end |
|
48 |
|
49 function escape_pattern(s) |
|
50 return s:gsub("(%p)", "%%%1"); |
|
51 end |
|
52 |
|
53 local sub; |
|
54 |
|
55 print("<html><head><title>"..escape_html(arg[1]).."</title><style type='text/css'>"); |
|
56 print [[ |
|
57 .getglobal { color:#cc0000 !important; text-decoration:underline; } |
|
58 .setglobal { color:#ff0000; font-weight:bold; text-decoration:underline; } |
|
59 .getupval { color:#00cc00; } |
|
60 .nop { color:#cccccc; } |
|
61 .keyword { color:#0000cc; } |
|
62 .comment, .comment .keyword { color:#007733; } |
|
63 .string, .string * { color:#007755; } |
|
64 |
|
65 td .test-false, .test-false { background-color:#ffa6be !important; display:block; width:100%; } |
|
66 td .test-true, .test-true { background-color:#beffa6; display:block; width:100%; } |
|
67 td .test-miss, .test-miss { background-color:#a6beff; display:block; width:100%; } |
|
68 |
|
69 table { border-collapse: collapse; } |
|
70 span, tr, *, table { padding:2px; } |
|
71 td { padding:0px; padding-right:2em; } |
|
72 ]] |
|
73 print("</style></head><body>\n<br/>\n<table>"); |
|
74 for line in io.lines(arg[1]) do |
|
75 line = " "..line:gsub("%s", html_spaces).." "; |
|
76 line = line:gsub("FIXME:?", "<b>%1</b>"); |
|
77 line = line:gsub("TODO:?", "<b>%1</b>"); |
|
78 line = line:gsub("%-%-.*$", "<span class='comment'>%1</span>"); |
|
79 line = line:gsub("\".-[^\\]\"", "<span class='string'>%1</span>"); |
|
80 if ops[linenum] then |
|
81 for _, op in pairs(ops[linenum]) do |
|
82 if op.comment and #op.comment > 0 and op.name ~= "JMP" then |
|
83 --print("Replacing "..escape_pattern(op.comment).."("..op.comment..") for "..op.name); |
|
84 line, sub = line:gsub("([^%w_])("..escape_pattern(op.comment)..")([^%w_])", "%1<span class='"..op.name:lower().."'>"..escape_html(op.comment).."</span>%3"); |
|
85 if sub == 0 and op.name == "GETTABLE" then |
|
86 --print("Trying again..."); |
|
87 local id = op.comment:match("[%w_]+"); |
|
88 if id then |
|
89 --print("Got "..id); |
|
90 line = line:gsub(escape_pattern("."..id), ".<span class='"..op.name:lower().."'>"..escape_html(id).."</span>"); |
|
91 end |
|
92 end |
|
93 else |
|
94 --print("??", tostring(op.comment), tostring(#op.comment)); |
|
95 end |
|
96 end |
|
97 line = line:gsub("[%w_]+", function (id) if keywords[id] then return "<span class='keyword'>"..id.."</span>"; end end); |
|
98 local classes = { "line" }; |
|
99 |
|
100 if test_coverage and test_coverage[linenum] ~= nil then |
|
101 table.insert(classes, "test-"..test_coverage[linenum]); |
|
102 end |
|
103 print("<tr><td><span class='"..table.concat(classes, " ").."'>"..linenum.."</span></td><td>", line, "</td></tr>"); |
|
104 else |
|
105 line = line:gsub("[%w_]+", function (id) if keywords[id] then return "<span class='keyword'>"..id.."</span>"; end end); |
|
106 print("<tr><td><span class='line nop'>"..linenum.."</span></td><td>", line, "</td></tr>"); |
|
107 end |
|
108 linenum = linenum + 1; |
|
109 end |
|
110 |
|
111 print("</table>\n</body>\n</html>"); |