parse.c revision 1.61 1 /* $NetBSD: parse.c,v 1.61 2023/05/18 05:33:27 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.61 2023/05/18 05:33:27 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_println("parse token: %s", psym_name[psym]);
67
68 if (psym != psym_else) {
69 while (ps.s_sym[ps.tos] == psym_if_expr_stmt) {
70 ps.s_sym[ps.tos] = psym_stmt;
71 reduce();
72 }
73 }
74
75 switch (psym) {
76
77 case psym_decl:
78 if (ps.s_sym[ps.tos] == psym_decl)
79 break; /* only put one declaration onto stack */
80
81 break_comma = true; /* while in a declaration, force a
82 * newline after comma */
83 ps.s_sym[++ps.tos] = psym_decl;
84 ps.s_ind_level[ps.tos] = ps.ind_level_follow;
85
86 if (opt.ljust_decl)
87 ps.ind_level_follow = ps.ind_level = decl_level();
88 break;
89
90 case psym_if_expr:
91 if (ps.s_sym[ps.tos] == psym_if_expr_stmt_else
92 && opt.else_if) {
93 /* Reduce "else if" to "if". This saves a lot of stack
94 * space in case of a long "if-else-if ... else-if"
95 * sequence. */
96 ps.ind_level_follow = ps.s_ind_level[ps.tos--];
97 }
98 /* FALLTHROUGH */
99 case psym_do:
100 case psym_for_exprs:
101 ps.s_sym[++ps.tos] = psym;
102 ps.s_ind_level[ps.tos] = ps.ind_level = ps.ind_level_follow;
103 ++ps.ind_level_follow;
104 break;
105
106 case psym_lbrace:
107 break_comma = false; /* don't break comma in an initializer
108 * list */
109 if (ps.s_sym[ps.tos] == psym_stmt
110 || ps.s_sym[ps.tos] == psym_decl
111 || ps.s_sym[ps.tos] == psym_stmt_list)
112 ++ps.ind_level_follow; /* it is a random, isolated
113 * stmt group or a declaration
114 */
115 else {
116 if (code.len == 0) {
117 /* it is a group as part of a while, for, etc.
118 */
119 --ps.ind_level;
120
121 /* for a switch, brace should be two levels out
122 * from the code */
123 if (ps.s_sym[ps.tos] == psym_switch_expr &&
124 opt.case_indent >= 1.0F)
125 --ps.ind_level;
126 }
127 }
128
129 ps.s_sym[++ps.tos] = psym_lbrace;
130 ps.s_ind_level[ps.tos] = ps.ind_level;
131 ps.s_sym[++ps.tos] = psym_stmt;
132 ps.s_ind_level[ps.tos] = ps.ind_level_follow;
133 break;
134
135 case psym_while_expr:
136 if (ps.s_sym[ps.tos] == psym_do_stmt) {
137 /* it is matched with do stmt */
138 ps.ind_level =
139 ps.ind_level_follow = ps.s_ind_level[ps.tos];
140 ps.s_sym[++ps.tos] = psym_while_expr;
141 ps.s_ind_level[ps.tos] = ps.ind_level;
142
143 } else { /* it is a while loop */
144 ps.s_sym[++ps.tos] = psym_while_expr;
145 ps.s_ind_level[ps.tos] = ps.ind_level_follow;
146 ++ps.ind_level_follow;
147 }
148
149 break;
150
151 case psym_else:
152 if (ps.s_sym[ps.tos] != psym_if_expr_stmt)
153 diag(1, "Unmatched 'else'");
154 else {
155 ps.ind_level = ps.s_ind_level[ps.tos];
156 ps.ind_level_follow = ps.ind_level + 1;
157 ps.s_sym[ps.tos] = psym_if_expr_stmt_else;
158 }
159 break;
160
161 case psym_rbrace:
162 /* stack should have <lbrace> <stmt> or <lbrace> <stmt_list> */
163 if (ps.tos > 0 && ps.s_sym[ps.tos - 1] == psym_lbrace) {
164 ps.ind_level = ps.ind_level_follow
165 = ps.s_ind_level[--ps.tos];
166 ps.s_sym[ps.tos] = psym_stmt;
167 } else
168 diag(1, "Statement nesting error");
169 break;
170
171 case psym_switch_expr:
172 ps.s_sym[++ps.tos] = psym_switch_expr;
173 ps.s_case_ind_level[ps.tos] = case_ind;
174 ps.s_ind_level[ps.tos] = ps.ind_level_follow;
175 case_ind = (float)ps.ind_level_follow + opt.case_indent;
176 ps.ind_level_follow += (int)opt.case_indent + 1;
177 break;
178
179 case psym_0: /* a simple statement */
180 break_comma = false; /* don't break after comma in a
181 * declaration */
182 ps.s_sym[++ps.tos] = psym_stmt;
183 ps.s_ind_level[ps.tos] = ps.ind_level;
184 break;
185
186 default:
187 diag(1, "Unknown code to parser");
188 return;
189 }
190
191 if (ps.tos >= STACKSIZE - 1)
192 errx(1, "Parser stack overflow");
193
194 debug_parse_stack("before reduction");
195 reduce(); /* see if any reduction can be done */
196 debug_parse_stack("after reduction");
197 }
198
199 /*
200 * Try to combine the statement on the top of the parse stack with the symbol
201 * directly below it, replacing these two symbols with a single symbol.
202 */
203 static bool
204 reduce_stmt(void)
205 {
206 switch (ps.s_sym[ps.tos - 1]) {
207
208 case psym_stmt:
209 case psym_stmt_list:
210 ps.s_sym[--ps.tos] = psym_stmt_list;
211 return true;
212
213 case psym_do:
214 ps.s_sym[--ps.tos] = psym_do_stmt;
215 ps.ind_level_follow = ps.s_ind_level[ps.tos];
216 return true;
217
218 case psym_if_expr:
219 ps.s_sym[--ps.tos] = psym_if_expr_stmt;
220 int i = ps.tos - 1;
221 while (ps.s_sym[i] != psym_stmt &&
222 ps.s_sym[i] != psym_stmt_list &&
223 ps.s_sym[i] != psym_lbrace)
224 --i;
225 ps.ind_level_follow = ps.s_ind_level[i];
226 /* For the time being, assume that there is no 'else' on this
227 * 'if', and set the indentation level accordingly. If an
228 * 'else' is scanned, it will be fixed up later. */
229 return true;
230
231 case psym_switch_expr:
232 case_ind = ps.s_case_ind_level[ps.tos - 1];
233 /* FALLTHROUGH */
234 case psym_decl: /* finish of a declaration */
235 case psym_if_expr_stmt_else:
236 case psym_for_exprs:
237 case psym_while_expr:
238 ps.s_sym[--ps.tos] = psym_stmt;
239 ps.ind_level_follow = ps.s_ind_level[ps.tos];
240 return true;
241
242 default:
243 return false;
244 }
245 }
246
247 /*
248 * Repeatedly try to reduce the top two symbols on the parse stack to a single
249 * symbol, until no more reductions are possible.
250 */
251 static void
252 reduce(void)
253 {
254 again:
255 if (ps.s_sym[ps.tos] == psym_stmt && reduce_stmt())
256 goto again;
257 if (ps.s_sym[ps.tos] == psym_while_expr &&
258 ps.s_sym[ps.tos - 1] == psym_do_stmt) {
259 ps.tos -= 2;
260 goto again;
261 }
262 }
263