1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
/*
Copyright (C) 2019, 2020 Leo Tenenbaum.
This file is part of toc. toc is distributed under version 3 of the GNU General Public License, without any warranty whatsoever.
You should have received a copy of the GNU General Public License along with toc. If not, see <https://www.gnu.org/licenses/>.
*/
/*
NOTE:
Structure of the toc compiler:
tokenizer => parser => typing (types.c) => cgen
(lexing)
toc tries to continue even after the first error.
If one stage fails, the following ones do not
start.
toc's memory management works using an allocator which never frees anything.
This is because most of toc's data is kept around until the end of the program anyways.
Use the allocator for "permanent" allocations, and err_malloc/calloc/realloc for temporary
allocations (to avoid having it take up space for a long time).
Because of this, memory leaks can happen if the compilation fails at any point, but they
should not happen if the compilation succeeds. Usually if there's an error
which causes a memory leak, it will be very small.
Functions which can fail (i.e. print an error message and stop) return a Status,
which is a bool, but GCC warns about not using the return value.
The fixed-width types U8/16/32/64 and I8/16/32/64 have been defined.
data_structures.c contains a dynamic array implementation which is very useful.
Many of the members of the types below are dynamic arrays.
It is assumed that the number of identifiers in a declaration, or parameters to a function
will fit in an int, since a function with (at least) 32768 parameters is ridiculous.
*/
/* Includes all of toc's files */
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <float.h>
#include <inttypes.h>
#ifndef COMPILE_TIME_FOREIGN_FN_SUPPORT
#define COMPILE_TIME_FOREIGN_FN_SUPPORT 0
#endif
#ifdef __cplusplus
#define new new_
#define this this_
#define export export_
#elif __STDC_VERSION__ < 199901
#ifndef inline
#define inline
#endif
#endif
/* use toc_alignof only for non-structs. it may be incorrect for pre-C(++)11. */
#if (__STDC_VERSION__ >= 201112 || __cplusplus >= 201103L) && !defined __TINYC__ && !defined __OpenBSD__ && !defined __FreeBSD__
#include <stdalign.h>
#define toc_alignof alignof
#ifdef __GNUC__
/* GCC supports non-string literals as the message for a static assertion */
#define possibly_static_assert(cond) static_assert(cond, "Assertion " #cond " failed.")
#else
#define possibly_static_assert(cond) static_assert(cond, "Assertion failed")
#endif
#else
#define toc_alignof sizeof
#define possibly_static_assert(cond) assert(cond)
#endif
#include "types.h"
/* forward declarations for debugging */
static void print_val(Value v, Type *t);
static void print_token(Token *t);
static void print_type(Type *t);
static void print_block(Block *b);
static void print_decl(Declaration *d);
static void print_stmt(Statement *s);
static void print_block_location(Block *b);
/* misc */
#define join3(a,b) a##b
#define join2(a,b) join3(a,b)
#define join(a,b) join2(a,b)
static void fprint_char_literal(FILE *f, char c) {
if (isprint(c))
fprintf(f, "'%c'", c);
else
fprintf(f, "'\\x%02x'", c);
}
static inline bool type_is_builtin(Type *t, BuiltinType b) {
return t->kind == TYPE_BUILTIN && t->builtin == b;
}
static inline bool type_is_slicechar(Type *t) {
return t->kind == TYPE_SLICE && type_is_builtin(t->slice, BUILTIN_CHAR);
}
/* utilities */
#include "allocator.c"
#include "misc.c"
#include "data_structures.c"
#include "err.c"
static size_t compiler_alignof(Type *t);
static size_t compiler_sizeof(Type *t);
#include "instance_table.c"
/* returns NULL on error */
static char *read_file_contents(Allocator *a, const char *filename, Location where) {
FILE *in = fopen(filename, "r");
if (!in) {
err_print(where, "Could not open file: %s.", filename);
return NULL;
}
char *contents = allocr_malloc(a, 4096);
contents[0] = 0; /* put 0 byte at the start of the file. see err.c:err_print_location_text to find out why */
contents[1] = 0; /* if fgets fails the first time */
long contents_cap = 4095;
long contents_len = 1;
while (fgets(contents + contents_len, (int)(contents_cap - contents_len), in)) {
contents_len += (long)strlen(contents + contents_len);
if (contents_len >= (long)contents_cap - 1024) {
size_t prev = (size_t)contents_cap + 1;
contents_cap *= 2;
contents = allocr_realloc(a, contents, prev, (size_t)contents_cap + 1);
}
}
++contents;
fclose(in);
return contents;
}
#include "identifiers.c"
#include "copy.c"
#include "tokenizer.c"
#include "parse.c"
#include "foreign.c"
#include "eval.c"
#include "infer.c"
#include "types.c"
static void cgen_decls_file(CGenerator *g, ParsedFile *f);
static void cgen_sdecls_file(CGenerator *g, ParsedFile *f);
#include "cgen.c"
#include "decls_cgen.c"
#ifdef RUN_TESTS
#include "tests.c"
#endif
#ifdef __cplusplus
#undef new
#undef this
#elif __STDC_VERSION__ < 199901
#undef inline
#endif
|