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