diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bytecode/darray.h (renamed from src/bootstrap/darray.h) | 0 | ||||
-rw-r--r-- | src/bytecode/debug.h | 32 | ||||
-rw-r--r-- | src/bytecode/errors.c (renamed from src/bootstrap/errors.c) | 0 | ||||
-rw-r--r-- | src/bytecode/errors.h (renamed from src/bootstrap/errors.h) | 0 | ||||
-rw-r--r-- | src/bytecode/lexer.c (renamed from src/bootstrap/lexer.c) | 0 | ||||
-rw-r--r-- | src/bytecode/lexer.h | 60 | ||||
-rw-r--r-- | src/bytecode/main.c | 197 | ||||
-rw-r--r-- | src/bytecode/ops.h | 8 | ||||
-rw-r--r-- | src/bytecode/read_line.c (renamed from src/bootstrap/read_line.c) | 0 | ||||
-rw-r--r-- | src/bytecode/read_line.h (renamed from src/bootstrap/read_line.h) | 0 | ||||
-rw-r--r-- | src/bytecode/string_view.c (renamed from src/bootstrap/string_view.c) | 0 | ||||
-rw-r--r-- | src/bytecode/string_view.h (renamed from src/bootstrap/string_view.h) | 0 | ||||
-rw-r--r-- | src/bytecode/types.h | 30 | ||||
-rw-r--r-- | src/treewalk/darray.h | 78 | ||||
-rw-r--r-- | src/treewalk/environment.c (renamed from src/bootstrap/environment.c) | 0 | ||||
-rw-r--r-- | src/treewalk/environment.h (renamed from src/bootstrap/environment.h) | 0 | ||||
-rw-r--r-- | src/treewalk/errors.c | 29 | ||||
-rw-r--r-- | src/treewalk/errors.h | 38 | ||||
-rw-r--r-- | src/treewalk/gc.c (renamed from src/bootstrap/gc.c) | 0 | ||||
-rw-r--r-- | src/treewalk/gc.h (renamed from src/bootstrap/gc.h) | 0 | ||||
-rw-r--r-- | src/treewalk/hashtable.h (renamed from src/bootstrap/hashtable.h) | 0 | ||||
-rw-r--r-- | src/treewalk/lexer.c | 257 | ||||
-rw-r--r-- | src/treewalk/lexer.h (renamed from src/bootstrap/lexer.h) | 0 | ||||
-rwxr-xr-x | src/treewalk/main.c (renamed from src/bootstrap/main.c) | 0 | ||||
-rw-r--r-- | src/treewalk/objects.c (renamed from src/bootstrap/objects.c) | 0 | ||||
-rw-r--r-- | src/treewalk/objects.h (renamed from src/bootstrap/objects.h) | 0 | ||||
-rw-r--r-- | src/treewalk/parser.c (renamed from src/bootstrap/parser.c) | 0 | ||||
-rw-r--r-- | src/treewalk/parser.h (renamed from src/bootstrap/parser.h) | 0 | ||||
-rw-r--r-- | src/treewalk/primitives.c (renamed from src/bootstrap/primitives.c) | 0 | ||||
-rw-r--r-- | src/treewalk/primitives.h (renamed from src/bootstrap/primitives.h) | 0 | ||||
-rw-r--r-- | src/treewalk/read_line.c | 32 | ||||
-rw-r--r-- | src/treewalk/read_line.h | 10 | ||||
-rw-r--r-- | src/treewalk/singletons.c (renamed from src/bootstrap/singletons.c) | 0 | ||||
-rw-r--r-- | src/treewalk/string_view.c | 40 | ||||
-rw-r--r-- | src/treewalk/string_view.h | 21 |
35 files changed, 832 insertions, 0 deletions
diff --git a/src/bootstrap/darray.h b/src/bytecode/darray.h index db6234d..db6234d 100644 --- a/src/bootstrap/darray.h +++ b/src/bytecode/darray.h | |||
diff --git a/src/bytecode/debug.h b/src/bytecode/debug.h new file mode 100644 index 0000000..3d08d8f --- /dev/null +++ b/src/bytecode/debug.h | |||
@@ -0,0 +1,32 @@ | |||
1 | #ifndef BDL_DEBUG_H | ||
2 | #define BDL_DEBUG_H | ||
3 | |||
4 | void disassemble_chunk(u8 *chunk, const char *name); | ||
5 | size_t disassemble_instruction(u8 *chunk, size_t offset); | ||
6 | |||
7 | void | ||
8 | disassemble_chunk(u8 *chunk, const char *name) { | ||
9 | printf("== %s ==\n", name); | ||
10 | size_t offset = 0; | ||
11 | while (offset < array_size(chunk)) { | ||
12 | offset = disassemble_instruction(chunk, offset); | ||
13 | } | ||
14 | } | ||
15 | |||
16 | size_t | ||
17 | disassemble_instruction(u8 *chunk, size_t offset) { | ||
18 | printf("%04ld ", offset); | ||
19 | u8 instruction = chunk[offset]; | ||
20 | switch (instruction) { | ||
21 | case OP_RETURN: { | ||
22 | printf("OP_RETURN\n"); | ||
23 | return offset + 1; | ||
24 | } break; | ||
25 | default: { | ||
26 | printf("Unknown OP: %d\n", instruction); | ||
27 | return offset + 1; | ||
28 | } break; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | #endif // BDL_DEBUG_H | ||
diff --git a/src/bootstrap/errors.c b/src/bytecode/errors.c index d957cfa..d957cfa 100644 --- a/src/bootstrap/errors.c +++ b/src/bytecode/errors.c | |||
diff --git a/src/bootstrap/errors.h b/src/bytecode/errors.h index 7916f4a..7916f4a 100644 --- a/src/bootstrap/errors.h +++ b/src/bytecode/errors.h | |||
diff --git a/src/bootstrap/lexer.c b/src/bytecode/lexer.c index 38ca37c..38ca37c 100644 --- a/src/bootstrap/lexer.c +++ b/src/bytecode/lexer.c | |||
diff --git a/src/bytecode/lexer.h b/src/bytecode/lexer.h new file mode 100644 index 0000000..e58dd05 --- /dev/null +++ b/src/bytecode/lexer.h | |||
@@ -0,0 +1,60 @@ | |||
1 | #ifndef BDL_LEXER_H | ||
2 | #define BDL_LEXER_H | ||
3 | |||
4 | #include "string_view.h" | ||
5 | |||
6 | |||
7 | typedef enum TokenType { | ||
8 | TOKEN_UNKNOWN = 0, | ||
9 | TOKEN_LPAREN, | ||
10 | TOKEN_RPAREN, | ||
11 | TOKEN_QUOTE, | ||
12 | TOKEN_TRUE, | ||
13 | TOKEN_FALSE, | ||
14 | TOKEN_NIL, | ||
15 | TOKEN_FIXNUM, | ||
16 | TOKEN_SYMBOL, | ||
17 | TOKEN_STRING, | ||
18 | TOKEN_EOF, | ||
19 | } TokenType; | ||
20 | |||
21 | typedef struct Token { | ||
22 | TokenType type; | ||
23 | StringView value; | ||
24 | size_t line; | ||
25 | size_t column; | ||
26 | } Token; | ||
27 | |||
28 | typedef struct Scanner { | ||
29 | StringView current; | ||
30 | size_t line_number; | ||
31 | size_t col_number; | ||
32 | size_t offset; | ||
33 | } Scanner; | ||
34 | |||
35 | // Print a token to standard output for debugging purposes. | ||
36 | void print_token(Token tok); | ||
37 | |||
38 | // Same functionality as the ScanView pairs, but keeping track of line and | ||
39 | // column numbers. | ||
40 | char scan_next(Scanner *scanner); | ||
41 | char scan_peek(const Scanner *scanner); | ||
42 | |||
43 | // Check if the current scanner still have characters left. | ||
44 | bool scan_has_next(const Scanner *scanner); | ||
45 | |||
46 | // Advance the scanner until we ran out of whitespace. | ||
47 | void skip_whitespace(Scanner *scanner); | ||
48 | |||
49 | // Check if a given character is a delimiter. | ||
50 | bool is_delimiter(char c); | ||
51 | |||
52 | // Extract the token type from the current string. | ||
53 | TokenType find_primitive_type(const StringView value); | ||
54 | |||
55 | // Generate a list of tokens from the given string. | ||
56 | Token * tokenize(const StringView *sv); | ||
57 | |||
58 | #define TOK_BUF_CAP 256 | ||
59 | |||
60 | #endif // BDL_LEXER_H | ||
diff --git a/src/bytecode/main.c b/src/bytecode/main.c new file mode 100644 index 0000000..78fdfd3 --- /dev/null +++ b/src/bytecode/main.c | |||
@@ -0,0 +1,197 @@ | |||
1 | #include <assert.h> | ||
2 | #include <getopt.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | |||
7 | #include "types.h" | ||
8 | #include "darray.h" | ||
9 | #include "ops.h" | ||
10 | #include "debug.h" | ||
11 | #include "errors.c" | ||
12 | #include "lexer.c" | ||
13 | #include "read_line.c" | ||
14 | #include "string_view.c" | ||
15 | |||
16 | void | ||
17 | init(void) { | ||
18 | // STUB | ||
19 | } | ||
20 | |||
21 | void | ||
22 | process_source(const StringView *source) { | ||
23 | Token *tokens = tokenize(source); | ||
24 | if (errors_n != 0) { | ||
25 | array_free(tokens); | ||
26 | return; | ||
27 | } | ||
28 | |||
29 | // Test chunks and debugging utilities. | ||
30 | u8 *chunk = NULL; | ||
31 | array_init(chunk, 0); | ||
32 | array_push(chunk, OP_RETURN); | ||
33 | array_push(chunk, OP_RETURN); | ||
34 | array_push(chunk, OP_RETURN); | ||
35 | array_push(chunk, OP_RETURN); | ||
36 | disassemble_chunk(chunk, "test chunk"); | ||
37 | |||
38 | array_free(chunk); | ||
39 | array_free(tokens); | ||
40 | } | ||
41 | |||
42 | #define REPL_PROMPT "bdl> " | ||
43 | |||
44 | void | ||
45 | run_repl(void) { | ||
46 | printf("BDL REPL (Press Ctrl-D or Ctrl-C to exit)\n"); | ||
47 | while (true) { | ||
48 | printf(REPL_PROMPT); | ||
49 | StringView sv = read_line(); | ||
50 | if (sv.start == NULL) { | ||
51 | return; | ||
52 | } | ||
53 | process_source(&sv); | ||
54 | |||
55 | // Check if there were any errors. | ||
56 | if (errors_n != 0 && !supress_errors) { | ||
57 | for (size_t i = 0; i < errors_n; i++) { | ||
58 | Error err = errors[i]; | ||
59 | for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) { | ||
60 | putchar(' '); | ||
61 | } | ||
62 | printf("|\n"); | ||
63 | for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) { | ||
64 | putchar(' '); | ||
65 | } | ||
66 | printf("%s\n", error_msgs[err.value]); | ||
67 | } | ||
68 | errors_n = 0; | ||
69 | continue; | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | void | ||
75 | run_file(char *file_name) { | ||
76 | FILE *file = fopen(file_name, "r"); | ||
77 | if (!file) { | ||
78 | fprintf(stderr, "error: couldn't open input file: %s\n", file_name); | ||
79 | exit(EXIT_FAILURE); | ||
80 | } | ||
81 | |||
82 | // Read entire file into memory. | ||
83 | fseek(file, 0, SEEK_END); | ||
84 | size_t file_size = ftell(file); | ||
85 | fseek(file, 0, SEEK_SET); | ||
86 | |||
87 | char *source = malloc(file_size + 1); | ||
88 | fread(source, 1, file_size, file); | ||
89 | source[file_size] = 0; | ||
90 | |||
91 | StringView sv = (StringView){ | ||
92 | .start = source, | ||
93 | .n = file_size, | ||
94 | }; | ||
95 | |||
96 | process_source(&sv); | ||
97 | |||
98 | // Check if there were any errors. | ||
99 | if (errors_n != 0 && !supress_errors) { | ||
100 | for (size_t i = 0; i < errors_n; i++) { | ||
101 | Error err = errors[i]; | ||
102 | fprintf(stderr, "%s", file_name); | ||
103 | if (err.line != 0) { | ||
104 | fprintf(stderr, ":%ld:%ld", err.line, err.col); | ||
105 | } | ||
106 | fprintf(stderr, ": %s\n", error_msgs[err.value]); | ||
107 | } | ||
108 | errors_n = 0; | ||
109 | } | ||
110 | |||
111 | free(source); | ||
112 | fclose(file); | ||
113 | } | ||
114 | |||
115 | #define STDIN_BUF_CAP 16 | ||
116 | |||
117 | void | ||
118 | run_stdin(void) { | ||
119 | size_t buf_size = 0; | ||
120 | char *source = NULL; | ||
121 | array_init(source, STDIN_BUF_CAP); | ||
122 | |||
123 | char c; | ||
124 | while ((c = getchar()) != EOF) { | ||
125 | array_push(source, c); | ||
126 | buf_size++; | ||
127 | } | ||
128 | |||
129 | StringView sv = (StringView){ | ||
130 | .start = source, | ||
131 | .n = buf_size, | ||
132 | }; | ||
133 | |||
134 | process_source(&sv); | ||
135 | |||
136 | // Check if there were any errors. | ||
137 | if (errors_n != 0 && !supress_errors) { | ||
138 | for (size_t i = 0; i < errors_n; i++) { | ||
139 | Error err = errors[i]; | ||
140 | fprintf(stderr, "stdin"); | ||
141 | if (err.line != 0) { | ||
142 | fprintf(stderr, ":%ld:%ld", err.line, err.col); | ||
143 | } | ||
144 | fprintf(stderr, ": %s\n", error_msgs[err.value]); | ||
145 | } | ||
146 | errors_n = 0; | ||
147 | } | ||
148 | |||
149 | array_free(source); | ||
150 | } | ||
151 | |||
152 | #ifndef BIN_NAME | ||
153 | #define BIN_NAME "bdl" | ||
154 | #endif | ||
155 | |||
156 | void | ||
157 | print_usage(void) { | ||
158 | printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME); | ||
159 | printf("\n"); | ||
160 | printf("\t-i\tInteractive mode (REPL).\n"); | ||
161 | printf("\n"); | ||
162 | } | ||
163 | |||
164 | int | ||
165 | main(int argc, char *argv[]) { | ||
166 | init(); | ||
167 | |||
168 | int option; | ||
169 | while ((option = getopt(argc, argv, "i")) != -1) { | ||
170 | switch (option) { | ||
171 | case 'i': { | ||
172 | // Interactive mode. | ||
173 | run_repl(); | ||
174 | return EXIT_SUCCESS; | ||
175 | } break; | ||
176 | default: { | ||
177 | print_usage(); | ||
178 | return EXIT_FAILURE; | ||
179 | } break; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | // Run from stdin. | ||
184 | if (optind == argc) { | ||
185 | run_stdin(); | ||
186 | return EXIT_SUCCESS; | ||
187 | } | ||
188 | |||
189 | // Run from file. | ||
190 | while (optind < argc) { | ||
191 | char *file_name = argv[optind]; | ||
192 | run_file(file_name); | ||
193 | optind++; | ||
194 | } | ||
195 | |||
196 | return EXIT_SUCCESS; | ||
197 | } | ||
diff --git a/src/bytecode/ops.h b/src/bytecode/ops.h new file mode 100644 index 0000000..f7001ad --- /dev/null +++ b/src/bytecode/ops.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef BDL_OPS_H | ||
2 | #define BDL_OPS_H | ||
3 | |||
4 | typedef enum Ops { | ||
5 | OP_RETURN = 1, | ||
6 | } Ops; | ||
7 | |||
8 | #endif // BDL_OPS_H | ||
diff --git a/src/bootstrap/read_line.c b/src/bytecode/read_line.c index 03146ad..03146ad 100644 --- a/src/bootstrap/read_line.c +++ b/src/bytecode/read_line.c | |||
diff --git a/src/bootstrap/read_line.h b/src/bytecode/read_line.h index 160bce0..160bce0 100644 --- a/src/bootstrap/read_line.h +++ b/src/bytecode/read_line.h | |||
diff --git a/src/bootstrap/string_view.c b/src/bytecode/string_view.c index 39fabe9..39fabe9 100644 --- a/src/bootstrap/string_view.c +++ b/src/bytecode/string_view.c | |||
diff --git a/src/bootstrap/string_view.h b/src/bytecode/string_view.h index 42273ab..42273ab 100644 --- a/src/bootstrap/string_view.h +++ b/src/bytecode/string_view.h | |||
diff --git a/src/bytecode/types.h b/src/bytecode/types.h new file mode 100644 index 0000000..dc21756 --- /dev/null +++ b/src/bytecode/types.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #ifndef BDL_TYPES_H | ||
2 | #define BDL_TYPES_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stddef.h> | ||
6 | #include <stdint.h> | ||
7 | |||
8 | typedef uint8_t u8; | ||
9 | typedef uint16_t u16; | ||
10 | typedef uint32_t u32; | ||
11 | typedef uint64_t u64; | ||
12 | typedef int8_t s8; | ||
13 | typedef int16_t s16; | ||
14 | typedef int32_t s32; | ||
15 | typedef int64_t s64; | ||
16 | typedef volatile u8 vu8; | ||
17 | typedef volatile u16 vu16; | ||
18 | typedef volatile u32 vu32; | ||
19 | typedef volatile u64 vu64; | ||
20 | typedef volatile s8 vs8; | ||
21 | typedef volatile s16 vs16; | ||
22 | typedef volatile s32 vs32; | ||
23 | typedef volatile s64 vs64; | ||
24 | |||
25 | #define KB(N) ((u64)(N) * 1024) | ||
26 | #define MB(N) ((u64)KB(N) * 1024) | ||
27 | #define GB(N) ((u64)MB(N) * 1024) | ||
28 | #define TB(N) ((u64)GB(N) * 1024) | ||
29 | |||
30 | #endif // BDL_TYPES_H | ||
diff --git a/src/treewalk/darray.h b/src/treewalk/darray.h new file mode 100644 index 0000000..db6234d --- /dev/null +++ b/src/treewalk/darray.h | |||
@@ -0,0 +1,78 @@ | |||
1 | #ifndef BDL_DARRAY_H | ||
2 | #define BDL_DARRAY_H | ||
3 | |||
4 | #include <string.h> | ||
5 | |||
6 | typedef struct ArrayHeader { | ||
7 | size_t size; | ||
8 | size_t cap; | ||
9 | } ArrayHeader; | ||
10 | |||
11 | // Header/Size/capacity accessors. | ||
12 | #define array_head(ARR) ((ArrayHeader *)((char *)(ARR) - sizeof(ArrayHeader))) | ||
13 | #define array_size(ARR) ((ARR) ? array_head(ARR)->size : 0) | ||
14 | #define array_cap(ARR) ((ARR) ? array_head(ARR)->cap : 0) | ||
15 | |||
16 | // Initialize a dynamic array ARR with N elements. The initialization doesn't | ||
17 | // zero out the data, so thread carefully.. | ||
18 | #define array_init(ARR,N) ((ARR) = _array_reserve(N, sizeof(*(ARR)))) | ||
19 | |||
20 | // Push a given element T to the dynamic array ARR. | ||
21 | #define array_push(ARR, T) \ | ||
22 | ((ARR) = _array_maybe_grow(ARR, sizeof(T)), \ | ||
23 | (ARR)[array_head(ARR)->size++] = (T)) | ||
24 | |||
25 | // Return the last element of the array. Can be used to build stacks. | ||
26 | #define array_pop(ARR) (ARR)[--array_head(ARR)->size] | ||
27 | |||
28 | // Insert N bytes from the SRC array into the ARR dynamic array. | ||
29 | #define array_insert(ARR, SRC, N) \ | ||
30 | ((ARR) = _array_insert(ARR, SRC, N, sizeof(*(ARR)))) | ||
31 | |||
32 | // Free the memory from the original allocated position. | ||
33 | #define array_free(ARR) ((ARR) ? free(array_head(ARR)), (ARR) = NULL : 0) | ||
34 | |||
35 | static inline void * | ||
36 | _array_reserve(size_t num_elem, size_t type_size) { | ||
37 | char *p = malloc(num_elem * type_size + sizeof(ArrayHeader)); | ||
38 | p += sizeof(ArrayHeader); | ||
39 | array_head(p)->size = 0; | ||
40 | array_head(p)->cap = num_elem; | ||
41 | return p; | ||
42 | } | ||
43 | |||
44 | static inline void * | ||
45 | _array_maybe_grow(void *arr, size_t type_size) { | ||
46 | ArrayHeader *head = array_head(arr); | ||
47 | if (head->cap == head->size) { | ||
48 | if (head->cap == 0) { | ||
49 | head->cap++; | ||
50 | } else { | ||
51 | head->cap *= 2; | ||
52 | } | ||
53 | head = realloc(head, head->cap * type_size + sizeof(ArrayHeader)); | ||
54 | } | ||
55 | arr = (char *)head + sizeof(ArrayHeader); | ||
56 | return arr; | ||
57 | } | ||
58 | |||
59 | static inline | ||
60 | char * _array_insert(char *arr, const char *src, size_t n_bytes, size_t type_size) { | ||
61 | ArrayHeader *head = array_head(arr); | ||
62 | size_t new_size = n_bytes + head->size; | ||
63 | if (new_size >= head->cap * type_size) { | ||
64 | if (head->cap == 0) { | ||
65 | head->cap = 1; | ||
66 | } | ||
67 | while (new_size >= head->cap * type_size) { | ||
68 | head->cap *= 2; | ||
69 | } | ||
70 | head = realloc(head, head->cap * type_size + sizeof(ArrayHeader)); | ||
71 | } | ||
72 | arr = (char *)head + sizeof(ArrayHeader); | ||
73 | memcpy((arr + head->size), src, n_bytes); | ||
74 | head->size = new_size; | ||
75 | return arr; | ||
76 | } | ||
77 | |||
78 | #endif // BDL_DARRAY_H | ||
diff --git a/src/bootstrap/environment.c b/src/treewalk/environment.c index dd4a648..dd4a648 100644 --- a/src/bootstrap/environment.c +++ b/src/treewalk/environment.c | |||
diff --git a/src/bootstrap/environment.h b/src/treewalk/environment.h index 5ee21ad..5ee21ad 100644 --- a/src/bootstrap/environment.h +++ b/src/treewalk/environment.h | |||
diff --git a/src/treewalk/errors.c b/src/treewalk/errors.c new file mode 100644 index 0000000..d957cfa --- /dev/null +++ b/src/treewalk/errors.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include "errors.h" | ||
2 | |||
3 | static const char* error_msgs[] = { | ||
4 | [ERR_UNKNOWN] = "error: something unexpected happened", | ||
5 | [ERR_UNMATCHED_STRING] = "error: unmatched string delimiter", | ||
6 | [ERR_UNBALANCED_PAREN] = "error: unbalanced parentheses", | ||
7 | [ERR_NOT_IMPLEMENTED] = "error: not implemented", | ||
8 | [ERR_EOF_REACHED] = "error: EOF reached", | ||
9 | [ERR_UNKNOWN_TOKEN] = "error: unknown token", | ||
10 | [ERR_UNKNOWN_OBJ_TYPE] = "error: can't eval unknown object type", | ||
11 | [ERR_NOT_A_SYMBOL] = "error: object is not a symbol", | ||
12 | [ERR_SYMBOL_NOT_FOUND] = "error: symbol not found", | ||
13 | [ERR_OBJ_NOT_CALLABLE] = "error: object is not callable", | ||
14 | [ERR_NOT_ENOUGH_ARGS] = "error: not enough arguments", | ||
15 | [ERR_TOO_MANY_ARGS] = "error: too many arguments", | ||
16 | [ERR_WRONG_ARG_TYPE] = "error: wrong argument type", | ||
17 | [ERR_DIVISION_BY_ZERO] = "error: division by zero", | ||
18 | }; | ||
19 | |||
20 | static Error errors[ERR_MAX_NUMBER]; | ||
21 | static size_t errors_n = 0; | ||
22 | static bool supress_errors = false; | ||
23 | |||
24 | void | ||
25 | error_push(Error error) { | ||
26 | if (errors_n < ERR_MAX_NUMBER) { | ||
27 | errors[errors_n++] = error; | ||
28 | } | ||
29 | } | ||
diff --git a/src/treewalk/errors.h b/src/treewalk/errors.h new file mode 100644 index 0000000..7916f4a --- /dev/null +++ b/src/treewalk/errors.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #ifndef BDL_ERRORS_H | ||
2 | #define BDL_ERRORS_H | ||
3 | |||
4 | typedef enum ErrorType { | ||
5 | ERR_TYPE_LEXER, | ||
6 | ERR_TYPE_PARSER, | ||
7 | ERR_TYPE_RUNTIME, | ||
8 | } ErrorType; | ||
9 | |||
10 | typedef enum ErrorValue { | ||
11 | ERR_UNKNOWN = 0, | ||
12 | ERR_UNMATCHED_STRING, | ||
13 | ERR_UNBALANCED_PAREN, | ||
14 | ERR_NOT_IMPLEMENTED, | ||
15 | ERR_EOF_REACHED, | ||
16 | ERR_UNKNOWN_TOKEN, | ||
17 | ERR_UNKNOWN_OBJ_TYPE, | ||
18 | ERR_NOT_A_SYMBOL, | ||
19 | ERR_SYMBOL_NOT_FOUND, | ||
20 | ERR_OBJ_NOT_CALLABLE, | ||
21 | ERR_NOT_ENOUGH_ARGS, | ||
22 | ERR_TOO_MANY_ARGS, | ||
23 | ERR_WRONG_ARG_TYPE, | ||
24 | ERR_DIVISION_BY_ZERO, | ||
25 | } ErrorValue; | ||
26 | |||
27 | typedef struct Error { | ||
28 | ErrorType type; | ||
29 | ErrorValue value; | ||
30 | size_t line; | ||
31 | size_t col; | ||
32 | } Error; | ||
33 | |||
34 | void error_push(Error error); | ||
35 | |||
36 | #define ERR_MAX_NUMBER 16 | ||
37 | |||
38 | #endif // BDL_ERRORS_H | ||
diff --git a/src/bootstrap/gc.c b/src/treewalk/gc.c index 358a07e..358a07e 100644 --- a/src/bootstrap/gc.c +++ b/src/treewalk/gc.c | |||
diff --git a/src/bootstrap/gc.h b/src/treewalk/gc.h index 9ad1615..9ad1615 100644 --- a/src/bootstrap/gc.h +++ b/src/treewalk/gc.h | |||
diff --git a/src/bootstrap/hashtable.h b/src/treewalk/hashtable.h index 8f210e3..8f210e3 100644 --- a/src/bootstrap/hashtable.h +++ b/src/treewalk/hashtable.h | |||
diff --git a/src/treewalk/lexer.c b/src/treewalk/lexer.c new file mode 100644 index 0000000..38ca37c --- /dev/null +++ b/src/treewalk/lexer.c | |||
@@ -0,0 +1,257 @@ | |||
1 | #include "lexer.h" | ||
2 | |||
3 | void | ||
4 | print_token(Token tok) { | ||
5 | printf("LINE: %3ld COL: %3ld ", tok.line, tok.column); | ||
6 | switch (tok.type) { | ||
7 | case TOKEN_LPAREN: { | ||
8 | printf("TOKEN_LPAREN"); | ||
9 | } break; | ||
10 | case TOKEN_RPAREN: { | ||
11 | printf("TOKEN_RPAREN"); | ||
12 | } break; | ||
13 | case TOKEN_QUOTE: { | ||
14 | printf("TOKEN_QUOTE"); | ||
15 | } break; | ||
16 | case TOKEN_TRUE: { | ||
17 | printf("TOKEN_TRUE"); | ||
18 | } break; | ||
19 | case TOKEN_FALSE: { | ||
20 | printf("TOKEN_FALSE"); | ||
21 | } break; | ||
22 | case TOKEN_NIL: { | ||
23 | printf("TOKEN_NIL"); | ||
24 | } break; | ||
25 | case TOKEN_FIXNUM: { | ||
26 | printf("TOKEN_FIXNUM -> "); | ||
27 | sv_write(&tok.value, stdout); | ||
28 | } break; | ||
29 | case TOKEN_SYMBOL: { | ||
30 | printf("TOKEN_SYMBOL -> "); | ||
31 | sv_write(&tok.value, stdout); | ||
32 | } break; | ||
33 | case TOKEN_STRING: { | ||
34 | printf("TOKEN_STRING -> "); | ||
35 | sv_write(&tok.value, stdout); | ||
36 | } break; | ||
37 | case TOKEN_EOF: { | ||
38 | printf("TOKEN_EOF"); | ||
39 | } break; | ||
40 | case TOKEN_UNKNOWN: { | ||
41 | printf("TOKEN_UNKNOWN"); | ||
42 | } break; | ||
43 | } | ||
44 | printf("\n"); | ||
45 | } | ||
46 | |||
47 | char | ||
48 | scan_next(Scanner *scanner) { | ||
49 | char c = sv_next(&scanner->current); | ||
50 | if (c == '\n') { | ||
51 | scanner->line_number++; | ||
52 | scanner->col_number = 1; | ||
53 | } else { | ||
54 | scanner->col_number++; | ||
55 | } | ||
56 | scanner->offset++; | ||
57 | return c; | ||
58 | } | ||
59 | |||
60 | char | ||
61 | scan_peek(const Scanner *scanner) { | ||
62 | return sv_peek(&scanner->current); | ||
63 | } | ||
64 | |||
65 | bool | ||
66 | scan_has_next(const Scanner *scanner) { | ||
67 | return scanner->current.n != 0; | ||
68 | } | ||
69 | |||
70 | void | ||
71 | skip_whitespace(Scanner *scanner) { | ||
72 | while (scan_has_next(scanner)) { | ||
73 | char c = scan_peek(scanner); | ||
74 | switch (c) { | ||
75 | case ' ': | ||
76 | case '\f': | ||
77 | case '\n': | ||
78 | case '\r': | ||
79 | case '\t': | ||
80 | case '\v': { | ||
81 | scan_next(scanner); | ||
82 | } break; | ||
83 | default: { | ||
84 | return; | ||
85 | } break; | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | bool | ||
91 | is_delimiter(char c) { | ||
92 | switch (c) { | ||
93 | case EOF: | ||
94 | case '\0': | ||
95 | case ';': | ||
96 | case '"': | ||
97 | case '\'': | ||
98 | case '(': | ||
99 | case ')': | ||
100 | case ' ': | ||
101 | case '\f': | ||
102 | case '\n': | ||
103 | case '\r': | ||
104 | case '\t': | ||
105 | case '\v': { | ||
106 | return true; | ||
107 | } break; | ||
108 | } | ||
109 | return false; | ||
110 | } | ||
111 | |||
112 | TokenType | ||
113 | find_primitive_type(const StringView value) { | ||
114 | bool is_fixnum = true; | ||
115 | for (size_t i = 0; i < value.n; i++) { | ||
116 | char c = value.start[i]; | ||
117 | if (i == 0 && c == '-' && value.n > 1) { | ||
118 | continue; | ||
119 | } | ||
120 | if (!(c >= '0' && c <= '9')) { | ||
121 | is_fixnum = false; | ||
122 | break; | ||
123 | } | ||
124 | } | ||
125 | if (is_fixnum) { | ||
126 | return TOKEN_FIXNUM; | ||
127 | } | ||
128 | if (sv_equal(&value, &(StringView){"true", 4})) { | ||
129 | return TOKEN_TRUE; | ||
130 | } | ||
131 | if (sv_equal(&value, &(StringView){"false", 5})) { | ||
132 | return TOKEN_FALSE; | ||
133 | } | ||
134 | return TOKEN_SYMBOL; | ||
135 | } | ||
136 | |||
137 | Token * | ||
138 | tokenize(const StringView *sv) { | ||
139 | Token *tokens = NULL; | ||
140 | array_init(tokens, 1); | ||
141 | Scanner scanner = (Scanner){ | ||
142 | .current = *sv, | ||
143 | .line_number = 1, | ||
144 | .col_number = 1, | ||
145 | }; | ||
146 | |||
147 | while (scan_has_next(&scanner)) { | ||
148 | skip_whitespace(&scanner); | ||
149 | size_t line = scanner.line_number; | ||
150 | size_t col = scanner.col_number; | ||
151 | size_t offset = scanner.offset; | ||
152 | char c = scan_next(&scanner); | ||
153 | switch (c) { | ||
154 | case ';': { | ||
155 | while ((c = scan_next(&scanner)) != '\n' && c != '\0') {} | ||
156 | } break; | ||
157 | case '"': { | ||
158 | char prev = c; | ||
159 | bool found = false; | ||
160 | size_t n = 0; | ||
161 | while (scan_has_next(&scanner)) { | ||
162 | c = scan_next(&scanner); | ||
163 | if (c == '"' && prev != '\\') { | ||
164 | found = true; | ||
165 | break; | ||
166 | } | ||
167 | prev = c; | ||
168 | n++; | ||
169 | } | ||
170 | if (!found) { | ||
171 | error_push((Error){ | ||
172 | .type = ERR_TYPE_LEXER, | ||
173 | .value = ERR_UNMATCHED_STRING, | ||
174 | .line = line, | ||
175 | .col = col, | ||
176 | }); | ||
177 | return tokens; | ||
178 | } | ||
179 | Token token = (Token){ | ||
180 | .value = (StringView){ | ||
181 | .start = &sv->start[offset + 1], | ||
182 | .n = n, | ||
183 | }, | ||
184 | .type = TOKEN_STRING, | ||
185 | .line = line, | ||
186 | .column = col, | ||
187 | }; | ||
188 | array_push(tokens, token); | ||
189 | } break; | ||
190 | case '\'': { | ||
191 | Token token = (Token){ | ||
192 | .type = TOKEN_QUOTE, | ||
193 | .line = line, | ||
194 | .column = col, | ||
195 | }; | ||
196 | array_push(tokens, token); | ||
197 | } break; | ||
198 | case '(': { | ||
199 | if (scan_peek(&scanner) == ')') { | ||
200 | scan_next(&scanner); | ||
201 | Token token = (Token){ | ||
202 | .type = TOKEN_NIL, | ||
203 | .line = line, | ||
204 | .column = col, | ||
205 | }; | ||
206 | array_push(tokens, token); | ||
207 | } else { | ||
208 | Token token = (Token){ | ||
209 | .type = TOKEN_LPAREN, | ||
210 | .line = line, | ||
211 | .column = col, | ||
212 | }; | ||
213 | array_push(tokens, token); | ||
214 | } | ||
215 | } break; | ||
216 | case ')': { | ||
217 | Token token = (Token){ | ||
218 | .type = TOKEN_RPAREN, | ||
219 | .line = line, | ||
220 | .column = col, | ||
221 | }; | ||
222 | array_push(tokens, token); | ||
223 | } break; | ||
224 | default: { | ||
225 | size_t n = 1; | ||
226 | while (!is_delimiter(scan_peek(&scanner))) { | ||
227 | scan_next(&scanner); | ||
228 | n++; | ||
229 | } | ||
230 | if (c == EOF || c == '\0') { | ||
231 | break; | ||
232 | } | ||
233 | Token token = (Token){ | ||
234 | .value = (StringView){ | ||
235 | .start = &sv->start[offset], | ||
236 | .n = n, | ||
237 | }, | ||
238 | .type = TOKEN_SYMBOL, | ||
239 | .line = line, | ||
240 | .column = col, | ||
241 | }; | ||
242 | token.type = find_primitive_type(token.value); | ||
243 | array_push(tokens, token); | ||
244 | } break; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | // Push EOF token. | ||
249 | Token token = (Token){ | ||
250 | .type = TOKEN_EOF, | ||
251 | .line = scanner.line_number, | ||
252 | .column = 1, | ||
253 | }; | ||
254 | array_push(tokens, token); | ||
255 | |||
256 | return tokens; | ||
257 | } | ||
diff --git a/src/bootstrap/lexer.h b/src/treewalk/lexer.h index 2b2789f..2b2789f 100644 --- a/src/bootstrap/lexer.h +++ b/src/treewalk/lexer.h | |||
diff --git a/src/bootstrap/main.c b/src/treewalk/main.c index a5888fd..a5888fd 100755 --- a/src/bootstrap/main.c +++ b/src/treewalk/main.c | |||
diff --git a/src/bootstrap/objects.c b/src/treewalk/objects.c index c71bc40..c71bc40 100644 --- a/src/bootstrap/objects.c +++ b/src/treewalk/objects.c | |||
diff --git a/src/bootstrap/objects.h b/src/treewalk/objects.h index ed623eb..ed623eb 100644 --- a/src/bootstrap/objects.h +++ b/src/treewalk/objects.h | |||
diff --git a/src/bootstrap/parser.c b/src/treewalk/parser.c index a2f0f71..a2f0f71 100644 --- a/src/bootstrap/parser.c +++ b/src/treewalk/parser.c | |||
diff --git a/src/bootstrap/parser.h b/src/treewalk/parser.h index 3834c75..3834c75 100644 --- a/src/bootstrap/parser.h +++ b/src/treewalk/parser.h | |||
diff --git a/src/bootstrap/primitives.c b/src/treewalk/primitives.c index 8b0d407..8b0d407 100644 --- a/src/bootstrap/primitives.c +++ b/src/treewalk/primitives.c | |||
diff --git a/src/bootstrap/primitives.h b/src/treewalk/primitives.h index f874b17..f874b17 100644 --- a/src/bootstrap/primitives.h +++ b/src/treewalk/primitives.h | |||
diff --git a/src/treewalk/read_line.c b/src/treewalk/read_line.c new file mode 100644 index 0000000..03146ad --- /dev/null +++ b/src/treewalk/read_line.c | |||
@@ -0,0 +1,32 @@ | |||
1 | #include "read_line.h" | ||
2 | |||
3 | static char readline_buf[RL_BUF_SIZE]; | ||
4 | |||
5 | StringView | ||
6 | read_line(void) { | ||
7 | // Clear buffer. | ||
8 | for (size_t i = 0; i < RL_BUF_SIZE; i++) { | ||
9 | readline_buf[i] = 0; | ||
10 | } | ||
11 | |||
12 | // Barebones readline implementation. | ||
13 | size_t n = 0; | ||
14 | char c; | ||
15 | while ((c = getchar()) != '\n') { | ||
16 | if (c == '\b') { | ||
17 | readline_buf[n] = '\0'; | ||
18 | n--; | ||
19 | } else if (c == EOF || c == '\0') { | ||
20 | return (StringView){ .start = NULL, .n = 0 }; | ||
21 | } else if ((c >= ' ' && c <= '~') && n < RL_BUF_SIZE) { | ||
22 | readline_buf[n] = c; | ||
23 | n++; | ||
24 | } | ||
25 | } | ||
26 | |||
27 | StringView sv = (StringView){ | ||
28 | .start = (char *)&readline_buf, | ||
29 | .n = n, | ||
30 | }; | ||
31 | return sv; | ||
32 | } | ||
diff --git a/src/treewalk/read_line.h b/src/treewalk/read_line.h new file mode 100644 index 0000000..160bce0 --- /dev/null +++ b/src/treewalk/read_line.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef BDL_READ_LINE_H | ||
2 | #define BDL_READ_LINE_H | ||
3 | |||
4 | #include "string_view.h" | ||
5 | |||
6 | StringView read_line(void); | ||
7 | |||
8 | #define RL_BUF_SIZE 1024 | ||
9 | |||
10 | #endif // BDL_READ_LINE_H | ||
diff --git a/src/bootstrap/singletons.c b/src/treewalk/singletons.c index eb9c397..eb9c397 100644 --- a/src/bootstrap/singletons.c +++ b/src/treewalk/singletons.c | |||
diff --git a/src/treewalk/string_view.c b/src/treewalk/string_view.c new file mode 100644 index 0000000..39fabe9 --- /dev/null +++ b/src/treewalk/string_view.c | |||
@@ -0,0 +1,40 @@ | |||
1 | #include "string_view.h" | ||
2 | |||
3 | char | ||
4 | sv_next(StringView *sv) { | ||
5 | if (sv->n == 0) { | ||
6 | return '\0'; | ||
7 | } | ||
8 | char c = sv->start[0]; | ||
9 | sv->start++; | ||
10 | sv->n--; | ||
11 | return c; | ||
12 | } | ||
13 | |||
14 | char | ||
15 | sv_peek(const StringView *sv) { | ||
16 | if (sv->n == 0) { | ||
17 | return '\0'; | ||
18 | } | ||
19 | return sv->start[0]; | ||
20 | } | ||
21 | |||
22 | bool | ||
23 | sv_equal(const StringView *a, const StringView *b) { | ||
24 | if (a->n != b->n) { | ||
25 | return false; | ||
26 | } | ||
27 | for (size_t i = 0; i < a->n; i++) { | ||
28 | if (a->start[i] != b->start[i]) { | ||
29 | return false; | ||
30 | } | ||
31 | } | ||
32 | return true; | ||
33 | } | ||
34 | |||
35 | void | ||
36 | sv_write(const StringView *sv, FILE *file) { | ||
37 | for (size_t i = 0; i < sv->n; i++) { | ||
38 | putc(sv->start[i], file); | ||
39 | } | ||
40 | } | ||
diff --git a/src/treewalk/string_view.h b/src/treewalk/string_view.h new file mode 100644 index 0000000..42273ab --- /dev/null +++ b/src/treewalk/string_view.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef BDL_STRINGVIEW_H | ||
2 | #define BDL_STRINGVIEW_H | ||
3 | |||
4 | typedef struct StringView { | ||
5 | char *start; | ||
6 | size_t n; | ||
7 | } StringView; | ||
8 | |||
9 | // Consume a character in the stream. | ||
10 | char sv_next(StringView *sv); | ||
11 | |||
12 | // Check what is the current character in the stream. | ||
13 | char sv_peek(const StringView *sv); | ||
14 | |||
15 | // Compare if the arguments are the same. | ||
16 | bool sv_equal(const StringView *a, const StringView *b); | ||
17 | |||
18 | // Write a character to the given output stream. | ||
19 | void sv_write(const StringView *sv, FILE *file); | ||
20 | |||
21 | #endif // BDL_STRINGVIEW_H | ||