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 | b5904bf | 2018-02-20 13:28:18 -0700 | [diff] [blame] | 4 | * Copyright 2018 Gavin D. Howard |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 5 | * |
| 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 Howard | b5904bf | 2018-02-20 13:28:18 -0700 | [diff] [blame] | 17 | * ***************************************************************************** |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 18 | * |
Gavin Howard | d2a0525 | 2018-09-27 14:00:40 -0600 | [diff] [blame] | 19 | * Common code for the lexers. |
Gavin Howard | 5715b04 | 2018-02-12 16:11:42 -0700 | [diff] [blame] | 20 | * |
| 21 | */ |
| 22 | |
Gavin Howard | 27fdfb9 | 2018-03-21 07:56:59 -0600 | [diff] [blame] | 23 | #include <assert.h> |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 24 | #include <ctype.h> |
| 25 | #include <stdbool.h> |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 26 | #include <string.h> |
| 27 | |
Gavin Howard | 2949306 | 2018-03-20 19:57:37 -0600 | [diff] [blame] | 28 | #include <status.h> |
Gavin Howard | 3ba6c8d | 2018-02-15 12:23:35 -0700 | [diff] [blame] | 29 | #include <lex.h> |
Gavin Howard | d555167 | 2018-09-22 19:52:42 -0600 | [diff] [blame] | 30 | #include <vm.h> |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 31 | |
Gavin Howard | 50c8c2d | 2018-12-27 11:58:34 -0700 | [diff] [blame] | 32 | BcStatus bc_lex_invalidChar(BcLex *l, char c) { |
| 33 | l->t = BC_LEX_INVALID; |
| 34 | return bc_vm_error(BC_ERROR_PARSE_CHAR, l->line, c); |
| 35 | } |
| 36 | |
Gavin Howard | ed5c831 | 2018-09-27 12:04:08 -0600 | [diff] [blame] | 37 | void bc_lex_lineComment(BcLex *l) { |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 38 | l->t = BC_LEX_WHITESPACE; |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 39 | while (l->i < l->len && l->buf[l->i++] != '\n'); |
Gavin Howard | ed5c831 | 2018-09-27 12:04:08 -0600 | [diff] [blame] | 40 | } |
| 41 | |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 42 | BcStatus bc_lex_comment(BcLex *l) { |
| 43 | |
| 44 | size_t i, nlines = 0; |
| 45 | const char *buf = l->buf; |
| 46 | bool end = false; |
| 47 | char c; |
| 48 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 49 | l->t = BC_LEX_WHITESPACE; |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 50 | |
| 51 | for (i = ++l->i; !end; i += !end) { |
| 52 | |
| 53 | for (c = buf[i]; c != '*' && c != 0; c = buf[++i]) nlines += c == '\n'; |
| 54 | |
| 55 | if (c == 0 || buf[i + 1] == '\0') { |
| 56 | l->i = i; |
Gavin Howard | 8e30663 | 2018-12-21 12:55:35 -0700 | [diff] [blame] | 57 | return bc_vm_error(BC_ERROR_PARSE_COMMENT, l->line); |
Gavin Howard | 52446f2 | 2018-12-13 11:39:22 -0700 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | end = buf[i + 1] == '/'; |
| 61 | } |
| 62 | |
| 63 | l->i = i + 2; |
| 64 | l->line += nlines; |
| 65 | |
| 66 | return BC_STATUS_SUCCESS; |
| 67 | } |
| 68 | |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 69 | void bc_lex_whitespace(BcLex *l) { |
| 70 | char c; |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 71 | l->t = BC_LEX_WHITESPACE; |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 72 | 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] | 73 | } |
| 74 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 75 | BcStatus bc_lex_number(BcLex *l, char start) { |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 76 | |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 77 | const char *buf = l->buf + l->i; |
Gavin Howard | c39fd49 | 2018-10-04 10:07:03 -0600 | [diff] [blame] | 78 | size_t len, hits = 0, bslashes = 0, i = 0, j; |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 79 | char c = buf[i]; |
| 80 | bool last_pt, pt = start == '.'; |
Gavin Howard | f2a4049 | 2018-03-05 11:27:29 -0700 | [diff] [blame] | 81 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 82 | last_pt = pt; |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 83 | l->t = BC_LEX_NUMBER; |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 84 | |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 85 | while (c != 0 && (isdigit(c) || (c >= 'A' && c <= 'F') || |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 86 | (c == '.' && !pt) || (c == '\\' && buf[i + 1] == '\n'))) |
| 87 | { |
| 88 | if (c != '\\') { |
| 89 | last_pt = c == '.'; |
| 90 | pt = pt || last_pt; |
| 91 | } |
| 92 | else { |
| 93 | ++i; |
| 94 | bslashes += 1; |
| 95 | } |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 96 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 97 | c = buf[++i]; |
| 98 | } |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 99 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 100 | len = i + 1 * !last_pt - bslashes * 2; |
Gavin Howard | 7536dcf | 2018-12-15 19:27:09 -0700 | [diff] [blame] | 101 | |
| 102 | if (len > BC_MAX_NUM) return bc_vm_error(BC_ERROR_EXEC_NUM_LEN, l->line); |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 103 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 104 | bc_vec_npop(&l->str, l->str.len); |
| 105 | bc_vec_expand(&l->str, len + 1); |
| 106 | bc_vec_push(&l->str, &start); |
Gavin Howard | a628aa2 | 2018-09-12 13:52:45 -0600 | [diff] [blame] | 107 | |
Gavin Howard | c39fd49 | 2018-10-04 10:07:03 -0600 | [diff] [blame] | 108 | for (buf -= 1, j = 1; j < len + hits * 2; ++j) { |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 109 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 110 | c = buf[j]; |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 111 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 112 | // If we have hit a backslash, skip it. We don't have |
| 113 | // to check for a newline because it's guaranteed. |
| 114 | if (hits < bslashes && c == '\\') { |
| 115 | ++hits; |
| 116 | ++j; |
| 117 | continue; |
| 118 | } |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 119 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 120 | bc_vec_push(&l->str, &c); |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 121 | } |
Gavin Howard | 07732ec | 2018-02-27 15:40:02 -0700 | [diff] [blame] | 122 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 123 | bc_vec_pushByte(&l->str, '\0'); |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 124 | l->i += i; |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 125 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 126 | return BC_STATUS_SUCCESS; |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 127 | } |
| 128 | |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 129 | BcStatus bc_lex_name(BcLex *l) { |
| 130 | |
Gavin Howard | 88c2530 | 2018-10-17 13:32:23 -0600 | [diff] [blame] | 131 | size_t i = 0; |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 132 | const char *buf = l->buf + l->i - 1; |
Gavin Howard | 88c2530 | 2018-10-17 13:32:23 -0600 | [diff] [blame] | 133 | char c = buf[i]; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 134 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 135 | l->t = BC_LEX_NAME; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 136 | |
Gavin Howard | 9a4b6cd | 2018-10-23 15:13:30 -0600 | [diff] [blame] | 137 | while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i]; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 138 | |
Gavin Howard | 7536dcf | 2018-12-15 19:27:09 -0700 | [diff] [blame] | 139 | if (i > BC_MAX_STRING) return bc_vm_error(BC_ERROR_EXEC_NAME_LEN, l->line); |
| 140 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 141 | bc_vec_string(&l->str, i, buf); |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 142 | |
| 143 | // Increment the index. We minus 1 because it has already been incremented. |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 144 | l->i += i - 1; |
Gavin Howard | 8412ba8 | 2018-10-04 13:03:24 -0600 | [diff] [blame] | 145 | |
| 146 | return BC_STATUS_SUCCESS; |
| 147 | } |
| 148 | |
Gavin Howard | ad0ecfe | 2018-10-30 01:16:01 -0600 | [diff] [blame] | 149 | void bc_lex_init(BcLex *l, BcLexNext next) { |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 150 | assert(l); |
Gavin Howard | ed5c831 | 2018-09-27 12:04:08 -0600 | [diff] [blame] | 151 | l->next = next; |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 152 | bc_vec_init(&l->str, sizeof(char), NULL); |
Gavin Howard | 6918504 | 2018-09-10 15:46:20 -0600 | [diff] [blame] | 153 | } |
| 154 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 155 | void bc_lex_free(BcLex *l) { |
| 156 | assert(l); |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 157 | bc_vec_free(&l->str); |
Gavin Howard | 6918504 | 2018-09-10 15:46:20 -0600 | [diff] [blame] | 158 | } |
| 159 | |
Gavin Howard | 6373820 | 2018-09-26 15:34:20 -0600 | [diff] [blame] | 160 | void bc_lex_file(BcLex *l, const char *file) { |
| 161 | assert(l && file); |
| 162 | l->line = 1; |
Gavin Howard | 7536dcf | 2018-12-15 19:27:09 -0700 | [diff] [blame] | 163 | vm->file = file; |
Gavin Howard | 8a596d4 | 2018-01-15 15:46:01 -0700 | [diff] [blame] | 164 | } |
| 165 | |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 166 | BcStatus bc_lex_next(BcLex *l) { |
| 167 | |
| 168 | BcStatus s; |
| 169 | |
| 170 | assert(l); |
| 171 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 172 | l->last = l->t; |
Gavin Howard | f9b86ee | 2018-12-28 13:18:27 -0700 | [diff] [blame^] | 173 | l->line += l->last == BC_LEX_NLINE; |
Gavin Howard | 7536dcf | 2018-12-15 19:27:09 -0700 | [diff] [blame] | 174 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 175 | if (l->last == BC_LEX_EOF) return bc_vm_error(BC_ERROR_PARSE_EOF, l->line); |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 176 | |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 177 | l->t = BC_LEX_EOF; |
Gavin Howard | c39fd49 | 2018-10-04 10:07:03 -0600 | [diff] [blame] | 178 | |
Gavin Howard | f9b86ee | 2018-12-28 13:18:27 -0700 | [diff] [blame^] | 179 | if (l->i == l->len) return BC_STATUS_SUCCESS; |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 180 | |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 181 | // Loop until failure or we don't have whitespace. This |
| 182 | // is so the parser doesn't get inundated with whitespace. |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 183 | do { |
| 184 | s = l->next(l); |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 185 | } while (!s && l->t == BC_LEX_WHITESPACE); |
Gavin Howard | 364df3b | 2018-09-28 09:48:19 -0600 | [diff] [blame] | 186 | |
| 187 | return s; |
Gavin Howard | 3575392 | 2018-03-21 19:22:08 -0600 | [diff] [blame] | 188 | } |
Gavin Howard | c9a9c47 | 2018-10-02 17:23:01 -0600 | [diff] [blame] | 189 | |
| 190 | BcStatus bc_lex_text(BcLex *l, const char *text) { |
| 191 | assert(l && text); |
Gavin Howard | 890d0c0 | 2018-10-30 16:34:50 -0600 | [diff] [blame] | 192 | l->buf = text; |
Gavin Howard | 53eba8b | 2018-10-31 15:14:37 -0600 | [diff] [blame] | 193 | l->i = 0; |
Gavin Howard | c9a9c47 | 2018-10-02 17:23:01 -0600 | [diff] [blame] | 194 | l->len = strlen(text); |
Gavin Howard | ad47731 | 2018-12-24 15:51:35 -0700 | [diff] [blame] | 195 | l->t = l->last = BC_LEX_INVALID; |
Gavin Howard | c9a9c47 | 2018-10-02 17:23:01 -0600 | [diff] [blame] | 196 | return bc_lex_next(l); |
| 197 | } |