summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.gitignore2
-rwxr-xr-xbuild.sh52
-rwxr-xr-xsource/base/base.h73
-rwxr-xr-xsource/base/base_arena.c138
-rwxr-xr-xsource/base/base_arena.h26
-rw-r--r--source/base/base_hash.c12
-rwxr-xr-xsource/base/base_include.h27
-rw-r--r--source/base/base_io.h11
-rw-r--r--source/base/base_mem.c0
-rw-r--r--source/base/base_mem.h26
-rw-r--r--source/base/base_os.h38
-rwxr-xr-xsource/base/base_stack.c187
-rwxr-xr-xsource/base/base_stack.h22
-rw-r--r--source/base/base_string.h59
-rw-r--r--source/base/base_test.h74
-rw-r--r--source/base/bash_hash.h15
-rw-r--r--source/fajr/fajr_main.c41
-rw-r--r--source/fajr/fajr_main.h6
-rw-r--r--source/fajr_lexer/fajr_lexer.c274
-rw-r--r--source/fajr_lexer/fajr_lexer.h119
-rw-r--r--source/fajr_parser/fajr_parser.c364
-rw-r--r--source/fajr_parser/fajr_parser.h60
-rw-r--r--tests/main.fajr6
23 files changed, 1632 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..59d28ca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
1.idea
2/build/compiler
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..678f327
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,52 @@
1#!/bin/bash
2
3set -eu
4
5DIR="$(dirname "$(readlink -f "$0")")"
6cd "$DIR"
7
8BUILD="build"
9mkdir -p "$BUILD"
10
11COMPILER="clang"
12CLEAN=0
13ASAN=0
14DEBUG=1
15RELEASE=0
16TARGET_FAJR=0
17
18[ "$#" = 0 ] && TARGET_FAJR=1
19for ARG in "$@"; do eval "$ARG=1" 2>/dev/null || :; done
20
21[ "$RELEASE" = 1 ] && DEBUG=0
22
23compile() {
24 local SOURCE="$1"
25 local OUT="$2"
26 local EXTRA="${3:-}"
27
28 local COMMON="-I$DIR -I$DIR/source -D_GNU_SOURCE=1 -fno-threadsafe-statics -nostdinc++"
29 local WARNINGS="-Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unused-parameter -Wno-missing-field-initializers -Wno-main-return-type -Wno-unused-variable -Wno-unused-but-set-variable -Wno-switch -Wno-unused-function"
30 local CLANG="-fno-omit-frame-pointer -fdiagnostics-absolute-paths -Wno-null-dereference"
31 local LINKER="-lm -ldl -lpthread"
32
33 local FLAGS="$COMMON $WARNINGS $CLANG"
34
35 [ "$DEBUG" = 1 ] && FLAGS="$FLAGS -g -ggdb3"
36 [ "$RELEASE" = 1 ] && FLAGS="$FLAGS -O3"
37 [ "$ASAN" = 1 ] && FLAGS="$FLAGS -fsanitize=address -fsanitize-trap -Isource"
38
39 echo "Building $OUT..."
40 $COMPILER $FLAGS $SOURCE $EXTRA $LINKER -o "$BUILD/$OUT"
41}
42
43if [ "$CLEAN" = 1 ]; then
44 rm -rf "$BUILD"/*
45 echo "Cleaned $BUILD"
46fi
47
48if [ "$TARGET_FAJR" = 1 ] || [ "${fajr:-0}" = 1 ]; then
49 compile "./source/fajr/fajr_main.c" "compiler"
50fi
51
52echo "Done."
diff --git a/source/base/base.h b/source/base/base.h
new file mode 100755
index 0000000..ef23391
--- /dev/null
+++ b/source/base/base.h
@@ -0,0 +1,73 @@
1#ifndef BASE_H
2#define BASE_H
3
4/* assert an expression and output the file and the line */
5
6#define internal static
7#define global_variable static
8#define local_persist static
9
10#define ERR_OK 0
11#define ERR_IO 1
12#define ERR_PARSE 2
13#define ERR_PERM 3
14#define ERR_INVALID 4
15
16#define KiB(n) (((u64)(n)) << 10)
17#define MiB(n) (((u64)(n)) << 20)
18#define GiB(n) (((u64)(n)) << 30)
19
20#define unused(x) (void)(x)
21
22#define PATH_MAX_LEN 128
23#define BUFF_SMALL 128
24#define BUFF_DEFAULT 256
25#define BUFF_LARGE 512
26
27#define NIL 0
28
29#define DEPRECATED __attribute__((__deprecated__))
30
31#if defined(__arm__) || defined(__aarch64__)
32#define breakpoint __asm__ volatile("brk #0");
33#define temp_breakpoint __asm__ volatile("udf #0");
34#elif defined(__i386__) || defined(__x86_64__)
35#define breakpoint __asm__ volatile("int3");
36#endif
37
38#define MemCpy(dest, src, len) memcpy((dest), (src), (len))
39#define MemSet(dest, len) memset((dest), (0), (len))
40
41#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
42#pragma section(".rdata$", read)
43#define read_only __declspec(allocate(".rdata$"))
44#elif (COMPILER_CLANG && OS_LINUX)
45#define read_only __attribute__((section(".rodata")))
46#else
47#define read_only
48#endif
49
50typedef uint64_t u64;
51typedef uint32_t u32;
52typedef uint16_t u16;
53typedef uint8_t u8;
54
55typedef int8_t i8;
56typedef int16_t i16;
57typedef int32_t i32;
58typedef int64_t i64;
59
60typedef float f32;
61typedef double f64;
62
63typedef i32 b32;
64typedef i16 b16;
65typedef u8 b8;
66
67typedef uintptr_t umm;
68typedef intptr_t smm;
69
70#define TRUE (0 == 0)
71#define FALSE (0 != 0)
72
73#endif
diff --git a/source/base/base_arena.c b/source/base/base_arena.c
new file mode 100755
index 0000000..5855e5e
--- /dev/null
+++ b/source/base/base_arena.c
@@ -0,0 +1,138 @@
1internal mem_arena *
2arena_create(u64 capacity)
3{
4 mem_arena *arena = (mem_arena *)mmap(
5 /* kernel decides where to throw the arena */
6 NULL,
7 capacity + sizeof(mem_arena),
8 PROT_READ | PROT_WRITE,
9 MAP_SHARED | MAP_ANONYMOUS,
10 -1,
11 0);
12
13 if (arena == MAP_FAILED)
14 {
15 return NULL;
16 }
17
18 arena->capacity = capacity;
19 arena->base_position = (u8 *)arena + sizeof(mem_arena);
20 arena->current_position = 0;
21 arena->previous_position = 0;
22
23 return arena;
24}
25
26internal void
27arena_destroy(mem_arena *arena)
28{
29 if (!arena)
30 {
31 return;
32 }
33 munmap(arena, arena->capacity + sizeof(mem_arena));
34}
35internal void *
36arena_alloc(mem_arena *arena, u64 size, b32 zero)
37{
38 if (!arena)
39 {
40 return NULL;
41 }
42 u64 aligned = Align(arena->current_position, ARENA_ALIGN);
43 u64 new_pos = aligned + size;
44 if (new_pos > arena->capacity)
45 {
46 return NULL;
47 }
48
49 void *out = arena->base_position + aligned;
50
51 arena->previous_position = arena->current_position;
52 arena->current_position = aligned + size;
53
54 if (zero) MemSet(out, size);
55
56 return out;
57}
58
59internal void
60arena_pop(mem_arena *arena, u64 size)
61{
62 size = MIN(size, arena->current_position);
63 arena->current_position -= size;
64}
65
66internal void
67arena_pop_to(mem_arena *arena, u64 pos)
68{
69 u64 size = pos < arena->current_position ? arena->current_position - pos : 0;
70 arena_pop(arena, size);
71}
72
73internal void
74arena_clear(mem_arena *arena)
75{
76 arena->current_position = 0;
77}
78
79internal mem_arena *
80arena_resize_align(mem_arena *arena, void *old_memory, u64 new_size, u64 old_size, umm alignment)
81{
82 u8 *old_mem = (u8 *)old_memory;
83
84 if (!is_pow(alignment))
85 {
86 Align(arena->current_position, alignment);
87 }
88
89 if (old_memory == NULL || old_size == 0)
90 {
91 return (mem_arena *)arena_alloc(arena, new_size, 0);
92 }
93 else if ((old_mem >= arena->base_position && old_mem < arena->base_position + arena->capacity))
94 {
95 if ((arena->base_position + arena->previous_position) == old_memory)
96 {
97 arena->current_position = arena->previous_position + new_size;
98 if (new_size > old_size)
99 {
100 MemSet(&arena->current_position, new_size - old_size);
101 }
102 return (mem_arena *)old_memory;
103 }
104 else
105 {
106 void *new_memory = arena_alloc(arena, new_size, 0);
107 umm copy_size = old_size < new_size ? old_size : new_size;
108 memmove(new_memory, old_mem, copy_size);
109 }
110 }
111 else
112 {
113 check(0);
114 }
115 return NULL;
116}
117
118internal mem_arena *
119arena_resize(mem_arena *arena, void *old_memory, u64 new_size, u64 old_size)
120{
121 return arena_resize_align(arena, old_memory, new_size, old_size, ARENA_ALIGN);
122}
123
124internal temp_arena
125temp_arena_begin(mem_arena *arena)
126{
127 temp_arena t;
128 t.arena = arena;
129 t.start_position = arena->current_position;
130
131 return t;
132}
133
134internal void
135temp_arena_end(temp_arena temp)
136{
137 temp.arena->current_position = temp.start_position;
138}
diff --git a/source/base/base_arena.h b/source/base/base_arena.h
new file mode 100755
index 0000000..2818ae4
--- /dev/null
+++ b/source/base/base_arena.h
@@ -0,0 +1,26 @@
1#ifndef BASE_ARENA_H
2#define BASE_ARENA_H
3
4#define Align(pointer, alignment) align((u64)(pointer), (umm)(alignment))
5#define PushStruct(arena, type) (type *)arena_alloc((arena), sizeof(type), 0)
6#define PushStructZero(arena, type) (type *)arena_alloc((arena), sizeof(type), 1)
7#define PushArray(arena, type, len) (type *)arena_alloc((arena), sizeof(type) * (len), 0)
8#define PushArrayZero(arena, type, len) (type *)arena_alloc((arena), sizeof(type) * (len), 1)
9
10typedef struct mem_arena mem_arena;
11struct mem_arena
12{
13 u64 current_position;
14 u64 previous_position;
15 u64 capacity;
16 u8 *base_position;
17};
18
19typedef struct temp_arena temp_arena;
20struct temp_arena
21{
22 mem_arena *arena;
23 u64 start_position;
24};
25
26#endif /* BASE_ARENA_H */
diff --git a/source/base/base_hash.c b/source/base/base_hash.c
new file mode 100644
index 0000000..1964441
--- /dev/null
+++ b/source/base/base_hash.c
@@ -0,0 +1,12 @@
1
2internal u64
3generate_hash()
4{
5
6
7
8}
9
10internal hash_map
11make_hash_map
12
diff --git a/source/base/base_include.h b/source/base/base_include.h
new file mode 100755
index 0000000..40ae5ea
--- /dev/null
+++ b/source/base/base_include.h
@@ -0,0 +1,27 @@
1#ifndef BASE_INCLUDE_H
2#define BASE_INCLUDE_H
3
4#include <dirent.h>
5#include <sys/mman.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <stdint.h>
9#include <stddef.h>
10#include <string.h>
11#include <unistd.h>
12
13#include "base.h"
14#include "base_mem.h"
15#include "base_arena.h"
16#include "base_stack.h"
17#include "base_test.h"
18#include "base_string.h"
19#include "base_os.h"
20
21#ifdef BASE_UNITY
22
23#include "base_arena.c"
24#include "base_stack.c"
25
26#endif
27#endif
diff --git a/source/base/base_io.h b/source/base/base_io.h
new file mode 100644
index 0000000..ece4d7c
--- /dev/null
+++ b/source/base/base_io.h
@@ -0,0 +1,11 @@
1#ifndef BASE_IO_H
2#define BASE_IO_H
3
4internal void
5input_read()
6{
7
8
9}
10
11#endif /* BASE_IO_H */
diff --git a/source/base/base_mem.c b/source/base/base_mem.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/source/base/base_mem.c
diff --git a/source/base/base_mem.h b/source/base/base_mem.h
new file mode 100644
index 0000000..2778fce
--- /dev/null
+++ b/source/base/base_mem.h
@@ -0,0 +1,26 @@
1#ifndef BASE_MEM_H
2#define BASE_MEM_H
3
4#define ARENA_ALIGN (2 * sizeof(void *))
5#define MIN(a, b) (((a) < (b)) ? (a) : (b))
6#define MAX(a, b) (((a) > (b)) ? (a) : (b))
7
8internal inline b8
9is_pow(umm x)
10{
11 return (x & (x - 1)) == 0;
12}
13
14internal inline u64
15align(u64 pointer, umm alignment)
16{
17 if ((alignment & (alignment - 1)) == 0)
18 {
19 return pointer;
20 }
21
22 return (pointer + alignment - 1) & ~(alignment - 1);
23}
24
25
26#endif
diff --git a/source/base/base_os.h b/source/base/base_os.h
new file mode 100644
index 0000000..23587c6
--- /dev/null
+++ b/source/base/base_os.h
@@ -0,0 +1,38 @@
1#ifndef BASE_OS_H
2#define BASE_OS_H
3
4internal void
5print(const char *str)
6{
7 i32 len = 0;
8 while (str[len]) len++;
9 write(STDOUT_FILENO, str, len);
10}
11
12internal string8
13load_file(const char *path)
14{
15 string8 result = {0};
16 struct stat sbuf = {0};
17
18 i32 file = open(path, O_RDONLY);
19 if(file == -1) return result;
20
21 if(fstat(file, &sbuf) == -1)
22 {
23 print("error: fstat failed");
24 close(file);
25 return result;
26 }
27
28 result.size = (u64)sbuf.st_size;
29 if(result.size != 0)
30 {
31 result.data = (u8 *)mmap(0, result.size, PROT_READ, MAP_PRIVATE, file, 0);
32 }
33
34 close(file);
35 return result;
36}
37
38#endif /* BASE_OS_H */
diff --git a/source/base/base_stack.c b/source/base/base_stack.c
new file mode 100755
index 0000000..9c1218a
--- /dev/null
+++ b/source/base/base_stack.c
@@ -0,0 +1,187 @@
1internal mem_stack *
2stack_create(u64 capacity)
3{
4 mem_stack *stack = (mem_stack *)mmap(
5 0,
6 capacity + sizeof(mem_stack),
7 PROT_READ | PROT_WRITE,
8 MAP_SHARED | MAP_ANONYMOUS,
9 -1,
10 0);
11
12 if (stack == MAP_FAILED)
13 {
14 return NULL;
15 }
16
17 stack->capacity = capacity;
18 stack->base_position = (u8 *)stack + sizeof(mem_stack);
19 stack->current_offset = 0;
20
21 return stack;
22}
23
24internal u8
25calculate_padding(u64 pointer, u8 alignment, u64 header_size)
26{
27 u8 modulo, padding;
28
29 if (!is_pow(alignment))
30 {
31 return 0;
32 }
33
34 modulo = pointer & (u8)(alignment - 1);
35
36 padding = 0;
37
38 if (0 == modulo)
39 {
40 padding = alignment - modulo;
41 }
42
43 if (padding < header_size)
44 {
45 header_size -= padding;
46
47 if ((header_size & (alignment - 1)) != 0)
48 {
49 padding += alignment * (1 + (header_size / alignment));
50 }
51 else
52 {
53 padding += alignment * (header_size / alignment);
54 }
55 }
56
57 return padding;
58}
59
60internal mem_stack *
61stack_push_align(mem_stack *stack, u64 size, u8 alignment)
62{
63 u8 padding = 0;
64
65 if (!is_pow(alignment))
66 {
67 return (0);
68 }
69
70 if (alignment > 128)
71 {
72 alignment = 128;
73 }
74
75 u64 current_address = (u64)stack->base_position + stack->current_offset;
76 padding = calculate_padding(current_address, alignment, sizeof(mem_stack_header));
77
78 if (stack->current_offset + padding + size > stack->capacity)
79 {
80 return 0;
81 }
82
83 stack->current_offset += padding;
84
85 u64 next_address = current_address + (u64)padding;
86 mem_stack_header *header = (mem_stack_header *)(next_address - sizeof(mem_stack_header));
87 header->padding = padding;
88
89 stack->current_offset += size;
90
91 return MemSet((void *)next_address, size);
92}
93internal void *
94stack_push(mem_stack *stack, u64 size)
95{
96 return stack_push_align(stack, size, ARENA_ALIGN);
97}
98
99internal void
100stack_pop(mem_stack *stack, void *pointer)
101{
102 if (pointer != NULL)
103 {
104 u64 start, end, current_address;
105 mem_stack_header *header;
106 u64 prev_offset;
107
108 start = (u64)stack->base_position;
109 end = start + (u64)stack->capacity;
110 current_address = (u64)pointer;
111
112 if (!(start <= current_address && current_address < end))
113 {
114 if (0 && "Out of bounds memory address passed to stack allocator (free)")
115 {
116 return;
117 }
118 return;
119 }
120
121 if (current_address >= start + (u64)stack->base_position)
122 {
123 return;
124 }
125
126 header = (mem_stack_header *)(current_address - sizeof(mem_stack_header));
127 prev_offset = (size_t)(current_address - (u64)header->padding - start);
128 stack->current_offset = prev_offset;
129 }
130}
131
132internal mem_stack *
133stack_resize_align(mem_stack *stack, void *pointer, u64 old_size, u64 new_size, u8 alignment)
134{
135 if (pointer == NULL)
136 {
137 return stack_push_align(stack, new_size, alignment);
138 }
139 else if (new_size == 0)
140 {
141 stack_pop(stack, pointer);
142 return NULL;
143 }
144
145 u64 start, end, current_address;
146 u64 min_size = old_size < new_size ? old_size : new_size;
147 void *new_pointer;
148
149 start = (u64)stack->base_position;
150 end = start + (u64)stack->capacity;
151 current_address = (u64)pointer;
152 if (!(start <= current_address && current_address < end))
153 {
154 return NULL;
155 }
156
157 if (current_address >= start + (u64)stack->current_offset)
158 {
159 return NULL;
160 }
161
162 if (old_size == new_size)
163 {
164 return pointer;
165 }
166
167 new_pointer = stack_push_align(stack, new_size, alignment);
168 memmove(new_pointer, pointer, min_size);
169 return new_pointer;
170}
171
172internal void
173stack_pop_all(mem_stack *stack)
174{
175 stack->current_offset = 0;
176}
177
178internal void
179stack_destroy(mem_stack *stack)
180{
181 if (!stack)
182 {
183 return;
184 }
185
186 munmap(stack, stack->capacity + sizeof(mem_stack));
187}
diff --git a/source/base/base_stack.h b/source/base/base_stack.h
new file mode 100755
index 0000000..54d61d3
--- /dev/null
+++ b/source/base/base_stack.h
@@ -0,0 +1,22 @@
1#ifndef STACK_H
2#define STACK_H
3
4typedef struct mem_stack_header mem_stack_header;
5struct mem_stack_header
6{
7 u8 padding;
8 u8 previous_offset;
9};
10
11
12typedef struct mem_stack mem_stack;
13struct mem_stack
14{
15 mem_stack_header *header;
16
17 u64 current_offset;
18 u64 capacity;
19 u8 *base_position;
20};
21
22#endif
diff --git a/source/base/base_string.h b/source/base/base_string.h
new file mode 100644
index 0000000..189b38a
--- /dev/null
+++ b/source/base/base_string.h
@@ -0,0 +1,59 @@
1#ifndef BASE_STRING_H
2#define BASE_STRING_H
3
4#include <string.h>
5
6#define StringLit(string) \
7 (string8){ .data = (u8 *)(string), .size = (sizeof(string) - 1) }
8
9 #define PushString(arena, size) \
10 (string8){ (u8 *)PushArray((arena), u8, (size)), (u64)(size) }
11
12#define StringFmt "%.*s"
13#define ULongFmt "%lu"
14#define ULLongFmt "%llu"
15
16typedef struct string8 string8;
17struct string8
18{
19 u8 *data;
20 u64 size;
21};
22
23internal b8
24string8_cmp(string8 a, string8 b)
25{
26 if (a.size != b.size) return 0;
27 return (b8)(memcmp(a.data, b.data, a.size) == 0);
28}
29
30internal u64
31string8_to_u64(u8 *buf, umm len)
32{
33 u64 value = 0;
34 for (umm i = 0; i < len; ++i)
35 {
36 u8 c = buf[i];
37 if (c < '0' || c > '9') break;
38 value = value * 10 + (c - '0');
39 }
40 return value;
41}
42
43internal void
44string8_append_char(string8 *buf, u8 c)
45{
46 buf->data[buf->size] = c;
47 buf->size += 1;
48}
49
50read_only global_variable
51string8 nil_string =
52{
53
54 .data = NULL,
55 .size = 0,
56
57};
58
59#endif /* BASE_STRING_H */
diff --git a/source/base/base_test.h b/source/base/base_test.h
new file mode 100644
index 0000000..412797b
--- /dev/null
+++ b/source/base/base_test.h
@@ -0,0 +1,74 @@
1#ifndef BASE_TEST_H
2#define BASE_TEST_H
3
4#define RED "\x1b[31m"
5#define GREEN "\x1b[32m"
6#define RESET "\x1b[0m"
7#define BLUE "\x1b[34m"
8
9#define LEN(s) (sizeof(s) - 1)
10
11internal void
12write_int(i32 num)
13{
14
15 if (num < 0)
16 {
17 write(STDERR_FILENO, "-", 1);
18 num = -num;
19 }
20 if (num >= 10)
21 write_int(num / 10);
22 char digit = '0' + (num % 10);
23
24 write(STDERR_FILENO, &digit, 1);
25}
26
27#define show \
28 do \
29 { \
30 write(STDOUT_FILENO, __FILE__, sizeof(__FILE__) - 1); \
31 write(STDOUT_FILENO, ":", 1); \
32 write(STDOUT_FILENO, __func__, sizeof(__func__) - 1); \
33 write(STDOUT_FILENO, ":", 1); \
34 write_int(__LINE__); \
35 write(STDOUT_FILENO, "\n", 1); \
36 } while (0)
37
38#define test(expr) \
39 { \
40 if ((expr) != 0) \
41 { \
42 write(STDERR_FILENO, "[FAILED] ", LEN("[FAILED] ")); \
43 show; \
44 _exit(1); \
45 } \
46 }
47
48#define check(expr) \
49 { \
50 if ((expr) != 0) \
51 { \
52 write(STDERR_FILENO, RED "[ERROR] ", LEN(RED "[ERROR] ")); \
53 show; \
54 write(STDERR_FILENO, RESET, LEN(RESET)); \
55 _exit(1); \
56 } \
57 else \
58 { \
59 write(STDERR_FILENO, GREEN "[SUCCESS] ", LEN(GREEN "[SUCCESS] ")); \
60 show; \
61 write(STDERR_FILENO, RESET, LEN(RESET)); \
62 } \
63 }
64
65#define checkpoint_output "<<CHECKPOINT>>\n"
66#define checkpoint_end_output "^^^^^^^^^^^^^^\n\n\n"
67#define checkpoint \
68 { \
69 write(STDERR_FILENO, BLUE checkpoint_output, LEN(BLUE checkpoint_output)); \
70 show; \
71 write(STDERR_FILENO, BLUE checkpoint_end_output, LEN(BLUE checkpoint_end_output)); \
72 }
73
74#endif /* BASE_TEST_H */
diff --git a/source/base/bash_hash.h b/source/base/bash_hash.h
new file mode 100644
index 0000000..2c286a2
--- /dev/null
+++ b/source/base/bash_hash.h
@@ -0,0 +1,15 @@
1##ifndef HEADER_H
2#define HEADER_H
3
4typedef struct hash_map hash_map;
5typedef struct hash hash;
6
7
8struct hash_map
9{
10
11
12};
13
14
15#endif /* HEADER_H */
diff --git a/source/fajr/fajr_main.c b/source/fajr/fajr_main.c
new file mode 100644
index 0000000..fc7be4a
--- /dev/null
+++ b/source/fajr/fajr_main.c
@@ -0,0 +1,41 @@
1#define BASE_UNITY
2#include "base/base_include.h"
3#include "fajr_lexer/fajr_lexer.h"
4#include "fajr_parser/fajr_parser.h"
5
6#include "fajr_lexer/fajr_lexer.c"
7#include "fajr_parser/fajr_parser.c"
8
9int
10main(int argc, char **argv)
11{
12 mem_arena *global = arena_create(GiB(1));
13 if (argc < 2) {
14 print("Usage: program <file>\n");
15 return 1;
16 }
17
18 i32 fd = open(argv[1], O_RDONLY);
19 if (fd < 0) {
20 print("Error: Cannot open file\n");
21 return 1;
22 }
23
24 u8 input[4096];
25 u64 bytes_read;
26 bytes_read = read(fd, input, sizeof(input));
27
28 string8 buffer = {0};
29
30 buffer.data = input;
31 buffer.size = bytes_read;
32
33 token_list *List = PushStruct(global, token_list);
34 concrete_syntax_tree *Tree = PushStruct(global, concrete_syntax_tree);
35
36 Lex(&buffer, global, List);
37 Parse(global, List, Tree);
38
39 close(fd);
40 return 0;
41}
diff --git a/source/fajr/fajr_main.h b/source/fajr/fajr_main.h
new file mode 100644
index 0000000..835ab65
--- /dev/null
+++ b/source/fajr/fajr_main.h
@@ -0,0 +1,6 @@
1#ifndef FAJR_MAIN_H
2#define FAJR_MAIN_H
3
4
5
6#endif /* FAJR_MAIN_H */
diff --git a/source/fajr_lexer/fajr_lexer.c b/source/fajr_lexer/fajr_lexer.c
new file mode 100644
index 0000000..3a98ca9
--- /dev/null
+++ b/source/fajr_lexer/fajr_lexer.c
@@ -0,0 +1,274 @@
1internal inline b32
2IsAlpha(u8 Character)
3{
4 return ((Character >= 'a' && Character <= 'z') ||
5 (Character >= 'A' && Character <= 'Z') ||
6 (Character == '_'));
7}
8
9internal inline b32
10IsDigit(u8 Character)
11{
12 return (Character >= '0' && Character <= '9');
13}
14
15internal b32
16IsDelimiter(u8 Character)
17{
18 for(i32 Index = 0; Index < (i32)sizeof(Delimiters); ++Index)
19 {
20 if(Delimiters[Index] == Character)
21 {
22 return 1;
23 }
24 }
25 return 0;
26}
27
28internal inline b32
29IsNilTokenNode(token_node *TokenNode)
30{
31 return TokenNode == &nil_token_node || TokenNode == NULL;
32}
33
34internal inline b32
35IsNilToken(token *Token)
36{
37 return Token == &nil_token || Token == NULL;
38}
39
40internal inline b32
41IsWhiteSpace(u8 Character)
42{
43 return (Character == '\n' || Character == '\r' ||
44 Character == ' ' || Character == '\t');
45}
46
47internal inline void
48ParseCStyleComment(u8 Buffer[])
49{
50 // TODO(nasr): handle c style comments
51 // couuld be usefull for function information visualiszation
52 // so think of a way to link themn to functions and variables?
53 // some sort of meta data per thing?
54 // and then we can do a visualization if the str8.count of the metadata thing is bigger then 0
55 // we should a visualization thing for the thing
56 // if the thing is less then 0, we dont do anything?
57
58 // TODO(nasr): while doingn this we could also add in some editor specific anotations ?
59}
60
61internal inline void
62ParseCPPStyleComment(u8 Buffer[])
63{
64 // TODO(nasr):
65}
66
67internal inline b32
68Is_TokenBreak(u8 Character)
69{
70 return (IsWhiteSpace(Character) || IsDelimiter(Character));
71}
72
73internal token_list *
74Lex(string8 *Buffer, mem_arena *Arena, token_list *List)
75{
76 b32 Initialized = 0;
77 i32 Line = 1;
78 i32 Column = 1;
79
80 for(u64 TextIndex = 0; TextIndex < Buffer->size; TextIndex++)
81 {
82 u8 Character = Buffer->data[TextIndex];
83
84 if(Character == '\r' || Character == '\n')
85 {
86 if(Character == '\r' &&
87 (TextIndex + 1 < Buffer->size) &&
88 Buffer->data[TextIndex + 1] == '\n')
89 {
90 TextIndex++;
91 }
92
93 ++TextIndex;
94 ++Line;
95
96 // NOTE(nasr): reset the column to the beginning of the line
97 Column = 1;
98 continue;
99 }
100
101 if(IsWhiteSpace(Character))
102 {
103 ++Column;
104 continue;
105 }
106
107 token_node *TokenNode = PushStruct(Arena, token_node);
108 token *Token = PushStruct(Arena, token);
109 TokenNode->Next = &nil_token_node;
110 TokenNode->Previous = &nil_token_node;
111 TokenNode->Token = Token;
112 Token->Line = Line;
113 Token->Column = Column;
114 Token->ByteOffset = (u64)TextIndex;
115 Token->Flags = FlagNone;
116
117 u64 TokenStart = TextIndex;
118 u64 TokenEnd = TextIndex;
119
120 if(Character > 126)
121 {
122 Token->Type = TokenUnwantedChild;
123 TokenEnd = TextIndex + 1;
124 }
125 else if(IsAlpha(Character))
126 {
127 while((TextIndex + 1 < Buffer->size) &&
128 (IsAlpha(Buffer->data[TextIndex + 1]) || IsDigit(Buffer->data[TextIndex + 1])))
129 {
130 ++TextIndex;
131 }
132
133 // TODO(nasr): build a lexeme
134 TokenEnd = TextIndex + 1;
135 string8 Lexeme = {
136 .data = (u8 *)Buffer->data,
137 .size = (u64)Buffer->data
138 }
139 ;
140
141 // TODO(nasr): handle functions
142 if(string8_cmp(Lexeme, StringLit("func"), 0))
143 Token->Type = TokenIf;
144 else if(string8_cmp(Lexeme, StringLit("if"), 0))
145 Token->Type = TokenElse;
146 else if(string8_cmp(Lexeme, StringLit("return"), 0))
147 Token->Type = TokenReturn;
148 else if(string8_cmp(Lexeme, StringLit("while"), 0))
149 Token->Type = TokenWhile;
150 else if(string8_cmp(Lexeme, StringLit("for"), 0))
151 Token->Type = TokenFor;
152 else if(string8_cmp(Lexeme, StringLit("break"), 0))
153 Token->Type = TokenBreak;
154 else if(string8_cmp(Lexeme, StringLit("continue"), 0))
155 Token->Type = TokenContinue;
156 else
157 Token->Type = TokenIdentifier;
158 }
159 else if(IsDigit(Character))
160 {
161 while((TextIndex + 1 < Buffer->size) &&
162 IsDigit(Buffer->data[TextIndex + 1]))
163 {
164 ++TextIndex;
165 }
166
167 TokenEnd = TextIndex + 1;
168 Token->Type = TokenNumber;
169 }
170
171 else
172 {
173 u8 Next = (TextIndex + 1 < Buffer->size) ? Buffer->data[TextIndex + 1] : 0;
174
175 switch(Character)
176 {
177 case '=':
178 {
179 if(Next == '=')
180 {
181 Token->Type = TokenDoubleEqual;
182 TextIndex++;
183 }
184 else
185 {
186 Token->Type = (token_type)'=';
187 }
188 }
189 break;
190
191 case '>':
192 {
193 if(Next == '=')
194 {
195 Token->Type = TokenGreaterEqual;
196 TextIndex++;
197 }
198 else if(Next == '>')
199 {
200 Token->Type = TokenRightShift;
201 TextIndex++;
202 }
203 else
204 {
205 Token->Type = (token_type)'>';
206 }
207 }
208 break;
209
210 case '<':
211 {
212 if(Next == '=')
213 {
214 Token->Type = TokenLesserEqual;
215 TextIndex++;
216 }
217 else if(Next == '<')
218 {
219 Token->Type = TokenLeftShift;
220 TextIndex++;
221 }
222 else
223 {
224 Token->Type = (token_type)'<';
225 }
226 }
227 break;
228
229 case '"':
230 {
231 while(Buffer->data[TextIndex + 1] != '"' && Buffer->data[TextIndex + 1] != '\0')
232 {
233 ++TextIndex;
234 if(Buffer->data[TextIndex + 1] == '\\')
235
236 ++TextIndex;
237 }
238
239 TokenStart += 1;
240 Token->Type = TokenString;
241 }
242 break;
243 default:
244 {
245 Token->Type = (token_type)Character;
246 }
247 break;
248 }
249 }
250
251 TokenEnd = TextIndex + 1;
252
253 Token->Lexeme.data = (u8 *)&Buffer->data[TokenStart];
254 Token->Lexeme.size = (u64)(TokenEnd - TokenStart);
255 Column += (i32)Token->Lexeme.size;
256
257 //Log("Token: \t%.lu*s\n", Token->Lexeme.Size, Token->Lexeme.Data);
258
259 if(!Initialized)
260 {
261 Initialized = 1;
262 List->Root = TokenNode;
263 List->Current = TokenNode;
264 }
265 else
266 {
267 TokenNode->Previous = List->Current;
268 List->Current->Next = TokenNode;
269 List->Current = TokenNode;
270 }
271 }
272
273 return List;
274}
diff --git a/source/fajr_lexer/fajr_lexer.h b/source/fajr_lexer/fajr_lexer.h
new file mode 100644
index 0000000..754b89a
--- /dev/null
+++ b/source/fajr_lexer/fajr_lexer.h
@@ -0,0 +1,119 @@
1#ifndef FAJR_LEXER_H
2#define FAJR_LEXER_H
3
4typedef enum token_type token_type;
5enum token_type
6{
7 TokenUndefined = 256,
8 TokenIdentifier,
9 TokenIdentifierAssignmentValue,
10 TokenValue,
11 TokenString,
12 TokenNumber,
13 TokenDoubleEqual,
14 TokenGreaterEqual,
15 TokenLesserEqual,
16 TokenParam,
17 TokenFunc,
18 TokenReturn,
19 TokenIf,
20 TokenElse,
21 TokenFor,
22 TokenWhile,
23 TokenBreak,
24 TokenContinue,
25 TokenExpression,
26 TokenFuncBody,
27 TokenUnwantedChild,
28 TokenNewLine,
29 TokenRightShift,
30 TokenLeftShift,
31 TokenStar,
32};
33
34typedef struct Tokenizer Tokenizer;
35struct Tokenizer
36{
37 i32 Line;
38 i32 Column;
39};
40
41typedef enum token_flags token_flags;
42enum token_flags
43{
44 FlagNone = (0),
45 FlagConstant = (1 << 0),
46 FlagGlobal = (1 << 1),
47 FlagsValue = (1 << 2),
48 FlagDefinition = (1 << 3),
49 FlagComparison = (1 << 4),
50 FlagDeprecated = (1 << 5),
51 FlagDirty = (1 << 6),
52};
53
54typedef struct token token;
55struct token
56{
57 string8 Lexeme;
58 token_type Type;
59 token_flags Flags;
60 u64 ByteOffset;
61 i32 Column;
62 i32 Line;
63
64 string8 MetaData;
65};
66
67typedef struct token_node token_node;
68struct token_node
69{
70 token_node *Next;
71 token_node *Previous;
72 token *Token;
73};
74
75typedef struct token_list token_list;
76struct token_list
77{
78 token_node *Root;
79 token_node *Current;
80};
81
82typedef struct lexer lexer;
83struct lexer
84{
85 u8 *Text;
86 u64 TextCount;
87 u8 *EndOfFile;
88 u8 *UndefinedTokens;
89};
90
91global_variable const u8 Delimiters[] =
92{
93 '{',
94 '}',
95 '(',
96 ')',
97 '[',
98 ']',
99 ';',
100};
101
102read_only global_variable token nil_token =
103{
104 .Lexeme = {NULL, 0},
105 .Type = TokenUndefined,
106 .Flags = FlagNone,
107 .ByteOffset = 0,
108 .Column = 0,
109 .Line = 0,
110};
111
112read_only global_variable token_node nil_token_node =
113{
114 .Next = &nil_token_node,
115 .Previous = &nil_token_node,
116 .Token = NULL,
117};
118
119#endif // FAJR_LEXER_H
diff --git a/source/fajr_parser/fajr_parser.c b/source/fajr_parser/fajr_parser.c
new file mode 100644
index 0000000..cfd1fb2
--- /dev/null
+++ b/source/fajr_parser/fajr_parser.c
@@ -0,0 +1,364 @@
1internal syntax_node *
2PeekToOffset(syntax_node *Node, i32 PeekOffset, b32 findChild)
3{
4 if(PeekOffset < 0)
5 {
6 for(i32 PeekCount = 0; PeekCount < PeekOffset; PeekCount++)
7 {
8 if(!Node || !Node->Next) return &nil_syntax_node;
9 Node = Node->Next;
10 }
11 }
12 else if(PeekOffset > 0)
13 {
14 for(i32 PeekCount = 0; PeekCount < PeekOffset; PeekCount++)
15 {
16 if(!Node || !Node->First) return &nil_syntax_node;
17 Node = Node->First;
18 }
19 }
20 else if(PeekOffset == 0)
21 {
22 return Node;
23 }
24
25 return &nil_syntax_node;
26}
27
28internal void
29NodePushChild(concrete_syntax_tree *Tree, syntax_node *Node)
30{
31 Node->Parent = Tree->Current;
32
33 if(Tree->Current->First == &nil_syntax_node)
34 {
35 Tree->Current->First = Node;
36 Tree->Current->Last = Node;
37 }
38 else
39 {
40 Tree->Current->Last->Next = Node;
41 Tree->Current->Last = Node;
42 }
43}
44
45internal void
46NodePushNext(concrete_syntax_tree *Tree, syntax_node *Node)
47{
48 Node->Parent = Tree->Current;
49
50 if(Tree->Current->Next == &nil_syntax_node) Tree->Current->Next = Node;
51 else Tree->Current->Last->Next = Node;
52}
53
54internal inline b32
55IsNilSyntaxNode(syntax_node *Node)
56{
57 return Node == &nil_syntax_node || Node == NULL;
58}
59
60internal void
61DisownNode(concrete_syntax_tree *Tree, syntax_node *Node)
62{
63 Node->First = &nil_syntax_node;
64 Node->Last = &nil_syntax_node;
65 Node->Parent = &nil_syntax_node;
66 Node->Next = &nil_syntax_node;
67 Node->Token = &nil_token;
68 Node->Type = SyntaxNodeUnwanted;
69}
70
71internal void
72CopyNode(syntax_node *Dest, syntax_node *Src)
73{
74 Src->First = Dest->First;
75 Src->Parent = Dest->Parent;
76 Src->Last = Dest->Last;
77 Src->Next = Dest->Next;
78 Src->Token = Dest->Token;
79 Src->Type = Dest->Type;
80}
81
82/* Insert the node at any given spot thus hopefully allowwing for code changes without breaking */
83internal void
84AdoptNode(concrete_syntax_tree *Tree, syntax_node *Node, syntax_node *ParentNode, b32 asChild)
85{
86 //
87 if (!IsNilSyntaxNode(ParentNode)) return;
88
89 //
90 if (asChild)
91 {
92 ParentNode->Last->Next = Node;
93 Node->Next = ParentNode->Last;
94 }
95 else
96 {
97 // copy node
98 ParentNode->Next = Node;
99 CopyNode(Node, ParentNode->Next);
100 }
101
102
103 NodePushNext(Tree, Node);
104
105 return;
106}
107
108
109internal inline void
110Ground(token *Token)
111{
112 Token->Flags = (token_flags)(Token->Flags | FlagDirty);
113}
114
115internal concrete_syntax_tree *
116Parse(mem_arena *Arena, token_list *List, concrete_syntax_tree *Tree)
117{
118 for(token_node *TokenNode = List->Root;
119 TokenNode != &nil_token_node && TokenNode != NULL;
120 TokenNode = TokenNode->Next)
121 {
122 token *Token = TokenNode->Token;
123 syntax_node *SyntaxNode;
124
125 {
126 SyntaxNode = PushStruct(Arena, syntax_node);
127 SyntaxNode->Token = Token;
128 SyntaxNode->First = &nil_syntax_node;
129 SyntaxNode->Last = &nil_syntax_node;
130 SyntaxNode->Next = &nil_syntax_node;
131 SyntaxNode->Parent = &nil_syntax_node;
132 }
133
134
135 if(IsNilSyntaxNode(Tree->Root))
136 {
137 Tree->Root = SyntaxNode;
138 Tree->Current = SyntaxNode;
139 }
140
141 switch((token_type)Token->Type)
142 {
143 case TokenIdentifier:
144 {
145 if(!IsNilTokenNode(TokenNode) && TokenNode->Next->Token->Type == (token_type)'=')
146 {
147 token_node *ValueNode = TokenNode->Next->Next;
148
149 if(ValueNode->Token->Type != TokenIdentifierAssignmentValue &&
150 ValueNode->Token->Type != TokenValue)
151 {
152 ValueNode->Token->Type = TokenIdentifierAssignmentValue;
153 }
154 }
155
156 if(Tree->Current != SyntaxNode)
157 {
158 NodePushChild(Tree, SyntaxNode);
159 }
160 }
161 break;
162
163 case TokenIdentifierAssignmentValue:
164 {
165 NodePushChild(Tree, SyntaxNode);
166 }
167 break;
168
169 case TokenNumber:
170 case TokenString:
171 {
172 if(Tree->Current && Tree->Current->Token->Type == (token_type)'=')
173 {
174 Token->Type = TokenIdentifierAssignmentValue;
175
176 if(Tree->Current->Parent && Tree->Current->Parent->Token->Type != TokenIdentifier)
177 {
178 Ground(Token);
179 }
180 }
181
182 NodePushChild(Tree, SyntaxNode);
183 }
184 break;
185
186 case TokenDoubleEqual:
187 {
188 NodePushChild(Tree, SyntaxNode);
189
190 if(!IsNilSyntaxNode(Tree->Current->Parent))
191 {
192 Tree->Current->First = Tree->Current->Parent;
193 Tree->Current->Last = Tree->Current->Next;
194 Tree->Current->Parent = Tree->Current;
195 }
196 }
197 break;
198
199 case TokenGreaterEqual:
200 case TokenLesserEqual:
201 case(token_type)'<':
202 case(token_type)'>':
203 {
204 NodePushChild(Tree, SyntaxNode);
205 Tree->Current = SyntaxNode;
206 }
207 break;
208
209 case(token_type)'(':
210 {
211 syntax_node *Current = Tree->Current;
212 Tree->Current->First = PeekToOffset(Tree->Current, 1, 0);
213
214 while(Tree->Current->Token->Type != (token_type)')' && !IsNilSyntaxNode(Tree->Current->Next))
215 {
216 Current = Current->Next;
217 }
218 if(Current == &nil_syntax_node)
219 {
220 print("Forgot to close paran");
221 }
222 }
223 break;
224
225 case(token_type)')':
226 {
227 while(Tree->Current &&
228 Tree->Current != &nil_syntax_node &&
229 Tree->Current->Token->Type != (token_type)'(')
230 {
231 Tree->Current = Tree->Current->Parent;
232 }
233
234 if(Tree->Current && Tree->Current->Parent)
235 {
236 Tree->Current = Tree->Current->Parent;
237 }
238 }
239 break;
240
241 case(token_type)'{':
242 {
243 syntax_node *Node = &nil_syntax_node;
244
245 for(i32 index = 0; PeekToOffset(Tree->Current, index, 0); ++index)
246 {
247 Tree->Current->First = Tree->Current->Next;
248 // TODO(nasr): was doing something here
249 }
250 }
251 break;
252
253 case(token_type)'}':
254 {
255 if(Tree->Current && Tree->Current->Parent)
256 {
257 Tree->Current = Tree->Current->Parent;
258 }
259 }
260 break;
261
262 case(token_type)';':
263 {
264 Tree->Current->Last = Tree->Current;
265 Tree->Current = Tree->Current->Parent;
266 }
267 break;
268
269 case TokenFunc:
270 {
271 // TODO(nasr): define the function body
272 NodePushChild(Tree, SyntaxNode);
273 Tree->Current = SyntaxNode;
274 }
275 break;
276
277 case TokenReturn:
278 {
279 if(Tree->Current &&
280 Tree->Current->Parent &&
281 Tree->Current->Parent->Token->Type != TokenFunc)
282 {
283 Ground(Token);
284 }
285
286 NodePushChild(Tree, SyntaxNode);
287 Tree->Current = SyntaxNode;
288 }
289 break;
290
291 case TokenIf:
292 {
293 NodePushChild(Tree, SyntaxNode);
294 Tree->Current = SyntaxNode;
295 }
296 break;
297
298 case TokenElse:
299 {
300 // TODO(nasr): handle no body
301 NodePushChild(Tree, SyntaxNode);
302 }
303 break;
304
305 case TokenWhile:
306 case TokenFor:
307 {
308 NodePushChild(Tree, SyntaxNode);
309 Tree->Current = SyntaxNode;
310 }
311 break;
312
313 case TokenBreak:
314 {
315 token_type Type = Tree->Current->Parent->Token->Type;
316
317 if(Type != TokenFor && Type != TokenWhile)
318 {
319 Ground(Token);
320 print("Break statement not allowed here");
321 }
322 NodePushChild(Tree, SyntaxNode);
323 }
324 break;
325
326 case TokenContinue:
327 {
328 NodePushChild(Tree, SyntaxNode);
329 }
330 break;
331
332 case TokenExpression:
333 case TokenParam:
334 {
335 NodePushChild(Tree, SyntaxNode);
336 Tree->Current = SyntaxNode;
337 }
338 break;
339
340 case TokenStar:
341 {
342 // TODO(nasr): once we get to better visualizations i think
343 NodePushChild(Tree, SyntaxNode);
344 }
345 break;
346
347 case TokenUndefined:
348 {
349 Ground(Token);
350 NodePushChild(Tree, SyntaxNode);
351 }
352 break;
353
354 default:
355 {
356 Ground(Token);
357 NodePushChild(Tree, SyntaxNode);
358 }
359 break;
360 }
361 }
362
363 return Tree;
364}
diff --git a/source/fajr_parser/fajr_parser.h b/source/fajr_parser/fajr_parser.h
new file mode 100644
index 0000000..d83dde2
--- /dev/null
+++ b/source/fajr_parser/fajr_parser.h
@@ -0,0 +1,60 @@
1#ifndef EDITOR_PARSER_H
2#define EDITOR_PARSER_H
3
4typedef enum syntax_node_type syntax_node_type;
5enum syntax_node_type
6{
7 SyntaxNodeLiteral,
8 SyntaxNodeIdentifier,
9 SyntaxNodeBinary,
10
11 SyntaxNodeAssignment,
12 SyntaxNodeReturn,
13 SyntaxNodeFunction,
14 SyntaxNodeUnwanted,
15};
16
17typedef struct syntax_node syntax_node;
18struct syntax_node
19{
20 syntax_node *First;
21 syntax_node *Last;
22 syntax_node *Parent;
23 syntax_node *Next;
24
25 token *Token;
26
27 syntax_node_type Type;
28};
29
30typedef struct concrete_syntax_tree concrete_syntax_tree;
31struct concrete_syntax_tree
32{
33 syntax_node *Root;
34 syntax_node *Current;
35};
36
37// TODO(nasr): implement this later together with file handling
38read_only global_variable
39syntax_node nil_syntax_node =
40{
41.First = &nil_syntax_node,
42.Last = &nil_syntax_node,
43.Parent = &nil_syntax_node,
44.Next = &nil_syntax_node,
45.Token = &nil_token,
46};
47
48read_only global_variable
49concrete_syntax_tree nil_concrete_syntax_tree =
50{
51.Root = &nil_syntax_node,
52.Current = &nil_syntax_node,
53};
54
55#define PeekForward(Node, Type, NilNode) \
56 for(; (Node) && (Node) != &(NilNode); (Node) = (Node)->Next) \
57 if((Node)->Token->Type == (Type)) \
58 break;
59
60#endif // EDITOR_PARSER_H
diff --git a/tests/main.fajr b/tests/main.fajr
new file mode 100644
index 0000000..88e2bb0
--- /dev/null
+++ b/tests/main.fajr
@@ -0,0 +1,6 @@
1module file;
2
3void main()
4{
5 s32 value = 0;
6}