lua2html.lua

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