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