blob: 459dbeab13fbf3831cd7a0145c002d1971ad1b1c [file] [log] [blame]
Gavin Howard5715b042018-02-12 16:11:42 -07001/*
Gavin Howardb5904bf2018-02-20 13:28:18 -07002 * *****************************************************************************
Gavin Howard5715b042018-02-12 16:11:42 -07003 *
Gavin Howardb5904bf2018-02-20 13:28:18 -07004 * Copyright 2018 Gavin D. Howard
Gavin Howard5715b042018-02-12 16:11:42 -07005 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 *
Gavin Howardb5904bf2018-02-20 13:28:18 -070017 * *****************************************************************************
Gavin Howard5715b042018-02-12 16:11:42 -070018 *
19 * The lexer.
20 *
21 */
22
Gavin Howard27fdfb92018-03-21 07:56:59 -060023#include <assert.h>
Gavin Howard8a596d42018-01-15 15:46:01 -070024#include <ctype.h>
25#include <stdbool.h>
26#include <stdint.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
Gavin Howard29493062018-03-20 19:57:37 -060031#include <status.h>
Gavin Howard3ba6c8d2018-02-15 12:23:35 -070032#include <lex.h>
Gavin Howard29493062018-03-20 19:57:37 -060033#include <bc.h>
Gavin Howard8a596d42018-01-15 15:46:01 -070034
Gavin Howard68f2bae2018-03-26 13:02:27 -060035BcStatus bc_lex_string(BcLex *lex) {
Gavin Howard07732ec2018-02-27 15:40:02 -070036
Gavin Howardf2a40492018-03-05 11:27:29 -070037 const char *start;
Gavin Howard4cf49922018-03-12 17:13:50 -060038 size_t newlines, len, i, j;
Gavin Howardf2a40492018-03-05 11:27:29 -070039 char c;
Gavin Howard07732ec2018-02-27 15:40:02 -070040
41 newlines = 0;
Gavin Howard68f2bae2018-03-26 13:02:27 -060042 lex->token.type = BC_LEX_STRING;
Gavin Howardf2a40492018-03-05 11:27:29 -070043 i = lex->idx;
Gavin Howard07732ec2018-02-27 15:40:02 -070044
Gavin Howard45ff3572018-05-15 16:33:58 -060045 for (c = lex->buffer[i]; c != '"' && c != '\0'; c = lex->buffer[++i]) {
Gavin Howard07732ec2018-02-27 15:40:02 -070046 if (c == '\n') ++newlines;
Gavin Howard07732ec2018-02-27 15:40:02 -070047 }
48
49 if (c == '\0') {
50 lex->idx = i;
51 return BC_STATUS_LEX_NO_STRING_END;
52 }
53
Gavin Howardf2a40492018-03-05 11:27:29 -070054 len = i - lex->idx;
Gavin Howard45ff3572018-05-15 16:33:58 -060055 if (!(lex->token.string = malloc(len + 1))) return BC_STATUS_MALLOC_FAIL;
Gavin Howard07732ec2018-02-27 15:40:02 -070056
Gavin Howardf2a40492018-03-05 11:27:29 -070057 start = lex->buffer + lex->idx;
Gavin Howard07732ec2018-02-27 15:40:02 -070058
Gavin Howard68f2bae2018-03-26 13:02:27 -060059 for (j = 0; j < len; ++j) lex->token.string[j] = start[j];
Gavin Howard07732ec2018-02-27 15:40:02 -070060
Gavin Howard68f2bae2018-03-26 13:02:27 -060061 lex->token.string[len] = '\0';
Gavin Howard07732ec2018-02-27 15:40:02 -070062 lex->idx = i + 1;
63 lex->line += newlines;
Gavin Howard8a596d42018-01-15 15:46:01 -070064
Gavin Howard4bc73ee2018-01-26 11:39:20 -070065 return BC_STATUS_SUCCESS;
Gavin Howard8a596d42018-01-15 15:46:01 -070066}
67
Gavin Howard68f2bae2018-03-26 13:02:27 -060068BcStatus bc_lex_comment(BcLex *lex) {
Gavin Howard8a596d42018-01-15 15:46:01 -070069
Gavin Howard6b5f5bb2018-03-22 23:16:57 -060070 size_t newlines, i;
Gavin Howard07732ec2018-02-27 15:40:02 -070071 const char *buffer;
Gavin Howard6b5f5bb2018-03-22 23:16:57 -060072 bool end;
Gavin Howard8a596d42018-01-15 15:46:01 -070073
Gavin Howard07732ec2018-02-27 15:40:02 -070074 newlines = 0;
Gavin Howard68f2bae2018-03-26 13:02:27 -060075 lex->token.type = BC_LEX_WHITESPACE;
Gavin Howarde7149d02018-03-21 20:09:22 -060076 end = false;
Gavin Howard07732ec2018-02-27 15:40:02 -070077
Gavin Howard07732ec2018-02-27 15:40:02 -070078 buffer = lex->buffer;
79
Gavin Howard45ff3572018-05-15 16:33:58 -060080 for (i = ++lex->idx; !end; i += !end) {
Gavin Howard07732ec2018-02-27 15:40:02 -070081
Gavin Howardf6e3fb32018-08-09 13:48:59 -060082 char c;
83
84 for (; (c = buffer[i]) != '*' && c != '\0'; ++i) {
Gavin Howard07732ec2018-02-27 15:40:02 -070085 if (c == '\n') ++newlines;
Gavin Howard07732ec2018-02-27 15:40:02 -070086 }
87
88 if (c == '\0' || buffer[i + 1] == '\0') {
89 lex->idx = i;
90 return BC_STATUS_LEX_NO_COMMENT_END;
91 }
92
93 end = buffer[i + 1] == '/';
Gavin Howard07732ec2018-02-27 15:40:02 -070094 }
95
96 lex->idx = i + 2;
97 lex->line += newlines;
Gavin Howard8a596d42018-01-15 15:46:01 -070098
Gavin Howard4bc73ee2018-01-26 11:39:20 -070099 return BC_STATUS_SUCCESS;
Gavin Howard8a596d42018-01-15 15:46:01 -0700100}
101
Gavin Howard68f2bae2018-03-26 13:02:27 -0600102BcStatus bc_lex_number(BcLex *lex, char start) {
Gavin Howard8a596d42018-01-15 15:46:01 -0700103
Gavin Howard6b5f5bb2018-03-22 23:16:57 -0600104 const char *buffer, *buf;
105 size_t backslashes, len, hits, i, j;
Gavin Howardf2a40492018-03-05 11:27:29 -0700106 char c;
107 bool point;
108
Gavin Howard68f2bae2018-03-26 13:02:27 -0600109 lex->token.type = BC_LEX_NUMBER;
Gavin Howardf2a40492018-03-05 11:27:29 -0700110 point = start == '.';
Gavin Howardf2a40492018-03-05 11:27:29 -0700111 buffer = lex->buffer + lex->idx;
Gavin Howardf2a40492018-03-05 11:27:29 -0700112 backslashes = 0;
113 i = 0;
114 c = buffer[i];
Gavin Howard07732ec2018-02-27 15:40:02 -0700115
116 while (c && ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
117 (c == '.' && !point) || (c == '\\' && buffer[i + 1] == '\n')))
118 {
119 if (c == '\\') {
120 ++i;
121 backslashes += 1;
122 }
123
124 c = buffer[++i];
125 }
126
Gavin Howard117de422018-03-24 15:04:50 -0600127 len = i + 1 * (*(buffer + i - 1) != '.');
Gavin Howard07732ec2018-02-27 15:40:02 -0700128
Gavin Howard68f2bae2018-03-26 13:02:27 -0600129 lex->token.string = malloc(len - backslashes * 2 + 1);
130 if (!lex->token.string) return BC_STATUS_MALLOC_FAIL;
Gavin Howard07732ec2018-02-27 15:40:02 -0700131
Gavin Howard68f2bae2018-03-26 13:02:27 -0600132 lex->token.string[0] = start;
Gavin Howardf2a40492018-03-05 11:27:29 -0700133 buf = buffer - 1;
134 hits = 0;
Gavin Howard07732ec2018-02-27 15:40:02 -0700135
Gavin Howardf2a40492018-03-05 11:27:29 -0700136 for (j = 1; j < len; ++j) {
Gavin Howard07732ec2018-02-27 15:40:02 -0700137
Gavin Howardf2a40492018-03-05 11:27:29 -0700138 c = buf[j];
Gavin Howard07732ec2018-02-27 15:40:02 -0700139
140 // If we have hit a backslash, skip it.
141 // We don't have to check for a newline
142 // because it's guaranteed.
143 if (hits < backslashes && c == '\\') {
144 ++hits;
145 ++j;
146 continue;
147 }
148
Gavin Howard68f2bae2018-03-26 13:02:27 -0600149 lex->token.string[j - (hits * 2)] = c;
Gavin Howard07732ec2018-02-27 15:40:02 -0700150 }
151
Gavin Howard68f2bae2018-03-26 13:02:27 -0600152 lex->token.string[j - (hits * 2)] = '\0';
Gavin Howard07732ec2018-02-27 15:40:02 -0700153 lex->idx += i;
Gavin Howard8a596d42018-01-15 15:46:01 -0700154
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700155 return BC_STATUS_SUCCESS;
Gavin Howard8a596d42018-01-15 15:46:01 -0700156}
157
Gavin Howard68f2bae2018-03-26 13:02:27 -0600158BcStatus bc_lex_name(BcLex *lex) {
Gavin Howard8a596d42018-01-15 15:46:01 -0700159
Gavin Howardc4c67532018-03-29 22:29:08 -0600160 BcStatus status;
Gavin Howardf2a40492018-03-05 11:27:29 -0700161 const char *buffer;
162 size_t i;
163 char c;
Gavin Howard8a596d42018-01-15 15:46:01 -0700164
Gavin Howardf2a40492018-03-05 11:27:29 -0700165 buffer = lex->buffer + lex->idx - 1;
Gavin Howard8a596d42018-01-15 15:46:01 -0700166
Gavin Howardceb0d6e2018-03-10 11:19:09 -0700167 for (i = 0; i < sizeof(bc_lex_keywords) / sizeof(bc_lex_keywords[0]); ++i) {
Gavin Howard07732ec2018-02-27 15:40:02 -0700168
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600169 unsigned long len = (unsigned long) bc_lex_keywords[i].len;
170 if (!strncmp(buffer, bc_lex_keywords[i].name, len)) {
Gavin Howard07732ec2018-02-27 15:40:02 -0700171
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600172 lex->token.type = BC_LEX_KEY_AUTO + (BcLexToken) i;
Gavin Howard07732ec2018-02-27 15:40:02 -0700173
Gavin Howardceb0d6e2018-03-10 11:19:09 -0700174 if (!bc_lex_keywords[i].posix &&
Gavin Howard45ff3572018-05-15 16:33:58 -0600175 (status = bc_posix_error(BC_STATUS_POSIX_BAD_KEYWORD, lex->file,
176 lex->line, bc_lex_keywords[i].name)))
Gavin Howard07732ec2018-02-27 15:40:02 -0700177 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600178 return status;
Gavin Howard07732ec2018-02-27 15:40:02 -0700179 }
180
181 // We need to minus one because the
182 // index has already been incremented.
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600183 lex->idx += (unsigned long) bc_lex_keywords[i].len - 1;
Gavin Howard07732ec2018-02-27 15:40:02 -0700184
185 return BC_STATUS_SUCCESS;
186 }
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700187 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700188
Gavin Howard68f2bae2018-03-26 13:02:27 -0600189 lex->token.type = BC_LEX_NAME;
Gavin Howard07732ec2018-02-27 15:40:02 -0700190
Gavin Howardf2a40492018-03-05 11:27:29 -0700191 i = 0;
192 c = buffer[i];
Gavin Howard07732ec2018-02-27 15:40:02 -0700193
Gavin Howarde7149d02018-03-21 20:09:22 -0600194 while ((c >= 'a' && c<= 'z') || (c >= '0' && c <= '9') || c == '_')
195 c = buffer[++i];
Gavin Howard8a596d42018-01-15 15:46:01 -0700196
Gavin Howardc4c67532018-03-29 22:29:08 -0600197 if (i > 1 && (status = bc_posix_error(BC_STATUS_POSIX_NAME_LEN,
198 lex->file, lex->line, buffer)))
Gavin Howard07732ec2018-02-27 15:40:02 -0700199 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600200 return status;
Gavin Howard07732ec2018-02-27 15:40:02 -0700201 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700202
Gavin Howard45ff3572018-05-15 16:33:58 -0600203 if (!(lex->token.string = malloc(i + 1))) return BC_STATUS_MALLOC_FAIL;
Gavin Howard07732ec2018-02-27 15:40:02 -0700204
Gavin Howard68f2bae2018-03-26 13:02:27 -0600205 strncpy(lex->token.string, buffer, i);
206 lex->token.string[i] = '\0';
Gavin Howard07732ec2018-02-27 15:40:02 -0700207
208 // Increment the index. It is minus one
209 // because it has already been incremented.
210 lex->idx += i - 1;
211
212 return BC_STATUS_SUCCESS;
Gavin Howard8a596d42018-01-15 15:46:01 -0700213}
214
Gavin Howard68f2bae2018-03-26 13:02:27 -0600215BcStatus bc_lex_token(BcLex *lex) {
Gavin Howard8a596d42018-01-15 15:46:01 -0700216
Gavin Howard45ff3572018-05-15 16:33:58 -0600217 BcStatus status = BC_STATUS_SUCCESS;
Gavin Howard6b5f5bb2018-03-22 23:16:57 -0600218 char c, c2;
Gavin Howard8a596d42018-01-15 15:46:01 -0700219
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700220 // This is the workhorse of the lexer.
Gavin Howard45ff3572018-05-15 16:33:58 -0600221 switch ((c = lex->buffer[lex->idx++])) {
Gavin Howard8a596d42018-01-15 15:46:01 -0700222
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700223 case '\0':
Gavin Howardc312ab32018-03-29 15:47:10 -0600224 case '\n':
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700225 {
Gavin Howarda58294c2018-03-24 14:07:28 -0600226 lex->newline = true;
Gavin Howardc312ab32018-03-29 15:47:10 -0600227 lex->token.type = BC_LEX_NEWLINE + (!c) * (BC_LEX_EOF - BC_LEX_NEWLINE);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700228 break;
229 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700230
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700231 case '\t':
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700232 case '\v':
233 case '\f':
234 case '\r':
235 case ' ':
Gavin Howardc312ab32018-03-29 15:47:10 -0600236 case '\\':
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700237 {
Gavin Howardc312ab32018-03-29 15:47:10 -0600238 lex->token.type = BC_LEX_WHITESPACE;
Gavin Howardce1069c2018-03-29 17:38:42 -0600239 c = lex->buffer[lex->idx];
240
Gavin Howardc312ab32018-03-29 15:47:10 -0600241 while ((isspace(c) && c != '\n') || c == '\\')
242 c = lex->buffer[++lex->idx];
243
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700244 break;
245 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700246
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700247 case '!':
248 {
249 c2 = lex->buffer[lex->idx];
Gavin Howard8a596d42018-01-15 15:46:01 -0700250
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700251 if (c2 == '=') {
252 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600253 lex->token.type = BC_LEX_OP_REL_NOT_EQ;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700254 }
255 else {
Gavin Howardfc71e242018-01-31 14:54:43 -0700256
Gavin Howardc4c67532018-03-29 22:29:08 -0600257 if ((status = bc_posix_error(BC_STATUS_POSIX_BOOL_OPS,
258 lex->file, lex->line, "!")))
Gavin Howardfc71e242018-01-31 14:54:43 -0700259 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600260 return status;
Gavin Howardfc71e242018-01-31 14:54:43 -0700261 }
262
Gavin Howard68f2bae2018-03-26 13:02:27 -0600263 lex->token.type = BC_LEX_OP_BOOL_NOT;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700264 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700265
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700266 break;
267 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700268
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700269 case '"':
270 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600271 status = bc_lex_string(lex);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700272 break;
273 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700274
Gavin Howard3eeff832018-01-31 12:57:54 -0700275 case '#':
276 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600277 if ((status = bc_posix_error(BC_STATUS_POSIX_SCRIPT_COMMENT,
278 lex->file, lex->line, NULL)))
Gavin Howardfc71e242018-01-31 14:54:43 -0700279 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600280 return status;
Gavin Howardfc71e242018-01-31 14:54:43 -0700281 }
Gavin Howard3eeff832018-01-31 12:57:54 -0700282
Gavin Howard68f2bae2018-03-26 13:02:27 -0600283 lex->token.type = BC_LEX_WHITESPACE;
Gavin Howard45ff3572018-05-15 16:33:58 -0600284 while (++lex->idx < lex->len && lex->buffer[lex->idx] != '\n');
Gavin Howard3eeff832018-01-31 12:57:54 -0700285
286 break;
287 }
288
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700289 case '%':
290 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600291 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700292 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600293 lex->token.type = BC_LEX_OP_ASSIGN_MODULUS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700294 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600295 else lex->token.type = BC_LEX_OP_MODULUS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700296 break;
297 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700298
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700299 case '&':
300 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600301 if ((c2 = lex->buffer[lex->idx]) == '&') {
Gavin Howardfc71e242018-01-31 14:54:43 -0700302
Gavin Howardc4c67532018-03-29 22:29:08 -0600303 if ((status = bc_posix_error(BC_STATUS_POSIX_BOOL_OPS,
304 lex->file, lex->line, "&&")))
Gavin Howardfc71e242018-01-31 14:54:43 -0700305 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600306 return status;
Gavin Howardfc71e242018-01-31 14:54:43 -0700307 }
308
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700309 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600310 lex->token.type = BC_LEX_OP_BOOL_AND;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700311 }
312 else {
Gavin Howard68f2bae2018-03-26 13:02:27 -0600313 lex->token.type = BC_LEX_INVALID;
Gavin Howardc4c67532018-03-29 22:29:08 -0600314 status = BC_STATUS_LEX_BAD_CHARACTER;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700315 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700316
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700317 break;
318 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700319
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700320 case '(':
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700321 case ')':
322 {
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600323 lex->token.type = (BcLexToken) (c - '(' + BC_LEX_LEFT_PAREN);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700324 break;
325 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700326
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700327 case '*':
328 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600329 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700330 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600331 lex->token.type = BC_LEX_OP_ASSIGN_MULTIPLY;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700332 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600333 else lex->token.type = BC_LEX_OP_MULTIPLY;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700334 break;
335 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700336
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700337 case '+':
338 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600339 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700340 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600341 lex->token.type = BC_LEX_OP_ASSIGN_PLUS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700342 }
343 else if (c2 == '+') {
344 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600345 lex->token.type = BC_LEX_OP_INC;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700346 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600347 else lex->token.type = BC_LEX_OP_PLUS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700348 break;
349 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700350
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700351 case ',':
352 {
Gavin Howard68f2bae2018-03-26 13:02:27 -0600353 lex->token.type = BC_LEX_COMMA;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700354 break;
355 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700356
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700357 case '-':
358 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600359 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700360 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600361 lex->token.type = BC_LEX_OP_ASSIGN_MINUS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700362 }
363 else if (c2 == '-') {
364 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600365 lex->token.type = BC_LEX_OP_DEC;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700366 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600367 else lex->token.type = BC_LEX_OP_MINUS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700368 break;
369 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700370
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700371 case '.':
372 {
Gavin Howard2a6e3a62018-03-05 10:03:20 -0700373 c2 = lex->buffer[lex->idx];
Gavin Howard45ff3572018-05-15 16:33:58 -0600374 if (isdigit(c2)) status = bc_lex_number(lex, c);
Gavin Howard2a6e3a62018-03-05 10:03:20 -0700375 else {
Gavin Howardc4c67532018-03-29 22:29:08 -0600376 status = bc_posix_error(BC_STATUS_POSIX_DOT_LAST,
377 lex->file, lex->line, NULL);
Gavin Howard68f2bae2018-03-26 13:02:27 -0600378 lex->token.type = BC_LEX_KEY_LAST;
Gavin Howard2a6e3a62018-03-05 10:03:20 -0700379 }
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700380 break;
381 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700382
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700383 case '/':
384 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600385 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700386 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600387 lex->token.type = BC_LEX_OP_ASSIGN_DIVIDE;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700388 }
Gavin Howardc4c67532018-03-29 22:29:08 -0600389 else if (c2 == '*') status = bc_lex_comment(lex);
Gavin Howard68f2bae2018-03-26 13:02:27 -0600390 else lex->token.type = BC_LEX_OP_DIVIDE;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700391 break;
392 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700393
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700394 case '0':
395 case '1':
396 case '2':
397 case '3':
398 case '4':
399 case '5':
400 case '6':
401 case '7':
402 case '8':
403 case '9':
404 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600405 status = bc_lex_number(lex, c);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700406 break;
407 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700408
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700409 case ';':
410 {
Gavin Howard68f2bae2018-03-26 13:02:27 -0600411 lex->token.type = BC_LEX_SEMICOLON;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700412 break;
413 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700414
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700415 case '<':
416 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600417 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700418 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600419 lex->token.type = BC_LEX_OP_REL_LESS_EQ;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700420 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600421 else lex->token.type = BC_LEX_OP_REL_LESS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700422 break;
423 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700424
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700425 case '=':
426 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600427 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700428 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600429 lex->token.type = BC_LEX_OP_REL_EQUAL;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700430 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600431 else lex->token.type = BC_LEX_OP_ASSIGN;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700432 break;
433 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700434
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700435 case '>':
436 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600437 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700438 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600439 lex->token.type = BC_LEX_OP_REL_GREATER_EQ;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700440 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600441 else lex->token.type = BC_LEX_OP_REL_GREATER;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700442 break;
443 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700444
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700445 case 'A':
446 case 'B':
447 case 'C':
448 case 'D':
449 case 'E':
450 case 'F':
451 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600452 status = bc_lex_number(lex, c);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700453 break;
454 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700455
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700456 case '[':
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700457 case ']':
458 {
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600459 lex->token.type = (BcLexToken) (c - '[' + BC_LEX_LEFT_BRACKET);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700460 break;
461 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700462
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700463 case '^':
464 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600465 if ((c2 = lex->buffer[lex->idx]) == '=') {
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700466 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600467 lex->token.type = BC_LEX_OP_ASSIGN_POWER;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700468 }
Gavin Howard68f2bae2018-03-26 13:02:27 -0600469 else lex->token.type = BC_LEX_OP_POWER;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700470 break;
471 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700472
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700473 case 'a':
474 case 'b':
475 case 'c':
476 case 'd':
477 case 'e':
478 case 'f':
479 case 'g':
480 case 'h':
481 case 'i':
482 case 'j':
483 case 'k':
484 case 'l':
485 case 'm':
486 case 'n':
487 case 'o':
488 case 'p':
489 case 'q':
490 case 'r':
491 case 's':
492 case 't':
493 case 'u':
494 case 'v':
495 case 'w':
496 case 'x':
497 case 'y':
498 case 'z':
499 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600500 status = bc_lex_name(lex);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700501 break;
502 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700503
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700504 case '{':
Gavin Howardc312ab32018-03-29 15:47:10 -0600505 case '}':
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700506 {
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600507 lex->token.type = (BcLexToken) (c - '{' + BC_LEX_LEFT_BRACE);
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700508 break;
509 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700510
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700511 case '|':
512 {
Gavin Howard45ff3572018-05-15 16:33:58 -0600513 if ((c2 = lex->buffer[lex->idx]) == '|') {
Gavin Howardfc71e242018-01-31 14:54:43 -0700514
Gavin Howardc4c67532018-03-29 22:29:08 -0600515 if ((status = bc_posix_error(BC_STATUS_POSIX_BOOL_OPS,
516 lex->file, lex->line, "||")))
Gavin Howardfc71e242018-01-31 14:54:43 -0700517 {
Gavin Howardc4c67532018-03-29 22:29:08 -0600518 return status;
Gavin Howardfc71e242018-01-31 14:54:43 -0700519 }
520
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700521 ++lex->idx;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600522 lex->token.type = BC_LEX_OP_BOOL_OR;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700523 }
524 else {
Gavin Howard68f2bae2018-03-26 13:02:27 -0600525 lex->token.type = BC_LEX_INVALID;
Gavin Howardc4c67532018-03-29 22:29:08 -0600526 status = BC_STATUS_LEX_BAD_CHARACTER;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700527 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700528
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700529 break;
530 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700531
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700532 default:
533 {
Gavin Howard68f2bae2018-03-26 13:02:27 -0600534 lex->token.type = BC_LEX_INVALID;
Gavin Howardc4c67532018-03-29 22:29:08 -0600535 status = BC_STATUS_LEX_BAD_CHARACTER;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700536 break;
537 }
538 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700539
Gavin Howardc4c67532018-03-29 22:29:08 -0600540 return status;
Gavin Howard8a596d42018-01-15 15:46:01 -0700541}
542
Gavin Howard27fdfb92018-03-21 07:56:59 -0600543void bc_lex_init(BcLex *lex, const char *file) {
544 assert(lex && file);
Gavin Howard07732ec2018-02-27 15:40:02 -0700545 lex->line = 1;
546 lex->newline = false;
547 lex->file = file;
Gavin Howard8a596d42018-01-15 15:46:01 -0700548}
549
Gavin Howard68f2bae2018-03-26 13:02:27 -0600550BcStatus bc_lex_next(BcLex *lex) {
Gavin Howard8a596d42018-01-15 15:46:01 -0700551
Gavin Howardc4c67532018-03-29 22:29:08 -0600552 BcStatus status;
Gavin Howardfc71e242018-01-31 14:54:43 -0700553
Gavin Howard68f2bae2018-03-26 13:02:27 -0600554 assert(lex);
Gavin Howard8a596d42018-01-15 15:46:01 -0700555
Gavin Howard0b190a02018-03-28 11:55:08 -0600556 if (lex->token.type == BC_LEX_EOF) return BC_STATUS_LEX_EOF;
557
Gavin Howard07732ec2018-02-27 15:40:02 -0700558 if (lex->idx == lex->len) {
Gavin Howarda58294c2018-03-24 14:07:28 -0600559 lex->newline = true;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600560 lex->token.type = BC_LEX_EOF;
Gavin Howard0b190a02018-03-28 11:55:08 -0600561 return BC_STATUS_SUCCESS;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700562 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700563
Gavin Howard07732ec2018-02-27 15:40:02 -0700564 if (lex->newline) {
565 ++lex->line;
566 lex->newline = false;
Gavin Howard4bc73ee2018-01-26 11:39:20 -0700567 }
Gavin Howard8a596d42018-01-15 15:46:01 -0700568
Gavin Howard07732ec2018-02-27 15:40:02 -0700569 // Loop until failure or we don't have whitespace. This
570 // is so the parser doesn't get inundated with whitespace.
571 do {
Gavin Howard68f2bae2018-03-26 13:02:27 -0600572 lex->token.string = NULL;
Gavin Howardc4c67532018-03-29 22:29:08 -0600573 status = bc_lex_token(lex);
574 } while (!status && lex->token.type == BC_LEX_WHITESPACE);
Gavin Howard217a5d52018-01-31 12:23:07 -0700575
Gavin Howardc4c67532018-03-29 22:29:08 -0600576 return status;
Gavin Howard8a596d42018-01-15 15:46:01 -0700577}
Gavin Howard35753922018-03-21 19:22:08 -0600578
Gavin Howard68f2bae2018-03-26 13:02:27 -0600579BcStatus bc_lex_text(BcLex *lex, const char *text) {
Gavin Howard68f2bae2018-03-26 13:02:27 -0600580 assert(lex && text);
Gavin Howard35753922018-03-21 19:22:08 -0600581 lex->buffer = text;
582 lex->idx = 0;
583 lex->len = strlen(text);
Gavin Howard9f4d9142018-03-28 12:21:34 -0600584 lex->token.type = BC_LEX_INVALID;
Gavin Howard68f2bae2018-03-26 13:02:27 -0600585 return bc_lex_next(lex);
Gavin Howard35753922018-03-21 19:22:08 -0600586}