Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 1 | /* |
Gavin Howard | b5904bf | 2018-02-20 13:28:18 -0700 | [diff] [blame] | 2 | * ***************************************************************************** |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 3 | * |
Gavin Howard | 29e00ba | 2020-06-30 09:25:21 -0600 | [diff] [blame] | 4 | * SPDX-License-Identifier: BSD-2-Clause |
| 5 | * |
Zach van Rijn | 6d2cf3f | 2020-01-14 22:05:02 +0000 | [diff] [blame] | 6 | * Copyright (c) 2018-2020 Gavin D. Howard and contributors. |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 7 | * |
Gavin Howard | 7345cb9 | 2019-04-08 14:13:43 -0600 | [diff] [blame] | 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. |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 29 | * |
Gavin Howard | b5904bf | 2018-02-20 13:28:18 -0700 | [diff] [blame] | 30 | * ***************************************************************************** |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 31 | * |
Gavin Howard | d2a0525 | 2018-09-27 14:00:40 -0600 | [diff] [blame] | 32 | * Common code for the lexers. |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 33 | * |
| 34 | */ |
| 35 | |
Gavin Howard | 27fdfb9 | 2018-03-21 07:56:59 -0600 | [diff] [blame] | 36 | #include <assert.h> |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 37 | #include <ctype.h> |
| 38 | #include <stdbool.h> |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 39 | #include <string.h> |
| 40 | |
Gavin Howard | 2949306 | 2018-03-20 19:57:37 -0600 | [diff] [blame] | 41 | #include <status.h> |
Gavin Howard | 9f9bef4 | 2020-10-22 21:37:46 -0600 | [diff] [blame^] | 42 | #include "lex.h" |
| 43 | #include "vm.h" |
| 44 | #include "bc.h" |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 45 | |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 46 | void bc_lex_invalidChar(BcLex *l, char c) { |
Gavin Howard | 50c8c2d | 2018-12-27 11:58:34 -0700 | [diff] [blame] | 47 | l->t = BC_LEX_INVALID; |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 48 | bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c); |
Gavin Howard | 50c8c2d | 2018-12-27 11:58:34 -0700 | [diff] [blame] | 49 | } |
| 50 | |
Gavin Howard | ed5c831 | 2018-09-27 12:04:08 -0600 | [diff] [blame] | 51 | void bc_lex_lineComment(BcLex *l) { |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 52 | l->t = BC_LEX_WHITESPACE; |
Gavin Howard | 5c14da6 | 2019-02-16 23:47:48 -0700 | [diff] [blame] | 53 | while (l->i < l->len && l->buf[l->i] != '\n') l->i += 1; |
Gavin Howard | ed5c831 | 2018-09-27 12:04:08 -0600 | [diff] [blame] | 54 | } |
| 55 | |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 56 | void bc_lex_comment(BcLex *l) { |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 57 | |
| 58 | size_t i, nlines = 0; |
| 59 | const char *buf = l->buf; |
| 60 | bool end = false; |
| 61 | char c; |
| 62 | |
Gavin Howard | 5c14da6 | 2019-02-16 23:47:48 -0700 | [diff] [blame] | 63 | l->i += 1; |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 64 | l->t = BC_LEX_WHITESPACE; |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 65 | |
Gavin Howard | 5c14da6 | 2019-02-16 23:47:48 -0700 | [diff] [blame] | 66 | for (i = l->i; !end; i += !end) { |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 67 | |
Gavin Howard | 1ab22d2 | 2019-01-03 13:32:17 -0700 | [diff] [blame] | 68 | for (; (c = buf[i]) && c != '*'; ++i) nlines += (c == '\n'); |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 69 | |
Gavin Howard | ecafd4f | 2019-02-23 09:30:45 -0700 | [diff] [blame] | 70 | if (BC_ERR(!c || buf[i + 1] == '\0')) { |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 71 | l->i = i; |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 72 | bc_lex_err(l, BC_ERROR_PARSE_COMMENT); |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | end = buf[i + 1] == '/'; |
| 76 | } |
| 77 | |
| 78 | l->i = i + 2; |
| 79 | l->line += nlines; |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 80 | } |
| 81 | |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 82 | void bc_lex_whitespace(BcLex *l) { |
| 83 | char c; |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 84 | l->t = BC_LEX_WHITESPACE; |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 85 | for (c = l->buf[l->i]; c != '\n' && isspace(c); c = l->buf[++l->i]); |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 86 | } |
| 87 | |
Gavin Howard | 3ae1f8e | 2019-02-15 11:56:25 -0700 | [diff] [blame] | 88 | void bc_lex_commonTokens(BcLex *l, char c) { |
| 89 | if (!c) l->t = BC_LEX_EOF; |
| 90 | else if (c == '\n') l->t = BC_LEX_NLINE; |
| 91 | else bc_lex_whitespace(l); |
| 92 | } |
| 93 | |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 94 | static size_t bc_lex_num(BcLex *l, char start, bool int_only) { |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 95 | |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 96 | const char *buf = l->buf + l->i; |
Gavin Howard | 8dd307e | 2019-01-08 23:05:19 -0700 | [diff] [blame] | 97 | size_t i; |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 98 | char c; |
Gavin Howard | 94f1410 | 2019-01-11 09:39:57 -0700 | [diff] [blame] | 99 | bool last_pt, pt = (start == '.'); |
Gavin Howard | f2a4049 | 2018-03-05 11:27:29 -0700 | [diff] [blame] | 100 | |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 101 | for (i = 0; (c = buf[i]) && (BC_LEX_NUM_CHAR(c, pt, int_only) || |
Gavin Howard | 8dd307e | 2019-01-08 23:05:19 -0700 | [diff] [blame] | 102 | (c == '\\' && buf[i + 1] == '\n')); ++i) |
| 103 | { |
Gavin Howard | 25f6088 | 2019-01-11 09:20:51 -0700 | [diff] [blame] | 104 | if (c == '\\') { |
| 105 | |
| 106 | if (buf[i + 1] == '\n') { |
| 107 | |
| 108 | i += 2; |
| 109 | |
| 110 | // Make sure to eat whitespace at the beginning of the line. |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 111 | while(isspace(buf[i]) && buf[i] != '\n') i += 1; |
Gavin Howard | 25f6088 | 2019-01-11 09:20:51 -0700 | [diff] [blame] | 112 | |
| 113 | c = buf[i]; |
| 114 | |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 115 | if (!BC_LEX_NUM_CHAR(c, pt, int_only)) break; |
Gavin Howard | 25f6088 | 2019-01-11 09:20:51 -0700 | [diff] [blame] | 116 | } |
| 117 | else break; |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 118 | } |
Gavin Howard | 8dd307e | 2019-01-08 23:05:19 -0700 | [diff] [blame] | 119 | |
Gavin Howard | 94f1410 | 2019-01-11 09:39:57 -0700 | [diff] [blame] | 120 | last_pt = (c == '.'); |
Gavin Howard | 25f6088 | 2019-01-11 09:20:51 -0700 | [diff] [blame] | 121 | if (pt && last_pt) break; |
Gavin Howard | 94f1410 | 2019-01-11 09:39:57 -0700 | [diff] [blame] | 122 | pt = pt || last_pt; |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 123 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 124 | bc_vec_push(&l->str, &c); |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 125 | } |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 126 | |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 127 | return i; |
| 128 | } |
| 129 | |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 130 | void bc_lex_number(BcLex *l, char start) { |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 131 | |
| 132 | l->t = BC_LEX_NUMBER; |
| 133 | |
| 134 | bc_vec_npop(&l->str, l->str.len); |
| 135 | bc_vec_push(&l->str, &start); |
| 136 | |
| 137 | l->i += bc_lex_num(l, start, false); |
Gavin Howard | a652788 | 2019-02-19 20:23:17 -0700 | [diff] [blame] | 138 | |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 139 | #if BC_ENABLE_EXTRA_MATH |
| 140 | { |
| 141 | char c = l->buf[l->i]; |
| 142 | |
| 143 | if (c == 'e') { |
| 144 | |
Gavin Howard | a652788 | 2019-02-19 20:23:17 -0700 | [diff] [blame] | 145 | #if BC_ENABLED |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 146 | if (BC_IS_POSIX) bc_lex_err(l, BC_ERROR_POSIX_EXP_NUM); |
Gavin Howard | a652788 | 2019-02-19 20:23:17 -0700 | [diff] [blame] | 147 | #endif // BC_ENABLED |
| 148 | |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 149 | bc_vec_push(&l->str, &c); |
| 150 | l->i += 1; |
| 151 | c = l->buf[l->i]; |
| 152 | |
| 153 | if (c == BC_LEX_NEG_CHAR) { |
| 154 | bc_vec_push(&l->str, &c); |
| 155 | l->i += 1; |
| 156 | c = l->buf[l->i]; |
| 157 | } |
| 158 | |
Gavin Howard | ecafd4f | 2019-02-23 09:30:45 -0700 | [diff] [blame] | 159 | if (BC_ERR(!BC_LEX_NUM_CHAR(c, false, true))) |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 160 | bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c); |
Gavin Howard | 7ad5a66 | 2019-02-19 14:40:46 -0700 | [diff] [blame] | 161 | |
| 162 | l->i += bc_lex_num(l, 0, true); |
| 163 | } |
| 164 | } |
| 165 | #endif // BC_ENABLE_EXTRA_MATH |
| 166 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 167 | bc_vec_pushByte(&l->str, '\0'); |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 168 | } |
| 169 | |
Gavin Howard | 5c14da6 | 2019-02-16 23:47:48 -0700 | [diff] [blame] | 170 | void bc_lex_name(BcLex *l) { |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 171 | |
Gavin Howard | 88c2530 | 2018-10-17 13:32:23 -0600 | [diff] [blame] | 172 | size_t i = 0; |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 173 | const char *buf = l->buf + l->i - 1; |
Gavin Howard | 88c2530 | 2018-10-17 13:32:23 -0600 | [diff] [blame] | 174 | char c = buf[i]; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 175 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 176 | l->t = BC_LEX_NAME; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 177 | |
Gavin Howard | 9a4b6cd | 2018-10-23 15:13:30 -0600 | [diff] [blame] | 178 | while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i]; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 179 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 180 | bc_vec_string(&l->str, i, buf); |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 181 | |
| 182 | // Increment the index. We minus 1 because it has already been incremented. |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 183 | l->i += i - 1; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 184 | } |
| 185 | |
Gavin Howard | 48354e8 | 2019-01-02 18:15:56 -0700 | [diff] [blame] | 186 | void bc_lex_init(BcLex *l) { |
Gavin Howard | bbd6f55 | 2020-05-22 19:39:13 -0600 | [diff] [blame] | 187 | BC_SIG_ASSERT_LOCKED; |
Gavin Howard | fe9a302 | 2019-06-21 20:40:45 -0600 | [diff] [blame] | 188 | assert(l != NULL); |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 189 | bc_vec_init(&l->str, sizeof(char), NULL); |
Gavin Howard | 6918504 | 2018-09-10 15:46:20 -0600 | [diff] [blame] | 190 | } |
| 191 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 192 | void bc_lex_free(BcLex *l) { |
Gavin Howard | bbd6f55 | 2020-05-22 19:39:13 -0600 | [diff] [blame] | 193 | BC_SIG_ASSERT_LOCKED; |
Gavin Howard | fe9a302 | 2019-06-21 20:40:45 -0600 | [diff] [blame] | 194 | assert(l != NULL); |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 195 | bc_vec_free(&l->str); |
Gavin Howard | 6918504 | 2018-09-10 15:46:20 -0600 | [diff] [blame] | 196 | } |
| 197 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 198 | void bc_lex_file(BcLex *l, const char *file) { |
Gavin Howard | fe9a302 | 2019-06-21 20:40:45 -0600 | [diff] [blame] | 199 | assert(l != NULL && file != NULL); |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 200 | l->line = 1; |
Gavin Howard | 3e9a844 | 2020-05-15 13:27:18 -0600 | [diff] [blame] | 201 | vm.file = file; |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 202 | } |
| 203 | |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 204 | void bc_lex_next(BcLex *l) { |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 205 | |
Gavin Howard | fe9a302 | 2019-06-21 20:40:45 -0600 | [diff] [blame] | 206 | assert(l != NULL); |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 207 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 208 | l->last = l->t; |
Gavin Howard | 5615879 | 2019-01-14 12:03:14 -0700 | [diff] [blame] | 209 | l->line += (l->i != 0 && l->buf[l->i - 1] == '\n'); |
Gavin Howard | 7536dcf | 2018-12-15 19:27:09 -0700 | [diff] [blame] | 210 | |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 211 | if (BC_ERR(l->last == BC_LEX_EOF)) bc_lex_err(l, BC_ERROR_PARSE_EOF); |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 212 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 213 | l->t = BC_LEX_EOF; |
Gavin Howard | c39fd49 | 2018-10-04 10:07:03 -0600 | [diff] [blame] | 214 | |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 215 | if (l->i == l->len) return; |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 216 | |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 217 | // Loop until failure or we don't have whitespace. This |
| 218 | // is so the parser doesn't get inundated with whitespace. |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 219 | do { |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 220 | vm.next(l); |
| 221 | } while (l->t == BC_LEX_WHITESPACE); |
Gavin Howard | 3575392 | 2018-03-21 19:22:08 -0600 | [diff] [blame] | 222 | } |
Gavin Howard | c9a9c47 | 2018-10-02 17:23:01 -0600 | [diff] [blame] | 223 | |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 224 | void bc_lex_text(BcLex *l, const char *text) { |
Gavin Howard | fe9a302 | 2019-06-21 20:40:45 -0600 | [diff] [blame] | 225 | assert(l != NULL && text != NULL); |
Gavin Howard | 890d0c0 | 2018-10-30 16:34:50 -0600 | [diff] [blame] | 226 | l->buf = text; |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 227 | l->i = 0; |
Gavin Howard | c9a9c47 | 2018-10-02 17:23:01 -0600 | [diff] [blame] | 228 | l->len = strlen(text); |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 229 | l->t = l->last = BC_LEX_INVALID; |
Gavin Howard | 5a32189 | 2020-05-16 17:27:55 -0600 | [diff] [blame] | 230 | bc_lex_next(l); |
Gavin Howard | c9a9c47 | 2018-10-02 17:23:01 -0600 | [diff] [blame] | 231 | } |