/* $NetBSD: parse.c,v 1.85 2025/01/07 03:55:00 rillig Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1985 Sun Microsystems, Inc. * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * All rights reserved. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 __RCSID("$NetBSD: parse.c,v 1.85 2025/01/07 03:55:00 rillig Exp $"); #include #include "indent.h" /* Replace the top 2 symbols with the given symbol. */ static void ps_psyms_replace2(parser_symbol psym) { ps.psyms.len--; ps.psyms.sym[ps.psyms.len - 1] = psym; ps.ind_level_follow = ps.psyms.ind_level[ps.psyms.len - 1]; } static int left_justify_decl_level(void) { int level = 0; for (size_t i = ps.psyms.len - 2; i > 0; i--) if (ps.psyms.sym[i] == psym_decl) level++; return level; } void ps_psyms_push(parser_symbol psym, int ind_level) { if (ps.psyms.len == ps.psyms.cap) { ps.psyms.cap += 16; ps.psyms.sym = nonnull(realloc(ps.psyms.sym, sizeof(ps.psyms.sym[0]) * ps.psyms.cap)); ps.psyms.ind_level = nonnull(realloc(ps.psyms.ind_level, sizeof(ps.psyms.ind_level[0]) * ps.psyms.cap)); } ps.psyms.len++; ps.psyms.sym[ps.psyms.len - 1] = psym; ps.psyms.ind_level[ps.psyms.len - 1] = ind_level; } /* * Repeatedly try to reduce the top two symbols on the parse stack to a single * symbol, until no more reductions are possible. */ static void psyms_reduce(void) { again: if (ps.psyms.len >= 2 && ps.psyms.sym[ps.psyms.len - 1] == psym_stmt) { switch (ps.psyms.sym[ps.psyms.len - 2]) { case psym_decl: case psym_stmt: case psym_for_exprs: case psym_if_expr_stmt_else: case psym_switch_expr: case psym_while_expr: ps_psyms_replace2(psym_stmt); goto again; case psym_if_expr: ps_psyms_replace2(psym_if_expr_stmt); goto again; case psym_do: ps_psyms_replace2(psym_do_stmt); goto again; default: return; } } if (ps.psyms.sym[ps.psyms.len - 1] == psym_while_expr && ps.psyms.sym[ps.psyms.len - 2] == psym_do_stmt) { ps.psyms.len -= 2; goto again; } } static bool is_lbrace(parser_symbol psym) { return psym == psym_lbrace_block || psym == psym_lbrace_struct || psym == psym_lbrace_union || psym == psym_lbrace_enum; } /* * Shift the token onto the parser stack, then try to reduce it by combining it with * previous tokens. */ void parse(parser_symbol psym) { debug_blank_line(); debug_println("parse: %s", psym_name[psym]); if (psym != psym_else) { while (ps.psyms.sym[ps.psyms.len - 1] == psym_if_expr_stmt) { ps.psyms.sym[ps.psyms.len - 1] = psym_stmt; psyms_reduce(); ps.ind_level = ps.ind_level_follow = ps.psyms.ind_level[ps.psyms.len - 1]; } } switch (psym) { case psym_lbrace_block: case psym_lbrace_struct: case psym_lbrace_union: case psym_lbrace_enum: ps.break_after_comma = false; if (ps.psyms.sym[ps.psyms.len - 1] == psym_decl || ps.psyms.sym[ps.psyms.len - 1] == psym_stmt) ps.ind_level_follow++; else if (code.len == 0) { /* It is part of a while, for, etc. */ ps.ind_level--; /* for a switch, brace should be two levels out from * the code */ if (ps.psyms.sym[ps.psyms.len - 1] == psym_switch_expr && opt.case_indent >= 1.0F) ps.ind_level--; } ps_psyms_push(psym, ps.ind_level); ps_psyms_push(psym_stmt, ps.ind_level_follow); break; case psym_rbrace: /* stack should have or */ if (!(ps.psyms.len >= 2 && is_lbrace(ps.psyms.sym[ps.psyms.len - 2]))) { diag(1, "Statement nesting error"); break; } ps_psyms_replace2(psym_stmt); ps.ind_level = ps.ind_level_follow; break; case psym_decl: if (ps.psyms.sym[ps.psyms.len - 1] == psym_decl) break; /* only put one declaration onto stack */ ps.break_after_comma = true; ps_psyms_push(psym_decl, ps.ind_level_follow); if (opt.left_justify_decl) { ps.ind_level = left_justify_decl_level(); ps.ind_level_follow = ps.ind_level; } break; case psym_stmt: ps.break_after_comma = false; ps_psyms_push(psym_stmt, ps.ind_level); break; case psym_if_expr: if (ps.psyms.sym[ps.psyms.len - 1] == psym_if_expr_stmt_else && opt.else_if_in_same_line) { ps.ind_level_follow = ps.psyms.ind_level[ps.psyms.len - 1]; ps.psyms.len--; } /* FALLTHROUGH */ case psym_do: case psym_for_exprs: ps.ind_level = ps.ind_level_follow; ps.ind_level_follow = ps.ind_level + 1; ps_psyms_push(psym, ps.ind_level); break; case psym_else: if (ps.psyms.sym[ps.psyms.len - 1] != psym_if_expr_stmt) { diag(1, "Unmatched 'else'"); break; } ps.ind_level = ps.psyms.ind_level[ps.psyms.len - 1]; ps.ind_level_follow = ps.ind_level + 1; ps.psyms.sym[ps.psyms.len - 1] = psym_if_expr_stmt_else; break; case psym_switch_expr: ps_psyms_push(psym_switch_expr, ps.ind_level_follow); ps.ind_level_follow += (int)opt.case_indent + 1; break; case psym_while_expr: if (ps.psyms.sym[ps.psyms.len - 1] == psym_do_stmt) { ps.ind_level = ps.psyms.ind_level[ps.psyms.len - 1]; ps.ind_level_follow = ps.ind_level; ps_psyms_push(psym_while_expr, ps.ind_level); } else { ps_psyms_push(psym_while_expr, ps.ind_level_follow); ps.ind_level_follow++; } break; default: diag(1, "Unknown code to parser"); return; } #if debug static struct buffer before, after; buf_clear(&before); ps_psyms_to_string(&before, &ps); #endif psyms_reduce(); #if debug buf_clear(&after); ps_psyms_to_string(&after, &ps); if (strcmp(before.s, after.s) != 0) { debug_println("psyms before: %s", before.s); debug_println("psyms after: %s", after.s); } else debug_println("psyms: %s", after.s); #endif }