debug.c revision 1.56
1/*	$NetBSD: debug.c,v 1.56 2023/06/14 08:36:51 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.56 2023/06/14 08:36:51 rillig Exp $");
34
35#include <stdarg.h>
36#include <string.h>
37
38#include "indent.h"
39
40#ifdef debug
41
42static struct {
43	/*-
44	 * false	show only the changes to the parser state
45	 * true		show unchanged parts of the parser state as well
46	 */
47	bool full_parser_state;
48} config = {
49	.full_parser_state = false,
50};
51
52const char *const lsym_name[] = {
53	"eof",
54	"preprocessing",
55	"newline",
56	"comment",
57	"lparen",
58	"rparen",
59	"lbracket",
60	"rbracket",
61	"lbrace",
62	"rbrace",
63	"period",
64	"unary_op",
65	"sizeof",
66	"offsetof",
67	"postfix_op",
68	"binary_op",
69	"question",
70	"question_colon",
71	"comma",
72	"typedef",
73	"modifier",
74	"tag",
75	"type_outside_parentheses",
76	"type_in_parentheses",
77	"word",
78	"funcname",
79	"label_colon",
80	"other_colon",
81	"semicolon",
82	"case",
83	"default",
84	"do",
85	"else",
86	"for",
87	"if",
88	"switch",
89	"while",
90	"return",
91};
92
93const char *const psym_name[] = {
94	"-",
95	"{block",
96	"{struct",
97	"{union",
98	"{enum",
99	"}",
100	"decl",
101	"stmt",
102	"for_exprs",
103	"if_expr",
104	"if_expr_stmt",
105	"if_expr_stmt_else",
106	"else",
107	"switch_expr",
108	"do",
109	"do_stmt",
110	"while_expr",
111};
112
113static const char *const declaration_name[] = {
114	"no",
115	"begin",
116	"end",
117};
118
119const char *const paren_level_cast_name[] = {
120	"(unknown cast)",
121	"(maybe cast)",
122	"(no cast)",
123};
124
125const char *const line_kind_name[] = {
126	"other",
127	"blank",
128	"#if",
129	"#endif",
130	"stmt head",
131	"}",
132	"block comment",
133	"case/default",
134};
135
136static const char *const extra_expr_indent_name[] = {
137	"no",
138	"maybe",
139	"last",
140};
141
142static struct {
143	struct parser_state prev_ps;
144	bool ps_first;
145	const char *heading;
146	unsigned wrote_newlines;
147} state = {
148	.ps_first = true,
149	.wrote_newlines = 1,
150};
151
152void
153debug_printf(const char *fmt, ...)
154{
155	FILE *f = output == stdout ? stderr : stdout;
156	va_list ap;
157
158	if (state.heading != NULL) {
159		fprintf(f, "%s\n", state.heading);
160		state.heading = NULL;
161	}
162	va_start(ap, fmt);
163	vfprintf(f, fmt, ap);
164	va_end(ap);
165	state.wrote_newlines = 0;
166}
167
168void
169debug_println(const char *fmt, ...)
170{
171	FILE *f = output == stdout ? stderr : stdout;
172	va_list ap;
173
174	if (state.heading != NULL) {
175		fprintf(f, "%s\n", state.heading);
176		state.heading = NULL;
177		state.wrote_newlines = 1;
178	}
179	va_start(ap, fmt);
180	vfprintf(f, fmt, ap);
181	va_end(ap);
182	fprintf(f, "\n");
183	state.wrote_newlines = fmt[0] == '\0' ? state.wrote_newlines + 1 : 1;
184}
185
186void
187debug_blank_line(void)
188{
189	while (state.wrote_newlines < 2)
190		debug_println("");
191}
192
193void
194debug_vis_range(const char *prefix, const char *s, size_t len,
195    const char *suffix)
196{
197	debug_printf("%s", prefix);
198	for (size_t i = 0; i < len; i++) {
199		const char *p = s + i;
200		if (*p == '\\' || *p == '"')
201			debug_printf("\\%c", *p);
202		else if (isprint((unsigned char)*p))
203			debug_printf("%c", *p);
204		else if (*p == '\n')
205			debug_printf("\\n");
206		else if (*p == '\t')
207			debug_printf("\\t");
208		else
209			debug_printf("\\x%02x", (unsigned char)*p);
210	}
211	debug_printf("%s", suffix);
212}
213
214void
215debug_print_buf(const char *name, const struct buffer *buf)
216{
217	if (buf->len > 0) {
218		debug_printf(" %s ", name);
219		debug_vis_range("\"", buf->s, buf->len, "\"");
220	}
221}
222
223void
224debug_buffers(void)
225{
226	debug_print_buf("label", &lab);
227	debug_print_buf("code", &code);
228	debug_print_buf("comment", &com);
229	debug_blank_line();
230}
231
232static void
233write_ps_bool(const char *name, bool prev, bool curr)
234{
235	if (!state.ps_first && curr != prev) {
236		char diff = " -+x"[(prev ? 1 : 0) + (curr ? 2 : 0)];
237		debug_println("        [%c]  ps.%s", diff, name);
238	} else if (config.full_parser_state || state.ps_first)
239		debug_println("        [%c]  ps.%s", curr ? 'x' : ' ', name);
240}
241
242static void
243write_ps_int(const char *name, int prev, int curr)
244{
245	if (!state.ps_first && curr != prev)
246		debug_println(" %3d -> %3d  ps.%s", prev, curr, name);
247	else if (config.full_parser_state || state.ps_first)
248		debug_println("        %3d  ps.%s", curr, name);
249}
250
251static void
252write_ps_enum(const char *name, const char *prev, const char *curr)
253{
254	if (!state.ps_first && strcmp(prev, curr) != 0)
255		debug_println(" %3s -> %3s  ps.%s", prev, curr, name);
256	else if (config.full_parser_state || state.ps_first)
257		debug_println(" %10s  ps.%s", curr, name);
258}
259
260static bool
261ps_paren_has_changed(void)
262{
263	if (state.prev_ps.nparen != ps.nparen)
264		return true;
265
266	const struct paren_level *prev = state.prev_ps.paren, *curr = ps.paren;
267	for (int i = 0; i < ps.nparen; i++)
268		if (curr[i].indent != prev[i].indent
269		    || curr[i].cast != prev[i].cast)
270			return true;
271	return false;
272}
273
274static void
275debug_ps_paren(void)
276{
277	if (!config.full_parser_state && !ps_paren_has_changed()
278	    && !state.ps_first)
279		return;
280
281	debug_printf("             ps.paren:");
282	for (int i = 0; i < ps.nparen; i++) {
283		debug_printf(" %s%d",
284		    paren_level_cast_name[ps.paren[i].cast],
285		    ps.paren[i].indent);
286	}
287	if (ps.nparen == 0)
288		debug_printf(" none");
289	debug_println("");
290}
291
292static bool
293ps_di_stack_has_changed(void)
294{
295	if (state.prev_ps.decl_level != ps.decl_level)
296		return true;
297	for (int i = 0; i < ps.decl_level; i++)
298		if (state.prev_ps.di_stack[i] != ps.di_stack[i])
299			return true;
300	return false;
301}
302
303static void
304debug_ps_di_stack(void)
305{
306	bool changed = ps_di_stack_has_changed();
307	if (!config.full_parser_state && !changed && !state.ps_first)
308		return;
309
310	debug_printf("     %s      ps.di_stack:", changed ? "->" : "  ");
311	for (int i = 0; i < ps.decl_level; i++)
312		debug_printf(" %d", ps.di_stack[i]);
313	if (ps.decl_level == 0)
314		debug_printf(" none");
315	debug_println("");
316}
317
318#define debug_ps_bool(name) \
319	write_ps_bool(#name, state.prev_ps.name, ps.name)
320#define debug_ps_int(name) \
321	write_ps_int(#name, state.prev_ps.name, ps.name)
322#define debug_ps_enum(name, names) \
323        write_ps_enum(#name, (names)[state.prev_ps.name], (names)[ps.name])
324
325void
326debug_parser_state(void)
327{
328	debug_blank_line();
329
330	state.heading = "token classification";
331	debug_ps_enum(prev_lsym, lsym_name);
332	debug_ps_bool(in_stmt_or_decl);
333	debug_ps_bool(in_decl);
334	debug_ps_bool(in_var_decl);
335	debug_ps_bool(in_init);
336	debug_ps_int(init_level);
337	debug_ps_bool(line_has_func_def);
338	debug_ps_bool(in_func_def_params);
339	debug_ps_bool(line_has_decl);
340	debug_ps_enum(lbrace_kind, psym_name);
341	debug_ps_enum(spaced_expr_psym, psym_name);
342	debug_ps_bool(seen_case);
343	debug_ps_bool(prev_paren_was_cast);
344	debug_ps_int(quest_level);
345
346	state.heading = "indentation of statements and declarations";
347	debug_ps_int(ind_level);
348	debug_ps_int(ind_level_follow);
349	debug_ps_bool(in_stmt_cont);
350	debug_ps_int(decl_level);
351	debug_ps_di_stack();
352	debug_ps_bool(decl_indent_done);
353	debug_ps_int(decl_ind);
354	debug_ps_bool(tabs_to_var);
355	debug_ps_enum(extra_expr_indent, extra_expr_indent_name);
356
357	// The parser symbol stack is printed in debug_psyms_stack instead.
358
359	state.heading = "spacing inside a statement or declaration";
360	debug_ps_bool(next_unary);
361	debug_ps_bool(want_blank);
362	debug_ps_int(ind_paren_level);
363	debug_ps_int(nparen);
364	debug_ps_paren();
365
366	state.heading = "horizontal spacing for comments";
367	debug_ps_int(comment_delta);
368	debug_ps_int(n_comment_delta);
369	debug_ps_int(com_ind);
370
371	state.heading = "vertical spacing";
372	debug_ps_bool(break_after_comma);
373	debug_ps_bool(force_nl);
374	debug_ps_enum(declaration, declaration_name);
375	debug_ps_bool(blank_line_after_decl);
376
377	state.heading = NULL;
378	debug_blank_line();
379
380	state.prev_ps = ps;
381	state.ps_first = false;
382}
383
384void
385debug_psyms_stack(const char *situation)
386{
387	debug_printf("parse stack %s:", situation);
388	const struct psym_stack *psyms = &ps.psyms;
389	for (int i = 0; i <= psyms->top; ++i)
390		debug_printf(" %d %s",
391		    psyms->ind_level[i], psym_name[psyms->sym[i]]);
392	debug_println("");
393}
394#endif
395