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