├── .gitignore ├── xml_parser ├── examples │ ├── test2.xml │ ├── test1.xml │ ├── test3.xml │ ├── test4.xml │ ├── goblin_camp.xml │ └── example1.xml ├── main.c2 └── xml_structure.c2 ├── unit_test ├── mod2.c2 ├── mod1.c2 ├── mod2_test.c2 ├── test_main.c2 └── mod1_test.c2 ├── graphics └── sdl │ ├── c2sdl2.gif │ ├── recipe.txt │ └── main.c2 ├── load_file ├── files │ ├── data1.dat │ └── file2.txt ├── recipe.txt └── main.c2 ├── json_parser ├── example5.json ├── example4.json ├── example2.json ├── example1.json ├── example3.json ├── json_serialize.c2 ├── json_builder.c2 ├── main.c2 └── json_data.c2 ├── toml_parser ├── example5.toml ├── example7.toml ├── example1.toml ├── example4.toml ├── example3.toml ├── example6.toml ├── example2.toml ├── main.c2 └── toml_tokenizer.c2 ├── frog ├── main.c2 ├── recipe.txt ├── utils.c2 ├── list.c2 └── frog.c2 ├── plugin ├── plugin2.c2 ├── other.c2 ├── plugin1.c2 ├── test_lib.c2 └── plugin_mgr.c2 ├── Makefile ├── source_lib_use └── main.c2 ├── file_ops └── main.c2 ├── multicast ├── receiver.c2 ├── sender.c2 ├── multicast_sender.c2 └── multicast_receiver.c2 ├── inline_asm └── main.c2 ├── string_buffer └── main.c2 ├── base64 ├── main.c2 └── base64.c2 ├── common ├── color.c2 ├── file │ ├── writer.c2 │ └── reader.c2 ├── string_buffer.c2 └── logger.c2 ├── lua ├── script1.lua ├── script2.lua ├── main.c2 └── script.c2 ├── logger └── main.c2 ├── longjmp └── jump.c2 ├── yaml_parser ├── main.c2 ├── yaml_dump.c2 ├── yaml_data.c2 ├── yaml_iterator.c2 ├── yaml_tokenizer.c2 └── yaml_parser.c2 ├── sha1 ├── main.c2 └── sha1.c2 ├── signals └── main.c2 ├── pthread └── pthread.c2 ├── dir_walker └── main.c2 ├── README.md ├── list ├── list.c2 └── main.c2 ├── event ├── main.c2 ├── example_socket.c2 └── events.c2 ├── sudoku ├── boards.c2 └── sudoku.c2 ├── socket └── server.c2 ├── comment_strip └── main.c2 └── recipe.txt /.gitignore: -------------------------------------------------------------------------------- 1 | output/ 2 | output_arm/ 3 | -------------------------------------------------------------------------------- /xml_parser/examples/test2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /load_file/files/file2.txt: -------------------------------------------------------------------------------- 1 | This is a random 2 | text file that is used 3 | 4 | to show how to embed TEXT files 5 | -------------------------------------------------------------------------------- /graphics/sdl/recipe.txt: -------------------------------------------------------------------------------- 1 | 2 | executable sdltest 3 | $warnings no-unused 4 | $backend c 5 | $use sdl2 dynamic 6 | $use sdl2_image dynamic 7 | main.c2 8 | end 9 | -------------------------------------------------------------------------------- /json_parser/example5.json: -------------------------------------------------------------------------------- 1 | [ 2, 2 | "19223201", 3 | "BootNotification", 4 | {"chargePointVendor": "VendorX", "chargePointModel": "SingleSocketCharger"} 5 | ] 6 | 7 | -------------------------------------------------------------------------------- /toml_parser/example5.toml: -------------------------------------------------------------------------------- 1 | [[deps]] 2 | name = "math" 3 | type = "dynamic" 4 | 5 | [[deps]] 6 | name = "bar" 7 | type = "static" 8 | 9 | [table] 10 | bla = "foo" 11 | -------------------------------------------------------------------------------- /load_file/recipe.txt: -------------------------------------------------------------------------------- 1 | 2 | plugin refs_generator [all-targets] 3 | 4 | executable test 5 | $warnings no-unused 6 | $backend c 7 | 8 | main.c2 9 | end 10 | 11 | -------------------------------------------------------------------------------- /frog/main.c2: -------------------------------------------------------------------------------- 1 | module frog_main; 2 | import frog; 3 | 4 | public fn i32 main(i32 argc, char** argv) { 5 | frog.init(); 6 | frog.dump(); 7 | frog.run(); 8 | 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /frog/recipe.txt: -------------------------------------------------------------------------------- 1 | 2 | executable frog 3 | $warnings no-unused 4 | $refs 5 | # $deps 6 | $generate-c single-module 7 | main.c2 8 | frog.c2 9 | list.c2 10 | utils.c2 11 | end 12 | -------------------------------------------------------------------------------- /plugin/plugin2.c2: -------------------------------------------------------------------------------- 1 | module plugin_main; 2 | 3 | //import plugin; 4 | 5 | import stdio; 6 | 7 | public fn void load(i32 arg) { 8 | stdio.printf("Plugin2.load() %d\n", arg); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /xml_parser/examples/test4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frog/utils.c2: -------------------------------------------------------------------------------- 1 | module utils; 2 | import stdlib local; 3 | 4 | public fn void init() { 5 | srand(1); 6 | } 7 | 8 | public fn u32 roll() { 9 | return cast((rand() % 6) + 1); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /plugin/other.c2: -------------------------------------------------------------------------------- 1 | module other; 2 | 3 | import stdio; 4 | 5 | i32 counter = 10; 6 | 7 | public fn i32 test1() { 8 | counter++; 9 | stdio.printf("counter %d\n", counter); 10 | 11 | return 0; 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /toml_parser/example7.toml: -------------------------------------------------------------------------------- 1 | 2 | [library] 3 | language = "C2" 4 | type = [ "source" ] 5 | 6 | [[module]] 7 | name = "foo" 8 | files = [ "file1.c2", "file2.c2", "file3.c2" ] 9 | 10 | [[module]] 11 | name = "bar" 12 | files = [ "file4.c2", "file5.c2", "file6.c2" ] 13 | 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | c2c 3 | 4 | errors: 5 | @( grep -n 'error:' `find . -name build.log` | sed -E 's/build.log:[0-9]+://' ; true ) 6 | 7 | warnings: 8 | @( grep -n '[[]-W' `find . -name build.log` | sed -E 's/build.log:[0-9]+://' ; true ) 9 | 10 | clean: 11 | rm -rf output 12 | -------------------------------------------------------------------------------- /json_parser/example4.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "1", 3 | "index_start_at": "56", 4 | "integer": "6", 5 | "float": "12.8328", 6 | "name": "Marcus", 7 | "surname": "Sawyer", 8 | "fullname": "Janet Murphy", 9 | "email": "tracy@hester.gt", 10 | "bool": "false" 11 | } 12 | -------------------------------------------------------------------------------- /json_parser/example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "UpdateAvailable": true, 3 | "Platform": 4 | { 5 | "Url": null, 6 | "Checksum": null, 7 | "Version": null 8 | }, 9 | "Application": 10 | { 11 | "Url": "DownloadUrl", 12 | "Checksum": "Checksum", 13 | "Version": "2.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /source_lib_use/main.c2: -------------------------------------------------------------------------------- 1 | module test_main; 2 | 3 | import foo; 4 | import bar; 5 | 6 | import stdio local; 7 | 8 | public fn i32 main(i32 argc, char** argv) { 9 | i32 res = 1; 10 | res = foo.test2(argc); 11 | res += bar.test1(1); 12 | res += bar.test2(1); 13 | 14 | printf("test %d\n", res); 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /toml_parser/example1.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | target = "x86_64-unknown-linux-gnu" 3 | cc = "arm-xilinx-linux-gnueabi-gcc" 4 | cflags = "-E -march=armv7-a -marm -mfpu=neon -mfloat-abi=hard --sysroot=.." 5 | path = "/opt/toolchain/sysrootfs/.." 6 | 7 | [[libdir]] 8 | dir = "../c2libs" 9 | 10 | [[libdir]] 11 | dir = "/tmp/bla" 12 | 13 | -------------------------------------------------------------------------------- /unit_test/mod2_test.c2: -------------------------------------------------------------------------------- 1 | module mod2; 2 | 3 | // NOTE: import c2test not needed without asserts! 4 | 5 | type Test struct @(unittest) { 6 | i32 x; 7 | } 8 | 9 | fn void Test.setup(Test* t) { 10 | t.x = 20; 11 | } 12 | 13 | fn void Test.teardown(Test* t) { } 14 | 15 | fn void Test.test1(Test* t) { 16 | } 17 | 18 | fn void Test.test2(Test* t) { 19 | } 20 | 21 | -------------------------------------------------------------------------------- /toml_parser/example4.toml: -------------------------------------------------------------------------------- 1 | [library] 2 | language = "C" 3 | type = [ "static", "dynamic" ] 4 | linkname = "mylink" 5 | 6 | [[deps]] 7 | name = "math" 8 | type = "dynamic" 9 | 10 | [[module]] 11 | name = "stdio" 12 | 13 | [[module]] 14 | name = "c_errno" 15 | 16 | [[module]] 17 | name = "libc_poll" 18 | 19 | type2 = [ "a", "b", "", "d", "a", "e" ] 20 | type-2_a = "3" 21 | -------------------------------------------------------------------------------- /toml_parser/example3.toml: -------------------------------------------------------------------------------- 1 | begin = "foo" 2 | 3 | [[fruit]] 4 | name = "apple" 5 | 6 | [fruit.physical] 7 | color = "red" 8 | shape = "round" 9 | 10 | [[fruit.variety]] 11 | name = "red delicious" 12 | 13 | [[fruit.variety]] 14 | name = "granny smith" 15 | 16 | [[fruit]] 17 | name = "banana" 18 | 19 | [[fruit.variety]] 20 | name = "plantain" 21 | -------------------------------------------------------------------------------- /json_parser/example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "update": "yes", 3 | "platform": 4 | { 5 | "version": "3.6.1.0", 6 | "url": "https://blabla/platform", 7 | "checksum": "1234456789", 8 | "size": "46000000" 9 | }, 10 | "application": 11 | { 12 | "version": "3.6.1.0", 13 | "url": "https://blabla/application", 14 | "checksum": "1234456789", 15 | "size": "7000000" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /file_ops/main.c2: -------------------------------------------------------------------------------- 1 | module event_test; 2 | 3 | import csignal local; 4 | import string local; 5 | import stdlib local; 6 | import stdio local; 7 | import unistd local; 8 | import file_utils; 9 | 10 | public fn i32 main(i32 argc, char** argv) { 11 | file_utils.Reader file; 12 | file.open(argv[1]); 13 | 14 | printf("%s", file.char_data()); 15 | 16 | file.close(); 17 | 18 | return 0; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /toml_parser/example6.toml: -------------------------------------------------------------------------------- 1 | # Created by developer X 2 | 3 | [room] 4 | public = true 5 | number = 1234 6 | name = "My First Room" 7 | domains = [ "Coders", "Goblins" ] 8 | descr = """ 9 | A very long 10 | multiline 11 | 12 | string 13 | """ 14 | 15 | [[obj]] 16 | spawn_id = "area1/chest_key" 17 | spawn_reset = "20m" 18 | spawn_msg = "A key falls out of the sky" 19 | 20 | [[exit]] 21 | name = "north" 22 | filter = "my_level_filter" 23 | 24 | [[exit]] 25 | name = "east" 26 | 27 | -------------------------------------------------------------------------------- /toml_parser/example2.toml: -------------------------------------------------------------------------------- 1 | first = "alpha" 2 | 3 | second = "dual" 4 | 5 | [a.b.c.d] 6 | key = "secret" 7 | 8 | [test.b] 9 | [toolchain] 10 | target = "x86_64-unknown-linux-gnu" 11 | cc = "arm-xilinx-linux-gnueabi-gcc" 12 | cflags = "-E -march=armv7-a -marm -mfpu=neon -mfloat-abi=hard --sysroot=.." 13 | path = "/opt/toolchain/sysrootfs/.." 14 | 15 | [[libdir]] 16 | dir = "../c2libs" 17 | 18 | [[libdir]] 19 | dir = "/tmp/bla" 20 | 21 | empty = "" 22 | 23 | key = [ 1, 2, 3 ] 24 | 25 | -------------------------------------------------------------------------------- /multicast/receiver.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | 3 | import events; 4 | import multicast_receiver local; 5 | 6 | import stdio local; 7 | import string local; 8 | import unistd; 9 | 10 | const char[] MULTICAST_IP = "225.0.0.37"; 11 | const u16 MULTICAST_PORT = 12345; 12 | 13 | 14 | public fn i32 main() { 15 | events.Base* evbase = events.Base.create(); 16 | 17 | Receiver receiver; 18 | receiver.init(evbase, MULTICAST_IP, MULTICAST_PORT); 19 | 20 | u8 ret = evbase.mainloop(); 21 | 22 | receiver.destroy(); 23 | evbase.destroy(); 24 | return ret; 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /inline_asm/main.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | import stdio; 3 | 4 | fn u64 rdtsc() @(inline) { 5 | #if ARCH_X86_64 6 | u32 lo; 7 | u32 hi; 8 | asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); 9 | u64 res = hi; 10 | res <<= 32; 11 | res |= lo; 12 | return res; 13 | #elif ARCH_ARM64 14 | u64 val; 15 | asm volatile("mrs %0, cntvct_el0" : "=r" (val)); 16 | return val; 17 | #else 18 | return 0; 19 | #endif 20 | } 21 | 22 | public fn i32 main() { 23 | stdio.printf("time is now: %d\n", rdtsc()); 24 | stdio.printf("time is now: %d\n", rdtsc()); 25 | stdio.printf("time is now: %d\n", rdtsc()); 26 | return 0; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /load_file/main.c2: -------------------------------------------------------------------------------- 1 | module test_main; 2 | import stdio local; 3 | 4 | const char[] Data1 @(embed="files/data1.dat"); 5 | const char[] File2 @(embed="files/file2.txt"); 6 | 7 | fn void hex_dump(const u8* data, u32 len) { 8 | for (u32 i=0; i - run module tests 12 | public fn i32 main(i32 argc, char** argv) { 13 | // TODO proper argument parsing 14 | if (argc == 1) { 15 | return c2test_main.run_tests(); 16 | } 17 | if (argc == 2) { 18 | if (string.strcmp("-l", argv[1]) == 0) { 19 | c2test_main.show_tests(); 20 | return 0; 21 | } else { 22 | // TODO find matching group 23 | } 24 | } 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /string_buffer/main.c2: -------------------------------------------------------------------------------- 1 | module tester; 2 | 3 | import stdio local; 4 | import utils; 5 | 6 | type Callback fn void (utils.StringBuffer* buf, const char* text); 7 | 8 | public fn i32 main() { 9 | utils.StringBuffer* buf = utils.StringBuffer.create(8); 10 | //utils.StringBuffer* buf = utils.StringBuffer.create(100); 11 | 12 | buf.add("hello "); 13 | buf.add("this "); 14 | buf.add("is "); 15 | buf.add("a "); 16 | buf.add("test!"); 17 | 18 | printf("size = %d\n", buf.size()); 19 | printf("data = '%s'\n", buf.data()); 20 | 21 | Callback cb = utils.StringBuffer.add; 22 | 23 | buf.clear(); 24 | buf.destroy(); 25 | return 0; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /base64/main.c2: -------------------------------------------------------------------------------- 1 | module tester; 2 | 3 | import stdio local; 4 | import string local; 5 | import base64; 6 | 7 | fn void test(const char* input) { 8 | i8[64] encoded; 9 | u8[64] decoded; 10 | memset(encoded, 0, sizeof(encoded)); 11 | memset(decoded, 0, sizeof(decoded)); 12 | base64.encode(cast(input), cast(strlen(input)), encoded); 13 | base64.decode(encoded, cast(strlen(cast(encoded))), decoded); 14 | printf("[%s]\n", input); 15 | printf(" encoded: %s\n", cast(encoded)); 16 | printf(" decoded: %s\n", cast(decoded)); 17 | } 18 | 19 | public fn i32 main() { 20 | test("pleasure."); 21 | test("The quick brown fox jumped"); 22 | return 0; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /multicast/sender.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | 3 | import multicast_sender local; 4 | 5 | import stdio local; 6 | import string local; 7 | import unistd; 8 | 9 | const char[] MULTICAST_IP = "225.0.0.37"; 10 | const u16 MULTICAST_PORT = 12345; 11 | 12 | 13 | public fn i32 main() { 14 | Sender sender; 15 | sender.init(MULTICAST_IP, MULTICAST_PORT); 16 | 17 | u32 nr = 0; 18 | while (1) { 19 | const char* message = "Hello Multicast World!"; 20 | char[64] tmp; 21 | sprintf(tmp, "%s %d", message, nr); 22 | nr++; 23 | 24 | sender.broadcast(cast(tmp), cast(strlen(tmp))); 25 | unistd.sleep(1); 26 | } 27 | 28 | 29 | sender.destroy(); 30 | return 0; 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /common/color.c2: -------------------------------------------------------------------------------- 1 | module color; 2 | 3 | public const char[] Black = "\033[0;30m"; 4 | public const char[] Red = "\033[0;31m"; 5 | public const char[] Green = "\033[0;32m"; 6 | public const char[] Yellow = "\033[0;33m"; 7 | public const char[] Blue = "\033[0;34m"; 8 | public const char[] Magenta = "\033[0;35m"; 9 | public const char[] Cyan = "\033[0;36m"; 10 | public const char[] Grey = "\033[0;37m"; 11 | public const char[] Darkgrey = "\033[01;30m"; 12 | public const char[] Bred = "\033[01;31m"; 13 | public const char[] Bgreen = "\033[01;32m"; 14 | public const char[] Byellow = "\033[01;33m"; 15 | public const char[] Bblue = "\033[01;34m"; 16 | public const char[] Bmagenta = "\033[01;35m"; 17 | public const char[] Bcyan = "\033[01;36m"; 18 | public const char[] White = "\033[01;37m"; 19 | public const char[] Normal = "\033[0m"; 20 | 21 | -------------------------------------------------------------------------------- /lua/script1.lua: -------------------------------------------------------------------------------- 1 | 2 | timesCalled = 0 3 | 4 | function doTick() 5 | timesCalled = timesCalled + 1 6 | print ("LUA> doTick() timesCalled "..timesCalled) 7 | end 8 | 9 | function timesTen(a) 10 | return a * 10 11 | end 12 | 13 | local function start(name) 14 | print("LUA> starting script "..name) 15 | local ok, value = startScript(name) 16 | if (ok == false) then error(value) end 17 | end 18 | 19 | local function stop(name) 20 | local ok, value = stopScript(name) 21 | if (ok == false) then error(value) end 22 | end 23 | 24 | function function1() 25 | print("LUA> running function1") 26 | 27 | start("foo") 28 | start("bar") 29 | start(10) 30 | print("LUA> part2") 31 | stop("foo"); 32 | stop("foo"); 33 | -- error 34 | end 35 | 36 | function runScript(a) 37 | print("LUA> running script ("..tostring(a)..")") 38 | --error("script todo") 39 | end 40 | 41 | -------------------------------------------------------------------------------- /lua/script2.lua: -------------------------------------------------------------------------------- 1 | 2 | timesCalled = 0 3 | 4 | function doTick() 5 | timesCalled = timesCalled + 1 6 | print ("LUA> doTick() timesCalled "..timesCalled) 7 | end 8 | 9 | function timesTen(a) 10 | return a * 10 11 | end 12 | 13 | local function start(name) 14 | print("LUA> starting script "..name) 15 | local ok, value = startScript(name) 16 | if (ok == false) then error(value) end 17 | end 18 | 19 | local function stop(name) 20 | local ok, value = stopScript(name) 21 | if (ok == false) then error(value) end 22 | end 23 | 24 | function function1() 25 | print("LUA> running function1") 26 | 27 | start("bar") 28 | start("bar") 29 | start("bar") 30 | start(20) 31 | print("LUA> part2") 32 | stop("bar"); 33 | -- error 34 | end 35 | 36 | function runScript(a) 37 | print("LUA> running script ("..tostring(a)..")") 38 | --error("script todo") 39 | end 40 | 41 | -------------------------------------------------------------------------------- /common/file/writer.c2: -------------------------------------------------------------------------------- 1 | module file_utils; 2 | 3 | import stdio local; 4 | import stdlib local; 5 | import libc_fcntl local; 6 | import string local; 7 | import c_errno local; 8 | import unistd local; 9 | 10 | public type Writer struct { 11 | char[256] msg; 12 | } 13 | 14 | public fn bool Writer.write(Writer* writer, const char* filename, const u8* data, u32 len) { 15 | writer.msg[0] = 0; 16 | i32 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660); 17 | if (fd == -1) { 18 | sprintf(writer.msg, "open failed: %s", strerror(*errno2())); 19 | return false; 20 | } 21 | 22 | i64 written = write(fd, data, len); 23 | if (written != len) { 24 | sprintf(writer.msg, "write failed: %s", strerror(*errno2())); 25 | return false; 26 | } 27 | 28 | close(fd); 29 | return true; 30 | } 31 | 32 | public fn const char* Writer.getError(const Writer* writer) { 33 | return writer.msg; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /xml_parser/examples/goblin_camp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | This is the main goblin camp. Several tiny passages that are too small 6 | too crawl through line the walls and floors. They must lead to even bigger 7 | goblin outposts. There is a big chest standing in the corner. 8 | 9 | 10 | 11 | 12 | It's a solid goblin war chest. Scribbled with what appears to be blood, 13 | it says 'Propetie of Haagh' 14 | 15 | 16 | 17 | HHAAAAAGGGHHHH!! 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /logger/main.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | import logger; 3 | import unistd; 4 | 5 | public fn i32 main(i32 argc, char** argv) { 6 | logger.Base logbase; 7 | logbase.init(logger.Level.Info, unistd.isatty(1), argc == 2); 8 | 9 | logger.Log log; 10 | log.init(&logbase, "main"); 11 | 12 | log.info("call this example with an extra argument to show relative times"); 13 | log.debug("debug msg"); 14 | log.info("info msg"); 15 | log.warn("this is a %s warning", "real"); 16 | log.error("Error number %d", 10); 17 | 18 | // meanwhile in another component.. 19 | logger.Log log2; 20 | log2.init(&logbase, "server"); 21 | log2.setLevel(logger.Level.Debug); 22 | log2.info("got new connection"); 23 | log2.debug("client connected from %s, fd %d", "127.0.0.1", 34); 24 | log2.warn("client disconnected"); 25 | 26 | // and back to the first one.. 27 | log.info("almost done"); 28 | log.fatal("uh ooh, totally FUBAR"); 29 | 30 | logbase.destroy(); 31 | 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /longjmp/jump.c2: -------------------------------------------------------------------------------- 1 | module jump; 2 | 3 | import stdio local; 4 | import unistd local; 5 | import csetjmp local; 6 | 7 | JmpBufTag env1; 8 | JmpBufTag env2; 9 | 10 | fn i32 function2(char n) 11 | { 12 | printf("function2(%c)\n", n); 13 | if (n == 'D') longjmp(&env1, 5); 14 | if (n == 'F') longjmp(&env2, 6); 15 | printf("returning from function2(%c)\n", n); 16 | return 15; 17 | } 18 | 19 | fn void function1() 20 | { 21 | char i; 22 | for (i='A'; i<'H'; i++) { 23 | printf("--- %c ---\n", i); 24 | i32 res = setjmp(&env1); 25 | if (res != 0) { 26 | printf("coming from jump ONE, res=%d\n", res); 27 | continue; 28 | } 29 | res = setjmp(&env2); 30 | if (res != 0) { 31 | printf("comping from jump TWO, res=%d\n", res); 32 | continue; 33 | } 34 | usleep(250000); 35 | res = function2(i); 36 | } 37 | } 38 | 39 | public fn i32 main() { 40 | printf("size=%d\n", sizeof(JmpBufTag)); 41 | function1(); 42 | return 0; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /lua/main.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | 3 | import script; 4 | import stdio local; 5 | import lua; 6 | 7 | // TODO wrap in own type so lua remains hidden 8 | fn i32 atpanic(lua.State* l) { 9 | printf("lua panic\n"); 10 | return 0; 11 | } 12 | 13 | public fn i32 main(i32 argc, char** argv) { 14 | const char* scriptName = "lua/script1.lua"; 15 | if (argc == 2) scriptName = argv[1]; 16 | 17 | script.LuaState ls; 18 | ls.init(); 19 | 20 | lua.atpanic(ls.l, atpanic); 21 | ls.registerFunc("startScript", script.startScript); 22 | ls.registerFunc("stopScript", script.stopScript); 23 | 24 | if (!ls.loadFile(scriptName)) return -1; 25 | if (!ls.execute()) return -1; 26 | 27 | ls.runFunction("doTick"); 28 | ls.runFunction("doTick"); 29 | ls.runFunction("doTick"); 30 | ls.runFunction("doTick"); 31 | ls.runFunction("doTick"); 32 | 33 | ls.callIntInt("timesTen", 2); 34 | ls.callIntInt("timesTen", 3); 35 | 36 | if (!ls.runFunction("function1")) return -1; 37 | 38 | ls.destroy(); 39 | return 0; 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /yaml_parser/main.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | 3 | import stdio local; 4 | import string local; 5 | import file_utils; 6 | import yaml; 7 | 8 | fn void get_info(yaml.Parser* parser) { 9 | const yaml.Node* cmds; 10 | } 11 | 12 | public fn i32 main(i32 argc, char** argv) { 13 | if (argc != 2) { 14 | printf("Usage: parser [yaml-file]\n"); 15 | return 0; 16 | } 17 | const char* filename = argv[1]; 18 | 19 | file_utils.Reader file; 20 | if (!file.open(filename)) { 21 | fprintf(stderr, "%s", strerror(file.errno)); 22 | return -1; 23 | 24 | } 25 | 26 | if (file.isEmpty()) { 27 | fprintf(stderr, "file %s is empty\n", filename); 28 | file.close(); 29 | return -1; 30 | } 31 | 32 | yaml.Parser* parser = yaml.Parser.create(); 33 | 34 | bool ok = parser.parse(cast(file.region)); 35 | if (ok) { 36 | parser.dump(true); 37 | get_info(parser); 38 | } else { 39 | fprintf(stderr, "Error: %s\n", parser.getMessage()); 40 | } 41 | 42 | parser.destroy(); 43 | file.close(); 44 | return 0; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /sha1/main.c2: -------------------------------------------------------------------------------- 1 | module sha1_test; 2 | 3 | import sha1; 4 | import string local; 5 | import stdio local; 6 | 7 | fn void test(const char* name, const char* input, const char* expect) { 8 | char[21] result; 9 | char[41] hexresult; 10 | sha1.hash(result, input, cast(strlen(input))); 11 | for (u32 i=0; i<20; i++) { 12 | sprintf(&hexresult[2*i], "%02x", result[i] & 0xFF); 13 | } 14 | //printf("expect %s\n", expect); 15 | //printf("got %s\n", hexresult); 16 | if (strcmp(expect, hexresult) != 0) printf("FAIL %s\n", name); 17 | } 18 | 19 | public fn i32 main() { 20 | const char[] input1 = "abc"; 21 | const char[] expect1 = "a9993e364706816aba3e25717850c26c9cd0d89d"; 22 | test("test1", input1, expect1); 23 | 24 | const char[] input2 = ""; 25 | const char[] expect2 = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; 26 | test("test2", input2, expect2); 27 | 28 | const char[] input3 = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; 29 | const char[] expect3 = "84983e441c3bd26ebaae4aa1f95129e5e54670f1"; 30 | test("test3", input3, expect3); 31 | 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /plugin/test_lib.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | 3 | import stdio local; 4 | import plugin_main; 5 | 6 | public fn i32 main() { 7 | #if 0 8 | u32 len1 = elemsof(plugin_main.array1); 9 | #else 10 | u32 len1 = 3; 11 | #endif 12 | printf("i32[%d] plugin1.array1 = {", len1); 13 | for (u32 i = 0; i < len1; i++) { 14 | printf("%d, ", plugin_main.array1[i]); 15 | } 16 | printf("};\n"); 17 | 18 | #if 0 19 | u32 len2 = elemsof(plugin_main.array2); 20 | #else 21 | // length of incremental arrays is not exposed in the interface 22 | u32 len2 = 4; 23 | #endif 24 | printf("i32[%d] plugin1.array2 = {", len2); 25 | for (u32 i = 0; i < len2; i++) { 26 | printf("%d, ", plugin_main.array2[i]); 27 | } 28 | printf("};\n"); 29 | 30 | #if 0 31 | u32 len3 = elemsof(plugin_main.array3); 32 | #else 33 | // length of incremental arrays is not exposed in the interface 34 | u32 len3 = 5; 35 | #endif 36 | printf("i32[%d] plugin1.array3 = {", len3); 37 | for (u32 i = 0; i < len3; i++) { 38 | printf("%d, ", plugin_main.array3[i]); 39 | } 40 | printf("};\n"); 41 | 42 | return 0; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /signals/main.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | import csignal local; 3 | import string local; 4 | import stdlib local; 5 | import stdio local; 6 | import unistd local; 7 | 8 | fn void fatal(const char* msg) { 9 | exit(EXIT_FAILURE); 10 | } 11 | 12 | fn void signal_handler(i32 num) { 13 | printf("got signal %d\n", num); 14 | exit(EXIT_SUCCESS); 15 | } 16 | 17 | public fn i32 main() { 18 | Sigaction act; 19 | memset(&act, 0, sizeof(act)); 20 | act.sa_flags = SA_NOCLDSTOP|SA_RESTART; 21 | //act.sa_handler = SIG_IGN; 22 | //if (sigaction(SIGPIPE, &act, nil) != 0) fatal("sigaction(SIGPIPE)"); 23 | //if (sigaction(SIGHUP, &act, nil) != 0) fatal("sigaction(SIGHUP)"); 24 | //if (sigaction(SIGQUIT, &act, nil) != 0) fatal("sigaction(SIGQUIT)"); 25 | //if (sigaction(SIGALRM, &act, nil) != 0) fatal("sigaction(SIGALRM)"); 26 | 27 | act.sa_handler = signal_handler; 28 | if (sigaction(SIGINT, &act, nil) != 0) fatal("sigaction(SIGINT)"); 29 | if (sigaction(SIGTERM, &act, nil) != 0) fatal("sigaction(SIGTERM)"); 30 | 31 | printf("press Ctrl-C to stop\n"); 32 | while (1) { 33 | sleep(1); 34 | } 35 | 36 | return 0; 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /pthread/pthread.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | 3 | import pthread; 4 | import stdio as io; 5 | import unistd local; 6 | 7 | bool stop = false; 8 | 9 | fn void* startThread1(void*) { 10 | io.printf("started thread1\n"); 11 | while (!stop) { 12 | sleep(1); 13 | io.printf("running1\n"); 14 | } 15 | io.printf("quit thread1\n"); 16 | return nil; 17 | } 18 | 19 | fn void* startThread2(void*) { 20 | io.printf("started thread2\n"); 21 | while (!stop) { 22 | sleep(1); 23 | io.printf("running2\n"); 24 | } 25 | io.printf("quit thread2\n"); 26 | return nil; 27 | } 28 | 29 | public fn i32 main() { 30 | pthread.Pthread[2] threads; 31 | i32 rc = pthread.create(&threads[0], nil, startThread1, nil); 32 | if (rc != 0) { 33 | io.perror("pthread_create"); 34 | return -1; 35 | } 36 | i32 rc2 = pthread.create(&threads[1], nil, startThread2, nil); 37 | if (rc2 != 0) { 38 | io.perror("pthread_create"); 39 | return -1; 40 | } 41 | 42 | sleep(4); 43 | io.printf("stopping threads\n"); 44 | stop = true; 45 | i32 res; 46 | res = pthread.join(threads[0], nil); 47 | res = pthread.join(threads[1], nil); 48 | io.printf("done\n"); 49 | return 0; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /xml_parser/main.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | 3 | import stdio local; 4 | import file_utils; 5 | import xml; 6 | 7 | public fn i32 main(i32 argc, char** argv) { 8 | if (argc != 2 && argc != 3) { 9 | printf("Usage: parser [xml-file] \n"); 10 | return 0; 11 | } 12 | const char* filename = argv[1]; 13 | bool verbose = (argc == 3); 14 | 15 | printf("parsing %s\n", filename); 16 | file_utils.Reader file; 17 | file.open(filename); 18 | 19 | if (file.isEmpty()) { 20 | printf("file %s is empty\n", filename); 21 | return 0; 22 | } 23 | 24 | xml.Parser parser; 25 | if (!parser.parse(file.char_data(), verbose)) { 26 | printf("error parsing %s: %s\n", filename, parser.getDiag()); 27 | } 28 | 29 | xml.Node2 root = parser.getRoot(); 30 | 31 | printf("root = %s\n", root.getName()); 32 | const char* name = root.getAttr("name"); 33 | if (name) printf(" name = %s\n", name); 34 | 35 | // iterate all children 36 | xml.NodeIter iter = root.getIter(); 37 | while (!iter.done()) { 38 | xml.Node2 child = iter.get(); 39 | printf(" child %s\n", child.getName()); 40 | iter.next(); 41 | } 42 | 43 | parser.destroy(); 44 | file.close(); 45 | 46 | return 0; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /plugin/plugin_mgr.c2: -------------------------------------------------------------------------------- 1 | module plugin_mgr; 2 | 3 | import other; 4 | 5 | import c_errno local; 6 | import dlfcn local; 7 | import stdio local; 8 | import string local; 9 | 10 | type LoadFn fn void(i32 arg); 11 | 12 | public fn i32 main(i32 argc, char** argv) { 13 | if (argc != 2) { 14 | printf("Usage: %s [filename]\n", argv[0]); 15 | return 0; 16 | } 17 | 18 | printf("other.test1 = %p\n", other.test1); 19 | other.test1(); 20 | 21 | const char* filename = argv[1]; 22 | void* handle = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); 23 | if (handle == nil) { 24 | fprintf(stderr, "error opening %s: %s\n", filename, dlerror()); 25 | return -1; 26 | } 27 | 28 | // TODO must symbol be in standard module as well? since otherwise we dont know name? 29 | void* symbol = dlsym(handle, "plugin_main_load"); 30 | if (!symbol) { 31 | fprintf(stderr, "invalid plugin (%s)\n", dlerror()); 32 | dlclose(handle); 33 | return -1; 34 | } 35 | 36 | // TODO allow casting of u64/void* to functions (and back) 37 | u64 temp = cast(symbol); 38 | LoadFn load = cast(temp); 39 | 40 | 41 | load(123); 42 | 43 | printf("unload %s\n", filename); 44 | dlclose(handle); 45 | 46 | other.test1(); 47 | 48 | return 0; 49 | } 50 | 51 | -------------------------------------------------------------------------------- /json_parser/example3.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "index": "1", 5 | "index_start_at": "56", 6 | "integer": "6", 7 | "float": "12.8328", 8 | "name": "Marcus", 9 | "surname": "Sawyer", 10 | "fullname": "Janet Murphy", 11 | "email": "tracy@hester.gt", 12 | "bool": "false" 13 | }, 14 | { 15 | "index": "2", 16 | "index_start_at": "57", 17 | "integer": "30", 18 | "float": "16.5476", 19 | "name": "Rita", 20 | "surname": "Locklear", 21 | "fullname": "Hazel Cooke", 22 | "email": "arthur@willis.be", 23 | "bool": "false" 24 | }, 25 | { 26 | "index": "3", 27 | "index_start_at": "58", 28 | "integer": "39", 29 | "float": "19.7193", 30 | "name": "Audrey", 31 | "surname": "Adkins", 32 | "fullname": "Kate Lang", 33 | "email": "stephanie@katz.aw", 34 | "bool": "true" 35 | }, 36 | { 37 | "index": "4", 38 | "index_start_at": "59", 39 | "integer": "41", 40 | "float": "18.3492", 41 | "name": "Donna", 42 | "surname": "Hicks", 43 | "fullname": "Allison Eason", 44 | "email": "julian@burnette.tl (changed from tp)", 45 | "bool": "true" 46 | } 47 | ] 48 | 49 | } 50 | -------------------------------------------------------------------------------- /unit_test/mod1_test.c2: -------------------------------------------------------------------------------- 1 | module mod1; 2 | 3 | import c2test local; 4 | 5 | type Test struct @(unittest) { 6 | i32 x; 7 | } 8 | 9 | fn void Test.setup(Test* t) { 10 | t.x = 10; 11 | } 12 | 13 | fn void Test.teardown(Test* t) { 14 | } 15 | 16 | fn void Test.test1(Test* t) { 17 | check_eq(2, func1(1)); 18 | check_eq(7, func1(3)); 19 | check_eq(4, func1(4)); 20 | } 21 | 22 | fn void Test.test2(Test* t) { 23 | t.helper(); 24 | check_str_contains("foobar", "oba"); 25 | check_str("foo", "bar"); 26 | log("test2.."); 27 | } 28 | 29 | fn void Test.test3(Test* t) { 30 | t.helper(); 31 | log("foo bar faa"); 32 | } 33 | 34 | /* 35 | fn void Test.test4(Test* t) @(skip) { 36 | check_eq(1, 2); 37 | } 38 | */ 39 | 40 | fn void Test.test5(Test* t) { 41 | u8[] a = { 0x1, 0x2, 0x3, 0x4 } 42 | u8[] b = { 0x1, 0x2, 0x8, 0x4 } 43 | check_data(a, elemsof(a), b, elemsof(b)); 44 | } 45 | 46 | fn void Test.test6(Test* t) { 47 | check_lt(10, 8); 48 | check_le(10, 10); 49 | check_between(10, 20, 19); 50 | } 51 | 52 | fn void Test.test7(Test* t) { 53 | check_not_nil(t); 54 | check_nil(t); 55 | } 56 | 57 | fn void Test.helper(Test* t) { 58 | log("helper"); 59 | } 60 | 61 | #if 0 62 | type Test2 stru{ 63 | i32 x; 64 | } @(unittest) 65 | 66 | fn void Test2.setup(Test2* t) { 67 | t.x = 10; 68 | } 69 | 70 | fn void Test2.test1(Test2* t) { 71 | } 72 | 73 | fn void Test2.test2(Test2* t) { 74 | //CHECK_EQ(t.x, 20); 75 | // -> need (hygienic) macros! 76 | } 77 | #endif 78 | 79 | -------------------------------------------------------------------------------- /dir_walker/main.c2: -------------------------------------------------------------------------------- 1 | module mod_main; 2 | 3 | import stdio local; 4 | import stdlib local; 5 | import string local; 6 | import sys_stat local; 7 | import sys_time; 8 | import libc_dirent local; 9 | import c_errno local; 10 | import unistd; 11 | 12 | fn void print_indent(u32 indent) { 13 | for (u32 i=0; i root node 11 | - value-list 12 | */ 13 | 14 | public fn i32 main(i32 argc, char** argv) { 15 | if (argc != 2) { 16 | printf("Usage: parser [toml-file]\n"); 17 | return 0; 18 | } 19 | const char* filename = argv[1]; 20 | 21 | toml.Reader* reader = toml.Reader.create(); 22 | if (!reader.parse(filename)) { 23 | printf("Error parsing %s: %s\n", filename, reader.getMsg()); 24 | reader.destroy(); 25 | return -1; 26 | } 27 | 28 | if (strcmp(filename, "toml_parser/example6.toml") == 0) { 29 | // getValue 30 | const char* key = "room.name"; 31 | printf("Value %s = %s\n", key, reader.getValue(key)); 32 | key = "room.descr"; 33 | printf("Value %s = %s\n", key, reader.getValue(key)); 34 | 35 | // ValueIter 36 | toml.ValueIter vi = reader.getValueIter("room.domains"); 37 | printf("Domains:\n"); 38 | while (!vi.done()) { 39 | printf(" %s\n", vi.getValue()); 40 | vi.next(); 41 | } 42 | 43 | // NodeIter 44 | printf("Exits:\n"); 45 | toml.NodeIter iter = reader.getNodeIter("exit"); 46 | while (!iter.done()) { 47 | const char* name = iter.getValue("name"); 48 | printf(" name=%s\n", name); 49 | iter.next(); 50 | } 51 | 52 | u32 number; 53 | if (reader.getNumber("room.number", &number)) printf("Number %d\n", number); 54 | bool isPublic; 55 | if (reader.getBool("room.public", &isPublic)) printf("Bool %d\n", isPublic); 56 | } 57 | 58 | reader.destroy(); 59 | return 0; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /graphics/sdl/main.c2: -------------------------------------------------------------------------------- 1 | // ported from Lesson 02 at https://lazyfoo.net/tutorials/SDL/index.php 2 | // modified to load a GIF instead of a BMP 3 | 4 | module sdltest_main; 5 | 6 | import stdio local; 7 | import stdlib local; 8 | import sdl; 9 | import sdl_image; 10 | 11 | const u32 SCREEN_WIDTH = 651; 12 | const u32 SCREEN_HEIGHT = 480; 13 | 14 | sdl.Window* gWindow = nil; 15 | sdl.Surface* gScreenSurface = nil; 16 | sdl.Surface* gHelloWorld = nil; 17 | 18 | fn void init() { 19 | if (sdl.init(sdl.INIT_VIDEO) < 0) { 20 | fprintf(stderr, "error initializing SDL2\n"); 21 | exit(-1); 22 | } 23 | 24 | sdl_image.init(0); 25 | 26 | gWindow = sdl.createWindow("SDL Tutorial", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, sdl.WINDOW_SHOWN); 27 | if( gWindow == nil ) { 28 | fprintf(stderr, "Window could not be created! SDL_Error: %s\n", sdl.getError()); 29 | exit(-1); 30 | } 31 | 32 | gScreenSurface = gWindow.getSurface(); 33 | } 34 | 35 | fn void loadMedia() { 36 | //const char* filename = "hello_world.bmp"; 37 | //gHelloWorld = sdl.loadBMP_RW(sdl.rwFromFile(filename, "rb"), 1); 38 | const char* filename = "c2sdl2.gif"; 39 | gHelloWorld = sdl_image.load(filename); 40 | if (gHelloWorld == nil) { 41 | fprintf(stderr, "Unable to load image %s! SDL Error: %s\n", filename, sdl.getError()); 42 | exit(-1); 43 | } 44 | } 45 | 46 | fn void mainloop() { 47 | sdl.Event e; 48 | while (1) { 49 | while (e.poll()) { 50 | if (e.type_ == sdl.EventType.KEYDOWN) return; 51 | if (e.type_ == sdl.EventType.QUIT) return; 52 | } 53 | } 54 | } 55 | 56 | fn void close() { 57 | sdl_image.quit(); 58 | 59 | gHelloWorld.free(); 60 | gHelloWorld = nil; 61 | 62 | gWindow.destroy(); 63 | gWindow = nil; 64 | 65 | sdl.quit(); 66 | } 67 | 68 | public fn i32 main(i32 argc, char** argv) { 69 | 70 | init(); 71 | 72 | loadMedia(); 73 | 74 | gHelloWorld.blit(nil, gScreenSurface, nil); 75 | 76 | gWindow.updateSurface(); 77 | 78 | mainloop(); 79 | 80 | close(); 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /common/string_buffer.c2: -------------------------------------------------------------------------------- 1 | module utils; 2 | 3 | import stdarg local; 4 | import stdio local; 5 | import stdlib local; 6 | import string local; 7 | 8 | public type StringBuffer struct { 9 | u32 capacity; 10 | u32 size_; 11 | char* data_; 12 | } 13 | 14 | static_assert(16, sizeof(StringBuffer)); 15 | 16 | public fn StringBuffer* StringBuffer.create(u32 capacity) 17 | { 18 | StringBuffer* buf = malloc(sizeof(StringBuffer)); 19 | buf.capacity = capacity; 20 | buf.size_ = 0; 21 | buf.data_ = malloc(capacity); 22 | return buf; 23 | } 24 | 25 | public fn void StringBuffer.destroy(StringBuffer* buf) 26 | { 27 | free(buf.data_); 28 | free(buf); 29 | } 30 | 31 | public fn u32 StringBuffer.size(const StringBuffer* buf) 32 | { 33 | return buf.size_; 34 | } 35 | 36 | public fn const char* StringBuffer.data(const StringBuffer* buf) 37 | { 38 | return buf.data_; 39 | } 40 | 41 | public fn void StringBuffer.clear(StringBuffer* buf) 42 | { 43 | buf.size_ = 0; 44 | } 45 | 46 | public fn void StringBuffer.add(StringBuffer* buf, const char* text) 47 | { 48 | u32 len = cast(strlen(text)); 49 | buf.add2(text, len); 50 | } 51 | 52 | public fn void StringBuffer.add2(StringBuffer* buf, const char* text, u32 len) 53 | { 54 | if ((len + 1) >= (buf.capacity - buf.size_)) { 55 | u32 newcap = buf.capacity * 2; 56 | if ((len + 1) >= (newcap - buf.size_)) newcap += buf.size_ + len + 1; 57 | buf.capacity = newcap; 58 | char* data2 = malloc(newcap); 59 | memcpy(data2, buf.data_, buf.size_ + 1); 60 | free(buf.data_); 61 | buf.data_ = data2; 62 | } 63 | strcpy(&buf.data_[buf.size_], text); 64 | buf.size_ += len; 65 | buf.data_[buf.size_] = 0; 66 | } 67 | 68 | public fn void StringBuffer.print(StringBuffer* buf, const char* format, ...) { 69 | // NOTE: no growing 70 | va_list args; 71 | va_start(args, format); 72 | i32 len = vsprintf(&buf.data_[buf.size_], format, args); 73 | // TODO check size 74 | buf.size_ += len; 75 | va_end(args); 76 | } 77 | 78 | public fn void StringBuffer.indent(StringBuffer* buf, u32 indent) { 79 | // NOTE: no growing/checking 80 | for (u32 i=0; i(&my_addr), sizeof(Sockaddr)) != 0) fatal("bind() failed"); 33 | 34 | if (listen(fd, 4) != 0) fatal("listen() failed"); 35 | 36 | return fd; 37 | } 38 | 39 | fn i32 wait_for_connection(i32 fd, char** src_ipnr) 40 | { 41 | u32 sin_size = sizeof(Sockaddr_in); 42 | Sockaddr_in remote; 43 | i32 new_fd = accept4(fd, cast(&remote), &sin_size, SOCK_CLOEXEC); 44 | if (new_fd == -1) fatal("accept4"); 45 | 46 | *src_ipnr = inet_ntoa(remote.sin_addr); 47 | return new_fd; 48 | } 49 | 50 | public fn i32 main() { 51 | // open socket 52 | u16 port = 8888; 53 | i32 fd = open_socket(port); 54 | printf("listering on port %d (press Ctrl-C to quit)\n", port); 55 | 56 | // wait until accept 57 | char* src_ipnr = nil; 58 | i32 conn_fd = wait_for_connection(fd, &src_ipnr); 59 | printf("connection from %s\n", src_ipnr); 60 | 61 | // read until disconnect 62 | while (1) { 63 | const u32 bufSize = 64; 64 | char[bufSize] buffer; 65 | i64 numread = read(conn_fd, buffer, bufSize-1); 66 | if (numread == 0) { 67 | printf("client disconnected\n"); 68 | break; 69 | } 70 | buffer[numread] = 0; 71 | if (buffer[numread-1] == '\n') buffer[numread-1] = 0; 72 | printf("read %d bytes: %s\n", numread, buffer); 73 | } 74 | 75 | // close socket 76 | close(conn_fd); 77 | close(fd); 78 | 79 | return 0; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /multicast/multicast_sender.c2: -------------------------------------------------------------------------------- 1 | module multicast_sender; 2 | 3 | import stdio local; 4 | import stdlib local; 5 | import unistd local; 6 | import c_errno local; 7 | import string local; 8 | import sys_socket local; 9 | 10 | public type Sender struct { 11 | i32 fd; 12 | char[16] ipnr; 13 | u16 port; 14 | } 15 | 16 | public fn void Sender.init(Sender* s, const char* ipnr, u16 port) { 17 | s.fd = -1; 18 | strcpy(s.ipnr, ipnr); 19 | s.port = port; 20 | s.open_socket(); 21 | } 22 | 23 | public fn void Sender.destroy(Sender* s) { 24 | close(s.fd); 25 | } 26 | 27 | fn void Sender.open_socket(Sender* s) { 28 | // create what looks like an ordinary UDP socket 29 | //if ((s.fd = socket(AF_INET, SocketType.SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP)) < 0) { 30 | if ((s.fd = socket(AF_INET, SocketType.SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) { 31 | fprintf(stderr, "error opening socket: %s\n", strerror(*errno2())); 32 | exit(EXIT_FAILURE); 33 | } 34 | 35 | // set up destination address 36 | Sockaddr_in addr; 37 | memset(&addr, 0, sizeof(addr)); 38 | addr.sin_family = AF_INET; 39 | addr.sin_addr.s_addr = inet_addr(s.ipnr); 40 | addr.sin_port = htons(s.port); 41 | 42 | u8 ttl = 1; 43 | if (setsockopt(s.fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) != 0) { 44 | fprintf(stderr, "setsockopt(IP_MULTICAST_TTL): %s\n", strerror(*errno2())); 45 | exit(EXIT_FAILURE); 46 | } 47 | 48 | i32 reuse = 1; 49 | if (setsockopt(s.fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0) { 50 | fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(*errno2())); 51 | exit(EXIT_FAILURE); 52 | } 53 | 54 | if (bind(s.fd, cast(&addr), sizeof(addr))) { 55 | fprintf(stderr, "bind: %s\n", strerror(*errno2())); 56 | exit(EXIT_FAILURE); 57 | } 58 | 59 | if (connect(s.fd, cast(&addr), sizeof(addr)) < 0) { 60 | fprintf(stderr, "connect: %s\n", strerror(*errno2())); 61 | exit(EXIT_FAILURE); 62 | } 63 | 64 | //u8 loop; 65 | //socklen_t size; 66 | //getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &size); 67 | //printf("loop = %u\n", loop); 68 | 69 | //printf("sending to %s, port %d\n", s.ipnr, s.port); 70 | } 71 | 72 | public fn void Sender.broadcast(Sender* s, const u8* data, u32 size) { 73 | if (send(s.fd, data, size, 0) < 0) { 74 | fprintf(stderr, "send: %s\n", strerror(*errno2())); 75 | exit(EXIT_FAILURE); 76 | } 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /common/file/reader.c2: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 Bas van den Berg 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | module file_utils; 17 | 18 | import libc_fcntl local; 19 | import c_errno local; 20 | import sys_stat local; 21 | import unistd local; 22 | import stdlib; 23 | 24 | u8 empty; 25 | 26 | public const i32 Err_not_a_file = 2001; 27 | 28 | public type Reader struct { 29 | void* region; 30 | u32 size; 31 | i32 errno; 32 | } 33 | 34 | public fn bool Reader.open(Reader* file, const char* filename) { 35 | file.region = nil; 36 | file.size = 0; 37 | 38 | i32 fd = open(filename, O_RDONLY); 39 | if (fd == -1) { 40 | file.errno = *errno2(); 41 | return false; 42 | } 43 | 44 | Stat statbuf; 45 | i32 err = fstat(fd, &statbuf); 46 | if (err) { 47 | file.errno = *errno2(); 48 | return false; 49 | } 50 | 51 | if ((statbuf.st_mode & S_IFMT) != S_IFREG) { 52 | close(fd); 53 | file.errno = Err_not_a_file; 54 | return false; 55 | } 56 | 57 | file.size = cast(statbuf.st_size); 58 | 59 | if (file.size == 0) { 60 | file.region = ∅ 61 | } else { 62 | file.region = stdlib.malloc(file.size+1); 63 | isize numread = read(fd, file.region, file.size); 64 | if (numread != file.size) return false; 65 | // 0-terminate 66 | u8* ptr = file.region; 67 | ptr[file.size] = 0; 68 | } 69 | close(fd); 70 | return true; 71 | } 72 | 73 | public fn void Reader.close(Reader* file) { 74 | if (file.region) { 75 | stdlib.free(file.region); 76 | file.region = nil; 77 | } 78 | } 79 | 80 | public fn bool Reader.isOpen(const Reader* file) { 81 | return file.region != nil; 82 | } 83 | 84 | public fn const u8* Reader.data(Reader* file) { 85 | return cast(file.region); 86 | } 87 | 88 | public fn const char* Reader.char_data(Reader* file) { 89 | return cast(file.region); 90 | } 91 | 92 | public fn bool Reader.isEmpty(const Reader* file) { 93 | return file.size == 0; 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /yaml_parser/yaml_dump.c2: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 Bas van den Berg 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | module yaml; 17 | 18 | import stdio local; 19 | import string local; 20 | 21 | public fn void Parser.dump(const Parser* p, bool verbose) { 22 | p.data.dump(verbose); 23 | } 24 | 25 | fn void Data.dump(const Data* d, bool verbose) { 26 | u32 node_count = cast(d.nodes_cur - d.nodes); 27 | if (verbose) { 28 | printf("Text %d/%d\n", cast(d.text_cur - d.text), d.text_size); 29 | const char* cp = d.text + 1; 30 | while (cp < d.text_cur) { 31 | u32 len = cast(strlen(cp)); 32 | u32 offset = cast(cp - d.text); 33 | printf(" [%3d] %s\n", offset, cp); 34 | cp += len + 1; 35 | } 36 | 37 | printf("Nodes %d/%d\n", node_count, d.nodes_count); 38 | for (u32 i=1; i 1) d.dump_node(&d.nodes[1], 0); 45 | } 46 | 47 | fn Node* Data.idx2node(const Data* d, u32 idx) { 48 | return &d.nodes[idx]; 49 | } 50 | 51 | fn void Data.dump_node(const Data* d, const Node* n, i32 indent) { 52 | // TEMP crude way 53 | for (i32 i=0; i(cp - linestart)); 31 | outlines++; 32 | } 33 | linestart = cp; 34 | white = true; 35 | skip = false; 36 | lines++; 37 | } else { 38 | switch (*cp) { 39 | case ' ': fallthrough; 40 | case '\t': 41 | break; 42 | case '/': 43 | if (cp[1] == '/') { 44 | // read until newline 45 | while (*cp != NEWLINE && *cp != 0) cp++; 46 | #if 0 47 | char[256] line; 48 | u32 len = cast(cp - linestart); 49 | memcpy(line, linestart, len); 50 | line[len] = 0; 51 | printf("%d %s", white, line); 52 | #endif 53 | if (white) skip = true; 54 | continue; 55 | } 56 | fallthrough; 57 | default: 58 | white = false; 59 | break; 60 | } 61 | cp++; 62 | } 63 | } 64 | printf("lines %d -> %d (stripped %d lines)\n", lines, outlines, lines - outlines); 65 | } 66 | 67 | public fn i32 main(i32 argc, char** argv) { 68 | if (argc != 2) { 69 | printf("Usage: parser [c-file]\n"); 70 | return 0; 71 | } 72 | file_utils.Reader infile; 73 | infile.open(argv[1]); 74 | 75 | //printf("%s", file.data()); 76 | if (infile.isEmpty()) { 77 | printf("file %s is empty\n", argv[1]); 78 | return 0; 79 | } 80 | 81 | utils.StringBuffer* buffer = utils.StringBuffer.create(infile.size); 82 | 83 | strip_comments(infile.char_data(), buffer); 84 | infile.close(); 85 | 86 | file_utils.Writer outfile; 87 | const char* outname = "out.c"; 88 | printf("written %s\n", outname); 89 | if (!outfile.write(outname, cast(buffer.data()), buffer.size())) { 90 | printf("error writing file: %s\n", outfile.getError()); 91 | } 92 | 93 | buffer.destroy(); 94 | 95 | return 0; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /list/main.c2: -------------------------------------------------------------------------------- 1 | module test; 2 | import stdio local; 3 | import stdlib local; 4 | import list as ll local; 5 | 6 | type Module struct { 7 | const char* name; 8 | i32 depth; 9 | Element list; 10 | Module*[4] deps; 11 | u32 numDeps; 12 | } 13 | 14 | fn void printList(const char* name, const Element* list, bool printDeps) { 15 | printf("%s:\n", name); 16 | Element* node = list.next; 17 | while (node != list) { 18 | const Module* m = to_container(Module, list, node); 19 | printf(" %-4s %d\n", m.name, m.depth); 20 | if (printDeps) { 21 | for (u32 i=0; i %s\n", m.deps[i].name); 23 | } 24 | } 25 | node = node.next; 26 | } 27 | } 28 | 29 | fn Module* createModule(Element* list, const char* name) { 30 | Module* m = calloc(1, sizeof(Module)); 31 | m.name = name; 32 | m.depth = -1; 33 | list.addTail(&m.list); 34 | return m; 35 | } 36 | 37 | fn void Module.addDep(Module* m, Module* dep) { 38 | m.deps[m.numDeps] = dep; 39 | m.numDeps++; 40 | } 41 | 42 | fn void check(Element* all, Module* top) { 43 | Element todo; 44 | Element done; 45 | todo.init(); 46 | done.init(); 47 | 48 | i32 max_depth = cast(all.size()); 49 | top.list.remove(); 50 | todo.addFront(&top.list); 51 | top.depth = 0; 52 | 53 | 54 | while (!todo.isEmpty()) { 55 | Element* node = todo.popFront(); 56 | Module* m = to_container(Module, list, node); 57 | if (m.depth > max_depth) { 58 | printf("LOOP found\n"); 59 | break; 60 | } 61 | done.addTail(node); 62 | for (u32 i=0; i(&addr), sizeof(addr))) { 65 | fprintf(stderr, "bind: %s\n", strerror(*errno2())); 66 | exit(EXIT_FAILURE); 67 | } 68 | s.addr = addr; 69 | 70 | // use setsockopt() to request that the kernel join a multicast group 71 | Ip_Mreq mreq; 72 | mreq.imr_multiaddr.s_addr = inet_addr(s.ipnr); 73 | mreq.imr_interface.s_addr = htonl(INADDR_ANY); 74 | if (setsockopt(s.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { 75 | fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(*errno2())); 76 | exit(EXIT_FAILURE); 77 | } 78 | } 79 | 80 | fn void on_data(i32 fd, void* arg, u16 flags) { 81 | Receiver* r = arg; 82 | 83 | char[64] msgbuf; 84 | u32 addrlen = sizeof(r.addr); 85 | isize recv_bytes = recvfrom(r.fd, msgbuf, sizeof(msgbuf), 0, cast(&r.addr), &addrlen); 86 | if (recv_bytes < 0) { 87 | perror("recvfrom"); 88 | exit(1); 89 | } 90 | msgbuf[recv_bytes] = 0; 91 | printf("received: %s\n", msgbuf); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /xml_parser/examples/example1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /json_parser/json_serialize.c2: -------------------------------------------------------------------------------- 1 | module json; 2 | 3 | import stdlib local; 4 | import stdio local; 5 | import libc_fcntl local; 6 | import string local; 7 | import c_errno local; 8 | import unistd local; 9 | import file_utils; 10 | 11 | fn u32 roundup(u32 size) { 12 | if ((size & 0x3) == 0) return size; 13 | return (size + 3) & ~0x3; 14 | } 15 | 16 | type Header struct @(packed) { 17 | u32 names_size; 18 | u32 values_size; 19 | u32 nodes_size; 20 | u8[0] data; 21 | } 22 | 23 | fn bool Data.write(const Data* d, i32 fd) { 24 | // NOTE: copy to internal buffer first, do single write() 25 | u32 size = sizeof(Header); 26 | size += roundup(d.names.cur); 27 | size += roundup(d.values.cur); 28 | size += roundup(d.nodes.getSize()); 29 | u8* buf = malloc(size); 30 | 31 | Header* hdr = cast(buf); 32 | hdr.names_size = d.names.cur; 33 | hdr.values_size = d.values.cur; 34 | hdr.nodes_size = d.nodes.getSize(); 35 | u32 offset = 0; 36 | memcpy(&hdr.data[offset], d.names.data, d.names.cur); 37 | offset += roundup(d.names.cur); 38 | memcpy(&hdr.data[offset], d.values.data, d.values.cur); 39 | offset += roundup(d.values.cur); 40 | memcpy(&hdr.data[offset], cast(d.nodes.data), d.nodes.getSize()); 41 | 42 | i64 written = write(fd, buf, size); 43 | free(buf); 44 | if (written != size) return false; 45 | return true; 46 | } 47 | 48 | fn Data* Data.read(const u8* data, u32 size) { 49 | if (size < sizeof(Header)) return nil; 50 | const Header* hdr = cast(data); 51 | u32 exp_size = sizeof(Header); 52 | exp_size += roundup(hdr.names_size); 53 | exp_size += roundup(hdr.values_size); 54 | exp_size += roundup(hdr.nodes_size); 55 | if (size != exp_size) return nil; 56 | 57 | Data* d = malloc(sizeof(Data)); 58 | u32 offset = 0; 59 | 60 | d.names.init(hdr.names_size); 61 | memcpy(d.names.data, &hdr.data[offset], hdr.names_size); 62 | d.names.cur = hdr.names_size; 63 | offset += roundup(hdr.names_size); 64 | 65 | d.values.init(hdr.values_size); 66 | memcpy(d.values.data, &hdr.data[offset], hdr.values_size); 67 | d.values.cur = hdr.values_size; 68 | offset += roundup(hdr.values_size); 69 | 70 | d.nodes.init(hdr.nodes_size / sizeof(Node)); 71 | memcpy(d.nodes.data, &hdr.data[offset], hdr.nodes_size); 72 | d.nodes.cur = hdr.nodes_size / sizeof(Node); 73 | 74 | return d; 75 | } 76 | 77 | public fn bool Parser.write_cache(Parser* p, const char* filename) { 78 | if (!p.data) return true; 79 | i32 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660); 80 | if (fd == -1) { 81 | sprintf(p.message, "cannot open file: %s", strerror(*errno2())); 82 | return false; 83 | } 84 | if (!p.data.write(fd)) { 85 | sprintf(p.message, "cannot write file: %s", strerror(*errno2())); 86 | close(fd); 87 | return false; 88 | } 89 | close(fd); 90 | return true; 91 | } 92 | 93 | public fn bool Parser.read_cache(Parser* p, const char* filename) { 94 | if (p.data) p.data.destroy(); 95 | p.message[0] = 0; 96 | 97 | file_utils.Reader file; 98 | if (!file.open(filename)) { 99 | sprintf(p.message, "cannot read file: %s", strerror(*errno2())); 100 | return false; 101 | } 102 | if (file.isEmpty()) { 103 | strcpy(p.message, "invalid data"); 104 | return false; 105 | } 106 | p.data = Data.read(file.data(), file.size); 107 | file.close(); 108 | if (!p.data) { 109 | strcpy(p.message, "invalid data"); 110 | return false; 111 | } 112 | return true; 113 | } 114 | 115 | -------------------------------------------------------------------------------- /base64/base64.c2: -------------------------------------------------------------------------------- 1 | module base64; 2 | 3 | // based on github.com/zhicheng/base64.git 4 | 5 | const i8[] Lut_enc = { 6 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 7 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 8 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 9 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 10 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 11 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 12 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', 13 | '4', '5', '6', '7', '8', '9', '+', '/', 14 | } 15 | 16 | const i8[] Lut_dec = { 17 | // '+', ',', '-', '.', '/', '0', '1', '2' 18 | 62, -1, -1, -1, 63, 52, 53, 54, 19 | // '3', '4', '5', '6', '7', '8', '9', ':' 20 | 55, 56, 57, 58, 59, 60, 61, -1, 21 | // ';', '<', '=', '>', '?', '@', 'A', 'B' 22 | -1, -1, -1, -1, -1, -1, 0, 1, 23 | // 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' 24 | 2, 3, 4, 5, 6, 7, 8, 9, 25 | // 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R' 26 | 10, 11, 12, 13, 14, 15, 16, 17, 27 | // 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' 28 | 18, 19, 20, 21, 22, 23, 24, 25, 29 | // '[', '\', ']', '^', '_', '`', 'a', 'b' 30 | -1, -1, -1, -1, -1, -1, 26, 27, 31 | // 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' 32 | 28, 29, 30, 31, 32, 33, 34, 35, 33 | // 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r' 34 | 36, 37, 38, 39, 40, 41, 42, 43, 35 | // 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' 36 | 44, 45, 46, 47, 48, 49, 50, 51, 37 | } 38 | 39 | 40 | const u8 PAD = '='; 41 | const u8 FIRST = '+'; 42 | const u8 LAST = 'z'; 43 | 44 | public fn void encode(const u8* in, u32 len, i8* out) { 45 | u32 i = 0; 46 | u32 j = 0; 47 | 48 | for (; i< len; ++i) { 49 | switch (i % 3) { 50 | case 0: 51 | out[j++] = Lut_enc[(in[i] >> 2) & 0x3F]; 52 | continue; 53 | case 1: 54 | out[j++] = Lut_enc[((in[i-1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)]; 55 | continue; 56 | case 2: 57 | out[j++] = Lut_enc[((in[i-1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)]; 58 | out[j++] = Lut_enc[in[i] & 0x3F]; 59 | break; 60 | } 61 | } 62 | i -= 1; // move back 63 | // check the last and add padding 64 | switch (i % 3) { 65 | case 0: 66 | out[j++] = Lut_enc[(in[i] & 0x3) << 4]; 67 | out[j++] = PAD; 68 | out[j++] = PAD; 69 | break; 70 | case 1: 71 | out[j++] = Lut_enc[(in[i] & 0xF) << 2]; 72 | out[j++] = PAD; 73 | break; 74 | } 75 | } 76 | 77 | public fn i32 decode(const i8* in, u32 len, u8* out) { 78 | u32 i = 0; 79 | u32 j = 0; 80 | 81 | for (; i < len; ++i) { 82 | if (in[i] == PAD) return cast(j); 83 | i8 c; 84 | if (in[i] < FIRST || in[i] > LAST || (c = Lut_dec[in[i] - FIRST]) == -1) return -1; 85 | 86 | i8 cc; 87 | switch (i % 4) { 88 | case 0: 89 | cc = (i8)(c << 2); 90 | out[j] = cast(cc); 91 | //out[j] = cast((c << 2) & 0xFF); // TODO c2c assert fails on cast 92 | continue; 93 | case 1: 94 | out[j++] += (c >> 4) & 0x3; 95 | // if not last char with padding 96 | if (i < (len -3) || in[len -2] != PAD) { 97 | //out[j] = cast((c & 0xF) << 4); // TODO c2c assert fails on cast 98 | cc = (i8)((c & 0xF) << 4); 99 | out[j] = cast(cc); 100 | } 101 | continue; 102 | case 2: 103 | out[j++] += (c >> 2) & 0xF; 104 | if (i < (len -2) || in[len -1] != PAD) { 105 | //out[j] = cast((c & 0x3) << 6); // TODO c2c assert fails on cast 106 | cc = (i8)((c & 0x3) << 6); 107 | out[j] = cast(cc); 108 | } 109 | continue; 110 | case 3: 111 | out[j++] += c; 112 | break; 113 | } 114 | } 115 | return cast(j); 116 | } 117 | 118 | -------------------------------------------------------------------------------- /json_parser/json_builder.c2: -------------------------------------------------------------------------------- 1 | module json; 2 | 3 | import stdlib local; 4 | import stdio local; 5 | 6 | fn const char* indent(u32 i) { 7 | local const char[] indents = " "; 8 | local const u32 max = 28; 9 | return &indents[max - i*2]; 10 | } 11 | 12 | 13 | public type Builder struct { 14 | char* buffer; 15 | u32 size; 16 | u32 capacity; 17 | bool[6] hasEntry; 18 | bool[6] inArray; 19 | u32 indent; 20 | } 21 | 22 | public fn void Builder.init(Builder* b, u32 capacity) { 23 | b.buffer = malloc(capacity); 24 | b.capacity = capacity; 25 | b.reset(); 26 | } 27 | 28 | public fn void Builder.free(Builder* b) { 29 | free(b.buffer); 30 | } 31 | 32 | public fn void Builder.finalize(Builder* b) { 33 | char* orig = &b.buffer[b.size]; 34 | i32 len = sprintf(orig, "\n"); 35 | b.size += len; 36 | } 37 | 38 | public fn const char* Builder.getData(const Builder* b) { 39 | return b.buffer; 40 | } 41 | 42 | public fn u32 Builder.getSize(const Builder* b) { 43 | return b.size + 1; // include 0-termination 44 | } 45 | 46 | public fn void Builder.addNumber(Builder* b, const char* key, i64 value) { 47 | char* orig = &b.buffer[b.size]; 48 | char* cp = orig; 49 | if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); 50 | if (b.inArray[b.indent]) { 51 | cp += sprintf(cp, "%s%d", indent(b.indent), value); 52 | } else { 53 | cp += sprintf(cp, "%s\"%s\": %d", indent(b.indent), key, value); 54 | } 55 | b.size += (cp - orig); 56 | b.hasEntry[b.indent] = true; 57 | } 58 | 59 | public fn void Builder.addValue(Builder* b, const char* key, const char* value) { 60 | char* orig = &b.buffer[b.size]; 61 | char* cp = orig; 62 | if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); 63 | if (b.inArray[b.indent]) { 64 | cp += sprintf(cp, "%s\"%s\"", indent(b.indent), value); 65 | } else { 66 | cp += sprintf(cp, "%s\"%s\": \"%s\"", indent(b.indent), key, value); 67 | } 68 | b.size += (cp - orig); 69 | b.hasEntry[b.indent] = true; 70 | } 71 | 72 | public fn void Builder.addObject(Builder* b, const char* key) { 73 | char* orig = &b.buffer[b.size]; 74 | char* cp = orig; 75 | if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); 76 | if (b.inArray[b.indent]) { 77 | cp += sprintf(cp, "%s{\n", indent(b.indent)); 78 | } else { 79 | cp += sprintf(cp, "%s\"%s\":\n%s{\n", indent(b.indent), key, indent(b.indent)); 80 | } 81 | b.size += (cp - orig); 82 | b.hasEntry[b.indent] = true; 83 | b.indent++; 84 | b.hasEntry[b.indent] = false; 85 | b.inArray[b.indent] = false; 86 | } 87 | 88 | public fn void Builder.closeObject(Builder* b) { 89 | // NOTE: no sanity check 90 | b.indent--; 91 | char* orig = &b.buffer[b.size]; 92 | char* cp = orig; 93 | *cp++ = '\n'; 94 | cp += sprintf(cp, "%s}", indent(b.indent)); 95 | b.size += (cp - orig); 96 | } 97 | 98 | public fn void Builder.addArray(Builder* b, const char* key) { 99 | char* orig = &b.buffer[b.size]; 100 | char* cp = orig; 101 | if (b.hasEntry[b.indent]) cp += sprintf(cp, ",\n"); 102 | if (key) { 103 | cp += sprintf(cp, "%s\"%s\":\n%s[\n", indent(b.indent), key, indent(b.indent)); 104 | } else { 105 | cp += sprintf(cp, "%s[\n", indent(b.indent)); 106 | } 107 | b.size += (cp - orig); 108 | b.hasEntry[b.indent] = true; 109 | b.indent++; 110 | b.inArray[b.indent] = true; 111 | b.hasEntry[b.indent] = false; 112 | } 113 | 114 | public fn void Builder.closeArray(Builder* b) { 115 | b.inArray[b.indent] = false; 116 | b.indent--; 117 | char* orig = &b.buffer[b.size]; 118 | char* cp = orig; 119 | *cp++ = '\n'; 120 | cp += sprintf(cp, "%s]", indent(b.indent)); 121 | b.size += (cp - orig); 122 | } 123 | 124 | public fn void Builder.reset(Builder* b) { 125 | b.size = 0; 126 | for (u32 i=0; i(d.nodes_cur - d.nodes); 87 | 88 | d.nodes_count *= 2; 89 | Node* nodes2 = malloc(d.nodes_count * sizeof(Node)); 90 | memcpy(nodes2, d.nodes, idx * sizeof(Node)); 91 | 92 | // fix-up stack pointers 93 | for (u32 i=0; i(sl.node - d.nodes); 97 | sl.node = &nodes2[node_idx]; 98 | } 99 | if (sl.last_child) { 100 | u32 last_child_idx = cast(sl.last_child - d.nodes); 101 | sl.last_child = &nodes2[last_child_idx]; 102 | } 103 | } 104 | 105 | free(d.nodes); 106 | d.nodes = nodes2; 107 | d.nodes_cur = &d.nodes[idx]; 108 | } 109 | 110 | fn void Data.resize_text(Data* d) { 111 | u32 idx = cast(d.text_cur - d.text); 112 | 113 | d.text_size *= 2; 114 | char* text2 = malloc(d.text_size); 115 | memcpy(text2, d.text, idx + 1); // also copy 0-termination 116 | free(d.text); 117 | d.text = text2; 118 | d.text_cur = &d.text[idx]; 119 | } 120 | 121 | fn Node* Data.add_node(Data* d, NodeKind kind, u32 name_idx) { 122 | u32 idx = cast(d.nodes_cur - d.nodes); 123 | if (idx >= d.nodes_count -1) d.resize_nodes(); 124 | 125 | Node* result = d.nodes_cur; 126 | d.nodes_cur++; 127 | result.kind = kind; 128 | result.next_idx = 0; 129 | result.name_idx = name_idx; 130 | result.child_idx = 0; 131 | return result; 132 | } 133 | 134 | fn u32 Data.node2idx(const Data* d, const Node* n) @(inline) { 135 | return cast(n - d.nodes); 136 | } 137 | 138 | fn u32 Data.add_text(Data* d, const char* text, u32 len) { 139 | u32 idx = cast(d.text_cur - d.text); 140 | while (idx + len + 1 >= d.text_size) d.resize_text(); 141 | 142 | memcpy(d.text_cur, text, len); 143 | d.text_cur[len] = 0; 144 | d.text_cur += len+1; // add 0-terminator 145 | return idx; 146 | } 147 | 148 | -------------------------------------------------------------------------------- /common/logger.c2: -------------------------------------------------------------------------------- 1 | module logger; 2 | 3 | import color; 4 | import stdarg local; 5 | import string local; 6 | import stdio local; 7 | import stdlib local; 8 | import sys_time local; 9 | import unistd; 10 | import libc_time; 11 | 12 | public type Level enum u8 { 13 | Fatal = 0, 14 | Error, 15 | Warn, 16 | Info, 17 | Debug, 18 | } 19 | 20 | fn const char* level2str(Level lvl) { 21 | switch (lvl) { 22 | case Fatal: return "Fatal"; 23 | case Error: return "Error"; 24 | case Warn: return "Warn "; 25 | case Info: return "Info "; 26 | case Debug: return "Debug"; 27 | } 28 | return nil; 29 | } 30 | 31 | fn u64 current_time() { 32 | Timeval tv; 33 | gettimeofday(&tv, nil); 34 | u64 tv64 = cast(tv.tv_sec); 35 | tv64 *= 1000000; 36 | tv64 += tv.tv_usec; 37 | return tv64; 38 | } 39 | 40 | public type WriteFunc fn void(const char* data, u32 len); 41 | 42 | public type Base struct { 43 | Level defaultLevel; 44 | bool useColor; 45 | bool relativeTimes; 46 | u64 startTime; 47 | WriteFunc write; 48 | } 49 | 50 | fn void log_stdout(const char* data, u32 len) { 51 | printf("%s", data); 52 | } 53 | 54 | public fn void Base.init(Base* base, Level lvl_, bool color_, bool relativeTimes_) 55 | { 56 | base.defaultLevel = lvl_; 57 | base.useColor = color_; 58 | base.relativeTimes = relativeTimes_; 59 | base.startTime = current_time(); 60 | base.write = log_stdout; 61 | } 62 | 63 | public fn void Base.destroy(Base* base) 64 | { 65 | } 66 | 67 | public type Log struct { 68 | Base* base; 69 | Level level; 70 | char[16] name; 71 | } 72 | 73 | public fn void Log.init(Log* log, Base* base, const char* name_) { 74 | log.base = base; 75 | log.level = base.defaultLevel; 76 | strncpy(log.name, name_, 16); 77 | log.name[15] = 0; 78 | } 79 | 80 | public fn void Log.setLevel(Log* log, Level lvl) { 81 | log.level = lvl; 82 | } 83 | 84 | public fn void Log.debug(Log* log, const char* format, ...) { 85 | if (Level.Debug > log.level) return; 86 | va_list args; 87 | va_start(args, format); 88 | log.internal(Level.Debug, color.Blue, format, args); 89 | va_end(args); 90 | } 91 | 92 | public fn void Log.info(Log* log, const char* format, ...) { 93 | if (Level.Info > log.level) return; 94 | va_list args; 95 | va_start(args, format); 96 | log.internal(Level.Info, color.White, format, args); 97 | va_end(args); 98 | } 99 | 100 | public fn void Log.warn(Log* log, const char* format, ...) { 101 | if (Level.Warn > log.level) return; 102 | va_list args; 103 | va_start(args, format); 104 | log.internal(Level.Warn, color.Yellow, format, args); 105 | va_end(args); 106 | } 107 | 108 | public fn void Log.error(Log* log, const char* format, ...) { 109 | if (Level.Error > log.level) return; 110 | va_list args; 111 | va_start(args, format); 112 | log.internal(Level.Error, color.Red, format, args); 113 | va_end(args); 114 | } 115 | 116 | public fn void Log.fatal(Log* log, const char* format, ...) { 117 | va_list args; 118 | va_start(args, format); 119 | log.internal(Level.Fatal, color.Red, format, args); 120 | va_end(args); 121 | exit(EXIT_FAILURE); 122 | } 123 | 124 | fn void Log.internal(Log* log, Level level, const char* color_, const char* format, va_list args) { 125 | char[256] buffer; 126 | char* cp = buffer; 127 | 128 | if (log.base.useColor) cp += sprintf(cp, "%s", color_); 129 | *cp++ = '['; 130 | if (log.base.relativeTimes) { 131 | u64 now = current_time(); 132 | now -= log.base.startTime; 133 | u64 subsec = now % 1000000; 134 | cp += sprintf(cp, "%04d.%06d", now / 1000000, subsec); 135 | } else { 136 | Timeval tv; 137 | gettimeofday(&tv, nil); 138 | Time now = tv.tv_sec; 139 | libc_time.Tm* now2 = libc_time.localtime(&now); 140 | cp += sprintf(cp, "%02d/%02d/%02d %02d:%02d:%02d.%d", 141 | now2.tm_year % 100, now2.tm_mon + 1, now2.tm_mday, 142 | now2.tm_hour, now2.tm_min, now2.tm_sec, tv.tv_usec / 1000); 143 | } 144 | *cp++ = ']'; 145 | cp += sprintf(cp, " %s %s: ", level2str(level), log.name); 146 | cp += vsprintf(cp, format, args); 147 | if (log.base.useColor) cp += sprintf(cp, "%s", color.Normal); 148 | *cp++ = '\n'; 149 | *cp = 0; 150 | log.base.write(buffer, cast(cp - buffer)); 151 | } 152 | 153 | -------------------------------------------------------------------------------- /lua/script.c2: -------------------------------------------------------------------------------- 1 | module script; 2 | 3 | import stdio local; 4 | import lua; 5 | import lua_auxlib local; 6 | import lua_lib local; 7 | import color; 8 | 9 | public type LuaState struct { 10 | lua.State* l; 11 | } 12 | 13 | public fn void LuaState.init(LuaState* ls) { 14 | ls.l = luaL_newstate(); 15 | ls.loadLib("", luaopen_base); 16 | //ls.loadLib(LUA_TABLIBNAME, luaopen_table); 17 | ls.loadLib("table", luaopen_table); 18 | //ls.loadLib(LUA_IOLIBNAME, luaopen_io); 19 | //ls.loadLib(LUA_OSLIBNAME, luaopen_os); 20 | //ls.loadLib(LUA_MATHLIBNAME, luaopen_math); 21 | ls.loadLib("math", luaopen_math); 22 | //ls.loadLib(LUA_STRINGLIBNAME, luaopen_string); 23 | ls.loadLib("string", luaopen_string); 24 | //ls.loadLib(LUA_DBLIBNAME, luaopen_debug); 25 | ls.loadLib("debug", luaopen_debug); 26 | //ls.loadLib(LUA_COLIBNAME, luaopen_coroutine); 27 | ls.loadLib("coroutine", luaopen_coroutine); 28 | } 29 | 30 | public fn void LuaState.destroy(LuaState* ls) { 31 | lua.close(ls.l); 32 | } 33 | 34 | fn void LuaState.loadLib(LuaState* ls, const char* name, lua.CFunction f) { 35 | luaL_requiref(ls.l, name, f, 1); 36 | //lua_pop(ls.l, 1); // TODO macro 37 | lua.settop(ls.l, -(1)-1); 38 | } 39 | 40 | public fn void LuaState.registerFunc(LuaState* ls, const char* name, lua.CFunction func) { 41 | //lua_pushcfunction(ls.l, name, func); 42 | lua.pushcclosure(ls.l, func, 0); 43 | lua.setglobal(ls.l, name); 44 | } 45 | 46 | public fn bool LuaState.loadFile(LuaState* ls, const char* filename) { 47 | i32 status = luaL_loadfilex(ls.l, filename, nil); 48 | if (status != lua.LUA_OK) { 49 | printf("lua: %s\n", lua.tolstring(ls.l, -1, nil)); 50 | //lua_pop(ls.l, 1); 51 | lua.settop(ls.l, -(1)-1); 52 | return false; 53 | } 54 | return true; 55 | 56 | } 57 | 58 | public fn bool LuaState.execute(LuaState* ls) { 59 | i32 status = lua.pcallk(ls.l, 0, lua.LUA_MULTRET, 0, 0, nil); 60 | if (status != lua.LUA_OK) { 61 | printf("lua: %s\n", lua.tolstring(ls.l, -1, nil)); 62 | //lua_pop(ls.l, 1); 63 | lua.settop(ls.l, -(1)-1); 64 | return false; 65 | } 66 | return true; 67 | } 68 | 69 | public fn bool LuaState.runFunction(LuaState* ls, const char* fname) { 70 | printf(" C> running function %s\n", fname); 71 | // TODO check returns 72 | lua.getglobal(ls.l, fname); 73 | i32 status = lua.pcallk(ls.l, 0, 0, 0, 0, nil); 74 | if (status != lua.LUA_OK) { 75 | printf("lua: %s\n", lua.tolstring(ls.l, -1, nil)); 76 | //lua_pop(ls.l, 1); 77 | lua.settop(ls.l, -(1)-1); 78 | return false; 79 | } 80 | return true; 81 | } 82 | 83 | public fn i32 startScript(lua.State* l) { 84 | // TODO how to stop calling lua script? 85 | i32 args = lua.gettop(l); 86 | if (args != 1) { 87 | lua.pushboolean(l, false); 88 | lua.pushfstring(l, "need 1 argument, got %d", args); 89 | //printf(" C> startScript: need 1 arg\n"); 90 | return 2; 91 | //return -1; 92 | } 93 | if (!lua.isstring(l, 1)) { 94 | printf(" C> startScript: arg not a string\n"); 95 | return -1; 96 | } 97 | const char* scriptname = lua.tolstring(l, -1, nil); 98 | printf(" C> startScript(%s%s%s)\n", color.Green, scriptname, color.Normal); 99 | 100 | // TODO check returns 101 | lua.getglobal(l, "runScript"); 102 | lua.pushstring(l, scriptname); 103 | i32 status = lua.pcallk(l, 1, 0, 0, 0, nil); 104 | if (status != lua.LUA_OK) { 105 | printf(" C> error starting script %s: %s\n", scriptname, lua.tolstring(l, -1, nil)); 106 | lua.settop(l, -(1)-1); 107 | return 0; 108 | // if we return -1, calling script is not continued 109 | } 110 | return 0; 111 | } 112 | 113 | public fn i32 stopScript(lua.State* l) { 114 | i32 args = lua.gettop(l); 115 | if (args != 1) { 116 | lua.pushboolean(l, false); 117 | lua.pushfstring(l, "need 1 argument, got %d", args); 118 | return -2; 119 | } 120 | if (!lua.isstring(l, 1)) { 121 | printf(" C> stopString: arg not a string\n"); 122 | return -1; 123 | } 124 | const char* scriptname = lua.tolstring(l, -1, nil); 125 | printf(" C> stopScript(%s%s%s)\n", color.Red, scriptname, color.Normal); 126 | 127 | return 0; 128 | } 129 | 130 | public fn void LuaState.callIntInt(LuaState* ls, const char* fname, i32 arg1) { 131 | // TODO check returns 132 | lua.getglobal(ls.l, fname); 133 | lua.pushinteger(ls.l, arg1); 134 | lua.callk(ls.l, 1, 1, 0, nil); 135 | i32 result = lua.tointegerx(ls.l, 1, nil); 136 | //lua_pop(ls.l, 1); 137 | lua.settop(ls.l, -(1)-1); 138 | printf(" C> calling LUA %s(%d) = %d\n", fname, arg1, result); 139 | } 140 | 141 | -------------------------------------------------------------------------------- /recipe.txt: -------------------------------------------------------------------------------- 1 | 2 | plugin refs_generator 3 | 4 | executable base64 5 | $backend c 6 | base64/base64.c2 7 | base64/main.c2 8 | end 9 | 10 | #executable sha1 11 | # $warnings no-unused 12 | # $backend c 13 | # sha1/sha1.c2 14 | # sha1/main.c2 15 | #end 16 | 17 | executable cstrip 18 | $warnings no-unused 19 | $backend c 20 | common/file/reader.c2 21 | common/file/writer.c2 22 | common/string_buffer.c2 23 | comment_strip/main.c2 24 | end 25 | 26 | executable event 27 | $warnings no-unused 28 | $backend c 29 | common/color.c2 30 | common/logger.c2 31 | event/events.c2 32 | event/example_socket.c2 33 | event/main.c2 34 | end 35 | 36 | executable file_ops 37 | $warnings no-unused 38 | $backend c 39 | common/file/reader.c2 40 | file_ops/main.c2 41 | end 42 | 43 | executable inline_asm 44 | # $warnings no-unused 45 | $backend c 46 | inline_asm/main.c2 47 | end 48 | 49 | executable json_parser 50 | $warnings no-unused 51 | $backend c 52 | common/file/reader.c2 53 | common/file/writer.c2 54 | json_parser/json_parser.c2 55 | json_parser/json_data.c2 56 | json_parser/json_builder.c2 57 | json_parser/json_serialize.c2 58 | json_parser/main.c2 59 | end 60 | 61 | executable list 62 | $warnings no-unused 63 | $backend c 64 | common/color.c2 65 | list/list.c2 66 | list/main.c2 67 | end 68 | 69 | executable log 70 | $warnings no-unused 71 | $backend c 72 | common/logger.c2 73 | common/color.c2 74 | logger/main.c2 75 | end 76 | 77 | executable jump 78 | $warnings no-unused 79 | $backend c 80 | longjmp/jump.c2 81 | end 82 | 83 | executable lua_test 84 | $warnings no-unused 85 | $backend c 86 | $use lua static 87 | common/color.c2 88 | lua/script.c2 89 | lua/main.c2 90 | end 91 | 92 | executable signal_test 93 | $warnings no-unused 94 | $backend c 95 | signals/main.c2 96 | end 97 | 98 | executable socket 99 | $backend c 100 | socket/server.c2 101 | end 102 | 103 | executable string 104 | $warnings no-unused 105 | $backend c 106 | common/string_buffer.c2 107 | string_buffer/main.c2 108 | end 109 | 110 | executable toml_parser 111 | $warnings no-unused 112 | $backend c 113 | # $config DEBUG_NODES 114 | common/file/reader.c2 115 | toml_parser/toml_tokenizer.c2 116 | toml_parser/toml_parser.c2 117 | toml_parser/main.c2 118 | end 119 | 120 | executable yaml_parser 121 | $warnings no-unused 122 | $backend c 123 | # $config YAML_PRINT_TOKENS 124 | common/file/reader.c2 125 | yaml_parser/yaml_parser.c2 126 | yaml_parser/yaml_tokenizer.c2 127 | yaml_parser/yaml_data.c2 128 | yaml_parser/yaml_iterator.c2 129 | yaml_parser/yaml_dump.c2 130 | yaml_parser/main.c2 131 | end 132 | 133 | executable xml_parser 134 | $warnings no-unused 135 | $backend c 136 | common/color.c2 137 | common/file/reader.c2 138 | # file_utils.c2 139 | xml_parser/xml_parser.c2 140 | xml_parser/xml_structure.c2 141 | xml_parser/main.c2 142 | end 143 | 144 | executable sudoku 145 | $warnings no-unused 146 | $backend c 147 | common/color.c2 148 | sudoku/sudoku.c2 149 | sudoku/boards.c2 150 | end 151 | 152 | lib plugin1 dynamic 153 | $warnings no-unused 154 | $backend c 155 | $export plugin_main 156 | plugin/other.c2 157 | plugin/plugin1.c2 158 | end 159 | 160 | lib plugin2 dynamic 161 | $warnings no-unused 162 | $backend c 163 | $export plugin_main 164 | plugin/plugin2.c2 165 | end 166 | 167 | executable plugin_mgr 168 | $warnings no-unused 169 | $backend c 170 | $use dl dynamic 171 | plugin/other.c2 172 | plugin/plugin_mgr.c2 173 | end 174 | 175 | executable test_lib 176 | $warnings no-unused 177 | $backend c 178 | $use plugin1 dynamic 179 | plugin/test_lib.c2 180 | end 181 | 182 | executable pthread_test 183 | $warnings no-unused 184 | $backend c 185 | $use pthread dynamic 186 | 187 | pthread/pthread.c2 188 | end 189 | 190 | executable unit_test 191 | $warnings no-unused 192 | $backend c 193 | $plugin unit_test [] 194 | 195 | unit_test/mod1.c2 196 | unit_test/mod2.c2 197 | 198 | unit_test/mod1_test.c2 199 | unit_test/mod2_test.c2 200 | unit_test/test_main.c2 201 | end 202 | 203 | executable mc_sender 204 | $warnings no-unused 205 | $backend c 206 | 207 | multicast/multicast_sender.c2 208 | multicast/sender.c2 209 | end 210 | 211 | executable mc_receiver 212 | $warnings no-unused 213 | $backend c 214 | 215 | event/events.c2 216 | multicast/multicast_receiver.c2 217 | multicast/receiver.c2 218 | end 219 | 220 | executable dir_walker 221 | $warnings no-unused 222 | $backend c 223 | 224 | dir_walker/main.c2 225 | end 226 | 227 | executable source_lib_use 228 | $warnings no-unused 229 | $backend c 230 | #$use libc static 231 | $use foobar source 232 | #$use foobar static 233 | #$use foobar dynamic 234 | 235 | source_lib_use/main.c2 236 | end 237 | -------------------------------------------------------------------------------- /event/example_socket.c2: -------------------------------------------------------------------------------- 1 | module example_socket; 2 | 3 | import stdio local; 4 | import stdlib local; 5 | import unistd local; 6 | import string local; 7 | import sys_socket local; 8 | import events; 9 | import logger; 10 | 11 | const u8 LF = 0x0A; 12 | const u8 CR = 0x0D; 13 | 14 | fn void fatal(const char* msg) { 15 | printf("%s\n", msg); 16 | exit(EXIT_FAILURE); 17 | } 18 | 19 | fn void stripNewline(char* buffer, i64 len) 20 | { 21 | if (buffer[len-1] == LF) { 22 | buffer[len-1] = 0; 23 | len--; 24 | } 25 | if (buffer[len-1] == CR) { 26 | buffer[len-1] = 0; 27 | len--; 28 | } 29 | } 30 | 31 | type Connection struct { 32 | i32 fd; 33 | Connection* next; 34 | logger.Log* log; 35 | events.Event event; 36 | Socket* socket; 37 | } 38 | 39 | fn Connection* Connection.create(i32 fd, Socket* s, logger.Log* log) { 40 | Connection* conn = calloc(1, sizeof(Connection)); 41 | conn.fd = fd; 42 | conn.log = log; 43 | conn.socket = s; 44 | conn.event.assign(s.base, fd, events.READ, connection_on_data, conn); 45 | conn.event.add(); 46 | 47 | const char* msg = "welcome\n"; 48 | write(fd, msg, strlen(msg)); 49 | return conn; 50 | } 51 | 52 | fn void Connection.onData(Connection* conn) 53 | { 54 | char[128] buffer; 55 | 56 | i64 numread = read(conn.fd, buffer, 128); 57 | if (numread == 0) { 58 | conn.log.warn("client disconnected"); 59 | conn.destroy(); 60 | return; 61 | } 62 | buffer[numread] = 0; 63 | stripNewline(buffer, numread); 64 | 65 | conn.log.info("[%d] read [%s]", conn.fd, buffer); 66 | if (strcmp(buffer, "quit") == 0) { 67 | conn.log.warn("client requested disconnect"); 68 | conn.destroy(); 69 | } 70 | } 71 | 72 | fn void Connection.destroy(Connection* conn) 73 | { 74 | conn.log.debug("destroy conn %d", conn.fd); 75 | conn.socket.removeConnection(conn); 76 | conn.event.del(); 77 | close(conn.fd); 78 | free(conn); 79 | } 80 | 81 | fn void connection_on_data(i32 fd, void* arg, u16 flags) 82 | { 83 | Connection* conn = arg; 84 | conn.onData(); 85 | } 86 | 87 | 88 | public type Socket struct @(opaque) { 89 | i32 fd; 90 | events.Base* base; 91 | events.Event event; 92 | logger.Log log; 93 | Connection* conns; 94 | } 95 | 96 | public fn Socket* Socket.create(events.Base* base, logger.Base* logbase, const char* name, u16 port) 97 | { 98 | Socket* s = calloc(1, sizeof(Socket)); 99 | s.base = base; 100 | s.log.init(logbase, name); 101 | s.log.setLevel(logger.Level.Debug); 102 | 103 | s.fd = socket(AF_INET, SocketType.SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP); 104 | if (s.fd == -1) { 105 | fatal("opening TCP socket"); 106 | } 107 | 108 | Sockaddr_in my_addr; 109 | my_addr.sin_family = AF_INET; 110 | my_addr.sin_port = htons(port); 111 | my_addr.sin_addr.s_addr = INADDR_ANY; 112 | memset(&my_addr.sin_zero, 0, 8); 113 | 114 | i32 reuse = 1; 115 | if (setsockopt(s.fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0) { 116 | fatal("setsockopt(SO_REUSEADDR)"); 117 | } 118 | 119 | if (bind(s.fd, cast(&my_addr), sizeof(Sockaddr)) != 0) fatal("bind() failed"); 120 | 121 | if (listen(s.fd, 1) != 0) fatal("listen() failed"); 122 | 123 | s.log.debug("listening to port %d", port); 124 | s.event.assign(base, s.fd, events.READ, socket_on_connection, s); 125 | s.event.add(); 126 | return s; 127 | } 128 | 129 | public fn void Socket.destroy(Socket* s) 130 | { 131 | s.log.debug("closing socket"); 132 | while (s.conns) { 133 | s.conns.destroy(); // self-removes itself from list 134 | } 135 | s.event.del(); 136 | close(s.fd); 137 | free(s); 138 | } 139 | 140 | fn void Socket.addConnection(Socket* s, Connection* c) 141 | { 142 | s.log.debug("add conn %d", c.fd); 143 | if (s.conns == nil) { 144 | s.conns = c; 145 | } else { 146 | Connection* cur = s.conns; 147 | while (cur.next) cur = cur.next; 148 | cur.next = c; 149 | } 150 | } 151 | 152 | fn void Socket.removeConnection(Socket* s, Connection* c) 153 | { 154 | s.log.debug("remove conn %d", c.fd); 155 | if (s.conns == c) { 156 | s.conns = c.next; 157 | } else { 158 | Connection* cur = s.conns; 159 | while (cur.next != c) cur = cur.next; 160 | cur.next = c.next; 161 | } 162 | c.next = nil; 163 | } 164 | 165 | fn void Socket.onConnection(Socket* s) 166 | { 167 | u32 sin_size = sizeof(Sockaddr_in); 168 | Sockaddr_in remote; 169 | i32 new_fd = accept4(s.fd, cast(&remote), &sin_size, SOCK_CLOEXEC); 170 | if (new_fd == -1) fatal("accept4"); 171 | 172 | char* src_ipnr = inet_ntoa(remote.sin_addr); 173 | s.log.info("new connection from %s", src_ipnr); 174 | s.addConnection(Connection.create(new_fd, s, &s.log)); 175 | } 176 | 177 | fn void socket_on_connection(i32 fd, void* arg, u16 flags) 178 | { 179 | Socket* s = arg; 180 | s.onConnection(); 181 | } 182 | 183 | -------------------------------------------------------------------------------- /yaml_parser/yaml_iterator.c2: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 Bas van den Berg 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | module yaml; 17 | 18 | import string; 19 | 20 | public fn bool Node.isMap(const Node* n) { return n.kind == NodeKind.Map; } 21 | 22 | public fn bool Node.isSequence(const Node* n) { return n.kind == NodeKind.Sequence; } 23 | 24 | public fn bool Node.isScalar(const Node* n) { return n.kind == NodeKind.Scalar; } 25 | 26 | 27 | // TODO only pass iterators? (that way we dont need Parser* anymore) 28 | public fn const Node* Parser.getRoot(const Parser* p) { 29 | u32 node_count = cast(p.data.nodes_cur - p.data.nodes) - 1; 30 | if (node_count == 0) return nil; 31 | return &p.data.nodes[1]; 32 | } 33 | 34 | public fn const char* Parser.getScalarValue(const Parser* p, const char* path) { 35 | const Node* n = p.findNode(path); 36 | if (n && n.isScalar()) return &p.data.text[n.text_idx]; 37 | return nil; 38 | } 39 | 40 | public fn const Node* Parser.findNode(const Parser* p, const char* path) { 41 | return p.data.findNode(path); 42 | } 43 | 44 | fn const Node* Data.findNode(const Data* d, const char* path) { 45 | u32 node_count = cast(d.nodes_cur - d.nodes) - 1; 46 | if (node_count == 0) return nil; 47 | const Node* root = &d.nodes[1]; 48 | if (root.kind == NodeKind.Sequence) return nil; 49 | return d.findChildNode(path, root.child_idx); 50 | } 51 | 52 | fn const Node* Data.findChildNode(const Data* d, const char* path, u32 next) { 53 | // Note: path can be 'a.b.c' while node names are 'a.b' and 'c' so look for starts-with 54 | while (next) { 55 | const Node* node = d.idx2node(next); 56 | if (node.name_idx) { 57 | const char* name = &d.text[node.name_idx]; 58 | const char* rest = starts_with(path, name); 59 | if (rest) { // match 60 | path = rest; 61 | if (path[0] == 0) return node; // found node 62 | if (node.kind == NodeKind.Sequence) return nil; // dont search in sequence 63 | next = node.child_idx; 64 | continue; 65 | } 66 | } 67 | 68 | next = node.next_idx; 69 | } 70 | return nil; 71 | } 72 | 73 | public type Iter struct { 74 | const void* data; 75 | const Node* node; 76 | } 77 | 78 | public fn Iter Parser.getNodeChildIter(const Parser* p, const Node* n) { 79 | Iter iter = { .data = &p.data, .node = nil } 80 | if (n && n.kind != NodeKind.Scalar && n.child_idx) { 81 | iter.node = p.data.idx2node(n.child_idx); 82 | } 83 | return iter; 84 | } 85 | 86 | public fn void Iter.next(Iter* iter) { 87 | const Data* d = cast(iter.data); 88 | if (iter.node) { 89 | if (iter.node.next_idx) iter.node = d.idx2node(iter.node.next_idx); 90 | else iter.node = nil; 91 | } 92 | } 93 | 94 | public fn bool Iter.done(const Iter* iter) { 95 | return iter.node == nil; 96 | } 97 | 98 | public fn const char* Iter.getName(const Iter* iter) { 99 | const Data* d = cast(iter.data); 100 | if (iter.node) return &d.text[iter.node.name_idx]; 101 | return nil; 102 | } 103 | 104 | public fn const char* Iter.getValue(const Iter* iter) { 105 | const Data* d = cast(iter.data); 106 | if (iter.node && iter.node.kind == NodeKind.Scalar) return &d.text[iter.node.text_idx]; 107 | return nil; 108 | } 109 | 110 | public fn Iter Iter.getChildIter(Iter* parent) { 111 | Iter iter = { .data = parent.data, .node = nil } 112 | if (parent.node == nil) return iter; 113 | 114 | const Node* n = parent.node; 115 | if (n.kind != NodeKind.Scalar && n.child_idx) { 116 | const Data* d = cast(iter.data); 117 | iter.node = d.idx2node(n.child_idx); 118 | } 119 | return iter; 120 | } 121 | 122 | public fn const char* Iter.getChildScalarValue(Iter* iter, const char* path) { 123 | if (!iter.node) return nil; 124 | 125 | if (iter.node.kind == NodeKind.Sequence) return nil; 126 | const Data* d = cast(iter.data); 127 | const Node* n = d.findChildNode(path, iter.node.child_idx); 128 | if (n && n.isScalar()) return &d.text[n.text_idx]; 129 | return nil; 130 | } 131 | 132 | fn const char* starts_with(const char* full, const char* start) { 133 | u32 len = cast(string.strlen(start)); 134 | if (string.strncmp(full, start, len) == 0) { 135 | full += len; 136 | if (full[0] == '.') return full+1; 137 | if (full[0] == 0) return full; 138 | } 139 | return nil; 140 | } 141 | 142 | -------------------------------------------------------------------------------- /frog/frog.c2: -------------------------------------------------------------------------------- 1 | module frog; 2 | import stdio local; 3 | import string local; 4 | import list; 5 | import utils; 6 | 7 | /* 8 | - vooruit: on eigen plaatje, nogmaals 9 | - als op ander: die vorige eigen basis 10 | - vooruit: op ander plaatje, dan moet ander 1 stap terug 11 | */ 12 | 13 | const u8 Num_animals = 5; 14 | const u8 Num_tiles = 40; 15 | 16 | type Tile enum u8 { 17 | None = 0, // also for start 18 | Frog = 1, 19 | Goose = 2, 20 | Hare = 4, 21 | Pig = 8, 22 | Rat = 16, 23 | End = 64, 24 | } 25 | 26 | type Board struct { 27 | u8[elemsof(Tiles)] positions; 28 | u8 result_count; 29 | Tile[Num_animals] results; 30 | 31 | Player[Num_animals] players; 32 | list.Element order; 33 | } 34 | 35 | const Tile[] Tiles = { 36 | Tile.None, // 0 37 | Tile.Frog, 38 | Tile.Pig, 39 | Tile.Goose, 40 | Tile.Rat, 41 | Tile.Frog, // 5 42 | Tile.Goose, 43 | Tile.Hare, 44 | Tile.Pig, 45 | Tile.Rat, 46 | Tile.Hare, // 10 47 | Tile.Frog, 48 | Tile.Pig, 49 | Tile.Rat, 50 | Tile.Goose, 51 | Tile.Frog, // 15 52 | Tile.Pig, 53 | Tile.Hare, 54 | Tile.Goose, 55 | Tile.Frog, 56 | Tile.Rat, // 20 57 | Tile.Pig, 58 | Tile.Rat, 59 | Tile.Goose, 60 | Tile.Hare, 61 | Tile.Pig, // 25 62 | Tile.Frog, 63 | Tile.Hare, 64 | Tile.Goose, 65 | Tile.Rat, 66 | Tile.Hare, // 30 67 | Tile.Pig, 68 | Tile.Frog, 69 | Tile.Rat, 70 | Tile.Goose, 71 | Tile.Hare, // 35 72 | Tile.Rat, 73 | Tile.Hare, 74 | Tile.Goose, 75 | Tile.Pig, 76 | Tile.Frog, // 40 77 | Tile.End, 78 | } 79 | 80 | fn char tile2str(Tile t) { 81 | switch (t) { 82 | case Tile.Frog: return 'F'; 83 | case Tile.Goose: return 'G'; 84 | case Tile.Hare: return 'H'; 85 | case Tile.Pig: return 'P'; 86 | case Tile.Rat: return 'R'; 87 | case Tile.None: break; 88 | case Tile.End: return '_'; 89 | } 90 | return ' '; 91 | } 92 | 93 | Board board; 94 | 95 | type Player struct { 96 | list.Element element; 97 | Tile tile; 98 | u32 position; 99 | } 100 | 101 | public fn void init() { 102 | memset(&board, 0, sizeof(Board)); 103 | board.positions[0] |= Tile.Frog; 104 | board.positions[0] |= Tile.Goose; 105 | board.positions[0] |= Tile.Hare; 106 | board.positions[0] |= Tile.Pig; 107 | board.positions[0] |= Tile.Rat; 108 | 109 | board.players[0].tile |= Tile.Frog; 110 | board.players[1].tile |= Tile.Goose; 111 | board.players[2].tile |= Tile.Hare; 112 | board.players[3].tile |= Tile.Pig; 113 | board.players[4].tile |= Tile.Rat; 114 | board.order.init(); 115 | for (u8 i=0; i Num_tiles + 1) { 131 | u32 overshoot = p.position - (Num_tiles + 1); 132 | p.position = Num_tiles + 1 - overshoot; 133 | } 134 | board.positions[p.position] |= p.tile; 135 | 136 | // TODO check 137 | 138 | //printf(" -> %u\n", p.position); 139 | // add to list if not done 140 | if (p.position != Num_tiles+1) board.order.addTail(cur); 141 | } 142 | 143 | public fn void run() { 144 | u32 step = 1; 145 | while (!board.order.isEmpty()) { 146 | do_step(step); 147 | step++; 148 | dump(); 149 | 150 | //if (step > 10) break; 151 | } 152 | } 153 | 154 | fn void print_pos(Dump* d, u8 pos) { 155 | for (u32 i=Tile.Frog; i<= Tile.Rat; i = i*2) { 156 | *d.l1++ = ' '; 157 | Tile t = pos & i; 158 | if (t == Tile.None) *d.l2 = ' '; 159 | else *d.l2 = tile2str(t); 160 | d.l2++; 161 | } 162 | } 163 | 164 | type Dump struct { 165 | char[128] line1; 166 | char[128] line2; 167 | char* l1; 168 | char* l2; 169 | } 170 | 171 | fn void Dump.init(Dump* d) { 172 | memset(d, 0, sizeof(Dump)); 173 | d.l1 = d.line1; 174 | d.l2 = d.line2; 175 | } 176 | 177 | public fn void dump() { 178 | Dump d; 179 | d.init(); 180 | print_pos(&d, board.positions[0]); 181 | for (u32 i=1; i(tv.tv_sec); 17 | now64 *= 1000000; 18 | now64 += tv.tv_usec; 19 | return now64; 20 | } 21 | 22 | fn bool isCache(const char* filename) { 23 | u64 len = strlen(filename); 24 | u64 suflen = strlen(Cache_suffix); 25 | if (len <= suflen + 1) return false; 26 | if (strcmp(&filename[len - suflen], Cache_suffix) == 0) return true; 27 | return false; 28 | } 29 | 30 | public fn i32 main(i32 argc, char** argv) { 31 | if (argc < 2) { 32 | printf("Usage: parser [json-file]\n"); 33 | return 0; 34 | } 35 | const char* filename = argv[1]; 36 | 37 | bool use_cache = false; 38 | json.Parser parser; 39 | parser.init(); 40 | 41 | if (!use_cache || !isCache(filename)) { 42 | u64 t1 = current_time(); 43 | file_utils.Reader file; 44 | file.open(filename); 45 | 46 | //printf("%s", file.data()); 47 | if (file.isEmpty()) { 48 | printf("file %s is empty\n", filename); 49 | return 0; 50 | } 51 | 52 | if (!parser.parse(cast(file.data()))) { 53 | fprintf(stderr, "error parsing %s: %s\n", filename, parser.getDiag()); 54 | return -1; 55 | } 56 | u64 t2 = current_time(); 57 | printf("parsing took %d usec\n", t2 - t1); 58 | file.close(); 59 | } else { 60 | u64 t1 = current_time(); 61 | if (!parser.read_cache(filename)) { 62 | fprintf(stderr, "error reading cache %s: %s\n", filename, parser.getDiag()); 63 | return -1; 64 | } 65 | u64 t2 = current_time(); 66 | printf("reading cache took %d usec\n", t2 - t1); 67 | } 68 | parser.report(); 69 | 70 | if (argc >= 3) parser.dump(argc > 3); 71 | 72 | json.Iter root = parser.get_root(); 73 | 74 | if (strcmp(filename, "json_parser/example1.json") == 0) { 75 | const char* search = "platform.checksum"; 76 | const char* value = root.child_value(search); 77 | if (value) { 78 | printf("%s = [%s]\n", search, value); 79 | } else { 80 | printf("cannot find %s\n", search); 81 | } 82 | } 83 | 84 | if (strcmp(filename, "json_parser/example3.json") == 0) { 85 | // generated by http://jsongen.pykaso.net 86 | printf("Items:\n"); 87 | json.Iter iter = root.get_child("items"); 88 | if (iter.done()) printf(" No items found\n"); 89 | if (!iter.is_array()) printf("Not array\n"); 90 | iter = iter.get_child_iter(); 91 | while (!iter.done()) { 92 | printf(" item:\n"); 93 | json.Iter iter2 = iter.get_child_iter(); 94 | while (!iter2.done()) { 95 | const char* k = iter2.key(); 96 | const char* v = iter2.value(); 97 | printf(" %s -> %s\n", k, v); 98 | iter2.next(); 99 | } 100 | iter.next(); 101 | } 102 | } 103 | 104 | if (strcmp(filename, "json_parser/example4.json") == 0) { 105 | printf("top level:\n"); 106 | json.Iter iter = root.get_child_iter(); 107 | while (!iter.done()) { 108 | const char* k = iter.key(); 109 | const char* v = iter.value(); 110 | printf(" %s: %s\n", k, v); 111 | iter.next(); 112 | } 113 | } 114 | 115 | if (strcmp(filename, "json_parser/example5.json") == 0) { 116 | json.Iter iter = root.get_child_iter(); 117 | if (iter.check_schema("ssso(ss)")) { 118 | i32 id = stdlib.atoi(iter.value()); 119 | iter.next(); 120 | const char* uid = iter.value(); 121 | iter.next(); 122 | const char* action = iter.value(); 123 | iter.next(); 124 | printf("OBJECT %d\n", iter.cur_idx); 125 | const char* vendor = iter.child_value("chargePointVendor"); 126 | const char* model = iter.child_value("chargePointModel"); 127 | printf("%d uid %s action %s (vendor %s, model %s)\n", id, uid, action, vendor, model); 128 | } else { 129 | printf("incorrect schema\n"); 130 | } 131 | } 132 | 133 | if (use_cache && !isCache(filename)) { 134 | u64 t3 = current_time(); 135 | char[128] cache_file; 136 | sprintf(cache_file, "%s%s", filename, Cache_suffix); 137 | parser.write_cache(cache_file); 138 | u64 t4 = current_time(); 139 | printf("writing %s took %d usec\n", cache_file, t4 - t3); 140 | } 141 | 142 | parser.destroy(); 143 | 144 | #if 0 145 | json.Builder builder; 146 | builder.init(1024); 147 | builder.addArray(nil); 148 | builder.addNumber(nil, 2); 149 | builder.addValue(nil, "19223201"); 150 | builder.addValue(nil, "BootNotification"); 151 | builder.addObject(nil); 152 | builder.addValue("chargePointVendor", "VendorX"); 153 | builder.addValue("chargePointModel", "SingleSocketCharger"); 154 | builder.closeObject(); 155 | builder.closeArray(); 156 | //builder.finalize(); 157 | printf("---\n%s\n---\n", builder.getData()); 158 | builder.free(); 159 | #endif 160 | 161 | return 0; 162 | } 163 | 164 | -------------------------------------------------------------------------------- /sha1/sha1.c2: -------------------------------------------------------------------------------- 1 | module sha1; 2 | 3 | import string local; 4 | 5 | public type Ctx struct { 6 | u32[5] state; 7 | u32[2] count; 8 | u8[64] buffer; 9 | } 10 | 11 | type Char64Long16 union { 12 | u8[64] c; 13 | u32[16] l; 14 | } 15 | 16 | fn u32 rol(u32 value, u32 bits) { 17 | return ((value) << (bits)) | ((value) >> (32 - (bits))); 18 | } 19 | 20 | // Little Endian 21 | #define blk0(i) (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) \ 22 | |(rol(block.l[i],8)&0x00FF00FF)) 23 | 24 | // Big Endian 25 | //#define blk0(i) block.l[i] 26 | 27 | #define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \ 28 | ^block.l[(i+2)&15]^block.l[i&15],1)) 29 | 30 | 31 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 32 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 33 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 34 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 35 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 36 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 37 | 38 | //func void transform(u32[5] state, const u8[64] buffer) { 39 | fn void transform(u32* state, const u8* buffer) { 40 | Char64Long16 block; 41 | memcpy(&block, buffer, 64); 42 | 43 | u32 a = state[0]; 44 | u32 b = state[1]; 45 | u32 c = state[2]; 46 | u32 d = state[3]; 47 | u32 e = state[4]; 48 | 49 | R0(a, b, c, d, e, 0) 50 | 51 | R0(e, a, b, c, d, 1) 52 | R0(d, e, a, b, c, 2) 53 | R0(c, d, e, a, b, 3) 54 | R0(b, c, d, e, a, 4) 55 | R0(a, b, c, d, e, 5) 56 | R0(e, a, b, c, d, 6) 57 | R0(d, e, a, b, c, 7) 58 | R0(c, d, e, a, b, 8) 59 | R0(b, c, d, e, a, 9) 60 | R0(a, b, c, d, e, 10) 61 | R0(e, a, b, c, d, 11) 62 | R0(d, e, a, b, c, 12) 63 | R0(c, d, e, a, b, 13) 64 | R0(b, c, d, e, a, 14) 65 | R0(a, b, c, d, e, 15) 66 | R1(e, a, b, c, d, 16) 67 | R1(d, e, a, b, c, 17) 68 | R1(c, d, e, a, b, 18) 69 | R1(b, c, d, e, a, 19) 70 | R2(a, b, c, d, e, 20) 71 | R2(e, a, b, c, d, 21) 72 | R2(d, e, a, b, c, 22) 73 | R2(c, d, e, a, b, 23) 74 | R2(b, c, d, e, a, 24) 75 | R2(a, b, c, d, e, 25) 76 | R2(e, a, b, c, d, 26) 77 | R2(d, e, a, b, c, 27) 78 | R2(c, d, e, a, b, 28) 79 | R2(b, c, d, e, a, 29) 80 | R2(a, b, c, d, e, 30) 81 | R2(e, a, b, c, d, 31) 82 | R2(d, e, a, b, c, 32) 83 | R2(c, d, e, a, b, 33) 84 | R2(b, c, d, e, a, 34) 85 | R2(a, b, c, d, e, 35) 86 | R2(e, a, b, c, d, 36) 87 | R2(d, e, a, b, c, 37) 88 | R2(c, d, e, a, b, 38) 89 | R2(b, c, d, e, a, 39) 90 | R3(a, b, c, d, e, 40) 91 | R3(e, a, b, c, d, 41) 92 | R3(d, e, a, b, c, 42) 93 | R3(c, d, e, a, b, 43) 94 | R3(b, c, d, e, a, 44) 95 | R3(a, b, c, d, e, 45) 96 | R3(e, a, b, c, d, 46) 97 | R3(d, e, a, b, c, 47) 98 | R3(c, d, e, a, b, 48) 99 | R3(b, c, d, e, a, 49) 100 | R3(a, b, c, d, e, 50) 101 | R3(e, a, b, c, d, 51) 102 | R3(d, e, a, b, c, 52) 103 | R3(c, d, e, a, b, 53) 104 | R3(b, c, d, e, a, 54) 105 | R3(a, b, c, d, e, 55) 106 | R3(e, a, b, c, d, 56) 107 | R3(d, e, a, b, c, 57) 108 | R3(c, d, e, a, b, 58) 109 | R3(b, c, d, e, a, 59) 110 | R4(a, b, c, d, e, 60) 111 | R4(e, a, b, c, d, 61) 112 | R4(d, e, a, b, c, 62) 113 | R4(c, d, e, a, b, 63) 114 | R4(b, c, d, e, a, 64) 115 | R4(a, b, c, d, e, 65) 116 | R4(e, a, b, c, d, 66) 117 | R4(d, e, a, b, c, 67) 118 | R4(c, d, e, a, b, 68) 119 | R4(b, c, d, e, a, 69) 120 | R4(a, b, c, d, e, 70) 121 | R4(e, a, b, c, d, 71) 122 | R4(d, e, a, b, c, 72) 123 | R4(c, d, e, a, b, 73) 124 | R4(b, c, d, e, a, 74) 125 | R4(a, b, c, d, e, 75) 126 | R4(e, a, b, c, d, 76) 127 | R4(d, e, a, b, c, 77) 128 | R4(c, d, e, a, b, 78) 129 | R4(b, c, d, e, a, 79) 130 | 131 | state[0] += a; 132 | state[1] += b; 133 | state[2] += c; 134 | state[3] += d; 135 | state[4] += e; 136 | a = b = c = d = e = 0; 137 | memset(&block, 0, sizeof(block)); 138 | } 139 | 140 | public fn void Ctx.init(Ctx* ctx) { 141 | ctx.state[0] = 0x67452301; 142 | ctx.state[1] = 0xEFCDAB89; 143 | ctx.state[2] = 0x98BADCFE; 144 | ctx.state[3] = 0x10325476; 145 | ctx.state[4] = 0xC3D2E1F0; 146 | ctx.count[0] = 0; 147 | ctx.count[1] = 0; 148 | 149 | } 150 | 151 | public fn void Ctx.update(Ctx* ctx, const u8* data, u32 len) { 152 | u32 i; 153 | u32 j = ctx.count[0]; 154 | if ((ctx.count[0] += len << 3) < j) ctx.count[1]++; 155 | 156 | ctx.count[1] += (len >> 29); 157 | j = (j >> 3) & 63; 158 | if ((j + len) > 63) { 159 | i = 64 - j; 160 | memcpy(&ctx.buffer[j], data, i); 161 | transform(ctx.state, ctx.buffer); 162 | for (; i + 63 < len; i += 64) { 163 | transform(ctx.state, &data[i]); 164 | } 165 | j = 0; 166 | } else { 167 | i = 0; 168 | } 169 | memcpy(&ctx.buffer[j], &data[i], len - i); 170 | } 171 | 172 | // digest is 20 long 173 | public fn void Ctx.finalize(Ctx* ctx, u8* digest) { 174 | u8[8] finalcount; 175 | for(u32 i=0; i<8; i++) { 176 | finalcount[i] = cast(((ctx.count[i >= 4 ? 0 : 1] >> ((3 - (i & 3)) * 8)) & 255)); 177 | } 178 | u8 c = 0200; 179 | ctx.update(&c, 1); 180 | while ((ctx.count[0] & 504) != 448) { 181 | c = 0000; 182 | ctx.update(&c, 1); 183 | } 184 | ctx.update(finalcount, 8); 185 | for (u32 i=0; i<20; i++) { 186 | digest[i] = cast(((ctx.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255)); 187 | } 188 | memset(ctx, 0, sizeof(Ctx)); 189 | memset(&finalcount, 0, sizeof(finalcount)); 190 | } 191 | 192 | public fn void hash(char* output, const char* input, u32 len) { 193 | Ctx ctx; 194 | ctx.init(); 195 | 196 | for (u32 i=0; i(output)); 200 | output[20] = 0; 201 | } 202 | 203 | -------------------------------------------------------------------------------- /toml_parser/toml_tokenizer.c2: -------------------------------------------------------------------------------- 1 | module toml; 2 | 3 | import stdio local; 4 | import string local; 5 | import ctype local; 6 | import stdlib local; 7 | 8 | const u32 MaxText = 1024; 9 | 10 | type TokenKind enum u8 { 11 | Word, // abc 12 | Text, // ".." or ' ..' 13 | Number, // 1234 14 | Kw_true, // true 15 | Kw_false, // false 16 | Lbrace, // [ 17 | Lbrace2, // [[ 18 | Rbrace, // ] 19 | Rbrace2, // ]] 20 | Equals, 21 | Dot, 22 | Comma, 23 | Eof, 24 | Error, 25 | } 26 | 27 | fn const char* token2str(TokenKind k) { 28 | switch (k) { 29 | case Word : return "word"; 30 | case Text : return "text"; 31 | case Number : return "number"; 32 | case Kw_true : return "true"; 33 | case Kw_false: return "false"; 34 | case Lbrace : return "["; 35 | case Lbrace2 : return "[["; 36 | case Rbrace : return "]"; 37 | case Rbrace2 : return "]]"; 38 | case Equals : return "="; 39 | case Dot : return "."; 40 | case Comma : return ","; 41 | case Eof : return "eof"; 42 | case Error : return "error"; 43 | } 44 | return "?"; 45 | } 46 | 47 | type Location struct { 48 | u32 line; 49 | u32 column; 50 | } 51 | 52 | fn void Location.init(Location* l, u32 line, u32 col) { 53 | l.line = line; 54 | l.column = col; 55 | } 56 | 57 | fn const char* Location.str(Location* l) { 58 | local char[32] msg; 59 | sprintf(msg, "line %d:%d", l.line, l.column); 60 | return msg; 61 | } 62 | 63 | type Token struct { 64 | Location loc; 65 | TokenKind kind; 66 | // TODO union? 67 | const char* text; 68 | u32 number; 69 | } 70 | 71 | fn void Token.init(Token* t) { 72 | t.loc.init(0, 0); 73 | t.kind = TokenKind.Eof; 74 | t.text = nil; 75 | t.number = 0; 76 | } 77 | 78 | fn void Token.clear(Token* t) { 79 | t.text = nil; 80 | t.number = 0; 81 | } 82 | 83 | fn void Token.setLocation(Token* t, Location l) { 84 | t.loc = l; 85 | } 86 | 87 | fn bool Token.is(const Token* t, TokenKind k) { 88 | return t.kind == k; 89 | } 90 | 91 | fn bool Token.isNot(const Token* t, TokenKind k) { 92 | return t.kind != k; 93 | } 94 | 95 | fn const char* Token.getName(const Token* t) { 96 | return token2str(t.kind); 97 | } 98 | 99 | type Tokenizer struct { 100 | const char* dataStart; 101 | const char* current; 102 | Location loc; 103 | char[MaxText] text; 104 | Token nextToken; 105 | bool haveNext; 106 | } 107 | 108 | fn void Tokenizer.init(Tokenizer* t, const char* input) { 109 | t.dataStart = input; 110 | t.current = input; 111 | t.loc.init(1, 1); 112 | t.haveNext = false; 113 | t.text[0] = 0; 114 | } 115 | 116 | fn void Tokenizer.lex(Tokenizer* t, Token* result) { 117 | if (t.haveNext) { 118 | // Q: ptr assign or copy? 119 | *result = t.nextToken; 120 | t.haveNext = false; 121 | return; 122 | } 123 | result.clear(); 124 | while (1) { 125 | switch (t.current[0]) { 126 | case 0: 127 | result.loc = t.loc; 128 | result.kind = TokenKind.Eof; 129 | return; 130 | case '#': 131 | if (t.loc.column != 1) { 132 | sprintf(t.text, "unexpected '#' after line start at %s", t.loc.str()); 133 | result.kind = TokenKind.Error; 134 | result.text = t.text; 135 | return; 136 | } 137 | t.parseComment(); 138 | break; 139 | case ' ': fallthrough; 140 | case '\t': 141 | t.advance(1); 142 | break; 143 | case '\n': 144 | t.current++; 145 | t.loc.line++; 146 | t.loc.column = 1; 147 | break; 148 | case '=': 149 | result.loc = t.loc; 150 | result.kind = TokenKind.Equals; 151 | t.advance(1); 152 | return; 153 | case '.': 154 | result.loc = t.loc; 155 | result.kind = TokenKind.Dot; 156 | t.advance(1); 157 | return; 158 | case ',': 159 | result.loc = t.loc; 160 | result.kind = TokenKind.Comma; 161 | t.advance(1); 162 | return; 163 | case '[': 164 | result.loc = t.loc; 165 | if (t.current[1] == '[') { 166 | t.advance(2); 167 | result.kind = TokenKind.Lbrace2; 168 | } else { 169 | t.advance(1); 170 | result.kind = TokenKind.Lbrace; 171 | } 172 | return; 173 | case ']': 174 | result.loc = t.loc; 175 | if (t.current[1] == ']') { 176 | t.advance(2); 177 | result.kind = TokenKind.Rbrace2; 178 | } else { 179 | t.advance(1); 180 | result.kind = TokenKind.Rbrace; 181 | } 182 | return; 183 | case '"': 184 | if (t.current[1] == '"' && t.current[2] == '"') t.parseMultiText(result); 185 | else t.parseText(result); 186 | return; 187 | default: 188 | // key or number 189 | result.loc = t.loc; 190 | if (isdigit(t.current[0])) { 191 | t.parseNumber(result); 192 | return; 193 | } 194 | if (t.current[0] == 'f' && strncmp("false", t.current, 5) == 0) { 195 | t.advance(5); 196 | result.number = 0; 197 | result.kind = TokenKind.Kw_false; 198 | return; 199 | } 200 | if (t.current[0] == 't' && strncmp("true", t.current, 4) == 0) { 201 | t.advance(4); 202 | result.number = 1; 203 | result.kind = TokenKind.Kw_true; 204 | return; 205 | } 206 | if (isalpha(t.current[0])) { 207 | t.parseKey(result); 208 | return; 209 | } 210 | sprintf(t.text, "unexpected char '%c' at %s", t.current[0], t.loc.str()); 211 | result.kind = TokenKind.Error; 212 | result.text = t.text; 213 | return; 214 | } 215 | } 216 | } 217 | 218 | fn Token* Tokenizer.lookahead(Tokenizer* t) { 219 | if (!t.haveNext) { 220 | t.lex(&t.nextToken); 221 | t.haveNext = true; 222 | } 223 | return &t.nextToken; 224 | } 225 | 226 | fn void Tokenizer.advance(Tokenizer* t, u32 amount) { 227 | t.loc.column += amount; 228 | t.current += amount; 229 | } 230 | 231 | fn void Tokenizer.parseComment(Tokenizer* t) { 232 | while (1) { 233 | switch (t.current[0]) { 234 | case 0: 235 | return; 236 | case '\n': 237 | t.current++; 238 | t.loc.line++; 239 | t.loc.column = 1; 240 | return; 241 | default: 242 | t.current++; 243 | t.loc.column++; 244 | break; 245 | } 246 | } 247 | } 248 | 249 | fn void Tokenizer.parseText(Tokenizer* t, Token* result) { 250 | // TODO handle literal strings ' .. ' -> no escaping 251 | // TODO handle escape chars for normal strings " .. \" \r \n " 252 | t.advance(1); 253 | result.loc = t.loc; 254 | const char* start = t.current; 255 | while (t.current[0] && t.current[0] != '"') t.current++; 256 | 257 | u32 len = cast(t.current - start); 258 | // assert(len < MaxText); 259 | memcpy(t.text, start, len); 260 | t.text[len] = 0; 261 | result.kind = TokenKind.Text; 262 | result.text = t.text; 263 | t.loc.column += len; 264 | t.advance(1); 265 | } 266 | 267 | fn void Tokenizer.parseMultiText(Tokenizer* t, Token* result) { 268 | t.advance(3); 269 | if (t.current[0] == '\n') { 270 | t.current++; 271 | t.loc.line++; 272 | t.loc.column = 1; 273 | } 274 | result.loc = t.loc; 275 | const char* start = t.current; 276 | while (1) { 277 | if (t.current[0] == 0) { 278 | sprintf(t.text, "missing end \"\"\" %s", t.loc.str()); 279 | result.kind = TokenKind.Error; 280 | result.text = t.text; 281 | return; 282 | } 283 | if (t.current[0] == '\n') { 284 | t.loc.line++; 285 | t.loc.column = 1; 286 | } else { 287 | t.loc.column++; 288 | } 289 | if (t.current[0] == '"' && t.current[1] == '"' && t.current[2] == '"') break; 290 | t.current++; 291 | } 292 | 293 | u32 len = cast(t.current - start); 294 | // assert(len < MaxText); 295 | memcpy(t.text, start, len); 296 | t.text[len] = 0; 297 | result.kind = TokenKind.Text; 298 | result.text = t.text; 299 | t.advance(3); 300 | } 301 | 302 | fn void Tokenizer.parseNumber(Tokenizer* t, Token* result) { 303 | // TODO handle prefix +/- 304 | // handle hexadecimal/ocal/binary number 305 | // handle '_', like 1_000_000 306 | 307 | u32 number = cast(atoi(t.current)); 308 | result.kind = TokenKind.Number; 309 | result.number = number; 310 | while (t.current[0] && isdigit(t.current[0])) { 311 | t.current++; 312 | t.loc.column++; 313 | } 314 | } 315 | 316 | fn bool isKeyChar(u8 c) { 317 | if (c >= 128) return true; 318 | if (isalpha(c)) return true; 319 | if (isdigit(c)) return true; 320 | if (c == '_' || c == '-') return true; 321 | return false; 322 | } 323 | 324 | fn void Tokenizer.parseKey(Tokenizer* t, Token* result) { 325 | const char* start = t.current; 326 | while (t.current[0] && isKeyChar(cast(t.current[0]))) t.current++; 327 | 328 | u32 len = cast(t.current - start); 329 | // assert(len < MaxText); 330 | memcpy(t.text, start, len); 331 | t.text[len] = 0; 332 | result.kind = TokenKind.Word; 333 | result.text = t.text; 334 | t.loc.column += len; 335 | } 336 | 337 | -------------------------------------------------------------------------------- /xml_parser/xml_structure.c2: -------------------------------------------------------------------------------- 1 | module xml; 2 | 3 | import stdlib local; 4 | import string local; 5 | import stdio local; // TEMP 6 | 7 | const u32 MAX_NODES = 1024; 8 | const u32 MAX_ATTRS = 1024; 9 | 10 | public type Blocks struct { 11 | Node* nodes; 12 | u32 node_count; 13 | u32 max_nodes; 14 | 15 | char* node_names; 16 | u32 node_name_off; 17 | u32 node_name_size; 18 | // TODO remove these 2, only used during parse 19 | char* last_node_name; 20 | u32 last_node_name_off; 21 | 22 | Attr* attrs; 23 | u32 attr_count; 24 | u32 max_attrs; 25 | 26 | char* attr_names; 27 | u32 attr_name_off; 28 | u32 attr_name_size; 29 | 30 | char* data; // attribute values + text 31 | u32 data_off; 32 | u32 data_size; 33 | } 34 | 35 | public type Node2 struct { 36 | const Node* node; 37 | const Blocks* blocks; 38 | } 39 | 40 | public fn const char* Node2.getName(const Node2* node) { 41 | return &node.blocks.node_names[node.node.name_off]; 42 | } 43 | 44 | public fn const char* Node2.getAttr(const Node2* node, const char* name) { 45 | u32 offset = node.node.attr_off; 46 | while (offset != 0) { 47 | const Attr* attr = &node.blocks.attrs[offset]; 48 | const char* attr_name = &node.blocks.attr_names[attr.name_off]; 49 | if (strcmp(attr_name, name) == 0) { 50 | return &node.blocks.data[attr.value_off]; 51 | } 52 | offset = attr.next_attr; 53 | } 54 | return nil; 55 | } 56 | 57 | public fn NodeIter Node2.getIter(const Node2* node) { 58 | NodeIter iter; 59 | // TODO check CHILD_TEXT 60 | u32 child = node.node.children; 61 | if (child == 0) iter.current = nil; 62 | else iter.current = &node.blocks.nodes[child]; 63 | iter.blocks = node.blocks; 64 | return iter; 65 | } 66 | public type NodeIter struct { 67 | Node* current; 68 | const Blocks* blocks; 69 | } 70 | 71 | public fn bool NodeIter.done(const NodeIter* iter) { 72 | return iter.current == nil; 73 | } 74 | 75 | public fn void NodeIter.next(NodeIter* iter) { 76 | u32 next = iter.current.next_node; 77 | if (next == 0) iter.current = nil; 78 | else iter.current = &iter.blocks.nodes[next]; 79 | } 80 | 81 | public fn Node2 NodeIter.get(NodeIter* iter) { 82 | Node2 result; 83 | result.node = iter.current; 84 | result.blocks = iter.blocks; 85 | return result; 86 | } 87 | 88 | public type Node struct @(packed, opaque) { 89 | u32 name_off; // off in node_names 90 | u32 next_node; // off in nodes (index of Node) 91 | u32 attr_off; // off in attrs 92 | u32 children; // if upper bit is set, point to Data 93 | } 94 | 95 | public type Attr struct @(packed, opaque) { 96 | u32 name_off; // off in attr_names 97 | u32 next_attr; // off in attrs (index of Attr) 98 | u32 value_off; // off in data; 99 | } 100 | 101 | 102 | fn void Blocks.init(Blocks* b) { 103 | memset(b, 0, sizeof(Blocks)); 104 | b.nodes = calloc(MAX_NODES, sizeof(Node)); 105 | b.max_nodes = MAX_NODES; 106 | 107 | b.node_name_size = 4096; 108 | b.node_names = calloc(1, b.node_name_size); 109 | 110 | b.attrs = calloc(MAX_ATTRS, sizeof(Attr)); 111 | b.attr_count = 1; // 0 is used to indicate no attrs 112 | b.max_attrs = MAX_ATTRS; 113 | 114 | b.attr_name_size = 4096; 115 | b.attr_names = calloc(1, b.attr_name_size); 116 | 117 | b.data_size = 4096; 118 | b.data = calloc(1, b.data_size); 119 | } 120 | 121 | fn void Blocks.destroy(Blocks* b) { 122 | free(b.attrs); 123 | free(b.node_names); 124 | free(b.nodes); 125 | } 126 | 127 | fn u32 Blocks.addNode(Blocks* b, const char* name) { 128 | // TODO check if fits in block, otherwise re-alloc 129 | if (b.node_count == b.max_nodes) { 130 | printf("NODE LIMIT REACHED!\n"); 131 | exit(-1); 132 | } 133 | u32 off = b.node_count; 134 | Node* node = &b.nodes[off]; 135 | b.node_count++; 136 | 137 | // TODO improve matching with depth etc 138 | if (b.last_node_name && strcmp(b.last_node_name, name) == 0) { 139 | node.name_off = b.last_node_name_off; 140 | } else { 141 | // TODO check if it fits in block 142 | u32 len = cast(strlen(name)) + 1; 143 | u32 name_off = b.node_name_off; 144 | node.name_off = name_off; 145 | char* newname = &b.node_names[name_off]; 146 | memcpy(newname, name, len); 147 | b.last_node_name = newname; 148 | b.last_node_name_off = name_off; 149 | b.node_name_off += len; 150 | } 151 | return off; 152 | } 153 | 154 | fn u32 Blocks.addAttr(Blocks* b, const char* name) { 155 | // TODO check if fits in block, otherwise re-alloc 156 | if (b.attr_count == b.max_attrs) { 157 | printf("ATTR LIMIT REACHED!\n"); 158 | exit(-1); 159 | } 160 | u32 off = b.attr_count; 161 | Attr* attr = &b.attrs[off]; 162 | b.attr_count++; 163 | 164 | // TODO search existing attribute names 165 | 166 | u32 len = cast(strlen(name)) + 1; 167 | // TODO check if it fits in block 168 | u32 name_off = b.attr_name_off; 169 | attr.name_off= name_off; 170 | memcpy(&b.attr_names[name_off], name, len); 171 | b.attr_name_off += len; 172 | return off; 173 | } 174 | 175 | fn u32 Blocks.addValue(Blocks* b, const char* value) { 176 | u32 len = cast(strlen(value)) + 1; 177 | if (b.data_off + len >= b.data_size) { 178 | printf("DATA LIMIT REACHED\n"); 179 | exit(-1); 180 | } 181 | u32 off = b.data_off; 182 | memcpy(&b.data[off], value, len); 183 | b.data_off += len; 184 | return off; 185 | } 186 | 187 | fn void Blocks.dump(const Blocks* b, bool full) { 188 | if (b.node_count != 0) b.dumpNode(0, 0); 189 | 190 | if (full) { 191 | u32 size = sizeof(Blocks); 192 | size += (b.max_nodes * sizeof(Node)); 193 | size += b.node_name_size; 194 | size += (b.max_attrs * sizeof(Attr)); 195 | size += b.attr_name_size; 196 | size += b.data_size; 197 | u32 used = sizeof(Blocks); 198 | used += (b.node_count * sizeof(Node)); 199 | used += b.node_name_off; 200 | used += (b.attr_count * sizeof(Attr)); 201 | used += b.attr_name_off; 202 | used += b.data_off; 203 | printf("blocks: (%d/%d Kb)\n", (used + 1023) / 1024, (size + 1023)/ 1024); 204 | 205 | printf(" nodes %d/%d\n", b.node_count * sizeof(Node), b.max_nodes * sizeof(Node)); 206 | for (u32 i=0; i 32) { 261 | printf(" [%3d] %.32s..\n", start, &b.data[start]); 262 | } else { 263 | printf(" [%3d] %s\n", start, &b.data[start]); 264 | } 265 | i++; 266 | start = i; 267 | } else { 268 | i++; 269 | } 270 | } 271 | } 272 | } 273 | } 274 | 275 | fn void Blocks.dumpNode(const Blocks* b, u32 off, u32 depth) { 276 | const Node* node = &b.nodes[off]; 277 | for (u32 i=0; i\n"); 298 | #if 0 299 | // TODO print first 32 chars 300 | u32 value_off = node.children & ~CHILD_TEXT; 301 | printf("%s\n", &b.data[value_off]); 302 | #endif 303 | } else { 304 | b.dumpNode(node.children, depth+1); 305 | } 306 | } 307 | if (node.next_node) b.dumpNode(node.next_node, depth); 308 | } 309 | 310 | -------------------------------------------------------------------------------- /yaml_parser/yaml_tokenizer.c2: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 Bas van den Berg 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | module yaml; 17 | 18 | import string local; 19 | import stdio local; 20 | import ctype local; 21 | 22 | type Location struct { 23 | u32 line; 24 | u32 column; 25 | } 26 | 27 | fn const char* Location.str(const Location* loc) { 28 | local char[32] msg; 29 | sprintf(msg, "at line %d:%d", loc.line, loc.column); 30 | return msg; 31 | } 32 | 33 | type TokenKind enum u8 { 34 | None, 35 | Plain_Scalar, 36 | Single_Quoted_Scalar, 37 | Double_Quoted_Scalar, 38 | Colon, 39 | Dash, 40 | Indent, 41 | Dedent, 42 | Doc_Start, 43 | Doc_End, 44 | Directive, 45 | Eof, 46 | Error, 47 | } 48 | 49 | // NOTE: keep in sync with TokenKind 50 | const char*[] token_names = { 51 | "none", 52 | "scalar", 53 | "'scalar'", 54 | "\"scalar\"", 55 | ":", 56 | "-", 57 | "indent", 58 | "dedent", 59 | "---", 60 | "...", 61 | "%", 62 | "eof", 63 | "error", 64 | } 65 | 66 | fn const char* Token.str(const Token* tok) { 67 | return token_names[tok.kind]; 68 | } 69 | 70 | type Token struct { 71 | Location loc; 72 | TokenKind kind; 73 | bool same_line; 74 | union { 75 | const char* error_msg; // ERROR 76 | u32 text_idx; // SCALAR, DIRECTIVE 77 | i32 indent; // INDENT, DEDENT 78 | } 79 | } 80 | 81 | type Tokenizer struct { 82 | const char* cur; 83 | Location loc; 84 | const char* input_start; 85 | char* error_msg; 86 | i32 cur_indent; 87 | bool same_line; 88 | Data* data; 89 | 90 | Token next; 91 | } 92 | 93 | fn void Tokenizer.init(Tokenizer* t, const char* input, Data* d, char* error_msg) { 94 | memset(t, 0, sizeof(Tokenizer)); 95 | t.cur = input; 96 | t.input_start = input; 97 | t.loc.line = 1; 98 | t.loc.column = 1; 99 | t.error_msg = error_msg; 100 | t.data = d; 101 | t.next.kind = TokenKind.None; 102 | } 103 | 104 | fn void Tokenizer.lex(Tokenizer* t, Token* result) { 105 | if (t.next.kind != TokenKind.None) { 106 | memcpy(result, &t.next, sizeof(Token)); 107 | t.next.kind = TokenKind.None; 108 | return; 109 | } 110 | 111 | result.same_line = t.same_line; 112 | t.same_line = true; 113 | result.text_idx = 0; 114 | 115 | while (1) { 116 | // dont emit Dedent for empty lines 117 | if (t.loc.column == 1 && t.cur_indent && *t.cur != ' ' && *t.cur != '\r' && *t.cur != '\n') { 118 | result.loc = t.loc; 119 | result.kind = TokenKind.Dedent; 120 | result.indent = 0; 121 | t.cur_indent = 0; 122 | t.same_line = false; 123 | return; 124 | } 125 | 126 | switch (*t.cur) { 127 | case 0: 128 | result.loc = t.loc; 129 | result.kind = TokenKind.Eof; 130 | return; 131 | case '\t': 132 | sprintf(t.error_msg, "file contains TAB characters %s", t.loc.str()); 133 | t.error(result); 134 | return; 135 | case '\r': 136 | t.cur++; 137 | if (*t.cur != '\n') { 138 | sprintf(t.error_msg, "unexpected char 0x%02X %s", *t.cur, t.loc.str()); 139 | t.error(result); 140 | return; 141 | } 142 | fallthrough; 143 | case '\n': 144 | t.cur++; 145 | t.loc.line++; 146 | t.loc.column = 1; 147 | t.same_line = true; 148 | result.same_line = false; 149 | break; 150 | case ' ': 151 | if (t.loc.column == 1) { 152 | if (t.lex_indent(result)) return; 153 | break; 154 | } 155 | t.cur++; 156 | t.loc.column++; 157 | break; 158 | case '"': 159 | t.lex_quoted_string(result, '"'); 160 | return; 161 | case '#': 162 | t.lex_comment(); 163 | break; 164 | case '%': 165 | t.lex_directive(result); 166 | return; 167 | case '\'': 168 | t.lex_quoted_string(result, '\''); 169 | return; 170 | case '-': 171 | // if followed by SPACE or NEWLINE, it is a dash 172 | if (t.cur[1] == ' ' || t.cur[1] == '\r' || t.cur[1] == '\n') { 173 | t.cur++; 174 | result.loc = t.loc; 175 | result.kind = TokenKind.Dash; 176 | t.loc.column++; 177 | return; 178 | } 179 | 180 | if (t.loc.column == 1 && t.cur[1] == '-' && t.cur[2] == '-') { 181 | t.cur += 3; 182 | result.loc = t.loc; 183 | result.kind = TokenKind.Doc_Start; 184 | t.loc.column += 3; 185 | return; 186 | } 187 | 188 | t.lex_string(result); 189 | return; 190 | case '.': 191 | // can be DOCUMENT_END at start, otherwise scalar 192 | if (t.loc.column == 1 && t.cur[1] == '.' && t.cur[2] == '.') { 193 | result.loc = t.loc; 194 | result.kind = TokenKind.Doc_End; 195 | t.cur += 3; 196 | t.loc.column += 3; 197 | return; 198 | } 199 | t.lex_string(result); 200 | return; 201 | case ':': 202 | t.cur++; 203 | result.loc = t.loc; 204 | result.kind = TokenKind.Colon; 205 | t.loc.column++; 206 | return; 207 | default: 208 | if (is_string(*t.cur)) { 209 | t.lex_string(result); 210 | return; 211 | } 212 | sprintf(t.error_msg, "unhandled char 0x%02x (%c) %s", 213 | *t.cur, isprint(*t.cur) ? *t.cur : ' ', t.loc.str()); 214 | t.error(result); 215 | return; 216 | } 217 | } 218 | } 219 | 220 | fn Token* Tokenizer.lex_next(Tokenizer* t) { 221 | if (t.next.kind == TokenKind.None) t.lex(&t.next); 222 | 223 | return &t.next; 224 | } 225 | 226 | fn bool Tokenizer.lex_indent(Tokenizer* t, Token* result) { 227 | const char* start = t.cur; 228 | while (*t.cur == ' ') t.cur++; 229 | 230 | i32 indent = cast(t.cur - start); 231 | result.loc = t.loc; 232 | t.loc.column += indent; 233 | if (t.cur_indent == indent) return false; 234 | 235 | if (t.cur_indent > indent) result.kind = TokenKind.Dedent; 236 | else result.kind = TokenKind.Indent; 237 | 238 | result.indent = indent; 239 | t.cur_indent = indent; 240 | return true; 241 | 242 | } 243 | 244 | fn void Tokenizer.lex_comment(Tokenizer* t) { 245 | const char* start = t.cur; 246 | t.cur++; 247 | while (1) { 248 | switch (*t.cur) { 249 | case 0: fallthrough; 250 | case '\r': fallthrough; 251 | case '\n': 252 | t.loc.column += (t.cur - start); 253 | return; 254 | default: 255 | t.cur++; 256 | break; 257 | } 258 | } 259 | } 260 | 261 | fn void Tokenizer.lex_directive(Tokenizer* t, Token* result) { 262 | t.cur++; 263 | const char* start = t.cur; 264 | u32 count; 265 | while (1) { 266 | switch (*t.cur) { 267 | case 0: fallthrough; 268 | case '\r': fallthrough; 269 | case '\n': 270 | goto out; 271 | default: 272 | t.cur++; 273 | break; 274 | } 275 | } 276 | out: 277 | count = cast(t.cur - start); 278 | t.error_msg[count] = 0; // HUH? 279 | result.loc = t.loc; 280 | result.kind = TokenKind.Directive; 281 | result.text_idx = t.data.add_text(start, count); 282 | t.loc.column += count + 1; 283 | } 284 | 285 | fn void Tokenizer.lex_quoted_string(Tokenizer* t, Token* result, char delim) { 286 | t.cur++; 287 | const char* start = t.cur; 288 | u32 count; 289 | while (1) { 290 | switch (*t.cur) { 291 | case 0: fallthrough; 292 | case '\r': fallthrough; 293 | case '\n': 294 | t.loc.column += (t.cur - start); 295 | sprintf(t.error_msg, "unterminated string %s", t.loc.str()); 296 | t.error(result); 297 | return; 298 | default: 299 | if (*t.cur == delim) goto out; 300 | t.cur++; 301 | break; 302 | } 303 | } 304 | out: 305 | count = cast(t.cur - start); 306 | t.cur++; // skip terminating delimiter 307 | result.loc = t.loc; 308 | result.kind = (delim == '"') ? TokenKind.Double_Quoted_Scalar : TokenKind.Single_Quoted_Scalar; 309 | result.text_idx = t.data.add_text(start, count); 310 | t.loc.column += count + 2; // add quotes 311 | } 312 | 313 | fn bool is_string(char c) { 314 | if (isalpha(c) || isdigit(c) 315 | || c == '_' || c == '-' 316 | || c == '.' || c == '/' 317 | || c == '~') { 318 | return true; 319 | } 320 | return false; 321 | } 322 | 323 | fn void Tokenizer.lex_string(Tokenizer* t, Token* result) { 324 | // NOTE: multiple words with only space in between are merged 325 | const char* start = t.cur; 326 | t.cur++; 327 | while (1) { 328 | char c = *t.cur; 329 | if (is_string(c)) { 330 | t.cur++; 331 | continue; 332 | } 333 | if (c == ' ' && is_string(t.cur[1])) { 334 | t.cur += 2; 335 | continue; 336 | } 337 | break; 338 | } 339 | 340 | u32 count = cast(t.cur - start); 341 | result.loc = t.loc; 342 | result.kind = TokenKind.Plain_Scalar; 343 | result.text_idx = t.data.add_text(start, count); 344 | t.loc.column += count; 345 | } 346 | 347 | fn void Tokenizer.error(Tokenizer* t, Token* result) { 348 | result.loc = t.loc; 349 | result.kind = TokenKind.Error; 350 | result.error_msg = t.error_msg; 351 | } 352 | -------------------------------------------------------------------------------- /event/events.c2: -------------------------------------------------------------------------------- 1 | module events; 2 | 3 | import stdlib local; 4 | import unistd local; 5 | import sys_time; 6 | import stdio local; 7 | import string local; 8 | import libc_poll local; 9 | import libc_fcntl local; 10 | 11 | public fn u64 current_time() { 12 | sys_time.Timeval tv; 13 | sys_time.gettimeofday(&tv, nil); 14 | u64 now64 = cast(tv.tv_sec); 15 | now64 *= 1000000; 16 | now64 += tv.tv_usec; 17 | return now64; 18 | } 19 | 20 | const u32 MaxEvents = 64; 21 | 22 | type CmdKind enum u8 { 23 | LoopBreak = 0, 24 | Callback, 25 | TimerAdd, 26 | TimerDel, 27 | } 28 | 29 | type Cmd struct { 30 | CmdKind kind; 31 | union { 32 | struct callback { 33 | Function func; 34 | void* arg1; 35 | void* arg2; 36 | } 37 | struct timer { 38 | Timer* timer; 39 | u64 delay_us; 40 | } 41 | } 42 | } 43 | 44 | public type Base struct @(opaque) { 45 | u32 running_loop; 46 | u32 loop_break; 47 | u8 return_value; 48 | Event* event_head; 49 | Event* event_tail; 50 | Timer* timers; 51 | Pollfd[MaxEvents] fds; 52 | Event*[MaxEvents] events; 53 | Event selfpipe; 54 | i32 read_pipe; 55 | i32 write_pipe; 56 | u32 numfds; 57 | bool eventsDirty; 58 | } 59 | 60 | public fn Base* Base.create() { 61 | i32[2] pipefd; 62 | if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) != 0) { 63 | return nil; 64 | } 65 | 66 | Base* base = stdlib.calloc(1, sizeof(Base)); 67 | base.read_pipe = pipefd[0]; 68 | base.write_pipe = pipefd[1]; 69 | base.selfpipe.assign(base, base.read_pipe, READ, handlePipe, base); 70 | base.selfpipe.add(); 71 | base.updateEvents(); 72 | return base; 73 | } 74 | 75 | public fn void Base.destroy(Base* base) { 76 | stdlib.free(base); 77 | } 78 | 79 | public fn u8 Base.mainloop(Base* base) { 80 | base.return_value = 0; 81 | base.running_loop = 1; 82 | while (!base.loop_break) { 83 | base.internal_loop(); 84 | } 85 | base.running_loop = 0; 86 | return base.return_value; 87 | } 88 | 89 | public fn void Base.loopbreak(Base* base, u8 return_value) { 90 | base.return_value = return_value; 91 | base.loop_break = 1; 92 | if (base.running_loop) { 93 | Cmd cmd; 94 | memset(&cmd, 0, sizeof(Cmd)); 95 | cmd.kind = CmdKind.LoopBreak; 96 | base.writeCmd(&cmd); 97 | } 98 | } 99 | 100 | fn void Base.writeCmd(Base* base, Cmd* cmd) { 101 | i64 written = write(base.write_pipe, cmd, sizeof(Cmd)); 102 | if (written != sizeof(Cmd)) { 103 | fprintf(stderr, "Error writing to pipe\n"); 104 | exit(EXIT_FAILURE); 105 | } 106 | } 107 | 108 | fn void handlePipe(i32 fd, void* arg, u16 flags) { 109 | Base* base = cast(arg); 110 | Cmd cmd; 111 | i64 numread = read(base.read_pipe, &cmd, sizeof(Cmd)); 112 | // TODO assert numread 113 | switch (cmd.kind) { 114 | case LoopBreak: 115 | break; 116 | case Callback: 117 | cmd.callback.func(cmd.callback.arg1, cmd.callback.arg2); 118 | break; 119 | case TimerAdd: 120 | cmd.timer.timer.add(cmd.timer.delay_us); 121 | break; 122 | case TimerDel: 123 | cmd.timer.timer.del(); 124 | break; 125 | } 126 | } 127 | 128 | fn void Base.fire_timer(Base* base) { 129 | Timer* timer = base.timers; 130 | base.timers = timer.next; 131 | timer.next = nil; 132 | if (timer.flags & PERSIST) { 133 | timer.timeout += timer.interval_usec; 134 | timer.add_internal(); 135 | } else { 136 | timer.flags &= ~TIMER_PENDING; 137 | } 138 | timer.handler(timer.handler_arg); 139 | } 140 | 141 | fn void Base.internal_loop(Base* base) { 142 | i32 timeout_ms = -1; // infinity 143 | if (base.timers) { 144 | u64 now = current_time(); 145 | Timer* timer = base.timers; 146 | if (now >= timer.timeout) { 147 | base.fire_timer(); 148 | return; 149 | } 150 | u64 delay = (timer.timeout - now); 151 | // Round delay to ms 152 | delay += 500; 153 | delay /= 1000; 154 | timeout_ms = cast(delay); 155 | //timeout_ms = (delay + 500) / 1000; // causes assert in c2c 156 | } 157 | if (base.eventsDirty) { 158 | Event* ev = base.event_head; 159 | u32 i = 0; 160 | while (ev) { 161 | base.fds[i].fd = ev.fd; 162 | base.fds[i].events = 0; 163 | if (ev.flags & READ) base.fds[i].events |= POLLIN; 164 | if (ev.flags & WRITE) base.fds[i].events |= POLLOUT; 165 | base.events[i] = ev; 166 | ev = ev.next; 167 | i++; 168 | } 169 | base.numfds = i; 170 | base.eventsDirty = false; 171 | } 172 | i32 ret = poll(base.fds, base.numfds, timeout_ms); 173 | if (ret == 0) { // timeout 174 | if (base.timers) base.fire_timer(); 175 | } else if (ret < 0) { // error 176 | // interrupted (by signal, etc) 177 | } else { // events 178 | for (u32 i=0; i timeout) break; 315 | cur = cur.next; 316 | } 317 | timer.next = cur.next; 318 | cur.next = timer; 319 | } 320 | } else { 321 | // add as first 322 | base.timers = timer; 323 | } 324 | } 325 | 326 | public fn void Timer.add(Timer* timer, u64 delay_usec) 327 | { 328 | u64 now = current_time(); 329 | timer.interval_usec = delay_usec; 330 | timer.timeout = now + delay_usec; 331 | timer.flags |= TIMER_PENDING; 332 | timer.next = nil; 333 | timer.add_internal(); 334 | } 335 | 336 | public fn bool Timer.isActive(const Timer* timer) 337 | { 338 | return (timer.flags & TIMER_PENDING) != 0; 339 | } 340 | 341 | public fn void Timer.del(Timer* timer) 342 | { 343 | if (!timer.isActive()) return; 344 | Base* base = timer.base; 345 | 346 | if (timer == base.timers) { 347 | base.timers = timer.next; 348 | } else { 349 | Timer* cur = base.timers; 350 | while (cur.next) { 351 | if (cur.next == timer) { 352 | cur.next = timer.next; 353 | break; 354 | } 355 | cur = cur.next; 356 | } 357 | } 358 | timer.next = nil; 359 | timer.flags &= ~TIMER_PENDING; 360 | 361 | } 362 | 363 | public fn void Timer.add_sync(Timer* timer, u64 delay_us) 364 | { 365 | Cmd cmd; 366 | memset(&cmd, 0, sizeof(Cmd)); 367 | cmd.kind = CmdKind.TimerAdd; 368 | cmd.timer.timer = timer; 369 | cmd.timer.delay_us = delay_us; 370 | timer.base.writeCmd(&cmd); 371 | } 372 | 373 | public fn void Timer.del_sync(Timer* timer) 374 | { 375 | Cmd cmd; 376 | memset(&cmd, 0, sizeof(Cmd)); 377 | cmd.kind = CmdKind.TimerDel; 378 | cmd.timer.timer = timer; 379 | timer.base.writeCmd(&cmd); 380 | } 381 | 382 | public fn u64 sec(u64 t) { 383 | return 1000 * 1000 * t; 384 | } 385 | 386 | public fn u64 msec(u64 t) { 387 | return 1000 * t; 388 | } 389 | 390 | -------------------------------------------------------------------------------- /yaml_parser/yaml_parser.c2: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 Bas van den Berg 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | module yaml; 17 | 18 | import stdlib local; 19 | import csetjmp local; 20 | import stdarg local; 21 | import stdio local; 22 | 23 | const u32 MaxDiag = 256; 24 | 25 | public type Parser struct @(opaque) { 26 | Token token; 27 | Tokenizer tokenizer; 28 | 29 | i32 cur_indent; 30 | bool doc_started; 31 | bool in_document; 32 | StackLevel[MaxDepth] stack; 33 | u32 stack_size; // number of items on the stack 34 | 35 | Data data; 36 | 37 | JmpBufTag jmp_err; 38 | char[MaxDiag] message; 39 | } 40 | 41 | public fn Parser* Parser.create() { 42 | Parser* p = calloc(1, sizeof(Parser)); 43 | p.data.init(1024, 32, p.stack); 44 | return p; 45 | } 46 | 47 | public fn void Parser.destroy(Parser* p) { 48 | p.data.destroy(); 49 | free(p); 50 | } 51 | 52 | public fn bool Parser.parse(Parser* p, const char* input) { 53 | p.tokenizer.init(input, &p.data, p.message); 54 | 55 | p.token.kind = TokenKind.None; 56 | 57 | i32 res = setjmp(&p.jmp_err); 58 | if (res == 0) { 59 | p.consumeToken(); 60 | 61 | while (p.token.kind != TokenKind.Eof) p.parse_doc(); 62 | } else { 63 | // got error, error_msg should be set 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | 70 | public fn const char* Parser.getMessage(const Parser* p) { 71 | return p.message; 72 | } 73 | 74 | fn void Parser.error(Parser* p, const char* format @(printf_format), ...) { 75 | va_list args; 76 | va_start(args, format); 77 | char* cp = p.message; 78 | cp += vsnprintf(cp, MaxDiag-1, format, args); 79 | va_end(args); 80 | sprintf(cp, " %s", p.token.loc.str()); 81 | longjmp(&p.jmp_err, 1); 82 | } 83 | 84 | fn void Parser.consumeToken(Parser* p) { 85 | p.tokenizer.lex(&p.token); 86 | #if YamlPrintToken 87 | printf("%s %s %d\n", p.token.str(), p.token.loc.str(), p.token.same_line); 88 | #endif 89 | if (p.token.kind == TokenKind.Error) longjmp(&p.jmp_err, 1); 90 | } 91 | 92 | fn void Parser.expectAndConsume(Parser* p, TokenKind kind) { 93 | if (p.token.kind != kind) { 94 | p.error("expected '%s', got '%s'", token_names[kind], p.token.str()); 95 | } 96 | p.consumeToken(); 97 | } 98 | 99 | fn void Parser.parse_doc(Parser* p) { 100 | while (1) { 101 | switch (p.token.kind) { 102 | case Doc_Start: 103 | p.consumeToken(); 104 | if (p.doc_started) p.doc_end(); 105 | p.doc_start(); 106 | break; 107 | case Doc_End: 108 | if (!p.doc_started || !p.in_document) { 109 | p.error("END document without start"); 110 | } 111 | p.consumeToken(); 112 | p.doc_end(); 113 | return; 114 | case Directive: 115 | // ignore 116 | p.consumeToken(); 117 | break; 118 | case Eof: 119 | return; 120 | default: 121 | if (!p.doc_started) p.doc_start(); 122 | p.parse_node(); 123 | break; 124 | } 125 | } 126 | } 127 | 128 | fn void Parser.parse_node(Parser* p) { 129 | switch (p.token.kind) { 130 | case Plain_Scalar: fallthrough; 131 | case Single_Quoted_Scalar: fallthrough; 132 | case Double_Quoted_Scalar: 133 | Node* n = p.data.add_node(NodeKind.Unknown, p.token.text_idx); 134 | p.push_node(n, NodeKind.Unknown, p.cur_indent); 135 | p.consumeToken(); 136 | p.expectAndConsume(TokenKind.Colon); 137 | p.parse_value(); 138 | break; 139 | case Dash: 140 | p.consumeToken(); 141 | Node* n = p.data.add_node(NodeKind.Unknown, 0); 142 | p.push_node(n, NodeKind.Sequence, p.cur_indent + 1); 143 | p.parse_node_or_value(); 144 | break; 145 | case Indent: 146 | p.cur_indent = p.token.indent; 147 | p.consumeToken(); 148 | break; 149 | case Dedent: 150 | p.cur_indent = p.token.indent; 151 | p.consumeToken(); 152 | p.pop(); 153 | break; 154 | case Doc_Start: fallthrough; 155 | case Doc_End: 156 | break; 157 | default: 158 | //p.error("%s() unhandled token '%s'", __func__, p.token.str()); 159 | p.error("%s() unhandled token '%s'", "parse_node", p.token.str()); 160 | break; 161 | } 162 | } 163 | 164 | fn void Parser.parse_value(Parser* p) { 165 | switch (p.token.kind) { 166 | case Plain_Scalar: fallthrough; 167 | case Single_Quoted_Scalar: fallthrough; 168 | case Double_Quoted_Scalar: 169 | if (p.token.same_line) { 170 | p.add_scalar_value(p.token.text_idx); 171 | p.consumeToken(); 172 | } else { 173 | //assert(0); // valid?? 174 | } 175 | return; 176 | case Dash: 177 | p.consumeToken(); 178 | Node* n = p.data.add_node(NodeKind.Unknown, 0); 179 | p.push_node(n, NodeKind.Sequence, p.cur_indent + 1); 180 | p.parse_node_or_value(); 181 | return; 182 | case Indent: 183 | p.cur_indent = p.token.indent; 184 | p.consumeToken(); 185 | p.parse_node(); 186 | return; 187 | case Dedent: 188 | p.cur_indent = p.token.indent; 189 | p.consumeToken(); 190 | p.pop(); 191 | return; 192 | case Doc_Start: fallthrough; 193 | case Doc_End: 194 | return; 195 | case Eof: 196 | p.add_scalar_value(0); 197 | return; 198 | default: 199 | //p.error("%s() unhandled token '%s'", __func__, p.token.str()); 200 | p.error("%s() unhandled token '%s'", "parse_value", p.token.str()); 201 | break; 202 | } 203 | } 204 | 205 | fn void Parser.parse_node_or_value(Parser* p) { 206 | switch (p.token.kind) { 207 | case Plain_Scalar: fallthrough; 208 | case Single_Quoted_Scalar: fallthrough; 209 | case Double_Quoted_Scalar: 210 | Token* next = p.tokenizer.lex_next(); 211 | if (next.kind == TokenKind.Colon) { 212 | // NOTE: this doesn't work, because tokenizer doesn't know (and doesn't give DEDENT) 213 | p.cur_indent += 2; // one for dash, one for node 214 | // TEMP DIRTY HACK, how to do properly? 215 | p.tokenizer.cur_indent += 2; 216 | p.parse_node(); 217 | return; 218 | } 219 | break; 220 | default: 221 | break; 222 | } 223 | p.parse_value(); 224 | } 225 | 226 | fn void Parser.doc_start(Parser* p) { 227 | p.push_root(); 228 | p.doc_started = true; 229 | p.in_document = true; 230 | } 231 | 232 | fn void Parser.doc_end(Parser* p) { 233 | p.cur_indent = -1; 234 | if (p.stack_size == 1 && p.stack[0].node.kind == NodeKind.Unknown) { 235 | p.stack[0].node.kind = NodeKind.Map; 236 | } 237 | p.pop(); 238 | p.cur_indent = 0; 239 | p.in_document = false; 240 | } 241 | 242 | fn void Parser.add_scalar_value(Parser* p, u32 value_idx) { 243 | StackLevel* top = &p.stack[p.stack_size-1]; 244 | Node* n = top.node; 245 | if (n.kind != NodeKind.Unknown) { 246 | //p.error("%s() cannot add scalar to node", __func__); 247 | p.error("%s() cannot add scalar to node", "add_scalar_value"); 248 | } 249 | n.kind = NodeKind.Scalar; 250 | n.text_idx = value_idx; 251 | } 252 | 253 | fn void Parser.pop(Parser* p) { 254 | i32 indent = p.cur_indent; 255 | while (1) { 256 | StackLevel* top = &p.stack[p.stack_size-1]; 257 | if (top.indent <= indent) break; 258 | 259 | if (p.stack_size >= 1) { 260 | StackLevel* prev = &p.stack[p.stack_size-2]; 261 | prev.last_child = top.node; 262 | } 263 | if (top.node.kind == NodeKind.Unknown) top.node.kind = NodeKind.Scalar; 264 | 265 | top.indent = 0; 266 | top.node = nil; 267 | top.last_child = nil; 268 | p.stack_size--; 269 | } 270 | } 271 | 272 | fn void Parser.push_root(Parser* p) { 273 | Node* root = p.data.add_node(NodeKind.Unknown, 0); 274 | StackLevel* top = &p.stack[0]; 275 | if (p.stack_size) { 276 | top.node.next_idx = p.data.node2idx(root); 277 | } 278 | top.node = root; 279 | top.indent = -1; 280 | top.last_child = nil; 281 | p.stack_size = 1; 282 | } 283 | 284 | fn void Parser.push_node(Parser* p, Node* n, NodeKind parent_kind, i32 indent) { 285 | assert(p.stack_size); 286 | u32 n_idx = p.data.node2idx(n); 287 | StackLevel* top = &p.stack[p.stack_size-1]; 288 | 289 | if (indent < top.indent) { // can happen at end of sequence (1 lower) 290 | assert(indent + 1 == top.indent); 291 | p.pop(); 292 | top = &p.stack[p.stack_size-1]; 293 | } 294 | 295 | if (top.indent == indent) { // same level 296 | if (top.node) { 297 | // close old node as SCALAR with empty data 298 | if (top.node.kind == NodeKind.Unknown) top.node.kind = NodeKind.Scalar; 299 | top.node.next_idx = n_idx; 300 | } 301 | top.last_child = nil; 302 | } else { 303 | assert(p.stack_size + 1 < MaxDepth); 304 | assert(indent > top.indent); 305 | Node* parent = top.node; 306 | 307 | if (parent.kind == NodeKind.Unknown) { 308 | // just assign it 309 | if (parent_kind == NodeKind.Unknown) parent_kind = NodeKind.Map; 310 | parent.kind = parent_kind; 311 | } 312 | if (top.last_child) { 313 | top.last_child.next_idx = n_idx; 314 | } else { 315 | assert(parent.child_idx == 0); 316 | parent.child_idx = n_idx; 317 | } 318 | top.last_child = n; // set last_child on current level before adding level 319 | p.stack_size++; 320 | top = &p.stack[p.stack_size-1]; 321 | } 322 | top.indent = indent; 323 | top.node = n; 324 | 325 | // add child info to parent 326 | StackLevel* prev = &p.stack[p.stack_size-2]; 327 | Node* parent = prev.node; 328 | 329 | if (parent.kind != parent_kind && !(parent.kind == NodeKind.Map && parent_kind == NodeKind.Unknown)) { 330 | if (parent.kind == NodeKind.Sequence) { 331 | p.error("invalid scalar after sequence"); 332 | } else { 333 | p.error("invalid scalar after %s", node_names[parent.kind]); 334 | } 335 | } 336 | } 337 | 338 | -------------------------------------------------------------------------------- /json_parser/json_data.c2: -------------------------------------------------------------------------------- 1 | module json; 2 | 3 | import stdlib local; 4 | import string local; 5 | import stdio local; 6 | 7 | const u32 TEXT_INIT_SIZE = 256; 8 | const u32 NODE_INIT_SIZE = 64; 9 | 10 | type TextBuffer struct { 11 | char* data; 12 | u32 cur; 13 | u32 cap; 14 | } 15 | 16 | fn void TextBuffer.init(TextBuffer* b, u32 size) { 17 | // NOTE: entry 0 is reserved for empty strings 18 | b.cap = size; 19 | b.cur = 1; 20 | b.data = malloc(b.cap); 21 | b.data[0] = 0; 22 | } 23 | 24 | fn void TextBuffer.reset(TextBuffer* b) { 25 | b.cur = 1; 26 | b.data[0] = 0; 27 | } 28 | 29 | fn void TextBuffer.destroy(TextBuffer* b) { 30 | free(b.data); 31 | } 32 | 33 | fn void TextBuffer.resize(TextBuffer* b, u32 cap2) { 34 | b.cap = cap2; 35 | char* data2 = malloc(b.cap); 36 | memcpy(data2, b.data, b.cur); 37 | free(b.data); 38 | b.data = data2; 39 | } 40 | 41 | fn u32 TextBuffer.add(TextBuffer* b, const char* text, u32 len) { 42 | u32 left = b.cap - b.cur; 43 | if (len >= left) { 44 | u32 cap2 = b.cap; 45 | while (len >= left) { 46 | cap2 *= 2; 47 | left = cap2 - b.cur; 48 | } 49 | b.resize(cap2); 50 | } 51 | 52 | u32 idx = b.cur; 53 | memcpy(&b.data[idx], text, len); 54 | b.data[idx+len] = 0; 55 | b.cur += len+1; 56 | return idx; 57 | } 58 | 59 | fn void TextBuffer.dump(const TextBuffer* b, const char* text) { 60 | printf("%s: %d|%d\n", text, b.cur, b.cap); 61 | u32 idx = 1; 62 | while (idx < b.cur) { 63 | const char* name = &b.data[idx]; 64 | printf(" [%3d] %s\n", idx, name); 65 | idx += strlen(name) + 1; 66 | } 67 | } 68 | 69 | 70 | type NodeKind enum u8 { 71 | Object = 0, 72 | Value = 1, 73 | Array = 2, 74 | } 75 | 76 | fn const char* kind2str(NodeKind k) { 77 | switch (k) { 78 | case Object: return ""; 79 | case Value: return " "; 80 | case Array: return " "; 81 | } 82 | return nil; 83 | } 84 | 85 | 86 | type Node struct @(packed) { 87 | u32 next_kind; // 2 upper bits is kind, rest is next_idx; 88 | u32 name_idx; 89 | union { 90 | u32 child_idx; 91 | u32 value_idx; 92 | } 93 | } 94 | 95 | fn u32 Node.next(const Node* n) { 96 | return n.next_kind & 0x3FFFFFFF; 97 | } 98 | 99 | fn NodeKind Node.kind(const Node* n) { 100 | return cast(n.next_kind >> 30); 101 | } 102 | 103 | fn bool Node.has_child(const Node* n) { 104 | if (n.is_value()) return false; 105 | return n.child_idx != 0; 106 | } 107 | 108 | fn bool Node.is_value(const Node* n) { 109 | return n.kind() == NodeKind.Value; 110 | } 111 | 112 | fn bool Node.is_object(const Node* n) { 113 | return n.kind() == NodeKind.Object; 114 | } 115 | 116 | fn bool Node.is_array(const Node* n) { 117 | return n.kind() == NodeKind.Array; 118 | } 119 | 120 | type NodeBuffer struct { 121 | Node* data; 122 | u32 cur; 123 | u32 cap; 124 | } 125 | 126 | fn void NodeBuffer.init(NodeBuffer* b, u32 size) { 127 | b.cap = size; 128 | b.cur = 1; // skip nodes with idx 0 (reserved value) 129 | b.data = malloc(size * sizeof(Node)); 130 | } 131 | 132 | fn void NodeBuffer.destroy(NodeBuffer* b) { 133 | free(b.data); 134 | } 135 | 136 | fn void NodeBuffer.reset(NodeBuffer* b) { 137 | b.cur = 1; 138 | } 139 | 140 | fn u32 NodeBuffer.getSize(const NodeBuffer* b) { 141 | return b.cur * sizeof(Node); 142 | } 143 | 144 | fn void NodeBuffer.resize(NodeBuffer* b, u32 cap2) { 145 | b.cap = cap2; 146 | char* data2 = malloc(b.cap * sizeof(Node)); 147 | memcpy(data2, b.data, b.cur * sizeof(Node)); 148 | free(b.data); 149 | b.data = cast(data2); 150 | } 151 | 152 | fn u32 NodeBuffer.add(NodeBuffer* b, NodeKind kind, u32 name_idx, u32 value_idx) { 153 | if (b.cur == b.cap) b.resize(b.cap * 2); 154 | 155 | u32 idx = b.cur; 156 | Node* n = &b.data[idx]; 157 | n.next_kind = cast(kind << 30); 158 | n.name_idx = name_idx; 159 | n.value_idx = value_idx; 160 | b.cur++; 161 | return idx; 162 | } 163 | 164 | fn void NodeBuffer.dump(const NodeBuffer* b) { 165 | printf("Nodes: %d|%d\n", b.cur, b.cap); 166 | u32 idx = 1; 167 | while (idx < b.cur) { 168 | const Node* n = &b.data[idx]; 169 | NodeKind kind = n.kind(); 170 | printf(" [%3d] %s %3d [next %3d]", idx, kind2str(kind), n.name_idx, n.next()); 171 | switch (kind) { 172 | case Object: 173 | printf(" child %3d\n", n.child_idx); 174 | break; 175 | case Value: 176 | printf(" value %3d\n", n.value_idx); 177 | break; 178 | case Array: 179 | printf(" child %3d\n", n.child_idx); 180 | break; 181 | } 182 | idx++; 183 | } 184 | } 185 | 186 | 187 | public type Data struct @(opaque) { 188 | TextBuffer names; 189 | TextBuffer values; 190 | NodeBuffer nodes; 191 | } 192 | 193 | fn Data* Data.create() { 194 | Data* d = malloc(sizeof(Data)); 195 | d.names.init(TEXT_INIT_SIZE); 196 | d.values.init(TEXT_INIT_SIZE); 197 | d.nodes.init(NODE_INIT_SIZE); 198 | return d; 199 | } 200 | 201 | fn void Data.destroy(Data* d) { 202 | d.names.destroy(); 203 | d.values.destroy(); 204 | d.nodes.destroy(); 205 | free(d); 206 | } 207 | 208 | fn void Data.reset(Data* d) { 209 | d.names.reset(); 210 | d.values.reset(); 211 | d.nodes.reset(); 212 | } 213 | 214 | fn u32 Data.addName(Data* d, const char* name, u32 len) { 215 | return d.names.add(name, len); 216 | } 217 | 218 | fn u32 Data.addValue(Data* d, const char* value, u32 len) { 219 | return d.values.add(value, len); 220 | } 221 | 222 | fn u32 Data.addNode(Data* d, NodeKind kind, u32 name_idx, u32 value_idx) { 223 | return d.nodes.add(kind, name_idx, value_idx); 224 | } 225 | 226 | fn void Data.setChild(Data* d, u32 parent_idx, u32 child_idx) { 227 | Node* n = &d.nodes.data[parent_idx]; 228 | n.child_idx = child_idx; 229 | } 230 | 231 | fn void Data.setNext(Data* d, u32 prev_idx, u32 next_idx) { 232 | Node* n = &d.nodes.data[prev_idx]; 233 | n.next_kind |= next_idx; 234 | } 235 | 236 | fn u32 Data.find_child(const Data* d, u32 idx, const char* key) { 237 | if (idx == 0) return 0; 238 | const Node* n = &d.nodes.data[idx]; 239 | if (n.is_value()) return 0; 240 | // TODO 241 | return 0; 242 | } 243 | 244 | fn void Data.dump(const Data* d, bool verbose) { 245 | u32 used = d.nodes.cur * sizeof(Node) + d.names.cur + d.values.cur; 246 | u32 cap = d.nodes.cap * sizeof(Node) + d.names.cap + d.values.cap; 247 | printf("JSON (memory: %d|%d)\n", used, cap); 248 | if (verbose) { 249 | d.nodes.dump(); 250 | d.names.dump("Names"); 251 | d.values.dump("Values"); 252 | } 253 | 254 | if (d.nodes.cur == 1) return; 255 | d.dumpNode(1, 0); 256 | } 257 | 258 | fn void Data.dumpNode(const Data* d, u32 idx, u32 indent_) { 259 | while (idx != 0) { 260 | const Node* n = &d.nodes.data[idx]; 261 | switch (n.kind()) { 262 | case Object: 263 | printf("%s%s\n", indent(indent_), &d.names.data[n.name_idx]); 264 | if (n.child_idx != 0) d.dumpNode(n.child_idx, indent_+1); 265 | break; 266 | case Value: 267 | printf("%s%s = '%s'\n", indent(indent_), &d.names.data[n.name_idx], &d.values.data[n.value_idx]); 268 | break; 269 | case Array: 270 | printf("%s %s\n", indent(indent_), &d.names.data[n.name_idx]); 271 | if (n.child_idx != 0) d.dumpNode(n.child_idx, indent_+1); 272 | break; 273 | } 274 | idx = n.next(); 275 | } 276 | } 277 | 278 | 279 | public type Iter struct { 280 | const Data* data; 281 | u32 cur_idx; 282 | } 283 | 284 | public fn bool Iter.done(const Iter* i) { 285 | return i.cur_idx == 0; 286 | } 287 | 288 | public fn void Iter.next(Iter* i) { 289 | if (i.cur_idx == 0) return; 290 | 291 | const Node* n = &i.data.nodes.data[i.cur_idx]; 292 | i.cur_idx = n.next(); 293 | } 294 | 295 | public fn bool Iter.is_value(const Iter* i) { 296 | if (i.cur_idx == 0) return false; 297 | const Node* n = &i.data.nodes.data[i.cur_idx]; 298 | return n.is_value(); 299 | } 300 | 301 | public fn bool Iter.is_object(const Iter* i) { 302 | if (i.cur_idx == 0) return false; 303 | const Node* n = &i.data.nodes.data[i.cur_idx]; 304 | return n.is_object(); 305 | } 306 | 307 | public fn bool Iter.is_array(const Iter* i) { 308 | if (i.cur_idx == 0) return false; 309 | const Node* n = &i.data.nodes.data[i.cur_idx]; 310 | return n.is_array(); 311 | } 312 | 313 | public fn bool Iter.is_valid(const Iter* i) { 314 | return i.cur_idx != 0; 315 | } 316 | 317 | public fn bool Iter.has_child(const Iter* i) { 318 | if (i.cur_idx == 0) return false; 319 | const Node* n = &i.data.nodes.data[i.cur_idx]; 320 | return n.has_child(); 321 | } 322 | 323 | public fn const char* Iter.key(const Iter* i) { 324 | if (i.cur_idx == 0) return nil; 325 | const Node* n = &i.data.nodes.data[i.cur_idx]; 326 | if (!n.is_value()) return nil; 327 | return &i.data.names.data[n.name_idx]; 328 | } 329 | 330 | public fn const char* Iter.value(const Iter* i) { 331 | if (i.cur_idx == 0) return nil; 332 | const Node* n = &i.data.nodes.data[i.cur_idx]; 333 | if (!n.is_value()) return nil; 334 | return &i.data.values.data[n.value_idx]; 335 | } 336 | 337 | // for objects/arrays 338 | 339 | // Note: key == nil returns first child TODO 340 | public fn Iter Iter.get_child(const Iter* i, const char* key) { 341 | Iter i2 = { i.data, 0 } 342 | if (i.cur_idx == 0) return i2; 343 | 344 | i2.cur_idx = find_nested_child(i.data, i.cur_idx, key); 345 | return i2; 346 | } 347 | 348 | public fn Iter Iter.get_child_iter(const Iter* i) { 349 | Iter iter = { i.data, 0 } 350 | const Node* n = &i.data.nodes.data[i.cur_idx]; 351 | if (n.is_value()) return iter; 352 | iter.cur_idx = n.child_idx; 353 | return iter; 354 | } 355 | 356 | 357 | // Returns first child if key == nil, return nil if not found/not value 358 | public fn const char* Iter.child_value(const Iter* i, const char* key) { 359 | u32 idx = find_nested_child(i.data, i.cur_idx, key); 360 | if (idx == 0) return nil; 361 | 362 | const Node* n = &i.data.nodes.data[idx]; 363 | if (!n.is_value()) return nil; 364 | return &i.data.values.data[n.value_idx]; 365 | } 366 | 367 | fn u32 find_nested_child(const Data* data, u32 idx, const char* key) { 368 | if (idx == 0) return 0; 369 | 370 | const Node* n = &data.nodes.data[idx]; 371 | if (n.kind() != NodeKind.Object) return 0; 372 | if (key == nil) return 0; // TODO return first child? 373 | 374 | u32 node_idx = idx; 375 | const char* cp = key; 376 | while (*cp) { 377 | // NOTE: max name 64 378 | const char* start = cp; 379 | char[64] node_name; 380 | while (*cp) { 381 | if (*cp == '.') break; 382 | cp++; 383 | } 384 | u32 len = cast(cp - start); 385 | memcpy(node_name, start, len); 386 | node_name[len] = 0; 387 | 388 | node_idx = find_child(data, node_idx, node_name); 389 | if (node_idx == 0) return 0; 390 | if (*cp == '.') cp++; 391 | } 392 | n = &data.nodes.data[node_idx]; 393 | return node_idx; 394 | } 395 | 396 | fn u32 find_child(const Data* data, u32 cur_idx, const char* key) { 397 | const Node* n = &data.nodes.data[cur_idx]; 398 | 399 | if (n.kind() == NodeKind.Value) return 0; 400 | if (key == nil) return n.child_idx; 401 | 402 | u32 idx = n.child_idx; 403 | while (idx != 0) { 404 | const Node* child = &data.nodes.data[idx]; 405 | const char* name = &data.names.data[child.name_idx]; 406 | if (strcmp(key, name) == 0) return idx; 407 | idx = child.next(); 408 | } 409 | return 0; 410 | } 411 | 412 | // Schema stuff 413 | fn bool Iter.check_schema_priv(const Iter* i, const char** schema) { 414 | u32 cur_idx = i.cur_idx; 415 | while (**schema != 0) { 416 | if (cur_idx == 0) { 417 | if (**schema == ')') return true; 418 | return false; 419 | } 420 | const Node* n = &i.data.nodes.data[cur_idx]; 421 | switch (**schema) { 422 | case 's': 423 | if (!n.is_value()) return false; 424 | break; 425 | case 'a': 426 | if (!n.is_array()) return false; 427 | break; 428 | case 'o': 429 | if (!n.is_object()) return false; 430 | if ((*schema)[1] == '(') { 431 | *schema += 2; 432 | Iter obj = { i.data, n.child_idx } 433 | if (!obj.check_schema_priv(schema)) return false; 434 | } 435 | break; 436 | case '.': 437 | return true; 438 | default: 439 | return false; 440 | } 441 | cur_idx = n.next(); 442 | (*schema)++; 443 | } 444 | return (cur_idx == 0); 445 | } 446 | 447 | // schema: s = string, a = array, o = object, o(..) = object children, . = more coming (dont care) 448 | public fn bool Iter.check_schema(const Iter* i, const char* schema) { 449 | return i.check_schema_priv(&schema); 450 | } 451 | 452 | -------------------------------------------------------------------------------- /sudoku/sudoku.c2: -------------------------------------------------------------------------------- 1 | module sukodu; 2 | 3 | import stdio local; 4 | import color; 5 | import string local; 6 | 7 | /* 8 | 2 extra algorithms: 9 | - if 2 fields have options like: 1,2,4 and 1,2 (and 1/2 only appear there), then eliminate 4 10 | - a col/row must have all numbers, so we sometimes know that a certain number must be in that square col/row 11 | */ 12 | 13 | type Field struct { 14 | u8 value; // !0 if known 15 | u16 options; // bitbise if still valid TODO 0 is option, 1 is crossed out 16 | } 17 | 18 | type Board struct { 19 | Field[9*9] fields; 20 | bool inSetup; 21 | u32 todo; 22 | } 23 | 24 | const i8[] SquareOffsets = { 25 | 1, 2, 9, 10, 11, 18, 19, 20, // top left 26 | -1, 1, 8, 9, 10, 17, 18, 19, // top middle 27 | -2, -1, 7, 8, 9, 16, 17, 18, // top right 28 | -9, -8, -7, 1, 2, 9, 10, 11, // middle left 29 | -10, -9, -8, -1, 1, 8, 9, 10, // middle 30 | -11, -10, -9, -2, -1, 7, 8, 9, // middle right 31 | -18, -17, -16, -9, -8, -7, 1, 2, // bottom left 32 | -19, -18, -17, -10, -9, -8, -1, 1, // bottom middle 33 | -20, -19, -18, -11, -10, -9, -2, -1, // bottom right 34 | } 35 | 36 | const i8[] RowOffsets = { // from top row element in Square 37 | 27, 36, 45, 54, 63, 70, // top square row 38 | -27, -18, -9, 27, 36, 45, // middle square row 39 | -54, -45, -36, -27, -18, -9, // bottom square row 40 | } 41 | 42 | const i8[] ColOffsets = { // from left column element in Square 43 | 3, 4, 5, 6, 7, 8, // left square column 44 | -3, -2, -1, 3, 4, 5, // middle square column 45 | -6, -5, -4, -3, -2, -1, // right square column 46 | } 47 | 48 | fn void Board.set(Board* board, u8 x, u8 y, u8 value) { 49 | if (!board.inSetup) printf("set %d,%d -> %d\n", y, x, value); 50 | u32 idx = 9*y + x; 51 | board.fields[idx].value = value; 52 | board.fields[idx].options = 0; 53 | board.todo--; 54 | 55 | u32 clear = ~(1<(&buffer[offset]); 140 | for (u8 o=1; o<10; o++) { 141 | if (f.options & (1<> o) & 1) { 213 | count[o]++; 214 | pos[o] = x; 215 | } 216 | } 217 | } 218 | for (u8 o=1; o<10; o++) { 219 | if (count[o] == 1) { 220 | board.set(pos[o], row, o); 221 | } 222 | } 223 | } 224 | 225 | fn void Board.checkColumn(Board* board, u8 col) { 226 | u8[10] count = { 0 } 227 | u8[10] pos = { 0 } 228 | for (u8 y=0; y<9; y++) { 229 | u32 idx = 9*y + col; 230 | const Field* f = &board.fields[idx]; 231 | for (u32 o=1; o<10; o++) { 232 | if ((f.options >> o) & 1) { 233 | count[o]++; 234 | pos[o] = y; 235 | } 236 | } 237 | } 238 | for (u8 o=1; o<10; o++) { 239 | if (count[o] == 1) board.set(col, pos[o], o); 240 | } 241 | } 242 | 243 | fn bool Board.checkSquares(Board* board, bool extra) { 244 | u32 cleared = 0; 245 | for (u8 y=0; y<3; y++) { 246 | for (u8 x=0; x<3; x++) { 247 | // check square 248 | u8 topleft = (y*9 + x) *3; 249 | u8[10] count = { 0 } 250 | u8[10] pos = { 0 } 251 | u8[10] localRows = { 0 } 252 | u8[10] localCols = { 0 } 253 | const Field* f = &board.fields[topleft + 0]; 254 | for (u32 o=1; o<10; o++) { 255 | // TODO refactor with block below 256 | if ((f.options >> o) & 1) { 257 | count[o]++; 258 | pos[o] = topleft; 259 | // NOTE: lcol,lrow should be 0,0 260 | u8 lcol = (topleft % 9) % 3; // TODO simplify 261 | u8 lrow = (topleft / 9) % 3; // TODO simplify 262 | localRows[o] |= (1 << lrow); 263 | localCols[o] |= (1 << lcol); 264 | } 265 | } 266 | for (u8 i=0; i<8; i++) { 267 | // TODO use i+1 as index into count/pos 268 | u8 off = cast(topleft + SquareOffsets[i]); 269 | f = &board.fields[off]; 270 | for (u32 o=1; o<10; o++) { 271 | if ((f.options >> o) & 1) { 272 | count[o]++; 273 | pos[o] = off; 274 | 275 | u8 lcol = (off % 9) % 3; // TODO simplify 276 | u8 lrow = (off / 9) % 3; // TODO simplify 277 | localRows[o] |= (1 << lrow); 278 | localCols[o] |= (1 << lcol); 279 | } 280 | } 281 | } 282 | for (u8 o=1; o<10; o++) { 283 | if (count[o] == 1) { 284 | board.set(pos[o] % 9, pos[o] / 9, o); 285 | //printf(" FOUND %d count %d pos %d -> %d,%d\n", o, count[o], pos[o], pos[o] % 9, pos[o] / 9); 286 | continue; 287 | } 288 | if (extra) { 289 | // NOTE: HAS ISSUES 290 | u32 clear = ~(1<> 1); 293 | u8 leftCol = topleft + (localRows[o] >> 1) * 9; // NOTE: rshift 1 does 1-> 0, 2 -> 1, 4 -> 2 294 | // clear row outside current square 295 | for (i8 i=0; i<6; i++) { 296 | i8 idx = cast(leftCol + ColOffsets[x*6 + i]); 297 | u16 before = board.fields[idx].options; 298 | board.fields[idx].options &= clear; 299 | u16 after = board.fields[idx].options; 300 | if (before != after) cleared++; 301 | } 302 | } 303 | if (localCols[o] == 1 || localCols[o] == 2 || localCols[o] == 4) { 304 | printf(" FOUND %d,%d num %d COL %d\n", y, x, o, localCols[o] >> 1); 305 | u8 topRow = topleft + (localCols[o] >> 1); // NOTE: rshift 1 does 1 -> 0, 2 -> 1, 4 -> 2 306 | // clear column outside current square 307 | for (i8 i=0; i<6; i++) { 308 | i8 idx = cast(topRow + RowOffsets[y*6 + i]); 309 | u16 before = board.fields[idx].options; 310 | board.fields[idx].options &= clear; 311 | u16 after = board.fields[idx].options; 312 | if (before != after) cleared++; 313 | } 314 | } 315 | } 316 | } 317 | } 318 | } 319 | return (cleared != 0); 320 | } 321 | 322 | fn void Board.solve(Board* board) { 323 | printf("solving Sudoku\n"); 324 | u32 loops = 0; 325 | while (1) { 326 | u32 before = board.todo; 327 | 328 | printf("checking rows (%d todo)\n", board.todo); 329 | for (u8 i=0; i<9; i++) board.checkRow(i); 330 | 331 | printf("checking columns (%d todo)\n", board.todo); 332 | for (u8 i=0; i<9; i++) board.checkColumn(i); 333 | 334 | printf("checking fields (%d todo)\n", board.todo); 335 | board.checkFields(); 336 | 337 | printf("checking squares (%d todo)\n", board.todo); 338 | bool cleared = board.checkSquares(false); 339 | 340 | loops++; 341 | if (board.todo == 0) break; 342 | if (!cleared && board.todo == before) break; 343 | } 344 | printf("%d loops\n", loops); 345 | } 346 | 347 | type Setup struct @(packed) { 348 | u8 y; 349 | u8 x; 350 | u8 value; 351 | } 352 | 353 | type Puzzle struct { 354 | const char* name; 355 | const Setup* setup; 356 | u32 setup_count; 357 | } 358 | 359 | fn const Puzzle* find(const Puzzle* puzzles, u32 count, const char* name) { 360 | for (u32 i=0; i