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
47static 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
55static inline void dc_parse_string(BcParse *p) {
56	bc_parse_addString(p);
57	bc_lex_next(&p->l);
58}
59
60static 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
74static 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
90static 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
192void 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
222void 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
231exit:
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