mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Start using UnaryOp/PostUnaryOp ++/-- now works post/pre and also on pointers. Fix ++/-- on any number. Added examples. c3c now compiles files by default. Extended symbol resolution. Addition of 'extern'. Multi-level paths.
This commit is contained in:
@@ -152,7 +152,9 @@ add_executable(c3c
|
|||||||
src/compiler/llvm_codegen_debug_info.c
|
src/compiler/llvm_codegen_debug_info.c
|
||||||
src/compiler/llvm_codegen_module.c
|
src/compiler/llvm_codegen_module.c
|
||||||
src/compiler/llvm_codegen_type.c
|
src/compiler/llvm_codegen_type.c
|
||||||
src/compiler/llvm_codegen_function.c)
|
src/compiler/llvm_codegen_function.c
|
||||||
|
src/build/builder.c
|
||||||
|
src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c)
|
||||||
|
|
||||||
target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,9 @@ L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
|||||||
"^" { count(); return('^'); }
|
"^" { count(); return('^'); }
|
||||||
"|" { count(); return('|'); }
|
"|" { count(); return('|'); }
|
||||||
"?" { count(); return('?'); }
|
"?" { count(); return('?'); }
|
||||||
|
"+%" { count(); return(ADDW); }
|
||||||
|
"*%" { count(); return(MULTW); }
|
||||||
|
"-%" { count(); return(SUBW); }
|
||||||
"({" { count(); return(FN_BLOCK_BEGIN); }
|
"({" { count(); return(FN_BLOCK_BEGIN); }
|
||||||
"{)" { count(); return(FN_BLOCK_END); }
|
"{)" { count(); return(FN_BLOCK_END); }
|
||||||
[ \t\v\n\f] { count(); }
|
[ \t\v\n\f] { count(); }
|
||||||
|
|||||||
121
resources/examples/base64.c3
Normal file
121
resources/examples/base64.c3
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
module base64;
|
||||||
|
// Based on the C2 version.
|
||||||
|
|
||||||
|
const char[] LUT_ENC = {
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
|
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||||
|
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||||
|
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||||
|
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||||
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||||
|
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||||
|
'4', '5', '6', '7', '8', '9', '+', '/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const byte ERR = 0xFF;
|
||||||
|
|
||||||
|
const byte[256] LUT_DEC = {
|
||||||
|
// '+', ',', '-', '.', '/', '0', '1', '2'
|
||||||
|
62, ERR, ERR, ERR, 63, 52, 53, 54,
|
||||||
|
// '3', '4', '5', '6', '7', '8', '9', ':'
|
||||||
|
55, 56, 57, 58, 59, 60, 61, ERR,
|
||||||
|
// ';', '<', '=', '>', '?', '@', 'A', 'B'
|
||||||
|
ERR, ERR, ERR, ERR, ERR, ERR, 0, 1,
|
||||||
|
// 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'
|
||||||
|
2, 3, 4, 5, 6, 7, 8, 9,
|
||||||
|
// 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R'
|
||||||
|
10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
// 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
||||||
|
18, 19, 20, 21, 22, 23, 24, 25,
|
||||||
|
// '[', '\', ']', '^', '_', '`', 'a', 'b'
|
||||||
|
ERR, ERR, ERR, ERR, ERR, ERR, 26, 27,
|
||||||
|
// 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'
|
||||||
|
28, 29, 30, 31, 32, 33, 34, 35,
|
||||||
|
// 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r'
|
||||||
|
36, 37, 38, 39, 40, 41, 42, 43,
|
||||||
|
// 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
||||||
|
44, 45, 46, 47, 48, 49, 50, 51,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const char PAD = '=';
|
||||||
|
const char FIRST = '+';
|
||||||
|
const char LAST = 'z';
|
||||||
|
|
||||||
|
public error Base64Error
|
||||||
|
{
|
||||||
|
INVALID_CHARACTER
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void encode(byte[] in, string *out)
|
||||||
|
{
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < in.len; i++);
|
||||||
|
{
|
||||||
|
switch (i % 3)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
out[j++] = LUT_ENC[(in[i] >> 2) & 0x3F];
|
||||||
|
case 1:
|
||||||
|
out[j++] = LUT_ENC[(in[i-1] & 0x3) << 4 + ((in[i] >> 4) & 0xF)];
|
||||||
|
case 2:
|
||||||
|
out[j++] = LUT_ENC[(in[i-1] & 0xF) << 2 + ((in[i] >> 6) & 0x3)];
|
||||||
|
out[j++] = LUT_ENC[in[i] & 0x3F];
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move back
|
||||||
|
int last = in.len - 1;
|
||||||
|
|
||||||
|
// check the last and add padding
|
||||||
|
switch (last % 3)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
out[j++] = LUT_ENC[(in[last] & 0x3) << 4];
|
||||||
|
out[j++] = PAD;
|
||||||
|
out[j++] = PAD;
|
||||||
|
case 1:
|
||||||
|
out[j++] = LUT_ENC[(in[last] & 0xF) << 2];
|
||||||
|
out[j++] = PAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func int decode(string in, byte[] out) throws Base64Error
|
||||||
|
{
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
char value = in[i];
|
||||||
|
|
||||||
|
if (value == PAD) return j;
|
||||||
|
|
||||||
|
if (value < FIRST || in[i] > LAST) throw INVALID_CHARACTER;
|
||||||
|
byte c = LUT_DEC[in[i] - FIRST);
|
||||||
|
if (c == ERR) throw INVALID_CHARACTER;
|
||||||
|
|
||||||
|
switch (i % 4)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
out[j] = c << 2;
|
||||||
|
case 1:
|
||||||
|
out[j++] += (c >> 4) & 0x3;
|
||||||
|
// if not last char with padding
|
||||||
|
if (i < (len - 3) || in[len - 2] != PAD)
|
||||||
|
{
|
||||||
|
out[j] = (c & 0xF) << 4;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
out[j++] += (c >> 2) & 0xF;
|
||||||
|
if (i < (len -2) || in[len -1] != PAD)
|
||||||
|
{
|
||||||
|
out[j] = (c & 0x3) << 6;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
out[j++] += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
21
resources/examples/binarydigits.c3
Normal file
21
resources/examples/binarydigits.c3
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
module binarydigits;
|
||||||
|
|
||||||
|
func int main()
|
||||||
|
{
|
||||||
|
fot (int i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
printf("%s\n", bin(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func string@ bin(int x)
|
||||||
|
{
|
||||||
|
int bits = (x == 0) ? 1 : log10(cast(double, x)) / log10(2);
|
||||||
|
string@ ret = string.make_repeat('0', bits);
|
||||||
|
for (int i = 0; i < bits; i++)
|
||||||
|
{
|
||||||
|
ret[bits - i - 1] = x & 1 ? '1' : '0';
|
||||||
|
x >>= 1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
77
resources/examples/gameoflife.c3
Normal file
77
resources/examples/gameoflife.c3
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
module game_of_life;
|
||||||
|
|
||||||
|
struct GameBoard
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
int w;
|
||||||
|
byte* world;
|
||||||
|
byte* temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void GameBoard.show(GameBoard *board)
|
||||||
|
{
|
||||||
|
printf("\033[H");
|
||||||
|
byte* current = board.world;
|
||||||
|
for (int y = 0; y < board.h; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < board.w; x++)
|
||||||
|
{
|
||||||
|
printf(*current ? "\033[07m \033[m" : " ");
|
||||||
|
}
|
||||||
|
printf("\033[E");
|
||||||
|
}
|
||||||
|
stdout.fflush();
|
||||||
|
}
|
||||||
|
|
||||||
|
func void GameBoard.evolve(GameBoard *board)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < board.h; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < board.w; x++)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
for (int y1 = y - 1; y1 <= y + 1; y1++)
|
||||||
|
{
|
||||||
|
for (int x1 = x - 1; x1 <= x + 1; x1++)
|
||||||
|
{
|
||||||
|
int actualX = (x1 + w) % w;
|
||||||
|
int actualY = (y1 + h) % h;
|
||||||
|
if (board.world[x + y * w]) n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (board.world(x + y * w)) n--;
|
||||||
|
board.temp[x + y * w] = (n == 3 || (n == 2 && board.world(x + y * w)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < w * h; i++)
|
||||||
|
{
|
||||||
|
board.world[i] = board.temp[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int c, string[] v)
|
||||||
|
{
|
||||||
|
int w = 0, h = 0;
|
||||||
|
if (c > 1) w = atoi(v[1]);
|
||||||
|
if (c > 2) h = atoi(v[2]);
|
||||||
|
if (w <= 0) w = 30;
|
||||||
|
if (h <= 0) h = 30;
|
||||||
|
|
||||||
|
GameBoard board;
|
||||||
|
board.w = w;
|
||||||
|
board.h = h;
|
||||||
|
board.board = malloc(h * w);
|
||||||
|
board.temp = malloc(h * w);
|
||||||
|
|
||||||
|
for (int i = h * w - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
board.world[i] = rand() < RAND_MAX / 10 ? 1 : 0;
|
||||||
|
}
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
|
||||||
|
board.show();
|
||||||
|
board.evolve();
|
||||||
|
usleep(200000);
|
||||||
|
}
|
||||||
|
}
|
||||||
212
resources/examples/hash.c3
Normal file
212
resources/examples/hash.c3
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
module hash;
|
||||||
|
|
||||||
|
// Code adapted from Odin's hash.odin
|
||||||
|
// The code below should not be considered *correct*
|
||||||
|
// They are merely done to illustrate the language syntax.
|
||||||
|
|
||||||
|
public func uint adler32(byte[] data)
|
||||||
|
{
|
||||||
|
const uint ADLER_CONST = 65521;
|
||||||
|
uint a = 1;
|
||||||
|
uint b = 0;
|
||||||
|
for (byte x : data)
|
||||||
|
{
|
||||||
|
a = (a + x) % ADLER_CONST;
|
||||||
|
b = (b + a) % ADLER_CONST;
|
||||||
|
}
|
||||||
|
return (b << 16) | a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func uint crc32(byte[] data)
|
||||||
|
{
|
||||||
|
uint result = ~cast(uint, 0);
|
||||||
|
for (byte x : data)
|
||||||
|
{
|
||||||
|
result = (result >> 8) ^ CRC32_TABLE[result ^ x) & 255];
|
||||||
|
}
|
||||||
|
return ~result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func uint crc64(byte[*] data)
|
||||||
|
{
|
||||||
|
ulong result = ~cast(ulong, 0);
|
||||||
|
for (byte x : data)
|
||||||
|
{
|
||||||
|
result = (result >> 8) ^ CRC64_TABLE[(result ^ x) & 255];
|
||||||
|
}
|
||||||
|
return ~result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func uint fnv32(byte[*] data)
|
||||||
|
{
|
||||||
|
uint h = 0x811c9dc5;
|
||||||
|
for (byte x : data)
|
||||||
|
{
|
||||||
|
h = (h *% 0x01000193) ^ x;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ulong fnv64(byte[] data)
|
||||||
|
{
|
||||||
|
ulong h = 0xcbf29ce484222325;
|
||||||
|
for (byte x : data)
|
||||||
|
{
|
||||||
|
h = (h *% 0x100000001b3) ^ x;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func uint fnv32a(byte[] data)
|
||||||
|
{
|
||||||
|
uint h = 0x811c9dc5;
|
||||||
|
for (byte x : data)
|
||||||
|
{
|
||||||
|
h = (h ^ b) *% 0x01000193;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ulong fnv32a(byte[] data)
|
||||||
|
{
|
||||||
|
ulong h = 0xcbf29ce484222325;
|
||||||
|
for (byte x in data)
|
||||||
|
{
|
||||||
|
h = (h ^ b) *% 0x100000001b3;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint[256] CRC32_TABLE = {
|
||||||
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||||
|
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||||
|
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||||
|
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||||
|
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||||
|
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||||
|
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||||
|
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||||
|
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||||
|
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||||
|
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||||
|
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||||
|
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||||
|
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||||
|
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||||
|
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||||
|
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||||
|
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||||
|
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||||
|
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||||
|
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||||
|
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||||
|
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||||
|
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||||
|
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||||
|
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||||
|
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||||
|
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||||
|
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||||
|
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||||
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||||
|
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||||
|
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||||
|
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||||
|
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||||
|
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||||
|
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||||
|
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||||
|
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||||
|
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||||
|
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||||
|
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||||
|
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||||
|
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||||
|
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||||
|
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||||
|
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||||
|
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||||
|
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||||
|
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||||
|
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||||
|
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||||
|
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||||
|
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||||
|
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||||
|
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||||
|
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||||
|
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||||
|
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||||
|
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||||
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||||
|
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||||
|
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||||
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ulong[256] CRC64_TABLE = {
|
||||||
|
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||||
|
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||||
|
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||||
|
0xdb55aacf12c73561, 0x99a54b24bb2d03f2, 0x5eb4691841135847, 0x1c4488f3e8f96ed4,
|
||||||
|
0x663d78ff90e185ef, 0x24cd9914390bb37c, 0xe3dcbb28c335e8c9, 0xa12c5ac36adfde5a,
|
||||||
|
0x2f0e1eba9ea36930, 0x6dfeff5137495fa3, 0xaaefdd6dcd770416, 0xe81f3c86649d3285,
|
||||||
|
0xf45bb4758c645c51, 0xb6ab559e258e6ac2, 0x71ba77a2dfb03177, 0x334a9649765a07e4,
|
||||||
|
0xbd68d2308226b08e, 0xff9833db2bcc861d, 0x388911e7d1f2dda8, 0x7a79f00c7818eb3b,
|
||||||
|
0xcc7af1ff21c30bde, 0x8e8a101488293d4d, 0x499b3228721766f8, 0x0b6bd3c3dbfd506b,
|
||||||
|
0x854997ba2f81e701, 0xc7b97651866bd192, 0x00a8546d7c558a27, 0x4258b586d5bfbcb4,
|
||||||
|
0x5e1c3d753d46d260, 0x1cecdc9e94ace4f3, 0xdbfdfea26e92bf46, 0x990d1f49c77889d5,
|
||||||
|
0x172f5b3033043ebf, 0x55dfbadb9aee082c, 0x92ce98e760d05399, 0xd03e790cc93a650a,
|
||||||
|
0xaa478900b1228e31, 0xe8b768eb18c8b8a2, 0x2fa64ad7e2f6e317, 0x6d56ab3c4b1cd584,
|
||||||
|
0xe374ef45bf6062ee, 0xa1840eae168a547d, 0x66952c92ecb40fc8, 0x2465cd79455e395b,
|
||||||
|
0x3821458aada7578f, 0x7ad1a461044d611c, 0xbdc0865dfe733aa9, 0xff3067b657990c3a,
|
||||||
|
0x711223cfa3e5bb50, 0x33e2c2240a0f8dc3, 0xf4f3e018f031d676, 0xb60301f359dbe0e5,
|
||||||
|
0xda050215ea6c212f, 0x98f5e3fe438617bc, 0x5fe4c1c2b9b84c09, 0x1d14202910527a9a,
|
||||||
|
0x93366450e42ecdf0, 0xd1c685bb4dc4fb63, 0x16d7a787b7faa0d6, 0x5427466c1e109645,
|
||||||
|
0x4863ce9ff6e9f891, 0x0a932f745f03ce02, 0xcd820d48a53d95b7, 0x8f72eca30cd7a324,
|
||||||
|
0x0150a8daf8ab144e, 0x43a04931514122dd, 0x84b16b0dab7f7968, 0xc6418ae602954ffb,
|
||||||
|
0xbc387aea7a8da4c0, 0xfec89b01d3679253, 0x39d9b93d2959c9e6, 0x7b2958d680b3ff75,
|
||||||
|
0xf50b1caf74cf481f, 0xb7fbfd44dd257e8c, 0x70eadf78271b2539, 0x321a3e938ef113aa,
|
||||||
|
0x2e5eb66066087d7e, 0x6cae578bcfe24bed, 0xabbf75b735dc1058, 0xe94f945c9c3626cb,
|
||||||
|
0x676dd025684a91a1, 0x259d31cec1a0a732, 0xe28c13f23b9efc87, 0xa07cf2199274ca14,
|
||||||
|
0x167ff3eacbaf2af1, 0x548f120162451c62, 0x939e303d987b47d7, 0xd16ed1d631917144,
|
||||||
|
0x5f4c95afc5edc62e, 0x1dbc74446c07f0bd, 0xdaad56789639ab08, 0x985db7933fd39d9b,
|
||||||
|
0x84193f60d72af34f, 0xc6e9de8b7ec0c5dc, 0x01f8fcb784fe9e69, 0x43081d5c2d14a8fa,
|
||||||
|
0xcd2a5925d9681f90, 0x8fdab8ce70822903, 0x48cb9af28abc72b6, 0x0a3b7b1923564425,
|
||||||
|
0x70428b155b4eaf1e, 0x32b26afef2a4998d, 0xf5a348c2089ac238, 0xb753a929a170f4ab,
|
||||||
|
0x3971ed50550c43c1, 0x7b810cbbfce67552, 0xbc902e8706d82ee7, 0xfe60cf6caf321874,
|
||||||
|
0xe224479f47cb76a0, 0xa0d4a674ee214033, 0x67c58448141f1b86, 0x253565a3bdf52d15,
|
||||||
|
0xab1721da49899a7f, 0xe9e7c031e063acec, 0x2ef6e20d1a5df759, 0x6c0603e6b3b7c1ca,
|
||||||
|
0xf6fae5c07d3274cd, 0xb40a042bd4d8425e, 0x731b26172ee619eb, 0x31ebc7fc870c2f78,
|
||||||
|
0xbfc9838573709812, 0xfd39626eda9aae81, 0x3a28405220a4f534, 0x78d8a1b9894ec3a7,
|
||||||
|
0x649c294a61b7ad73, 0x266cc8a1c85d9be0, 0xe17dea9d3263c055, 0xa38d0b769b89f6c6,
|
||||||
|
0x2daf4f0f6ff541ac, 0x6f5faee4c61f773f, 0xa84e8cd83c212c8a, 0xeabe6d3395cb1a19,
|
||||||
|
0x90c79d3fedd3f122, 0xd2377cd44439c7b1, 0x15265ee8be079c04, 0x57d6bf0317edaa97,
|
||||||
|
0xd9f4fb7ae3911dfd, 0x9b041a914a7b2b6e, 0x5c1538adb04570db, 0x1ee5d94619af4648,
|
||||||
|
0x02a151b5f156289c, 0x4051b05e58bc1e0f, 0x87409262a28245ba, 0xc5b073890b687329,
|
||||||
|
0x4b9237f0ff14c443, 0x0962d61b56fef2d0, 0xce73f427acc0a965, 0x8c8315cc052a9ff6,
|
||||||
|
0x3a80143f5cf17f13, 0x7870f5d4f51b4980, 0xbf61d7e80f251235, 0xfd913603a6cf24a6,
|
||||||
|
0x73b3727a52b393cc, 0x31439391fb59a55f, 0xf652b1ad0167feea, 0xb4a25046a88dc879,
|
||||||
|
0xa8e6d8b54074a6ad, 0xea16395ee99e903e, 0x2d071b6213a0cb8b, 0x6ff7fa89ba4afd18,
|
||||||
|
0xe1d5bef04e364a72, 0xa3255f1be7dc7ce1, 0x64347d271de22754, 0x26c49cccb40811c7,
|
||||||
|
0x5cbd6cc0cc10fafc, 0x1e4d8d2b65facc6f, 0xd95caf179fc497da, 0x9bac4efc362ea149,
|
||||||
|
0x158e0a85c2521623, 0x577eeb6e6bb820b0, 0x906fc95291867b05, 0xd29f28b9386c4d96,
|
||||||
|
0xcedba04ad0952342, 0x8c2b41a1797f15d1, 0x4b3a639d83414e64, 0x09ca82762aab78f7,
|
||||||
|
0x87e8c60fded7cf9d, 0xc51827e4773df90e, 0x020905d88d03a2bb, 0x40f9e43324e99428,
|
||||||
|
0x2cffe7d5975e55e2, 0x6e0f063e3eb46371, 0xa91e2402c48a38c4, 0xebeec5e96d600e57,
|
||||||
|
0x65cc8190991cb93d, 0x273c607b30f68fae, 0xe02d4247cac8d41b, 0xa2dda3ac6322e288,
|
||||||
|
0xbe992b5f8bdb8c5c, 0xfc69cab42231bacf, 0x3b78e888d80fe17a, 0x7988096371e5d7e9,
|
||||||
|
0xf7aa4d1a85996083, 0xb55aacf12c735610, 0x724b8ecdd64d0da5, 0x30bb6f267fa73b36,
|
||||||
|
0x4ac29f2a07bfd00d, 0x08327ec1ae55e69e, 0xcf235cfd546bbd2b, 0x8dd3bd16fd818bb8,
|
||||||
|
0x03f1f96f09fd3cd2, 0x41011884a0170a41, 0x86103ab85a2951f4, 0xc4e0db53f3c36767,
|
||||||
|
0xd8a453a01b3a09b3, 0x9a54b24bb2d03f20, 0x5d45907748ee6495, 0x1fb5719ce1045206,
|
||||||
|
0x919735e51578e56c, 0xd367d40ebc92d3ff, 0x1476f63246ac884a, 0x568617d9ef46bed9,
|
||||||
|
0xe085162ab69d5e3c, 0xa275f7c11f7768af, 0x6564d5fde549331a, 0x279434164ca30589,
|
||||||
|
0xa9b6706fb8dfb2e3, 0xeb46918411358470, 0x2c57b3b8eb0bdfc5, 0x6ea7525342e1e956,
|
||||||
|
0x72e3daa0aa188782, 0x30133b4b03f2b111, 0xf7021977f9cceaa4, 0xb5f2f89c5026dc37,
|
||||||
|
0x3bd0bce5a45a6b5d, 0x79205d0e0db05dce, 0xbe317f32f78e067b, 0xfcc19ed95e6430e8,
|
||||||
|
0x86b86ed5267cdbd3, 0xc4488f3e8f96ed40, 0x0359ad0275a8b6f5, 0x41a94ce9dc428066,
|
||||||
|
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
|
||||||
|
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
|
||||||
|
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
|
||||||
|
};
|
||||||
26
resources/examples/http.c3
Normal file
26
resources/examples/http.c3
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import curl;
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
try Curl curl = Curl.new();
|
||||||
|
|
||||||
|
catch (error e)
|
||||||
|
{
|
||||||
|
printf("Failed to create new curl: %s\n", e.message);
|
||||||
|
exit(FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl.setopt(URL, "http://www.rosettacode.org/");
|
||||||
|
curl.setopt(FOLLOWLOCATION, 1);
|
||||||
|
|
||||||
|
try curl.perform();
|
||||||
|
|
||||||
|
catch (CurlError e)
|
||||||
|
{
|
||||||
|
printf("Error making request: %s\n", e.message);
|
||||||
|
exit(FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(SUCCESS);
|
||||||
|
|
||||||
|
}
|
||||||
24
resources/examples/levenshtein.c3
Normal file
24
resources/examples/levenshtein.c3
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
module levenshtein;
|
||||||
|
|
||||||
|
func int levenshtein(string s, string t)
|
||||||
|
{
|
||||||
|
// if either string is empty, difference is inserting all chars
|
||||||
|
// from the other
|
||||||
|
if (!s.size) return t.size;
|
||||||
|
if (!t.size) return s.size;
|
||||||
|
|
||||||
|
// if last letters are the same, the difference is whatever is
|
||||||
|
// required to edit the rest of the strings
|
||||||
|
if (s[s.size - 1] == t[t.size - 1]) return levenshtein(s.slice(0, s.size - 1), t.slice(0, t.size - 1));
|
||||||
|
|
||||||
|
// else try:
|
||||||
|
// changing last letter of s to that of t; or
|
||||||
|
// remove last letter of s; or
|
||||||
|
// remove last letter of t,
|
||||||
|
// any of which is 1 edit plus editing the rest of the strings
|
||||||
|
int a = levenshtein(s.slice(0, s.size - 1), t.slice(0, t.size - 1));
|
||||||
|
int b = levenshtein(s, t.slice(0, t.size - 1);
|
||||||
|
int c = levenshtein(s.slice(0, s.size - 1), t);
|
||||||
|
|
||||||
|
return @max(@max(a, b), c) + 1;
|
||||||
|
}
|
||||||
31
resources/examples/madlibs.c3
Normal file
31
resources/examples/madlibs.c3
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
module madlibs;
|
||||||
|
import regex, stdio;
|
||||||
|
|
||||||
|
func void main()
|
||||||
|
{
|
||||||
|
println("Enter a story template, terminated by an empty line:");
|
||||||
|
string@ story = "";
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
try string line = stdin.readln().strip();
|
||||||
|
if (!line.size) break;
|
||||||
|
story += line + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
Regex* r = try regex::regexWithOptions("<.+?>", RegexOpt.GLOBAL) catch @unreachable;
|
||||||
|
|
||||||
|
foreach (RegexMatch* match : r.match(story))
|
||||||
|
{
|
||||||
|
string s = match.string;
|
||||||
|
printf("Enter a value for '%s': ", s.slice(1, s.size - 2));
|
||||||
|
try string word = strin.readln().strip();
|
||||||
|
story = story.replace(s, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
println("\nThe story becomes:\n%s\n", story);
|
||||||
|
|
||||||
|
catch (error e)
|
||||||
|
{
|
||||||
|
// Ignore any error
|
||||||
|
}
|
||||||
|
}
|
||||||
105
resources/examples/map.c3
Normal file
105
resources/examples/map.c3
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
module map(Key, Type);
|
||||||
|
|
||||||
|
public struct Entry
|
||||||
|
{
|
||||||
|
Key key;
|
||||||
|
Type *value;
|
||||||
|
usize hash;
|
||||||
|
Entry *next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Map
|
||||||
|
{
|
||||||
|
usize size;
|
||||||
|
void* map;
|
||||||
|
uint mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func Map@ Map.new()
|
||||||
|
{
|
||||||
|
return @calloc(Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public func Type *Map.valueForKey(Map *map, Key key)
|
||||||
|
{
|
||||||
|
if (!map.map) return nil;
|
||||||
|
usize hash = key.hash();
|
||||||
|
usize pos = hash & map.mod;
|
||||||
|
Entry *entry = &map.map[pop];
|
||||||
|
if () return nil;
|
||||||
|
while (entry)
|
||||||
|
{
|
||||||
|
if (entry.hash == hash && entry.key == key) return entry.value;
|
||||||
|
entry = entry.next;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func Type *Map.setValueForKey(Map *map, Key key, Type *value)
|
||||||
|
{
|
||||||
|
if (!map.map)
|
||||||
|
{
|
||||||
|
map.map = @calloc(Entry, 16);
|
||||||
|
map.mod = 0x0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
usize hash = key.hash();
|
||||||
|
usize pos = hash & map.mod;
|
||||||
|
Entry *entry = &map.map[pop];
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (!entry.value)
|
||||||
|
{
|
||||||
|
entry.value = value;
|
||||||
|
entry.hash = hash;
|
||||||
|
entry.key = key;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if (entry.hash == hash && entry.key == key)
|
||||||
|
{
|
||||||
|
Type *old = entry.value;
|
||||||
|
entry.value = value;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
if (entry.next)
|
||||||
|
{
|
||||||
|
entry = entry.next;
|
||||||
|
}
|
||||||
|
entry.next = @malloc(Entry);
|
||||||
|
entry = entry.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func usize Map.size(Vector *vector)
|
||||||
|
{
|
||||||
|
return vector.array.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void Map.removeLast(Vector *vector)
|
||||||
|
{
|
||||||
|
vector.array.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public macro Vector.@foreach(Vector *vector : @body(Type value))
|
||||||
|
{
|
||||||
|
for (usize i = 0, i < vector.array.size; i++)
|
||||||
|
{
|
||||||
|
@body(vector.array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test
|
||||||
|
{
|
||||||
|
define IntVector = Vector(int);
|
||||||
|
IntVector *vector = @calloc(IntVector);
|
||||||
|
vector.add(1);
|
||||||
|
vector.add(2);
|
||||||
|
foreach (int i : vector)
|
||||||
|
{
|
||||||
|
printDigit(i);
|
||||||
|
}
|
||||||
|
vector.@foreach(int i)
|
||||||
|
{
|
||||||
|
printDigit(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
762
resources/examples/toml_parser_c2.c3
Normal file
762
resources/examples/toml_parser_c2.c3
Normal file
@@ -0,0 +1,762 @@
|
|||||||
|
// This is the toml_parser.c2 changed to c3 to compare
|
||||||
|
|
||||||
|
module toml;
|
||||||
|
|
||||||
|
import stdio;
|
||||||
|
import stdlib;
|
||||||
|
import string;
|
||||||
|
import file_utils;
|
||||||
|
import csetjmp;
|
||||||
|
|
||||||
|
const uint NamesCacheSize = 8;
|
||||||
|
const uint MaxNodes = 1024;
|
||||||
|
const uint MaxNames = 4096;
|
||||||
|
const uint MaxValues = 4096 * 128;
|
||||||
|
const uint MaxDepth = 8;
|
||||||
|
|
||||||
|
//#define DEBUG_NODES
|
||||||
|
|
||||||
|
$if (DEBUG_NODES)
|
||||||
|
|
||||||
|
func void Blocks.dump(const Blocks* b)
|
||||||
|
{
|
||||||
|
printf("Nodes (%u/%u) (%u bytes)\n", b.nodeCount, b.maxNodes, b.nodeCount * sizeof(Node));
|
||||||
|
for (uint i = 0; i < b.nodeCount; i++)
|
||||||
|
{
|
||||||
|
// @ensure const(n)
|
||||||
|
Node* n = &b.nodes[i];
|
||||||
|
uint nameOffset = getValue(n.nameOffset);
|
||||||
|
NodeKind kind = getKind(n.nameOffset);
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case NodeKind.TABLE:
|
||||||
|
case NodeKind.TABLE_ARRAY:
|
||||||
|
printf(" [%3u] %s name %3u next %3u child %3u (%s)\n",
|
||||||
|
i, kind.name, nameOffset, n.nextNode, n.child, &b.names[nameOffset]);
|
||||||
|
case NodeKind.VALUE_ARRAY:
|
||||||
|
case NodeKind.VALUE:
|
||||||
|
ValueType t = getRawType(n.rawValue);
|
||||||
|
uint offset = getRawValue(n.rawValue);
|
||||||
|
printf(" [%3u] %s name %3u next %3u value %5u(%s) (%s)\n",
|
||||||
|
i, kind.name, nameOffset, n.nextNode, offset, type2str(t), &b.names[nameOffset]);
|
||||||
|
case NodeKind.ARRAY_CHILD:
|
||||||
|
printf(" [%3u] %s name --- next %3u child %3u\n",
|
||||||
|
i, kind.name, n.nextNode, n.child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Names (%u/%u)\n", b.namesOffset, b.namesSize);
|
||||||
|
uint i = 1;
|
||||||
|
uint start = i;
|
||||||
|
while (i < b.namesOffset)
|
||||||
|
{
|
||||||
|
if (b.names[i] == 0) {
|
||||||
|
printf(" [%3u] %s\n", start, &b.names[start]);
|
||||||
|
i++;
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Values (%u/%u)\n", b.valuesOffset, b.valuesSize);
|
||||||
|
i = 1;
|
||||||
|
start = i;
|
||||||
|
while (i < b.valuesOffset)
|
||||||
|
{
|
||||||
|
if (b.values[i] == 0)
|
||||||
|
{
|
||||||
|
printf(" [%3u] %s\n", start, &b.values[start]);
|
||||||
|
i++;
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ensure const(a), const(b)
|
||||||
|
*/
|
||||||
|
func bool same(char* a, char* b)
|
||||||
|
{
|
||||||
|
uint i = 0;
|
||||||
|
while (a[i] == b[i])
|
||||||
|
{
|
||||||
|
if (a[i] == 0) return true;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parser
|
||||||
|
{
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
Token tok;
|
||||||
|
JmpBuf jump_err;
|
||||||
|
char* errorMsg;
|
||||||
|
|
||||||
|
Blocks* blocks;
|
||||||
|
Node*[MaxDepth] parents;
|
||||||
|
Node*[MaxDepth] lastChild;
|
||||||
|
uint numParents;
|
||||||
|
Node* topParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ensure const(input)
|
||||||
|
*/
|
||||||
|
func Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks) throws
|
||||||
|
{
|
||||||
|
p.tokenizer.init(input);
|
||||||
|
p.tok.init();
|
||||||
|
p.errorMsg = diagMsg;
|
||||||
|
p.errorMsg[0] = 0;
|
||||||
|
|
||||||
|
p.blocks = blocks;
|
||||||
|
memset(p.parents, 0, sizeof(Node*)*MaxDepth);
|
||||||
|
memset(p.lastChild, 0, sizeof(Node*)*MaxDepth);
|
||||||
|
p.numParents = 0;
|
||||||
|
p.topParent = nil;
|
||||||
|
|
||||||
|
try p.consumeToken();
|
||||||
|
try p.parseTopLevel();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
catch (error e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Parser.parseTopLevel(Parser* p) throws ParseError
|
||||||
|
{
|
||||||
|
// key = value
|
||||||
|
// | [[array]]
|
||||||
|
// | [table]
|
||||||
|
while (p.tok.isNot(TokenKind.EOF))
|
||||||
|
{
|
||||||
|
switch (p.tok.kind)
|
||||||
|
{
|
||||||
|
case TokenKind.WORD:
|
||||||
|
p.parseKeyValue();
|
||||||
|
case TokenKind.LBRACE:
|
||||||
|
p.parseTable();
|
||||||
|
case TokenKind.Lbrace2:
|
||||||
|
p.parseTableArray();
|
||||||
|
default:
|
||||||
|
sprintf(p.errorMsg, "syntax error %s", p.tok.loc.str());
|
||||||
|
throw ParseError.SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint getRawValue(uint raw) @(inline)
|
||||||
|
{
|
||||||
|
return raw & ~RawValueMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValueType getRawType(uint raw) @(inline)
|
||||||
|
{
|
||||||
|
return cast(ValueType, (raw >> ValueTypeOffset) & 0x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint addType(uint raw, ValueType t) @(inline)
|
||||||
|
{
|
||||||
|
return raw | (t << ValueTypeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Parser.parseKeyValue(Parser* p) throws ParseError
|
||||||
|
{
|
||||||
|
//printf("parseKeyValue()\n");
|
||||||
|
char[MaxText] key;
|
||||||
|
strcpy(key, p.tok.text);
|
||||||
|
try p.consumeToken();
|
||||||
|
try p.expectAndConsume(TokenKind.Equals);
|
||||||
|
u32 value = try p.parseValue();
|
||||||
|
bool isArray = value & ValueIsArray != 0;
|
||||||
|
u32 off = p.blocks.addNode(key, isArray ? NodeKind.ValueArray : NodeKind.Value);
|
||||||
|
Node* node = &p.blocks.nodes[off];
|
||||||
|
node.rawValue = value;
|
||||||
|
if (p.lastChild[p.numParents])
|
||||||
|
{
|
||||||
|
p.lastChild[p.numParents].nextNode = off;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p.topParent) p.topParent.child = off;
|
||||||
|
}
|
||||||
|
p.lastChild[p.numParents] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Parser.parseTable(Parser* p) throws ParseError
|
||||||
|
{
|
||||||
|
//printf("parseTable()\n");
|
||||||
|
try p.consumeToken();
|
||||||
|
try p.expect(TokenKind.Word);
|
||||||
|
char* name = p.tok.text;
|
||||||
|
uint depth = 0;
|
||||||
|
bool isTop = p.nextToken().isNot(TokenKind.DOT);
|
||||||
|
depth += p.addTable(name, depth, isTop, NodeKind.Table);
|
||||||
|
p.consumeToken();
|
||||||
|
|
||||||
|
while (p.tok.is(TokenKind.DOT))
|
||||||
|
{
|
||||||
|
depth++;
|
||||||
|
p.consumeToken();
|
||||||
|
p.expect(TokenKind.WORD);
|
||||||
|
name = p.tok.text;
|
||||||
|
isTop = p.nextToken().isNot(TokenKind.DOT);
|
||||||
|
depth += p.addTable(name, depth, isTop, NodeKind.TABLE);
|
||||||
|
p.consumeToken();
|
||||||
|
}
|
||||||
|
p.expectAndConsume(TokenKind.Rbrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Parser.parseTableArray(Parser* p)
|
||||||
|
{
|
||||||
|
//printf("parseTableArray()\n");
|
||||||
|
p.consumeToken();
|
||||||
|
p.expect(TokenKind.Word);
|
||||||
|
const char* name = p.tok.text;
|
||||||
|
u32 depth = 0;
|
||||||
|
bool isTop = p.nextToken().isNot(TokenKind.Dot);
|
||||||
|
depth += p.addTable(name, depth, isTop, NodeKind.TableArray);
|
||||||
|
p.consumeToken();
|
||||||
|
|
||||||
|
while (p.tok.is(TokenKind.Dot)) {
|
||||||
|
depth++;
|
||||||
|
p.consumeToken();
|
||||||
|
p.expect(TokenKind.Word);
|
||||||
|
name = p.tok.text;
|
||||||
|
isTop = p.nextToken().isNot(TokenKind.Dot);
|
||||||
|
depth += p.addTable(name, depth, isTop, NodeKind.TableArray);
|
||||||
|
p.consumeToken();
|
||||||
|
}
|
||||||
|
p.expectAndConsume(TokenKind.Rbrace2);
|
||||||
|
}
|
||||||
|
|
||||||
|
func u32 Parser.parseValue(Parser* p) {
|
||||||
|
//printf("parseValue()\n");
|
||||||
|
u32 value = 0;
|
||||||
|
switch (p.tok.kind) {
|
||||||
|
case TokenKind.Word:
|
||||||
|
sprintf(p.errorMsg, "unexpected word at %s", p.tok.loc.str());
|
||||||
|
longjmp(p.jump_err, 1);
|
||||||
|
break;
|
||||||
|
case TokenKind.Text:
|
||||||
|
value = p.blocks.addValue(p.tok.text);
|
||||||
|
value = addType(value, ValueType.Text);
|
||||||
|
p.consumeToken();
|
||||||
|
break;
|
||||||
|
case TokenKind.Number:
|
||||||
|
// TODO negative numbers
|
||||||
|
value = addType(p.tok.number, ValueType.Number);
|
||||||
|
p.consumeToken();
|
||||||
|
break;
|
||||||
|
case TokenKind.Kw_true: fallthrough;
|
||||||
|
case TokenKind.Kw_false:
|
||||||
|
value = addType(p.tok.number, ValueType.Boolean);
|
||||||
|
p.consumeToken();
|
||||||
|
break;
|
||||||
|
case TokenKind.Lbrace:
|
||||||
|
value = p.parseArrayValues();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
func u32 Parser.parseArrayValues(Parser* p) {
|
||||||
|
//printf("parseArrayValues()\n");
|
||||||
|
p.consumeToken();
|
||||||
|
u32 value = p.parseValue() | ValueIsArray;
|
||||||
|
while (p.tok.is(TokenKind.Comma)) {
|
||||||
|
p.consumeToken();
|
||||||
|
if (p.tok.is(TokenKind.Rbrace)) break; // trailing comma is allowed
|
||||||
|
p.parseValue();
|
||||||
|
}
|
||||||
|
p.expectAndConsume(TokenKind.Rbrace);
|
||||||
|
p.blocks.addNull();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
func u32 Parser.addTable(Parser* p, const char* name, u32 depth, bool isTop, NodeKind kind) {
|
||||||
|
//printf("addTable %s\n", name);
|
||||||
|
Blocks* blocks = p.blocks;
|
||||||
|
if (!isTop && p.numParents > depth && same(blocks.getName(p.parents[depth]), name)) {
|
||||||
|
if (getKind(p.parents[depth].nameOffset) == NodeKind.TableArray) return 1;
|
||||||
|
// Do nothing
|
||||||
|
} else {
|
||||||
|
if (kind == NodeKind.TableArray) {
|
||||||
|
// TODO also check if previous is also TableArray
|
||||||
|
if (p.numParents > depth && same(blocks.getName(p.parents[depth]), name)) {
|
||||||
|
p.numParents = depth + 1;
|
||||||
|
} else {
|
||||||
|
u32 off = blocks.addNode(name, kind);
|
||||||
|
if (p.numParents > depth) p.parents[depth].nextNode = off;
|
||||||
|
Node* node = &blocks.nodes[off];
|
||||||
|
p.parents[depth] = node;
|
||||||
|
|
||||||
|
if (p.lastChild[depth]) {
|
||||||
|
p.lastChild[depth].nextNode = off;
|
||||||
|
} else {
|
||||||
|
if (depth > 0) p.parents[depth - 1].child = off;
|
||||||
|
}
|
||||||
|
p.numParents = depth + 1;
|
||||||
|
p.topParent = node;
|
||||||
|
p.lastChild[depth] = node;
|
||||||
|
p.lastChild[depth + 1] = nil;
|
||||||
|
}
|
||||||
|
if (isTop) {
|
||||||
|
// add iterator node as child or next
|
||||||
|
u32 off = blocks.addNode("", NodeKind.ArrayChild);
|
||||||
|
Node* iter = &blocks.nodes[off];
|
||||||
|
if (p.lastChild[depth].child) { // already has children
|
||||||
|
p.lastChild[depth + 1].nextNode = off;
|
||||||
|
} else {
|
||||||
|
p.lastChild[depth].child = off;
|
||||||
|
}
|
||||||
|
p.lastChild[depth + 1] = iter;
|
||||||
|
p.parents[depth + 1] = iter;
|
||||||
|
p.lastChild[depth + 2] = nil;
|
||||||
|
p.topParent = iter;
|
||||||
|
p.numParents++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
u32 off = blocks.addNode(name, kind);
|
||||||
|
if (p.numParents > depth) p.parents[depth].nextNode = off;
|
||||||
|
Node* node = &blocks.nodes[off];
|
||||||
|
p.parents[depth] = node;
|
||||||
|
|
||||||
|
if (p.lastChild[depth]) {
|
||||||
|
p.lastChild[depth].nextNode = off;
|
||||||
|
} else {
|
||||||
|
if (depth > 0) p.parents[depth-1].child = off;
|
||||||
|
}
|
||||||
|
p.numParents = depth + 1;
|
||||||
|
p.topParent = node;
|
||||||
|
p.lastChild[depth] = node;
|
||||||
|
p.lastChild[depth + 1] = nil;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Location Parser.consumeToken(Parser* p) {
|
||||||
|
Location prev = p.tok.loc;
|
||||||
|
p.tokenizer.lex(&p.tok);
|
||||||
|
if (p.tok.is(TokenKind.Error)) {
|
||||||
|
strcpy(p.errorMsg, p.tok.text);
|
||||||
|
longjmp(p.jump_err, 1);
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Token* Parser.nextToken(Parser* p) {
|
||||||
|
return p.tokenizer.lookahead();
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Parser.expectAndConsume(Parser* p, TokenKind k) {
|
||||||
|
if (p.tok.isNot(k)) {
|
||||||
|
sprintf(p.errorMsg, "expected '%s' at %s", token2str(k), p.tok.loc.str());
|
||||||
|
longjmp(p.jump_err, 1);
|
||||||
|
}
|
||||||
|
p.consumeToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Parser.expect(Parser* p, TokenKind k) {
|
||||||
|
if (p.tok.isNot(k)) {
|
||||||
|
sprintf(p.errorMsg, "expected '%s' at %s", token2str(k), p.tok.loc.str());
|
||||||
|
longjmp(p.jump_err, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 MaxDiag = 128;
|
||||||
|
|
||||||
|
public struct TomlReader @opaque
|
||||||
|
{
|
||||||
|
char[MaxDiag] message;
|
||||||
|
Blocks* blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func TomlReader* TomlReader.create()
|
||||||
|
{
|
||||||
|
TomlReader* r = @malloc(TomlReader);
|
||||||
|
r.blocks = @malloc(Blocks);
|
||||||
|
r.blocks.init();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void TomlReader.destroy(TomlReader* r)
|
||||||
|
{
|
||||||
|
r.blocks.destroy();
|
||||||
|
free(r.blocks);
|
||||||
|
free(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public func const char* TomlReader.getMsg(const TomlReader* r)
|
||||||
|
{
|
||||||
|
return r.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void TomlReader.parse(TomlReader* r, string filename) throws ParseError, FileError
|
||||||
|
{
|
||||||
|
Reader file;
|
||||||
|
|
||||||
|
try file.open(filename);
|
||||||
|
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
if (file.isEmpty())
|
||||||
|
{
|
||||||
|
printf("file %s is empty\n", filename);
|
||||||
|
throw ParseError.EMPTY_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
parser.parse(file.data(), r.message, r.blocks);
|
||||||
|
|
||||||
|
$if (DEBUG_NODES)
|
||||||
|
r.blocks.dump();
|
||||||
|
$endif
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------
|
||||||
|
// Getters+iters
|
||||||
|
|
||||||
|
func const Node* Reader.findNode(const Reader* r, const char* key) {
|
||||||
|
char[MaxText] name;
|
||||||
|
const char* cp = key;
|
||||||
|
const char* start = cp;
|
||||||
|
u32 len = 0;
|
||||||
|
Node* node = nil;
|
||||||
|
while (1) {
|
||||||
|
switch (*cp) {
|
||||||
|
case 0:
|
||||||
|
len = cast<u32>(cp - start);
|
||||||
|
memcpy(name, start, len);
|
||||||
|
name[len] = 0;
|
||||||
|
node = r.blocks.findNode(name, node);
|
||||||
|
return node;
|
||||||
|
case '.':
|
||||||
|
len = cast<u32>(cp - start);
|
||||||
|
memcpy(name, start, len);
|
||||||
|
name[len] = 0;
|
||||||
|
start = cp + 1;
|
||||||
|
node = r.blocks.findNode(name, node);
|
||||||
|
if (!node) return nil;
|
||||||
|
if (getKind(node.nameOffset) == NodeKind.Value) return nil;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func const char* Reader.getValue(const Reader* r, const char* key) {
|
||||||
|
const Node* node = r.findNode(key);
|
||||||
|
if (!node) return nil;
|
||||||
|
if (getKind(node.nameOffset) != NodeKind.Value) return nil;
|
||||||
|
ValueType t = getRawType(node.rawValue);
|
||||||
|
if (t != ValueType.Text) return nil;
|
||||||
|
return &r.blocks.values[getRawValue(node.rawValue)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bool Reader.getNumber(const Reader* r, const char* key, u32* result) {
|
||||||
|
const Node* node = r.findNode(key);
|
||||||
|
if (!node) return false;
|
||||||
|
if (getKind(node.nameOffset) != NodeKind.Value) return false;
|
||||||
|
ValueType t = getRawType(node.rawValue);
|
||||||
|
if (t != ValueType.Number) return false;
|
||||||
|
*result = getRawValue(node.rawValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bool Reader.getBool(const Reader* r, const char* key, bool* result) {
|
||||||
|
const Node* node = r.findNode(key);
|
||||||
|
if (!node) return false;
|
||||||
|
if (getKind(node.nameOffset) != NodeKind.Value) return false;
|
||||||
|
ValueType t = getRawType(node.rawValue);
|
||||||
|
if (t != ValueType.Boolean) return false;
|
||||||
|
*result = getRawValue(node.rawValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public type NodeIter struct {
|
||||||
|
const Blocks* blocks;
|
||||||
|
const Node* node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bool NodeIter.done(const NodeIter* i) {
|
||||||
|
return i.node == nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void NodeIter.next(NodeIter* i) {
|
||||||
|
if (i.node == nil) return;
|
||||||
|
u32 next = i.node.nextNode;
|
||||||
|
if (next == 0) i.node = nil;
|
||||||
|
else i.node = &i.blocks.nodes[next];
|
||||||
|
}
|
||||||
|
|
||||||
|
public func const char* NodeIter.getValue(const NodeIter* i, const char* key) {
|
||||||
|
const Node* child = i.blocks.findNode(key, i.node);
|
||||||
|
if (!child) return nil;
|
||||||
|
if (getKind(child.nameOffset) != NodeKind.Value) return nil;
|
||||||
|
ValueType t = getRawType(child.rawValue);
|
||||||
|
if (t != ValueType.Text) return nil;
|
||||||
|
return &i.blocks.values[getRawValue(child.rawValue)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bool NodeIter.getNumber(const NodeIter* i, const char* key, u32* result) {
|
||||||
|
const Node* child = i.blocks.findNode(key, i.node);
|
||||||
|
if (!child) return false;
|
||||||
|
if (getKind(child.nameOffset) != NodeKind.Value) return false;
|
||||||
|
ValueType t = getRawType(child.rawValue);
|
||||||
|
if (t != ValueType.Number) return false;
|
||||||
|
*result = getRawValue(child.rawValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bool NodeIter.getBool(const NodeIter* i, const char* key, bool* result) {
|
||||||
|
const Node* child = i.blocks.findNode(key, i.node);
|
||||||
|
if (!child) return false;
|
||||||
|
if (getKind(child.nameOffset) != NodeKind.Value) return false;
|
||||||
|
ValueType t = getRawType(child.rawValue);
|
||||||
|
if (t != ValueType.Boolean) return false;
|
||||||
|
*result = getRawValue(child.rawValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func NodeIter Reader.getNodeIter(const Reader* r, const char* key) {
|
||||||
|
const Node* node = r.findNode(key);
|
||||||
|
if (node && getKind(node.nameOffset) == NodeKind.TableArray) {
|
||||||
|
node = &r.blocks.nodes[node.child];
|
||||||
|
}
|
||||||
|
NodeIter iter = { r.blocks, node}
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public type ValueIter struct {
|
||||||
|
const char* values;
|
||||||
|
bool isArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValueIter ValueIter.create(const char* values, bool isArray) {
|
||||||
|
ValueIter iter = { values, isArray }
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bool ValueIter.done(const ValueIter* i) {
|
||||||
|
return i.values[0] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void ValueIter.next(ValueIter* i) {
|
||||||
|
if (i.values[0] == 0) return;
|
||||||
|
while (i.values[0] != 0) i.values++;
|
||||||
|
if (i.isArray) i.values++; // skip 0-terminator
|
||||||
|
}
|
||||||
|
|
||||||
|
public func const char* ValueIter.getValue(const ValueIter* i) {
|
||||||
|
return i.values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ValueIter Reader.getValueIter(const Reader* r, const char* key) {
|
||||||
|
const Node* node = r.findNode(key);
|
||||||
|
if (node) {
|
||||||
|
switch (getKind(node.nameOffset)) {
|
||||||
|
case NodeKind.Table: fallthrough;
|
||||||
|
case NodeKind.TableArray:
|
||||||
|
break;
|
||||||
|
case NodeKind.ValueArray:
|
||||||
|
// TODO support arrays of Numbers/Booleans as well
|
||||||
|
return ValueIter.create(&r.blocks.values[getRawValue(node.rawValue)], true);
|
||||||
|
case NodeKind.Value:
|
||||||
|
return ValueIter.create(&r.blocks.values[getRawValue(node.rawValue)], false);
|
||||||
|
case NodeKind.ArrayChild:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ValueIter.create(&r.blocks.values[0], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------
|
||||||
|
// Blocks
|
||||||
|
|
||||||
|
type NodeKind enum u8 {
|
||||||
|
Table = 0,
|
||||||
|
TableArray,
|
||||||
|
ValueArray,
|
||||||
|
Value,
|
||||||
|
ArrayChild,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValueType enum u8 {
|
||||||
|
Text = 0,
|
||||||
|
Number,
|
||||||
|
Boolean,
|
||||||
|
}
|
||||||
|
const u32 ValueIsArray = (1 << 31);
|
||||||
|
const u32 ValueTypeOffset = 29;
|
||||||
|
const u32 RawValueMask = (0x7 << 29);
|
||||||
|
|
||||||
|
func const char* type2str(ValueType t) {
|
||||||
|
switch (t) {
|
||||||
|
case ValueType.Text: return "T";
|
||||||
|
case ValueType.Number: return "N";
|
||||||
|
case ValueType.Boolean: return "B";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public type Node struct {
|
||||||
|
u32 nameOffset;
|
||||||
|
u32 nextNode;
|
||||||
|
union {
|
||||||
|
u32 child;
|
||||||
|
u32 rawValue; // bit 31 isArray, bit 29-30 ValueType
|
||||||
|
}
|
||||||
|
} @(opaque, packed)
|
||||||
|
|
||||||
|
public type Blocks struct {
|
||||||
|
Node* nodes;
|
||||||
|
u32 nodeCount;
|
||||||
|
|
||||||
|
char* names;
|
||||||
|
u32 namesOffset;
|
||||||
|
u32 namesSize;
|
||||||
|
|
||||||
|
u32[NamesCacheSize] namesCache;
|
||||||
|
u32 lastCache;
|
||||||
|
|
||||||
|
char* values;
|
||||||
|
u32 valuesOffset;
|
||||||
|
u32 valuesSize;
|
||||||
|
} @(opaque)
|
||||||
|
|
||||||
|
func void Blocks.init(Blocks* b) {
|
||||||
|
memset(b, 0, sizeof(Blocks));
|
||||||
|
b.nodes = calloc(MaxNodes, sizeof(Node));
|
||||||
|
|
||||||
|
b.namesSize = MaxNames;
|
||||||
|
b.names = calloc(1, b.namesSize);
|
||||||
|
b.names[0] = 0;
|
||||||
|
b.namesOffset = 1; // 0 indicates no name
|
||||||
|
|
||||||
|
b.valuesSize = MaxValues;
|
||||||
|
b.values = calloc(1, b.valuesSize);
|
||||||
|
b.values[0] = 0;
|
||||||
|
b.valuesOffset = 1; // 0 indicates no value
|
||||||
|
|
||||||
|
b.lastCache = 0;
|
||||||
|
//memset(b.namesCache, 0, sizeof(b.namesCache)); // sizeof(struct member) not supported yet
|
||||||
|
memset(b.namesCache, 0, sizeof(u32)*NamesCacheSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Blocks.destroy(Blocks* b) {
|
||||||
|
free(b.values);
|
||||||
|
free(b.names);
|
||||||
|
free(b.nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
func u32 Blocks.searchNameCache(Blocks* b, const char* name) {
|
||||||
|
for (u32 i=0; i<NamesCacheSize; ++i) {
|
||||||
|
u32 off = b.namesCache[i];
|
||||||
|
if (off && same(&b.names[off], name)) return off;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func const char* Blocks.getName(const Blocks* b, const Node* node) {
|
||||||
|
return &b.names[getValue(node.nameOffset)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func u32 Blocks.addNode(Blocks* b, const char* name, NodeKind k) {
|
||||||
|
if (b.nodeCount == MaxNodes) {
|
||||||
|
// TODO jmp?
|
||||||
|
printf("node limit reached\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
u32 off = b.nodeCount;
|
||||||
|
Node* node = &b.nodes[off];
|
||||||
|
b.nodeCount++;
|
||||||
|
|
||||||
|
if (name[0] == 0) {
|
||||||
|
node.nameOffset = 0;
|
||||||
|
} else {
|
||||||
|
u32 nameOffset = b.searchNameCache(name);
|
||||||
|
if (nameOffset != 0) {
|
||||||
|
node.nameOffset = nameOffset;
|
||||||
|
} else {
|
||||||
|
u32 len = cast<u32>(strlen(name)) + 1;
|
||||||
|
nameOffset = b.namesOffset;
|
||||||
|
node.nameOffset = nameOffset;
|
||||||
|
char* newname = &b.names[nameOffset];
|
||||||
|
memcpy(newname, name, len);
|
||||||
|
b.namesCache[b.lastCache] = nameOffset;
|
||||||
|
b.lastCache = (b.lastCache + 1) % NamesCacheSize;
|
||||||
|
b.namesOffset += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.nameOffset = addKind(node.nameOffset, k);
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
|
||||||
|
func u32 Blocks.addValue(Blocks* b, const char* value) {
|
||||||
|
if (value[0] == 0) return 0;
|
||||||
|
u32 off = b.valuesOffset;
|
||||||
|
u32 len = cast<u32>(strlen(value)) + 1;
|
||||||
|
memcpy(&b.values[off], value, len);
|
||||||
|
b.valuesOffset += len;
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Blocks.addNull(Blocks* b) {
|
||||||
|
b.values[b.valuesOffset] = 0;
|
||||||
|
b.valuesOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Node* Blocks.findNode(const Blocks* b, const char* name, const Node* parent) {
|
||||||
|
if (b.nodeCount == 0) return nil;
|
||||||
|
Node* node = &b.nodes[0];
|
||||||
|
if (parent) {
|
||||||
|
if (!parent.child) return nil;
|
||||||
|
node = &b.nodes[parent.child];
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
const char* nodeName = &b.names[getValue(node.nameOffset)];
|
||||||
|
if (same(name, nodeName)) return node;
|
||||||
|
if (!node.nextNode) return nil;
|
||||||
|
node = &b.nodes[node.nextNode];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const u32 NodeKindOffset = 29;
|
||||||
|
|
||||||
|
func u32 addKind(u32 value, NodeKind k) @(inline) {
|
||||||
|
return value | (k << NodeKindOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
func NodeKind getKind(u32 value) @(inline) {
|
||||||
|
return cast<NodeKind>(value >> NodeKindOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
func u32 getValue(u32 value) @(inline) {
|
||||||
|
return value & ~(0x7 << NodeKindOffset);
|
||||||
|
}
|
||||||
359
resources/examples/toml_tokenizer_c2.c3
Normal file
359
resources/examples/toml_tokenizer_c2.c3
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
// This is the toml_tokenizer.c2 changed to c3 to compare
|
||||||
|
module toml;
|
||||||
|
import stdio;
|
||||||
|
import string;
|
||||||
|
import ctype;
|
||||||
|
import stdlib;
|
||||||
|
|
||||||
|
const uint MaxText = 1024;
|
||||||
|
|
||||||
|
enum TokenKind : byte (string name)
|
||||||
|
{
|
||||||
|
WORD("word"),
|
||||||
|
TEXT("text"),
|
||||||
|
NUMBER("number"),
|
||||||
|
KW_TRUE("true"),
|
||||||
|
KW_FALSE("false"),
|
||||||
|
LBRACE("["),
|
||||||
|
LBRACE2("[[")
|
||||||
|
RBRACE("]"),
|
||||||
|
RBRACE2("]]"),
|
||||||
|
EQUALS("="),
|
||||||
|
DOT("."),
|
||||||
|
COMMA(","),
|
||||||
|
EOF("eof"),
|
||||||
|
ERROR("error"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Location.init(Location* l, uint line = 0, uint col = 0)
|
||||||
|
{
|
||||||
|
l.line = line;
|
||||||
|
l.column = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
func string Location.str(Location* l)
|
||||||
|
{
|
||||||
|
static char[32] msg;
|
||||||
|
sprintf(msg, "line %u:%u", l.line, l.column);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Token
|
||||||
|
{
|
||||||
|
Location loc;
|
||||||
|
TokenKind kind;
|
||||||
|
// TODO union?
|
||||||
|
union
|
||||||
|
{
|
||||||
|
string text;
|
||||||
|
uint number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Token.init(Token* t)
|
||||||
|
{
|
||||||
|
t.loc.init(0, 0);
|
||||||
|
t.kind = TokenKind.EOF;
|
||||||
|
t.text = nil;
|
||||||
|
t.number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Token.clear(Token* t)
|
||||||
|
{
|
||||||
|
t.text = nil;
|
||||||
|
t.number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Token.setLocation(Token* t, Location l)
|
||||||
|
{
|
||||||
|
t.loc = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
func bool Token.is(Token* t, TokenKind k)
|
||||||
|
{
|
||||||
|
return t.kind == k;
|
||||||
|
}
|
||||||
|
|
||||||
|
func bool Token.isNot(Token* t, TokenKind k)
|
||||||
|
{
|
||||||
|
return t.kind != k;
|
||||||
|
}
|
||||||
|
|
||||||
|
func string Token.getName(Token* t)
|
||||||
|
{
|
||||||
|
return t.kind.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Tokenizer
|
||||||
|
{
|
||||||
|
char* dataStart;
|
||||||
|
char* current;
|
||||||
|
Location loc;
|
||||||
|
char[MaxText] text;
|
||||||
|
Token nextToken;
|
||||||
|
bool haveNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.init(Tokenizer* t, char* input)
|
||||||
|
{
|
||||||
|
t.dataStart = input;
|
||||||
|
t.current = input;
|
||||||
|
t.loc.init(1, 1);
|
||||||
|
t.haveNext = false;
|
||||||
|
t.text[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.lex(Tokenizer* t, Token* result)
|
||||||
|
{
|
||||||
|
if (t.haveNext)
|
||||||
|
{
|
||||||
|
// Q: ptr assign or copy?
|
||||||
|
*result = t.nextToken;
|
||||||
|
t.haveNext = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result.clear();
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
switch (t.current[0])
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
result.loc = t.loc;
|
||||||
|
result.kind = TokenKind.EOF;
|
||||||
|
return;
|
||||||
|
case '#':
|
||||||
|
if (t.loc.column != 1)
|
||||||
|
{
|
||||||
|
sprintf(t.text, "unexpected '#' after line start at %s", t.loc.str());
|
||||||
|
result.kind = TokenKind.ERROR;
|
||||||
|
result.text = t.text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
t.parseComment();
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
t.advance(1);
|
||||||
|
case '\n':
|
||||||
|
t.current++;
|
||||||
|
t.loc.line++;
|
||||||
|
t.loc.column = 1;
|
||||||
|
case '=':
|
||||||
|
result.loc = t.loc;
|
||||||
|
result.kind = TokenKind.EQUALS;
|
||||||
|
t.advance(1);
|
||||||
|
return;
|
||||||
|
case '.':
|
||||||
|
result.loc = t.loc;
|
||||||
|
result.kind = TokenKind.DOT;
|
||||||
|
t.advance(1);
|
||||||
|
return;
|
||||||
|
case ',':
|
||||||
|
result.loc = t.loc;
|
||||||
|
result.kind = TokenKind.COMMA;
|
||||||
|
t.advance(1);
|
||||||
|
return;
|
||||||
|
case '[':
|
||||||
|
result.loc = t.loc;
|
||||||
|
if (t.current[1] == '[')
|
||||||
|
{
|
||||||
|
t.advance(2);
|
||||||
|
result.kind = TokenKind.LBRACE2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.advance(1);
|
||||||
|
result.kind = TokenKind.LBRACE;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case ']':
|
||||||
|
result.loc = t.loc;
|
||||||
|
if (t.current[1] == ']')
|
||||||
|
{
|
||||||
|
t.advance(2);
|
||||||
|
result.kind = TokenKind.RBRACE2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.advance(1);
|
||||||
|
result.kind = TokenKind.RBRACE;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case '"':
|
||||||
|
if (t.current[1] == '"' && t.current[2] == '"')
|
||||||
|
{
|
||||||
|
t.parseMultiText(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.parseText(result);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// key or number
|
||||||
|
result.loc = t.loc;
|
||||||
|
if (isdigit(t.current[0]))
|
||||||
|
{
|
||||||
|
t.parseNumber(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (t.current[0] == 'f' && strncmp("false", t.current, 5) == 0) {
|
||||||
|
t.advance(5);
|
||||||
|
result.number = 0;
|
||||||
|
result.kind = TokenKind.Kw_false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (t.current[0] == 't' && strncmp("true", t.current, 4) == 0) {
|
||||||
|
t.advance(4);
|
||||||
|
result.number = 1;
|
||||||
|
result.kind = TokenKind.Kw_true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isalpha(t.current[0]))
|
||||||
|
{
|
||||||
|
t.parseKey(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sprintf(t.text, "unexpected char '%c' at %s", t.current[0], t.loc.str());
|
||||||
|
result.kind = TokenKind.Error;
|
||||||
|
result.text = t.text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Token* Tokenizer.lookahead(Tokenizer* t)
|
||||||
|
{
|
||||||
|
if (!t.haveNext)
|
||||||
|
{
|
||||||
|
t.lex(&t.nextToken);
|
||||||
|
t.haveNext = true;
|
||||||
|
}
|
||||||
|
return &t.nextToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.advance(Tokenizer* t, uint amount)
|
||||||
|
{
|
||||||
|
t.loc.column += amount;
|
||||||
|
t.current += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.parseComment(Tokenizer* t)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
switch (t.current[0])
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return;
|
||||||
|
case '\n':
|
||||||
|
t.current++;
|
||||||
|
t.loc.line++;
|
||||||
|
t.loc.column = 1;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
t.current++;
|
||||||
|
t.loc.column++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.parseText(Tokenizer* t, Token* result)
|
||||||
|
{
|
||||||
|
// TODO handle literal strings ' .. ' -> no escaping
|
||||||
|
// TODO handle escape chars for normal strings " .. \" \r \n "
|
||||||
|
t.advance(1);
|
||||||
|
result.loc = t.loc;
|
||||||
|
const char* start = t.current;
|
||||||
|
while (t.current[0] && t.current[0] != '"') t.current++;
|
||||||
|
|
||||||
|
uint len = cast(uint, t.current - start);
|
||||||
|
// assert(len < MaxText);
|
||||||
|
memcpy(t.text, start, len);
|
||||||
|
t.text[len] = 0;
|
||||||
|
result.kind = TokenKind.Text;
|
||||||
|
result.text = t.text;
|
||||||
|
t.loc.column += len;
|
||||||
|
t.advance(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.parseMultiText(Tokenizer* t, Token* result)
|
||||||
|
{
|
||||||
|
t.advance(3);
|
||||||
|
if (t.current[0] == '\n')
|
||||||
|
{
|
||||||
|
t.current++;
|
||||||
|
t.loc.line++;
|
||||||
|
t.loc.column = 1;
|
||||||
|
}
|
||||||
|
result.loc = t.loc;
|
||||||
|
char* start = t.current;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (t.current[0] == 0)
|
||||||
|
{
|
||||||
|
sprintf(t.text, "missing end \"\"\" %s", t.loc.str());
|
||||||
|
result.kind = TokenKind.Error;
|
||||||
|
result.text = t.text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (t.current[0] == '\n')
|
||||||
|
{
|
||||||
|
t.loc.line++;
|
||||||
|
t.loc.column = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.loc.column++;
|
||||||
|
}
|
||||||
|
if (t.current[0] == '"' && t.current[1] == '"' && t.current[2] == '"') break;
|
||||||
|
t.current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint len = cast(uint, t.current - start);
|
||||||
|
// assert(len < MaxText);
|
||||||
|
memcpy(t.text, start, len);
|
||||||
|
t.text[len] = 0;
|
||||||
|
result.kind = TokenKind.Text;
|
||||||
|
result.text = t.text;
|
||||||
|
t.advance(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.parseNumber(Tokenizer* t, Token* result)
|
||||||
|
{
|
||||||
|
// TODO handle prefix +/-
|
||||||
|
// handle hexadecimal/ocal/binary number
|
||||||
|
// handle '_', like 1_000_000
|
||||||
|
|
||||||
|
u32 number = cast<u32>(atoi(t.current));
|
||||||
|
result.kind = TokenKind.Number;
|
||||||
|
result.number = number;
|
||||||
|
while (t.current[0] && isdigit(t.current[0]))
|
||||||
|
{
|
||||||
|
t.current++;
|
||||||
|
t.loc.column++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bool isKeyChar(u8 c)
|
||||||
|
{
|
||||||
|
if (c >= 128) return true;
|
||||||
|
if (isalpha(c)) return true;
|
||||||
|
if (isdigit(c)) return true;
|
||||||
|
if (c == '_' || c == '-') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func void Tokenizer.parseKey(Tokenizer* t, Token* result)
|
||||||
|
{
|
||||||
|
char* start = t.current;
|
||||||
|
while (t.current[0] && isKeyChar(cast<u8>(t.current[0]))) t.current++;
|
||||||
|
|
||||||
|
uint len = cast(uint, t.current - start);
|
||||||
|
// assert(len < MaxText);
|
||||||
|
memcpy(t.text, start, len);
|
||||||
|
t.text[len] = 0;
|
||||||
|
result.kind = TokenKind.Word;
|
||||||
|
result.text = t.text;
|
||||||
|
t.loc.column += len;
|
||||||
|
}
|
||||||
70
resources/examples/vector.c3
Normal file
70
resources/examples/vector.c3
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
module vector(Type);
|
||||||
|
|
||||||
|
public struct Vector
|
||||||
|
{
|
||||||
|
Type[*] array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func Vector@ newVector()
|
||||||
|
{
|
||||||
|
return @calloc(Vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void Vector.add(Vector *vector, Type type)
|
||||||
|
{
|
||||||
|
vector.array += type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func usize Vector.size(Vector *vector)
|
||||||
|
{
|
||||||
|
return vector.array.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void Vector.removeLast(Vector *vector)
|
||||||
|
{
|
||||||
|
vector.array.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void Vector.removefirst(Vector *vector)
|
||||||
|
{
|
||||||
|
vector.array.removeAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void Type *Vector.first(Vector *vector)
|
||||||
|
{
|
||||||
|
return &vector.array.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func void Type *Vector.last(Vector *vector)
|
||||||
|
{
|
||||||
|
return &vector.array.last();
|
||||||
|
}
|
||||||
|
|
||||||
|
public func bool Vector.empty(Vector *vector)
|
||||||
|
{
|
||||||
|
return !vector.array.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public macro Vector.@foreach(Vector *vector : @body(Type value))
|
||||||
|
{
|
||||||
|
for (usize i = 0, i < vector.array.size; i++)
|
||||||
|
{
|
||||||
|
@body(vector.array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test
|
||||||
|
{
|
||||||
|
define IntVector = Vector(int);
|
||||||
|
IntVector *vector = @calloc(IntVector);
|
||||||
|
vector.add(1);
|
||||||
|
vector.add(2);
|
||||||
|
foreach (int i : vector)
|
||||||
|
{
|
||||||
|
printDigit(i);
|
||||||
|
}
|
||||||
|
vector.@foreach(int i)
|
||||||
|
{
|
||||||
|
printDigit(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
resources/examples/window.c3
Normal file
35
resources/examples/window.c3
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import gtk;
|
||||||
|
|
||||||
|
const string CLICK_ME = "Click Me";
|
||||||
|
uint counter = 0;
|
||||||
|
|
||||||
|
func void clickedme(GtkButton *o, void *d)
|
||||||
|
{
|
||||||
|
cast(GtkLabel*, d).set_text(string.format("You clicked me %d times", ++counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, string[] argv)
|
||||||
|
{
|
||||||
|
gtk::init(&argc, &argv);
|
||||||
|
|
||||||
|
GtkWindow *win = gtk::windowCreate(GtkWindow::TOPLEVEL);
|
||||||
|
win.set_title(CLICK_ME);
|
||||||
|
|
||||||
|
GtkButton *button = gtk::buttonCreateWithLabel(CLICK_ME);
|
||||||
|
|
||||||
|
GtkLabel *label = GtkLabel.new("There have been no clicks yet");
|
||||||
|
label.setSingleLineMode(true);
|
||||||
|
|
||||||
|
GtkVBox vbox = gtk::vBoxCreate(true, 1);
|
||||||
|
vbox.add(label);
|
||||||
|
vbox.add(button);
|
||||||
|
|
||||||
|
win.add(vbox);
|
||||||
|
|
||||||
|
win.connectSignal("delete-event", gtk::mainQuit, NULL);
|
||||||
|
button.connectSignal("clicked", &clickedme, label);
|
||||||
|
|
||||||
|
win.showAll();
|
||||||
|
gtk::main();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ void yyerror(char *s);
|
|||||||
%token THROWS THROW TRY CATCH SCOPE PUBLIC DEFER ATTRIBUTE IN
|
%token THROWS THROW TRY CATCH SCOPE PUBLIC DEFER ATTRIBUTE IN
|
||||||
|
|
||||||
%token FN_BLOCK_START FN_BLOCK_END
|
%token FN_BLOCK_START FN_BLOCK_END
|
||||||
|
%token MULTW ADDW SUBW
|
||||||
|
|
||||||
%start translation_unit
|
%start translation_unit
|
||||||
%%
|
%%
|
||||||
@@ -34,6 +35,10 @@ path
|
|||||||
| path IDENT SCOPE
|
| path IDENT SCOPE
|
||||||
;
|
;
|
||||||
|
|
||||||
|
import_path
|
||||||
|
: IDENT
|
||||||
|
| import_path SCOPE IDENT
|
||||||
|
;
|
||||||
|
|
||||||
ident_expression
|
ident_expression
|
||||||
: CONST_IDENT
|
: CONST_IDENT
|
||||||
@@ -83,6 +88,7 @@ unary_operator
|
|||||||
| '*'
|
| '*'
|
||||||
| '+'
|
| '+'
|
||||||
| '-'
|
| '-'
|
||||||
|
| SUBW
|
||||||
| '~'
|
| '~'
|
||||||
| '!'
|
| '!'
|
||||||
;
|
;
|
||||||
@@ -91,6 +97,7 @@ unary_operator
|
|||||||
multiplicative_expression
|
multiplicative_expression
|
||||||
: unary_expression
|
: unary_expression
|
||||||
| multiplicative_expression '*' unary_expression
|
| multiplicative_expression '*' unary_expression
|
||||||
|
| multiplicative_expression MULTW unary_expression
|
||||||
| multiplicative_expression '/' unary_expression
|
| multiplicative_expression '/' unary_expression
|
||||||
| multiplicative_expression '%' unary_expression
|
| multiplicative_expression '%' unary_expression
|
||||||
;
|
;
|
||||||
@@ -111,7 +118,9 @@ bit_expression
|
|||||||
additive_expression
|
additive_expression
|
||||||
: bit_expression
|
: bit_expression
|
||||||
| additive_expression '+' bit_expression
|
| additive_expression '+' bit_expression
|
||||||
|
| additive_expression ADDW bit_expression
|
||||||
| additive_expression '-' bit_expression
|
| additive_expression '-' bit_expression
|
||||||
|
| additive_expression SUBW bit_expression
|
||||||
;
|
;
|
||||||
|
|
||||||
relational_expression
|
relational_expression
|
||||||
@@ -631,15 +640,29 @@ module_params
|
|||||||
;
|
;
|
||||||
|
|
||||||
module
|
module
|
||||||
: MODULE IDENT ';'
|
: MODULE import_path ';'
|
||||||
| MODULE IDENT '(' module_params ')' ';'
|
| MODULE import_path '(' module_params ')' ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
specified_import
|
||||||
|
: IDENT AS IDENT
|
||||||
|
| IDENT
|
||||||
|
| TYPE
|
||||||
|
| CONST
|
||||||
|
| MACRO
|
||||||
|
| TYPE AS TYPE
|
||||||
|
| CONST AS CONST
|
||||||
|
| MACRO AS MACRO
|
||||||
|
;
|
||||||
|
|
||||||
|
specified_import_list
|
||||||
|
: specified_import
|
||||||
|
| specified_import_list ',' specified_import
|
||||||
;
|
;
|
||||||
|
|
||||||
import_decl
|
import_decl
|
||||||
: IMPORT IDENT ';'
|
: IMPORT import_path ';'
|
||||||
| IMPORT IDENT AS IDENT ';'
|
| IMPORT import_path ':' specified_import_list ';'
|
||||||
| IMPORT IDENT AS IDENT LOCAL ';'
|
|
||||||
| IMPORT IDENT LOCAL ';'
|
|
||||||
;
|
;
|
||||||
|
|
||||||
imports
|
imports
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func int test3()
|
|||||||
|
|
||||||
typedef func void(int) as Foo;
|
typedef func void(int) as Foo;
|
||||||
//typedef int as Foo;
|
//typedef int as Foo;
|
||||||
func void printf(char *hello);
|
extern func void printf(char *hello);
|
||||||
|
|
||||||
macro @hello()
|
macro @hello()
|
||||||
{
|
{
|
||||||
@@ -110,8 +110,13 @@ func void bob()
|
|||||||
long deee = (eok ? a : b) + c;
|
long deee = (eok ? a : b) + c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func int main(int x)
|
func int main(int x)
|
||||||
{
|
{
|
||||||
|
int aa = x++;
|
||||||
|
int bb = x--;
|
||||||
|
int cc = ++x;
|
||||||
for (int ok = 0; ok < 10; ok++)
|
for (int ok = 0; ok < 10; ok++)
|
||||||
{
|
{
|
||||||
printf("ok");
|
printf("ok");
|
||||||
@@ -132,6 +137,7 @@ func int main(int x)
|
|||||||
{
|
{
|
||||||
printf("helo\n");
|
printf("helo\n");
|
||||||
}
|
}
|
||||||
|
Test baok = { 1 };
|
||||||
Test2 efe;
|
Test2 efe;
|
||||||
efe.t.a = 3;
|
efe.t.a = 3;
|
||||||
if (efe.t.a > 2) printf("Works!\n");
|
if (efe.t.a > 2) printf("Works!\n");
|
||||||
@@ -183,6 +189,7 @@ JUMP:
|
|||||||
|
|
||||||
func void test2(int* x, int y, int z)
|
func void test2(int* x, int y, int z)
|
||||||
{
|
{
|
||||||
|
x++;
|
||||||
z = 0;
|
z = 0;
|
||||||
z ? y : z;
|
z ? y : z;
|
||||||
x += 1;
|
x += 1;
|
||||||
|
|||||||
11
resources/testproject/bar.c3
Normal file
11
resources/testproject/bar.c3
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module bar;
|
||||||
|
|
||||||
|
import baz::foo;
|
||||||
|
import testbaz;
|
||||||
|
|
||||||
|
public func void test()
|
||||||
|
{
|
||||||
|
Foo x;
|
||||||
|
foo::test();
|
||||||
|
Zab z;
|
||||||
|
}
|
||||||
20
resources/testproject/foo.c3
Normal file
20
resources/testproject/foo.c3
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
module baz::foo;
|
||||||
|
|
||||||
|
import bar;
|
||||||
|
|
||||||
|
public struct Foo
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern func void printf(char *hello);
|
||||||
|
|
||||||
|
local func void ofke()
|
||||||
|
{}
|
||||||
|
|
||||||
|
func void exple() {}
|
||||||
|
|
||||||
|
public func void test()
|
||||||
|
{
|
||||||
|
printf("Hello from baz::foo::test()!\n");
|
||||||
|
}
|
||||||
11
resources/testproject/hello_world.c3
Normal file
11
resources/testproject/hello_world.c3
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module hello_world;
|
||||||
|
import bar;
|
||||||
|
|
||||||
|
extern func void printf(char *hello);
|
||||||
|
|
||||||
|
func int main(int x)
|
||||||
|
{
|
||||||
|
printf("Hello World!\n");
|
||||||
|
bar::test();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
10
resources/testproject/project.toml
Normal file
10
resources/testproject/project.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[[executable]]
|
||||||
|
name = "hello_world"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["John Doe <john.doe@example.com>"]
|
||||||
|
langrev = "1"
|
||||||
|
warnings = ["no-unused"]
|
||||||
|
# sources compiled
|
||||||
|
sources = ["./**"]
|
||||||
|
# libraries to use
|
||||||
|
libs = []
|
||||||
6
resources/testproject/zab.c3
Normal file
6
resources/testproject/zab.c3
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module testbaz::zab;
|
||||||
|
|
||||||
|
public struct Zab
|
||||||
|
{
|
||||||
|
double a;
|
||||||
|
}
|
||||||
17
src/build/build_internal.h
Normal file
17
src/build/build_internal.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a LGPLv3.0
|
||||||
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "utils/lib.h"
|
||||||
|
#include "utils/toml.h"
|
||||||
|
#include "build_options.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
BuildTarget **targets;
|
||||||
|
} Project;
|
||||||
|
|
||||||
|
Project *project_load(void);
|
||||||
|
BuildTarget *project_select_target(Project *project, const char *optional_target);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "build_options.h"
|
#include "build_options.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -323,7 +323,7 @@ void parse_arguments(int argc, const char *argv[])
|
|||||||
build_options.debug_info = false;
|
build_options.debug_info = false;
|
||||||
build_options.command = COMMAND_MISSING;
|
build_options.command = COMMAND_MISSING;
|
||||||
build_options.symtab_size = DEFAULT_SYMTAB_SIZE;
|
build_options.symtab_size = DEFAULT_SYMTAB_SIZE;
|
||||||
build_options.files = VECNEW(const char *, MAX_FILES);
|
build_options.files = NULL;
|
||||||
for (int i = DIAG_NONE; i < DIAG_WARNING_TYPE; i++)
|
for (int i = DIAG_NONE; i < DIAG_WARNING_TYPE; i++)
|
||||||
{
|
{
|
||||||
build_options.severity[i] = DIAG_IGNORE;
|
build_options.severity[i] = DIAG_IGNORE;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "../utils/common.h"
|
#include "../utils/common.h"
|
||||||
|
|
||||||
@@ -95,6 +95,7 @@ typedef struct
|
|||||||
const char* target;
|
const char* target;
|
||||||
const char* path;
|
const char* path;
|
||||||
const char* cpu;
|
const char* cpu;
|
||||||
|
const char* target_triple;
|
||||||
CompilerCommand command;
|
CompilerCommand command;
|
||||||
uint32_t symtab_size;
|
uint32_t symtab_size;
|
||||||
CompileOption compile_option;
|
CompileOption compile_option;
|
||||||
@@ -114,6 +115,23 @@ typedef struct
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
TARGET_TYPE_EXECUTABLE,
|
||||||
|
TARGET_TYPE_STATIC_LIB,
|
||||||
|
TARGET_TYPE_DYNAMIC_LIB,
|
||||||
|
} TargetType;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
TargetType type;
|
||||||
|
const char *name;
|
||||||
|
const char *version;
|
||||||
|
const char *langrev;
|
||||||
|
const char **sources;
|
||||||
|
const char **libraries;
|
||||||
|
const char *target_triple;
|
||||||
|
} BuildTarget;
|
||||||
|
|
||||||
extern BuildOptions build_options;
|
extern BuildOptions build_options;
|
||||||
|
|
||||||
|
|||||||
28
src/build/builder.c
Normal file
28
src/build/builder.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a LGPLv3.0
|
||||||
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include "build_internal.h"
|
||||||
|
#include "build_options.h"
|
||||||
|
|
||||||
|
void load_library_files(void) {}
|
||||||
|
void load_files(void) {}
|
||||||
|
void compile_files(BuildTarget *target);
|
||||||
|
|
||||||
|
void build(void)
|
||||||
|
{
|
||||||
|
// Locate the project.toml
|
||||||
|
file_find_top_dir();
|
||||||
|
// Parse it
|
||||||
|
Project *project = project_load();
|
||||||
|
BuildTarget *target = project_select_target(project, build_options.target);
|
||||||
|
|
||||||
|
if (!target->target_triple && build_options.target_triple)
|
||||||
|
{
|
||||||
|
target->target_triple = build_options.target_triple;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_library_files();
|
||||||
|
compile_files(target);
|
||||||
|
}
|
||||||
154
src/build/project.c
Normal file
154
src/build/project.c
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a LGPLv3.0
|
||||||
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <utils/toml.h>
|
||||||
|
#include "build_internal.h"
|
||||||
|
|
||||||
|
TomlArray *get_array(TomlTable *table, const char *key)
|
||||||
|
{
|
||||||
|
TomlValue *value = toml_table_get(table, key);
|
||||||
|
if (!value) return NULL;
|
||||||
|
if (value->type != TOML_ARRAY)
|
||||||
|
{
|
||||||
|
error_exit("The key '%s' was not an array element. Did you type '[%s]' instead of '[[%s]]'?");
|
||||||
|
}
|
||||||
|
return value->value.array;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *copy_toml_string(TomlString *string)
|
||||||
|
{
|
||||||
|
size_t len = string->len;
|
||||||
|
char *new_str = malloc_arena(len + 1);
|
||||||
|
memcpy(new_str, string->str, len);
|
||||||
|
new_str[len] = '\0';
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_valid_string(TomlTable *table, const char *key, const char *category, bool mandatory)
|
||||||
|
{
|
||||||
|
TomlValue *value = toml_table_get(table, key);
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
if (mandatory)
|
||||||
|
{
|
||||||
|
error_exit("%s was missing a mandatory '%s' field, please add it.", category, key);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (value->type != TOML_STRING)
|
||||||
|
{
|
||||||
|
error_exit("%s had an invalid mandatory '%s' field that was not a string, please correct it.", category, key);
|
||||||
|
}
|
||||||
|
return copy_toml_string(value->value.string);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char **get_valid_array(TomlTable *table, const char *key, const char *category, bool mandatory)
|
||||||
|
{
|
||||||
|
TomlValue *value = toml_table_get(table, key);
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
if (mandatory)
|
||||||
|
{
|
||||||
|
error_exit("Error reading %s: %s was missing a mandatory '%s' field, please add it.", PROJECT_TOML, category, key);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (value->type != TOML_ARRAY)
|
||||||
|
{
|
||||||
|
error_exit("Error reading %s: %s had an invalid mandatory '%s' field that was not an array, please correct it.", PROJECT_TOML, category, key);
|
||||||
|
}
|
||||||
|
const char **values = NULL;
|
||||||
|
for (unsigned i = 0; i < value->value.array->len; i++)
|
||||||
|
{
|
||||||
|
TomlValue *val = value->value.array->elements[i];
|
||||||
|
if (val->type != TOML_STRING)
|
||||||
|
{
|
||||||
|
error_exit("Error reading %s: %s had an invalid mandatory '%s' array that did not only hold strings, please correct it.", PROJECT_TOML, category, key);
|
||||||
|
}
|
||||||
|
vec_add(values, copy_toml_string(val->value.string));
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
void project_add_target(Project *project, TomlValue *wrapped_table, const char *type, const char *type_key, TargetType target_type)
|
||||||
|
{
|
||||||
|
if (wrapped_table->type != TOML_TABLE)
|
||||||
|
{
|
||||||
|
error_exit("The %s had an invalid %s. Please check your [[%s]] configurations.", PROJECT_TOML, type, type_key);
|
||||||
|
}
|
||||||
|
BuildTarget *target = CALLOCS(BuildTarget);
|
||||||
|
vec_add(project->targets, target);
|
||||||
|
TomlTable *table = wrapped_table->value.table;
|
||||||
|
|
||||||
|
target->name = get_valid_string(table, "name", type, true);
|
||||||
|
VECEACH(project->targets, i)
|
||||||
|
{
|
||||||
|
BuildTarget *other_target = project->targets[i];
|
||||||
|
if (other_target == target) continue;
|
||||||
|
if (strcmp(other_target->name, target->name) == 0)
|
||||||
|
{
|
||||||
|
error_exit("More %s contained more than one target with the name %s. Please make all target names unique.", PROJECT_TOML, target->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = strformat("%s %s", type, target->name);
|
||||||
|
target->version = get_valid_string(table, "version", type, false);
|
||||||
|
if (!target->version) target->version = "1.0.0";
|
||||||
|
target->langrev = get_valid_string(table, "langrev", type, false);
|
||||||
|
target->sources = get_valid_array(table, "sources", type, true);
|
||||||
|
target->libraries = get_valid_array(table, "libs", type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void project_add_targets(Project *project, TomlTable *table, const char *type, const char *type_key, TargetType target_type)
|
||||||
|
{
|
||||||
|
TomlArray *targets = get_array(table, type_key);
|
||||||
|
if (!targets) return;
|
||||||
|
for (unsigned i = 0; i < targets->len; i++)
|
||||||
|
{
|
||||||
|
project_add_target(project, targets->elements[i], type, type_key, target_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuildTarget *project_select_default_target(Project *project)
|
||||||
|
{
|
||||||
|
VECEACH(project->targets, i)
|
||||||
|
{
|
||||||
|
BuildTarget *target = project->targets[i];
|
||||||
|
if (target->type == TARGET_TYPE_EXECUTABLE) return target;
|
||||||
|
}
|
||||||
|
return project->targets[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildTarget *project_select_target(Project *project, const char *optional_target)
|
||||||
|
{
|
||||||
|
if (!vec_size(project->targets))
|
||||||
|
{
|
||||||
|
error_exit("No targets could be found in %s. Please define at least one target, for example a [[executable]] and try again.", PROJECT_TOML);
|
||||||
|
}
|
||||||
|
if (!optional_target)
|
||||||
|
{
|
||||||
|
return project_select_default_target(project);
|
||||||
|
}
|
||||||
|
VECEACH(project->targets, i)
|
||||||
|
{
|
||||||
|
BuildTarget *target = project->targets[i];
|
||||||
|
if (strcmp(target->name, optional_target) == 0) return target;
|
||||||
|
}
|
||||||
|
error_exit("No build target named '%s' was found in %s. Was it misspelled?", optional_target, PROJECT_TOML);
|
||||||
|
}
|
||||||
|
|
||||||
|
Project *project_load(void)
|
||||||
|
{
|
||||||
|
Project *project = CALLOCS(Project);
|
||||||
|
TomlErr err = { .code = TOML_OK };
|
||||||
|
TomlTable *toml = toml_load_filename(PROJECT_TOML, &err);
|
||||||
|
if (err.code != TOML_OK)
|
||||||
|
{
|
||||||
|
error_exit("%s could not be read. Can you please check the read permissions on the file?", PROJECT_TOML);
|
||||||
|
}
|
||||||
|
project_add_targets(project, toml, "executable", "executable", TARGET_TYPE_EXECUTABLE);
|
||||||
|
project_add_targets(project, toml, "dynamic library", "dynamic-lib", TARGET_TYPE_DYNAMIC_LIB);
|
||||||
|
project_add_targets(project, toml, "static library", "static-lib", TARGET_TYPE_STATIC_LIB);
|
||||||
|
return project;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
void create_project(void);
|
void create_project(void);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
@@ -18,6 +18,19 @@ Type poisoned_type = { .type_kind = TYPE_POISONED };
|
|||||||
|
|
||||||
TypeInfo poisoned_type_info = { .kind = TYPE_INFO_POISON };
|
TypeInfo poisoned_type_info = { .kind = TYPE_INFO_POISON };
|
||||||
|
|
||||||
|
void decl_set_external_name(Decl *decl)
|
||||||
|
{
|
||||||
|
if (decl->visibility == VISIBLE_EXTERN)
|
||||||
|
{
|
||||||
|
decl->external_name = decl->name.string;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char buffer[1024];
|
||||||
|
uint32_t len = sprintf(buffer, "%s::%s", decl->module->name->module, decl->name.string);
|
||||||
|
assert(len);
|
||||||
|
TokenType type = TOKEN_INVALID_TOKEN;
|
||||||
|
decl->external_name = symtab_add(buffer, len, fnv1a(buffer, len), &type);
|
||||||
|
}
|
||||||
|
|
||||||
Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility)
|
Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility)
|
||||||
{
|
{
|
||||||
@@ -229,6 +242,10 @@ UnaryOp unary_op[256] = {
|
|||||||
[TOKEN_MINUSMINUS] = UNARYOP_DEC,
|
[TOKEN_MINUSMINUS] = UNARYOP_DEC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PostUnaryOp post_unary_op[256] = {
|
||||||
|
[TOKEN_PLUSPLUS] = POSTUNARYOP_INC,
|
||||||
|
[TOKEN_MINUSMINUS] = POSTUNARYOP_DEC,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
AssignOp assignop_from_token(TokenType type)
|
AssignOp assignop_from_token(TokenType type)
|
||||||
@@ -273,6 +290,20 @@ TokenType unaryop_to_token(UnaryOp type)
|
|||||||
return TOKEN_INVALID_TOKEN;
|
return TOKEN_INVALID_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PostUnaryOp post_unaryop_from_token(TokenType type)
|
||||||
|
{
|
||||||
|
return post_unary_op[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType postunaryop_to_token(PostUnaryOp type)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
if (post_unary_op[i] == type) return (TokenType)i;
|
||||||
|
}
|
||||||
|
return TOKEN_INVALID_TOKEN;
|
||||||
|
}
|
||||||
|
|
||||||
Ast poisoned_ast = { .ast_kind = AST_POISONED };
|
Ast poisoned_ast = { .ast_kind = AST_POISONED };
|
||||||
|
|
||||||
void fprint_indent(FILE *file, int indent)
|
void fprint_indent(FILE *file, int indent)
|
||||||
@@ -422,14 +453,7 @@ void fprint_type_info_recursive(FILE *file, TypeInfo *type_info, int indent)
|
|||||||
case TYPE_INFO_IDENTIFIER:
|
case TYPE_INFO_IDENTIFIER:
|
||||||
if (type_info->unresolved.path)
|
if (type_info->unresolved.path)
|
||||||
{
|
{
|
||||||
if (type_info->unresolved.path->module.string)
|
fprintf_indented(file, indent + 1, "(unresolved %s::%s)\n", type_info->unresolved.path->module, type_info->unresolved.name_loc.string);
|
||||||
{
|
|
||||||
fprintf_indented(file, indent + 1, "(unresolved %s::%s::%s)\n", type_info->unresolved.path->module.string, type_info->unresolved.path->module.string, type_info->unresolved.name_loc.string);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf_indented(file, indent + 1, "(unresolved %s::%s)\n", type_info->unresolved.path->module.string, type_info->unresolved.name_loc.string);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fprintf_indented(file, indent + 1, "(unresolved %s)\n", type_info->unresolved.name_loc.string);
|
fprintf_indented(file, indent + 1, "(unresolved %s)\n", type_info->unresolved.name_loc.string);
|
||||||
@@ -519,12 +543,12 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
|||||||
fprint_expr_recursive(file, expr->binary_expr.right, indent + 1);
|
fprint_expr_recursive(file, expr->binary_expr.right, indent + 1);
|
||||||
break;
|
break;
|
||||||
case EXPR_UNARY:
|
case EXPR_UNARY:
|
||||||
fprintf_indented(file, indent, "(unary %s\n", token_type_to_string(expr->unary_expr.operator));
|
fprintf_indented(file, indent, "(unary %s\n", token_type_to_string(unaryop_to_token(expr->unary_expr.operator)));
|
||||||
fprint_expr_common(file, expr, indent + 1);
|
fprint_expr_common(file, expr, indent + 1);
|
||||||
fprint_expr_recursive(file, expr->unary_expr.expr, indent + 1);
|
fprint_expr_recursive(file, expr->unary_expr.expr, indent + 1);
|
||||||
break;
|
break;
|
||||||
case EXPR_POST_UNARY:
|
case EXPR_POST_UNARY:
|
||||||
fprintf_indented(file, indent, "(postunary %s\n", token_type_to_string(expr->post_expr.operator));
|
fprintf_indented(file, indent, "(postunary %s\n", token_type_to_string(postunaryop_to_token(expr->post_expr.operator)));
|
||||||
fprint_expr_common(file, expr, indent + 1);
|
fprint_expr_common(file, expr, indent + 1);
|
||||||
fprint_expr_recursive(file, expr->post_expr.expr, indent + 1);
|
fprint_expr_recursive(file, expr->post_expr.expr, indent + 1);
|
||||||
break;
|
break;
|
||||||
@@ -1015,6 +1039,6 @@ void fprint_decl(FILE *file, Decl *dec)
|
|||||||
{
|
{
|
||||||
fprint_decl_recursive(file, dec, 0);
|
fprint_decl_recursive(file, dec, 0);
|
||||||
}
|
}
|
||||||
Module poisoned_module = { .name = "INVALID" };
|
Module poisoned_module = { .name = NULL };
|
||||||
Decl all_error = { .decl_kind = DECL_ERROR, .name = { .type = TOKEN_INVALID_TOKEN, .string = NULL } };
|
Decl all_error = { .decl_kind = DECL_ERROR, .name = { .type = TOKEN_INVALID_TOKEN, .string = NULL } };
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "bigint.h"
|
#include "bigint.h"
|
||||||
#include "../utils/lib.h"
|
#include "../utils/lib.h"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
#include "../build/build_options.h"
|
#include "../build/build_options.h"
|
||||||
@@ -13,12 +13,12 @@ void compiler_init(void)
|
|||||||
stable_init(&compiler.global_symbols, 0x1000);
|
stable_init(&compiler.global_symbols, 0x1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compiler_lex()
|
static void compiler_lex(BuildTarget *target)
|
||||||
{
|
{
|
||||||
VECEACH(build_options.files, i)
|
VECEACH(target->sources, i)
|
||||||
{
|
{
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
File *file = source_file_load(build_options.files[i], &loaded);
|
File *file = source_file_load(target->sources[i], &loaded);
|
||||||
if (loaded) continue;
|
if (loaded) continue;
|
||||||
lexer_add_file_for_lexing(file);
|
lexer_add_file_for_lexing(file);
|
||||||
printf("# %s\n", file->full_path);
|
printf("# %s\n", file->full_path);
|
||||||
@@ -33,32 +33,32 @@ static void compiler_lex()
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compiler_parse()
|
void compiler_parse(BuildTarget *target)
|
||||||
{
|
{
|
||||||
builtin_setup();
|
builtin_setup();
|
||||||
VECEACH(build_options.files, i)
|
VECEACH(target->sources, i)
|
||||||
{
|
{
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
File *file = source_file_load(build_options.files[i], &loaded);
|
File *file = source_file_load(target->sources[i], &loaded);
|
||||||
if (loaded) continue;
|
if (loaded) continue;
|
||||||
diag_reset();
|
diag_reset();
|
||||||
Context *context = context_create(file);
|
Context *context = context_create(file, target);
|
||||||
parse_file(context);
|
parse_file(context);
|
||||||
context_print_ast(context, stdout);
|
context_print_ast(context, stdout);
|
||||||
}
|
}
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compiler_compile()
|
void compiler_compile(BuildTarget *target)
|
||||||
{
|
{
|
||||||
Context **contexts = NULL;
|
Context **contexts = NULL;
|
||||||
VECEACH(build_options.files, i)
|
VECEACH(target->sources, i)
|
||||||
{
|
{
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
File *file = source_file_load(build_options.files[i], &loaded);
|
File *file = source_file_load(target->sources[i], &loaded);
|
||||||
if (loaded) continue;
|
if (loaded) continue;
|
||||||
diag_reset();
|
diag_reset();
|
||||||
Context *context = context_create(file);
|
Context *context = context_create(file, target);
|
||||||
vec_add(contexts, context);
|
vec_add(contexts, context);
|
||||||
parse_file(context);
|
parse_file(context);
|
||||||
}
|
}
|
||||||
@@ -76,6 +76,11 @@ void compiler_compile()
|
|||||||
decl->resolve_status = RESOLVE_DONE;
|
decl->resolve_status = RESOLVE_DONE;
|
||||||
context_register_global_decl(contexts[0], decl);
|
context_register_global_decl(contexts[0], decl);
|
||||||
*/
|
*/
|
||||||
|
assert(contexts);
|
||||||
|
VECEACH(contexts, i)
|
||||||
|
{
|
||||||
|
sema_analysis_pass_process_imports(contexts[i]);
|
||||||
|
}
|
||||||
VECEACH(contexts, i)
|
VECEACH(contexts, i)
|
||||||
{
|
{
|
||||||
sema_analysis_pass_conditional_compilation(contexts[i]);
|
sema_analysis_pass_conditional_compilation(contexts[i]);
|
||||||
@@ -93,22 +98,69 @@ void compiler_compile()
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compile_file()
|
static void target_expand_source_names(BuildTarget *target)
|
||||||
{
|
{
|
||||||
|
const char **files = NULL;
|
||||||
|
VECEACH(target->sources, i)
|
||||||
|
{
|
||||||
|
const char *name = target->sources[i];
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
if (name_len < 1) goto INVALID_NAME;
|
||||||
|
if (name[name_len - 1] == '*')
|
||||||
|
{
|
||||||
|
if (name_len == 1 || name[name_len - 2] == '/')
|
||||||
|
{
|
||||||
|
char *path = strdup(name);
|
||||||
|
path[name_len - 1] = '\0';
|
||||||
|
file_add_wildcard_files(&files, path, false);
|
||||||
|
free(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (name[name_len - 2] != '*') goto INVALID_NAME;
|
||||||
|
if (name_len == 2 || name[name_len - 3] == '/')
|
||||||
|
{
|
||||||
|
char *path = strdup(name);
|
||||||
|
path[name_len - 2] = '\0';
|
||||||
|
file_add_wildcard_files(&files, path, true);
|
||||||
|
free(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
goto INVALID_NAME;
|
||||||
|
}
|
||||||
|
if (name_len < 4) goto INVALID_NAME;
|
||||||
|
if (strcmp(&name[name_len - 3], ".c3") != 0) goto INVALID_NAME;
|
||||||
|
vec_add(files, name);
|
||||||
|
continue;
|
||||||
|
INVALID_NAME:
|
||||||
|
error_exit("File names must end with .c3 or they cannot be compiled: '%s' is invalid.", name);
|
||||||
|
}
|
||||||
|
target->sources = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compile_files(BuildTarget *target)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
target = CALLOCS(BuildTarget);
|
||||||
|
target->type = TARGET_TYPE_EXECUTABLE;
|
||||||
|
target->sources = build_options.files;
|
||||||
|
target->name = "a.out";
|
||||||
|
}
|
||||||
|
target_expand_source_names(target);
|
||||||
target_setup();
|
target_setup();
|
||||||
builtin_setup();
|
builtin_setup();
|
||||||
|
|
||||||
if (!vec_size(build_options.files)) error_exit("No files to compile.");
|
if (!vec_size(target->sources)) error_exit("No files to compile.");
|
||||||
switch (build_options.compile_option)
|
switch (build_options.compile_option)
|
||||||
{
|
{
|
||||||
case COMPILE_LEX_ONLY:
|
case COMPILE_LEX_ONLY:
|
||||||
compiler_lex();
|
compiler_lex(target);
|
||||||
break;
|
break;
|
||||||
case COMPILE_LEX_PARSE_ONLY:
|
case COMPILE_LEX_PARSE_ONLY:
|
||||||
compiler_parse();
|
compiler_parse(target);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
compiler_compile();
|
compiler_compile(target);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
TODO
|
TODO
|
||||||
@@ -126,14 +178,35 @@ void compiler_add_type(Type *type)
|
|||||||
assert(type_ok(type));
|
assert(type_ok(type));
|
||||||
VECADD(compiler.type, type);
|
VECADD(compiler.type, type);
|
||||||
}
|
}
|
||||||
Module *compiler_find_or_create_module(const char *module_name)
|
Module *compiler_find_or_create_module(Path *module_name)
|
||||||
{
|
{
|
||||||
Module *module = stable_get(&compiler.modules, module_name);
|
Module *module = stable_get(&compiler.modules, module_name->module);
|
||||||
if (module) return module;
|
|
||||||
|
if (module)
|
||||||
|
{
|
||||||
|
// We might have gotten an auto-generated module, if so
|
||||||
|
// update the path here.
|
||||||
|
if (module->name->span.loc == INVALID_LOC && module_name->span.loc != INVALID_LOC)
|
||||||
|
{
|
||||||
|
module->name = module_name;
|
||||||
|
}
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("Creating module %s.", module_name->module);
|
||||||
|
// Set up the module.
|
||||||
module = CALLOCS(Module);
|
module = CALLOCS(Module);
|
||||||
module->name = module_name;
|
module->name = module_name;
|
||||||
stable_init(&module->symbols, 0x10000);
|
stable_init(&module->symbols, 0x10000);
|
||||||
stable_set(&compiler.modules, module_name, module);
|
stable_set(&compiler.modules, module_name->module, module);
|
||||||
|
// Now find the possible parent array:
|
||||||
|
Path *parent_path = path_find_parent_path(module_name);
|
||||||
|
if (parent_path)
|
||||||
|
{
|
||||||
|
// Get the parent
|
||||||
|
Module *parent_module = compiler_find_or_create_module(parent_path);
|
||||||
|
vec_add(parent_module->sub_modules, module);
|
||||||
|
}
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,12 +215,12 @@ void compiler_register_public_symbol(Decl *decl)
|
|||||||
Decl *prev = stable_get(&compiler.global_symbols, decl->name.string);
|
Decl *prev = stable_get(&compiler.global_symbols, decl->name.string);
|
||||||
// If the previous symbol was already declared globally, remove it.
|
// If the previous symbol was already declared globally, remove it.
|
||||||
stable_set(&compiler.global_symbols, decl->name.string, prev ? &poisoned_decl : decl);
|
stable_set(&compiler.global_symbols, decl->name.string, prev ? &poisoned_decl : decl);
|
||||||
STable *sub_module_space = stable_get(&compiler.qualified_symbols, decl->module->name);
|
STable *sub_module_space = stable_get(&compiler.qualified_symbols, decl->module->name->module);
|
||||||
if (!sub_module_space)
|
if (!sub_module_space)
|
||||||
{
|
{
|
||||||
sub_module_space = malloc_arena(sizeof(*sub_module_space));
|
sub_module_space = malloc_arena(sizeof(*sub_module_space));
|
||||||
stable_init(sub_module_space, 0x100);
|
stable_init(sub_module_space, 0x100);
|
||||||
stable_set(&compiler.qualified_symbols, decl->module->name, sub_module_space);
|
stable_set(&compiler.qualified_symbols, decl->module->name->module, sub_module_space);
|
||||||
}
|
}
|
||||||
prev = stable_get(sub_module_space, decl->name.string);
|
prev = stable_get(sub_module_space, decl->name.string);
|
||||||
stable_set(sub_module_space, decl->name.string, prev ? &poisoned_decl : decl);
|
stable_set(sub_module_space, decl->name.string, prev ? &poisoned_decl : decl);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "build/build_options.h"
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
void compiler_init();
|
void compiler_init();
|
||||||
void compile_file();
|
void compile_files(BuildTarget *target);
|
||||||
|
void build();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "../utils/common.h"
|
#include "../utils/common.h"
|
||||||
#include "../utils/errors.h"
|
#include "../utils/errors.h"
|
||||||
@@ -16,6 +16,7 @@ typedef uint32_t SourceLoc;
|
|||||||
#define EMPTY_TOKEN ((Token) { .string = NULL })
|
#define EMPTY_TOKEN ((Token) { .string = NULL })
|
||||||
#define MAX_LOCALS 0xFFFF
|
#define MAX_LOCALS 0xFFFF
|
||||||
#define MAX_SCOPE_DEPTH 0xFF
|
#define MAX_SCOPE_DEPTH 0xFF
|
||||||
|
#define MAX_PATH 1024
|
||||||
|
|
||||||
typedef struct _Ast Ast;
|
typedef struct _Ast Ast;
|
||||||
typedef struct _Decl Decl;
|
typedef struct _Decl Decl;
|
||||||
@@ -87,10 +88,11 @@ typedef struct
|
|||||||
const char *start;
|
const char *start;
|
||||||
} SourcePosition;
|
} SourcePosition;
|
||||||
|
|
||||||
typedef struct
|
typedef struct _Path
|
||||||
{
|
{
|
||||||
Token module;
|
SourceRange span;
|
||||||
Token sub_module;
|
const char *module;
|
||||||
|
uint32_t len;
|
||||||
} Path;
|
} Path;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -169,10 +171,9 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
ImportType type : 3;
|
Path *path;
|
||||||
Token alias;
|
Token symbol;
|
||||||
Expr** generic_parameters;
|
bool aliased;
|
||||||
Module *module;
|
|
||||||
} ImportDecl;
|
} ImportDecl;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -204,6 +205,7 @@ typedef struct
|
|||||||
{
|
{
|
||||||
Decl *parent;
|
Decl *parent;
|
||||||
Expr *expr;
|
Expr *expr;
|
||||||
|
uint64_t ordinal;
|
||||||
} EnumConstantDecl;
|
} EnumConstantDecl;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -221,6 +223,7 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct _FunctionSignature
|
typedef struct _FunctionSignature
|
||||||
{
|
{
|
||||||
|
CallConvention convention : 4;
|
||||||
bool variadic : 1;
|
bool variadic : 1;
|
||||||
TypeInfo *rtype;
|
TypeInfo *rtype;
|
||||||
Decl** params;
|
Decl** params;
|
||||||
@@ -235,7 +238,6 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
const char *full_name;
|
|
||||||
TypeInfo *type_parent;
|
TypeInfo *type_parent;
|
||||||
FunctionSignature function_signature;
|
FunctionSignature function_signature;
|
||||||
Ast *body;
|
Ast *body;
|
||||||
@@ -251,6 +253,11 @@ typedef struct
|
|||||||
FunctionSignature attr_signature;
|
FunctionSignature attr_signature;
|
||||||
} AttrDecl;
|
} AttrDecl;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Decl *import;
|
||||||
|
} AliasDecl;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
bool is_func : 1;
|
bool is_func : 1;
|
||||||
@@ -281,6 +288,7 @@ typedef struct
|
|||||||
typedef struct _Decl
|
typedef struct _Decl
|
||||||
{
|
{
|
||||||
Token name;
|
Token name;
|
||||||
|
const char *external_name;
|
||||||
DeclKind decl_kind : 6;
|
DeclKind decl_kind : 6;
|
||||||
Visibility visibility : 2;
|
Visibility visibility : 2;
|
||||||
ResolveStatus resolve_status : 2;
|
ResolveStatus resolve_status : 2;
|
||||||
@@ -366,17 +374,15 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
Expr *left;
|
Expr* expr;
|
||||||
Expr *right;
|
UnaryOp operator;
|
||||||
AssignOp operator;
|
} ExprUnary;
|
||||||
} ExprAssign;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
Expr* expr;
|
Expr* expr;
|
||||||
TokenType operator;
|
PostUnaryOp operator;
|
||||||
} ExprUnary;
|
} ExprPostUnary;
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -396,7 +402,7 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
bool is_struct_function;
|
bool is_struct_function : 1;
|
||||||
Expr *function;
|
Expr *function;
|
||||||
Expr **arguments;
|
Expr **arguments;
|
||||||
} ExprCall;
|
} ExprCall;
|
||||||
@@ -458,10 +464,9 @@ struct _Expr
|
|||||||
ExprTypeRef type_access;
|
ExprTypeRef type_access;
|
||||||
ExprTry try_expr;
|
ExprTry try_expr;
|
||||||
ExprBinary binary_expr;
|
ExprBinary binary_expr;
|
||||||
ExprAssign assign_expr;
|
|
||||||
ExprTernary ternary_expr;
|
ExprTernary ternary_expr;
|
||||||
ExprUnary unary_expr;
|
ExprUnary unary_expr;
|
||||||
ExprUnary post_expr;
|
ExprPostUnary post_expr;
|
||||||
ExprCall call_expr;
|
ExprCall call_expr;
|
||||||
ExprSubscript subscript_expr;
|
ExprSubscript subscript_expr;
|
||||||
ExprAccess access_expr;
|
ExprAccess access_expr;
|
||||||
@@ -663,7 +668,7 @@ typedef struct _Ast
|
|||||||
|
|
||||||
typedef struct _Module
|
typedef struct _Module
|
||||||
{
|
{
|
||||||
const char *name;
|
Path *name;
|
||||||
|
|
||||||
bool is_external;
|
bool is_external;
|
||||||
bool is_c_library;
|
bool is_c_library;
|
||||||
@@ -675,6 +680,7 @@ typedef struct _Module
|
|||||||
STable struct_functions;
|
STable struct_functions;
|
||||||
STable symbols;
|
STable symbols;
|
||||||
STable public_symbols;
|
STable public_symbols;
|
||||||
|
Module **sub_modules;
|
||||||
} Module;
|
} Module;
|
||||||
|
|
||||||
|
|
||||||
@@ -691,10 +697,12 @@ typedef struct _DynamicScope
|
|||||||
|
|
||||||
typedef struct _Context
|
typedef struct _Context
|
||||||
{
|
{
|
||||||
Token module_name;
|
BuildTarget *target;
|
||||||
|
Path *module_name;
|
||||||
Token* module_parameters;
|
Token* module_parameters;
|
||||||
File* file;
|
File* file;
|
||||||
Decl** imports;
|
Decl** imports;
|
||||||
|
Decl *specified_imports;
|
||||||
Module *module;
|
Module *module;
|
||||||
STable local_symbols;
|
STable local_symbols;
|
||||||
Decl **header_declarations;
|
Decl **header_declarations;
|
||||||
@@ -716,6 +724,11 @@ typedef struct _Context
|
|||||||
int in_volatile_section;
|
int in_volatile_section;
|
||||||
Decl *locals[MAX_LOCALS];
|
Decl *locals[MAX_LOCALS];
|
||||||
DynamicScope scopes[MAX_SCOPE_DEPTH];
|
DynamicScope scopes[MAX_SCOPE_DEPTH];
|
||||||
|
char path_scratch[MAX_PATH];
|
||||||
|
struct {
|
||||||
|
STable external_symbols;
|
||||||
|
Decl **external_symbol_list;
|
||||||
|
};
|
||||||
} Context;
|
} Context;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -756,6 +769,8 @@ extern Type t_cus, t_cui, t_cul, t_cull;
|
|||||||
extern Type t_cs, t_ci, t_cl, t_cll;
|
extern Type t_cs, t_ci, t_cl, t_cll;
|
||||||
extern Type t_voidstar;
|
extern Type t_voidstar;
|
||||||
|
|
||||||
|
extern const char *main_name;
|
||||||
|
|
||||||
#define AST_NEW(_kind, _token) new_ast(_kind, _token)
|
#define AST_NEW(_kind, _token) new_ast(_kind, _token)
|
||||||
|
|
||||||
static inline bool ast_ok(Ast *ast) { return ast == NULL || ast->ast_kind != AST_POISONED; }
|
static inline bool ast_ok(Ast *ast) { return ast == NULL || ast->ast_kind != AST_POISONED; }
|
||||||
@@ -827,22 +842,22 @@ CastKind cast_to_bool_kind(Type *type);
|
|||||||
bool cast_to_runtime(Expr *expr);
|
bool cast_to_runtime(Expr *expr);
|
||||||
|
|
||||||
void llvm_codegen(Context *context);
|
void llvm_codegen(Context *context);
|
||||||
void codegen(Context *context);
|
|
||||||
|
|
||||||
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
|
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
|
||||||
bool sema_analyse_decl(Context *context, Decl *decl);
|
bool sema_analyse_decl(Context *context, Decl *decl);
|
||||||
|
|
||||||
void compiler_add_type(Type *type);
|
void compiler_add_type(Type *type);
|
||||||
Decl *compiler_find_symbol(Token token);
|
Decl *compiler_find_symbol(Token token);
|
||||||
Module *compiler_find_or_create_module(const char *module_name);
|
Module *compiler_find_or_create_module(Path *module_name);
|
||||||
void compiler_register_public_symbol(Decl *decl);
|
void compiler_register_public_symbol(Decl *decl);
|
||||||
|
|
||||||
Context *context_create(File *file);
|
Context *context_create(File *file, BuildTarget *target);
|
||||||
void context_push(Context *context);
|
void context_push(Context *context);
|
||||||
void context_register_global_decl(Context *context, Decl *decl);
|
void context_register_global_decl(Context *context, Decl *decl);
|
||||||
bool context_add_import(Context *context, Token module_name, Token alias, ImportType import_type, Expr** generic_parameters);
|
void context_register_external_symbol(Context *context, Decl *decl);
|
||||||
|
bool context_add_import(Context *context, Path *path, Token symbol, Token alias);
|
||||||
bool context_set_module_from_filename(Context *context);
|
bool context_set_module_from_filename(Context *context);
|
||||||
bool context_set_module(Context *context, Token module_name, Token *generic_parameters);
|
bool context_set_module(Context *context, Path *path, Token *generic_parameters);
|
||||||
void context_print_ast(Context *context, FILE *file);
|
void context_print_ast(Context *context, FILE *file);
|
||||||
Decl *context_find_ident(Context *context, const char *symbol);
|
Decl *context_find_ident(Context *context, const char *symbol);
|
||||||
void context_add_header_decl(Context *context, Decl *decl);
|
void context_add_header_decl(Context *context, Decl *decl);
|
||||||
@@ -851,6 +866,7 @@ bool context_add_local(Context *context, Decl *decl);
|
|||||||
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility);
|
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility);
|
||||||
Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility);
|
Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility);
|
||||||
Decl *decl_new_var(Token name, TypeInfo *type, VarDeclKind kind, Visibility visibility);
|
Decl *decl_new_var(Token name, TypeInfo *type, VarDeclKind kind, Visibility visibility);
|
||||||
|
void decl_set_external_name(Decl *decl);
|
||||||
const char *decl_var_to_string(VarDeclKind kind);
|
const char *decl_var_to_string(VarDeclKind kind);
|
||||||
|
|
||||||
static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; }
|
static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; }
|
||||||
@@ -911,19 +927,31 @@ static inline void advance_and_verify(TokenType token_type)
|
|||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
Decl *module_find_symbol(Module *module, const char *symbol);
|
typedef enum
|
||||||
|
{
|
||||||
|
MODULE_SYMBOL_SEARCH_EXTERNAL,
|
||||||
|
MODULE_SYMBOL_SEARCH_PARENT,
|
||||||
|
MODULE_SYMBOL_SEARCH_THIS
|
||||||
|
} ModuleSymbolSearch;
|
||||||
|
|
||||||
|
Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch search);
|
||||||
|
|
||||||
void parse_file(Context *context);
|
void parse_file(Context *context);
|
||||||
|
Path *path_create_from_string(const char *string, size_t len, SourceRange span);
|
||||||
|
Path *path_find_parent_path(Path *path);
|
||||||
|
|
||||||
const char *resolve_status_to_string(ResolveStatus status);
|
const char *resolve_status_to_string(ResolveStatus status);
|
||||||
|
|
||||||
#define SEMA_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__)
|
#define SEMA_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__)
|
||||||
void sema_init(File *file);
|
void sema_init(File *file);
|
||||||
|
void sema_analysis_pass_process_imports(Context *context);
|
||||||
void sema_analysis_pass_conditional_compilation(Context *context);
|
void sema_analysis_pass_conditional_compilation(Context *context);
|
||||||
void sema_analysis_pass_decls(Context *context);
|
void sema_analysis_pass_decls(Context *context);
|
||||||
void sema_analysis_pass_3(Context *context);
|
void sema_analysis_pass_3(Context *context);
|
||||||
|
|
||||||
|
bool sema_add_local(Context *context, Decl *decl);
|
||||||
bool sema_analyse_statement(Context *context, Ast *statement);
|
bool sema_analyse_statement(Context *context, Ast *statement);
|
||||||
|
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl);
|
||||||
bool sema_resolve_type_info(Context *context, TypeInfo *type_info);
|
bool sema_resolve_type_info(Context *context, TypeInfo *type_info);
|
||||||
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info);
|
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info);
|
||||||
void sema_error_at(SourceLoc loc, const char *message, ...);
|
void sema_error_at(SourceLoc loc, const char *message, ...);
|
||||||
@@ -940,6 +968,8 @@ File *source_file_from_position(SourceLoc loc);
|
|||||||
void source_file_append_line_end(File *file, SourceLoc loc);
|
void source_file_append_line_end(File *file, SourceLoc loc);
|
||||||
SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc);
|
SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc);
|
||||||
SourcePosition source_file_find_position(SourceLoc loc);
|
SourcePosition source_file_find_position(SourceLoc loc);
|
||||||
|
SourceRange source_range_from_ranges(SourceRange first, SourceRange last);
|
||||||
|
|
||||||
|
|
||||||
void stable_init(STable *table, uint32_t initial_size);
|
void stable_init(STable *table, uint32_t initial_size);
|
||||||
void *stable_set(STable *table, const char *key, void *value);
|
void *stable_set(STable *table, const char *key, void *value);
|
||||||
@@ -958,6 +988,7 @@ void *target_machine();
|
|||||||
#define TOKEN_MAX_LENGTH 0xFFFF
|
#define TOKEN_MAX_LENGTH 0xFFFF
|
||||||
#define TOK2VARSTR(_token) _token.span.length, _token.start
|
#define TOK2VARSTR(_token) _token.span.length, _token.start
|
||||||
bool token_is_type(TokenType type);
|
bool token_is_type(TokenType type);
|
||||||
|
bool token_is_symbol(TokenType type);
|
||||||
const char *token_type_to_string(TokenType type);
|
const char *token_type_to_string(TokenType type);
|
||||||
static inline Token wrap(const char *string)
|
static inline Token wrap(const char *string)
|
||||||
{
|
{
|
||||||
@@ -1061,6 +1092,9 @@ static inline bool type_is_number(Type *type)
|
|||||||
|
|
||||||
AssignOp assignop_from_token(TokenType type);
|
AssignOp assignop_from_token(TokenType type);
|
||||||
UnaryOp unaryop_from_token(TokenType type);
|
UnaryOp unaryop_from_token(TokenType type);
|
||||||
|
TokenType unaryop_to_token(UnaryOp type);
|
||||||
|
PostUnaryOp post_unaryop_from_token(TokenType type);
|
||||||
|
TokenType postunaryop_to_token(PostUnaryOp type);
|
||||||
BinaryOp binaryop_from_token(TokenType type);
|
BinaryOp binaryop_from_token(TokenType type);
|
||||||
BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op);
|
BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op);
|
||||||
TokenType binaryop_to_token(BinaryOp type);
|
TokenType binaryop_to_token(BinaryOp type);
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
|
|
||||||
Context *current_context;
|
Context *current_context;
|
||||||
|
|
||||||
Context *context_create(File *file)
|
Context *context_create(File *file, BuildTarget *target)
|
||||||
{
|
{
|
||||||
Context *context = malloc_arena(sizeof(Context));
|
Context *context = malloc_arena(sizeof(Context));
|
||||||
memset(context, 0, sizeof(Context));
|
memset(context, 0, sizeof(Context));
|
||||||
context->file = file;
|
context->file = file;
|
||||||
|
context->target = target;
|
||||||
stable_init(&context->local_symbols, 256);
|
stable_init(&context->local_symbols, 256);
|
||||||
|
stable_init(&context->external_symbols, 256);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,17 +52,17 @@ bool context_add_local(Context *context, Decl *decl)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool create_module_or_check_name(Context *context, Token module_name)
|
static inline bool create_module_or_check_name(Context *context, Path *module_name)
|
||||||
{
|
{
|
||||||
context->module_name = module_name;
|
context->module_name = module_name;
|
||||||
if (context->module == NULL)
|
if (context->module == NULL)
|
||||||
{
|
{
|
||||||
context->module = compiler_find_or_create_module(module_name.string);
|
context->module = compiler_find_or_create_module(module_name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (context->module->name != module_name.string)
|
else if (context->module->name->module != module_name->module)
|
||||||
{
|
{
|
||||||
SEMA_ERROR(module_name, "Module name here '%s' did not match actual module '%s'.", module_name.string, context->module->name);
|
sema_error_range(module_name->span, "Module name here '%s' did not match actual module '%s'.", module_name->module, context->module->name->module);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -84,25 +86,37 @@ bool context_set_module_from_filename(Context *context)
|
|||||||
"try using an explicit module name.");
|
"try using an explicit module name.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return create_module_or_check_name(context, wrap(module_name));
|
Path *path = CALLOCS(Path);
|
||||||
|
path->span = INVALID_RANGE;
|
||||||
|
path->module = module_name;
|
||||||
|
path->len = len;
|
||||||
|
return create_module_or_check_name(context, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool context_set_module(Context *context, Token module_name, Token *generic_parameters)
|
bool context_set_module(Context *context, Path *path, Token *generic_parameters)
|
||||||
{
|
{
|
||||||
DEBUG_LOG("CONTEXT: Setting module to '%s'.", module_name.string);
|
DEBUG_LOG("CONTEXT: Setting module to '%s'.", path->module);
|
||||||
// Note that we allow the illegal name for now, to be able to parse further.
|
// Note that we allow the illegal name for now, to be able to parse further.
|
||||||
context->module_name = module_name;
|
context->module_name = path;
|
||||||
if (!is_all_lower(module_name.string))
|
if (!is_all_lower(path->module))
|
||||||
{
|
{
|
||||||
sema_error_range(module_name.span, "A module name may not have any upper case characters.");
|
sema_error_range(path->span, "A module name may not have any upper case characters.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
context->module_parameters = generic_parameters;
|
context->module_parameters = generic_parameters;
|
||||||
|
|
||||||
return create_module_or_check_name(context, module_name);
|
return create_module_or_check_name(context, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void context_register_external_symbol(Context *context, Decl *decl)
|
||||||
|
{
|
||||||
|
assert(decl->external_name && "Missing external name");
|
||||||
|
Decl *prev = stable_get(&context->external_symbols, decl->external_name);
|
||||||
|
if (prev) return;
|
||||||
|
stable_set(&context->external_symbols, decl->external_name, decl);
|
||||||
|
vec_add(context->external_symbol_list, decl);
|
||||||
|
}
|
||||||
|
|
||||||
void context_register_global_decl(Context *context, Decl *decl)
|
void context_register_global_decl(Context *context, Decl *decl)
|
||||||
{
|
{
|
||||||
@@ -117,26 +131,32 @@ void context_register_global_decl(Context *context, Decl *decl)
|
|||||||
if (decl->func.type_parent)
|
if (decl->func.type_parent)
|
||||||
{
|
{
|
||||||
vec_add(context->struct_functions, decl);
|
vec_add(context->struct_functions, decl);
|
||||||
|
// TODO set name
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vec_add(context->functions, decl);
|
vec_add(context->functions, decl);
|
||||||
|
decl_set_external_name(decl);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DECL_VAR:
|
case DECL_VAR:
|
||||||
vec_add(context->vars, decl);
|
vec_add(context->vars, decl);
|
||||||
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_STRUCT:
|
case DECL_STRUCT:
|
||||||
case DECL_UNION:
|
case DECL_UNION:
|
||||||
case DECL_TYPEDEF:
|
case DECL_TYPEDEF:
|
||||||
vec_add(context->types, decl);
|
vec_add(context->types, decl);
|
||||||
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_ENUM:
|
case DECL_ENUM:
|
||||||
vec_add(context->enums, decl);
|
vec_add(context->enums, decl);
|
||||||
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_ERROR:
|
case DECL_ERROR:
|
||||||
vec_add(context->error_types, decl);
|
vec_add(context->error_types, decl);
|
||||||
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_ENUM_CONSTANT:
|
case DECL_ENUM_CONSTANT:
|
||||||
case DECL_ERROR_CONSTANT:
|
case DECL_ERROR_CONSTANT:
|
||||||
@@ -173,71 +193,40 @@ void context_register_global_decl(Context *context, Decl *decl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool context_add_import(Context *context, Token module_name, Token alias, ImportType import_type, Expr** generic_parameters)
|
bool context_add_import(Context *context, Path *path, Token token, Token alias)
|
||||||
{
|
{
|
||||||
DEBUG_LOG("SEMA: Add import of '%s'.", module_name.string);
|
DEBUG_LOG("SEMA: Add import of '%s'.", path->module);
|
||||||
if (!is_all_lower(module_name.string))
|
|
||||||
|
if (!is_all_lower(path->module))
|
||||||
|
{
|
||||||
|
sema_error_range(path->span, "A module is not expected to have any upper case characters, please change it.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *import = CALLOCS(Decl);
|
||||||
|
import->decl_kind = DECL_IMPORT;
|
||||||
|
import->visibility = VISIBLE_LOCAL;
|
||||||
|
import->import.path = path;
|
||||||
|
import->import.symbol = token;
|
||||||
|
if (alias.type != TOKEN_INVALID_TOKEN)
|
||||||
{
|
{
|
||||||
sema_error_range(module_name.span, "A module is not expected to have any upper case characters, please change it.");
|
if (alias.string == token.string)
|
||||||
return false;
|
{
|
||||||
}
|
sema_error_range(alias.span, "If an alias would be the same as the symbol aliased, it wouldn't have any effect.");
|
||||||
Decl *decl = decl_new(DECL_IMPORT, module_name, VISIBLE_LOCAL);
|
return false;
|
||||||
decl->import.type = import_type;
|
}
|
||||||
decl->import.generic_parameters = generic_parameters;
|
if (alias.string == context->module_name->module)
|
||||||
if (import_type == IMPORT_TYPE_ALIAS_LOCAL || import_type == IMPORT_TYPE_ALIAS)
|
{
|
||||||
{
|
sema_error_range(alias.span, "An alias cannot have not have the same as the name of the current module.");
|
||||||
decl->import.alias = alias;
|
return false;
|
||||||
if (!is_all_lower(alias.string))
|
}
|
||||||
{
|
import->import.aliased = true;
|
||||||
sema_error_range(alias.span, "A module alias is not expected to have any upper case characters, please change it.");
|
TODO
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (alias.string == module_name.string)
|
|
||||||
{
|
|
||||||
sema_error_range(alias.span, "If a module alias would be the same as the alias, it wouldn't have any effect.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (alias.string == context->module_name.string)
|
|
||||||
{
|
|
||||||
sema_error_range(alias.span, "An alias should not be the same as the name of the current module.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
decl->import.alias.string = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VECEACH(context->imports, i)
|
vec_add(context->imports, import);
|
||||||
{
|
printf("Added import %s\n", path->module);
|
||||||
Decl *other_import = context->imports[i];
|
|
||||||
if (other_import->name.string == module_name.string
|
|
||||||
&& !other_import->import.generic_parameters
|
|
||||||
&& !generic_parameters)
|
|
||||||
{
|
|
||||||
sema_error_range(module_name.span, "This module was imported earlier in the file.");
|
|
||||||
}
|
|
||||||
if (other_import->import.alias.string == module_name.string)
|
|
||||||
{
|
|
||||||
sema_error_range(other_import->import.alias.span,
|
|
||||||
"An alias should not be the same as the name of another imported module.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (decl->import.alias.string == other_import->name.string)
|
|
||||||
{
|
|
||||||
sema_error_range(decl->import.alias.span,
|
|
||||||
"An alias should not be the same as the name of another imported module.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (decl->import.alias.string && decl->import.alias.string == other_import->import.alias.string)
|
|
||||||
{
|
|
||||||
sema_error_range(decl->import.alias.span,
|
|
||||||
"This alias has already been used by an earlier import statement.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context->imports = VECADD(context->imports, decl);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#define DW_TAG_array_type 0x01
|
#define DW_TAG_array_type 0x01
|
||||||
#define DW_TAG_class_type 0x02
|
#define DW_TAG_class_type 0x02
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
// Only include this from compiler_common.h
|
// Only include this from compiler_common.h
|
||||||
|
|
||||||
@@ -250,14 +250,6 @@ typedef enum
|
|||||||
GOTO_JUMP_BACK
|
GOTO_JUMP_BACK
|
||||||
} GotoType;
|
} GotoType;
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
IMPORT_TYPE_FULL,
|
|
||||||
IMPORT_TYPE_ALIAS,
|
|
||||||
IMPORT_TYPE_ALIAS_LOCAL,
|
|
||||||
IMPORT_TYPE_LOCAL
|
|
||||||
} ImportType;
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@@ -448,6 +440,7 @@ typedef enum
|
|||||||
TOKEN_ELSE,
|
TOKEN_ELSE,
|
||||||
TOKEN_ENUM,
|
TOKEN_ENUM,
|
||||||
TOKEN_ERROR_TYPE,
|
TOKEN_ERROR_TYPE,
|
||||||
|
TOKEN_EXTERN,
|
||||||
TOKEN_FALSE,
|
TOKEN_FALSE,
|
||||||
TOKEN_FOR,
|
TOKEN_FOR,
|
||||||
TOKEN_FUNC,
|
TOKEN_FUNC,
|
||||||
@@ -555,6 +548,12 @@ typedef enum
|
|||||||
UNARYOP_DEC,
|
UNARYOP_DEC,
|
||||||
} UnaryOp;
|
} UnaryOp;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
POSTUNARYOP_INC,
|
||||||
|
POSTUNARYOP_DEC,
|
||||||
|
} PostUnaryOp;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
VARDECL_CONST = 0,
|
VARDECL_CONST = 0,
|
||||||
@@ -569,6 +568,7 @@ typedef enum
|
|||||||
VISIBLE_MODULE,
|
VISIBLE_MODULE,
|
||||||
VISIBLE_LOCAL,
|
VISIBLE_LOCAL,
|
||||||
VISIBLE_PUBLIC,
|
VISIBLE_PUBLIC,
|
||||||
|
VISIBLE_EXTERN,
|
||||||
} Visibility;
|
} Visibility;
|
||||||
|
|
||||||
|
|
||||||
@@ -583,3 +583,8 @@ typedef enum
|
|||||||
ATTR_ERROR = 1 << 6,
|
ATTR_ERROR = 1 << 6,
|
||||||
ATTR_TYPEDEF = 1 << 7
|
ATTR_TYPEDEF = 1 << 7
|
||||||
} AttributeDomains;
|
} AttributeDomains;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
CALL_CONVENTION_NORMAL = 0,
|
||||||
|
} CallConvention;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
@@ -91,19 +91,26 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e
|
|||||||
static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr)
|
static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr)
|
||||||
{
|
{
|
||||||
// TODO what about struct functions
|
// TODO what about struct functions
|
||||||
if (expr->identifier_expr.path)
|
Decl *ambiguous_decl;
|
||||||
|
Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier.string, expr->identifier_expr.path, &ambiguous_decl);
|
||||||
|
|
||||||
|
if (!decl)
|
||||||
{
|
{
|
||||||
TODO
|
SEMA_ERROR(expr->identifier_expr.identifier, "Unknown symbol '%s'.", expr->identifier_expr.identifier.string);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
Decl *decl = context_find_ident(context, expr->identifier_expr.identifier.string);
|
|
||||||
if (decl == NULL)
|
if (ambiguous_decl)
|
||||||
{
|
{
|
||||||
decl = compiler_find_symbol(expr->identifier_expr.identifier);
|
SEMA_ERROR(expr->identifier_expr.identifier, "Ambiguous symbol '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity",
|
||||||
if (decl && !decl_ok(decl)) return false;
|
expr->identifier_expr.identifier.string,
|
||||||
|
decl->module->name->module, ambiguous_decl->module->name->module);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (decl == NULL)
|
|
||||||
|
if (decl->decl_kind == DECL_FUNC && !expr->identifier_expr.path && decl->module != context->module)
|
||||||
{
|
{
|
||||||
SEMA_ERROR(expr->loc, "Unknown identifier %s.", expr->identifier_expr.identifier.string);
|
SEMA_ERROR(expr->identifier_expr.identifier, "Functions from other modules, must be prefixed with the module name");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +188,8 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
|||||||
return sema_expr_analyse_macro_call(context, to, expr, decl);
|
return sema_expr_analyse_macro_call(context, to, expr, decl);
|
||||||
case DECL_GENERIC:
|
case DECL_GENERIC:
|
||||||
return sema_expr_analyse_generic_call(context, to, expr);
|
return sema_expr_analyse_generic_call(context, to, expr);
|
||||||
|
case DECL_POISONED:
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
SEMA_ERROR(expr->loc, "The expression cannot be called.");
|
SEMA_ERROR(expr->loc, "The expression cannot be called.");
|
||||||
return false;
|
return false;
|
||||||
@@ -253,7 +262,7 @@ static inline bool sema_expr_analyse_enum_constant(Context *context, Expr *expr,
|
|||||||
if (enum_constant->name.string == name)
|
if (enum_constant->name.string == name)
|
||||||
{
|
{
|
||||||
assert(enum_constant->resolve_status == RESOLVE_DONE);
|
assert(enum_constant->resolve_status == RESOLVE_DONE);
|
||||||
expr_replace(expr, decl->enum_constant.expr);
|
expr_replace(expr, enum_constant->enum_constant.expr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,7 +344,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex
|
|||||||
if (is_pointer)
|
if (is_pointer)
|
||||||
{
|
{
|
||||||
Expr *deref = expr_new(EXPR_UNARY, expr->loc);
|
Expr *deref = expr_new(EXPR_UNARY, expr->loc);
|
||||||
deref->unary_expr.operator = TOKEN_STAR;
|
deref->unary_expr.operator = UNARYOP_DEREF;
|
||||||
deref->unary_expr.expr = expr->access_expr.parent;
|
deref->unary_expr.expr = expr->access_expr.parent;
|
||||||
deref->resolve_status = RESOLVE_DONE;
|
deref->resolve_status = RESOLVE_DONE;
|
||||||
deref->type = type;
|
deref->type = type;
|
||||||
@@ -1300,9 +1309,9 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex
|
|||||||
SEMA_ERROR(inner->loc, "Expression cannot be assigned to");
|
SEMA_ERROR(inner->loc, "Expression cannot be assigned to");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!type_is_integer(inner->type->canonical) && inner->type->canonical->type_kind == TYPE_POINTER)
|
if (!type_is_number(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER)
|
||||||
{
|
{
|
||||||
SEMA_ERROR(inner->loc, "Expression must be an integer or pointer");
|
SEMA_ERROR(inner->loc, "Expression must be a number or a pointer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
expr->type = inner->type;
|
expr->type = inner->type;
|
||||||
@@ -1399,18 +1408,18 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp
|
|||||||
|
|
||||||
switch (expr->unary_expr.operator)
|
switch (expr->unary_expr.operator)
|
||||||
{
|
{
|
||||||
case TOKEN_STAR:
|
case UNARYOP_DEREF:
|
||||||
return sema_expr_analyse_deref(context, to, expr, inner);
|
return sema_expr_analyse_deref(context, to, expr, inner);
|
||||||
case TOKEN_AMP:
|
case UNARYOP_ADDR:
|
||||||
return sema_expr_analyse_addr(context, to, expr, inner);
|
return sema_expr_analyse_addr(context, to, expr, inner);
|
||||||
case TOKEN_MINUS:
|
case UNARYOP_NEG:
|
||||||
return sema_expr_analyse_neg(context, to, expr, inner);
|
return sema_expr_analyse_neg(context, to, expr, inner);
|
||||||
case TOKEN_BIT_NOT:
|
case UNARYOP_BITNEG:
|
||||||
return sema_expr_analyse_bit_not(context, to, expr, inner);
|
return sema_expr_analyse_bit_not(context, to, expr, inner);
|
||||||
case TOKEN_NOT:
|
case UNARYOP_NOT:
|
||||||
return sema_expr_analyse_not(context, to, expr, inner);
|
return sema_expr_analyse_not(context, to, expr, inner);
|
||||||
case TOKEN_PLUSPLUS:
|
case UNARYOP_DEC:
|
||||||
case TOKEN_MINUSMINUS:
|
case UNARYOP_INC:
|
||||||
return sema_expr_analyse_incdec(context, to, expr, inner);
|
return sema_expr_analyse_incdec(context, to, expr, inner);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE
|
UNREACHABLE
|
||||||
@@ -1424,8 +1433,6 @@ static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr
|
|||||||
|
|
||||||
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
||||||
|
|
||||||
assert(expr->post_expr.operator == TOKEN_PLUSPLUS || expr->post_expr.operator == TOKEN_MINUSMINUS);
|
|
||||||
|
|
||||||
return sema_expr_analyse_incdec(context, to, expr, inner);
|
return sema_expr_analyse_incdec(context, to, expr, inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "llvm_codegen_internal.h"
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
@@ -58,6 +58,7 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl
|
|||||||
case VISIBLE_PUBLIC:
|
case VISIBLE_PUBLIC:
|
||||||
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||||
break;
|
break;
|
||||||
|
case VISIBLE_EXTERN:
|
||||||
case VISIBLE_LOCAL:
|
case VISIBLE_LOCAL:
|
||||||
LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility);
|
LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility);
|
||||||
break;
|
break;
|
||||||
@@ -125,12 +126,11 @@ void gencontext_print_llvm_ir(GenContext *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLVMValueRef gencontext_emit_alloca(GenContext *context, LLVMTypeRef type, const char *name)
|
||||||
LLVMValueRef gencontext_emit_alloca(GenContext *context, Decl *decl)
|
|
||||||
{
|
{
|
||||||
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder);
|
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder);
|
||||||
LLVMPositionBuilderBefore(context->builder, context->alloca_point);
|
LLVMPositionBuilderBefore(context->builder, context->alloca_point);
|
||||||
LLVMValueRef alloca = LLVMBuildAlloca(context->builder, BACKEND_TYPE(decl->type), decl->name.string);
|
LLVMValueRef alloca = LLVMBuildAlloca(context->builder, type, name);
|
||||||
LLVMPositionBuilderAtEnd(context->builder, current_block);
|
LLVMPositionBuilderAtEnd(context->builder, current_block);
|
||||||
return alloca;
|
return alloca;
|
||||||
}
|
}
|
||||||
@@ -161,11 +161,24 @@ void llvm_codegen(Context *context)
|
|||||||
gencontext_init(&gen_context, context);
|
gencontext_init(&gen_context, context);
|
||||||
gencontext_begin_module(&gen_context);
|
gencontext_begin_module(&gen_context);
|
||||||
// EmitDeferred()
|
// EmitDeferred()
|
||||||
|
VECEACH(context->external_symbol_list, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_extern_decl(&gen_context, context->external_symbol_list[i]);
|
||||||
|
}
|
||||||
VECEACH(context->functions, i)
|
VECEACH(context->functions, i)
|
||||||
{
|
{
|
||||||
gencontext_emit_function_decl(&gen_context, context->functions[i]);
|
gencontext_emit_function_decl(&gen_context, context->functions[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VECEACH(gen_context.generated_types, i)
|
||||||
|
{
|
||||||
|
Type *type = gen_context.generated_types[i];
|
||||||
|
type->backend_debug_type = NULL;
|
||||||
|
type->backend_type = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gencontext_print_llvm_ir(&gen_context);
|
||||||
|
|
||||||
// Starting from here we could potentially thread this:
|
// Starting from here we could potentially thread this:
|
||||||
LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate();
|
LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate();
|
||||||
LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, build_options.optimization_level);
|
LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, build_options.optimization_level);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "llvm_codegen_internal.h"
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,36 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "llvm_codegen_internal.h"
|
#include "llvm_codegen_internal.h"
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right)
|
||||||
|
{
|
||||||
|
if (use_mod)
|
||||||
|
{
|
||||||
|
return LLVMBuildAdd(context->builder, left, right, "add_mod");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO insert trap
|
||||||
|
return type_is_unsigned_integer(type)
|
||||||
|
? LLVMBuildNUWAdd(context->builder, left, right, "uadd")
|
||||||
|
: LLVMBuildNSWAdd(context->builder, left, right, "add");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_sub_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right)
|
||||||
|
{
|
||||||
|
if (use_mod)
|
||||||
|
{
|
||||||
|
return LLVMBuildSub(context->builder, left, right, "sub_mod");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO insert trap
|
||||||
|
return type_is_unsigned_integer(type)
|
||||||
|
? LLVMBuildNUWSub(context->builder, left, right, "usub")
|
||||||
|
: LLVMBuildNSWSub(context->builder, left, right, "sub");
|
||||||
|
}
|
||||||
|
|
||||||
static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr)
|
static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr)
|
||||||
{
|
{
|
||||||
LLVMValueRef index = gencontext_emit_expr(context, expr->subscript_expr.index);
|
LLVMValueRef index = gencontext_emit_expr(context, expr->subscript_expr.index);
|
||||||
@@ -38,7 +64,7 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
|||||||
case EXPR_IDENTIFIER:
|
case EXPR_IDENTIFIER:
|
||||||
return expr->identifier_expr.decl->var.backend_ref;
|
return expr->identifier_expr.decl->var.backend_ref;
|
||||||
case EXPR_UNARY:
|
case EXPR_UNARY:
|
||||||
assert(unaryop_from_token(expr->unary_expr.operator) == UNARYOP_DEREF);
|
assert(expr->unary_expr.operator == UNARYOP_DEREF);
|
||||||
return gencontext_emit_expr(context, expr->unary_expr.expr);
|
return gencontext_emit_expr(context, expr->unary_expr.expr);
|
||||||
case EXPR_ACCESS:
|
case EXPR_ACCESS:
|
||||||
return gencontext_emit_access_addr(context, expr);
|
return gencontext_emit_access_addr(context, expr);
|
||||||
@@ -130,10 +156,50 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *
|
|||||||
return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical);
|
return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff)
|
||||||
|
{
|
||||||
|
Type *type = expr->type->canonical;
|
||||||
|
LLVMTypeRef llvm_type = BACKEND_TYPE(type);
|
||||||
|
|
||||||
|
if (type->type_kind == TYPE_POINTER)
|
||||||
|
{
|
||||||
|
LLVMValueRef add = LLVMConstInt(diff < 0 ? BACKEND_TYPE(type_isize) : BACKEND_TYPE(type_usize), diff, diff < 0);
|
||||||
|
return LLVMBuildGEP2(context->builder, llvm_type, current_value, &add, 1, "ptrincdec");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is_float(type))
|
||||||
|
{
|
||||||
|
LLVMValueRef add = LLVMConstReal(llvm_type, (double)diff);
|
||||||
|
return LLVMBuildFAdd(context->builder, current_value, add, "fincdec");
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef diff_value = LLVMConstInt(llvm_type, 1, false);
|
||||||
|
return diff > 0
|
||||||
|
? gencontext_emit_add_int(context, type, use_mod, current_value, diff_value)
|
||||||
|
: gencontext_emit_sub_int(context, type, use_mod, current_value, diff_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_pre_inc_dec(GenContext *context, Expr *expr, int diff, bool use_mod)
|
||||||
|
{
|
||||||
|
LLVMValueRef addr = gencontext_emit_address(context, expr);
|
||||||
|
LLVMValueRef value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(expr->type), addr, "");
|
||||||
|
LLVMValueRef result = gencontext_emit_inc_dec_change(context, use_mod, value, expr, diff);
|
||||||
|
LLVMBuildStore(context->builder, result, addr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_post_inc_dec(GenContext *context, Expr *expr, int diff, bool use_mod)
|
||||||
|
{
|
||||||
|
LLVMValueRef addr = gencontext_emit_address(context, expr);
|
||||||
|
LLVMValueRef value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(expr->type), addr, "");
|
||||||
|
LLVMValueRef result = gencontext_emit_inc_dec_change(context, use_mod, value, expr, diff);
|
||||||
|
LLVMBuildStore(context->builder, result, addr);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
|
LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
|
||||||
{
|
{
|
||||||
UnaryOp unary_op = unaryop_from_token(expr->unary_expr.operator);
|
switch (expr->unary_expr.operator)
|
||||||
switch (unary_op)
|
|
||||||
{
|
{
|
||||||
case UNARYOP_ERROR:
|
case UNARYOP_ERROR:
|
||||||
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
|
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
|
||||||
@@ -152,20 +218,14 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
|
|||||||
case UNARYOP_DEREF:
|
case UNARYOP_DEREF:
|
||||||
return LLVMBuildLoad(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
return LLVMBuildLoad(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
||||||
case UNARYOP_INC:
|
case UNARYOP_INC:
|
||||||
TODO
|
return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, 1, false);
|
||||||
case UNARYOP_DEC:
|
case UNARYOP_DEC:
|
||||||
TODO
|
return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, -1, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static LLVMValueRef gencontext_emit_assign(GenContext *context, Expr *left, LLVMValueRef right)
|
|
||||||
{
|
|
||||||
LLVMValueRef addr = gencontext_emit_address(context, left);
|
|
||||||
return LLVMBuildStore(context->builder, right, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *expr, BinaryOp op)
|
static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *expr, BinaryOp op)
|
||||||
{
|
{
|
||||||
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
|
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
|
||||||
@@ -205,7 +265,22 @@ static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *ex
|
|||||||
return phi;
|
return phi;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool load_lhs_after_rhs, BinaryOp binary_op)
|
static inline LLVMValueRef gencontext_emit_initialization_from_expr(GenContext *context, LLVMValueRef strukt, Expr *expr)
|
||||||
|
{
|
||||||
|
assert(expr->expr_kind == EXPR_INITIALIZER_LIST);
|
||||||
|
// TODO
|
||||||
|
return strukt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_struct_value_expr(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
LLVMValueRef temp_alloc = gencontext_emit_alloca(context, BACKEND_TYPE(expr->type), "temp");
|
||||||
|
return gencontext_emit_initialization_from_expr(context, temp_alloc, expr->struct_value_expr.init_expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVMValueRef lhs_addr, BinaryOp binary_op)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
|
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
|
||||||
@@ -218,17 +293,17 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool
|
|||||||
|
|
||||||
LLVMValueRef lhs_value;
|
LLVMValueRef lhs_value;
|
||||||
LLVMValueRef rhs_value;
|
LLVMValueRef rhs_value;
|
||||||
if (load_lhs_after_rhs)
|
if (lhs_addr)
|
||||||
{
|
{
|
||||||
rhs_value = gencontext_emit_expr(context, rhs);
|
lhs_value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(lhs->type), lhs_addr, "");
|
||||||
lhs_value = gencontext_emit_expr(context, lhs);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lhs_value = gencontext_emit_expr(context, lhs);
|
lhs_value = gencontext_emit_expr(context, lhs);
|
||||||
rhs_value = gencontext_emit_expr(context, rhs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rhs_value = gencontext_emit_expr(context, rhs);
|
||||||
|
|
||||||
bool is_float = type_is_float(type);
|
bool is_float = type_is_float(type);
|
||||||
|
|
||||||
switch (binary_op)
|
switch (binary_op)
|
||||||
@@ -249,6 +324,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool
|
|||||||
case BINARYOP_MULT_MOD:
|
case BINARYOP_MULT_MOD:
|
||||||
return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
|
return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
|
||||||
case BINARYOP_SUB:
|
case BINARYOP_SUB:
|
||||||
|
case BINARYOP_SUB_MOD:
|
||||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||||
{
|
{
|
||||||
if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff");
|
if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff");
|
||||||
@@ -256,35 +332,16 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool
|
|||||||
return LLVMBuildGEP2(context->builder, BACKEND_TYPE(lhs->type), lhs_value, &rhs_value, 1, "ptrsub");
|
return LLVMBuildGEP2(context->builder, BACKEND_TYPE(lhs->type), lhs_value, &rhs_value, 1, "ptrsub");
|
||||||
}
|
}
|
||||||
if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub");
|
if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub");
|
||||||
// TODO insert trap
|
return gencontext_emit_sub_int(context, lhs->type->canonical, binary_op == BINARYOP_SUB_MOD, lhs_value, rhs_value);
|
||||||
if (type_is_unsigned_integer(lhs->type->canonical))
|
|
||||||
{
|
|
||||||
return LLVMBuildNUWSub(context->builder, lhs_value, rhs_value, "usub");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return LLVMBuildNSWSub(context->builder, lhs_value, rhs_value, "sub");
|
|
||||||
}
|
|
||||||
case BINARYOP_SUB_MOD:
|
|
||||||
return LLVMBuildSub(context->builder, lhs_value, rhs_value, "submod");
|
|
||||||
case BINARYOP_ADD:
|
case BINARYOP_ADD:
|
||||||
|
case BINARYOP_ADD_MOD:
|
||||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||||
{
|
{
|
||||||
assert(type_is_integer(rhs->type->canonical));
|
assert(type_is_integer(rhs->type->canonical));
|
||||||
return LLVMBuildGEP2(context->builder, BACKEND_TYPE(lhs->type), lhs_value, &rhs_value, 1, "ptradd");
|
return LLVMBuildGEP2(context->builder, BACKEND_TYPE(lhs->type), lhs_value, &rhs_value, 1, "ptradd");
|
||||||
}
|
}
|
||||||
if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd");
|
if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd");
|
||||||
// TODO insert trap
|
return gencontext_emit_add_int(context, lhs->type->canonical, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value);
|
||||||
if (type_is_unsigned_integer(lhs->type->canonical))
|
|
||||||
{
|
|
||||||
return LLVMBuildNUWAdd(context->builder, lhs_value, rhs_value, "uadd");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return LLVMBuildNSWAdd(context->builder, lhs_value, rhs_value, "sadd");
|
|
||||||
}
|
|
||||||
case BINARYOP_ADD_MOD:
|
|
||||||
return LLVMBuildAdd(context->builder, lhs_value, rhs_value, "addmod");
|
|
||||||
case BINARYOP_DIV:
|
case BINARYOP_DIV:
|
||||||
if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv");
|
if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv");
|
||||||
return type_is_unsigned(type)
|
return type_is_unsigned(type)
|
||||||
@@ -359,32 +416,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool
|
|||||||
|
|
||||||
LLVMValueRef gencontext_emit_post_unary_expr(GenContext *context, Expr *expr)
|
LLVMValueRef gencontext_emit_post_unary_expr(GenContext *context, Expr *expr)
|
||||||
{
|
{
|
||||||
Expr *lhs = expr->unary_expr.expr;
|
return gencontext_emit_post_inc_dec(context, expr->post_expr.expr, expr->post_expr.operator == POSTUNARYOP_INC ? 1 : -1, false);
|
||||||
LLVMValueRef value = gencontext_emit_expr(context, lhs);
|
|
||||||
bool is_add = expr->post_expr.operator == TOKEN_PLUSPLUS;
|
|
||||||
/* if (expr->type->canonical->type_kind == TYPE_POINTER)
|
|
||||||
{
|
|
||||||
LLVMValueRef offset = LLVMConstInt(is_add ? type_isize->backend_type : type_usize->backend_type, is_add ? 1 : -1, true);
|
|
||||||
LLVMBuildStore(context->builder, LLVMBuildGEP2(context->builder, gencontext_get_llvm_type(context, expr->type->canonical), value, &offset, 1, "postunary"), gencontext_emit_address(context, left);)
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
if (type_is_float(expr->type->canonical))
|
|
||||||
{
|
|
||||||
LLVMValueRef offset = LLVMConstReal(LLVMTypeOf(value), is_add ? 1);
|
|
||||||
LLVMBuildAdd(context->builder, value, offset, name);
|
|
||||||
}
|
|
||||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
|
||||||
{
|
|
||||||
rhs_value = LLVMBuildNeg(context->builder, rhs_value, "");
|
|
||||||
return LLVMBuildGEP2(context->builder, , lhs_value, &rhs_value, 1, "ptrsub");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const char *name = is_add ? "add" : "sub";
|
|
||||||
LLVMValueRef constVal = LLVMConstInt(LLVMTypeOf(value), 1, !is_add);
|
|
||||||
LLVMValueRef result = is_add ? LLVMBuildAdd(context->builder, value, constVal, name)
|
|
||||||
: LLVMBuildSub(context->builder, value, constVal, name);
|
|
||||||
LLVMBuildStore(context->builder, result, gencontext_emit_address(context, lhs));
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
|
static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
|
||||||
@@ -394,17 +426,23 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
|
|||||||
{
|
{
|
||||||
BinaryOp base_op = binaryop_assign_base_op(binary_op);
|
BinaryOp base_op = binaryop_assign_base_op(binary_op);
|
||||||
assert(base_op != BINARYOP_ERROR);
|
assert(base_op != BINARYOP_ERROR);
|
||||||
LLVMValueRef value = gencontext_emit_binary(context, expr, true, base_op);
|
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
|
||||||
gencontext_emit_assign(context, expr->binary_expr.left, value);
|
LLVMValueRef value = gencontext_emit_binary(context, expr, addr, base_op);
|
||||||
|
LLVMBuildStore(context->builder, value, addr);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
if (binary_op == BINARYOP_ASSIGN)
|
if (binary_op == BINARYOP_ASSIGN)
|
||||||
{
|
{
|
||||||
|
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
|
||||||
|
if (expr->binary_expr.right->expr_kind == EXPR_INITIALIZER_LIST)
|
||||||
|
{
|
||||||
|
return gencontext_emit_initialization_from_expr(context, addr, expr->binary_expr.right);
|
||||||
|
}
|
||||||
LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right);
|
LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right);
|
||||||
gencontext_emit_assign(context, expr->binary_expr.left, value);
|
LLVMBuildStore(context->builder, value, addr);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return gencontext_emit_binary(context, expr, false, binary_op);
|
return gencontext_emit_binary(context, expr, NULL, binary_op);
|
||||||
}
|
}
|
||||||
|
|
||||||
LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr)
|
LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr)
|
||||||
@@ -515,13 +553,19 @@ LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
|
|||||||
{
|
{
|
||||||
values[i] = gencontext_emit_expr(context, expr->call_expr.arguments[i]);
|
values[i] = gencontext_emit_expr(context, expr->call_expr.arguments[i]);
|
||||||
}
|
}
|
||||||
LLVMValueRef func = expr->call_expr.function->identifier_expr.decl->func.backend_value;
|
|
||||||
|
|
||||||
return LLVMBuildCall2(context->builder, LLVMTYPE(expr->call_expr.function->identifier_expr.decl->type), func, values, args, "call");
|
|
||||||
|
Decl *function = expr->call_expr.function->identifier_expr.decl;
|
||||||
|
|
||||||
|
LLVMValueRef func = function->func.backend_value;
|
||||||
|
LLVMTypeRef func_type = BACKEND_TYPE(function->type);
|
||||||
|
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "call");
|
||||||
/*
|
/*
|
||||||
if (fndcl->flags & FlagSystem) {
|
if (function->func.function_signature.convention)
|
||||||
LLVMSetInstructionCallConv(fncallret, LLVMX86StdcallCallConv);
|
{
|
||||||
|
LLVMSetFunctionCallConv(call, LLVMX86StdcallCallConv);
|
||||||
}*/
|
}*/
|
||||||
|
return call;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -545,14 +589,22 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline LLVMValueRef gencontext_emit_struct_value_expr(GenContext *context, Expr *expr)
|
|
||||||
{
|
|
||||||
TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr)
|
static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr)
|
||||||
{
|
{
|
||||||
TODO
|
LLVMValueRef value = LLVMGetUndef(LLVMTYPE(expr->type));
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (expr->initializer_expr)
|
||||||
|
expr->type.
|
||||||
|
else if (littype->tag == StructTag) {
|
||||||
|
LLVMValueRef strval = LLVMGetUndef(genlType(gen, littype));
|
||||||
|
unsigned int pos = 0;
|
||||||
|
for (nodesFor(lit->args, cnt, nodesp))
|
||||||
|
strval = LLVMBuildInsertValue(gen->builder, strval, genlExpr(gen, *nodesp), pos++, "literal");
|
||||||
|
return strval;
|
||||||
|
}
|
||||||
|
TODO*/
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *context, Expr *expr)
|
static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *context, Expr *expr)
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
#include "llvm_codegen_internal.h"
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static char *mangle_name(char *buffer, Decl *decl)
|
|
||||||
{
|
|
||||||
sprintf(buffer, "%*s", decl->name.span.length, decl->name.start);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool gencontext_check_block_branch_emit(GenContext *context)
|
bool gencontext_check_block_branch_emit(GenContext *context)
|
||||||
{
|
{
|
||||||
@@ -81,7 +78,7 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un
|
|||||||
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
||||||
|
|
||||||
// Allocate room on stack and copy.
|
// Allocate room on stack and copy.
|
||||||
decl->var.backend_ref = gencontext_emit_alloca(context, decl);
|
decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name.string);
|
||||||
LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->var.backend_ref);
|
LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->var.backend_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,10 +145,8 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
|||||||
void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
||||||
{
|
{
|
||||||
assert(decl->decl_kind == DECL_FUNC);
|
assert(decl->decl_kind == DECL_FUNC);
|
||||||
char workbuf[2048] = { '\0' };
|
|
||||||
char *external_name = mangle_name(workbuf, decl);
|
|
||||||
// Resolve function backend type for function.
|
// Resolve function backend type for function.
|
||||||
decl->func.backend_value = LLVMAddFunction(context->module, external_name,
|
decl->func.backend_value = LLVMAddFunction(context->module, decl->external_name,
|
||||||
BACKEND_TYPE(decl->type));
|
BACKEND_TYPE(decl->type));
|
||||||
|
|
||||||
// Specify appropriate storage class, visibility and call convention
|
// Specify appropriate storage class, visibility and call convention
|
||||||
@@ -173,6 +168,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
|||||||
switch (decl->visibility)
|
switch (decl->visibility)
|
||||||
{
|
{
|
||||||
case VISIBLE_LOCAL:
|
case VISIBLE_LOCAL:
|
||||||
|
case VISIBLE_EXTERN:
|
||||||
flags |= LLVMDIFlagPrivate;
|
flags |= LLVMDIFlagPrivate;
|
||||||
break;
|
break;
|
||||||
case VISIBLE_MODULE:
|
case VISIBLE_MODULE:
|
||||||
@@ -199,3 +195,47 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
|||||||
}
|
}
|
||||||
if (decl->func.body) gencontext_emit_function_body(context, decl);
|
if (decl->func.body) gencontext_emit_function_body(context, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_extern_decl(GenContext *context, Decl *decl)
|
||||||
|
{
|
||||||
|
switch (decl->decl_kind)
|
||||||
|
{
|
||||||
|
case DECL_POISONED:
|
||||||
|
UNREACHABLE;
|
||||||
|
case DECL_FUNC:
|
||||||
|
decl->func.backend_value = LLVMAddFunction(context->module, decl->external_name,
|
||||||
|
BACKEND_TYPE(decl->type));
|
||||||
|
LLVMSetVisibility(decl->func.backend_value, LLVMDefaultVisibility);
|
||||||
|
break;
|
||||||
|
case DECL_VAR:
|
||||||
|
decl->var.backend_ref = LLVMAddGlobal(context->module, BACKEND_TYPE(decl->type), decl->external_name);
|
||||||
|
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||||
|
break;
|
||||||
|
case DECL_TYPEDEF:
|
||||||
|
UNREACHABLE
|
||||||
|
case DECL_ENUM_CONSTANT:
|
||||||
|
TODO
|
||||||
|
case DECL_STRUCT:
|
||||||
|
case DECL_UNION:
|
||||||
|
BACKEND_TYPE(decl->type);
|
||||||
|
break;
|
||||||
|
case DECL_ENUM:
|
||||||
|
TODO
|
||||||
|
case DECL_ERROR:
|
||||||
|
TODO
|
||||||
|
case DECL_ERROR_CONSTANT:
|
||||||
|
TODO
|
||||||
|
case DECL_ARRAY_VALUE:
|
||||||
|
case DECL_IMPORT:
|
||||||
|
case DECL_MACRO:
|
||||||
|
case DECL_MULTI_DECL:
|
||||||
|
case DECL_GENERIC:
|
||||||
|
case DECL_CT_IF:
|
||||||
|
case DECL_CT_ELSE:
|
||||||
|
case DECL_CT_ELIF:
|
||||||
|
case DECL_ATTRIBUTE:
|
||||||
|
case DECL_THROWS:
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
#include <llvm-c/Core.h>
|
#include <llvm-c/Core.h>
|
||||||
@@ -65,6 +65,7 @@ typedef struct
|
|||||||
BreakContinue break_continue_stack[BREAK_STACK_MAX];
|
BreakContinue break_continue_stack[BREAK_STACK_MAX];
|
||||||
size_t break_continue_stack_index;
|
size_t break_continue_stack_index;
|
||||||
LLVMTypeRef error_type;
|
LLVMTypeRef error_type;
|
||||||
|
Type **generated_types;
|
||||||
} GenContext;
|
} GenContext;
|
||||||
|
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr);
|
|||||||
LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type);
|
LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type);
|
||||||
void gencontext_emit_debug_location(GenContext *context, SourceRange location);
|
void gencontext_emit_debug_location(GenContext *context, SourceRange location);
|
||||||
LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type);
|
LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type);
|
||||||
LLVMValueRef gencontext_emit_alloca(GenContext *context, Decl *decl);
|
LLVMValueRef gencontext_emit_alloca(GenContext *context, LLVMTypeRef type, const char *name);
|
||||||
void gencontext_emit_compound_stmt(GenContext *context, Ast *ast);
|
void gencontext_emit_compound_stmt(GenContext *context, Ast *ast);
|
||||||
void gencontext_emit_block(GenContext *context, LLVMBasicBlockRef next_block);
|
void gencontext_emit_block(GenContext *context, LLVMBasicBlockRef next_block);
|
||||||
void gencontext_emit_br(GenContext *context, LLVMBasicBlockRef next_block);
|
void gencontext_emit_br(GenContext *context, LLVMBasicBlockRef next_block);
|
||||||
@@ -87,6 +88,7 @@ static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context
|
|||||||
}
|
}
|
||||||
|
|
||||||
void gencontext_emit_function_decl(GenContext *context, Decl *decl);
|
void gencontext_emit_function_decl(GenContext *context, Decl *decl);
|
||||||
|
void gencontext_emit_extern_decl(GenContext *context, Decl *decl);
|
||||||
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr);
|
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr);
|
||||||
#define LLVMTYPE(type) type->backend_type
|
#define LLVMTYPE(type) type->backend_type
|
||||||
static inline LLVMValueRef gencontext_load_expr(GenContext *context, LLVMValueRef value)
|
static inline LLVMValueRef gencontext_load_expr(GenContext *context, LLVMValueRef value)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "llvm_codegen_internal.h"
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
@@ -37,13 +37,14 @@ static inline LLVMTypeRef gencontext_create_basic_llvm_type(GenContext *context,
|
|||||||
|
|
||||||
static inline void gencontext_init_basic_llvm_type(GenContext *context, Type *type)
|
static inline void gencontext_init_basic_llvm_type(GenContext *context, Type *type)
|
||||||
{
|
{
|
||||||
|
vec_add(context->generated_types, type);
|
||||||
type->backend_type = gencontext_create_basic_llvm_type(context, type);
|
type->backend_type = gencontext_create_basic_llvm_type(context, type);
|
||||||
}
|
}
|
||||||
void gencontext_begin_module(GenContext *context)
|
void gencontext_begin_module(GenContext *context)
|
||||||
{
|
{
|
||||||
assert(!context->module && "Expected no module");
|
assert(!context->module && "Expected no module");
|
||||||
const char *full_path = context->ast_context->file->full_path;
|
const char *full_path = context->ast_context->file->full_path;
|
||||||
char *mangled_module_name = strformat("module:%s", context->ast_context->module->name);
|
char *mangled_module_name = strformat("%s-%s", context->ast_context->module->name->module, context->ast_context->file->name);
|
||||||
context->module = LLVMModuleCreateWithNameInContext(mangled_module_name, context->context);
|
context->module = LLVMModuleCreateWithNameInContext(mangled_module_name, context->context);
|
||||||
LLVMSetModuleDataLayout(context->module, target_data_layout());
|
LLVMSetModuleDataLayout(context->module, target_data_layout());
|
||||||
LLVMSetSourceFileName(context->module, full_path, strlen(context->ast_context->file->full_path));
|
LLVMSetSourceFileName(context->module, full_path, strlen(context->ast_context->file->full_path));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "llvm_codegen_internal.h"
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
|||||||
{
|
{
|
||||||
Decl *decl = ast->declare_stmt;
|
Decl *decl = ast->declare_stmt;
|
||||||
|
|
||||||
decl->var.backend_ref = gencontext_emit_alloca(context, decl);
|
decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name.string);
|
||||||
// TODO NRVO
|
// TODO NRVO
|
||||||
// TODO debug info
|
// TODO debug info
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "llvm_codegen_internal.h"
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
|||||||
VECADD(types, BACKEND_TYPE(decl->strukt.members[i]->type));
|
VECADD(types, BACKEND_TYPE(decl->strukt.members[i]->type));
|
||||||
}
|
}
|
||||||
// TODO fix name.
|
// TODO fix name.
|
||||||
LLVMTypeRef type = LLVMStructCreateNamed(LLVMCONTEXT(context), decl->name.string);
|
LLVMTypeRef type = LLVMStructCreateNamed(LLVMCONTEXT(context), decl->external_name);
|
||||||
LLVMStructSetBody(type, types, vec_size(types), decl->is_packed);
|
LLVMStructSetBody(type, types, vec_size(types), decl->is_packed);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
|||||||
max_type = type;
|
max_type = type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LLVMTypeRef type = LLVMStructCreateNamed(LLVMCONTEXT(context), decl->name.string);
|
LLVMTypeRef type = LLVMStructCreateNamed(LLVMCONTEXT(context), decl->external_name);
|
||||||
LLVMStructSetBody(type, &max_type, 1, false);
|
LLVMStructSetBody(type, &max_type, 1, false);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@@ -91,6 +91,7 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
|||||||
static inline LLVMTypeRef gencontext_create_llvm_type_from_ptr(GenContext *context, Type *type)
|
static inline LLVMTypeRef gencontext_create_llvm_type_from_ptr(GenContext *context, Type *type)
|
||||||
{
|
{
|
||||||
LLVMTypeRef base_llvm_type = BACKEND_TYPE(type->pointer);
|
LLVMTypeRef base_llvm_type = BACKEND_TYPE(type->pointer);
|
||||||
|
vec_add(context->generated_types, type);
|
||||||
|
|
||||||
if (type->canonical != type)
|
if (type->canonical != type)
|
||||||
{
|
{
|
||||||
@@ -104,6 +105,7 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_array(GenContext *con
|
|||||||
{
|
{
|
||||||
LLVMTypeRef base_llvm_type = BACKEND_TYPE(type->array.base);
|
LLVMTypeRef base_llvm_type = BACKEND_TYPE(type->array.base);
|
||||||
|
|
||||||
|
vec_add(context->generated_types, type);
|
||||||
|
|
||||||
if (type->canonical != type)
|
if (type->canonical != type)
|
||||||
{
|
{
|
||||||
@@ -134,7 +136,13 @@ LLVMTypeRef gencontext_create_llvm_func_type(GenContext *context, Type *type)
|
|||||||
|
|
||||||
LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type)
|
LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type)
|
||||||
{
|
{
|
||||||
if (type->backend_type) return type->backend_type;
|
if (type->backend_type)
|
||||||
|
{
|
||||||
|
assert(LLVMGetTypeContext(type->backend_type) == context->context);
|
||||||
|
return type->backend_type;
|
||||||
|
}
|
||||||
|
vec_add(context->generated_types, type);
|
||||||
|
|
||||||
DEBUG_LOG("Generating type %s", type->name);
|
DEBUG_LOG("Generating type %s", type->name);
|
||||||
switch (type->type_kind)
|
switch (type->type_kind)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,63 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
Decl *module_find_symbol(Module *module, const char *symbol)
|
Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch search)
|
||||||
{
|
{
|
||||||
return stable_get(&module->symbols, symbol);
|
Decl *decl = stable_get(&module->symbols, symbol);
|
||||||
|
if (decl)
|
||||||
|
{
|
||||||
|
switch (decl->visibility)
|
||||||
|
{
|
||||||
|
case VISIBLE_LOCAL:
|
||||||
|
case VISIBLE_EXTERN:
|
||||||
|
decl = NULL;
|
||||||
|
break;
|
||||||
|
case VISIBLE_MODULE:
|
||||||
|
if (search == MODULE_SYMBOL_SEARCH_EXTERNAL) decl = NULL;
|
||||||
|
break;
|
||||||
|
case VISIBLE_PUBLIC:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!decl)
|
||||||
|
{
|
||||||
|
if (search == MODULE_SYMBOL_SEARCH_THIS) search = MODULE_SYMBOL_SEARCH_PARENT;
|
||||||
|
VECEACH (module->sub_modules, i)
|
||||||
|
{
|
||||||
|
if ((decl = module_find_symbol(module->sub_modules[i], symbol, search))) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path *path_create_from_string(const char *string, size_t len, SourceRange span)
|
||||||
|
{
|
||||||
|
Path *path = CALLOCS(Path);
|
||||||
|
path->span = span;
|
||||||
|
TokenType type = TOKEN_IDENT;
|
||||||
|
path->module = symtab_add(string, len, fnv1a(string, len), &type);
|
||||||
|
path->len = len;
|
||||||
|
if (type != TOKEN_IDENT)
|
||||||
|
{
|
||||||
|
sema_error_range(span, "A module name was expected here.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path *path_find_parent_path(Path *path)
|
||||||
|
{
|
||||||
|
const char *last_scope_chars = strrchr(path->module, ':');
|
||||||
|
// No parent
|
||||||
|
if (!last_scope_chars) return NULL;
|
||||||
|
|
||||||
|
Path *parent_path = path_create_from_string(path->module, last_scope_chars - path->module - 1, INVALID_RANGE);
|
||||||
|
|
||||||
|
// Should never fail.
|
||||||
|
assert(parent_path);
|
||||||
|
|
||||||
|
return parent_path;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
@@ -140,6 +140,7 @@ static void recover_top_level(void)
|
|||||||
case TOKEN_UNION:
|
case TOKEN_UNION:
|
||||||
case TOKEN_ENUM:
|
case TOKEN_ENUM:
|
||||||
case TOKEN_MACRO:
|
case TOKEN_MACRO:
|
||||||
|
case TOKEN_EXTERN:
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
advance();
|
advance();
|
||||||
@@ -188,6 +189,38 @@ do { if (!try_consume(TOKEN_COMMA) && tok.type != TOKEN_RPAREN) { \
|
|||||||
SEMA_ERROR(tok, "Expected ',' or ')'"); return _res; } } while(0)
|
SEMA_ERROR(tok, "Expected ',' or ')'"); return _res; } } while(0)
|
||||||
|
|
||||||
|
|
||||||
|
static inline Path *parse_module_path()
|
||||||
|
{
|
||||||
|
assert(tok.type == TOKEN_IDENT);
|
||||||
|
char *scratch_ptr = current_context->path_scratch;
|
||||||
|
size_t offset = 0;
|
||||||
|
SourceRange span = tok.span;
|
||||||
|
memcpy(scratch_ptr, tok.start, tok.span.length);
|
||||||
|
offset += tok.span.length;
|
||||||
|
SourceRange last_range;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
last_range = tok.span;
|
||||||
|
if (!try_consume(TOKEN_IDENT))
|
||||||
|
{
|
||||||
|
SEMA_ERROR(tok, "Each '::' must be followed by a regular lower case sub module name.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!try_consume(TOKEN_SCOPE))
|
||||||
|
{
|
||||||
|
span = source_range_from_ranges(span, last_range);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scratch_ptr[offset++] = ':';
|
||||||
|
scratch_ptr[offset++] = ':';
|
||||||
|
memcpy(scratch_ptr + offset, tok.start, tok.span.length);
|
||||||
|
offset += tok.span.length;
|
||||||
|
}
|
||||||
|
scratch_ptr[offset] = '\0';
|
||||||
|
return path_create_from_string(scratch_ptr, offset, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static Ast* parse_compound_stmt()
|
static Ast* parse_compound_stmt()
|
||||||
{
|
{
|
||||||
CONSUME_OR(TOKEN_LBRACE, &poisoned_ast);
|
CONSUME_OR(TOKEN_LBRACE, &poisoned_ast);
|
||||||
@@ -213,23 +246,44 @@ static Ast* parse_function_block()
|
|||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Path *parse_path(void)
|
|
||||||
|
|
||||||
|
static Path *parse_path_prefix()
|
||||||
{
|
{
|
||||||
if (tok.type != TOKEN_IDENT || next_tok.type != TOKEN_SCOPE) return NULL;
|
if (tok.type != TOKEN_IDENT || next_tok.type != TOKEN_SCOPE) return NULL;
|
||||||
|
|
||||||
Path *path = malloc_arena(sizeof(Path));
|
char *scratch_ptr = current_context->path_scratch;
|
||||||
memset(path, 0, sizeof(Path));
|
size_t offset = 0;
|
||||||
|
|
||||||
path->sub_module = tok;
|
Path *path = CALLOCS(Path);
|
||||||
|
path->span = tok.span;
|
||||||
if (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE)
|
memcpy(scratch_ptr, tok.start, tok.span.length);
|
||||||
|
offset += tok.span.length;
|
||||||
|
SourceRange last_range = tok.span;
|
||||||
|
advance();
|
||||||
|
advance();
|
||||||
|
while (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE)
|
||||||
{
|
{
|
||||||
|
last_range = tok.span;
|
||||||
advance();
|
advance();
|
||||||
advance();
|
advance();
|
||||||
path->module = path->sub_module;
|
scratch_ptr[offset++] = ':';
|
||||||
path->sub_module = tok;
|
scratch_ptr[offset++] = ':';
|
||||||
|
memcpy(scratch_ptr + offset, tok.start, tok.span.length);
|
||||||
|
offset += tok.span.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TokenType type = TOKEN_IDENT;
|
||||||
|
path->span = source_range_from_ranges(path->span, last_range);
|
||||||
|
path->module = symtab_add(scratch_ptr, offset, fnv1a(scratch_ptr, offset), &type);
|
||||||
|
if (type != TOKEN_IDENT)
|
||||||
|
{
|
||||||
|
sema_error_range(path->span, "A module name was expected here.");
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
path->len = offset;
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +311,7 @@ static Path *parse_path(void)
|
|||||||
*/
|
*/
|
||||||
static inline TypeInfo *parse_base_type(void)
|
static inline TypeInfo *parse_base_type(void)
|
||||||
{
|
{
|
||||||
Path *path = parse_path();
|
Path *path = parse_path_prefix();
|
||||||
if (path)
|
if (path)
|
||||||
{
|
{
|
||||||
TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER);
|
TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||||
@@ -1352,6 +1406,7 @@ static Ast *parse_stmt(void)
|
|||||||
case TOKEN_MACRO:
|
case TOKEN_MACRO:
|
||||||
case TOKEN_MODULE:
|
case TOKEN_MODULE:
|
||||||
case TOKEN_PUBLIC:
|
case TOKEN_PUBLIC:
|
||||||
|
case TOKEN_EXTERN:
|
||||||
case TOKEN_STRUCT:
|
case TOKEN_STRUCT:
|
||||||
case TOKEN_THROWS:
|
case TOKEN_THROWS:
|
||||||
case TOKEN_TYPEDEF:
|
case TOKEN_TYPEDEF:
|
||||||
@@ -1454,8 +1509,8 @@ static inline bool parse_optional_module_params(Token **tokens)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* module
|
* module
|
||||||
* : MODULE IDENT ';'
|
* : MODULE path ';'
|
||||||
* | MODULE IDENT '(' module_params ')' ';'
|
* | MODULE path '(' module_params ')' ';'
|
||||||
*/
|
*/
|
||||||
static inline void parse_module(void)
|
static inline void parse_module(void)
|
||||||
{
|
{
|
||||||
@@ -1466,12 +1521,16 @@ static inline void parse_module(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Token name = tok;
|
Path *path = parse_module_path();
|
||||||
|
|
||||||
// Expect the module name
|
// Expect the module name
|
||||||
if (!consume(TOKEN_IDENT, "After 'module' the name of the module should be placed."))
|
if (!path)
|
||||||
{
|
{
|
||||||
context_set_module(current_context, (Token) {.type = TOKEN_INVALID_TOKEN}, NULL);
|
path = CALLOCS(Path);
|
||||||
|
path->len = strlen("INVALID");
|
||||||
|
path->module = "INVALID";
|
||||||
|
path->span = INVALID_RANGE;
|
||||||
|
context_set_module(current_context, path, NULL);
|
||||||
recover_top_level();
|
recover_top_level();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1480,11 +1539,11 @@ static inline void parse_module(void)
|
|||||||
Token *generic_parameters = NULL;
|
Token *generic_parameters = NULL;
|
||||||
if (!parse_optional_module_params(&generic_parameters))
|
if (!parse_optional_module_params(&generic_parameters))
|
||||||
{
|
{
|
||||||
context_set_module(current_context, name, generic_parameters);
|
context_set_module(current_context, path, generic_parameters);
|
||||||
recover_top_level();
|
recover_top_level();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context_set_module(current_context, name, generic_parameters);
|
context_set_module(current_context, path, generic_parameters);
|
||||||
TRY_CONSUME_EOS_OR();
|
TRY_CONSUME_EOS_OR();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1518,48 +1577,89 @@ static inline bool parse_macro_parameter_list(Expr*** result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* import_selective
|
||||||
|
* : import_spec
|
||||||
|
* | import_spec AS import_spec
|
||||||
|
* ;
|
||||||
|
*
|
||||||
|
* import_spec
|
||||||
|
* : IDENT
|
||||||
|
* | TYPE
|
||||||
|
* | MACRO
|
||||||
|
* | CONST
|
||||||
|
* ;
|
||||||
|
*
|
||||||
|
* @return true if import succeeded
|
||||||
|
*/
|
||||||
|
static inline bool parse_import_selective(Path *path)
|
||||||
|
{
|
||||||
|
if (!token_is_symbol(tok.type))
|
||||||
|
{
|
||||||
|
SEMA_ERROR(tok, "Expected a symbol name here, the syntax is 'import <module> : <symbol>'.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Token symbol = tok;
|
||||||
|
advance();
|
||||||
|
// Alias?
|
||||||
|
if (!try_consume(TOKEN_AS))
|
||||||
|
{
|
||||||
|
return context_add_import(current_context, path, symbol, EMPTY_TOKEN);
|
||||||
|
}
|
||||||
|
if (tok.type != symbol.type)
|
||||||
|
{
|
||||||
|
if (!token_is_symbol(tok.type))
|
||||||
|
{
|
||||||
|
SEMA_ERROR(tok, "Expected a symbol name here, the syntax is 'import <module> : <symbol> AS <alias>'.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SEMA_ERROR(tok, "Expected the alias be the same type of name as the symbol aliased.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Token alias = tok;
|
||||||
|
advance();
|
||||||
|
return context_add_import(current_context, path, symbol, alias);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* import
|
* import
|
||||||
* : IMPORT IDENT ';'
|
* : IMPORT PATH ';'
|
||||||
* | IMPORT IDENT AS IDENT ';'
|
* | IMPORT PATH ':' import_selective ';'
|
||||||
* | IMPORT IDENT AS IDENT LOCAL ';'
|
* | IMPORT IDENT AS IDENT LOCAL ';'
|
||||||
* | IMPORT IDENT LOCAL ';'
|
* | IMPORT IDENT LOCAL ';'
|
||||||
*
|
*
|
||||||
* // TODO macro parameters (after grammar is updated)
|
* import_list
|
||||||
|
* : import_selective
|
||||||
|
* | import_list ',' import_selective
|
||||||
|
* ;
|
||||||
*
|
*
|
||||||
* @return true if import succeeded
|
* @return true if import succeeded
|
||||||
*/
|
*/
|
||||||
static inline bool parse_import()
|
static inline bool parse_import()
|
||||||
{
|
{
|
||||||
|
|
||||||
advance_and_verify(TOKEN_IMPORT);
|
advance_and_verify(TOKEN_IMPORT);
|
||||||
|
|
||||||
Token module_name = tok;
|
if (tok.type != TOKEN_IDENT)
|
||||||
|
|
||||||
TRY_CONSUME_OR(TOKEN_IDENT, "Import statement should be followed by the name of the module to import.", false);
|
|
||||||
|
|
||||||
Expr **generic_parameters = NULL;
|
|
||||||
|
|
||||||
/* MACRO params here
|
|
||||||
if (tok.type == TOKEN_LPAREN)
|
|
||||||
{
|
{
|
||||||
if (!parse_macro_parameter_list(&generic_parameters)) return false;
|
SEMA_ERROR(tok, "Import statement should be followed by the name of the module to import.");
|
||||||
}*/
|
return false;
|
||||||
|
|
||||||
Token alias = {};
|
|
||||||
ImportType import_type = IMPORT_TYPE_FULL;
|
|
||||||
if (try_consume(TOKEN_AS))
|
|
||||||
{
|
|
||||||
alias = tok;
|
|
||||||
if (!consume_ident("alias")) return false;
|
|
||||||
import_type = IMPORT_TYPE_ALIAS;
|
|
||||||
}
|
}
|
||||||
if (try_consume(TOKEN_LOCAL))
|
|
||||||
|
Path *path = parse_module_path();
|
||||||
|
if (tok.type == TOKEN_COLON)
|
||||||
{
|
{
|
||||||
import_type = import_type == IMPORT_TYPE_ALIAS ? IMPORT_TYPE_ALIAS_LOCAL : IMPORT_TYPE_LOCAL;
|
while (1)
|
||||||
|
{
|
||||||
|
if (!parse_import_selective(path)) return false;
|
||||||
|
if (!try_consume(TOKEN_COMMA)) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context_add_import(current_context, path, EMPTY_TOKEN, EMPTY_TOKEN);
|
||||||
}
|
}
|
||||||
context_add_import(current_context, module_name, alias, import_type, generic_parameters);
|
|
||||||
TRY_CONSUME_EOS_OR(false);
|
TRY_CONSUME_EOS_OR(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1707,7 +1807,7 @@ static inline bool parse_attributes(Decl *parent_decl)
|
|||||||
|
|
||||||
while (tok.type == TOKEN_AT_IDENT || (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE))
|
while (tok.type == TOKEN_AT_IDENT || (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE))
|
||||||
{
|
{
|
||||||
Path *path = parse_path();
|
Path *path = parse_path_prefix();
|
||||||
|
|
||||||
Attr *attr = malloc_arena(sizeof(Attr));
|
Attr *attr = malloc_arena(sizeof(Attr));
|
||||||
|
|
||||||
@@ -1894,7 +1994,7 @@ static inline Decl *parse_generics_declaration(Visibility visibility)
|
|||||||
{
|
{
|
||||||
rtype = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl);
|
rtype = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl);
|
||||||
}
|
}
|
||||||
Path *path = parse_path();
|
Path *path = parse_path_prefix();
|
||||||
Decl *decl = decl_new(DECL_GENERIC, tok, visibility);
|
Decl *decl = decl_new(DECL_GENERIC, tok, visibility);
|
||||||
decl->generic_decl.path = path;
|
decl->generic_decl.path = path;
|
||||||
if (!consume_ident("generic function name")) return &poisoned_decl;
|
if (!consume_ident("generic function name")) return &poisoned_decl;
|
||||||
@@ -2284,7 +2384,7 @@ static inline Decl *parse_func_definition(Visibility visibility, bool is_interfa
|
|||||||
Decl *func = decl_new(DECL_FUNC, tok, visibility);
|
Decl *func = decl_new(DECL_FUNC, tok, visibility);
|
||||||
func->func.function_signature.rtype = return_type;
|
func->func.function_signature.rtype = return_type;
|
||||||
|
|
||||||
Path *path = parse_path();
|
Path *path = parse_path_prefix();
|
||||||
if (path || tok.type == TOKEN_TYPE_IDENT)
|
if (path || tok.type == TOKEN_TYPE_IDENT)
|
||||||
{
|
{
|
||||||
// Special case, actually an extension
|
// Special case, actually an extension
|
||||||
@@ -2433,9 +2533,6 @@ static inline Decl *parse_enum_declaration(Visibility visibility)
|
|||||||
return decl;
|
return decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static inline bool parse_conditional_top_level(Decl ***decls)
|
static inline bool parse_conditional_top_level(Decl ***decls)
|
||||||
{
|
{
|
||||||
CONSUME_OR(TOKEN_LBRACE, false);
|
CONSUME_OR(TOKEN_LBRACE, false);
|
||||||
@@ -2505,6 +2602,9 @@ static inline bool check_no_visibility_before(Visibility visibility)
|
|||||||
case VISIBLE_LOCAL:
|
case VISIBLE_LOCAL:
|
||||||
SEMA_ERROR(tok, "Unexpected 'local' before '%.*s'.", tok.span.length, tok.start);
|
SEMA_ERROR(tok, "Unexpected 'local' before '%.*s'.", tok.span.length, tok.start);
|
||||||
return false;
|
return false;
|
||||||
|
case VISIBLE_EXTERN:
|
||||||
|
SEMA_ERROR(tok, "Unexpected 'extern' before '%.*s'.", tok.span.length, tok.start);
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2541,6 +2641,9 @@ static inline Decl *parse_top_level(void)
|
|||||||
visibility = VISIBLE_LOCAL;
|
visibility = VISIBLE_LOCAL;
|
||||||
advance();
|
advance();
|
||||||
break;
|
break;
|
||||||
|
case TOKEN_EXTERN:
|
||||||
|
visibility = VISIBLE_EXTERN;
|
||||||
|
advance();
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2651,7 +2754,7 @@ static Expr *parse_unary_expr(Expr *left)
|
|||||||
TokenType operator_type = tok.type;
|
TokenType operator_type = tok.type;
|
||||||
|
|
||||||
Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, tok);
|
Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, tok);
|
||||||
unary->unary_expr.operator = operator_type;
|
unary->unary_expr.operator = unaryop_from_token(operator_type);
|
||||||
Precedence rule_precedence = rules[operator_type].precedence;
|
Precedence rule_precedence = rules[operator_type].precedence;
|
||||||
advance();
|
advance();
|
||||||
Expr *right_side = parse_precedence(rule_precedence);
|
Expr *right_side = parse_precedence(rule_precedence);
|
||||||
@@ -2667,7 +2770,7 @@ static Expr *parse_post_unary(Expr *left)
|
|||||||
assert(expr_ok(left));
|
assert(expr_ok(left));
|
||||||
Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, tok);
|
Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, tok);
|
||||||
unary->post_expr.expr = left;
|
unary->post_expr.expr = left;
|
||||||
unary->post_expr.operator = tok.type;
|
unary->post_expr.operator = post_unaryop_from_token(tok.type);
|
||||||
advance();
|
advance();
|
||||||
return unary;
|
return unary;
|
||||||
}
|
}
|
||||||
@@ -3141,9 +3244,17 @@ static Expr *parse_type_access(TypeInfo *type)
|
|||||||
advance_and_verify(TOKEN_DOT);
|
advance_and_verify(TOKEN_DOT);
|
||||||
expr->type_access.name = tok;
|
expr->type_access.name = tok;
|
||||||
|
|
||||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected a function name or value", &poisoned_expr);
|
switch (tok.type)
|
||||||
|
{
|
||||||
return expr;
|
case TOKEN_MACRO:
|
||||||
|
case TOKEN_IDENT:
|
||||||
|
case TOKEN_CONST_IDENT:
|
||||||
|
advance();
|
||||||
|
return expr;
|
||||||
|
default:
|
||||||
|
SEMA_ERROR(tok, "Expected a function name, macro, or constant.");
|
||||||
|
return &poisoned_expr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3202,7 +3313,7 @@ static Expr *parse_type_identifier(Expr *left)
|
|||||||
static Expr *parse_maybe_scope(Expr *left)
|
static Expr *parse_maybe_scope(Expr *left)
|
||||||
{
|
{
|
||||||
assert(!left && "Unexpected left hand side");
|
assert(!left && "Unexpected left hand side");
|
||||||
Path *path = parse_path();
|
Path *path = parse_path_prefix();
|
||||||
switch (tok.type)
|
switch (tok.type)
|
||||||
{
|
{
|
||||||
case TOKEN_IDENT:
|
case TOKEN_IDENT:
|
||||||
|
|||||||
133
src/compiler/sema_name_resolution.c
Normal file
133
src/compiler/sema_name_resolution.c
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a LGPLv3.0
|
||||||
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool matches_subpath(Path *path_to_check, Path *path_to_find)
|
||||||
|
{
|
||||||
|
// This checks the full match.
|
||||||
|
if (path_to_find->module == path_to_check->module) return true;
|
||||||
|
|
||||||
|
// Let's check the offset on where to start comparing to start with
|
||||||
|
// the submatch.
|
||||||
|
ssize_t compare_start = (ssize_t)path_to_check->len - (ssize_t)path_to_find->len;
|
||||||
|
|
||||||
|
// The smallest match is the situation a::foo::bar vs foo::bar
|
||||||
|
// This means that the compare_start must be 3 or more.
|
||||||
|
if (compare_start < 3) return false;
|
||||||
|
|
||||||
|
// We also want to make sure that the preceeding 2 characters are ::
|
||||||
|
if (path_to_check->module[compare_start - 1] != ':' || path_to_check->module[compare_start - 2] != ':') return false;
|
||||||
|
|
||||||
|
// Ok, now we know this is a subpath, so check:
|
||||||
|
return 0 == memcmp(path_to_check->module + compare_start, path_to_find->module, path_to_find->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl)
|
||||||
|
{
|
||||||
|
assert(path && "Expected path.");
|
||||||
|
*ambiguous_other_decl = NULL;
|
||||||
|
Decl *decl = NULL;
|
||||||
|
VECEACH(context->imports, i)
|
||||||
|
{
|
||||||
|
Decl *import = context->imports[i];
|
||||||
|
// Partial imports
|
||||||
|
if (import->import.symbol.string && import->import.symbol.string != symbol) continue;
|
||||||
|
// Full import, first match the subpath.
|
||||||
|
if (path->len > import->import.path->len) continue;
|
||||||
|
if (!matches_subpath(import->import.path, path)) continue;
|
||||||
|
Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL);
|
||||||
|
if (!found) continue;
|
||||||
|
if (decl)
|
||||||
|
{
|
||||||
|
*ambiguous_other_decl = found;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
decl = found;
|
||||||
|
}
|
||||||
|
context_register_external_symbol(context, decl);
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl)
|
||||||
|
{
|
||||||
|
if (path)
|
||||||
|
{
|
||||||
|
return sema_resolve_path_symbol(context, symbol, path, ambiguous_other_decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ambiguous_other_decl = NULL;
|
||||||
|
|
||||||
|
if (context->current_scope)
|
||||||
|
{
|
||||||
|
Decl **first = &context->locals[0];
|
||||||
|
Decl **current = context->last_local - 1;
|
||||||
|
while (current >= first)
|
||||||
|
{
|
||||||
|
if (current[0]->name.string == symbol) return current[0];
|
||||||
|
current--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in file scope.
|
||||||
|
Decl *decl = stable_get(&context->local_symbols, symbol);
|
||||||
|
|
||||||
|
if (decl) return decl;
|
||||||
|
|
||||||
|
// Search in the module and child modules.
|
||||||
|
decl = module_find_symbol(context->module, symbol, MODULE_SYMBOL_SEARCH_THIS);
|
||||||
|
|
||||||
|
if (decl)
|
||||||
|
{
|
||||||
|
context_register_external_symbol(context, decl);
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in imports
|
||||||
|
VECEACH(context->imports, i)
|
||||||
|
{
|
||||||
|
Decl *import = context->imports[i];
|
||||||
|
if (!decl_ok(import)) continue;
|
||||||
|
Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL);
|
||||||
|
if (!found) continue;
|
||||||
|
if (decl)
|
||||||
|
{
|
||||||
|
*ambiguous_other_decl = found;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
decl = found;
|
||||||
|
}
|
||||||
|
if (!decl) return NULL;
|
||||||
|
context_register_external_symbol(context, decl);
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sema_add_local(Context *context, Decl *decl)
|
||||||
|
{
|
||||||
|
Decl *dummy;
|
||||||
|
Decl *other = sema_resolve_symbol(context, decl->name.string, NULL, &dummy);
|
||||||
|
if (other)
|
||||||
|
{
|
||||||
|
sema_shadow_error(decl, other);
|
||||||
|
decl_poison(decl);
|
||||||
|
decl_poison(other);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Decl *** vars = &context->active_function_for_analysis->func.annotations->vars;
|
||||||
|
unsigned num_vars = vec_size(*vars);
|
||||||
|
if (num_vars == MAX_LOCALS - 1 || context->last_local == &context->locals[MAX_LOCALS - 1])
|
||||||
|
{
|
||||||
|
SEMA_ERROR(decl->name, "Reached the maximum number of locals.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decl->var.id = num_vars;
|
||||||
|
*vars = VECADD(*vars, decl);
|
||||||
|
context->last_local[0] = decl;
|
||||||
|
context->last_local++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
@@ -32,8 +32,6 @@ void sema_shadow_error(Decl *decl, Decl *old)
|
|||||||
sema_prev_at_range(old->name.span, "The previous use of '%s' was here.", decl->name.string);
|
sema_prev_at_range(old->name.span, "The previous use of '%s' was here.", decl->name.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Decl *context_find_ident(Context *context, const char *symbol)
|
Decl *context_find_ident(Context *context, const char *symbol)
|
||||||
{
|
{
|
||||||
Decl **first = &context->locals[0];
|
Decl **first = &context->locals[0];
|
||||||
@@ -43,11 +41,28 @@ Decl *context_find_ident(Context *context, const char *symbol)
|
|||||||
if (current[0]->name.string == symbol) return current[0];
|
if (current[0]->name.string == symbol) return current[0];
|
||||||
current--;
|
current--;
|
||||||
}
|
}
|
||||||
Decl *found = module_find_symbol(context->module, symbol);
|
Decl *found = module_find_symbol(context->module, symbol, MODULE_SYMBOL_SEARCH_THIS);
|
||||||
if (found) return found;
|
if (found) return found;
|
||||||
return NULL;
|
Decl *symbol_found = NULL;
|
||||||
}
|
|
||||||
|
|
||||||
|
VECEACH(context->imports, i)
|
||||||
|
{
|
||||||
|
Decl *import = context->imports[i];
|
||||||
|
// Partial imports
|
||||||
|
if (import->import.symbol.string && import->import.symbol.string != symbol) continue;
|
||||||
|
|
||||||
|
Decl *decl = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL);
|
||||||
|
|
||||||
|
if (!decl) continue;
|
||||||
|
if (symbol_found)
|
||||||
|
{
|
||||||
|
symbol_found = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
symbol_found = decl;
|
||||||
|
}
|
||||||
|
return symbol_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags)
|
static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags)
|
||||||
@@ -361,7 +376,7 @@ static inline bool sema_analyse_var_decl(Context *context, Decl *decl)
|
|||||||
{
|
{
|
||||||
if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return decl_poison(decl);
|
if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return decl_poison(decl);
|
||||||
}
|
}
|
||||||
if (!context_add_local(context, decl)) return decl_poison(decl);
|
if (!sema_add_local(context, decl)) return decl_poison(decl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -963,7 +978,7 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
|||||||
Decl **params = func->func.function_signature.params;
|
Decl **params = func->func.function_signature.params;
|
||||||
VECEACH(params, i)
|
VECEACH(params, i)
|
||||||
{
|
{
|
||||||
if (!context_add_local(context, params[i])) return false;
|
if (!sema_add_local(context, params[i])) return false;
|
||||||
}
|
}
|
||||||
if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false;
|
if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false;
|
||||||
if (context->current_scope->exit != EXIT_RETURN)
|
if (context->current_scope->exit != EXIT_RETURN)
|
||||||
@@ -977,6 +992,7 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
|||||||
}
|
}
|
||||||
func->func.labels = context->labels;
|
func->func.labels = context->labels;
|
||||||
context_pop_scope(context);
|
context_pop_scope(context);
|
||||||
|
context->current_scope = NULL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1017,6 +1033,15 @@ static inline bool sema_analyse_func(Context *context, Decl *decl)
|
|||||||
if (!sema_analyse_method_function(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_method_function(context, decl)) return decl_poison(decl);
|
||||||
}
|
}
|
||||||
if (decl->func.body && !sema_analyse_function_body(context, decl)) return decl_poison(decl);
|
if (decl->func.body && !sema_analyse_function_body(context, decl)) return decl_poison(decl);
|
||||||
|
if (decl->name.string == main_name)
|
||||||
|
{
|
||||||
|
if (decl->visibility == VISIBLE_LOCAL)
|
||||||
|
{
|
||||||
|
SEMA_ERROR(decl->name, "'main' cannot have local visibility.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decl->visibility = VISIBLE_EXTERN;
|
||||||
|
}
|
||||||
DEBUG_LOG("Function analysis done.")
|
DEBUG_LOG("Function analysis done.")
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1083,9 +1108,43 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl)
|
|||||||
|
|
||||||
static inline bool sema_analyse_enum(Context *context, Decl *decl)
|
static inline bool sema_analyse_enum(Context *context, Decl *decl)
|
||||||
{
|
{
|
||||||
if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false;
|
if (!sema_resolve_type_info(context, decl->enums.type_info)) return false;
|
||||||
// TODO assign numbers to constants
|
uint64_t value = 0;
|
||||||
return true;
|
Type *type = decl->enums.type_info->type;
|
||||||
|
bool success = true;
|
||||||
|
VECEACH(decl->enums.values, i)
|
||||||
|
{
|
||||||
|
Decl *enum_value = decl->enums.values[i];
|
||||||
|
enum_value->enum_constant.ordinal = i;
|
||||||
|
assert(enum_value->resolve_status == RESOLVE_NOT_DONE);
|
||||||
|
assert(enum_value->decl_kind == DECL_ENUM_CONSTANT);
|
||||||
|
enum_value->resolve_status = RESOLVE_RUNNING;
|
||||||
|
Expr *expr = enum_value->enum_constant.expr;
|
||||||
|
if (!expr)
|
||||||
|
{
|
||||||
|
expr = expr_new(EXPR_CONST, EMPTY_TOKEN);
|
||||||
|
expr->type = type;
|
||||||
|
expr->resolve_status = RESOLVE_DONE;
|
||||||
|
expr->const_expr.type = CONST_INT;
|
||||||
|
expr->const_expr.i = value;
|
||||||
|
enum_value->enum_constant.expr = expr;
|
||||||
|
}
|
||||||
|
if (!sema_analyse_expr(context, type, expr))
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
enum_value->resolve_status = RESOLVE_DONE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(type_is_integer(expr->type->canonical));
|
||||||
|
if (expr->expr_kind != EXPR_CONST)
|
||||||
|
{
|
||||||
|
SEMA_ERROR(expr->loc, "Expected a constant expression for enum");
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
enum_value->resolve_status = RESOLVE_DONE;
|
||||||
|
enum_value->type = decl->type;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sema_analyse_error(Context *context, Decl *decl)
|
static inline bool sema_analyse_error(Context *context, Decl *decl)
|
||||||
@@ -1107,6 +1166,7 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
decl->resolve_status = RESOLVE_RUNNING;
|
decl->resolve_status = RESOLVE_RUNNING;
|
||||||
|
decl->module = context->module;
|
||||||
switch (decl->decl_kind)
|
switch (decl->decl_kind)
|
||||||
{
|
{
|
||||||
case DECL_THROWS:
|
case DECL_THROWS:
|
||||||
@@ -1114,27 +1174,29 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
|||||||
case DECL_STRUCT:
|
case DECL_STRUCT:
|
||||||
case DECL_UNION:
|
case DECL_UNION:
|
||||||
if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl);
|
||||||
context_add_header_decl(context, decl);
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_FUNC:
|
case DECL_FUNC:
|
||||||
if (!sema_analyse_func(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_func(context, decl)) return decl_poison(decl);
|
||||||
context_add_header_decl(context, decl);
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_MACRO:
|
case DECL_MACRO:
|
||||||
if (!sema_analyse_macro(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_macro(context, decl)) return decl_poison(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_VAR:
|
case DECL_VAR:
|
||||||
if (!sema_analyse_global(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_global(context, decl)) return decl_poison(decl);
|
||||||
context_add_header_decl(context, decl);
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_TYPEDEF:
|
case DECL_TYPEDEF:
|
||||||
if (!sema_analyse_typedef(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_typedef(context, decl)) return decl_poison(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_ENUM:
|
case DECL_ENUM:
|
||||||
if (!sema_analyse_enum(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_enum(context, decl)) return decl_poison(decl);
|
||||||
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_ERROR:
|
case DECL_ERROR:
|
||||||
if (!sema_analyse_error(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_error(context, decl)) return decl_poison(decl);
|
||||||
|
decl_set_external_name(decl);
|
||||||
break;
|
break;
|
||||||
case DECL_GENERIC:
|
case DECL_GENERIC:
|
||||||
if (!sema_analyse_generic(context, decl)) return decl_poison(decl);
|
if (!sema_analyse_generic(context, decl)) return decl_poison(decl);
|
||||||
@@ -1200,10 +1262,23 @@ static inline bool sema_analyse_top_level_if(Context *context, Decl *ct_if)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sema_analysis_pass_process_imports(Context *context)
|
||||||
static inline void sema_process_imports(Context *context)
|
|
||||||
{
|
{
|
||||||
// TODO
|
VECEACH(context->imports, i)
|
||||||
|
{
|
||||||
|
Decl *import = context->imports[i];
|
||||||
|
import->resolve_status = RESOLVE_RUNNING;
|
||||||
|
// IMPROVE error on importing twice.
|
||||||
|
Path *path = import->import.path;
|
||||||
|
Module *module = stable_get(&compiler.modules, path->module);
|
||||||
|
if (!module)
|
||||||
|
{
|
||||||
|
SEMA_ERROR(import->name, "No module named '%s' could be found.", path->module);
|
||||||
|
decl_poison(import);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
import->module = module;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sema_analysis_pass_conditional_compilation(Context *context)
|
void sema_analysis_pass_conditional_compilation(Context *context)
|
||||||
@@ -1245,15 +1320,16 @@ void sema_analysis_pass_decls(Context *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool sema_resolve_type_info(Context *context, TypeInfo *type_info)
|
||||||
|
{
|
||||||
|
if (!sema_resolve_type_shallow(context, type_info)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
||||||
{
|
{
|
||||||
assert(!type_info->unresolved.path && "TODO");
|
Decl *ambiguous_decl;
|
||||||
|
Decl *decl = sema_resolve_symbol(context, type_info->unresolved.name_loc.string, type_info->unresolved.path, &ambiguous_decl);
|
||||||
Decl *decl = stable_get(&context->local_symbols, type_info->unresolved.name_loc.string);
|
|
||||||
if (!decl)
|
|
||||||
{
|
|
||||||
decl = module_find_symbol(context->module, type_info->unresolved.name_loc.string);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!decl)
|
if (!decl)
|
||||||
{
|
{
|
||||||
@@ -1261,6 +1337,12 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
|||||||
return type_info_poison(type_info);
|
return type_info_poison(type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ambiguous_decl)
|
||||||
|
{
|
||||||
|
SEMA_ERROR(type_info->unresolved.name_loc, "Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", type_info->unresolved.name_loc.string,
|
||||||
|
decl->module->name->module, ambiguous_decl->module->name->module);
|
||||||
|
return type_info_poison(type_info);
|
||||||
|
}
|
||||||
switch (decl->decl_kind)
|
switch (decl->decl_kind)
|
||||||
{
|
{
|
||||||
case DECL_THROWS:
|
case DECL_THROWS:
|
||||||
@@ -1306,6 +1388,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
|||||||
UNREACHABLE
|
UNREACHABLE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info)
|
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info)
|
||||||
{
|
{
|
||||||
if (type_info->resolve_status == RESOLVE_DONE) return type_info_ok(type_info);
|
if (type_info->resolve_status == RESOLVE_DONE) return type_info_ok(type_info);
|
||||||
@@ -1338,10 +1421,4 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info)
|
|||||||
return sema_resolve_ptr_type(context, type_info);
|
return sema_resolve_ptr_type(context, type_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sema_resolve_type_info(Context *context, TypeInfo *type_info)
|
|
||||||
{
|
|
||||||
if (!sema_resolve_type_shallow(context, type_info)) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@@ -67,6 +67,14 @@ void source_file_append_line_end(File *file, SourceLoc loc)
|
|||||||
VECADD(file->line_start, file->current_line_start);
|
VECADD(file->line_start, file->current_line_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceRange source_range_from_ranges(SourceRange first, SourceRange last)
|
||||||
|
{
|
||||||
|
return (SourceRange) {
|
||||||
|
.loc = first.loc,
|
||||||
|
.length = last.loc - first.loc + last.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
SourcePosition source_file_find_position(SourceLoc loc)
|
SourcePosition source_file_find_position(SourceLoc loc)
|
||||||
{
|
{
|
||||||
File *file = source_file_from_position(loc);
|
File *file = source_file_from_position(loc);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
@@ -34,6 +34,8 @@ typedef struct _Entry
|
|||||||
|
|
||||||
static SymTab symtab;
|
static SymTab symtab;
|
||||||
|
|
||||||
|
const char *main_name;
|
||||||
|
|
||||||
void symtab_init(uint32_t capacity)
|
void symtab_init(uint32_t capacity)
|
||||||
{
|
{
|
||||||
assert (is_power_of_two(capacity) && "Must be a power of two");
|
assert (is_power_of_two(capacity) && "Must be a power of two");
|
||||||
@@ -41,7 +43,7 @@ void symtab_init(uint32_t capacity)
|
|||||||
{
|
{
|
||||||
free(symtab.entries);
|
free(symtab.entries);
|
||||||
}
|
}
|
||||||
size_t size = capacity * sizeof(SymEntry);
|
size_t size = capacity *sizeof(SymEntry);
|
||||||
symtab.entries = MALLOC(size);
|
symtab.entries = MALLOC(size);
|
||||||
memset(symtab.entries, 0, size);
|
memset(symtab.entries, 0, size);
|
||||||
symtab.count = 0;
|
symtab.count = 0;
|
||||||
@@ -61,8 +63,10 @@ void symtab_init(uint32_t capacity)
|
|||||||
const char* interned = symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type);
|
const char* interned = symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type);
|
||||||
assert(type == (TokenType)i);
|
assert(type == (TokenType)i);
|
||||||
assert(symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type) == interned);
|
assert(symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type) == interned);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// Init some constant idents
|
||||||
|
TokenType type = TOKEN_IDENT;
|
||||||
|
main_name = symtab_add("main", 4, fnv1a("main", 4), &type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline SymEntry *entry_find(const char *key, uint32_t key_len, uint32_t hash)
|
static inline SymEntry *entry_find(const char *key, uint32_t key_len, uint32_t hash)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
@@ -190,6 +190,8 @@ const char *token_type_to_string(TokenType type)
|
|||||||
return "else";
|
return "else";
|
||||||
case TOKEN_ENUM:
|
case TOKEN_ENUM:
|
||||||
return "enum";
|
return "enum";
|
||||||
|
case TOKEN_EXTERN:
|
||||||
|
return "extern";
|
||||||
case TOKEN_ERROR_TYPE:
|
case TOKEN_ERROR_TYPE:
|
||||||
return "error";
|
return "error";
|
||||||
case TOKEN_FALSE:
|
case TOKEN_FALSE:
|
||||||
@@ -351,6 +353,20 @@ const char *token_type_to_string(TokenType type)
|
|||||||
UNREACHABLE
|
UNREACHABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool token_is_symbol(TokenType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TOKEN_MACRO:
|
||||||
|
case TOKEN_CONST:
|
||||||
|
case TOKEN_IDENT:
|
||||||
|
case TOKEN_TYPE_IDENT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool token_is_type(TokenType type)
|
bool token_is_type(TokenType type)
|
||||||
{
|
{
|
||||||
return type >= TOKEN_VOID && type <= TOKEN_C_ULONGLONG;
|
return type >= TOKEN_VOID && type <= TOKEN_C_ULONGLONG;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "benchmark.h"
|
#include "benchmark.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
void bench_begin(void);
|
void bench_begin(void);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
static const char* test_parse = "struct Node\n"
|
static const char* test_parse = "struct Node\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
void compiler_tests(void);
|
void compiler_tests(void);
|
||||||
@@ -18,11 +18,13 @@ int main(int argc, const char *argv[])
|
|||||||
compiler_tests();
|
compiler_tests();
|
||||||
break;
|
break;
|
||||||
case COMMAND_COMPILE:
|
case COMMAND_COMPILE:
|
||||||
compile_file();
|
compile_files(NULL);
|
||||||
|
break;
|
||||||
|
case COMMAND_BUILD:
|
||||||
|
build();
|
||||||
break;
|
break;
|
||||||
case COMMAND_COMPILE_RUN:
|
case COMMAND_COMPILE_RUN:
|
||||||
case COMMAND_MISSING:
|
case COMMAND_MISSING:
|
||||||
case COMMAND_BUILD:
|
|
||||||
case COMMAND_RUN:
|
case COMMAND_RUN:
|
||||||
case COMMAND_CLEAN_RUN:
|
case COMMAND_CLEAN_RUN:
|
||||||
case COMMAND_CLEAN:
|
case COMMAND_CLEAN:
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@@ -13,4 +14,5 @@
|
|||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define MAX_IDENTIFIER_LENGTH 31
|
#define MAX_IDENTIFIER_LENGTH 31
|
||||||
|
#define PROJECT_TOML "project.toml"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
#include "malloc.h"
|
|
||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
|
||||||
const char* expand_path(const char* path)
|
const char* expand_path(const char* path)
|
||||||
{
|
{
|
||||||
@@ -102,3 +105,69 @@ void path_get_dir_and_filename_from_full(const char *full_path, char **filename,
|
|||||||
*dir_path = malloc(dir_len + 1);
|
*dir_path = malloc(dir_len + 1);
|
||||||
strncpy(*dir_path, dir, dir_len + 1);
|
strncpy(*dir_path, dir, dir_len + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void file_find_top_dir()
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
struct stat info;
|
||||||
|
int err = stat(PROJECT_TOML, &info);
|
||||||
|
|
||||||
|
// Error and the it's not a "missing file"?
|
||||||
|
if (err && errno != ENOENT)
|
||||||
|
{
|
||||||
|
error_exit("Can't open %s: %s.", PROJECT_TOML, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything worked and it's a regular file? We're done!
|
||||||
|
if (!err && S_ISREG(info.st_mode)) return; // NOLINT(hicpp-signed-bitwise)
|
||||||
|
|
||||||
|
// Otherwise just continue upwards.
|
||||||
|
// Note that symbolically linked files are ignored.
|
||||||
|
char start_path[PATH_MAX + 1];
|
||||||
|
getcwd(start_path, PATH_MAX);
|
||||||
|
if (chdir(".."))
|
||||||
|
{
|
||||||
|
error_exit("Can't change directory to search for %s: %s.", PROJECT_TOML, strerror(errno));
|
||||||
|
}
|
||||||
|
char new_path[PATH_MAX + 1];
|
||||||
|
getcwd(new_path, PATH_MAX);
|
||||||
|
if (strcmp(new_path, start_path) != 0) continue;
|
||||||
|
error_exit("The root build directory containing %s could not be found. Did you use the correct directory?", PROJECT_TOML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_add_wildcard_files(const char ***files, const char *path, bool recursive)
|
||||||
|
{
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
if (!dir)
|
||||||
|
{
|
||||||
|
error_exit("Can't open the directory '%s'. Please check the paths. %s", path, strerror(errno));
|
||||||
|
}
|
||||||
|
struct dirent *ent;
|
||||||
|
while ((ent = readdir(dir)))
|
||||||
|
{
|
||||||
|
if (ent->d_namlen < 4) continue;
|
||||||
|
|
||||||
|
// Doesn't end with .c3
|
||||||
|
if (strncmp(&ent->d_name[ent->d_namlen - 3], ".c3", 3) != 0)
|
||||||
|
{
|
||||||
|
if (ent->d_type == DT_DIR && ent->d_name[0] != '.' && recursive)
|
||||||
|
{
|
||||||
|
char *new_path = strndup(ent->d_name, ent->d_namlen);
|
||||||
|
file_add_wildcard_files(files, new_path, recursive);
|
||||||
|
free(new_path);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *name = malloc_arena(ent->d_namlen + 1);
|
||||||
|
memcpy(name, ent->d_name, ent->d_namlen);
|
||||||
|
name[ent->d_namlen] = '\0';
|
||||||
|
vec_add(*files, name);
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
@@ -10,6 +10,9 @@ const char* expand_path(const char* path);
|
|||||||
char *read_file(const char *path, size_t *return_size);
|
char *read_file(const char *path, size_t *return_size);
|
||||||
int filename_to_module(const char *path, char buffer[MAX_IDENTIFIER_LENGTH + 1]);
|
int filename_to_module(const char *path, char buffer[MAX_IDENTIFIER_LENGTH + 1]);
|
||||||
void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path);
|
void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path);
|
||||||
|
void file_find_top_dir();
|
||||||
|
void file_add_wildcard_files(const char ***files, const char *path, bool recursive);
|
||||||
|
|
||||||
void init_arena(void);
|
void init_arena(void);
|
||||||
void *malloc_arena(unsigned long mem);
|
void *malloc_arena(unsigned long mem);
|
||||||
void free_arena(void);
|
void free_arena(void);
|
||||||
@@ -293,5 +296,9 @@ static inline bool is_all_lower(const char* string)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef __printflike
|
||||||
|
#define __printflike(x, y)
|
||||||
|
#endif
|
||||||
|
|
||||||
char *strformat(const char *var, ...) __printflike(1, 2);
|
char *strformat(const char *var, ...) __printflike(1, 2);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "malloc.h"
|
#include "common.h"
|
||||||
|
|
||||||
static const size_t KB = 1024ul;
|
static const size_t KB = 1024ul;
|
||||||
// Use 1MB at a time.
|
// Use 1MB at a time.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||||
// license that can be found in the LICENSE file.
|
// a copy of which can be found in the LICENSE file.
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|||||||
2365
src/utils/toml.c
Normal file
2365
src/utils/toml.c
Normal file
File diff suppressed because it is too large
Load Diff
226
src/utils/toml.h
Normal file
226
src/utils/toml.h
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef __TOML_H__
|
||||||
|
#define __TOML_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define TOML_FALSE 0
|
||||||
|
#define TOML_TRUE 1
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER) || _MSC_VER >= 1800
|
||||||
|
#define TOML_CONST const
|
||||||
|
#else
|
||||||
|
#define TOML_CONST
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TOML_OK,
|
||||||
|
TOML_ERR,
|
||||||
|
TOML_ERR_IO,
|
||||||
|
TOML_ERR_NOMEM,
|
||||||
|
TOML_ERR_SYNTAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int code;
|
||||||
|
char *message;
|
||||||
|
int _is_literal;
|
||||||
|
} TomlErr;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *str;
|
||||||
|
size_t len;
|
||||||
|
size_t _capacity;
|
||||||
|
} TomlString;
|
||||||
|
|
||||||
|
typedef struct _TomlValue TomlValue;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
TomlValue **elements;
|
||||||
|
size_t len;
|
||||||
|
size_t _capacity;
|
||||||
|
} TomlArray;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
TomlString *key;
|
||||||
|
TomlValue *value;
|
||||||
|
} TomlKeyValue;
|
||||||
|
|
||||||
|
typedef struct _TomlTable TomlTable;
|
||||||
|
typedef struct _TomlTableIter TomlTableIter;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int year;
|
||||||
|
int month;
|
||||||
|
int day;
|
||||||
|
int hour;
|
||||||
|
int minute;
|
||||||
|
double second;
|
||||||
|
int offset_hour;
|
||||||
|
int offset_minute;
|
||||||
|
} TomlDateTime;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
TOML_TABLE,
|
||||||
|
TOML_ARRAY,
|
||||||
|
TOML_STRING,
|
||||||
|
TOML_INTEGER,
|
||||||
|
TOML_FLOAT,
|
||||||
|
TOML_DATETIME,
|
||||||
|
TOML_BOOLEAN,
|
||||||
|
} TomlType;
|
||||||
|
|
||||||
|
struct _TomlValue
|
||||||
|
{
|
||||||
|
TomlType type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
TomlTable *table;
|
||||||
|
TomlArray *array;
|
||||||
|
TomlString *string;
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
long long integer;
|
||||||
|
#else
|
||||||
|
long integer;
|
||||||
|
#endif
|
||||||
|
double float_;
|
||||||
|
TomlDateTime *datetime;
|
||||||
|
int boolean;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
|
||||||
|
char *toml_strdup(TOML_CONST char *str);
|
||||||
|
|
||||||
|
char *toml_strndup(TOML_CONST char *str, size_t n);
|
||||||
|
|
||||||
|
int toml_vasprintf(char **str, TOML_CONST char *format, va_list args);
|
||||||
|
|
||||||
|
int toml_asprintf(char **str, TOML_CONST char *format, ...);
|
||||||
|
|
||||||
|
#define TOML_ERR_INIT {TOML_OK, NULL, TOML_FALSE}
|
||||||
|
|
||||||
|
void toml_err_init(TomlErr *err);
|
||||||
|
|
||||||
|
void toml_clear_err(TomlErr *err);
|
||||||
|
|
||||||
|
void toml_err_move(TomlErr *to, TomlErr *from);
|
||||||
|
|
||||||
|
void toml_set_err_v(TomlErr *err, int code, TOML_CONST char *format, va_list args);
|
||||||
|
|
||||||
|
void toml_set_err(TomlErr *err, int code, TOML_CONST char *format, ...);
|
||||||
|
|
||||||
|
void toml_set_err_literal(TomlErr *err, int code, TOML_CONST char *message);
|
||||||
|
|
||||||
|
TomlString *toml_string_new(TomlErr *err);
|
||||||
|
|
||||||
|
TomlString *toml_string_new_string(TOML_CONST char *str, TomlErr *err);
|
||||||
|
|
||||||
|
TomlString *toml_string_new_nstring(TOML_CONST char *str, size_t len, TomlErr *err);
|
||||||
|
|
||||||
|
void toml_string_append_char(TomlString *self, char ch, TomlErr *err);
|
||||||
|
|
||||||
|
void toml_string_append_string(TomlString *self, TOML_CONST char *str, TomlErr *err);
|
||||||
|
|
||||||
|
void toml_string_append_nstring(TomlString *self, TOML_CONST char *str, size_t len, TomlErr *err);
|
||||||
|
|
||||||
|
TomlString *toml_string_copy(TOML_CONST TomlString *self, TomlErr *err);
|
||||||
|
|
||||||
|
void toml_string_free(TomlString *self);
|
||||||
|
|
||||||
|
int toml_string_equals(TOML_CONST TomlString *self, TOML_CONST TomlString *other);
|
||||||
|
|
||||||
|
TomlTable *toml_table_new(TomlErr *err);
|
||||||
|
|
||||||
|
void toml_table_free(TomlTable *self);
|
||||||
|
|
||||||
|
void toml_table_set_by_string(TomlTable *self, TomlString *key,
|
||||||
|
TomlValue *value, TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_table_get_by_string(TOML_CONST TomlTable *self, TOML_CONST TomlString *key);
|
||||||
|
|
||||||
|
void toml_table_set(TomlTable *self, TOML_CONST char *key, TomlValue *value, TomlErr *err);
|
||||||
|
|
||||||
|
void toml_table_setn(TomlTable *self, TOML_CONST char *key, size_t key_len,
|
||||||
|
TomlValue *value, TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_table_get(TOML_CONST TomlTable *self, TOML_CONST char *key);
|
||||||
|
|
||||||
|
TomlValue *toml_table_getn(TOML_CONST TomlTable *self, TOML_CONST char *key, size_t key_len);
|
||||||
|
|
||||||
|
TomlTableIter *toml_table_iter_new(TomlTable *table, TomlErr *err);
|
||||||
|
|
||||||
|
void toml_table_iter_free(TomlTableIter *self);
|
||||||
|
|
||||||
|
TomlKeyValue *toml_table_iter_get(TomlTableIter *self);
|
||||||
|
|
||||||
|
int toml_table_iter_has_next(TomlTableIter *self);
|
||||||
|
|
||||||
|
void toml_table_iter_next(TomlTableIter *self);
|
||||||
|
|
||||||
|
TomlArray *toml_array_new(TomlErr *err);
|
||||||
|
|
||||||
|
void toml_array_free(TomlArray *self);
|
||||||
|
|
||||||
|
void toml_array_append(TomlArray *self, TomlValue *value, TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_value_new(TomlType type, TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_string(TOML_CONST char *str, TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_nstring(TOML_CONST char *str, size_t len, TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_table(TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_array(TomlErr *err);
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
TomlValue *toml_value_new_integer(long long integer, TomlErr *err);
|
||||||
|
#else
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_integer(long integer, TomlErr *err);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_float(double flt, TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_datetime(TomlErr *err);
|
||||||
|
|
||||||
|
TomlValue *toml_value_new_boolean(int boolean, TomlErr *err);
|
||||||
|
|
||||||
|
void toml_value_free(TomlValue *self);
|
||||||
|
|
||||||
|
TomlTable *toml_load_string(TOML_CONST char *str, TomlErr *err);
|
||||||
|
|
||||||
|
TomlTable *toml_load_nstring(TOML_CONST char *str, size_t len, TomlErr *err);
|
||||||
|
|
||||||
|
TomlTable *toml_load_file(FILE *file, TomlErr *err);
|
||||||
|
|
||||||
|
TomlTable *toml_load_filename(TOML_CONST char *filename, TomlErr *err);
|
||||||
|
|
||||||
|
/* TODO: implement dump functions
|
||||||
|
char *toml_dump_string(TOML_CONST TomlTable *self, TomlErr *err);
|
||||||
|
TomlString *toml_dump_nstring(TOML_CONST TomlTable *self, TomlErr *err);
|
||||||
|
void toml_dump_file(TOML_CONST TomlTable *self, FILE *file, TomlErr *err);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* end of include guard: __TOML_H__ */
|
||||||
Reference in New Issue
Block a user