summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Torres <lorenzotorres@outlook.it>2026-06-09 08:45:29 +0200
committerLorenzo Torres <lorenzotorres@outlook.it>2026-06-09 08:45:29 +0200
commit13d591df4ae3dc07f3be8477f7bcbed296d7bc40 (patch)
tree77a0f78763bff3c03c6282d961ea2c777fe133c9
feat: first commit
-rw-r--r--.gitignore2
-rw-r--r--Makefile16
-rw-r--r--config.mk3
-rw-r--r--examples/hello_world.ll7
-rw-r--r--lc.c20
-rw-r--r--lexer.c269
-rw-r--r--lexer.h93
7 files changed, 410 insertions, 0 deletions
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 <stdio.h>
+
+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 <stdio.h>
+#include <stdlib.h>
+#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 <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;
+}
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 <stddef.h>
+#include <stdint.h>
+
+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