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