1 /*
2 * *****************************************************************************
3 *
4 * Copyright (c) 2018-2019 Gavin D. Howard and contributors.
5 *
6 * All rights reserved.
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 * Common code for the lexers.
33 *
34 */
35
36 #include <assert.h>
37 #include <ctype.h>
38 #include <stdbool.h>
39 #include <string.h>
40
41 #include <status.h>
42 #include <lex.h>
43 #include <vm.h>
44 #include <bc.h>
45
bc_lex_invalidChar(BcLex * l,char c)46 BcStatus bc_lex_invalidChar(BcLex *l, char c) {
47 l->t = BC_LEX_INVALID;
48 return bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c);
49 }
50
bc_lex_lineComment(BcLex * l)51 void bc_lex_lineComment(BcLex *l) {
52 l->t = BC_LEX_WHITESPACE;
53 while (l->i < l->len && l->buf[l->i] != '\n') l->i += 1;
54 }
55
bc_lex_comment(BcLex * l)56 BcStatus bc_lex_comment(BcLex *l) {
57
58 size_t i, nlines = 0;
59 const char *buf = l->buf;
60 bool end = false;
61 char c;
62
63 l->i += 1;
64 l->t = BC_LEX_WHITESPACE;
65
66 for (i = l->i; !end; i += !end) {
67
68 for (; (c = buf[i]) && c != '*'; ++i) nlines += (c == '\n');
69
70 if (BC_ERR(!c || buf[i + 1] == '\0')) {
71 l->i = i;
72 return bc_lex_err(l, BC_ERROR_PARSE_COMMENT);
73 }
74
75 end = buf[i + 1] == '/';
76 }
77
78 l->i = i + 2;
79 l->line += nlines;
80
81 return BC_STATUS_SUCCESS;
82 }
83
bc_lex_whitespace(BcLex * l)84 void bc_lex_whitespace(BcLex *l) {
85 char c;
86 l->t = BC_LEX_WHITESPACE;
87 for (c = l->buf[l->i]; c != '\n' && isspace(c); c = l->buf[++l->i]);
88 }
89
bc_lex_commonTokens(BcLex * l,char c)90 void bc_lex_commonTokens(BcLex *l, char c) {
91 if (!c) l->t = BC_LEX_EOF;
92 else if (c == '\n') l->t = BC_LEX_NLINE;
93 else bc_lex_whitespace(l);
94 }
95
bc_lex_num(BcLex * l,char start,bool int_only)96 static size_t bc_lex_num(BcLex *l, char start, bool int_only) {
97
98 const char *buf = l->buf + l->i;
99 size_t i;
100 char c;
101 bool last_pt, pt = (start == '.');
102
103 for (i = 0; (c = buf[i]) && (BC_LEX_NUM_CHAR(c, pt, int_only) ||
104 (c == '\\' && buf[i + 1] == '\n')); ++i)
105 {
106 if (c == '\\') {
107
108 if (buf[i + 1] == '\n') {
109
110 i += 2;
111
112 // Make sure to eat whitespace at the beginning of the line.
113 while(isspace(buf[i]) && buf[i] != '\n') i += 1;
114
115 c = buf[i];
116
117 if (!BC_LEX_NUM_CHAR(c, pt, int_only)) break;
118 }
119 else break;
120 }
121
122 last_pt = (c == '.');
123 if (pt && last_pt) break;
124 pt = pt || last_pt;
125
126 bc_vec_push(&l->str, &c);
127 }
128
129 return i;
130 }
131
bc_lex_number(BcLex * l,char start)132 BcStatus bc_lex_number(BcLex *l, char start) {
133
134 l->t = BC_LEX_NUMBER;
135
136 bc_vec_npop(&l->str, l->str.len);
137 bc_vec_push(&l->str, &start);
138
139 l->i += bc_lex_num(l, start, false);
140
141 #if BC_ENABLE_EXTRA_MATH
142 {
143 char c = l->buf[l->i];
144
145 if (c == 'e') {
146
147 #if BC_ENABLED
148 if (BC_IS_POSIX) {
149 BcStatus s = bc_lex_err(l, BC_ERROR_POSIX_EXP_NUM);
150 if (BC_ERR(s)) return s;
151 }
152 #endif // BC_ENABLED
153
154 bc_vec_push(&l->str, &c);
155 l->i += 1;
156 c = l->buf[l->i];
157
158 if (c == BC_LEX_NEG_CHAR) {
159 bc_vec_push(&l->str, &c);
160 l->i += 1;
161 c = l->buf[l->i];
162 }
163
164 if (BC_ERR(!BC_LEX_NUM_CHAR(c, false, true)))
165 return bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c);
166
167 l->i += bc_lex_num(l, 0, true);
168 }
169 }
170 #endif // BC_ENABLE_EXTRA_MATH
171
172 bc_vec_pushByte(&l->str, '\0');
173
174 return BC_STATUS_SUCCESS;
175 }
176
bc_lex_name(BcLex * l)177 void bc_lex_name(BcLex *l) {
178
179 size_t i = 0;
180 const char *buf = l->buf + l->i - 1;
181 char c = buf[i];
182
183 l->t = BC_LEX_NAME;
184
185 while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i];
186
187 bc_vec_string(&l->str, i, buf);
188
189 // Increment the index. We minus 1 because it has already been incremented.
190 l->i += i - 1;
191 }
192
bc_lex_init(BcLex * l)193 void bc_lex_init(BcLex *l) {
194 assert(l != NULL);
195 bc_vec_init(&l->str, sizeof(char), NULL);
196 }
197
bc_lex_free(BcLex * l)198 void bc_lex_free(BcLex *l) {
199 assert(l != NULL);
200 bc_vec_free(&l->str);
201 }
202
bc_lex_file(BcLex * l,const char * file)203 void bc_lex_file(BcLex *l, const char *file) {
204 assert(l != NULL && file != NULL);
205 l->line = 1;
206 vm->file = file;
207 }
208
bc_lex_next(BcLex * l)209 BcStatus bc_lex_next(BcLex *l) {
210
211 BcStatus s;
212
213 assert(l != NULL);
214
215 l->last = l->t;
216 l->line += (l->i != 0 && l->buf[l->i - 1] == '\n');
217
218 if (BC_ERR(l->last == BC_LEX_EOF)) return bc_lex_err(l, BC_ERROR_PARSE_EOF);
219
220 l->t = BC_LEX_EOF;
221
222 if (l->i == l->len) return BC_STATUS_SUCCESS;
223
224 // Loop until failure or we don't have whitespace. This
225 // is so the parser doesn't get inundated with whitespace.
226 do {
227 s = vm->next(l);
228 } while (BC_NO_ERR(!s) && l->t == BC_LEX_WHITESPACE);
229
230 return s;
231 }
232
bc_lex_text(BcLex * l,const char * text)233 BcStatus bc_lex_text(BcLex *l, const char *text) {
234 assert(l != NULL && text != NULL);
235 l->buf = text;
236 l->i = 0;
237 l->len = strlen(text);
238 l->t = l->last = BC_LEX_INVALID;
239 return bc_lex_next(l);
240 }
241