blob: 9a24b7a2d6b830683e841cde9d91d3825567b524 [file] [log] [blame]
/*
* *****************************************************************************
*
* Copyright 2018 Gavin D. Howard
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* *****************************************************************************
*
* The lexer.
*
*/
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <string.h>
#include <status.h>
#include <lex.h>
#include <vm.h>
BcStatus bc_lex_comment(BcLex *l) {
size_t i, nls = 0;
const char *buf = l->buffer;
bool end = false;
l->t.t = BC_LEX_WHITESPACE;
for (i = ++l->idx; !end; i += !end) {
char c;
for (; (c = buf[i]) != '*' && c != '\0'; ++i) nls += (c == '\n');
if (c == '\0' || buf[i + 1] == '\0') {
l->idx = i;
return BC_STATUS_LEX_NO_COMMENT_END;
}
end = buf[i + 1] == '/';
}
l->idx = i + 2;
l->line += nls;
return BC_STATUS_SUCCESS;
}
void bc_lex_lineComment(BcLex *l) {
l->t.t = BC_LEX_WHITESPACE;
while (l->idx < l->len && l->buffer[l->idx++] != '\n');
--l->idx;
}
BcStatus bc_lex_number(BcLex *l, char start) {
BcStatus s;
const char *buf = l->buffer + l->idx;
size_t len, hits, bslashes = 0, i = 0, j;
char c = buf[i];
bool last_pt, pt = start == '.';
last_pt = pt;
l->t.t = BC_LEX_NUMBER;
while (c && ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
(c == '.' && !pt) || (c == '\\' && buf[i + 1] == '\n')))
{
if (c != '\\') {
last_pt = c == '.';
pt = pt || last_pt;
}
else {
++i;
bslashes += 1;
}
c = buf[++i];
}
len = i + 1 * !last_pt - bslashes * 2;
if (len > BC_MAX_NUM) return BC_STATUS_EXEC_NUM_LEN;
bc_vec_npop(&l->t.v, l->t.v.len);
if ((s = bc_vec_expand(&l->t.v, len + 1)))
return s;
if ((s = bc_vec_push(&l->t.v, 1, &start))) return s;
buf -= 1;
hits = 0;
for (j = 1; j < len + hits * 2; ++j) {
c = buf[j];
// If we have hit a backslash, skip it. We don't have
// to check for a newline because it's guaranteed.
if (hits < bslashes && c == '\\') {
++hits;
++j;
continue;
}
if ((s = bc_vec_push(&l->t.v, 1, &c))) return s;
}
if ((s = bc_vec_pushByte(&l->t.v, '\0'))) return s;
l->idx += i;
return BC_STATUS_SUCCESS;
}
BcStatus bc_lex_name(BcLex *l) {
BcStatus s;
size_t i;
char c;
const char *buf = l->buffer + l->idx - 1;
for (i = 0; i < sizeof(bc_lex_kws) / sizeof(bc_lex_kws[0]); ++i) {
unsigned long len = (unsigned long) bc_lex_kws[i].len;
if (!strncmp(buf, bc_lex_kws[i].name, len)) {
l->t.t = BC_LEX_KEY_AUTO + (BcLexToken) i;
if (!bc_lex_kws[i].posix &&
(s = bc_vm_posix_error(BC_STATUS_POSIX_BAD_KEYWORD, l->file,
l->line, bc_lex_kws[i].name)))
{
return s;
}
// We minus 1 because the index has already been incremented.
l->idx += len - 1;
return BC_STATUS_SUCCESS;
}
}
l->t.t = BC_LEX_NAME;
i = 0;
c = buf[i];
while ((c >= 'a' && c<= 'z') || (c >= '0' && c <= '9') || c == '_')
c = buf[++i];
if (i > 1 && (s = bc_vm_posix_error(BC_STATUS_POSIX_NAME_LEN,
l->file, l->line, buf)))
{
return s;
}
if (i > BC_MAX_STRING) return BC_STATUS_EXEC_NAME_LEN;
if ((s = bc_vec_string(&l->t.v, i, buf))) return s;
// Increment the index. We minus 1 because it has already been incremented.
l->idx += i - 1;
return BC_STATUS_SUCCESS;
}
BcStatus bc_lex_init(BcLex *l, BcLexNext next) {
assert(l);
l->next = next;
return bc_vec_init(&l->t.v, sizeof(uint8_t), NULL);
}
void bc_lex_free(BcLex *l) {
assert(l);
bc_vec_free(&l->t.v);
}
void bc_lex_file(BcLex *l, const char *file) {
assert(l && file);
l->line = 1;
l->newline = false;
l->file = file;
}
BcStatus bc_lex_text(BcLex *l, const char *text) {
assert(l && text);
l->buffer = text;
l->idx = 0;
l->len = strlen(text);
l->t.t = BC_LEX_INVALID;
return l->next(l);
}