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_module.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)
|
||||
|
||||
|
||||
@@ -136,6 +136,9 @@ L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
"^" { count(); return('^'); }
|
||||
"|" { count(); return('|'); }
|
||||
"?" { count(); return('?'); }
|
||||
"+%" { count(); return(ADDW); }
|
||||
"*%" { count(); return(MULTW); }
|
||||
"-%" { count(); return(SUBW); }
|
||||
"({" { count(); return(FN_BLOCK_BEGIN); }
|
||||
"{)" { count(); return(FN_BLOCK_END); }
|
||||
[ \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 FN_BLOCK_START FN_BLOCK_END
|
||||
%token MULTW ADDW SUBW
|
||||
|
||||
%start translation_unit
|
||||
%%
|
||||
@@ -34,6 +35,10 @@ path
|
||||
| path IDENT SCOPE
|
||||
;
|
||||
|
||||
import_path
|
||||
: IDENT
|
||||
| import_path SCOPE IDENT
|
||||
;
|
||||
|
||||
ident_expression
|
||||
: CONST_IDENT
|
||||
@@ -83,6 +88,7 @@ unary_operator
|
||||
| '*'
|
||||
| '+'
|
||||
| '-'
|
||||
| SUBW
|
||||
| '~'
|
||||
| '!'
|
||||
;
|
||||
@@ -91,6 +97,7 @@ unary_operator
|
||||
multiplicative_expression
|
||||
: unary_expression
|
||||
| multiplicative_expression '*' unary_expression
|
||||
| multiplicative_expression MULTW unary_expression
|
||||
| multiplicative_expression '/' unary_expression
|
||||
| multiplicative_expression '%' unary_expression
|
||||
;
|
||||
@@ -111,7 +118,9 @@ bit_expression
|
||||
additive_expression
|
||||
: bit_expression
|
||||
| additive_expression '+' bit_expression
|
||||
| additive_expression ADDW bit_expression
|
||||
| additive_expression '-' bit_expression
|
||||
| additive_expression SUBW bit_expression
|
||||
;
|
||||
|
||||
relational_expression
|
||||
@@ -631,15 +640,29 @@ module_params
|
||||
;
|
||||
|
||||
module
|
||||
: MODULE IDENT ';'
|
||||
| MODULE IDENT '(' module_params ')' ';'
|
||||
: MODULE import_path ';'
|
||||
| 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 IDENT ';'
|
||||
| IMPORT IDENT AS IDENT ';'
|
||||
| IMPORT IDENT AS IDENT LOCAL ';'
|
||||
| IMPORT IDENT LOCAL ';'
|
||||
: IMPORT import_path ';'
|
||||
| IMPORT import_path ':' specified_import_list ';'
|
||||
;
|
||||
|
||||
imports
|
||||
|
||||
@@ -94,7 +94,7 @@ func int test3()
|
||||
|
||||
typedef func void(int) as Foo;
|
||||
//typedef int as Foo;
|
||||
func void printf(char *hello);
|
||||
extern func void printf(char *hello);
|
||||
|
||||
macro @hello()
|
||||
{
|
||||
@@ -110,8 +110,13 @@ func void bob()
|
||||
long deee = (eok ? a : b) + c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
func int main(int x)
|
||||
{
|
||||
int aa = x++;
|
||||
int bb = x--;
|
||||
int cc = ++x;
|
||||
for (int ok = 0; ok < 10; ok++)
|
||||
{
|
||||
printf("ok");
|
||||
@@ -132,6 +137,7 @@ func int main(int x)
|
||||
{
|
||||
printf("helo\n");
|
||||
}
|
||||
Test baok = { 1 };
|
||||
Test2 efe;
|
||||
efe.t.a = 3;
|
||||
if (efe.t.a > 2) printf("Works!\n");
|
||||
@@ -183,6 +189,7 @@ JUMP:
|
||||
|
||||
func void test2(int* x, int y, int z)
|
||||
{
|
||||
x++;
|
||||
z = 0;
|
||||
z ? y : z;
|
||||
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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "build_options.h"
|
||||
#include <stdio.h>
|
||||
@@ -323,7 +323,7 @@ void parse_arguments(int argc, const char *argv[])
|
||||
build_options.debug_info = false;
|
||||
build_options.command = COMMAND_MISSING;
|
||||
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++)
|
||||
{
|
||||
build_options.severity[i] = DIAG_IGNORE;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
@@ -95,6 +95,7 @@ typedef struct
|
||||
const char* target;
|
||||
const char* path;
|
||||
const char* cpu;
|
||||
const char* target_triple;
|
||||
CompilerCommand command;
|
||||
uint32_t symtab_size;
|
||||
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;
|
||||
|
||||
|
||||
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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
|
||||
void create_project(void);
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
@@ -18,6 +18,19 @@ Type poisoned_type = { .type_kind = TYPE_POISONED };
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -229,6 +242,10 @@ UnaryOp unary_op[256] = {
|
||||
[TOKEN_MINUSMINUS] = UNARYOP_DEC,
|
||||
};
|
||||
|
||||
PostUnaryOp post_unary_op[256] = {
|
||||
[TOKEN_PLUSPLUS] = POSTUNARYOP_INC,
|
||||
[TOKEN_MINUSMINUS] = POSTUNARYOP_DEC,
|
||||
};
|
||||
|
||||
|
||||
AssignOp assignop_from_token(TokenType type)
|
||||
@@ -273,6 +290,20 @@ TokenType unaryop_to_token(UnaryOp type)
|
||||
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 };
|
||||
|
||||
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:
|
||||
if (type_info->unresolved.path)
|
||||
{
|
||||
if (type_info->unresolved.path->module.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);
|
||||
}
|
||||
fprintf_indented(file, indent + 1, "(unresolved %s::%s)\n", type_info->unresolved.path->module, type_info->unresolved.name_loc.string);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
break;
|
||||
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_recursive(file, expr->unary_expr.expr, indent + 1);
|
||||
break;
|
||||
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_recursive(file, expr->post_expr.expr, indent + 1);
|
||||
break;
|
||||
@@ -1015,6 +1039,6 @@ void fprint_decl(FILE *file, Decl *dec)
|
||||
{
|
||||
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 } };
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "bigint.h"
|
||||
#include "../utils/lib.h"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include "../build/build_options.h"
|
||||
@@ -13,12 +13,12 @@ void compiler_init(void)
|
||||
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;
|
||||
File *file = source_file_load(build_options.files[i], &loaded);
|
||||
File *file = source_file_load(target->sources[i], &loaded);
|
||||
if (loaded) continue;
|
||||
lexer_add_file_for_lexing(file);
|
||||
printf("# %s\n", file->full_path);
|
||||
@@ -33,32 +33,32 @@ static void compiler_lex()
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void compiler_parse()
|
||||
void compiler_parse(BuildTarget *target)
|
||||
{
|
||||
builtin_setup();
|
||||
VECEACH(build_options.files, i)
|
||||
VECEACH(target->sources, i)
|
||||
{
|
||||
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;
|
||||
diag_reset();
|
||||
Context *context = context_create(file);
|
||||
Context *context = context_create(file, target);
|
||||
parse_file(context);
|
||||
context_print_ast(context, stdout);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void compiler_compile()
|
||||
void compiler_compile(BuildTarget *target)
|
||||
{
|
||||
Context **contexts = NULL;
|
||||
VECEACH(build_options.files, i)
|
||||
VECEACH(target->sources, i)
|
||||
{
|
||||
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;
|
||||
diag_reset();
|
||||
Context *context = context_create(file);
|
||||
Context *context = context_create(file, target);
|
||||
vec_add(contexts, context);
|
||||
parse_file(context);
|
||||
}
|
||||
@@ -76,6 +76,11 @@ void compiler_compile()
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
context_register_global_decl(contexts[0], decl);
|
||||
*/
|
||||
assert(contexts);
|
||||
VECEACH(contexts, i)
|
||||
{
|
||||
sema_analysis_pass_process_imports(contexts[i]);
|
||||
}
|
||||
VECEACH(contexts, i)
|
||||
{
|
||||
sema_analysis_pass_conditional_compilation(contexts[i]);
|
||||
@@ -93,22 +98,69 @@ void compiler_compile()
|
||||
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();
|
||||
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)
|
||||
{
|
||||
case COMPILE_LEX_ONLY:
|
||||
compiler_lex();
|
||||
compiler_lex(target);
|
||||
break;
|
||||
case COMPILE_LEX_PARSE_ONLY:
|
||||
compiler_parse();
|
||||
compiler_parse(target);
|
||||
break;
|
||||
default:
|
||||
compiler_compile();
|
||||
compiler_compile(target);
|
||||
break;
|
||||
}
|
||||
TODO
|
||||
@@ -126,14 +178,35 @@ void compiler_add_type(Type *type)
|
||||
assert(type_ok(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);
|
||||
if (module) return module;
|
||||
Module *module = stable_get(&compiler.modules, module_name->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->name = module_name;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -142,12 +215,12 @@ void compiler_register_public_symbol(Decl *decl)
|
||||
Decl *prev = stable_get(&compiler.global_symbols, decl->name.string);
|
||||
// If the previous symbol was already declared globally, remove it.
|
||||
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)
|
||||
{
|
||||
sub_module_space = malloc_arena(sizeof(*sub_module_space));
|
||||
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);
|
||||
stable_set(sub_module_space, decl->name.string, prev ? &poisoned_decl : decl);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "build/build_options.h"
|
||||
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
void compiler_init();
|
||||
void compile_file();
|
||||
void compile_files(BuildTarget *target);
|
||||
void build();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "../utils/common.h"
|
||||
#include "../utils/errors.h"
|
||||
@@ -16,6 +16,7 @@ typedef uint32_t SourceLoc;
|
||||
#define EMPTY_TOKEN ((Token) { .string = NULL })
|
||||
#define MAX_LOCALS 0xFFFF
|
||||
#define MAX_SCOPE_DEPTH 0xFF
|
||||
#define MAX_PATH 1024
|
||||
|
||||
typedef struct _Ast Ast;
|
||||
typedef struct _Decl Decl;
|
||||
@@ -87,10 +88,11 @@ typedef struct
|
||||
const char *start;
|
||||
} SourcePosition;
|
||||
|
||||
typedef struct
|
||||
typedef struct _Path
|
||||
{
|
||||
Token module;
|
||||
Token sub_module;
|
||||
SourceRange span;
|
||||
const char *module;
|
||||
uint32_t len;
|
||||
} Path;
|
||||
|
||||
typedef struct
|
||||
@@ -169,10 +171,9 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ImportType type : 3;
|
||||
Token alias;
|
||||
Expr** generic_parameters;
|
||||
Module *module;
|
||||
Path *path;
|
||||
Token symbol;
|
||||
bool aliased;
|
||||
} ImportDecl;
|
||||
|
||||
typedef struct
|
||||
@@ -204,6 +205,7 @@ typedef struct
|
||||
{
|
||||
Decl *parent;
|
||||
Expr *expr;
|
||||
uint64_t ordinal;
|
||||
} EnumConstantDecl;
|
||||
|
||||
typedef struct
|
||||
@@ -221,6 +223,7 @@ typedef struct
|
||||
|
||||
typedef struct _FunctionSignature
|
||||
{
|
||||
CallConvention convention : 4;
|
||||
bool variadic : 1;
|
||||
TypeInfo *rtype;
|
||||
Decl** params;
|
||||
@@ -235,7 +238,6 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *full_name;
|
||||
TypeInfo *type_parent;
|
||||
FunctionSignature function_signature;
|
||||
Ast *body;
|
||||
@@ -251,6 +253,11 @@ typedef struct
|
||||
FunctionSignature attr_signature;
|
||||
} AttrDecl;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Decl *import;
|
||||
} AliasDecl;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool is_func : 1;
|
||||
@@ -281,6 +288,7 @@ typedef struct
|
||||
typedef struct _Decl
|
||||
{
|
||||
Token name;
|
||||
const char *external_name;
|
||||
DeclKind decl_kind : 6;
|
||||
Visibility visibility : 2;
|
||||
ResolveStatus resolve_status : 2;
|
||||
@@ -366,17 +374,15 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *left;
|
||||
Expr *right;
|
||||
AssignOp operator;
|
||||
} ExprAssign;
|
||||
Expr* expr;
|
||||
UnaryOp operator;
|
||||
} ExprUnary;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr* expr;
|
||||
TokenType operator;
|
||||
} ExprUnary;
|
||||
|
||||
PostUnaryOp operator;
|
||||
} ExprPostUnary;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -396,7 +402,7 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool is_struct_function;
|
||||
bool is_struct_function : 1;
|
||||
Expr *function;
|
||||
Expr **arguments;
|
||||
} ExprCall;
|
||||
@@ -458,10 +464,9 @@ struct _Expr
|
||||
ExprTypeRef type_access;
|
||||
ExprTry try_expr;
|
||||
ExprBinary binary_expr;
|
||||
ExprAssign assign_expr;
|
||||
ExprTernary ternary_expr;
|
||||
ExprUnary unary_expr;
|
||||
ExprUnary post_expr;
|
||||
ExprPostUnary post_expr;
|
||||
ExprCall call_expr;
|
||||
ExprSubscript subscript_expr;
|
||||
ExprAccess access_expr;
|
||||
@@ -663,7 +668,7 @@ typedef struct _Ast
|
||||
|
||||
typedef struct _Module
|
||||
{
|
||||
const char *name;
|
||||
Path *name;
|
||||
|
||||
bool is_external;
|
||||
bool is_c_library;
|
||||
@@ -675,6 +680,7 @@ typedef struct _Module
|
||||
STable struct_functions;
|
||||
STable symbols;
|
||||
STable public_symbols;
|
||||
Module **sub_modules;
|
||||
} Module;
|
||||
|
||||
|
||||
@@ -691,10 +697,12 @@ typedef struct _DynamicScope
|
||||
|
||||
typedef struct _Context
|
||||
{
|
||||
Token module_name;
|
||||
BuildTarget *target;
|
||||
Path *module_name;
|
||||
Token* module_parameters;
|
||||
File* file;
|
||||
Decl** imports;
|
||||
Decl *specified_imports;
|
||||
Module *module;
|
||||
STable local_symbols;
|
||||
Decl **header_declarations;
|
||||
@@ -716,6 +724,11 @@ typedef struct _Context
|
||||
int in_volatile_section;
|
||||
Decl *locals[MAX_LOCALS];
|
||||
DynamicScope scopes[MAX_SCOPE_DEPTH];
|
||||
char path_scratch[MAX_PATH];
|
||||
struct {
|
||||
STable external_symbols;
|
||||
Decl **external_symbol_list;
|
||||
};
|
||||
} Context;
|
||||
|
||||
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_voidstar;
|
||||
|
||||
extern const char *main_name;
|
||||
|
||||
#define AST_NEW(_kind, _token) new_ast(_kind, _token)
|
||||
|
||||
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);
|
||||
|
||||
void llvm_codegen(Context *context);
|
||||
void codegen(Context *context);
|
||||
|
||||
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
|
||||
bool sema_analyse_decl(Context *context, Decl *decl);
|
||||
|
||||
void compiler_add_type(Type *type);
|
||||
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);
|
||||
|
||||
Context *context_create(File *file);
|
||||
Context *context_create(File *file, BuildTarget *target);
|
||||
void context_push(Context *context);
|
||||
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(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);
|
||||
Decl *context_find_ident(Context *context, const char *symbol);
|
||||
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_with_type(Token name, DeclKind decl_type, 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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
#define SEMA_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__)
|
||||
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_decls(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);
|
||||
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_shallow(Context *context, TypeInfo *type_info);
|
||||
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);
|
||||
SourcePosition source_file_find_position_in_file(File *file, 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_set(STable *table, const char *key, void *value);
|
||||
@@ -958,6 +988,7 @@ void *target_machine();
|
||||
#define TOKEN_MAX_LENGTH 0xFFFF
|
||||
#define TOK2VARSTR(_token) _token.span.length, _token.start
|
||||
bool token_is_type(TokenType type);
|
||||
bool token_is_symbol(TokenType type);
|
||||
const char *token_type_to_string(TokenType type);
|
||||
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);
|
||||
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_assign_base_op(BinaryOp assign_binary_op);
|
||||
TokenType binaryop_to_token(BinaryOp type);
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
|
||||
Context *current_context;
|
||||
|
||||
Context *context_create(File *file)
|
||||
Context *context_create(File *file, BuildTarget *target)
|
||||
{
|
||||
Context *context = malloc_arena(sizeof(Context));
|
||||
memset(context, 0, sizeof(Context));
|
||||
context->file = file;
|
||||
context->target = target;
|
||||
stable_init(&context->local_symbols, 256);
|
||||
stable_init(&context->external_symbols, 256);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -50,17 +52,17 @@ bool context_add_local(Context *context, Decl *decl)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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 true;
|
||||
@@ -84,25 +86,37 @@ bool context_set_module_from_filename(Context *context)
|
||||
"try using an explicit module name.");
|
||||
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.
|
||||
context->module_name = module_name;
|
||||
if (!is_all_lower(module_name.string))
|
||||
context->module_name = path;
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -117,26 +131,32 @@ void context_register_global_decl(Context *context, Decl *decl)
|
||||
if (decl->func.type_parent)
|
||||
{
|
||||
vec_add(context->struct_functions, decl);
|
||||
// TODO set name
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec_add(context->functions, decl);
|
||||
decl_set_external_name(decl);
|
||||
}
|
||||
break;
|
||||
case DECL_VAR:
|
||||
vec_add(context->vars, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
case DECL_TYPEDEF:
|
||||
vec_add(context->types, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_ENUM:
|
||||
vec_add(context->enums, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_ERROR:
|
||||
vec_add(context->error_types, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_ENUM_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);
|
||||
if (!is_all_lower(module_name.string))
|
||||
DEBUG_LOG("SEMA: Add import of '%s'.", path->module);
|
||||
|
||||
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.");
|
||||
return false;
|
||||
}
|
||||
Decl *decl = decl_new(DECL_IMPORT, module_name, VISIBLE_LOCAL);
|
||||
decl->import.type = import_type;
|
||||
decl->import.generic_parameters = generic_parameters;
|
||||
if (import_type == IMPORT_TYPE_ALIAS_LOCAL || import_type == IMPORT_TYPE_ALIAS)
|
||||
{
|
||||
decl->import.alias = alias;
|
||||
if (!is_all_lower(alias.string))
|
||||
{
|
||||
sema_error_range(alias.span, "A module alias is not expected to have any upper case characters, please change it.");
|
||||
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;
|
||||
if (alias.string == token.string)
|
||||
{
|
||||
sema_error_range(alias.span, "If an alias would be the same as the symbol aliased, it wouldn't have any effect.");
|
||||
return false;
|
||||
}
|
||||
if (alias.string == context->module_name->module)
|
||||
{
|
||||
sema_error_range(alias.span, "An alias cannot have not have the same as the name of the current module.");
|
||||
return false;
|
||||
}
|
||||
import->import.aliased = true;
|
||||
TODO
|
||||
}
|
||||
|
||||
VECEACH(context->imports, i)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
vec_add(context->imports, import);
|
||||
printf("Added import %s\n", path->module);
|
||||
|
||||
context->imports = VECADD(context->imports, decl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include <math.h>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#define DW_TAG_array_type 0x01
|
||||
#define DW_TAG_class_type 0x02
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
// Only include this from compiler_common.h
|
||||
|
||||
@@ -250,14 +250,6 @@ typedef enum
|
||||
GOTO_JUMP_BACK
|
||||
} GotoType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
IMPORT_TYPE_FULL,
|
||||
IMPORT_TYPE_ALIAS,
|
||||
IMPORT_TYPE_ALIAS_LOCAL,
|
||||
IMPORT_TYPE_LOCAL
|
||||
} ImportType;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -448,6 +440,7 @@ typedef enum
|
||||
TOKEN_ELSE,
|
||||
TOKEN_ENUM,
|
||||
TOKEN_ERROR_TYPE,
|
||||
TOKEN_EXTERN,
|
||||
TOKEN_FALSE,
|
||||
TOKEN_FOR,
|
||||
TOKEN_FUNC,
|
||||
@@ -555,6 +548,12 @@ typedef enum
|
||||
UNARYOP_DEC,
|
||||
} UnaryOp;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
POSTUNARYOP_INC,
|
||||
POSTUNARYOP_DEC,
|
||||
} PostUnaryOp;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VARDECL_CONST = 0,
|
||||
@@ -569,6 +568,7 @@ typedef enum
|
||||
VISIBLE_MODULE,
|
||||
VISIBLE_LOCAL,
|
||||
VISIBLE_PUBLIC,
|
||||
VISIBLE_EXTERN,
|
||||
} Visibility;
|
||||
|
||||
|
||||
@@ -583,3 +583,8 @@ typedef enum
|
||||
ATTR_ERROR = 1 << 6,
|
||||
ATTR_TYPEDEF = 1 << 7
|
||||
} AttributeDomains;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CALL_CONVENTION_NORMAL = 0,
|
||||
} CallConvention;
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#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)
|
||||
{
|
||||
// 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);
|
||||
if (decl && !decl_ok(decl)) return false;
|
||||
SEMA_ERROR(expr->identifier_expr.identifier, "Ambiguous symbol '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity",
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
case DECL_GENERIC:
|
||||
return sema_expr_analyse_generic_call(context, to, expr);
|
||||
case DECL_POISONED:
|
||||
return false;
|
||||
default:
|
||||
SEMA_ERROR(expr->loc, "The expression cannot be called.");
|
||||
return false;
|
||||
@@ -253,7 +262,7 @@ static inline bool sema_expr_analyse_enum_constant(Context *context, Expr *expr,
|
||||
if (enum_constant->name.string == name)
|
||||
{
|
||||
assert(enum_constant->resolve_status == RESOLVE_DONE);
|
||||
expr_replace(expr, decl->enum_constant.expr);
|
||||
expr_replace(expr, enum_constant->enum_constant.expr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -335,7 +344,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex
|
||||
if (is_pointer)
|
||||
{
|
||||
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->resolve_status = RESOLVE_DONE;
|
||||
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");
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
case TOKEN_STAR:
|
||||
case UNARYOP_DEREF:
|
||||
return sema_expr_analyse_deref(context, to, expr, inner);
|
||||
case TOKEN_AMP:
|
||||
case UNARYOP_ADDR:
|
||||
return sema_expr_analyse_addr(context, to, expr, inner);
|
||||
case TOKEN_MINUS:
|
||||
case UNARYOP_NEG:
|
||||
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);
|
||||
case TOKEN_NOT:
|
||||
case UNARYOP_NOT:
|
||||
return sema_expr_analyse_not(context, to, expr, inner);
|
||||
case TOKEN_PLUSPLUS:
|
||||
case TOKEN_MINUSMINUS:
|
||||
case UNARYOP_DEC:
|
||||
case UNARYOP_INC:
|
||||
return sema_expr_analyse_incdec(context, to, expr, inner);
|
||||
default:
|
||||
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;
|
||||
|
||||
assert(expr->post_expr.operator == TOKEN_PLUSPLUS || expr->post_expr.operator == TOKEN_MINUSMINUS);
|
||||
|
||||
return sema_expr_analyse_incdec(context, to, expr, inner);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
@@ -58,6 +58,7 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl
|
||||
case VISIBLE_PUBLIC:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_EXTERN:
|
||||
case VISIBLE_LOCAL:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility);
|
||||
break;
|
||||
@@ -125,12 +126,11 @@ void gencontext_print_llvm_ir(GenContext *context)
|
||||
}
|
||||
|
||||
|
||||
|
||||
LLVMValueRef gencontext_emit_alloca(GenContext *context, Decl *decl)
|
||||
LLVMValueRef gencontext_emit_alloca(GenContext *context, LLVMTypeRef type, const char *name)
|
||||
{
|
||||
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder);
|
||||
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);
|
||||
return alloca;
|
||||
}
|
||||
@@ -161,11 +161,24 @@ void llvm_codegen(Context *context)
|
||||
gencontext_init(&gen_context, context);
|
||||
gencontext_begin_module(&gen_context);
|
||||
// EmitDeferred()
|
||||
VECEACH(context->external_symbol_list, i)
|
||||
{
|
||||
gencontext_emit_extern_decl(&gen_context, context->external_symbol_list[i]);
|
||||
}
|
||||
VECEACH(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:
|
||||
LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate();
|
||||
LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, build_options.optimization_level);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
|
||||
@@ -1,10 +1,36 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_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)
|
||||
{
|
||||
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:
|
||||
return expr->identifier_expr.decl->var.backend_ref;
|
||||
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);
|
||||
case EXPR_ACCESS:
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
UnaryOp unary_op = unaryop_from_token(expr->unary_expr.operator);
|
||||
switch (unary_op)
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_ERROR:
|
||||
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:
|
||||
return LLVMBuildLoad(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
||||
case UNARYOP_INC:
|
||||
TODO
|
||||
return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, 1, false);
|
||||
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)
|
||||
{
|
||||
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
|
||||
@@ -205,7 +265,22 @@ static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *ex
|
||||
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)
|
||||
@@ -218,17 +293,17 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool
|
||||
|
||||
LLVMValueRef lhs_value;
|
||||
LLVMValueRef rhs_value;
|
||||
if (load_lhs_after_rhs)
|
||||
if (lhs_addr)
|
||||
{
|
||||
rhs_value = gencontext_emit_expr(context, rhs);
|
||||
lhs_value = gencontext_emit_expr(context, lhs);
|
||||
lhs_value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(lhs->type), lhs_addr, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
|
||||
switch (binary_op)
|
||||
@@ -249,6 +324,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool
|
||||
case BINARYOP_MULT_MOD:
|
||||
return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
|
||||
case BINARYOP_SUB:
|
||||
case BINARYOP_SUB_MOD:
|
||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||
{
|
||||
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");
|
||||
}
|
||||
if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub");
|
||||
// TODO insert trap
|
||||
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");
|
||||
return gencontext_emit_sub_int(context, lhs->type->canonical, binary_op == BINARYOP_SUB_MOD, lhs_value, rhs_value);
|
||||
case BINARYOP_ADD:
|
||||
case BINARYOP_ADD_MOD:
|
||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||
{
|
||||
assert(type_is_integer(rhs->type->canonical));
|
||||
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");
|
||||
// TODO insert trap
|
||||
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");
|
||||
return gencontext_emit_add_int(context, lhs->type->canonical, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value);
|
||||
case BINARYOP_DIV:
|
||||
if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv");
|
||||
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)
|
||||
{
|
||||
Expr *lhs = expr->unary_expr.expr;
|
||||
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;
|
||||
return gencontext_emit_post_inc_dec(context, expr->post_expr.expr, expr->post_expr.operator == POSTUNARYOP_INC ? 1 : -1, false);
|
||||
}
|
||||
|
||||
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);
|
||||
assert(base_op != BINARYOP_ERROR);
|
||||
LLVMValueRef value = gencontext_emit_binary(context, expr, true, base_op);
|
||||
gencontext_emit_assign(context, expr->binary_expr.left, value);
|
||||
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
|
||||
LLVMValueRef value = gencontext_emit_binary(context, expr, addr, base_op);
|
||||
LLVMBuildStore(context->builder, value, addr);
|
||||
return value;
|
||||
}
|
||||
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);
|
||||
gencontext_emit_assign(context, expr->binary_expr.left, value);
|
||||
LLVMBuildStore(context->builder, value, addr);
|
||||
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)
|
||||
@@ -515,13 +553,19 @@ LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
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) {
|
||||
LLVMSetInstructionCallConv(fncallret, LLVMX86StdcallCallConv);
|
||||
if (function->func.function_signature.convention)
|
||||
{
|
||||
LLVMSetFunctionCallConv(call, LLVMX86StdcallCallConv);
|
||||
}*/
|
||||
return call;
|
||||
}
|
||||
|
||||
|
||||
@@ -545,14 +589,22 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -148,10 +145,8 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_FUNC);
|
||||
char workbuf[2048] = { '\0' };
|
||||
char *external_name = mangle_name(workbuf, decl);
|
||||
// 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));
|
||||
|
||||
// Specify appropriate storage class, visibility and call convention
|
||||
@@ -173,6 +168,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
||||
switch (decl->visibility)
|
||||
{
|
||||
case VISIBLE_LOCAL:
|
||||
case VISIBLE_EXTERN:
|
||||
flags |= LLVMDIFlagPrivate;
|
||||
break;
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include <llvm-c/Core.h>
|
||||
@@ -65,6 +65,7 @@ typedef struct
|
||||
BreakContinue break_continue_stack[BREAK_STACK_MAX];
|
||||
size_t break_continue_stack_index;
|
||||
LLVMTypeRef error_type;
|
||||
Type **generated_types;
|
||||
} GenContext;
|
||||
|
||||
|
||||
@@ -75,7 +76,7 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr);
|
||||
LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type);
|
||||
void gencontext_emit_debug_location(GenContext *context, SourceRange location);
|
||||
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_block(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_extern_decl(GenContext *context, Decl *decl);
|
||||
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr);
|
||||
#define LLVMTYPE(type) type->backend_type
|
||||
static inline LLVMValueRef gencontext_load_expr(GenContext *context, LLVMValueRef value)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#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)
|
||||
{
|
||||
vec_add(context->generated_types, type);
|
||||
type->backend_type = gencontext_create_basic_llvm_type(context, type);
|
||||
}
|
||||
void gencontext_begin_module(GenContext *context)
|
||||
{
|
||||
assert(!context->module && "Expected no module");
|
||||
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);
|
||||
LLVMSetModuleDataLayout(context->module, target_data_layout());
|
||||
LLVMSetSourceFileName(context->module, full_path, strlen(context->ast_context->file->full_path));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
@@ -22,7 +22,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||
{
|
||||
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 debug info
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#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));
|
||||
}
|
||||
// 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);
|
||||
return type;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
||||
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);
|
||||
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)
|
||||
{
|
||||
LLVMTypeRef base_llvm_type = BACKEND_TYPE(type->pointer);
|
||||
vec_add(context->generated_types, 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);
|
||||
|
||||
vec_add(context->generated_types, 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)
|
||||
{
|
||||
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);
|
||||
switch (type->type_kind)
|
||||
{
|
||||
|
||||
@@ -1,11 +1,63 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
@@ -140,6 +140,7 @@ static void recover_top_level(void)
|
||||
case TOKEN_UNION:
|
||||
case TOKEN_ENUM:
|
||||
case TOKEN_MACRO:
|
||||
case TOKEN_EXTERN:
|
||||
return;
|
||||
default:
|
||||
advance();
|
||||
@@ -188,6 +189,38 @@ do { if (!try_consume(TOKEN_COMMA) && tok.type != TOKEN_RPAREN) { \
|
||||
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()
|
||||
{
|
||||
CONSUME_OR(TOKEN_LBRACE, &poisoned_ast);
|
||||
@@ -213,23 +246,44 @@ static Ast* parse_function_block()
|
||||
return ast;
|
||||
}
|
||||
|
||||
static Path *parse_path(void)
|
||||
|
||||
|
||||
static Path *parse_path_prefix()
|
||||
{
|
||||
if (tok.type != TOKEN_IDENT || next_tok.type != TOKEN_SCOPE) return NULL;
|
||||
|
||||
Path *path = malloc_arena(sizeof(Path));
|
||||
memset(path, 0, sizeof(Path));
|
||||
char *scratch_ptr = current_context->path_scratch;
|
||||
size_t offset = 0;
|
||||
|
||||
path->sub_module = tok;
|
||||
|
||||
if (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE)
|
||||
Path *path = CALLOCS(Path);
|
||||
path->span = tok.span;
|
||||
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();
|
||||
path->module = path->sub_module;
|
||||
path->sub_module = tok;
|
||||
scratch_ptr[offset++] = ':';
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -257,7 +311,7 @@ static Path *parse_path(void)
|
||||
*/
|
||||
static inline TypeInfo *parse_base_type(void)
|
||||
{
|
||||
Path *path = parse_path();
|
||||
Path *path = parse_path_prefix();
|
||||
if (path)
|
||||
{
|
||||
TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||
@@ -1352,6 +1406,7 @@ static Ast *parse_stmt(void)
|
||||
case TOKEN_MACRO:
|
||||
case TOKEN_MODULE:
|
||||
case TOKEN_PUBLIC:
|
||||
case TOKEN_EXTERN:
|
||||
case TOKEN_STRUCT:
|
||||
case TOKEN_THROWS:
|
||||
case TOKEN_TYPEDEF:
|
||||
@@ -1454,8 +1509,8 @@ static inline bool parse_optional_module_params(Token **tokens)
|
||||
|
||||
/**
|
||||
* module
|
||||
* : MODULE IDENT ';'
|
||||
* | MODULE IDENT '(' module_params ')' ';'
|
||||
* : MODULE path ';'
|
||||
* | MODULE path '(' module_params ')' ';'
|
||||
*/
|
||||
static inline void parse_module(void)
|
||||
{
|
||||
@@ -1466,12 +1521,16 @@ static inline void parse_module(void)
|
||||
return;
|
||||
}
|
||||
|
||||
Token name = tok;
|
||||
Path *path = parse_module_path();
|
||||
|
||||
// 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();
|
||||
return;
|
||||
}
|
||||
@@ -1480,11 +1539,11 @@ static inline void parse_module(void)
|
||||
Token *generic_parameters = NULL;
|
||||
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();
|
||||
return;
|
||||
}
|
||||
context_set_module(current_context, name, generic_parameters);
|
||||
context_set_module(current_context, path, generic_parameters);
|
||||
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 IDENT ';'
|
||||
* | IMPORT IDENT AS IDENT ';'
|
||||
* : IMPORT PATH ';'
|
||||
* | IMPORT PATH ':' import_selective ';'
|
||||
* | IMPORT IDENT AS 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
|
||||
*/
|
||||
static inline bool parse_import()
|
||||
{
|
||||
|
||||
advance_and_verify(TOKEN_IMPORT);
|
||||
|
||||
Token module_name = tok;
|
||||
|
||||
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 (tok.type != TOKEN_IDENT)
|
||||
{
|
||||
if (!parse_macro_parameter_list(&generic_parameters)) 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;
|
||||
SEMA_ERROR(tok, "Import statement should be followed by the name of the module to import.");
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
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))
|
||||
{
|
||||
Path *path = parse_path();
|
||||
Path *path = parse_path_prefix();
|
||||
|
||||
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);
|
||||
}
|
||||
Path *path = parse_path();
|
||||
Path *path = parse_path_prefix();
|
||||
Decl *decl = decl_new(DECL_GENERIC, tok, visibility);
|
||||
decl->generic_decl.path = path;
|
||||
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);
|
||||
func->func.function_signature.rtype = return_type;
|
||||
|
||||
Path *path = parse_path();
|
||||
Path *path = parse_path_prefix();
|
||||
if (path || tok.type == TOKEN_TYPE_IDENT)
|
||||
{
|
||||
// Special case, actually an extension
|
||||
@@ -2433,9 +2533,6 @@ static inline Decl *parse_enum_declaration(Visibility visibility)
|
||||
return decl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static inline bool parse_conditional_top_level(Decl ***decls)
|
||||
{
|
||||
CONSUME_OR(TOKEN_LBRACE, false);
|
||||
@@ -2505,6 +2602,9 @@ static inline bool check_no_visibility_before(Visibility visibility)
|
||||
case VISIBLE_LOCAL:
|
||||
SEMA_ERROR(tok, "Unexpected 'local' before '%.*s'.", tok.span.length, tok.start);
|
||||
return false;
|
||||
case VISIBLE_EXTERN:
|
||||
SEMA_ERROR(tok, "Unexpected 'extern' before '%.*s'.", tok.span.length, tok.start);
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -2541,6 +2641,9 @@ static inline Decl *parse_top_level(void)
|
||||
visibility = VISIBLE_LOCAL;
|
||||
advance();
|
||||
break;
|
||||
case TOKEN_EXTERN:
|
||||
visibility = VISIBLE_EXTERN;
|
||||
advance();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2651,7 +2754,7 @@ static Expr *parse_unary_expr(Expr *left)
|
||||
TokenType operator_type = tok.type;
|
||||
|
||||
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;
|
||||
advance();
|
||||
Expr *right_side = parse_precedence(rule_precedence);
|
||||
@@ -2667,7 +2770,7 @@ static Expr *parse_post_unary(Expr *left)
|
||||
assert(expr_ok(left));
|
||||
Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, tok);
|
||||
unary->post_expr.expr = left;
|
||||
unary->post_expr.operator = tok.type;
|
||||
unary->post_expr.operator = post_unaryop_from_token(tok.type);
|
||||
advance();
|
||||
return unary;
|
||||
}
|
||||
@@ -3141,9 +3244,17 @@ static Expr *parse_type_access(TypeInfo *type)
|
||||
advance_and_verify(TOKEN_DOT);
|
||||
expr->type_access.name = tok;
|
||||
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected a function name or value", &poisoned_expr);
|
||||
|
||||
return expr;
|
||||
switch (tok.type)
|
||||
{
|
||||
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)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Path *path = parse_path();
|
||||
Path *path = parse_path_prefix();
|
||||
switch (tok.type)
|
||||
{
|
||||
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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Decl *context_find_ident(Context *context, const char *symbol)
|
||||
{
|
||||
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];
|
||||
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;
|
||||
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)
|
||||
@@ -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 (!context_add_local(context, decl)) return decl_poison(decl);
|
||||
if (!sema_add_local(context, decl)) return decl_poison(decl);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -963,7 +978,7 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
Decl **params = func->func.function_signature.params;
|
||||
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 (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;
|
||||
context_pop_scope(context);
|
||||
context->current_scope = NULL;
|
||||
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 (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.")
|
||||
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)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false;
|
||||
// TODO assign numbers to constants
|
||||
return true;
|
||||
if (!sema_resolve_type_info(context, decl->enums.type_info)) return false;
|
||||
uint64_t value = 0;
|
||||
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)
|
||||
@@ -1107,6 +1166,7 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
}
|
||||
|
||||
decl->resolve_status = RESOLVE_RUNNING;
|
||||
decl->module = context->module;
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_THROWS:
|
||||
@@ -1114,27 +1174,29 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl);
|
||||
context_add_header_decl(context, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_FUNC:
|
||||
if (!sema_analyse_func(context, decl)) return decl_poison(decl);
|
||||
context_add_header_decl(context, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_MACRO:
|
||||
if (!sema_analyse_macro(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_VAR:
|
||||
if (!sema_analyse_global(context, decl)) return decl_poison(decl);
|
||||
context_add_header_decl(context, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_TYPEDEF:
|
||||
if (!sema_analyse_typedef(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_ENUM:
|
||||
if (!sema_analyse_enum(context, decl)) return decl_poison(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_ERROR:
|
||||
if (!sema_analyse_error(context, decl)) return decl_poison(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_GENERIC:
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static inline void sema_process_imports(Context *context)
|
||||
void sema_analysis_pass_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)
|
||||
@@ -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)
|
||||
{
|
||||
assert(!type_info->unresolved.path && "TODO");
|
||||
|
||||
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);
|
||||
}
|
||||
Decl *ambiguous_decl;
|
||||
Decl *decl = sema_resolve_symbol(context, type_info->unresolved.name_loc.string, type_info->unresolved.path, &ambiguous_decl);
|
||||
|
||||
if (!decl)
|
||||
{
|
||||
@@ -1261,6 +1337,12 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *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)
|
||||
{
|
||||
case DECL_THROWS:
|
||||
@@ -1306,6 +1388,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
||||
UNREACHABLE
|
||||
|
||||
}
|
||||
|
||||
bool sema_resolve_type_shallow(Context *context, TypeInfo *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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include <sys/stat.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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
File *file = source_file_from_position(loc);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
@@ -34,6 +34,8 @@ typedef struct _Entry
|
||||
|
||||
static SymTab symtab;
|
||||
|
||||
const char *main_name;
|
||||
|
||||
void symtab_init(uint32_t capacity)
|
||||
{
|
||||
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);
|
||||
}
|
||||
size_t size = capacity * sizeof(SymEntry);
|
||||
size_t size = capacity *sizeof(SymEntry);
|
||||
symtab.entries = MALLOC(size);
|
||||
memset(symtab.entries, 0, size);
|
||||
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);
|
||||
assert(type == (TokenType)i);
|
||||
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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
@@ -190,6 +190,8 @@ const char *token_type_to_string(TokenType type)
|
||||
return "else";
|
||||
case TOKEN_ENUM:
|
||||
return "enum";
|
||||
case TOKEN_EXTERN:
|
||||
return "extern";
|
||||
case TOKEN_ERROR_TYPE:
|
||||
return "error";
|
||||
case TOKEN_FALSE:
|
||||
@@ -351,6 +353,20 @@ const char *token_type_to_string(TokenType type)
|
||||
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)
|
||||
{
|
||||
return type >= TOKEN_VOID && type <= TOKEN_C_ULONGLONG;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "benchmark.h"
|
||||
#include <time.h>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
|
||||
void bench_begin(void);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
static const char* test_parse = "struct Node\n"
|
||||
"{\n"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "tests.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
|
||||
void compiler_tests(void);
|
||||
@@ -18,11 +18,13 @@ int main(int argc, const char *argv[])
|
||||
compiler_tests();
|
||||
break;
|
||||
case COMMAND_COMPILE:
|
||||
compile_file();
|
||||
compile_files(NULL);
|
||||
break;
|
||||
case COMMAND_BUILD:
|
||||
build();
|
||||
break;
|
||||
case COMMAND_COMPILE_RUN:
|
||||
case COMMAND_MISSING:
|
||||
case COMMAND_BUILD:
|
||||
case COMMAND_RUN:
|
||||
case COMMAND_CLEAN_RUN:
|
||||
case COMMAND_CLEAN:
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
@@ -13,4 +14,5 @@
|
||||
#include "errors.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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "errors.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "malloc.h"
|
||||
#include "lib.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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)
|
||||
{
|
||||
@@ -102,3 +105,69 @@ void path_get_dir_and_filename_from_full(const char *full_path, char **filename,
|
||||
*dir_path = malloc(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
|
||||
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -10,6 +10,9 @@ const char* expand_path(const char* path);
|
||||
char *read_file(const char *path, size_t *return_size);
|
||||
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 file_find_top_dir();
|
||||
void file_add_wildcard_files(const char ***files, const char *path, bool recursive);
|
||||
|
||||
void init_arena(void);
|
||||
void *malloc_arena(unsigned long mem);
|
||||
void free_arena(void);
|
||||
@@ -293,5 +296,9 @@ static inline bool is_all_lower(const char* string)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef __printflike
|
||||
#define __printflike(x, y)
|
||||
#endif
|
||||
|
||||
char *strformat(const char *var, ...) __printflike(1, 2);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "malloc.h"
|
||||
#include "common.h"
|
||||
|
||||
static const size_t KB = 1024ul;
|
||||
// Use 1MB at a time.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include <stdarg.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