1 /* $NetBSD: parse.c,v 1.62 2023/05/23 12:12:29 rillig Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-4-Clause 5 * 6 * Copyright (c) 1985 Sun Microsystems, Inc. 7 * Copyright (c) 1980, 1993 8 * The Regents of the University of California. All rights reserved. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #include <sys/cdefs.h> 41 __RCSID("$NetBSD: parse.c,v 1.62 2023/05/23 12:12:29 rillig Exp $"); 42 43 #include <err.h> 44 45 #include "indent.h" 46 47 static void reduce(void); 48 49 static int 50 decl_level(void) 51 { 52 int level = 0; 53 for (int i = ps.tos - 1; i > 0; i--) 54 if (ps.s_sym[i] == psym_decl) 55 level++; 56 return level; 57 } 58 59 /* 60 * Shift the token onto the parser stack, or reduce it by combining it with 61 * previous tokens. 62 */ 63 void 64 parse(parser_symbol psym) 65 { 66 debug_blank_line(); 67 debug_println("parse token: %s", psym_name[psym]); 68 69 if (psym != psym_else) { 70 while (ps.s_sym[ps.tos] == psym_if_expr_stmt) { 71 ps.s_sym[ps.tos] = psym_stmt; 72 reduce(); 73 } 74 } 75 76 switch (psym) { 77 78 case psym_decl: 79 if (ps.s_sym[ps.tos] == psym_decl) 80 break; /* only put one declaration onto stack */ 81 82 break_comma = true; /* while in a declaration, force a 83 * newline after comma */ 84 ps.s_sym[++ps.tos] = psym_decl; 85 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 86 87 if (opt.ljust_decl) 88 ps.ind_level_follow = ps.ind_level = decl_level(); 89 break; 90 91 case psym_if_expr: 92 if (ps.s_sym[ps.tos] == psym_if_expr_stmt_else 93 && opt.else_if) { 94 /* Reduce "else if" to "if". This saves a lot of stack 95 * space in case of a long "if-else-if ... else-if" 96 * sequence. */ 97 ps.ind_level_follow = ps.s_ind_level[ps.tos--]; 98 } 99 /* FALLTHROUGH */ 100 case psym_do: 101 case psym_for_exprs: 102 ps.s_sym[++ps.tos] = psym; 103 ps.s_ind_level[ps.tos] = ps.ind_level = ps.ind_level_follow; 104 ++ps.ind_level_follow; 105 break; 106 107 case psym_lbrace: 108 break_comma = false; /* don't break comma in an initializer 109 * list */ 110 if (ps.s_sym[ps.tos] == psym_stmt 111 || ps.s_sym[ps.tos] == psym_decl 112 || ps.s_sym[ps.tos] == psym_stmt_list) 113 ++ps.ind_level_follow; /* it is a random, isolated 114 * stmt group or a declaration 115 */ 116 else { 117 if (code.len == 0) { 118 /* it is a group as part of a while, for, etc. 119 */ 120 --ps.ind_level; 121 122 /* for a switch, brace should be two levels out 123 * from the code */ 124 if (ps.s_sym[ps.tos] == psym_switch_expr && 125 opt.case_indent >= 1.0F) 126 --ps.ind_level; 127 } 128 } 129 130 ps.s_sym[++ps.tos] = psym_lbrace; 131 ps.s_ind_level[ps.tos] = ps.ind_level; 132 ps.s_sym[++ps.tos] = psym_stmt; 133 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 134 break; 135 136 case psym_while_expr: 137 if (ps.s_sym[ps.tos] == psym_do_stmt) { 138 /* it is matched with do stmt */ 139 ps.ind_level = 140 ps.ind_level_follow = ps.s_ind_level[ps.tos]; 141 ps.s_sym[++ps.tos] = psym_while_expr; 142 ps.s_ind_level[ps.tos] = ps.ind_level; 143 144 } else { /* it is a while loop */ 145 ps.s_sym[++ps.tos] = psym_while_expr; 146 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 147 ++ps.ind_level_follow; 148 } 149 150 break; 151 152 case psym_else: 153 if (ps.s_sym[ps.tos] != psym_if_expr_stmt) 154 diag(1, "Unmatched 'else'"); 155 else { 156 ps.ind_level = ps.s_ind_level[ps.tos]; 157 ps.ind_level_follow = ps.ind_level + 1; 158 ps.s_sym[ps.tos] = psym_if_expr_stmt_else; 159 } 160 break; 161 162 case psym_rbrace: 163 /* stack should have <lbrace> <stmt> or <lbrace> <stmt_list> */ 164 if (ps.tos > 0 && ps.s_sym[ps.tos - 1] == psym_lbrace) { 165 ps.ind_level = ps.ind_level_follow 166 = ps.s_ind_level[--ps.tos]; 167 ps.s_sym[ps.tos] = psym_stmt; 168 } else 169 diag(1, "Statement nesting error"); 170 break; 171 172 case psym_switch_expr: 173 ps.s_sym[++ps.tos] = psym_switch_expr; 174 ps.s_case_ind_level[ps.tos] = case_ind; 175 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 176 case_ind = (float)ps.ind_level_follow + opt.case_indent; 177 ps.ind_level_follow += (int)opt.case_indent + 1; 178 break; 179 180 case psym_0: /* a simple statement */ 181 break_comma = false; /* don't break after comma in a 182 * declaration */ 183 ps.s_sym[++ps.tos] = psym_stmt; 184 ps.s_ind_level[ps.tos] = ps.ind_level; 185 break; 186 187 default: 188 diag(1, "Unknown code to parser"); 189 return; 190 } 191 192 if (ps.tos >= STACKSIZE - 1) 193 errx(1, "Parser stack overflow"); 194 195 debug_parse_stack("before reduction"); 196 reduce(); /* see if any reduction can be done */ 197 debug_parse_stack("after reduction"); 198 } 199 200 /* 201 * Try to combine the statement on the top of the parse stack with the symbol 202 * directly below it, replacing these two symbols with a single symbol. 203 */ 204 static bool 205 reduce_stmt(void) 206 { 207 switch (ps.s_sym[ps.tos - 1]) { 208 209 case psym_stmt: 210 case psym_stmt_list: 211 ps.s_sym[--ps.tos] = psym_stmt_list; 212 return true; 213 214 case psym_do: 215 ps.s_sym[--ps.tos] = psym_do_stmt; 216 ps.ind_level_follow = ps.s_ind_level[ps.tos]; 217 return true; 218 219 case psym_if_expr: 220 ps.s_sym[--ps.tos] = psym_if_expr_stmt; 221 int i = ps.tos - 1; 222 while (ps.s_sym[i] != psym_stmt && 223 ps.s_sym[i] != psym_stmt_list && 224 ps.s_sym[i] != psym_lbrace) 225 --i; 226 ps.ind_level_follow = ps.s_ind_level[i]; 227 /* For the time being, assume that there is no 'else' on this 228 * 'if', and set the indentation level accordingly. If an 229 * 'else' is scanned, it will be fixed up later. */ 230 return true; 231 232 case psym_switch_expr: 233 case_ind = ps.s_case_ind_level[ps.tos - 1]; 234 /* FALLTHROUGH */ 235 case psym_decl: /* finish of a declaration */ 236 case psym_if_expr_stmt_else: 237 case psym_for_exprs: 238 case psym_while_expr: 239 ps.s_sym[--ps.tos] = psym_stmt; 240 ps.ind_level_follow = ps.s_ind_level[ps.tos]; 241 return true; 242 243 default: 244 return false; 245 } 246 } 247 248 /* 249 * Repeatedly try to reduce the top two symbols on the parse stack to a single 250 * symbol, until no more reductions are possible. 251 */ 252 static void 253 reduce(void) 254 { 255 again: 256 if (ps.s_sym[ps.tos] == psym_stmt && reduce_stmt()) 257 goto again; 258 if (ps.s_sym[ps.tos] == psym_while_expr && 259 ps.s_sym[ps.tos - 1] == psym_do_stmt) { 260 ps.tos -= 2; 261 goto again; 262 } 263 } 264