debug.c revision 1.61
1/*	$NetBSD: debug.c,v 1.61 2023/06/14 20:46:08 rillig Exp $	*/
2
3/*-
4 * Copyright (c) 2023 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Roland Illig <rillig@NetBSD.org>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__RCSID("$NetBSD: debug.c,v 1.61 2023/06/14 20:46:08 rillig Exp $");
34
35#include <stdarg.h>
36#include <string.h>
37
38#include "indent.h"
39
40#ifdef debug
41
42static struct {
43	// false	show only the changes to the parser state
44	// true		show unchanged parts of the parser state as well
45	bool full_parser_state;
46} config = {
47	.full_parser_state = false,
48};
49
50const char *const lsym_name[] = {
51	"eof",
52	"preprocessing",
53	"newline",
54	"comment",
55	"lparen",
56	"rparen",
57	"lbracket",
58	"rbracket",
59	"lbrace",
60	"rbrace",
61	"period",
62	"unary_op",
63	"sizeof",
64	"offsetof",
65	"postfix_op",
66	"binary_op",
67	"question",
68	"question_colon",
69	"comma",
70	"typedef",
71	"modifier",
72	"tag",
73	"type_outside_parentheses",
74	"type_in_parentheses",
75	"word",
76	"funcname",
77	"label_colon",
78	"other_colon",
79	"semicolon",
80	"case",
81	"default",
82	"do",
83	"else",
84	"for",
85	"if",
86	"switch",
87	"while",
88	"return",
89};
90
91const char *const psym_name[] = {
92	"-",
93	"{block",
94	"{struct",
95	"{union",
96	"{enum",
97	"}",
98	"decl",
99	"stmt",
100	"for_exprs",
101	"if_expr",
102	"if_expr_stmt",
103	"if_expr_stmt_else",
104	"else",
105	"switch_expr",
106	"do",
107	"do_stmt",
108	"while_expr",
109};
110
111static const char *const declaration_name[] = {
112	"no",
113	"begin",
114	"end",
115};
116
117const char *const paren_level_cast_name[] = {
118	"(unknown cast)",
119	"(maybe cast)",
120	"(no cast)",
121};
122
123const char *const line_kind_name[] = {
124	"other",
125	"blank",
126	"#if",
127	"#endif",
128	"stmt head",
129	"}",
130	"block comment",
131	"case/default",
132};
133
134static const char *const extra_expr_indent_name[] = {
135	"no",
136	"maybe",
137	"last",
138};
139
140static struct {
141	struct parser_state prev_ps;
142	bool ps_first;
143	const char *heading;
144	unsigned wrote_newlines;
145} state = {
146	.ps_first = true,
147	.wrote_newlines = 1,
148};
149
150void
151debug_printf(const char *fmt, ...)
152{
153	FILE *f = output == stdout ? stderr : stdout;
154	va_list ap;
155
156	if (state.heading != NULL) {
157		fprintf(f, "%s\n", state.heading);
158		state.heading = NULL;
159	}
160	va_start(ap, fmt);
161	vfprintf(f, fmt, ap);
162	va_end(ap);
163	state.wrote_newlines = 0;
164}
165
166void
167debug_println(const char *fmt, ...)
168{
169	FILE *f = output == stdout ? stderr : stdout;
170	va_list ap;
171
172	if (state.heading != NULL) {
173		fprintf(f, "%s\n", state.heading);
174		state.heading = NULL;
175		state.wrote_newlines = 1;
176	}
177	va_start(ap, fmt);
178	vfprintf(f, fmt, ap);
179	va_end(ap);
180	fprintf(f, "\n");
181	state.wrote_newlines = fmt[0] == '\0' ? state.wrote_newlines + 1 : 1;
182}
183
184void
185debug_blank_line(void)
186{
187	while (state.wrote_newlines < 2)
188		debug_println("");
189}
190
191void
192debug_vis_range(const char *s, size_t len)
193{
194	debug_printf("\"");
195	for (size_t i = 0; i < len; i++) {
196		const char *p = s + i;
197		if (*p == '\\' || *p == '"')
198			debug_printf("\\%c", *p);
199		else if (isprint((unsigned char)*p))
200			debug_printf("%c", *p);
201		else if (*p == '\n')
202			debug_printf("\\n");
203		else if (*p == '\t')
204			debug_printf("\\t");
205		else
206			debug_printf("\\x%02x", (unsigned char)*p);
207	}
208	debug_printf("\"");
209}
210
211void
212debug_print_buf(const char *name, const struct buffer *buf)
213{
214	if (buf->len > 0) {
215		debug_printf(" %s ", name);
216		debug_vis_range(buf->s, buf->len);
217	}
218}
219
220void
221debug_buffers(void)
222{
223	debug_print_buf("label", &lab);
224	debug_print_buf("code", &code);
225	debug_print_buf("comment", &com);
226	debug_blank_line();
227}
228
229static void
230debug_ps_bool_member(const char *name, bool prev, bool curr)
231{
232	if (!state.ps_first && curr != prev) {
233		char diff = " -+x"[(prev ? 1 : 0) + (curr ? 2 : 0)];
234		debug_println("        [%c]  ps.%s", diff, name);
235	} else if (config.full_parser_state || state.ps_first)
236		debug_println("        [%c]  ps.%s", curr ? 'x' : ' ', name);
237}
238
239static void
240debug_ps_int_member(const char *name, int prev, int curr)
241{
242	if (!state.ps_first && curr != prev)
243		debug_println(" %3d -> %3d  ps.%s", prev, curr, name);
244	else if (config.full_parser_state || state.ps_first)
245		debug_println("        %3d  ps.%s", curr, name);
246}
247
248static void
249debug_ps_enum_member(const char *name, const char *prev, const char *curr)
250{
251	if (!state.ps_first && strcmp(prev, curr) != 0)
252		debug_println(" %3s -> %3s  ps.%s", prev, curr, name);
253	else if (config.full_parser_state || state.ps_first)
254		debug_println(" %10s  ps.%s", curr, name);
255}
256
257static bool
258paren_stack_equal(const struct paren_stack *a, const struct paren_stack *b)
259{
260	if (a->len != b->len)
261		return false;
262
263	for (size_t i = 0, n = a->len; i < n; i++)
264		if (a->item[i].indent != b->item[i].indent
265		    || a->item[i].cast != b->item[i].cast)
266			return true;
267	return false;
268}
269
270static void
271debug_ps_paren(void)
272{
273	if (!config.full_parser_state
274	    && paren_stack_equal(&state.prev_ps.paren, &ps.paren)
275	    && !state.ps_first)
276		return;
277
278	debug_printf("             ps.paren:");
279	for (size_t i = 0; i < ps.paren.len; i++) {
280		debug_printf(" %s%d",
281		    paren_level_cast_name[ps.paren.item[i].cast],
282		    ps.paren.item[i].indent);
283	}
284	if (ps.paren.len == 0)
285		debug_printf(" none");
286	debug_println("");
287}
288
289static bool
290ps_di_stack_has_changed(void)
291{
292	if (state.prev_ps.decl_level != ps.decl_level)
293		return true;
294	for (int i = 0; i < ps.decl_level; i++)
295		if (state.prev_ps.di_stack[i] != ps.di_stack[i])
296			return true;
297	return false;
298}
299
300static void
301debug_ps_di_stack(void)
302{
303	bool changed = ps_di_stack_has_changed();
304	if (!config.full_parser_state && !changed && !state.ps_first)
305		return;
306
307	debug_printf("     %s      ps.di_stack:", changed ? "->" : "  ");
308	for (int i = 0; i < ps.decl_level; i++)
309		debug_printf(" %d", ps.di_stack[i]);
310	if (ps.decl_level == 0)
311		debug_printf(" none");
312	debug_println("");
313}
314
315#define debug_ps_bool(name) \
316	debug_ps_bool_member(#name, state.prev_ps.name, ps.name)
317#define debug_ps_int(name) \
318	debug_ps_int_member(#name, state.prev_ps.name, ps.name)
319#define debug_ps_enum(name, names) \
320        debug_ps_enum_member(#name, (names)[state.prev_ps.name], \
321	    (names)[ps.name])
322
323void
324debug_parser_state(void)
325{
326	debug_blank_line();
327
328	state.heading = "token classification";
329	debug_ps_enum(prev_lsym, lsym_name);
330	debug_ps_bool(in_stmt_or_decl);
331	debug_ps_bool(in_decl);
332	debug_ps_bool(in_var_decl);
333	debug_ps_bool(in_init);
334	debug_ps_int(init_level);
335	debug_ps_bool(line_has_func_def);
336	debug_ps_bool(in_func_def_params);
337	debug_ps_bool(line_has_decl);
338	debug_ps_enum(lbrace_kind, psym_name);
339	debug_ps_enum(spaced_expr_psym, psym_name);
340	debug_ps_bool(seen_case);
341	debug_ps_bool(prev_paren_was_cast);
342	debug_ps_int(quest_level);
343
344	state.heading = "indentation of statements and declarations";
345	debug_ps_int(ind_level);
346	debug_ps_int(ind_level_follow);
347	debug_ps_bool(in_stmt_cont);
348	debug_ps_int(decl_level);
349	debug_ps_di_stack();
350	debug_ps_bool(decl_indent_done);
351	debug_ps_int(decl_ind);
352	debug_ps_bool(tabs_to_var);
353	debug_ps_enum(extra_expr_indent, extra_expr_indent_name);
354
355	// The parser symbol stack is printed in debug_psyms_stack instead.
356
357	state.heading = "spacing inside a statement or declaration";
358	debug_ps_bool(next_unary);
359	debug_ps_bool(want_blank);
360	debug_ps_int(ind_paren_level);
361	debug_ps_paren();
362
363	state.heading = "indentation of comments";
364	debug_ps_int(comment_ind);
365	debug_ps_int(comment_shift);
366	debug_ps_bool(comment_in_first_line);
367
368	state.heading = "vertical spacing";
369	debug_ps_bool(break_after_comma);
370	debug_ps_bool(want_newline);
371	debug_ps_enum(declaration, declaration_name);
372	debug_ps_bool(blank_line_after_decl);
373
374	state.heading = NULL;
375	debug_blank_line();
376
377	state.prev_ps = ps;
378	state.ps_first = false;
379}
380
381void
382debug_psyms_stack(const char *situation)
383{
384	debug_printf("parse stack %s:", situation);
385	const struct psym_stack *psyms = &ps.psyms;
386	for (size_t i = 0; i < psyms->len; ++i)
387		debug_printf(" %d %s",
388		    psyms->ind_level[i], psym_name[psyms->sym[i]]);
389	debug_println("");
390}
391#endif
392