From 13d591df4ae3dc07f3be8477f7bcbed296d7bc40 Mon Sep 17 00:00:00 2001 From: Lorenzo Torres Date: Tue, 9 Jun 2026 08:45:29 +0200 Subject: feat: first commit --- .gitignore | 2 + Makefile | 16 +++ config.mk | 3 + examples/hello_world.ll | 7 ++ lc.c | 20 ++++ lexer.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++ lexer.h | 93 +++++++++++++++++ 7 files changed, 410 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 config.mk create mode 100644 examples/hello_world.ll create mode 100644 lc.c create mode 100644 lexer.c create mode 100644 lexer.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2f1174 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/*.o +lc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d98a13d --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +include config.mk + +SRC:=lc.c\ + lexer.c + +OBJ:=$(SRC:.c=.o) + +all: $(OBJ) + $(CC) $(OBJ) -o lc + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +.PHONY: clean +clean: + @rm -rfv $(OBJ) lc diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..8b2673c --- /dev/null +++ b/config.mk @@ -0,0 +1,3 @@ +CC:=cc +CFLAGS:=-Wall -Wextra -pedantic -std=c99 -ggdb -O2 + diff --git a/examples/hello_world.ll b/examples/hello_world.ll new file mode 100644 index 0000000..9fe2910 --- /dev/null +++ b/examples/hello_world.ll @@ -0,0 +1,7 @@ +#include + +i32 main() { + printf("Hello world!\n"); + + return 0; +} diff --git a/lc.c b/lc.c new file mode 100644 index 0000000..651ed95 --- /dev/null +++ b/lc.c @@ -0,0 +1,20 @@ +#include +#include +#include "lexer.h" + +int main(void) +{ + char *src = "12.3"; + token *head = lexer_parse(src); + + if (!head) { + printf("Parsing error!\n"); + exit(1); + } + while (head->next) { + head = head->next; + printf("%d -> %.*s\n", head->type, head->length, head->lexeme); + free(head->prev); + } + return 0; +} 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 +#include +#include + +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; +} diff --git a/lexer.h b/lexer.h new file mode 100644 index 0000000..6ebdb01 --- /dev/null +++ b/lexer.h @@ -0,0 +1,93 @@ +#ifndef LEXER_H +#define LEXER_H + +#include +#include + +typedef enum { + TOKEN_HEAD, + + TOKEN_IDENTIFIER, + TOKEN_INTEGER, + TOKEN_FLOAT, + TOKEN_STRING, + TOKEN_CHAR, + + TOKEN_TRUE, + TOKEN_FALSE, + + TOKEN_IF, + TOKEN_ELSE, + TOKEN_WHILE, + TOKEN_FOR, + TOKEN_BREAK, + TOKEN_CASE, + TOKEN_CONST, + TOKEN_DO, + TOKEN_ENUM, + TOKEN_EXTERN, + TOKEN_GOTO, + TOKEN_INLINE, + TOKEN_RETURN, + TOKEN_SIGNED, + TOKEN_UNSIGNED, + TOKEN_STATIC, + TOKEN_STRUCT, + TOKEN_SWITCH, + TOKEN_TYPEDEF, + TOKEN_UNION, + TOKEN_VOLATILE, + + TOKEN_COLON, + TOKEN_SEMICOLON, + TOKEN_COMMA, + TOKEN_DOT, + TOKEN_ARROW, + TOKEN_ASSIGN, + TOKEN_EQUAL, + TOKEN_LSHIFT, + TOKEN_RSHIFT, + TOKEN_LSHIFT_EQ, + TOKEN_RSHIFT_EQ, + TOKEN_XOR, + TOKEN_XOR_EQ, + TOKEN_OR, + TOKEN_BOR, + TOKEN_BOR_EQ, + TOKEN_NOT, + TOKEN_NOT_EQ, + TOKEN_GREATER, + TOKEN_GREATER_EQ, + TOKEN_LESS, + TOKEN_LESS_EQ, + TOKEN_LPAREN, + TOKEN_RPAREN, + TOKEN_LSQUARE, + TOKEN_RSQUARE, + TOKEN_LCURLY, + TOKEN_RCURLY, + TOKEN_PLUS, + TOKEN_MINUS, + TOKEN_STAR, + TOKEN_SLASH, + TOKEN_PLUS_EQ, + TOKEN_PLUS_PLUS, + TOKEN_MINUS_EQ, + TOKEN_MINUS_MINUS, + TOKEN_STAR_EQ, + TOKEN_SLASH_EQ, + TOKEN_REF, +} token_type; + +typedef struct _token { + char *lexeme; + size_t length; + token_type type; + + struct _token *next; + struct _token *prev; +} token; + +token *lexer_parse(char *src); + +#endif -- cgit v1.2.3