xref: /freebsd/contrib/bc/src/lex.c (revision 3aa99676b43a74a61fae17e19fd554e1937fa746)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
4*3aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
6*3aa99676SStefan Eßer  * Copyright (c) 2018-2020 Gavin D. Howard and contributors.
7252884aeSStefan Eßer  *
8252884aeSStefan Eßer  * Redistribution and use in source and binary forms, with or without
9252884aeSStefan Eßer  * modification, are permitted provided that the following conditions are met:
10252884aeSStefan Eßer  *
11252884aeSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
12252884aeSStefan Eßer  *   list of conditions and the following disclaimer.
13252884aeSStefan Eßer  *
14252884aeSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
15252884aeSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
16252884aeSStefan Eßer  *   and/or other materials provided with the distribution.
17252884aeSStefan Eßer  *
18252884aeSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19252884aeSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20252884aeSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21252884aeSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22252884aeSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23252884aeSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24252884aeSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25252884aeSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26252884aeSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27252884aeSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28252884aeSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
29252884aeSStefan Eßer  *
30252884aeSStefan Eßer  * *****************************************************************************
31252884aeSStefan Eßer  *
32252884aeSStefan Eßer  * Common code for the lexers.
33252884aeSStefan Eßer  *
34252884aeSStefan Eßer  */
35252884aeSStefan Eßer 
36252884aeSStefan Eßer #include <assert.h>
37252884aeSStefan Eßer #include <ctype.h>
38252884aeSStefan Eßer #include <stdbool.h>
39252884aeSStefan Eßer #include <string.h>
40252884aeSStefan Eßer 
41252884aeSStefan Eßer #include <status.h>
42252884aeSStefan Eßer #include <lex.h>
43252884aeSStefan Eßer #include <vm.h>
44252884aeSStefan Eßer #include <bc.h>
45252884aeSStefan Eßer 
46252884aeSStefan Eßer void bc_lex_invalidChar(BcLex *l, char c) {
47252884aeSStefan Eßer 	l->t = BC_LEX_INVALID;
48252884aeSStefan Eßer 	bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c);
49252884aeSStefan Eßer }
50252884aeSStefan Eßer 
51252884aeSStefan Eßer void bc_lex_lineComment(BcLex *l) {
52252884aeSStefan Eßer 	l->t = BC_LEX_WHITESPACE;
53252884aeSStefan Eßer 	while (l->i < l->len && l->buf[l->i] != '\n') l->i += 1;
54252884aeSStefan Eßer }
55252884aeSStefan Eßer 
56252884aeSStefan Eßer void bc_lex_comment(BcLex *l) {
57252884aeSStefan Eßer 
58252884aeSStefan Eßer 	size_t i, nlines = 0;
59252884aeSStefan Eßer 	const char *buf = l->buf;
60252884aeSStefan Eßer 	bool end = false;
61252884aeSStefan Eßer 	char c;
62252884aeSStefan Eßer 
63252884aeSStefan Eßer 	l->i += 1;
64252884aeSStefan Eßer 	l->t = BC_LEX_WHITESPACE;
65252884aeSStefan Eßer 
66252884aeSStefan Eßer 	for (i = l->i; !end; i += !end) {
67252884aeSStefan Eßer 
68252884aeSStefan Eßer 		for (; (c = buf[i]) && c != '*'; ++i) nlines += (c == '\n');
69252884aeSStefan Eßer 
70252884aeSStefan Eßer 		if (BC_ERR(!c || buf[i + 1] == '\0')) {
71252884aeSStefan Eßer 			l->i = i;
72252884aeSStefan Eßer 			bc_lex_err(l, BC_ERROR_PARSE_COMMENT);
73252884aeSStefan Eßer 		}
74252884aeSStefan Eßer 
75252884aeSStefan Eßer 		end = buf[i + 1] == '/';
76252884aeSStefan Eßer 	}
77252884aeSStefan Eßer 
78252884aeSStefan Eßer 	l->i = i + 2;
79252884aeSStefan Eßer 	l->line += nlines;
80252884aeSStefan Eßer }
81252884aeSStefan Eßer 
82252884aeSStefan Eßer void bc_lex_whitespace(BcLex *l) {
83252884aeSStefan Eßer 	char c;
84252884aeSStefan Eßer 	l->t = BC_LEX_WHITESPACE;
85252884aeSStefan Eßer 	for (c = l->buf[l->i]; c != '\n' && isspace(c); c = l->buf[++l->i]);
86252884aeSStefan Eßer }
87252884aeSStefan Eßer 
88252884aeSStefan Eßer void bc_lex_commonTokens(BcLex *l, char c) {
89252884aeSStefan Eßer 	if (!c) l->t = BC_LEX_EOF;
90252884aeSStefan Eßer 	else if (c == '\n') l->t = BC_LEX_NLINE;
91252884aeSStefan Eßer 	else bc_lex_whitespace(l);
92252884aeSStefan Eßer }
93252884aeSStefan Eßer 
94252884aeSStefan Eßer static size_t bc_lex_num(BcLex *l, char start, bool int_only) {
95252884aeSStefan Eßer 
96252884aeSStefan Eßer 	const char *buf = l->buf + l->i;
97252884aeSStefan Eßer 	size_t i;
98252884aeSStefan Eßer 	char c;
99252884aeSStefan Eßer 	bool last_pt, pt = (start == '.');
100252884aeSStefan Eßer 
101252884aeSStefan Eßer 	for (i = 0; (c = buf[i]) && (BC_LEX_NUM_CHAR(c, pt, int_only) ||
102252884aeSStefan Eßer 	                             (c == '\\' && buf[i + 1] == '\n')); ++i)
103252884aeSStefan Eßer 	{
104252884aeSStefan Eßer 		if (c == '\\') {
105252884aeSStefan Eßer 
106252884aeSStefan Eßer 			if (buf[i + 1] == '\n') {
107252884aeSStefan Eßer 
108252884aeSStefan Eßer 				i += 2;
109252884aeSStefan Eßer 
110252884aeSStefan Eßer 				// Make sure to eat whitespace at the beginning of the line.
111252884aeSStefan Eßer 				while(isspace(buf[i]) && buf[i] != '\n') i += 1;
112252884aeSStefan Eßer 
113252884aeSStefan Eßer 				c = buf[i];
114252884aeSStefan Eßer 
115252884aeSStefan Eßer 				if (!BC_LEX_NUM_CHAR(c, pt, int_only)) break;
116252884aeSStefan Eßer 			}
117252884aeSStefan Eßer 			else break;
118252884aeSStefan Eßer 		}
119252884aeSStefan Eßer 
120252884aeSStefan Eßer 		last_pt = (c == '.');
121252884aeSStefan Eßer 		if (pt && last_pt) break;
122252884aeSStefan Eßer 		pt = pt || last_pt;
123252884aeSStefan Eßer 
124252884aeSStefan Eßer 		bc_vec_push(&l->str, &c);
125252884aeSStefan Eßer 	}
126252884aeSStefan Eßer 
127252884aeSStefan Eßer 	return i;
128252884aeSStefan Eßer }
129252884aeSStefan Eßer 
130252884aeSStefan Eßer void bc_lex_number(BcLex *l, char start) {
131252884aeSStefan Eßer 
132252884aeSStefan Eßer 	l->t = BC_LEX_NUMBER;
133252884aeSStefan Eßer 
134252884aeSStefan Eßer 	bc_vec_npop(&l->str, l->str.len);
135252884aeSStefan Eßer 	bc_vec_push(&l->str, &start);
136252884aeSStefan Eßer 
137252884aeSStefan Eßer 	l->i += bc_lex_num(l, start, false);
138252884aeSStefan Eßer 
139252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
140252884aeSStefan Eßer 	{
141252884aeSStefan Eßer 		char c = l->buf[l->i];
142252884aeSStefan Eßer 
143252884aeSStefan Eßer 		if (c == 'e') {
144252884aeSStefan Eßer 
145252884aeSStefan Eßer #if BC_ENABLED
146252884aeSStefan Eßer 			if (BC_IS_POSIX) bc_lex_err(l, BC_ERROR_POSIX_EXP_NUM);
147252884aeSStefan Eßer #endif // BC_ENABLED
148252884aeSStefan Eßer 
149252884aeSStefan Eßer 			bc_vec_push(&l->str, &c);
150252884aeSStefan Eßer 			l->i += 1;
151252884aeSStefan Eßer 			c = l->buf[l->i];
152252884aeSStefan Eßer 
153252884aeSStefan Eßer 			if (c == BC_LEX_NEG_CHAR) {
154252884aeSStefan Eßer 				bc_vec_push(&l->str, &c);
155252884aeSStefan Eßer 				l->i += 1;
156252884aeSStefan Eßer 				c = l->buf[l->i];
157252884aeSStefan Eßer 			}
158252884aeSStefan Eßer 
159252884aeSStefan Eßer 			if (BC_ERR(!BC_LEX_NUM_CHAR(c, false, true)))
160252884aeSStefan Eßer 				bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c);
161252884aeSStefan Eßer 
162252884aeSStefan Eßer 			l->i += bc_lex_num(l, 0, true);
163252884aeSStefan Eßer 		}
164252884aeSStefan Eßer 	}
165252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
166252884aeSStefan Eßer 
167252884aeSStefan Eßer 	bc_vec_pushByte(&l->str, '\0');
168252884aeSStefan Eßer }
169252884aeSStefan Eßer 
170252884aeSStefan Eßer void bc_lex_name(BcLex *l) {
171252884aeSStefan Eßer 
172252884aeSStefan Eßer 	size_t i = 0;
173252884aeSStefan Eßer 	const char *buf = l->buf + l->i - 1;
174252884aeSStefan Eßer 	char c = buf[i];
175252884aeSStefan Eßer 
176252884aeSStefan Eßer 	l->t = BC_LEX_NAME;
177252884aeSStefan Eßer 
178252884aeSStefan Eßer 	while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i];
179252884aeSStefan Eßer 
180252884aeSStefan Eßer 	bc_vec_string(&l->str, i, buf);
181252884aeSStefan Eßer 
182252884aeSStefan Eßer 	// Increment the index. We minus 1 because it has already been incremented.
183252884aeSStefan Eßer 	l->i += i - 1;
184252884aeSStefan Eßer }
185252884aeSStefan Eßer 
186252884aeSStefan Eßer void bc_lex_init(BcLex *l) {
187252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
188252884aeSStefan Eßer 	assert(l != NULL);
189252884aeSStefan Eßer 	bc_vec_init(&l->str, sizeof(char), NULL);
190252884aeSStefan Eßer }
191252884aeSStefan Eßer 
192252884aeSStefan Eßer void bc_lex_free(BcLex *l) {
193252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
194252884aeSStefan Eßer 	assert(l != NULL);
195252884aeSStefan Eßer 	bc_vec_free(&l->str);
196252884aeSStefan Eßer }
197252884aeSStefan Eßer 
198252884aeSStefan Eßer void bc_lex_file(BcLex *l, const char *file) {
199252884aeSStefan Eßer 	assert(l != NULL && file != NULL);
200252884aeSStefan Eßer 	l->line = 1;
201252884aeSStefan Eßer 	vm.file = file;
202252884aeSStefan Eßer }
203252884aeSStefan Eßer 
204252884aeSStefan Eßer void bc_lex_next(BcLex *l) {
205252884aeSStefan Eßer 
206252884aeSStefan Eßer 	assert(l != NULL);
207252884aeSStefan Eßer 
208252884aeSStefan Eßer 	l->last = l->t;
209252884aeSStefan Eßer 	l->line += (l->i != 0 && l->buf[l->i - 1] == '\n');
210252884aeSStefan Eßer 
211252884aeSStefan Eßer 	if (BC_ERR(l->last == BC_LEX_EOF)) bc_lex_err(l, BC_ERROR_PARSE_EOF);
212252884aeSStefan Eßer 
213252884aeSStefan Eßer 	l->t = BC_LEX_EOF;
214252884aeSStefan Eßer 
215252884aeSStefan Eßer 	if (l->i == l->len) return;
216252884aeSStefan Eßer 
217252884aeSStefan Eßer 	// Loop until failure or we don't have whitespace. This
218252884aeSStefan Eßer 	// is so the parser doesn't get inundated with whitespace.
219252884aeSStefan Eßer 	do {
220252884aeSStefan Eßer 		vm.next(l);
221252884aeSStefan Eßer 	} while (l->t == BC_LEX_WHITESPACE);
222252884aeSStefan Eßer }
223252884aeSStefan Eßer 
224252884aeSStefan Eßer void bc_lex_text(BcLex *l, const char *text) {
225252884aeSStefan Eßer 	assert(l != NULL && text != NULL);
226252884aeSStefan Eßer 	l->buf = text;
227252884aeSStefan Eßer 	l->i = 0;
228252884aeSStefan Eßer 	l->len = strlen(text);
229252884aeSStefan Eßer 	l->t = l->last = BC_LEX_INVALID;
230252884aeSStefan Eßer 	bc_lex_next(l);
231252884aeSStefan Eßer }
232