Home | History | Annotate | Line # | Download | only in indent
t_options.lua revision 1.7.2.1
      1  1.7.2.1  perseant -- $NetBSD: t_options.lua,v 1.7.2.1 2025/08/02 05:58:13 perseant Exp $
      2      1.1    rillig --
      3      1.1    rillig -- Copyright (c) 2023 The NetBSD Foundation, Inc.
      4      1.1    rillig -- All rights reserved.
      5      1.1    rillig --
      6      1.1    rillig -- Redistribution and use in source and binary forms, with or without
      7      1.1    rillig -- modification, are permitted provided that the following conditions
      8      1.1    rillig -- are met:
      9      1.1    rillig -- 1. Redistributions of source code must retain the above copyright
     10      1.1    rillig --    notice, this list of conditions and the following disclaimer.
     11      1.1    rillig -- 2. Redistributions in binary form must reproduce the above copyright
     12      1.1    rillig --    notice, this list of conditions and the following disclaimer in the
     13      1.1    rillig --    documentation and/or other materials provided with the distribution.
     14      1.1    rillig --
     15      1.1    rillig -- THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     16      1.1    rillig -- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     17      1.1    rillig -- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18      1.1    rillig -- PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     19      1.1    rillig -- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     20      1.1    rillig -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     21      1.1    rillig -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     22      1.1    rillig -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     23      1.1    rillig -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     24      1.1    rillig -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     25      1.1    rillig -- POSSIBILITY OF SUCH DAMAGE.
     26      1.1    rillig 
     27  1.7.2.1  perseant -- usage: [INDENT=...] lua t_options.lua <file.c>...
     28      1.4    rillig --
     29  1.7.2.1  perseant -- Run indent on several inputs with different command line options, verifying
     30  1.7.2.1  perseant -- that the actual output equals the expected output.
     31      1.1    rillig --
     32      1.1    rillig -- The test files contain the input to be formatted, the formatting options
     33      1.1    rillig -- and the output, all as close together as possible. The test files use the
     34      1.1    rillig -- following directives:
     35      1.1    rillig --
     36      1.4    rillig --	//indent input
     37      1.1    rillig --		Specifies the input to be formatted.
     38      1.1    rillig --	//indent run [options]
     39      1.1    rillig --		Runs indent on the input, using the given options.
     40      1.4    rillig --	//indent end
     41      1.1    rillig --		Finishes an '//indent input' or '//indent run' section.
     42      1.1    rillig --	//indent run-equals-input [options]
     43  1.7.2.1  perseant --		Runs indent on the input, expecting that the output is the
     44  1.7.2.1  perseant --		same as the input.
     45      1.1    rillig --	//indent run-equals-prev-output [options]
     46      1.1    rillig --		Runs indent on the input, expecting the same output as from
     47      1.1    rillig --		the previous run.
     48      1.1    rillig --
     49      1.1    rillig -- All text outside input..end or run..end directives is not passed to indent.
     50      1.1    rillig --
     51      1.1    rillig -- Inside the input..end or run..end sections, comments that start with '$'
     52      1.1    rillig -- are filtered out, they can be used for remarks near the affected code.
     53      1.1    rillig --
     54      1.1    rillig -- The actual output from running indent is written to stdout, the expected
     55  1.7.2.1  perseant -- test output is written to 'expected.out', ready to be compared using diff.
     56      1.1    rillig 
     57      1.1    rillig local warned = false
     58      1.1    rillig 
     59      1.1    rillig local filename = ""
     60      1.1    rillig local lineno = 0
     61      1.1    rillig 
     62      1.1    rillig local prev_empty_lines = 0	-- the finished "block" of empty lines
     63      1.1    rillig local curr_empty_lines = 0	-- the ongoing "block" of empty lines
     64      1.1    rillig local max_empty_lines = 0	-- between sections
     65      1.1    rillig local seen_input_section = false-- the first input section is not checked
     66      1.1    rillig 
     67      1.1    rillig local section = ""		-- "", "input" or "run"
     68      1.1    rillig local section_excl_comm = ""	-- without dollar comments
     69      1.1    rillig local section_incl_comm = ""	-- with dollar comments
     70      1.1    rillig 
     71      1.1    rillig local input_excl_comm = ""	-- the input text for indent
     72      1.1    rillig local input_incl_comm = ""	-- used for duplicate checks
     73      1.1    rillig local unused_input_lineno = 0
     74      1.1    rillig 
     75      1.1    rillig local output_excl_comm = ""	-- expected output
     76      1.1    rillig local output_incl_comm = ""	-- used for duplicate checks
     77      1.1    rillig local output_lineno = 0
     78      1.1    rillig 
     79      1.1    rillig local expected_out = assert(io.open("expected.out", "w"))
     80      1.1    rillig 
     81  1.7.2.1  perseant local function err(ln, msg)
     82      1.1    rillig 	io.stderr:write(("%s:%d: error: %s\n"):format(filename, ln, msg))
     83      1.1    rillig 	os.exit(false)
     84      1.1    rillig end
     85      1.1    rillig 
     86      1.1    rillig local function warn(ln, msg)
     87      1.1    rillig 	io.stderr:write(("%s:%d: warning: %s\n"):format(filename, ln, msg))
     88      1.1    rillig 	warned = true
     89      1.1    rillig end
     90      1.1    rillig 
     91      1.1    rillig local function init_file(fname)
     92      1.1    rillig 	filename = fname
     93      1.1    rillig 	lineno = 0
     94      1.1    rillig 
     95      1.1    rillig 	prev_empty_lines = 0
     96      1.1    rillig 	curr_empty_lines = 0
     97      1.1    rillig 	max_empty_lines = 0
     98      1.1    rillig 	seen_input_section = false
     99      1.1    rillig 
    100      1.1    rillig 	section = ""
    101      1.1    rillig 	section_excl_comm = ""
    102      1.1    rillig 	section_incl_comm = ""
    103      1.1    rillig 
    104      1.1    rillig 	input_excl_comm = ""
    105      1.1    rillig 	input_incl_comm = ""
    106      1.1    rillig 	unused_input_lineno = 0
    107      1.1    rillig 
    108      1.1    rillig 	output_excl_comm = ""
    109      1.1    rillig 	output_incl_comm = ""
    110      1.1    rillig 	output_lineno = 0
    111      1.1    rillig end
    112      1.1    rillig 
    113      1.1    rillig local function check_empty_lines_block(n)
    114      1.1    rillig 	if max_empty_lines ~= n and seen_input_section then
    115      1.1    rillig 		warn(lineno, ("expecting %d empty %s, got %d")
    116  1.7.2.1  perseant 		    :format(n, n ~= 1 and "lines" or "line", max_empty_lines))
    117      1.1    rillig 	end
    118      1.1    rillig end
    119      1.1    rillig 
    120      1.1    rillig local function check_unused_input()
    121      1.1    rillig 	if unused_input_lineno ~= 0 then
    122      1.1    rillig 		warn(unused_input_lineno, "input is not used")
    123      1.1    rillig 	end
    124      1.1    rillig end
    125      1.1    rillig 
    126      1.1    rillig local function run_indent(inp, args)
    127      1.1    rillig 	local indent = os.getenv("INDENT") or "indent"
    128      1.5    rillig 	local cmd = indent .. " " .. args .. " 2>t_options.err"
    129      1.1    rillig 
    130      1.4    rillig 	local indent_in = assert(io.popen(cmd, "w"))
    131      1.1    rillig 	indent_in:write(inp)
    132      1.4    rillig 	local ok, kind, info = indent_in:close()
    133      1.1    rillig 	if not ok then
    134      1.6    rillig 		print("// " .. kind .. " " .. info)
    135      1.1    rillig 	end
    136      1.5    rillig 	for line in io.lines("t_options.err") do
    137      1.6    rillig 		print("// " .. line)
    138      1.5    rillig 	end
    139      1.1    rillig end
    140      1.1    rillig 
    141  1.7.2.1  perseant local function handle_line_outside_section(line)
    142      1.1    rillig 	if line == "" then
    143      1.1    rillig 		curr_empty_lines = curr_empty_lines + 1
    144  1.7.2.1  perseant 		return
    145  1.7.2.1  perseant 	end
    146  1.7.2.1  perseant 	if curr_empty_lines > max_empty_lines then
    147  1.7.2.1  perseant 		max_empty_lines = curr_empty_lines
    148  1.7.2.1  perseant 	end
    149  1.7.2.1  perseant 	if curr_empty_lines > 0 then
    150  1.7.2.1  perseant 		if prev_empty_lines > 1 then
    151  1.7.2.1  perseant 			warn(lineno - curr_empty_lines - 1,
    152  1.7.2.1  perseant 			    prev_empty_lines .. " empty lines a few "
    153  1.7.2.1  perseant 			    .. "lines above, should be only 1")
    154      1.1    rillig 		end
    155  1.7.2.1  perseant 		prev_empty_lines = curr_empty_lines
    156      1.1    rillig 	end
    157  1.7.2.1  perseant 	curr_empty_lines = 0
    158      1.1    rillig end
    159      1.1    rillig 
    160      1.2    rillig local function handle_indent_input()
    161      1.2    rillig 	if prev_empty_lines ~= 2 and seen_input_section then
    162      1.2    rillig 		warn(lineno, "input section needs 2 empty lines "
    163      1.2    rillig 		    .. "above, not " .. prev_empty_lines)
    164      1.2    rillig 	end
    165      1.2    rillig 	check_empty_lines_block(2)
    166      1.2    rillig 	check_unused_input()
    167      1.2    rillig 	section = "input"
    168      1.2    rillig 	section_excl_comm = ""
    169      1.2    rillig 	section_incl_comm = ""
    170      1.2    rillig 	unused_input_lineno = lineno
    171      1.2    rillig 	seen_input_section = true
    172      1.2    rillig 	output_excl_comm = ""
    173      1.2    rillig 	output_incl_comm = ""
    174      1.2    rillig 	output_lineno = 0
    175      1.2    rillig end
    176      1.1    rillig 
    177      1.2    rillig local function handle_indent_run(args)
    178      1.2    rillig 	if section ~= "" then
    179      1.2    rillig 		warn(lineno, "unfinished section '" .. section .. "'")
    180      1.2    rillig 	end
    181      1.2    rillig 	check_empty_lines_block(1)
    182      1.2    rillig 	if prev_empty_lines ~= 1 then
    183      1.2    rillig 		warn(lineno, "run section needs 1 empty line above, "
    184      1.2    rillig 		    .. "not " .. prev_empty_lines)
    185      1.2    rillig 	end
    186      1.2    rillig 	section = "run"
    187      1.2    rillig 	output_lineno = lineno
    188      1.2    rillig 	section_excl_comm = ""
    189      1.2    rillig 	section_incl_comm = ""
    190      1.1    rillig 
    191      1.2    rillig 	run_indent(input_excl_comm, args)
    192      1.2    rillig 	unused_input_lineno = 0
    193      1.2    rillig end
    194      1.1    rillig 
    195      1.2    rillig local function handle_indent_run_equals_input(args)
    196      1.2    rillig 	check_empty_lines_block(1)
    197      1.2    rillig 	run_indent(input_excl_comm, args)
    198      1.2    rillig 	expected_out:write(input_excl_comm)
    199      1.2    rillig 	unused_input_lineno = 0
    200      1.2    rillig 	max_empty_lines = 0
    201      1.7    rillig 	output_incl_comm = ""
    202      1.7    rillig 	output_excl_comm = ""
    203      1.2    rillig end
    204      1.1    rillig 
    205      1.2    rillig local function handle_indent_run_equals_prev_output(args)
    206      1.7    rillig 	if output_incl_comm == "" then
    207      1.7    rillig 		warn(lineno,
    208      1.7    rillig 		    "no previous output; use run-equals-input instead")
    209      1.7    rillig 	end
    210      1.2    rillig 	check_empty_lines_block(1)
    211      1.2    rillig 	run_indent(input_excl_comm, args)
    212      1.2    rillig 	expected_out:write(output_excl_comm)
    213      1.2    rillig 	max_empty_lines = 0
    214      1.2    rillig end
    215      1.1    rillig 
    216      1.2    rillig local function handle_indent_end_input()
    217      1.2    rillig 	if section_incl_comm == input_incl_comm then
    218      1.2    rillig 		warn(lineno, "duplicate input; remove this section")
    219      1.2    rillig 	end
    220      1.1    rillig 
    221      1.2    rillig 	input_excl_comm = section_excl_comm
    222      1.2    rillig 	input_incl_comm = section_incl_comm
    223      1.2    rillig 	section = ""
    224      1.2    rillig 	max_empty_lines = 0
    225      1.2    rillig end
    226      1.1    rillig 
    227      1.2    rillig local function handle_indent_end_run()
    228      1.2    rillig 	if section_incl_comm == input_incl_comm then
    229      1.2    rillig 		warn(output_lineno,
    230      1.2    rillig 		    "output == input; use run-equals-input")
    231      1.2    rillig 	end
    232      1.2    rillig 	if section_incl_comm == output_incl_comm then
    233      1.2    rillig 		warn(output_lineno,
    234      1.2    rillig 		    "duplicate output; use run-equals-prev-output")
    235      1.2    rillig 	end
    236      1.1    rillig 
    237      1.2    rillig 	output_excl_comm = section_excl_comm
    238      1.2    rillig 	output_incl_comm = section_incl_comm
    239      1.2    rillig 	section = ""
    240      1.2    rillig 	max_empty_lines = 0
    241      1.2    rillig end
    242      1.1    rillig 
    243      1.2    rillig local function handle_indent_directive(line, command, args)
    244      1.2    rillig 	print(line)
    245      1.2    rillig 	expected_out:write(line .. "\n")
    246      1.1    rillig 
    247      1.4    rillig 	if command == "input" and args ~= "" then
    248      1.4    rillig 		warn(lineno, "'//indent input' does not take arguments")
    249      1.4    rillig 	elseif command == "input" then
    250      1.2    rillig 		handle_indent_input()
    251      1.2    rillig 	elseif command == "run" then
    252      1.2    rillig 		handle_indent_run(args)
    253      1.2    rillig 	elseif command == "run-equals-input" then
    254      1.2    rillig 		handle_indent_run_equals_input(args)
    255      1.2    rillig 	elseif command == "run-equals-prev-output" then
    256      1.2    rillig 		handle_indent_run_equals_prev_output(args)
    257      1.2    rillig 	elseif command == "end" and args ~= "" then
    258      1.2    rillig 		warn(lineno, "'//indent end' does not take arguments")
    259      1.2    rillig 	elseif command == "end" and section == "input" then
    260      1.2    rillig 		handle_indent_end_input()
    261      1.2    rillig 	elseif command == "end" and section == "run" then
    262      1.2    rillig 		handle_indent_end_run()
    263      1.1    rillig 	elseif command == "end" then
    264      1.1    rillig 		warn(lineno, "misplaced '//indent end'")
    265      1.1    rillig 	else
    266  1.7.2.1  perseant 		err(lineno, "invalid line '" .. line .. "'")
    267      1.1    rillig 	end
    268      1.1    rillig 
    269      1.1    rillig 	prev_empty_lines = 0
    270      1.1    rillig 	curr_empty_lines = 0
    271      1.1    rillig end
    272      1.1    rillig 
    273      1.1    rillig local function handle_line(line)
    274      1.1    rillig 	if section == "" then
    275  1.7.2.1  perseant 		handle_line_outside_section(line)
    276      1.1    rillig 	end
    277      1.1    rillig 
    278      1.1    rillig 	-- Hide comments starting with dollar from indent; they are used for
    279      1.1    rillig 	-- marking bugs and adding other remarks directly in the input or
    280      1.1    rillig 	-- output sections.
    281      1.1    rillig 	if line:match("^%s*/[*]%s*[$].*[*]/$")
    282      1.1    rillig 	    or line:match("^%s*//%s*[$]") then
    283      1.1    rillig 		if section ~= "" then
    284      1.1    rillig 			section_incl_comm = section_incl_comm .. line .. "\n"
    285      1.1    rillig 		end
    286      1.1    rillig 		return
    287      1.1    rillig 	end
    288      1.1    rillig 
    289      1.1    rillig 	local cmd, args = line:match("^//indent%s+([^%s]+)%s*(.*)$")
    290      1.1    rillig 	if cmd then
    291      1.1    rillig 		handle_indent_directive(line, cmd, args)
    292      1.1    rillig 		return
    293      1.1    rillig 	end
    294      1.1    rillig 
    295      1.1    rillig 	if section == "input" or section == "run" then
    296      1.1    rillig 		section_excl_comm = section_excl_comm .. line .. "\n"
    297      1.1    rillig 		section_incl_comm = section_incl_comm .. line .. "\n"
    298      1.1    rillig 	end
    299      1.1    rillig 
    300      1.1    rillig 	if section == "run" then
    301      1.1    rillig 		expected_out:write(line .. "\n")
    302      1.1    rillig 	end
    303      1.1    rillig 
    304      1.1    rillig 	if section == ""
    305      1.1    rillig 	    and line ~= ""
    306      1.1    rillig 	    and line:sub(1, 1) ~= "/"
    307      1.1    rillig 	    and line:sub(1, 2) ~= " *" then
    308      1.1    rillig 		warn(lineno, "non-comment line outside 'input' or 'run' "
    309      1.1    rillig 		    .. "section")
    310      1.1    rillig 	end
    311      1.1    rillig end
    312      1.1    rillig 
    313      1.1    rillig local function handle_file(fname)
    314      1.1    rillig 	init_file(fname)
    315      1.1    rillig 	local f = assert(io.open(fname))
    316      1.1    rillig 	for line in f:lines() do
    317      1.1    rillig 		lineno = lineno + 1
    318      1.1    rillig 		handle_line(line)
    319      1.1    rillig 	end
    320      1.1    rillig 	f:close()
    321      1.1    rillig end
    322      1.1    rillig 
    323      1.1    rillig local function main()
    324      1.1    rillig 	for _, arg in ipairs(arg) do
    325      1.1    rillig 		handle_file(arg)
    326      1.1    rillig 	end
    327      1.1    rillig 	if section ~= "" then
    328  1.7.2.1  perseant 		err(lineno, "still in section '" .. section .. "'")
    329      1.1    rillig 	end
    330      1.1    rillig 	check_unused_input()
    331      1.1    rillig 	expected_out:close()
    332      1.1    rillig 	os.exit(not warned)
    333      1.1    rillig end
    334      1.1    rillig 
    335      1.1    rillig main()
    336