check-expect.lua revision 1.1 1 1.1 rillig #! /usr/bin/lua
2 1.1 rillig -- $NetBSD: check-expect.lua,v 1.1 2022/01/15 12:35:18 rillig Exp $
3 1.1 rillig
4 1.1 rillig --[[
5 1.1 rillig
6 1.1 rillig usage: lua ./check-expect.lua *.mk
7 1.1 rillig
8 1.1 rillig Check that each text from an '# expect: ...' comment in the .mk source files
9 1.1 rillig occurs in the corresponding .exp file, in the same order as in the .mk file.
10 1.1 rillig
11 1.1 rillig Check that each text from an '# expect[+-]offset: ...' comment in the .mk
12 1.1 rillig source files occurs in the corresponding .exp file and refers back to the
13 1.1 rillig correct line in the .mk file.
14 1.1 rillig
15 1.1 rillig ]]
16 1.1 rillig
17 1.1 rillig
18 1.1 rillig local had_errors = false
19 1.1 rillig ---@param fmt string
20 1.1 rillig function print_error(fmt, ...)
21 1.1 rillig print(fmt:format(...))
22 1.1 rillig had_errors = true
23 1.1 rillig end
24 1.1 rillig
25 1.1 rillig
26 1.1 rillig ---@return nil | string[]
27 1.1 rillig local function load_lines(fname)
28 1.1 rillig local lines = {}
29 1.1 rillig
30 1.1 rillig local f = io.open(fname, "r")
31 1.1 rillig if f == nil then return nil end
32 1.1 rillig
33 1.1 rillig for line in f:lines() do
34 1.1 rillig table.insert(lines, line)
35 1.1 rillig end
36 1.1 rillig f:close()
37 1.1 rillig
38 1.1 rillig return lines
39 1.1 rillig end
40 1.1 rillig
41 1.1 rillig
42 1.1 rillig ---@param exp_lines string[]
43 1.1 rillig local function collect_lineno_diagnostics(exp_lines)
44 1.1 rillig ---@type table<string, string[]>
45 1.1 rillig local by_location = {}
46 1.1 rillig
47 1.1 rillig for _, line in ipairs(exp_lines) do
48 1.1 rillig ---@type string | nil, string, string
49 1.1 rillig local l_fname, l_lineno, l_msg =
50 1.1 rillig line:match("^make: \"([^\"]+)\" line (%d+): (.*)")
51 1.1 rillig if l_fname ~= nil then
52 1.1 rillig local location = ("%s:%d"):format(l_fname, l_lineno)
53 1.1 rillig if by_location[location] == nil then
54 1.1 rillig by_location[location] = {}
55 1.1 rillig end
56 1.1 rillig table.insert(by_location[location], l_msg)
57 1.1 rillig end
58 1.1 rillig end
59 1.1 rillig
60 1.1 rillig return by_location
61 1.1 rillig end
62 1.1 rillig
63 1.1 rillig
64 1.1 rillig local function check_mk(mk_fname)
65 1.1 rillig local exp_fname = mk_fname:gsub("%.mk$", ".exp")
66 1.1 rillig local mk_lines = load_lines(mk_fname)
67 1.1 rillig local exp_lines = load_lines(exp_fname)
68 1.1 rillig if exp_lines == nil then return end
69 1.1 rillig local by_location = collect_lineno_diagnostics(exp_lines)
70 1.1 rillig local prev_expect_line = 0
71 1.1 rillig
72 1.1 rillig for mk_lineno, mk_line in ipairs(mk_lines) do
73 1.1 rillig for text in mk_line:gmatch("#%s*expect:%s*(.*)") do
74 1.1 rillig local i = prev_expect_line
75 1.1 rillig while i < #exp_lines and text ~= exp_lines[i + 1] do
76 1.1 rillig i = i + 1
77 1.1 rillig end
78 1.1 rillig if i < #exp_lines then
79 1.1 rillig prev_expect_line = i + 1
80 1.1 rillig else
81 1.1 rillig print_error("error: %s:%d: '%s:%d+' must contain '%s'",
82 1.1 rillig mk_fname, mk_lineno, exp_fname, prev_expect_line + 1, text)
83 1.1 rillig end
84 1.1 rillig end
85 1.1 rillig
86 1.1 rillig ---@param text string
87 1.1 rillig for offset, text in mk_line:gmatch("#%s*expect([+%-]%d+):%s*(.*)") do
88 1.1 rillig local location = ("%s:%d"):format(mk_fname, mk_lineno + tonumber(offset))
89 1.1 rillig
90 1.1 rillig local found = false
91 1.1 rillig if by_location[location] ~= nil then
92 1.1 rillig for i, message in ipairs(by_location[location]) do
93 1.1 rillig if message ~= "" and message:find(text, 1, true) then
94 1.1 rillig by_location[location][i] = ""
95 1.1 rillig found = true
96 1.1 rillig break
97 1.1 rillig end
98 1.1 rillig end
99 1.1 rillig end
100 1.1 rillig
101 1.1 rillig if not found then
102 1.1 rillig print_error("error: %s:%d: %s must contain '%s'",
103 1.1 rillig mk_fname, mk_lineno, exp_fname, text)
104 1.1 rillig end
105 1.1 rillig end
106 1.1 rillig end
107 1.1 rillig end
108 1.1 rillig
109 1.1 rillig for _, fname in ipairs(arg) do
110 1.1 rillig check_mk(fname)
111 1.1 rillig end
112 1.1 rillig os.exit(not had_errors)
113