xref: /freebsd/contrib/bc/src/dc_parse.c (revision dd41de95a84d979615a2ef11df6850622bf6184e)
1 /*
2  * *****************************************************************************
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * * Redistributions of source code must retain the above copyright notice, this
12  *   list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright notice,
15  *   this list of conditions and the following disclaimer in the documentation
16  *   and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * *****************************************************************************
31  *
32  * The parser for dc.
33  *
34  */
35 
36 #if DC_ENABLED
37 
38 #include <assert.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <setjmp.h>
42 
43 #include <dc.h>
44 #include <program.h>
45 #include <vm.h>
46 
47 static void dc_parse_register(BcParse *p, bool var) {
48 
49 	bc_lex_next(&p->l);
50 	if (p->l.t != BC_LEX_NAME) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
51 
52 	bc_parse_pushName(p, p->l.str.v, var);
53 }
54 
55 static inline void dc_parse_string(BcParse *p) {
56 	bc_parse_addString(p);
57 	bc_lex_next(&p->l);
58 }
59 
60 static void dc_parse_mem(BcParse *p, uchar inst, bool name, bool store) {
61 
62 	bc_parse_push(p, inst);
63 
64 	if (name) dc_parse_register(p, inst != BC_INST_ARRAY_ELEM);
65 
66 	if (store) {
67 		bc_parse_push(p, BC_INST_SWAP);
68 		bc_parse_push(p, BC_INST_ASSIGN_NO_VAL);
69 	}
70 
71 	bc_lex_next(&p->l);
72 }
73 
74 static void dc_parse_cond(BcParse *p, uchar inst) {
75 
76 	bc_parse_push(p, inst);
77 	bc_parse_push(p, BC_INST_EXEC_COND);
78 
79 	dc_parse_register(p, true);
80 
81 	bc_lex_next(&p->l);
82 
83 	if (p->l.t == BC_LEX_KW_ELSE) {
84 		dc_parse_register(p, true);
85 		bc_lex_next(&p->l);
86 	}
87 	else bc_parse_pushIndex(p, SIZE_MAX);
88 }
89 
90 static void dc_parse_token(BcParse *p, BcLexType t, uint8_t flags) {
91 
92 	uchar inst;
93 	bool assign, get_token = false;
94 
95 	switch (t) {
96 
97 		case BC_LEX_OP_REL_EQ:
98 		case BC_LEX_OP_REL_LE:
99 		case BC_LEX_OP_REL_GE:
100 		case BC_LEX_OP_REL_NE:
101 		case BC_LEX_OP_REL_LT:
102 		case BC_LEX_OP_REL_GT:
103 		{
104 			inst = (uchar) (t - BC_LEX_OP_REL_EQ + BC_INST_REL_EQ);
105 			dc_parse_cond(p, inst);
106 			break;
107 		}
108 
109 		case BC_LEX_SCOLON:
110 		case BC_LEX_COLON:
111 		{
112 			dc_parse_mem(p, BC_INST_ARRAY_ELEM, true, t == BC_LEX_COLON);
113 			break;
114 		}
115 
116 		case BC_LEX_STR:
117 		{
118 			dc_parse_string(p);
119 			break;
120 		}
121 
122 		case BC_LEX_NEG:
123 		{
124 			if (dc_lex_negCommand(&p->l)) {
125 				bc_parse_push(p, BC_INST_NEG);
126 				get_token = true;
127 				break;
128 			}
129 
130 			bc_lex_next(&p->l);
131 		}
132 		// Fallthrough.
133 		BC_FALLTHROUGH
134 
135 		case BC_LEX_NUMBER:
136 		{
137 			bc_parse_number(p);
138 
139 			if (t == BC_LEX_NEG) bc_parse_push(p, BC_INST_NEG);
140 			get_token = true;
141 
142 			break;
143 		}
144 
145 		case BC_LEX_KW_READ:
146 		{
147 			if (BC_ERR(flags & BC_PARSE_NOREAD))
148 				bc_parse_err(p, BC_ERR_EXEC_REC_READ);
149 			else bc_parse_push(p, BC_INST_READ);
150 			get_token = true;
151 			break;
152 		}
153 
154 		case BC_LEX_OP_ASSIGN:
155 		case BC_LEX_STORE_PUSH:
156 		{
157 			assign = t == BC_LEX_OP_ASSIGN;
158 			inst = assign ? BC_INST_VAR : BC_INST_PUSH_TO_VAR;
159 			dc_parse_mem(p, inst, true, assign);
160 			break;
161 		}
162 
163 		case BC_LEX_LOAD:
164 		case BC_LEX_LOAD_POP:
165 		{
166 			inst = t == BC_LEX_LOAD_POP ? BC_INST_PUSH_VAR : BC_INST_LOAD;
167 			dc_parse_mem(p, inst, true, false);
168 			break;
169 		}
170 
171 		case BC_LEX_STORE_IBASE:
172 		case BC_LEX_STORE_OBASE:
173 		case BC_LEX_STORE_SCALE:
174 #if BC_ENABLE_EXTRA_MATH
175 		case BC_LEX_STORE_SEED:
176 #endif // BC_ENABLE_EXTRA_MATH
177 		{
178 			inst = (uchar) (t - BC_LEX_STORE_IBASE + BC_INST_IBASE);
179 			dc_parse_mem(p, inst, false, true);
180 			break;
181 		}
182 
183 		default:
184 		{
185 			bc_parse_err(p, BC_ERR_PARSE_TOKEN);
186 		}
187 	}
188 
189 	if (get_token) bc_lex_next(&p->l);
190 }
191 
192 void dc_parse_expr(BcParse *p, uint8_t flags) {
193 
194 	BcInst inst;
195 	BcLexType t;
196 	bool have_expr = false, need_expr = (flags & BC_PARSE_NOREAD) != 0;
197 
198 	while ((t = p->l.t) != BC_LEX_EOF) {
199 
200 		if (t == BC_LEX_NLINE) {
201 			bc_lex_next(&p->l);
202 			continue;
203 		}
204 
205 		inst = dc_parse_insts[t];
206 
207 		if (inst != BC_INST_INVALID) {
208 			bc_parse_push(p, inst);
209 			bc_lex_next(&p->l);
210 		}
211 		else dc_parse_token(p, t, flags);
212 
213 		have_expr = true;
214 	}
215 
216 	if (BC_ERR(need_expr && !have_expr))
217 		bc_vm_err(BC_ERR_EXEC_READ_EXPR);
218 	else if (p->l.t == BC_LEX_EOF && (flags & BC_PARSE_NOCALL))
219 		bc_parse_push(p, BC_INST_POP_EXEC);
220 }
221 
222 void dc_parse_parse(BcParse *p) {
223 
224 	assert(p != NULL);
225 
226 	BC_SETJMP(exit);
227 
228 	if (BC_ERR(p->l.t == BC_LEX_EOF)) bc_parse_err(p, BC_ERR_PARSE_EOF);
229 	else dc_parse_expr(p, 0);
230 
231 exit:
232 	BC_SIG_MAYLOCK;
233 	if (BC_ERR(vm.status || vm.sig)) bc_parse_reset(p);
234 	BC_LONGJMP_CONT;
235 }
236 #endif // DC_ENABLED
237