aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xMakefile2
-rw-r--r--src/bytecode/darray.h (renamed from src/bootstrap/darray.h)0
-rw-r--r--src/bytecode/debug.h32
-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.h60
-rw-r--r--src/bytecode/main.c197
-rw-r--r--src/bytecode/ops.h8
-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.h30
-rw-r--r--src/treewalk/darray.h78
-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.c29
-rw-r--r--src/treewalk/errors.h38
-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.c257
-rw-r--r--src/treewalk/lexer.h (renamed from src/bootstrap/lexer.h)0
-rwxr-xr-xsrc/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.c32
-rw-r--r--src/treewalk/read_line.h10
-rw-r--r--src/treewalk/singletons.c (renamed from src/bootstrap/singletons.c)0
-rw-r--r--src/treewalk/string_view.c40
-rw-r--r--src/treewalk/string_view.h21
36 files changed, 833 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 4465daf..30784b2 100755
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
2.SUFFIXES: 2.SUFFIXES:
3 3
4# Source code location and files to watch for changes. 4# Source code location and files to watch for changes.
5SRC_DIR := src/bootstrap 5SRC_DIR := src/bytecode
6BUILD_DIR := build 6BUILD_DIR := build
7SRC_MAIN := $(SRC_DIR)/main.c 7SRC_MAIN := $(SRC_DIR)/main.c
8WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") 8WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h")
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
4void disassemble_chunk(u8 *chunk, const char *name);
5size_t disassemble_instruction(u8 *chunk, size_t offset);
6
7void
8disassemble_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
16size_t
17disassemble_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
7typedef 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
21typedef struct Token {
22 TokenType type;
23 StringView value;
24 size_t line;
25 size_t column;
26} Token;
27
28typedef 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.
36void print_token(Token tok);
37
38// Same functionality as the ScanView pairs, but keeping track of line and
39// column numbers.
40char scan_next(Scanner *scanner);
41char scan_peek(const Scanner *scanner);
42
43// Check if the current scanner still have characters left.
44bool scan_has_next(const Scanner *scanner);
45
46// Advance the scanner until we ran out of whitespace.
47void skip_whitespace(Scanner *scanner);
48
49// Check if a given character is a delimiter.
50bool is_delimiter(char c);
51
52// Extract the token type from the current string.
53TokenType find_primitive_type(const StringView value);
54
55// Generate a list of tokens from the given string.
56Token * 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
16void
17init(void) {
18 // STUB
19}
20
21void
22process_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
44void
45run_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
74void
75run_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
117void
118run_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
156void
157print_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
164int
165main(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
4typedef 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
8typedef uint8_t u8;
9typedef uint16_t u16;
10typedef uint32_t u32;
11typedef uint64_t u64;
12typedef int8_t s8;
13typedef int16_t s16;
14typedef int32_t s32;
15typedef int64_t s64;
16typedef volatile u8 vu8;
17typedef volatile u16 vu16;
18typedef volatile u32 vu32;
19typedef volatile u64 vu64;
20typedef volatile s8 vs8;
21typedef volatile s16 vs16;
22typedef volatile s32 vs32;
23typedef 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
6typedef 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
35static 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
44static 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
59static inline
60char * _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
3static 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
20static Error errors[ERR_MAX_NUMBER];
21static size_t errors_n = 0;
22static bool supress_errors = false;
23
24void
25error_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
4typedef enum ErrorType {
5 ERR_TYPE_LEXER,
6 ERR_TYPE_PARSER,
7 ERR_TYPE_RUNTIME,
8} ErrorType;
9
10typedef 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
27typedef struct Error {
28 ErrorType type;
29 ErrorValue value;
30 size_t line;
31 size_t col;
32} Error;
33
34void 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
3void
4print_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
47char
48scan_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
60char
61scan_peek(const Scanner *scanner) {
62 return sv_peek(&scanner->current);
63}
64
65bool
66scan_has_next(const Scanner *scanner) {
67 return scanner->current.n != 0;
68}
69
70void
71skip_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
90bool
91is_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
112TokenType
113find_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
137Token *
138tokenize(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
3static char readline_buf[RL_BUF_SIZE];
4
5StringView
6read_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
6StringView 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
3char
4sv_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
14char
15sv_peek(const StringView *sv) {
16 if (sv->n == 0) {
17 return '\0';
18 }
19 return sv->start[0];
20}
21
22bool
23sv_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
35void
36sv_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
4typedef struct StringView {
5 char *start;
6 size_t n;
7} StringView;
8
9// Consume a character in the stream.
10char sv_next(StringView *sv);
11
12// Check what is the current character in the stream.
13char sv_peek(const StringView *sv);
14
15// Compare if the arguments are the same.
16bool sv_equal(const StringView *a, const StringView *b);
17
18// Write a character to the given output stream.
19void sv_write(const StringView *sv, FILE *file);
20
21#endif // BDL_STRINGVIEW_H