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