1 /* $NetBSD: parse.c,v 1.49 2022/04/22 21:21:20 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 #if 0 41 static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93"; 42 #endif 43 44 #include <sys/cdefs.h> 45 #if defined(__NetBSD__) 46 __RCSID("$NetBSD: parse.c,v 1.49 2022/04/22 21:21:20 rillig Exp $"); 47 #else 48 __FBSDID("$FreeBSD: head/usr.bin/indent/parse.c 337651 2018-08-11 19:20:06Z pstef $"); 49 #endif 50 51 #include <assert.h> 52 #include <err.h> 53 #include <stdio.h> 54 55 #include "indent.h" 56 57 static void reduce(void); 58 59 #ifdef debug 60 static const char * 61 psym_name(parser_symbol psym) 62 { 63 static const char *const name[] = { 64 "semicolon", 65 "lbrace", 66 "rbrace", 67 "decl", 68 "stmt", 69 "stmt_list", 70 "for_exprs", 71 "if_expr", 72 "if_expr_stmt", 73 "if_expr_stmt_else", 74 "else", 75 "switch_expr", 76 "do", 77 "do_stmt", 78 "while_expr", 79 }; 80 81 assert(array_length(name) == (int)psym_while_expr + 1); 82 83 return name[psym]; 84 } 85 #endif 86 87 static int 88 decl_level(void) 89 { 90 int level = 0; 91 for (int i = ps.tos - 1; i > 0; i--) 92 if (ps.s_sym[i] == psym_decl) 93 level++; 94 return level; 95 } 96 97 /* 98 * Shift the token onto the parser stack, or reduce it by combining it with 99 * previous tokens. 100 */ 101 void 102 parse(parser_symbol psym) 103 { 104 debug_println("parse token: %s", psym_name(psym)); 105 106 if (psym != psym_else) { 107 while (ps.s_sym[ps.tos] == psym_if_expr_stmt) { 108 ps.s_sym[ps.tos] = psym_stmt; 109 reduce(); 110 } 111 } 112 113 switch (psym) { 114 115 case psym_decl: 116 ps.search_stmt = opt.brace_same_line; 117 /* indicate that following brace should be on same line */ 118 119 if (ps.s_sym[ps.tos] == psym_decl) 120 break; /* only put one declaration onto stack */ 121 122 break_comma = true; /* while in a declaration, force a newline 123 * after comma */ 124 ps.s_sym[++ps.tos] = psym_decl; 125 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 126 127 if (opt.ljust_decl) 128 ps.ind_level_follow = ps.ind_level = decl_level(); 129 break; 130 131 case psym_if_expr: 132 if (ps.s_sym[ps.tos] == psym_if_expr_stmt_else && opt.else_if) { 133 /* 134 * Reduce "else if" to "if". This saves a lot of stack space in 135 * case of a long "if-else-if ... else-if" sequence. 136 */ 137 ps.ind_level_follow = ps.s_ind_level[ps.tos--]; 138 } 139 /* FALLTHROUGH */ 140 case psym_do: 141 case psym_for_exprs: 142 ps.s_sym[++ps.tos] = psym; 143 ps.s_ind_level[ps.tos] = ps.ind_level = ps.ind_level_follow; 144 ++ps.ind_level_follow; /* subsequent statements should be indented 1 */ 145 ps.search_stmt = opt.brace_same_line; 146 break; 147 148 case psym_lbrace: 149 break_comma = false; /* don't break comma in an initializer list */ 150 if (ps.s_sym[ps.tos] == psym_stmt || ps.s_sym[ps.tos] == psym_decl 151 || ps.s_sym[ps.tos] == psym_stmt_list) 152 ++ps.ind_level_follow; /* it is a random, isolated stmt group 153 * or a declaration */ 154 else { 155 if (code.s == code.e) { 156 /* it is a group as part of a while, for, etc. */ 157 --ps.ind_level; 158 159 /* 160 * for a switch, brace should be two levels out from the code 161 */ 162 if (ps.s_sym[ps.tos] == psym_switch_expr && 163 opt.case_indent >= 1.0F) 164 --ps.ind_level; 165 } 166 } 167 168 ps.s_sym[++ps.tos] = psym_lbrace; 169 ps.s_ind_level[ps.tos] = ps.ind_level; 170 ps.s_sym[++ps.tos] = psym_stmt; 171 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 172 break; 173 174 case psym_while_expr: 175 if (ps.s_sym[ps.tos] == psym_do_stmt) { 176 /* it is matched with do stmt */ 177 ps.ind_level = ps.ind_level_follow = ps.s_ind_level[ps.tos]; 178 ps.s_sym[++ps.tos] = psym_while_expr; 179 ps.s_ind_level[ps.tos] = ps.ind_level = ps.ind_level_follow; 180 181 } else { /* it is a while loop */ 182 ps.s_sym[++ps.tos] = psym_while_expr; 183 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 184 ++ps.ind_level_follow; 185 ps.search_stmt = opt.brace_same_line; 186 } 187 188 break; 189 190 case psym_else: 191 if (ps.s_sym[ps.tos] != psym_if_expr_stmt) 192 diag(1, "Unmatched 'else'"); 193 else { 194 ps.ind_level = ps.s_ind_level[ps.tos]; 195 ps.ind_level_follow = ps.ind_level + 1; 196 ps.s_sym[ps.tos] = psym_if_expr_stmt_else; 197 198 ps.search_stmt = opt.brace_same_line || opt.else_if; 199 } 200 break; 201 202 case psym_rbrace: 203 /* stack should have <lbrace> <stmt> or <lbrace> <stmt_list> */ 204 if (ps.tos > 0 && ps.s_sym[ps.tos - 1] == psym_lbrace) { 205 ps.ind_level = ps.ind_level_follow = ps.s_ind_level[--ps.tos]; 206 ps.s_sym[ps.tos] = psym_stmt; 207 } else 208 diag(1, "Statement nesting error"); 209 break; 210 211 case psym_switch_expr: 212 ps.s_sym[++ps.tos] = psym_switch_expr; 213 ps.s_case_ind_level[ps.tos] = case_ind; 214 ps.s_ind_level[ps.tos] = ps.ind_level_follow; 215 case_ind = (float)ps.ind_level_follow + opt.case_indent; 216 ps.ind_level_follow += (int)opt.case_indent + 1; 217 ps.search_stmt = opt.brace_same_line; 218 break; 219 220 case psym_semicolon: /* a simple statement */ 221 break_comma = false; /* don't break after comma in a declaration */ 222 ps.s_sym[++ps.tos] = psym_stmt; 223 ps.s_ind_level[ps.tos] = ps.ind_level; 224 break; 225 226 default: 227 diag(1, "Unknown code to parser"); 228 return; 229 } 230 231 if (ps.tos >= STACKSIZE - 1) 232 errx(1, "Parser stack overflow"); 233 234 reduce(); /* see if any reduction can be done */ 235 236 #ifdef debug 237 printf("parse stack:"); 238 for (int i = 1; i <= ps.tos; ++i) 239 printf(" %s %d", psym_name(ps.s_sym[i]), ps.s_ind_level[i]); 240 if (ps.tos == 0) 241 printf(" empty"); 242 printf("\n"); 243 #endif 244 } 245 246 void 247 parse_stmt_head(stmt_head hd) 248 { 249 static const parser_symbol psym[] = { 250 [hd_for] = psym_for_exprs, 251 [hd_if] = psym_if_expr, 252 [hd_switch] = psym_switch_expr, 253 [hd_while] = psym_while_expr 254 }; 255 parse(psym[hd]); 256 } 257 258 /* 259 * Try to combine the statement on the top of the parse stack with the symbol 260 * directly below it, replacing these two symbols with a single symbol. 261 */ 262 static bool 263 reduce_stmt(void) 264 { 265 switch (ps.s_sym[ps.tos - 1]) { 266 267 case psym_stmt: 268 case psym_stmt_list: 269 ps.s_sym[--ps.tos] = psym_stmt_list; 270 return true; 271 272 case psym_do: 273 ps.s_sym[--ps.tos] = psym_do_stmt; 274 ps.ind_level_follow = ps.s_ind_level[ps.tos]; 275 return true; 276 277 case psym_if_expr: 278 ps.s_sym[--ps.tos] = psym_if_expr_stmt; 279 int i = ps.tos - 1; 280 while (ps.s_sym[i] != psym_stmt && 281 ps.s_sym[i] != psym_stmt_list && 282 ps.s_sym[i] != psym_lbrace) 283 --i; 284 ps.ind_level_follow = ps.s_ind_level[i]; 285 /* 286 * For the time being, assume that there is no 'else' on this 'if', 287 * and set the indentation level accordingly. If an 'else' is scanned, 288 * it will be fixed up later. 289 */ 290 return true; 291 292 case psym_switch_expr: 293 case_ind = ps.s_case_ind_level[ps.tos - 1]; 294 /* FALLTHROUGH */ 295 case psym_decl: /* finish of a declaration */ 296 case psym_if_expr_stmt_else: 297 case psym_for_exprs: 298 case psym_while_expr: 299 ps.s_sym[--ps.tos] = psym_stmt; 300 ps.ind_level_follow = ps.s_ind_level[ps.tos]; 301 return true; 302 303 default: 304 return false; 305 } 306 } 307 308 /* 309 * Repeatedly try to reduce the top two symbols on the parse stack to a 310 * single symbol, until no more reductions are possible. 311 */ 312 static void 313 reduce(void) 314 { 315 again: 316 if (ps.s_sym[ps.tos] == psym_stmt && reduce_stmt()) 317 goto again; 318 if (ps.s_sym[ps.tos] == psym_while_expr && 319 ps.s_sym[ps.tos - 1] == psym_do_stmt) { 320 ps.tos -= 2; 321 goto again; 322 } 323 } 324