summaryrefslogtreecommitdiff
path: root/lexer.c
diff options
context:
space:
mode:
Diffstat (limited to 'lexer.c')
-rw-r--r--lexer.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/lexer.c b/lexer.c
new file mode 100644
index 0000000..87e146a
--- /dev/null
+++ b/lexer.c
@@ -0,0 +1,269 @@
+#include "lexer.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+
+static token *token_create(char *lexeme, size_t length, token_type type)
+{
+ token *t = calloc(1, sizeof(token));
+ t->lexeme = lexeme;
+ t->length = length;
+ t->type = type;
+
+ return t;
+}
+
+static void token_add(token **t, token *new)
+{
+ if (t) {
+ (*t)->next = new;
+ new->prev = (*t);
+ }
+
+ *t = new;
+}
+
+size_t check_symbol(char *src, size_t index, token **last)
+{
+ switch(src[index]) {
+ case '+': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_PLUS_EQ));
+ return 2;
+ } else if (src[index+1] == '+') {
+ token_add(last, token_create(src+index, 2, TOKEN_PLUS_PLUS));
+ return 2;
+
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_PLUS));
+ return 1;
+ }
+ }
+ case '-': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_MINUS_EQ));
+ return 2;
+ } else if (src[index+1] == '-') {
+ token_add(last, token_create(src+index, 2, TOKEN_MINUS_MINUS));
+ return 2;
+
+ } else if (src[index+1] == '>') {
+ token_add(last, token_create(src+index, 2, TOKEN_ARROW));
+ return 2;
+
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_MINUS));
+ return 1;
+ }
+ }
+ case '/': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_SLASH_EQ));
+ return 2;
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_SLASH));
+ return 1;
+ }
+ }
+ case '*': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_STAR_EQ));
+ return 2;
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_STAR));
+ return 1;
+ }
+ }
+ case '>': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_GREATER_EQ));
+ return 2;
+ } else if (src[index+1] == '>') {
+ if (src[index+2] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_RSHIFT_EQ));
+ return 2;
+ } else {
+ token_add(last, token_create(src+index, 2, TOKEN_RSHIFT));
+ return 2;
+ }
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_GREATER));
+ return 1;
+ }
+ }
+ case '<': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_LESS_EQ));
+ return 2;
+ } else if (src[index+1] == '<') {
+ if (src[index+2] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_LSHIFT_EQ));
+ return 2;
+ } else {
+ token_add(last, token_create(src+index, 2, TOKEN_LSHIFT));
+ return 2;
+ }
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_LESS));
+ return 1;
+ }
+ }
+ case '!': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_NOT_EQ));
+ return 2;
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_NOT));
+ return 1;
+ }
+ }
+ case '=': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_EQUAL));
+ return 2;
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_ASSIGN));
+ return 1;
+ }
+ }
+
+ case '|': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_BOR_EQ));
+ return 2;
+ } else if (src[index+1] == '|') {
+ token_add(last, token_create(src+index, 2, TOKEN_OR));
+ return 2;
+
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_BOR));
+ return 1;
+ }
+ }
+ case '^': {
+ if (src[index+1] == '=') {
+ token_add(last, token_create(src+index, 2, TOKEN_XOR_EQ));
+ return 2;
+ } else {
+ token_add(last, token_create(src+index, 1, TOKEN_XOR));
+ return 1;
+ }
+ }
+ case '{':
+ token_add(last, token_create(src+index, 1, TOKEN_LCURLY));
+ case '}':
+ token_add(last, token_create(src+index, 1, TOKEN_RCURLY));
+ case '(':
+ token_add(last, token_create(src+index, 1, TOKEN_LPAREN));
+ case ')':
+ token_add(last, token_create(src+index, 1, TOKEN_RPAREN));
+ case '[':
+ token_add(last, token_create(src+index, 1, TOKEN_LSQUARE));
+ case ']':
+ token_add(last, token_create(src+index, 1, TOKEN_RSQUARE));
+ case ':':
+ token_add(last, token_create(src+index, 1, TOKEN_COLON));
+ case ';':
+ token_add(last, token_create(src+index, 1, TOKEN_SEMICOLON));
+ case ',':
+ token_add(last, token_create(src+index, 1, TOKEN_COMMA));
+ case '.':
+ token_add(last, token_create(src+index, 1, TOKEN_DOT));
+
+ default:
+ return 0;
+ }
+}
+
+token *lexer_parse(char *src)
+{
+ size_t index = 0;
+ token *head = token_create(src, 0, TOKEN_HEAD);
+ token *last = head;
+ while (src[index] != '\0') {
+ if (isspace(src[index])) {
+ index++;
+ continue;
+ }
+
+ size_t skip = check_symbol(src, index, &last);
+ if (skip) {
+ index += skip;
+ continue;
+ }
+
+ if (src[index] == '\'') {
+ if (src[index+1] != '\\' && src[index+2] != '\'') {
+ // TODO: error
+ return NULL;
+ }
+ token_add(&last, token_create(src+index+1, 1, TOKEN_CHAR));
+ index += 3;
+ continue;
+ }
+
+ if (src[index] == '\"') {
+ index++;
+ char *string_start = src+index;
+ size_t string_length = index;
+ while (src[index++] != '\"') {
+ if (src[index] == '\0') {
+ // TODO: error
+ return NULL;
+ }
+ }
+ string_length = index - string_length - 1;
+ token_add(&last, token_create(string_start, string_length, TOKEN_STRING));
+ continue;
+ }
+
+ if (isalpha(src[index]) || src[index] == '_') {
+ char *identifier_start = src+index;
+ size_t identifier_length = index;
+ while (isalnum(src[index]) || src[index] == '_') {
+ index++;
+ }
+ identifier_length = index - identifier_length;
+ token_add(&last, token_create(identifier_start, identifier_length, TOKEN_IDENTIFIER));
+ continue;
+ }
+
+ if (isdigit(src[index])) {
+ char *number_start = src+index;
+ size_t number_length = index;
+ uint8_t is_float = 0;
+
+ if (src[index] == '0' && !isdigit(src[index+1]) && src[index+1] != '.') {
+ if (src[index+1] == 'x') {
+ index += 2;
+ } else if (src[index+1] == 'o') {
+ index += 2;
+ } else if (src[index+1] == 'b') {
+ index += 2;
+ } else {
+ // TODO: error
+ return NULL;
+ }
+ }
+ index++;
+ while (isdigit(src[index]) || src[index] == '.') {
+ if (src[index] == '.') {
+ if (is_float) {
+ // TODO: error
+ return NULL;
+ }
+ is_float = 1;
+ }
+ index++;
+ }
+
+
+ number_length = index - number_length;
+ token_add(&last, token_create(number_start, number_length, is_float ? TOKEN_FLOAT : TOKEN_INTEGER));
+ continue;
+ }
+ index ++;
+ }
+
+ return head;
+}