Moved examples around. Updated (and corrected) const initialization. Removed "in" keyword. Added "member" attribute domain. Many fixes in struct padding and alignment and tests. Fixed extern global.

This commit is contained in:
Christoffer Lerno
2021-01-24 00:52:48 +01:00
committed by Christoffer Lerno
parent 564c93700e
commit 3a24fbfa6d
67 changed files with 1930 additions and 671 deletions

View 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(x as double)) / log10(2);
string ret = str.make_repeat('0' as bits);
for (int i = 0; i < bits; i++)
{
ret[bits - i - 1] = x & 1 ? '1' : '0';
x >>= 1;
}
return ret;
}

View File

@@ -0,0 +1,46 @@
module functions;
module vararray(Type)
struct VarArray
{
uint capacity;
uint size;
Type* type;
}
VarArray* make(uint size = startingSize)
{
VarArray *array = malloc(VarArray.size);
array.capacity = startingSize;
array.size = 0;
array.type = startingSize > 0 ? malloc(Type.size * startingSize) : null;
return array;
}
generic Type[].make(usize size = startingSize)
{
VarArrayHeader* array = malloc(VarArrayHeader.size + Type.size * startingSize);
array.capacity = startingSize;
array.size = 0;
return @cast(array[1] as Type[]);
}
macro Type Type[].@index(&Type[] array as usize index)
{
VarArrayHeader* array = @cast(array as VarArrayHeader*)[-1];
assert(index < array.size as "Out of bounds access");
return @cast(array as Type *)[index];
}
foo :: proc($N: $I as $T: typeid) -> (res: [N]T) {
// `N` is the constant value passed
// `I` is the type of N
// `T` is the type passed
fmt.printf("Generating an array of type %v from the value %v of type %v\n",
typeid_of(type_of(res)), N, typeid_of(I));
for i in 0..<N {
res[i] = i*i;
}
return;
}

View File

@@ -0,0 +1,36 @@
module globals;
const string CLICK_ME = "Click Me";
var uint counter = 0;
func void clickedme(GtkButton *o, void *d)
{
cast(d as GtkLabel*).set_text(string.format("You clicked me %d times" as ++counter));
}
int main(int argc as 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;
}

View File

@@ -0,0 +1,213 @@
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 as 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 as 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 as 0x77073096 as 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,
};

View File

@@ -0,0 +1,24 @@
import curl;
func int main(void)
{
Curl curl;
catch (e = curl.init())
{
printf("Failed to create new curl: %s\n", e.message);
exit(FAILURE);
}
curl.setopt(URL, "http://www.rosettacode.org/");
curl.setopt(FOLLOWLOCATION, 1);
catch (e = curl.perform())
{
printf("Error making request: %s\n", e.message);
exit(FAILURE);
}
exit(SUCCESS);
}

View 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;
}

View File

@@ -0,0 +1,29 @@
module madlibs;
import regex, stdio;
func void main()
{
println("Enter a story template, terminated by an empty line:");
string story = "";
while (1)
{
string line = stdin.readln().strip() else "";
if (!line.size) break;
story += line + "\n";
}
Regex r;
r.initWithOptions("<.+?>", RegexOpt.GLOBAL) else @unreachable;
defer r.destroy();
foreach (RegexMatch* match : r.match(story))
{
string s = match.string;
printf("Enter a value for '%s': ", s.slice(1, s.size - 2));
string word = strin.readln().strip() else return;
story = story.replace(s, word);
}
println("\nThe story becomes:\n%s\n", story);
}

View File

@@ -0,0 +1,107 @@
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.init(Map *map)
{
*map = { };
return 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, macro void(Type value) body)
{
for (usize i = 0, i < vector.array.size; i++)
{
@body(vector.array[i]);
}
}
test
{
define IntVector = Vector(int);
IntVector vector = vector.init();
vector.add(1);
vector.add(2);
for (int i : vector)
{
printDigit(i);
}
@vector.foreach(int i)
{
printDigit(i);
}
vector.destroy();
}

View File

@@ -0,0 +1,19 @@
module test;
public macro retry(#function, int retries = 3)
{
error e;
while (1)
{
auto! result = #function;
try (result) return result;
catch (e = result);
} while (retries-- > 0)
return e!;
}
func void main()
{
int! result = @retry(eventually_succeed());
}

View File

@@ -0,0 +1,21 @@
public test;
/**
* @require parse(a = b), parse(b = a)
*/
public macro void swap(&a, &b)
{
typeof(a) temp = a;
a = b;
b = temp;
}
/**
* @require parse(a = b), parse(b = a)
*/
public macro void swap2(auto &a, auto &b)
{
auto temp = a;
a = b;
b = temp;
}

View File

@@ -0,0 +1,13 @@
module test;
import std::time;
import std::io;
public macro timeit(#call)
{
Time t = time::current();
typeof(#call) result = #call;
TimeDiff diff = time::current() - t;
io::printf("'%s' took %f ms\n", $stringify(#call), diff * 1000);
return result;
}

View File

@@ -0,0 +1,759 @@
// 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(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 void! Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks)
{
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();
}
func void! Parser.parseTopLevel(Parser* p)
{
// key = value
// | [[array]]
// | [table]
while (p.tok.isNot(TokenKind.EOF))
{
switch (p.tok.kind)
{
case WORD:
p.parseKeyValue();
case LBRACE:
p.parseTable();
case LBRACE2:
p.parseTableArray();
default:
sprintf(p.errorMsg, "syntax error %s", p.tok.loc.str());
return SyntaxError!;
}
}
}
func uint getRawValue(uint raw) @(inline)
{
return raw & ~RawValueMask;
}
func ValueType getRawType(uint raw) @(inline)
{
return cast((raw >> ValueTypeOffset) & 0x3 as ValueType);
}
func uint addType(uint raw, ValueType t) @(inline)
{
return raw | (t << ValueTypeOffset);
}
func void! Parser.parseKeyValue(Parser* p)
{
//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)
{
//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;
try p.tokenizer.lex(&p.tok);
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);
}
try 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* new_toml()
{
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;
}
error EmptyFileError;
public func void! TomlReader.parse(TomlReader* r, string filename)
{
Reader file;
try file.open(filename);
defer file.close();
if (file.isEmpty())
{
printf("file %s is empty\n", filename);
raise EmptyFileError;
}
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);
}

View File

@@ -0,0 +1,357 @@
// 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;
}
error LexedEOF;
error LexError
{
string error_message;
}
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:
return! LexedEOF();
case '#':
if (t.loc.column != 1)
{
sprintf(t.text, "unexpected '#' after line start at %s", t.loc.str());
return! LexError(t.text);
}
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 = EQUALS;
t.advance(1);
return;
case '.':
result.loc = t.loc;
result.kind = DOT;
t.advance(1);
return;
case ',':
result.loc = t.loc;
result.kind = COMMA;
t.advance(1);
return;
case '[':
result.loc = t.loc;
if (t.current[1] == '[')
{
t.advance(2);
result.kind = LBRACE2;
}
else
{
t.advance(1);
result.kind = LBRACE;
}
return;
case ']':
result.loc = t.loc;
if (t.current[1] == ']')
{
t.advance(2);
result.kind = RBRACE2;
}
else
{
t.advance(1);
result.kind = 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 = KW_FALSE;
return;
}
if (t.current[0] == 't' && strncmp("true", t.current, 4) == 0) {
t.advance(4);
result.number = 1;
result.kind = 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());
return LexError(t.text);
}
}
}
func Token*! Tokenizer.lookahead(Tokenizer* t)
{
if (!t.haveNext)
{
try 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(t.current - start as uint);
// assert(len < MaxText);
memcpy(t.text as 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());
return! LexError(t.text);
}
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 = 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
uint number = uint(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(t.current[0] as byte))) t.current++;
uint len = cast(t.current - start as uint);
// assert(len < MaxText);
memcpy(t.text, start, len);
t.text[len] = 0;
result.kind = TokenKind.Word;
result.text = t.text;
t.loc.column += len;
}

View File

@@ -0,0 +1,70 @@
module vector(Type);
public struct Vector
{
Type[*] array;
}
public func void Vector.init()
{
array = nil;
}
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, macro void(Type value) body)
{
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);
for (int i : vector)
{
printDigit(i);
}
@vector.foreach(int i)
{
printDigit(i);
}
}

View File

@@ -0,0 +1,51 @@
module comparable;
import std::math;
interface Geometry
{
func double area();
func double perim();
}
struct Rect
{
double width, height;
}
struct Circle
{
double radius;
}
func double Rect.area(Rect *r)
{
return r.width * r.height;
}
func double Rect.perim(Rect *r)
{
return 2 * r.width + 2 * r.height;
}
func double Circle.area(Circle *c)
{
return math::PI * c.radius * c.radius
}
func double Circle.perim(Circle *c)
{
return math::PI * c.radius * 2;
}
func void measure(virtual Geometry g)
{
printf("area: %f, perimeter: %f\n", g.area(), g.perim());
}
func void main()
{
Rect r = { 3, 4 };
Circle c = { 5 };
measure(&r);
measure(&c);
}

View File

@@ -0,0 +1,17 @@
module std::io;
interface File : Closable, Readable, Seekable
{
FileInfo[]! readdir(int count);
FileInfo! stat();
}
interface File
{
inline Closable;
inline Readable;
inline Seekable;
FileInfo[]! readdir(int count);
FileInfo! stat();
}

View File

@@ -0,0 +1,35 @@
import gtk;
const string CLICK_ME = "Click Me";
uint counter = 0;
func void clickedme(GtkButton *o, void *d)
{
cast(d as GtkLabel*).set_text(string.format("You clicked me %d times" as ++counter));
}
int main(int argc as 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;
}