403 lines
9.1 KiB
C
403 lines
9.1 KiB
C
/* $NetBSD: debug.c,v 1.70 2023/06/27 04:41:23 rillig Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2023 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Roland Illig <rillig@NetBSD.org>.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__RCSID("$NetBSD: debug.c,v 1.70 2023/06/27 04:41:23 rillig Exp $");
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "indent.h"
|
|
|
|
#ifdef debug
|
|
|
|
static struct {
|
|
// false show only the changes to the parser state
|
|
// true show unchanged parts of the parser state as well
|
|
bool full_parser_state;
|
|
} config = {
|
|
.full_parser_state = false,
|
|
};
|
|
|
|
const char *const lsym_name[] = {
|
|
"eof",
|
|
"preprocessing",
|
|
"newline",
|
|
"comment",
|
|
"lparen",
|
|
"rparen",
|
|
"lbracket",
|
|
"rbracket",
|
|
"lbrace",
|
|
"rbrace",
|
|
"period",
|
|
"unary_op",
|
|
"sizeof",
|
|
"offsetof",
|
|
"postfix_op",
|
|
"binary_op",
|
|
"question",
|
|
"question_colon",
|
|
"comma",
|
|
"typedef",
|
|
"modifier",
|
|
"tag",
|
|
"type",
|
|
"word",
|
|
"funcname",
|
|
"label_colon",
|
|
"other_colon",
|
|
"semicolon",
|
|
"case",
|
|
"default",
|
|
"do",
|
|
"else",
|
|
"for",
|
|
"if",
|
|
"switch",
|
|
"while",
|
|
"return",
|
|
};
|
|
|
|
const char *const psym_name[] = {
|
|
"-",
|
|
"{block",
|
|
"{struct",
|
|
"{union",
|
|
"{enum",
|
|
"}",
|
|
"decl",
|
|
"stmt",
|
|
"for_exprs",
|
|
"if_expr",
|
|
"if_expr_stmt",
|
|
"if_expr_stmt_else",
|
|
"else",
|
|
"switch_expr",
|
|
"do",
|
|
"do_stmt",
|
|
"while_expr",
|
|
};
|
|
|
|
static const char *const declaration_name[] = {
|
|
"no",
|
|
"begin",
|
|
"end",
|
|
};
|
|
|
|
static const char *const badp_name[] = {
|
|
"none",
|
|
"seen{",
|
|
"decl",
|
|
"seen_decl",
|
|
"yes",
|
|
};
|
|
|
|
const char *const paren_level_cast_name[] = {
|
|
"(unknown cast)",
|
|
"(maybe cast)",
|
|
"(no cast)",
|
|
};
|
|
|
|
const char *const line_kind_name[] = {
|
|
"other",
|
|
"blank",
|
|
"#if",
|
|
"#endif",
|
|
"#other",
|
|
"stmt head",
|
|
"}",
|
|
"block comment",
|
|
"case/default",
|
|
};
|
|
|
|
static const char *const extra_expr_indent_name[] = {
|
|
"no",
|
|
"maybe",
|
|
"last",
|
|
};
|
|
|
|
static struct {
|
|
struct parser_state prev_ps;
|
|
bool ps_first;
|
|
const char *heading;
|
|
unsigned wrote_newlines;
|
|
} state = {
|
|
.ps_first = true,
|
|
.wrote_newlines = 1,
|
|
};
|
|
|
|
void
|
|
debug_printf(const char *fmt, ...)
|
|
{
|
|
FILE *f = output == stdout ? stderr : stdout;
|
|
va_list ap;
|
|
|
|
if (state.heading != NULL) {
|
|
fprintf(f, "%s\n", state.heading);
|
|
state.heading = NULL;
|
|
}
|
|
va_start(ap, fmt);
|
|
vfprintf(f, fmt, ap);
|
|
va_end(ap);
|
|
state.wrote_newlines = 0;
|
|
}
|
|
|
|
void
|
|
debug_println(const char *fmt, ...)
|
|
{
|
|
FILE *f = output == stdout ? stderr : stdout;
|
|
va_list ap;
|
|
|
|
if (state.heading != NULL) {
|
|
fprintf(f, "%s\n", state.heading);
|
|
state.heading = NULL;
|
|
state.wrote_newlines = 1;
|
|
}
|
|
va_start(ap, fmt);
|
|
vfprintf(f, fmt, ap);
|
|
va_end(ap);
|
|
fprintf(f, "\n");
|
|
state.wrote_newlines = fmt[0] == '\0' ? state.wrote_newlines + 1 : 1;
|
|
}
|
|
|
|
void
|
|
debug_blank_line(void)
|
|
{
|
|
while (state.wrote_newlines < 2)
|
|
debug_println("");
|
|
}
|
|
|
|
void
|
|
debug_vis_range(const char *s, size_t len)
|
|
{
|
|
debug_printf("\"");
|
|
for (size_t i = 0; i < len; i++) {
|
|
const char *p = s + i;
|
|
if (*p == '\\' || *p == '"')
|
|
debug_printf("\\%c", *p);
|
|
else if (isprint((unsigned char)*p))
|
|
debug_printf("%c", *p);
|
|
else if (*p == '\n')
|
|
debug_printf("\\n");
|
|
else if (*p == '\t')
|
|
debug_printf("\\t");
|
|
else
|
|
debug_printf("\\x%02x", (unsigned char)*p);
|
|
}
|
|
debug_printf("\"");
|
|
}
|
|
|
|
void
|
|
debug_print_buf(const char *name, const struct buffer *buf)
|
|
{
|
|
if (buf->len > 0) {
|
|
debug_printf(" %s ", name);
|
|
debug_vis_range(buf->s, buf->len);
|
|
}
|
|
}
|
|
|
|
void
|
|
debug_buffers(void)
|
|
{
|
|
debug_print_buf("label", &lab);
|
|
debug_print_buf("code", &code);
|
|
debug_print_buf("comment", &com);
|
|
debug_blank_line();
|
|
}
|
|
|
|
static void
|
|
debug_ps_bool_member(const char *name, bool prev, bool curr)
|
|
{
|
|
if (!state.ps_first && curr != prev) {
|
|
char diff = " -+x"[(prev ? 1 : 0) + (curr ? 2 : 0)];
|
|
debug_println(" [%c] ps.%s", diff, name);
|
|
} else if (config.full_parser_state || state.ps_first)
|
|
debug_println(" [%c] ps.%s", curr ? 'x' : ' ', name);
|
|
}
|
|
|
|
static void
|
|
debug_ps_int_member(const char *name, int prev, int curr)
|
|
{
|
|
if (!state.ps_first && curr != prev)
|
|
debug_println(" %3d -> %3d ps.%s", prev, curr, name);
|
|
else if (config.full_parser_state || state.ps_first)
|
|
debug_println(" %3d ps.%s", curr, name);
|
|
}
|
|
|
|
static void
|
|
debug_ps_enum_member(const char *name, const char *prev, const char *curr)
|
|
{
|
|
if (!state.ps_first && strcmp(prev, curr) != 0)
|
|
debug_println(" %3s -> %3s ps.%s", prev, curr, name);
|
|
else if (config.full_parser_state || state.ps_first)
|
|
debug_println(" %10s ps.%s", curr, name);
|
|
}
|
|
|
|
static bool
|
|
paren_stack_equal(const struct paren_stack *a, const struct paren_stack *b)
|
|
{
|
|
if (a->len != b->len)
|
|
return false;
|
|
|
|
for (size_t i = 0, n = a->len; i < n; i++)
|
|
if (a->item[i].indent != b->item[i].indent
|
|
|| a->item[i].cast != b->item[i].cast)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
debug_ps_paren(void)
|
|
{
|
|
if (!config.full_parser_state
|
|
&& paren_stack_equal(&state.prev_ps.paren, &ps.paren)
|
|
&& !state.ps_first)
|
|
return;
|
|
|
|
debug_printf(" ps.paren:");
|
|
for (size_t i = 0; i < ps.paren.len; i++) {
|
|
debug_printf(" %s%d",
|
|
paren_level_cast_name[ps.paren.item[i].cast],
|
|
ps.paren.item[i].indent);
|
|
}
|
|
if (ps.paren.len == 0)
|
|
debug_printf(" none");
|
|
debug_println("");
|
|
}
|
|
|
|
static bool
|
|
ps_di_stack_has_changed(void)
|
|
{
|
|
if (state.prev_ps.decl_level != ps.decl_level)
|
|
return true;
|
|
for (int i = 0; i < ps.decl_level; i++)
|
|
if (state.prev_ps.di_stack[i] != ps.di_stack[i])
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
debug_ps_di_stack(void)
|
|
{
|
|
bool changed = ps_di_stack_has_changed();
|
|
if (!config.full_parser_state && !changed && !state.ps_first)
|
|
return;
|
|
|
|
debug_printf(" %s ps.di_stack:", changed ? "->" : " ");
|
|
for (int i = 0; i < ps.decl_level; i++)
|
|
debug_printf(" %d", ps.di_stack[i]);
|
|
if (ps.decl_level == 0)
|
|
debug_printf(" none");
|
|
debug_println("");
|
|
}
|
|
|
|
#define debug_ps_bool(name) \
|
|
debug_ps_bool_member(#name, state.prev_ps.name, ps.name)
|
|
#define debug_ps_int(name) \
|
|
debug_ps_int_member(#name, state.prev_ps.name, ps.name)
|
|
#define debug_ps_enum(name, names) \
|
|
debug_ps_enum_member(#name, (names)[state.prev_ps.name], \
|
|
(names)[ps.name])
|
|
|
|
void
|
|
debug_parser_state(void)
|
|
{
|
|
debug_blank_line();
|
|
|
|
state.heading = "token classification";
|
|
debug_ps_enum(prev_lsym, lsym_name);
|
|
debug_ps_bool(in_stmt_or_decl);
|
|
debug_ps_bool(in_decl);
|
|
debug_ps_bool(in_typedef_decl);
|
|
debug_ps_bool(in_var_decl);
|
|
debug_ps_bool(in_init);
|
|
debug_ps_int(init_level);
|
|
debug_ps_bool(line_has_func_def);
|
|
debug_ps_bool(in_func_def_params);
|
|
debug_ps_bool(line_has_decl);
|
|
debug_ps_enum(lbrace_kind, psym_name);
|
|
debug_ps_enum(spaced_expr_psym, psym_name);
|
|
debug_ps_bool(seen_case);
|
|
debug_ps_bool(prev_paren_was_cast);
|
|
debug_ps_int(quest_level);
|
|
|
|
state.heading = "indentation of statements and declarations";
|
|
debug_ps_int(ind_level);
|
|
debug_ps_int(ind_level_follow);
|
|
debug_ps_bool(line_is_stmt_cont);
|
|
debug_ps_int(decl_level);
|
|
debug_ps_di_stack();
|
|
debug_ps_bool(decl_indent_done);
|
|
debug_ps_int(decl_ind);
|
|
debug_ps_bool(tabs_to_var);
|
|
debug_ps_enum(extra_expr_indent, extra_expr_indent_name);
|
|
|
|
// The parser symbol stack is printed in debug_psyms_stack instead.
|
|
|
|
state.heading = "spacing inside a statement or declaration";
|
|
debug_ps_bool(next_unary);
|
|
debug_ps_bool(want_blank);
|
|
debug_ps_int(ind_paren_level);
|
|
debug_ps_paren();
|
|
|
|
state.heading = "indentation of comments";
|
|
debug_ps_int(comment_ind);
|
|
debug_ps_int(comment_shift);
|
|
debug_ps_bool(comment_cont);
|
|
|
|
state.heading = "vertical spacing";
|
|
debug_ps_bool(break_after_comma);
|
|
debug_ps_bool(want_newline);
|
|
debug_ps_enum(declaration, declaration_name);
|
|
debug_ps_bool(blank_line_after_decl);
|
|
debug_ps_enum(badp, badp_name);
|
|
|
|
state.heading = NULL;
|
|
debug_blank_line();
|
|
|
|
parser_state_free(&state.prev_ps);
|
|
parser_state_back_up(&state.prev_ps);
|
|
state.ps_first = false;
|
|
}
|
|
|
|
void
|
|
debug_psyms_stack(const char *situation)
|
|
{
|
|
debug_printf("parse stack %s:", situation);
|
|
const struct psym_stack *psyms = &ps.psyms;
|
|
for (size_t i = 0; i < psyms->len; i++)
|
|
debug_printf(" %d %s",
|
|
psyms->ind_level[i], psym_name[psyms->sym[i]]);
|
|
debug_println("");
|
|
}
|
|
#endif
|