Repository: orangeduck/Cello Branch: master Commit: 61ee5c3d9bca Files: 135 Total size: 593.7 KB Directory structure: gitextract_qcet_0lf/ ├── .gitattributes ├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── TODO.md ├── benchmarks/ │ ├── Dict/ │ │ ├── dict_c.c │ │ ├── dict_cello.c │ │ ├── dict_cpp.cpp │ │ ├── dict_cs.cs │ │ ├── dict_d.d │ │ ├── dict_go.go │ │ ├── dict_java.java │ │ ├── dict_javascript.js │ │ ├── dict_lua.lua │ │ ├── dict_perl.pl │ │ ├── dict_python.py │ │ └── dict_ruby.rb │ ├── GC/ │ │ ├── gc_c.c │ │ ├── gc_cello.c │ │ ├── gc_cpp.cpp │ │ ├── gc_java.java │ │ ├── gc_javascript.js │ │ ├── gc_lua.lua │ │ ├── gc_python.py │ │ └── gc_ruby.rb │ ├── List/ │ │ ├── list_c.c │ │ ├── list_cello.c │ │ ├── list_cpp.cpp │ │ ├── list_java.java │ │ ├── list_javascript.js │ │ ├── list_lua.lua │ │ ├── list_python.py │ │ └── list_ruby.rb │ ├── Map/ │ │ ├── dict.lua │ │ ├── map_c.c │ │ ├── map_cello.c │ │ ├── map_cpp.cpp │ │ ├── map_cs.cs │ │ ├── map_d.d │ │ ├── map_go.go │ │ ├── map_java.java │ │ ├── map_javascript.js │ │ ├── map_lua.lua │ │ ├── map_perl.pl │ │ ├── map_python.py │ │ ├── map_ruby.rb │ │ ├── rb.lua │ │ └── redblack.lua │ ├── Matmul/ │ │ ├── matmul_c.c │ │ ├── matmul_cello.c │ │ ├── matmul_cpp.cpp │ │ ├── matmul_cs.cs │ │ ├── matmul_d.d │ │ ├── matmul_dart.dart │ │ ├── matmul_go.go │ │ ├── matmul_java.java │ │ ├── matmul_javascript.js │ │ ├── matmul_lua.lua │ │ ├── matmul_perl.pl │ │ ├── matmul_python.py │ │ ├── matmul_r.R │ │ └── matmul_ruby.rb │ ├── Nbodies/ │ │ ├── nbodies_c.c │ │ ├── nbodies_cello.c │ │ ├── nbodies_cpp.cpp │ │ ├── nbodies_java.java │ │ ├── nbodies_javascript.js │ │ ├── nbodies_lua.lua │ │ ├── nbodies_python.py │ │ └── nbodies_ruby.rb │ ├── Sudoku/ │ │ ├── sudoku_c.c │ │ ├── sudoku_cello.c │ │ ├── sudoku_cpp.cpp │ │ ├── sudoku_cs.cs │ │ ├── sudoku_d.d │ │ ├── sudoku_dart.dart │ │ ├── sudoku_go.go │ │ ├── sudoku_java.java │ │ ├── sudoku_javascript.js │ │ ├── sudoku_lua.lua │ │ ├── sudoku_perl.pl │ │ ├── sudoku_python.py │ │ └── sudoku_ruby.rb │ ├── benchmark │ ├── benchmark.sh │ ├── ext/ │ │ ├── cleantxt.c │ │ ├── genint.c │ │ ├── kbtree.h │ │ ├── khash.h │ │ ├── kvec.h │ │ ├── regexp9.c │ │ ├── regexp9.h │ │ ├── sudoku │ │ └── sudoku.txt │ └── graphs.py ├── examples/ │ ├── cello_world.c │ ├── help.c │ ├── iteration.c │ ├── newtype.c │ ├── object.c │ ├── ranges.c │ ├── table.c │ └── threads.c ├── include/ │ └── Cello.h ├── src/ │ ├── Alloc.c │ ├── Array.c │ ├── Assign.c │ ├── Cmp.c │ ├── Concat.c │ ├── Doc.c │ ├── Exception.c │ ├── File.c │ ├── Function.c │ ├── GC.c │ ├── Get.c │ ├── Hash.c │ ├── Iter.c │ ├── Len.c │ ├── List.c │ ├── Num.c │ ├── Pointer.c │ ├── Push.c │ ├── Resize.c │ ├── Show.c │ ├── Start.c │ ├── String.c │ ├── Table.c │ ├── Thread.c │ ├── Tree.c │ ├── Tuple.c │ └── Type.c └── tests/ ├── ptest.c ├── ptest.h └── test.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ text=auto ================================================ FILE: .gitignore ================================================ # Main *.log *~ *.libs *.o *.la *.so* *.a *.lo *.dll *.exe *.gz *.class # Tests test.bin test.txt test.exe test # Benchmarking profile.txt # Dist libCello* # VIM related .clang_complete # benchmark programs benchmarks/Dict/dict_c benchmarks/Dict/dict_cello benchmarks/Dict/dict_cpp benchmarks/ext/genint benchmarks/GC/gc_c benchmarks/GC/gc_cello benchmarks/GC/gc_cpp benchmarks/List/list_c benchmarks/List/list_cello benchmarks/List/list_cpp benchmarks/Map/map_c benchmarks/Map/map_cello benchmarks/Map/map_cpp benchmarks/Matmul/matmul_c benchmarks/Matmul/matmul_cello benchmarks/Matmul/matmul_cpp benchmarks/Nbodies/nbodies_c benchmarks/Nbodies/nbodies_cello benchmarks/Nbodies/nbodies_cpp benchmarks/Sudoku/sudoku_c benchmarks/Sudoku/sudoku_cello benchmarks/Sudoku/sudoku_cpp ================================================ FILE: LICENSE.md ================================================ Licensed Under BSD Copyright (c) 2012, Daniel Holden All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. ================================================ FILE: Makefile ================================================ CC ?= gcc AR ?= ar VERSION = 2.1.0 PACKAGE = libCello-$(VERSION) BINDIR = ${PREFIX}/bin INCDIR = ${PREFIX}/include LIBDIR = ${PREFIX}/lib SRC := $(wildcard src/*.c) OBJ := $(addprefix obj/,$(notdir $(SRC:.c=.o))) TESTS := $(wildcard tests/*.c) TESTS_OBJ := $(addprefix obj/,$(notdir $(TESTS:.c=.o))) EXAMPLES := $(wildcard examples/*.c) EXAMPLES_OBJ := $(addprefix obj/,$(notdir $(EXAMPLES:.c=.o))) EXAMPLES_EXE := $(EXAMPLES:.c=) CFLAGS = -I ./include -std=gnu99 -Wall -Wno-unused -g -ggdb PLATFORM := $(shell uname) COMPILER := $(shell $(CC) -v 2>&1 ) ifeq ($(findstring CYGWIN,$(PLATFORM)),CYGWIN) PREFIX ?= /usr/local DYNAMIC = libCello.so STATIC = libCello.a LIBS = -lpthread -lm ifneq (,$(wildcard ${LIBDIR}/libdbghelp.a)) LIBS += -lDbgHelp else CFLAGS += -DCELLO_NSTRACE endif INSTALL_LIB = mkdir -p ${LIBDIR} && cp -f ${STATIC} ${LIBDIR}/$(STATIC) INSTALL_INC = mkdir -p ${INCDIR} && cp -r include/Cello.h ${INCDIR} UNINSTALL_LIB = rm -f ${LIBDIR}/$(STATIC) UNINSTALL_INC = rm -f ${INCDIR}/Cello.h else ifeq ($(findstring MINGW,$(PLATFORM)),MINGW) # MSYS2 ifeq ($(findstring MINGW32,$(MSYSTEM)),MINGW32) CC = i686-w64-mingw32-gcc PREFIX ?= /mingw32 else ifeq ($(findstring MINGW64,$(MSYSTEM)),MINGW64) CC = x86_64-w64-mingw32-gcc PREFIX ?= /mingw64 else ifeq ($(findstring MSYS,$(MSYSTEM)),MSYS) CC = gcc PREFIX ?= /usr else # MinGW64 mingw-builds prefix ?= c:/mingw64/x86_64-w64-mingw32 endif DYNAMIC = libCello.dll STATIC = libCello.a ifneq (,$(wildcard ${LIBDIR}/libdbghelp.a)) LIBS += -lDbgHelp else CFLAGS += -DCELLO_NSTRACE endif INSTALL_LIB = cp $(STATIC) $(LIBDIR)/$(STATIC); cp $(DYNAMIC) $(BINDIR)/$(DYNAMIC) INSTALL_INC = cp -r include/Cello.h $(INCDIR) UNINSTALL_LIB = rm -f ${LIBDIR}/$(STATIC); rm -f ${BINDIR}/$(DYNAMIC) UNINSTALL_INC = rm -f ${INCDIR}/Cello.h else ifeq ($(findstring FreeBSD,$(PLATFORM)),FreeBSD) PREFIX ?= /usr/local DYNAMIC = libCello.so STATIC = libCello.a LIBS = -lpthread -lm CFLAGS += -fPIC ifneq (,$(wildcard ${LIBDIR}/libexecinfo.a)) LIBS += -lexecinfo LFLAGS += -rdynamic else CFLAGS += -DCELLO_NSTRACE endif INSTALL_LIB = mkdir -p ${LIBDIR} && cp -f ${STATIC} ${LIBDIR}/$(STATIC) INSTALL_INC = mkdir -p ${INCDIR} && cp -r include/Cello.h ${INCDIR} UNINSTALL_LIB = rm -f ${LIBDIR}/$(STATIC) UNINSTALL_INC = rm -f ${INCDIR}/Cello.h else PREFIX ?= /usr/local DYNAMIC = libCello.so STATIC = libCello.a LIBS = -lpthread -lm CFLAGS += -fPIC ifneq (,$(wildcard ${LIBDIR}/libexecinfo.a)) LIBS += -lexecinfo LFLAGS += -rdynamic else CFLAGS += -DCELLO_NSTRACE endif INSTALL_LIB = mkdir -p ${LIBDIR} && cp -f ${STATIC} ${LIBDIR}/$(STATIC) INSTALL_INC = mkdir -p ${INCDIR} && cp -r include/Cello.h ${INCDIR} UNINSTALL_LIB = rm -f ${LIBDIR}/$(STATIC) UNINSTALL_INC = rm -f ${INCDIR}/Cello.h endif # Libraries all: $(DYNAMIC) $(STATIC) $(DYNAMIC): $(OBJ) $(CC) $(OBJ) -shared $(LFLAGS) $(LIBS) -o $@ $(STATIC): $(OBJ) $(AR) rcs $@ $(OBJ) obj/%.o: src/%.c include/Cello.h | obj $(CC) $< -c $(CFLAGS) -o $@ obj: mkdir -p obj # Tests check: CFLAGS += -Werror -g -ggdb check: $(TESTS_OBJ) $(STATIC) $(CC) $(TESTS_OBJ) $(STATIC) $(LIBS) $(LFLAGS) -o ./tests/test ./tests/test rm -f ./tests/test.bin ./tests/test.txt obj/%.o: tests/%.c | obj $(CC) $< -c $(CFLAGS) -o $@ # Benchmarks ifeq ($(findstring Darwin,$(PLATFORM)),Darwin) bench: CFLAGS += -DCELLO_NDEBUG -O3 bench: clean $(STATIC) cd benchmarks; ./benchmark.sh; cd ../ else bench: CFLAGS += -DCELLO_NDEBUG -pg -O3 bench: clean $(STATIC) cd benchmarks; ./benchmark; cd ../ endif # Examples examples: $(EXAMPLES_EXE) examples/%: CFLAGS += -Werror examples/%: examples/%.c $(STATIC) | obj $(CC) $< $(STATIC) $(CFLAGS) $(LIBS) $(LFLAGS) -o $@ # Dist dist: all | $(PACKAGE) cp -R examples include src tests LICENSE.md Makefile README.md $(PACKAGE) tar -czf $(PACKAGE).tar.gz $(PACKAGE) --exclude='*.exe' --exclude='*.pdb' $(PACKAGE): mkdir -p $(PACKAGE) # Clean clean: rm -f $(OBJ) $(TESTS_OBJ) $(EXAMPLES_OBJ) $(STATIC) $(DYNAMIC) tests/test # Install install: all $(INSTALL_LIB) $(INSTALL_INC) # Uninstall uninstall: $(UNINSTALL_LIB) $(UNINSTALL_INC) ================================================ FILE: README.md ================================================ Cello ===== __Cello__ is a _library_ that brings higher level programming to C. By acting as a _modern_, _powerful_ runtime system Cello makes many things easy that were previously impractical or awkward in C such as: * __Generic Data Structures__ * __Polymorphic Functions__ * __Interfaces / Type Classes__ * __Constructors / Destructors__ * __Optional Garbage Collection__ * __Exceptions__ * __Reflection__ And because Cello works seamlessly alongside standard C you get all the other benefits such as great performance, powerful tooling, and extensive libraries. Examples -------- ```c #include "Cello.h" int main(int argc, char** argv) { /* Stack objects are created using "$" */ var i0 = $(Int, 5); var i1 = $(Int, 3); var i2 = $(Int, 4); /* Heap objects are created using "new" */ var items = new(Array, Int, i0, i1, i2); /* Collections can be looped over */ foreach (item in items) { print("Object %$ is of type %$\n", item, type_of(item)); } /* Heap objects destructed via Garbage Collection */ return 0; } ``` ```c #include "Cello.h" int main(int argc, char** argv) { /* Shorthand $ can be used for basic types */ var prices = new(Table, String, Int); set(prices, $S("Apple"), $I(12)); set(prices, $S("Banana"), $I( 6)); set(prices, $S("Pear"), $I(55)); /* Tables also support iteration */ foreach (key in prices) { var val = get(prices, key); print("Price of %$ is %$\n", key, val); } return 0; } ``` Articles -------- Learning Resources: * [Installation](http://libcello.org/learn/installation) * [Cello World](http://libcello.org/learn/cello-world) * [Quickstart](http://libcello.org/learn/quickstart) * [Common Queries / Pitfalls](http://libcello.org/learn/queries-and-pitfalls) Articles about its creation and internal workings: * [Best Improvements of Cello 2.0](http://libcello.org/learn/best-improvements-of-cello-2.0) * [A Fat Pointer Library](http://libcello.org/learn/a-fat-pointer-library) * [Cello vs C++ vs ObjC](http://libcello.org/learn/cello-vs-cpp-vs-objc) * [Benchmarks](http://libcello.org/learn/benchmarks) * [Garbage Collection](http://libcello.org/learn/garbage-collection) More Examples ------------- ```c #include "Cello.h" int main(int argc, char** argv) { var items = new(Array, Int, $I( 8), $I( 5), $I(20), $I(15), $I(16), $I(98)); /* Iterate over indices using "range" */ foreach (i in range($I(len(items)))) { print("Item Range %i is %i\n", i, get(items, i)); } /* Iterate over every other item with "slice" */ foreach (item in slice(items, _, _, $I(2))) { print("Item Slice %i\n", item); } return 0; } ``` ```c #include "Cello.h" /* Define a normal C structure */ struct Point { float x, y; }; /* Make it compatible with Cello */ var Point = Cello(Point); int main(int argc, char** argv) { /* Create on Stack or Heap */ var p0 = $(Point, 0.0, 1.0); var p1 = new(Point, $(Point, 0.0, 2.0)); /* It can be shown, compared, hashed, etc... ** ** p0: <'Point' At 0x000000000022FC58> ** p1: <'Point' At 0x00000000004C7CC8> ** cmp: 1 ** hash: 2849275892l */ print("p0: %$\np1: %$\ncmp: %i\nhash: %ul\n", p0, p1, $I(cmp(p0, p1)), $I(hash(p0))); /* And collected by the GC when out of scope */ return 0; } ``` F.A.Q ----- * __Why does this exist?__ I made Cello as a fun experiment to see what C looks like hacked to its limits. As well as being a powerful library and toolkit, it should be interesting to those who want to explore what is possible in C. * __How does it work?__ I recommend reading [A Fat Pointer Library](http://libcello.org/learn/a-fat-pointer-library) to get an overview of how Cello works. You can also peek at the source code, which I'm told is fairly readable, or ask me any questions you like via e-mail. * __Can it be used in Production?__ It might be better to try Cello out on a hobby project first. Cello does aim to be _production ready_, but because it is a hack it has its fair share of oddities and pitfalls, and if you are working in a team, or to a deadline, there is much better tooling, support and community for languages such as C++. * __Is anyone using Cello?__ People have experimented with it, but there is no high profile project I know of that uses it. Cello is too big and scary a dependency for new C projects if they want to be portable and easy to maintain. * __Can I get involved?__ Yes! That would be great. If you do anything with Cello I'd love to know, you can e-mail me at `contact@theorangeduck.com`, or help with the development at the [Cello github repo](https://github.com/orangeduck/libCello). Contributions are very welcome. * __Who are you?__ Hello! I'm Daniel Holden. You many know me from a [book I wrote](http://www.buildyourownlisp.com/) or my [personal website](http://theorangeduck.com/). I also have a rarely updated [twitter account](https://twitter.com/anorangeduck). ================================================ FILE: TODO.md ================================================ Cello 2.0 ========= Cello REPL ========== * REPL - Python prototype - Parse CPP and extract defines. - Parse Syntax tree and extract type/variable declarations - Record those in permanent storage which is added to every file ================================================ FILE: benchmarks/Dict/dict_c.c ================================================ #include #include "khash.h" KHASH_MAP_INIT_STR(str, int) #define BUF_SIZE 0x10000 #define BLOCK_SIZE 0x100000 int main(int argc, char *argv[]) { char *buf, **mem = 0; int ret, max = 1, block_end = 0, curr = 0; khint_t k; khash_t(str) *h; buf = malloc(BUF_SIZE); // string buffer h = kh_init(str); mem = malloc(sizeof(void*)); mem[0] = malloc(BLOCK_SIZE); // memory buffer to avoid memory fragments curr = block_end = 0; while (!feof(stdin)) { fgets(buf, BUF_SIZE, stdin); k = kh_put(str, h, buf, &ret); if (ret) { // absent int l = strlen(buf) + 1; if (block_end + l > BLOCK_SIZE) { ++curr; block_end = 0; mem = realloc(mem, (curr + 1) * sizeof(void*)); mem[curr] = malloc(BLOCK_SIZE); } memcpy(mem[curr] + block_end, buf, l); kh_key(h, k) = mem[curr] + block_end; block_end += l; kh_val(h, k) = 1; } else { ++kh_val(h, k); if (kh_val(h, k) > max) max = kh_val(h, k); } } //printf("%u\t%d\n", kh_size(h), max); for (ret = 0; ret <= curr; ++ret) free(mem[ret]); free(mem); kh_destroy(str, h); free(buf); return 0; } ================================================ FILE: benchmarks/Dict/dict_cello.c ================================================ #include "Cello.h" #include #include #include enum { BUF_SIZE = 0x10000 }; int main(int argc, char *argv[]) { var h = new(Table, String, Int); resize(h, 1500000); int max = 1; char *buf = malloc(BUF_SIZE); while (!feof(stdin)) { fgets(buf, BUF_SIZE, stdin); var key = $S(buf); if (mem(h, key)) { struct Int* v = get(h, key); v->val++; if (max < v->val) { max = v->val; } } else { set(h, key, $I(1)); } } del(h); return 0; } ================================================ FILE: benchmarks/Dict/dict_cpp.cpp ================================================ #include #include #include #include #include using namespace std; struct eqstr { inline bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) == 0; } }; namespace std { template<> struct hash : public std::unary_function { size_t operator()(const char *s) const { size_t h = *s; if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; return h; } }; } typedef unordered_map, eqstr> strhash; #define BUF_SIZE 0x10000 #define BLOCK_SIZE 0x100000 int main(int argc, char *argv[]) { char *buf; int ret, max = 1, block_end = 0, curr = 0; char **mem; strhash *h = new strhash; buf = (char*)malloc(BUF_SIZE); // buffer size mem = (char**)malloc(sizeof(void*)); mem[0] = (char*)malloc(BLOCK_SIZE); // memory buffer to avoid memory fragments curr = block_end = 0; while (!feof(stdin)) { fgets(buf, BUF_SIZE, stdin); strhash::iterator p = h->find(buf); if (p == h->end()) { int l = strlen(buf) + 1; if (block_end + l > BLOCK_SIZE) { ++curr; block_end = 0; mem = (char**)realloc(mem, (curr + 1) * sizeof(void*)); mem[curr] = (char*)malloc(BLOCK_SIZE); } memcpy(mem[curr] + block_end, buf, l); h->insert(pair(mem[curr] + block_end, 1)); block_end += l; } else { ++p->second; if (max < p->second) max = p->second; } } //printf("%u\t%d\n", h->size(), max); for (int i = 0; i <= curr; ++i) free(mem[i]); free(mem); free(buf); delete h; return 0; } ================================================ FILE: benchmarks/Dict/dict_cs.cs ================================================ using System; using System.Collections.Generic; class dict_v1 { static void Main() { string l; Dictionary h = new Dictionary(); int max = 1; while ((l = Console.ReadLine()) != null) { int v; if (h.TryGetValue(l, out v)) { if (v+1 > max) max = v+1; h[l] = v+1; } else h[l] = 1; } Console.WriteLine(h.Count); Console.WriteLine(max); } } ================================================ FILE: benchmarks/Dict/dict_d.d ================================================ // This program segfaults at the end of the input file. I think this is a bug // of GDC. I can use std.stream or std.c.stdio instead, but those modules are // extremely slow. In the end, I decide to specify the number of lines at the // command line. Pretty nasty. import std.stdio, std.string; void main(string[] args) { int N = 5000000; char[] buf; int[char[]] h; int max = 1, n = 0; if (args.length >= 2) N = atoi(args[1]); while (readln(stdin, buf)) { int current = ++h[buf]; max = (current > max)? current : max; if (++n == N) { writef(h.length); writef("\t"); writefln(max); } } writef(h.length); writef("\t"); writefln(max); } ================================================ FILE: benchmarks/Dict/dict_go.go ================================================ // Contributed by Pat package main import ( "fmt" "bufio" "os" ) func main() { r := bufio.NewReader(os.Stdin) h := make(map[string]int, 1e6) max := 1 for { b, e := r.ReadSlice('\n') if e != nil { break } l := string(b) v := h[l] + 1 h[l] = v if v > max { max = v } } fmt.Printf("%d\t%d\n", len(h), max) } ================================================ FILE: benchmarks/Dict/dict_java.java ================================================ import java.util.HashMap; import java.io.*; class dict_java { public static void main(String[] args) { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); HashMap h = new HashMap(); String l; int max = 0; try { while ((l = stdin.readLine()) != null) { int x = 1; if (h.containsKey(l)) { x = h.get(l) + 1; h.put(l, x); if (x > max) max = x; } else h.put(l, 1); } } catch (IOException e) { } //System.out.println(h.size()+"\t"+max); } } ================================================ FILE: benchmarks/Dict/dict_javascript.js ================================================ var h = {}, n = 0, max = 0; function processLine (l) { if (h[l]) { ++h[l]; if (max < h[l]) max = h[l]; } else h[l] = 1, ++n; } var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); rl.on('line', function(line){ processLine(line); }) //print(n, max) ================================================ FILE: benchmarks/Dict/dict_lua.lua ================================================ local h, max, n = {}, 0, 0 for l in io.lines() do if (h[l]) then h[l] = h[l] + 1 else n, h[l] = n + 1, 1 end max = max > h[l] and max or h[l] end -- print(n, max) ================================================ FILE: benchmarks/Dict/dict_perl.pl ================================================ my (%h, $max); while (<>) { next if (++$h{$_} > $max); $max = $h{$_}; } print scalar(keys(%h)), "\t$max\n"; ================================================ FILE: benchmarks/Dict/dict_python.py ================================================ import sys def count_duplicates(lines): h, m = {}, 0 for l in lines: if (l in h): h[l] += 1 else: h[l] = 1 if (m < h[l]): m = h[l] return h, m h, m = count_duplicates(sys.stdin) # print(len(h), m) ================================================ FILE: benchmarks/Dict/dict_ruby.rb ================================================ h, max = {}, 0 STDIN.each do |l| if h[l] == nil h[l] = 1 else h[l] = h[l] + 1 end max = h[l] if max < h[l] end # puts h.length, max ================================================ FILE: benchmarks/GC/gc_c.c ================================================ #include static void create_objects(int depth) { void *i00=malloc(16), *i01=malloc(16), *i02=malloc(16), *i03=malloc(16), *i04=malloc(16), *i05=malloc(16), *i06=malloc(16), *i07=malloc(16), *i08=malloc(16), *i09=malloc(16), *i10=malloc(16), *i11=malloc(16), *i12=malloc(16), *i13=malloc(16), *i14=malloc(16), *i15=malloc(16), *i16=malloc(16), *i17=malloc(16), *i18=malloc(16), *i19=malloc(16), *i20=malloc(16), *i21=malloc(16), *i22=malloc(16), *i23=malloc(16), *i24=malloc(16), *i25=malloc(16), *i26=malloc(16), *i27=malloc(16), *i28=malloc(16), *i29=malloc(16), *i30=malloc(16), *i31=malloc(16), *i32=malloc(16), *i33=malloc(16), *i34=malloc(16); if (depth == 2) { return; } for (size_t i = 0; i < 10; i++) { create_objects(depth+1); } volatile int noinline = 1; if (noinline) { free(i00); free(i01); free(i02); free(i03); free(i04); free(i05); free(i06); free(i07); free(i08); free(i09); free(i10); free(i11); free(i12); free(i13); free(i14); free(i15); free(i16); free(i17); free(i18); free(i19); free(i20); free(i21); free(i22); free(i23); free(i24); free(i25); free(i26); free(i27); free(i28); free(i29); free(i30); free(i31); free(i32); free(i34); } } int main(int argc, char** argv) { for (size_t i = 0; i < 100; i++) { create_objects(0); } } ================================================ FILE: benchmarks/GC/gc_cello.c ================================================ #include "Cello.h" static void create_objects(int depth) { var i00=new(Int), i01=new(Int), i02=new(Int), i03=new(Int), i04=new(Int), i05=new(Int), i06=new(Int), i07=new(Int), i08=new(Int), i09=new(Int), i10=new(Int), i11=new(Int), i12=new(Int), i13=new(Int), i14=new(Int), i15=new(Int), i16=new(Int), i17=new(Int), i18=new(Int), i19=new(Int), i20=new(Int), i21=new(Int), i22=new(Int), i23=new(Int), i24=new(Int), i25=new(Int), i26=new(Int), i27=new(Int), i28=new(Int), i29=new(Int), i30=new(Int), i31=new(Int), i32=new(Int), i33=new(Int), i34=new(Int); volatile int noinline = 0; if (noinline) { show(i00); show(i01); show(i02); show(i03); show(i04); show(i05); show(i06); show(i07); show(i08); show(i09); show(i10); show(i11); show(i12); show(i13); show(i14); show(i15); show(i16); show(i17); show(i18); show(i19); show(i20); show(i21); show(i22); show(i23); show(i24); show(i25); show(i26); show(i27); show(i28); show(i29); show(i30); show(i31); show(i32); show(i34); } if (depth == 2) { return; } for (size_t i = 0; i < 10; i++) { create_objects(depth+1); } } int main(int argc, char** argv) { for (size_t i = 0; i < 100; i++) { create_objects(0); } return 0; } ================================================ FILE: benchmarks/GC/gc_cpp.cpp ================================================ #include class Int { int64_t i; public: Int() : i(0) {} }; static void create_objects(int depth) { Int *i00=new Int(), *i01=new Int(), *i02=new Int(), *i03=new Int(), *i04=new Int(), *i05=new Int(), *i06=new Int(), *i07=new Int(), *i08=new Int(), *i09=new Int(), *i10=new Int(), *i11=new Int(), *i12=new Int(), *i13=new Int(), *i14=new Int(), *i15=new Int(), *i16=new Int(), *i17=new Int(), *i18=new Int(), *i19=new Int(), *i20=new Int(), *i21=new Int(), *i22=new Int(), *i23=new Int(), *i24=new Int(), *i25=new Int(), *i26=new Int(), *i27=new Int(), *i28=new Int(), *i29=new Int(), *i30=new Int(), *i31=new Int(), *i32=new Int(), *i33=new Int(), *i34=new Int(); if (depth == 2) { return; } for (size_t i = 0; i < 10; i++) { create_objects(depth+1); } volatile int noinline = 1; if (noinline) { delete(i00); delete(i01); delete(i02); delete(i03); delete(i04); delete(i05); delete(i06); delete(i07); delete(i08); delete(i09); delete(i10); delete(i11); delete(i12); delete(i13); delete(i14); delete(i15); delete(i16); delete(i17); delete(i18); delete(i19); delete(i20); delete(i21); delete(i22); delete(i23); delete(i24); delete(i25); delete(i26); delete(i27); delete(i28); delete(i29); delete(i30); delete(i31); delete(i32); delete(i34); } } int main(int argc, char** argv) { for (size_t i = 0; i < 100; i++) { create_objects(0); } } ================================================ FILE: benchmarks/GC/gc_java.java ================================================ public final class gc_java { final public static class Int { private int value; Int(int x) { value = x; } } public static void create_objects(int depth) { Int i00=new Int(0); Int i01=new Int(0); Int i02=new Int(0); Int i03=new Int(0); Int i04=new Int(0); Int i05=new Int(0); Int i06=new Int(0); Int i07=new Int(0); Int i08=new Int(0); Int i09=new Int(0); Int i10=new Int(0); Int i11=new Int(0); Int i12=new Int(0); Int i13=new Int(0); Int i14=new Int(0); Int i15=new Int(0); Int i16=new Int(0); Int i17=new Int(0); Int i18=new Int(0); Int i19=new Int(0); Int i20=new Int(0); Int i21=new Int(0); Int i22=new Int(0); Int i23=new Int(0); Int i24=new Int(0); Int i25=new Int(0); Int i26=new Int(0); Int i27=new Int(0); Int i28=new Int(0); Int i29=new Int(0); Int i30=new Int(0); Int i31=new Int(0); Int i32=new Int(0); Int i33=new Int(0); Int i34=new Int(0); if (depth == 2) { return; } for (int i = 0; i < 10; i++) { create_objects(depth+1); } } public static void main(String[] args) { for (int i=0; i<10000; ++i) { create_objects(0); } } } ================================================ FILE: benchmarks/GC/gc_javascript.js ================================================ function Int() { return {val:0} } function create_objects(depth) { var i00=Int(); var i01=Int(); var i02=Int(); var i03=Int(); var i04=Int(); var i05=Int(); var i06=Int(); var i07=Int(); var i08=Int(); var i09=Int(); var i10=Int(); var i11=Int(); var i12=Int(); var i13=Int(); var i14=Int(); var i15=Int(); var i16=Int(); var i17=Int(); var i18=Int(); var i19=Int(); var i20=Int(); var i21=Int(); var i22=Int(); var i23=Int(); var i24=Int(); var i25=Int(); var i26=Int(); var i27=Int(); var i28=Int(); var i29=Int(); var i30=Int(); var i31=Int(); var i32=Int(); var i33=Int(); var i34=Int(); if (depth == 2) { return; } for (var i = 0; i < 10; i++) { create_objects(depth+1); } } for (var i = 0; i < 10000; i++) { create_objects(0); } ================================================ FILE: benchmarks/GC/gc_lua.lua ================================================ function Int () return {val=0} end function create_objects(depth) local i00=Int(); local i01=Int(); local i02=Int(); local i03=Int(); local i04=Int(); local i05=Int(); local i06=Int(); local i07=Int(); local i08=Int(); local i09=Int(); local i10=Int(); local i11=Int(); local i12=Int(); local i13=Int(); local i14=Int(); local i15=Int(); local i16=Int(); local i17=Int(); local i18=Int(); local i19=Int(); local i20=Int(); local i21=Int(); local i22=Int(); local i23=Int(); local i24=Int(); local i25=Int(); local i26=Int(); local i27=Int(); local i28=Int(); local i29=Int(); local i30=Int(); local i31=Int(); local i32=Int(); local i33=Int(); local i34=Int(); if (depth == 2) then return; end for i=0,10 do create_objects(depth+1) end end for i=0, 10000 do create_objects(0) end ================================================ FILE: benchmarks/GC/gc_python.py ================================================ class Int: pass def create_objects(depth): i00=Int(); i01=Int(); i02=Int(); i03=Int(); i04=Int(); i05=Int(); i06=Int(); i07=Int(); i08=Int(); i09=Int(); i10=Int(); i11=Int(); i12=Int(); i13=Int(); i14=Int(); i15=Int(); i16=Int(); i17=Int(); i18=Int(); i19=Int(); i20=Int(); i21=Int(); i22=Int(); i23=Int(); i24=Int(); i25=Int(); i26=Int(); i27=Int(); i28=Int(); i29=Int(); i30=Int(); i31=Int(); i32=Int(); i33=Int(); i34=Int(); if depth == 2: return for i in range(10): create_objects(depth+1) for i in range(10000): create_objects(0) ================================================ FILE: benchmarks/GC/gc_ruby.rb ================================================ class Int end def create_objects(depth) i00=Int.new; i01=Int.new; i02=Int.new; i03=Int.new; i04=Int.new; i05=Int.new; i06=Int.new; i07=Int.new; i08=Int.new; i09=Int.new; i10=Int.new; i11=Int.new; i12=Int.new; i13=Int.new; i14=Int.new; i15=Int.new; i16=Int.new; i17=Int.new; i18=Int.new; i19=Int.new; i20=Int.new; i21=Int.new; i22=Int.new; i23=Int.new; i24=Int.new; i25=Int.new; i26=Int.new; i27=Int.new; i28=Int.new; i29=Int.new; i30=Int.new; i31=Int.new; i32=Int.new; i33=Int.new; i34=Int.new; if depth == 2 then return end for i in 0..10 create_objects(depth+1) end end for i in 0..10000 create_objects(0) end ================================================ FILE: benchmarks/List/list_c.c ================================================ #include "kvec.h" #include #include int main(int argc, char** argv) { kvec_t(int) x; kv_init(x); int n = 10000; for (int i = 0; i < n; i++) { kv_push(int, x, rand()); } for (int i = 0; i < n; i++) { kv_push(int, x, 0); int index = rand() % n; memmove( &x.a[index+1], &x.a[index+0], sizeof(int) * ((x.n-1) - index)); x.a[index] = rand(); } for (int i = 0; i < n; i++) { int index = rand() % n; memmove( &x.a[index+0], &x.a[index+1], sizeof(int) * ((x.n-1) - index)); kv_pop(x); } kv_destroy(x); return 0; } ================================================ FILE: benchmarks/List/list_cello.c ================================================ #include "Cello.h" int main(int argc, char** argv) { var x = new(Array, Int); int n = 10000; for (int i = 0; i < n; i++) { push(x, $I(rand())); } for (int i = 0; i < n; i++) { push_at(x, $I(rand()), $I(rand() % n)); } for (int i = 0; i < n; i++) { pop_at(x, $I(rand() % n)); } del(x); return 0; } ================================================ FILE: benchmarks/List/list_cpp.cpp ================================================ #include #include int main(int argc, char** argv) { std::vector x = std::vector(); int n = 10000; for (int i = 0; i < n; i++) { x.push_back(rand()); } for (int i = 0; i < n; i++) { x.insert(x.begin()+(rand() % n), rand()); } for (int i = 0; i < n; i++) { x.erase(x.begin()+(rand() % n)); } return 0; } ================================================ FILE: benchmarks/List/list_java.java ================================================ import java.util.ArrayList; import java.util.Random; import java.io.*; class list_java { public static void main(String[] args) { ArrayList x = new ArrayList(); Random rand = new Random(); int n = 10000; for (int i = 0; i < n; i++) { x.add(rand.nextInt()); } for (int i = 0; i < n; i++) { x.add(rand.nextInt(n), rand.nextInt()); } for (int i = 0; i < n; i++) { x.remove(rand.nextInt(n)); } } } ================================================ FILE: benchmarks/List/list_javascript.js ================================================ var l = [] var n = 10000 for (var i = 0; i < n; i++) { l.push(Math.floor((Math.random() * 32767) + 0)) } for (var i = 0; i < n; i++) { l.splice( Math.floor((Math.random() * n) + 0), 0, Math.floor((Math.random() * 32767) + 0)) } for (var i = 0; i < n; i++) { l.pop(Math.floor((Math.random() * n) + 0)) } ================================================ FILE: benchmarks/List/list_lua.lua ================================================ require "math" l = {} n = 10000 for i=1,n do l[i] = math.random(0, 32767) end for i=1,n do table.insert(l, math.random(1, n), math.random(0, 32767)) end for i=1,n do table.remove(l, math.random(1, n)) end ================================================ FILE: benchmarks/List/list_python.py ================================================ import random l = [] n = 10000 random.seed(123) for i in xrange(n): l.append(random.randint(0, 32767)) for i in xrange(n): l.insert( random.randint(0, n), random.randint(0, 32767)) for i in xrange(n): l.pop(random.randint(0, n)) del l ================================================ FILE: benchmarks/List/list_ruby.rb ================================================ l = [] n = 10000 n.times { l.push(rand()) } n.times { l.insert(rand(n), rand()) } n.times { l.delete_at(rand(n)) } ================================================ FILE: benchmarks/Map/dict.lua ================================================ local prv = {} local pub = {} dict = pub function prv.rot_less(node) local R = node.greater local M = R.less if R.weight == 0 then node.weight, R.weight = 1, -1 else node.weight, R.weight = 0, 0 end if M then M.parent = node end node.greater, node.parent, R.less, R.parent = M, R, node, node.parent return R end function prv.rot_greater(node) local L = node.less local M = L.greater if L.weight == 0 then node.weight, L.weight = -1, 1 else node.weight, L.weight = 0, 0 end if M then M.parent = node end node.less, node.parent, L.greater, L.parent = M, L, node, node.parent return L end function prv.rot2_less(node) local R = node.greater local M = R.less local ML, MR = M.less, M.greater if M.weight == 0 then node.weight, R.weight = 0, 0 elseif M.weight == -1 then node.weight, R.weight, M.weight = 0, 1, 0 else node.weight, R.weight, M.weight = -1, 0, 0 end if ML then ML.parent = node end if MR then MR.parent = R end node.greater, node.parent, M.less, M.greater, M.parent, R.less, R.parent = ML, M, node, R, node.parent, MR, M return M end function prv.rot2_greater(node) local L = node.less local M = L.greater local ML, MR = M.less, M.greater if M.weight == 0 then node.weight, L.weight = 0, 0 elseif M.weight == -1 then node.weight, L.weight, M.weight = 1, 0, 0 else node.weight, L.weight, M.weight = 0, -1, 0 end if ML then ML.parent = L end if MR then MR.parent = node end node.less, node.parent, M.greater, M.less, M.parent, L.greater, L.parent = MR, M, node, L, node.parent, ML, M return M end function prv.rebalance(dict, node) local p, n, short = node.parent if node.weight == -2 then local w = node.less.weight if w == 1 then short = 1 n = %prv.rot2_greater(node) else short = w == -1 n = %prv.rot_greater(node) end elseif node.weight == 2 then local w = node.greater.weight if w == -1 then short = 1 n = %prv.rot2_less(node) else short = w == 1 n = %prv.rot_less(node) end elseif node.weight ~= 0 or not p then return else return p.less == node and 1 or -1 end if not p then dict.root = n elseif node == p.less then p.less = n return short and 1 else p.greater = n return short and -1 end end local node_tag = newtag() function prv.node(record, parent) local n = { record = record, weight = 0, parent = parent, } settag(n, %node_tag) return n end local dict_tag = newtag() local dict_meta = {} settagmethod(dict_tag, "index", function(_, index) return %dict_meta[index] end) function dict_meta:insert(record) local n, node = self.root, %prv.node(record) if not n then self.root = node else local ro = self.record_order local p, t repeat if n.weight ~= 0 then t = n end p = n if ro(record, n.record) < 0 then n = n.less if not n then node.parent = p n = node p.less = n break end else n = n.greater if not n then node.parent = p n = node p.greater = n break end end until nil while p ~= t do p.weight = n == p.less and -1 or 1 p, n = p.parent, p end if t then if n == t.less then t.weight = t.weight - 1 else t.weight = t.weight + 1 end %prv.rebalance(self, t) end end self.n = self.n + 1 return node end function dict_meta:erase(node) assert(tag(node) == %node_tag, "erase takes an iterator!") local p, n, swap = node.parent if node.less then if node.greater then -- both children p, n = node, node.greater while n.less do p, n = n, n.less end swap, node, n = node, n, n.greater else -- only less child n = node.less end elseif node.greater then -- only greater child n = node.greater else -- no children -- n = nil end if n then n.parent = p end self.n = self.n - 1 if not p then self.root = n return end if p.greater == node then p.greater = n p.weight = p.weight - 1 else p.less = n p.weight = p.weight + 1 end if swap then local sl, sg, sp = swap.less, swap.greater, swap.parent node.weight, node.parent, node.less, node.greater = swap.weight, sp, sl, sg if sl then sl.parent = node end if sg then sg.parent = node end if not sp then self.root = node elseif sp.less == swap then sp.less = node else sp.greater = node end if p == swap then p = node end end local w p, w = p.parent, %prv.rebalance(self, p) while w do p.weight = p.weight + w p, w = p.parent, %prv.rebalance(self, p) end end function dict_meta:find(key) local n, t = self.root local ko = self.key_order while n do local ord = ko(key, n.record) if ord > 0 then n = n.greater elseif ord < 0 then n = n.less else t, n = n, n.less end end if t then return t.record, t end end function dict_meta:lwb(key) local n, t = self.root local ko = self.key_order while n do if ko(key, n.record) > 0 then n = n.greater else t, n = n, n.less end end if t then return t.record, t end end function dict_meta:upb(key) local n, t = self.root local ko = self.key_order while n do if ko(key, n.record) < 0 then t, n = n, n.less else n = n.greater end end if t then return t.record, t end end function dict_meta:next(node) local n if node then assert(tag(node) == %node_tag, "next takes an iterator") n = node.greater elseif self.root then n = self.root else return end if n then repeat node, n = n, n.less until not n return node.record, node end repeat n, node = node, node.parent until not node or node.less == n if node then return node.record, node end end function dict_meta:prev(node) local n if node then assert(tag(node) == %node_tag, "prev takes an iterator") n = node.less elseif self.root then n = self.root else return end if n then repeat node, n = n, n.greater until not n return node.record, node end repeat n, node = node, node.parent until not node or node.greater == n if node then return node.record, node end end function dict_meta:first() return self:next() end function dict_meta:last() return self:prev() end function pub.create(record_order, key_order) local d = { record_order = record_order or %pub.cmp, key_order = key_order or record_order or %pub.cmp, n = 0, } settag(d, %dict_tag) return d end function pub.record(node) assert(tag(node) == %node_tag, "record takes an iterator") return node.record end function pub.cmp(a, b) return a < b and -1 or (b < a and 1 or 0) end ================================================ FILE: benchmarks/Map/map_c.c ================================================ #include #include "kbtree.h" typedef struct { char *key; int count; } elem_t; #define elem_cmp(a, b) (strcmp((a).key, (b).key)) KBTREE_INIT(str, elem_t, elem_cmp) #define BUF_SIZE 0x10000 #define BLOCK_SIZE 0x100000 int main(int argc, char *argv[]) { char *buf, **mem = 0; int ret, max = 1, block_end = 0, curr = 0; kbtree_t(str) *h; h = kb_init(str, KB_DEFAULT_SIZE); buf = malloc(BUF_SIZE); // string buffer mem = malloc(sizeof(void*)); mem[0] = malloc(BLOCK_SIZE); // memory buffer to avoid memory fragments curr = block_end = 0; while (!feof(stdin)) { fgets(buf, BUF_SIZE, stdin); elem_t t = { buf, 1 }; elem_t *p = kb_getp(str, h, &t); if (!p) { int l = strlen(buf) + 1; if (block_end + l > BLOCK_SIZE) { ++curr; block_end = 0; mem = realloc(mem, (curr + 1) * sizeof(void*)); mem[curr] = malloc(BLOCK_SIZE); } memcpy(mem[curr] + block_end, buf, l); t.key = mem[curr] + block_end; t.count = 1; kb_putp(str, h, &t); block_end += l; } else { p->count++; if (p->count > max) max = p->count; } } //printf("%u\t%d\n", kh_size(h), max); for (ret = 0; ret <= curr; ++ret) free(mem[ret]); free(mem); kb_destroy(str, h); free(buf); return 0; } ================================================ FILE: benchmarks/Map/map_cello.c ================================================ #include "Cello.h" #include #include #include enum { BUF_SIZE = 0x10000 }; int main(int argc, char *argv[]) { var m = new(Tree, String, Int); int max = 1; char *buf = malloc(BUF_SIZE); while (!feof(stdin)) { fgets(buf, BUF_SIZE, stdin); var key = $S(buf); if (mem(m, key)) { struct Int* v = get(m, key); v->val++; if (max < v->val) { max = v->val; } } else { set(m, key, $I(1)); } } del(m); return 0; } ================================================ FILE: benchmarks/Map/map_cpp.cpp ================================================ #include #include #include #include #include using namespace std; struct eqstr { inline bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) == 0; } }; typedef map strmap; #define BUF_SIZE 0x10000 #define BLOCK_SIZE 0x100000 int main(int argc, char *argv[]) { char *buf; int ret, max = 1, block_end = 0, curr = 0; char **mem; strmap *m = new strmap; buf = (char*)malloc(BUF_SIZE); // buffer size mem = (char**)malloc(sizeof(void*)); mem[0] = (char*)malloc(BLOCK_SIZE); // memory buffer to avoid memory fragments curr = block_end = 0; while (!feof(stdin)) { fgets(buf, BUF_SIZE, stdin); strmap::iterator p = m->find(buf); if (p == m->end()) { int l = strlen(buf) + 1; if (block_end + l > BLOCK_SIZE) { ++curr; block_end = 0; mem = (char**)realloc(mem, (curr + 1) * sizeof(void*)); mem[curr] = (char*)malloc(BLOCK_SIZE); } memcpy(mem[curr] + block_end, buf, l); m->insert(pair(mem[curr] + block_end, 1)); block_end += l; } else { ++p->second; if (max < p->second) max = p->second; } } //printf("%u\t%d\n", h->size(), max); for (int i = 0; i <= curr; ++i) free(mem[i]); free(mem); free(buf); delete m; return 0; } ================================================ FILE: benchmarks/Map/map_cs.cs ================================================ using System; using System.Collections.Generic; class dict_v1 { static void Main() { string l; SortedSet m = new SortedSet(); int max = 1; while ((l = Console.ReadLine()) != null) { int v; if (m.TryGetValue(l, out v)) { if (v+1 > max) max = v+1; m[l] = v+1; } else m[l] = 1; } Console.WriteLine(m.Count); Console.WriteLine(max); } } ================================================ FILE: benchmarks/Map/map_d.d ================================================ // This program segfaults at the end of the input file. I think this is a bug // of GDC. I can use std.stream or std.c.stdio instead, but those modules are // extremely slow. In the end, I decide to specify the number of lines at the // command line. Pretty nasty. import std.stdio, std.string; void main(string[] args) { int N = 5000000; char[] buf; int[char[]] h; int max = 1, n = 0; if (args.length >= 2) N = atoi(args[1]); while (readln(stdin, buf)) { int current = ++h[buf]; max = (current > max)? current : max; if (++n == N) { writef(h.length); writef("\t"); writefln(max); } } writef(h.length); writef("\t"); writefln(max); } ================================================ FILE: benchmarks/Map/map_go.go ================================================ // Contributed by Pat package main import ( "fmt" "bufio" "os" ) func main() { r := bufio.NewReader(os.Stdin) h := make(map[string]int, 1e6) max := 1 for { b, e := r.ReadSlice('\n') if e != nil { break } l := string(b) v := h[l] + 1 h[l] = v if v > max { max = v } } fmt.Printf("%d\t%d\n", len(h), max) } ================================================ FILE: benchmarks/Map/map_java.java ================================================ import java.util.TreeMap; import java.io.*; class map_java { public static void main(String[] args) { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); TreeMap h = new TreeMap(); String l; int max = 0; try { while ((l = stdin.readLine()) != null) { int x = 1; if (h.containsKey(l)) { x = h.get(l) + 1; h.put(l, x); if (x > max) max = x; } else h.put(l, 1); } } catch (IOException e) { } //System.out.println(h.size()+"\t"+max); } } ================================================ FILE: benchmarks/Map/map_javascript.js ================================================ var RBTree = require('bintrees').RBTree; var strcmp = function (a, b) { return a.localeCompare(b); }; var h = new RBTree(strcmp), n = 0, max = 0; function processLine (l) { var val = h.find(l) if (val) { h.insert(val+1); if (max < val+1) max = val+1; } else h.insert(l, 1), ++n; } var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); rl.on('line', function(line){ processLine(line); }) //print(n, max) ================================================ FILE: benchmarks/Map/map_lua.lua ================================================ require 'Map/redblack' local h = redblack.newTree() local max = 0 for l in io.lines() do local c = redblack.find(h, l) if (c) then -- This Red black doesn't support value types so whatever redblack.insert(h, l) -- max = max > c and max or c else redblack.insert(h, l) end end -- print(n, max) ================================================ FILE: benchmarks/Map/map_perl.pl ================================================ my (%h, $max); while (<>) { next if (++$h{$_} > $max); $max = $h{$_}; } print scalar(keys(%h)), "\t$max\n"; ================================================ FILE: benchmarks/Map/map_python.py ================================================ import sys from bintrees import RBTree def count_duplicates(lines): h, m = RBTree(), 0 for l in lines: if (l in h): h[l] += 1 else: h[l] = 1 if (m < h[l]): m = h[l] return h, m h, m = count_duplicates(sys.stdin) # print(len(h), m) ================================================ FILE: benchmarks/Map/map_ruby.rb ================================================ require 'rbtree' h, max = RBTree[], 0 STDIN.each do |l| if h[l] == nil h[l] = 1 else h[l] = h[l] + 1 end max = h[l] if max < h[l] end # puts h.length, max ================================================ FILE: benchmarks/Map/rb.lua ================================================ --------------------------------------- -- -- rb.lua -- 20110212 -- -- from cormen 2nd edition -- --------------------------------------- --[[ node = { k; -- key red; -- true or false l; -- left subtree r; -- right subtree p; -- parent data; -- for the client } tree = { root; NIL; -- sentinel node: parent of root / leaves } --]] -- used for the simetric operations -- d is either 'left'/'right' or true/false (or error !) -- returns 'l','r' or 'r','l' local function direction(d) if d == 'left' or d == true then return 'l','r','left','right' elseif d == 'right' or d == false then return 'r','l','right','left' else error("bad argument to 'direction' : either 'left' or 'right'") end end local function new() local NIL = {} NIL.p = NIL NIL.l = NIL NIL.r = NIL return {root = NIL, NIL=NIL} end -- names of variables are consistent with minimum local function edge(x, NIL, d) local L = direction(d) while x[L] ~= NIL do x = x[L] end return x end -- names of variables are consistent with successor (!) local function next(x, NIL, d) local _, R = direction(d) if x[R] ~= NIL then return edge(x[R], NIL, d) end local y = x.p while y ~= NIL and x == y[R] do x = y y = y.p end return y end local function search(x, k, NIL) while x ~= NIL and x.k ~= k do if k < x.k then x = x.l else x = x.r end end return x end -- names of variables are consistent with left rotation local function rotate (t,x,d) local L,R = direction(d) local y = x[R] x[R] = y[L] if y[L] ~= t.NIL then y[L].p = x end y.p = x.p if x.p == t.NIL then t.root = y else if x == x.p[L] then x.p[L] = y else x.p[R] = y end end y[L] = x x.p = y end local function fixinsert(t,z) local n = z while z.p.red do local L,R,LEFT,RIGHT = direction(z.p == z.p.p.l) local y = z.p.p[R] if y.red then z.p.red = nil y.red = nil z.p.p.red = true z = z.p.p else if z == z.p[R] then z = z.p rotate(t,z,LEFT) end z.p.red = nil z.p.p.red = true rotate(t,z.p.p,RIGHT) end end t.root.red = nil return n end local function insert(t,k,data) local NIL = t.NIL local y = NIL local x = t.root while x ~= NIL do y = x if k == x.k then return x end if k < x.k then x = x.l else x = x.r end end local z = {k=k,data=data} z.p = y if y == NIL then t.root = z else if k == y.k then return y end if k < y.k then y.l = z else y.r = z end end z.l = NIL z.r = NIL z.red = true z.data = data return fixinsert(t,z) end local function fixdelete(t, x) local y,z,w while x ~= t.root and not x.red do local L,R,LEFT,RIGHT = direction(x == x.p.l) w = x.p[R] if w.red then w.red = nil x.p.red = true rotate(t, x.p, LEFT) w = x.p[R] end if not w[L].red and not w[R].red then w.red = true x = x.p else if not w[R].red then w[L].red = nil w.red = true rotate(t, w, RIGHT) w = x.p[R] end w.red = x.p.red x.p.red = nil w[R].red = nil rotate(t, x.p, LEFT) x = t.root end end x.red = nil end local function delete(t, z) local NIL = t.NIL local x,y if z.l == NIL or z.r == NIL then y = z else y = next(z, NIL, 'left') end if y.l ~= NIL then x = y.l else x = y.r end x.p = y.p if y.p == NIL then t.root = x else if y == y.p.l then y.p.l = x else y.p.r = x end end if y ~= z then z.k = y.k z.data = y.data end if not y.red then fixdelete(t, x) end return y end return { new = new; minimum = function(x,NIL) return edge(x,NIL,'left') end; maximum = function(x,NIL) return edge(x,NIL,'right') end; predecessor = function(x,NIL) return next(x,NIL,'right') end; successor = function(x,NIL) return next(x,NIL,'left') end; search = search; insert = insert; delete = delete; } ================================================ FILE: benchmarks/Map/redblack.lua ================================================ -------------------------------------------------------------------------------- -- Copyright (C) 2014, Greg Johnson. -- Released under the terms of the GNU GPL v2.0. -------------------------------------------------------------------------------- --[[ This is a lua implementation of red black trees. Here are the provided operations: - create a new tree - insert a data element - delete a data element - find a data element - iterate over the data elements A red black tree is a binary search tree in which each node is marked as a red node or a black node. The tree satisfies the following two properties, which together guarantee that insert, delete, and find operations are all O(logN): Red property: Red nodes do not have red parents. Black property: Siblings have the same maximum black height. (The maximum black height of a node is the maximum number of black nodes on any simple path starting at the node and descending into its subtree.) Data values are any lua values that support comparison via "<", including tables with metatables containing the "__lt" method. Usage: require 'redblack' local tree = redblack.newTree() redblack.insert(tree, 10) redblack.insert(tree, 20) for value in redblack.iterate(tree) do print(value) end print(redblack.find(tree, 10)) -- expect 10 redblack.delete(tree, 10) redblack.delete(tree, 20) print(redblack.find(tree, 10) ~= nil) -- expect false --]] local delete, deleteNode, farNephew, findNode, first, grandparent, insert local insertIntoSortedPosition, insideChild, isBlackNode, isInsideChild local isLeftChild, isRedNode, isRightChild, isRootNode, iterate local leftChild, ensureFarNephewIsRed, ensureOutsideChild, makeRootNode local ensureSiblingIsBlack, nearNephew, newNode, newTree, outsideChild local parent, restoreBlackProperty, restoreRedProperty, rightChild local rotateUp, rotateUpBlackNode, setChild, sibling, successor, swapColors local swapWithSuccessor, uncle, violatesBlackProperty, violatesRedProperty local getChildOrNil, getOnlyChild, find, ensureLeafOrParentOfOneLeaf ------------------------------- public functions ------------------------------- function newTree() return { childList = {} } end -- insert a node into the tree; data values must be comparable using "<". -- function insert(tree, data) local insertedNode = insertIntoSortedPosition(tree, tree.root, data) if violatesRedProperty(insertedNode) then restoreRedProperty(tree, insertedNode) end end -- delete a node from the tree; data values must be comparable using "<". -- function delete(tree, data) local deleteMe = findNode(tree.root, data) if deleteMe == nil then return end deleteMe = ensureLeafOrParentOfOneLeaf(deleteMe) if not isRootNode(deleteMe) and not isRedNode(deleteMe) then deleteMe.color = 'white' -- to satisfy pre-condition of restoreBlackProperty() restoreBlackProperty(tree, deleteMe) end deleteNode(tree, deleteMe) end function find(tree, data) local node = findNode(tree.root, data) return node and node.data end function iterate(xTree) local f = function(s, var) local result = s.returnMe s.returnMe = successor(s.returnMe) return result and result.data end return f, { returnMe = first(xTree.root) }, nil end ----------------------------- end public functions ----------------------------- -- Node "fixMe" is red and has a red parent, violating the red property. -- Other than that, all nodes in the tree satisfy the red and black properties. -- -- Upon return, the red property is restored for all nodes in the tree. -- function restoreRedProperty(tree, fixMe) -- base case 1: if isRootNode(parent(fixMe)) then parent(fixMe).color = 'black' -- base case 2: elseif not isRedNode(uncle(fixMe)) then -- rotateUp changes color of outside child's parent. -- So, if fixMe is outside child, then rotateUp(parent of fixMe) will fix red violation. -- -- (rotateUp requires a red node with black sibling as input) fixMe = ensureOutsideChild(tree, fixMe) rotateUp(tree, parent(fixMe)) -- inductive case: else parent(fixMe).color = 'black' uncle(fixMe).color = 'black' grandparent(fixMe).color = 'red' if violatesRedProperty(grandparent(fixMe)) then restoreRedProperty(tree, grandparent(fixMe)) end end end -- Node fixMe has max black height one less than its sibling, violating the black property. -- Other than that, all nodes in the tree satisfy the red and black properties. -- (This algorithm also requires that fixMe not be a red node.) -- -- Upon return, the black property is restored for all nodes in the tree. -- -- (Also, upon return the parent of fixMe (if it exists) is black; -- this fact is used to slightly simplify the delete algorithm.) -- function restoreBlackProperty(tree, fixMe) ensureSiblingIsBlack(tree, fixMe) -- base case 1: if isRedNode(nearNephew(fixMe)) or isRedNode(farNephew(fixMe)) then -- In this case we can treat parent(fixMe) as root of a tree; no recursion will be necessary. -- rotateUpBlackNode(sibling) will swap max black heights of root's two subtrees. -- rotate operation will make far nephew a child of root and uncle of fixMe. -- If we ensure that far nephew is red, we will be able to increase its max -- black height by simply changing its color to black. ensureFarNephewIsRed(fixMe) rotateUpBlackNode(tree, sibling(fixMe)) uncle(fixMe).color = 'black' else -- Fix black property violation by reducing sibling's max black height. (This also -- reduces max black height of parent, potentially giving parent a black violation.) sibling(fixMe).color = 'red' -- base case 2: if isRedNode(parent(fixMe)) then parent(fixMe).color = 'black' -- inductive case: elseif not isRootNode(parent(fixMe)) then restoreBlackProperty(tree, parent(fixMe)) end end end ----------------------------- rotateUp operations ------------------------------ -- pre-condition: redNode must be red and must not have a red sibling. -- This operation preserves the red and black properties of the tree and the -- in-order traversal of the tree. -- -- orig_parent (b) node (b) -- | | -- +----+----+ ===> +----+----+ -- | | | | -- node (r) t3 t1 orig_parent (r) -- | | -- +--+--+ +--+--+ -- | | | | -- t1 t2 t2 t3 -- function rotateUp(tree, redNode) assert(parent(redNode)) swapColors(redNode, parent(redNode)) local leftChild = isLeftChild(redNode) local p = parent(redNode) local gp = grandparent(redNode) setChild(tree, p, insideChild(redNode), leftChild) setChild(tree, gp, redNode, isLeftChild(p)) setChild(tree, redNode, p, not leftChild) end -- pre-condition: blackNode must be black and must not have a red outside child. -- This function preserves the red property and the in-order traversal of the tree, -- but it violates the black property: -- -- if we consider parent(blackNode) to be the root of a subtree, -- rotateUpBlackNode(node) swaps max black heights of root's two subtrees. -- -- (This function is identical to rotateUp(), except for the pre-conditions) -- function rotateUpBlackNode(tree, blackNode) rotateUp(tree, blackNode) end --------------------------- end rotateUp operations ---------------------------- ------------------------------- iterator support ------------------------------- function first(xNode) while leftChild(xNode) do xNode = leftChild(xNode) end return xNode end function successor(xNode) local result = first(rightChild(xNode)) if result == nil then while isRightChild(xNode) do xNode = parent(xNode) end result = parent(xNode) end return result end ----------------------------- end iterator support ----------------------------- ------------------------------ familial relations ------------------------------ function parent(node) return node and node.parent end function sibling(node) if isLeftChild(node) then return rightChild(parent(node)) elseif isRightChild(node) then return leftChild(parent(node)) else return nil end end function insideChild(node) if isLeftChild(node) then return node.childList[2] elseif isRightChild(node) then return node.childList[1] else return nil end end function outsideChild(node) if isLeftChild(node) then return node.childList[1] elseif isRightChild(node) then return node.childList[2] else return nil end end function grandparent(node) return parent(parent(node)) end function uncle(node) return sibling(parent(node)) end function nearNephew(node) return insideChild(sibling(node)) end function farNephew(node) return outsideChild(sibling(node)) end function getOnlyChild(node) return (node.childList[1] or node.childList[2]) end function isInsideChild(node) return isLeftChild(parent(node)) and isRightChild(node) or isRightChild(parent(node)) and isLeftChild(node) end function ensureOutsideChild(tree, node) if isInsideChild(node) then rotateUp(tree, node) node = outsideChild(node) end return node end function leftChild(node) return node and node.childList[1] end function rightChild(node) return node and node.childList[2] end function isLeftChild(child) return child and child == leftChild(parent(child)) end function isRightChild(child) return child and child == rightChild(parent(child)) end function setChild(tree, parentNode, childNode, makeLeftChild) if parentNode == nil then tree.root = childNode elseif makeLeftChild then parentNode.childList[1] = childNode else parentNode.childList[2] = childNode end if childNode ~= nil then childNode.parent = parentNode end return childNode end function isRootNode(node) return node and node.parent == nil end function makeRootNode(tree, node) tree.root = node return node end ---------------------------- end familial relations ---------------------------- ------------------------------ support functions ------------------------------- function violatesRedProperty(node) return isRedNode(node) and isRedNode(parent(node)) end function findNode(subtreeRoot, data) if subtreeRoot == nil then return nil elseif data < subtreeRoot.data then return findNode(subtreeRoot.childList[1], data) elseif subtreeRoot.data < data then return findNode(subtreeRoot.childList[2], data) else return subtreeRoot end end function insertIntoSortedPosition(tree, subtreeRoot, xData) if subtreeRoot == nil then return makeRootNode(tree, newNode(xData)) else local childIndex = (xData < subtreeRoot.data and 1 or 2) if subtreeRoot.childList[childIndex] == nil then return setChild(tree, subtreeRoot, newNode(xData), childIndex == 1) else return insertIntoSortedPosition(tree, subtreeRoot.childList[childIndex], xData) end end end function newNode(xData) local node = { data = xData, color = 'red', childList = {} } return node end function isBlackNode(node) return node and node.color == 'black' end function isRedNode(node) return node and node.color == 'red' end function ensureLeafOrParentOfOneLeaf(deleteMe) if deleteMe.childList[1] ~= nil and deleteMe.childList[2] ~= nil then deleteMe = swapWithSuccessor(deleteMe) end return deleteMe end function swapColors(node1, node2) node1.color, node2.color = node2.color, node1.color end function ensureSiblingIsBlack(tree, node) if isRedNode(sibling(node)) then rotateUp(tree, sibling(node)) end end function ensureFarNephewIsRed(tree, node) if isBlackNode(farNephew(node)) then rotateUp(tree, nearNephew(node)) end end function swapWithSuccessor(deleteMe) local succ = successor(deleteMe) deleteMe.data, succ.data = succ.data, deleteMe.data return succ end function getChildOrNil(node) return (node and (node.childList[1] or node.childList[2])) end function deleteNode(tree, deleteMe) assert(deleteMe ~= nil and (deleteMe.childList[1] == nil or deleteMe.childList[2] == nil)) setChild(tree, parent(deleteMe), getChildOrNil(deleteMe), isLeftChild(deleteMe)) end ---------------------------- end support functions ----------------------------- ------------------------- support for testing redblack ------------------------- local function printNode(node) io.write(tostring(node)) io.write('') io.write(' ') io.write(' ') io.write(' ') --for k,v in pairs(node) do -- io.write(' <' .. tostring(k) .. ': ' .. tostring(v) .. '>') --end print() end local function printTree(tree, depth) if tree == nil then return end if tree.root ~= nil then tree = tree.root end if depth == nil then depth = 0 end if depth == 0 then print() end printTree(tree.childList[1], depth+1) for i = 1, depth do io.write(" ") end printNode(tree) printTree(tree.childList[2], depth+1) if depth == 0 then print() end end testredblack = { insertIntoSortedPosition = insertIntoSortedPosition, printNode = printNode, printTree = printTree, } ----------------------- end support for testing redblack ----------------------- redblack = { newTree = newTree, insert = insert, delete = delete, iterate = iterate, find = find, } ================================================ FILE: benchmarks/Matmul/matmul_c.c ================================================ // Writen by Attractive Chaos; distributed under the MIT license #include #include double **mm_init(int n) { double **m; int i; m = (double**)malloc(n * sizeof(void*)); for (i = 0; i < n; ++i) m[i] = calloc(n, sizeof(double)); return m; } void mm_destroy(int n, double **m) { int i; for (i = 0; i < n; ++i) free(m[i]); free(m); } double **mm_gen(int n) { double **m, tmp = 1. / n / n; int i, j; m = mm_init(n); for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) m[i][j] = tmp * (i - j) * (i + j); return m; } // better cache performance by transposing the second matrix double **mm_mul(int n, double *const *a, double *const *b) { int i, j, k; double **m, **c; m = mm_init(n); c = mm_init(n); for (i = 0; i < n; ++i) // transpose for (j = 0; j < n; ++j) c[i][j] = b[j][i]; for (i = 0; i < n; ++i) { double *p = a[i], *q = m[i]; for (j = 0; j < n; ++j) { double t = 0.0, *r = c[j]; for (k = 0; k < n; ++k) t += p[k] * r[k]; q[j] = t; } } mm_destroy(n, c); return m; } int main(int argc, char *argv[]) { int n = 300; double **a, **b, **m; n = (n/2) * 2; a = mm_gen(n); b = mm_gen(n); m = mm_mul(n, a, b); //fprintf(stderr, "%lf\n", m[n/2][n/2]); mm_destroy(n, a); mm_destroy(n, b); mm_destroy(n, m); return 0; } ================================================ FILE: benchmarks/Matmul/matmul_cello.c ================================================ #include "Cello.h" struct Matrix { size_t n; double** d; }; static void Matrix_New(var self, var args) { struct Matrix* m = self; m->n = c_int(get(args, $I(0))); m->d = malloc(m->n * sizeof(double*)); for (int i = 0; i < m->n; ++i) { m->d[i] = calloc(m->n, sizeof(double)); } } static void Matrix_Del(var self) { struct Matrix* m = self; for (int i = 0; i < m->n; ++i) free(m->d[i]); free(m->d); } static var Matrix = Cello(Matrix, Instance(New, Matrix_New, Matrix_Del)); static struct Matrix* Matrix_Gen(int n) { struct Matrix* m = new(Matrix, $I(n)); double tmp = 1.0 / n / n; for (int i = 0; i < m->n; ++i) { for (int j = 0; j < m->n; ++j) { m->d[i][j] = tmp * (i - j) * (i + j); } } return m; } static var Matrix_Mul(var m0, var m1) { struct Matrix* a = m0; struct Matrix* b = m1; struct Matrix* m = new(Matrix, $I(a->n)); struct Matrix* c = new(Matrix, $I(a->n)); for (int i = 0; i < m->n; ++i) { for (int j = 0; j < m->n; ++j) { c->d[i][j] = b->d[j][i]; } } for (int i = 0; i < m->n; ++i) { double *p = a->d[i], *q = m->d[i]; for (int j = 0; j < m->n; ++j) { double t = 0.0, *r = c->d[j]; for (int k = 0; k < m->n; ++k) { t += p[k] * r[k]; } q[j] = t; } } del(c); return m; } int main(int argc, char *argv[]) { int n = 300; n = (n/2) * 2; var a = Matrix_Gen(n); var b = Matrix_Gen(n); var m = Matrix_Mul(a, b); del(a); del(b); del(m); return 0; } ================================================ FILE: benchmarks/Matmul/matmul_cpp.cpp ================================================ #include class Matrix { public: size_t n; double** d; Matrix(int n); ~Matrix(); }; Matrix::Matrix(int n) { n = n; d = (double**)malloc(n * sizeof(double*)); for (int i = 0; i < n; ++i) { d[i] = (double*)calloc(n, sizeof(double)); } } Matrix::~Matrix() { for (int i = 0; i < n; ++i) free(d[i]); free(d); } static Matrix* Matrix_Gen(int n) { Matrix* m = new Matrix(n); double tmp = 1.0 / n / n; for (int i = 0; i < m->n; ++i) { for (int j = 0; j < m->n; ++j) { m->d[i][j] = tmp * (i - j) * (i + j); } } return m; } static Matrix* Matrix_Mul(Matrix* m0, Matrix* m1) { Matrix* a = m0; Matrix* b = m1; Matrix* m = new Matrix(a->n); Matrix* c = new Matrix(a->n); for (int i = 0; i < m->n; ++i) { for (int j = 0; j < m->n; ++j) { c->d[i][j] = b->d[j][i]; } } for (int i = 0; i < m->n; ++i) { double *p = a->d[i], *q = m->d[i]; for (int j = 0; j < m->n; ++j) { double t = 0.0, *r = c->d[j]; for (int k = 0; k < m->n; ++k) { t += p[k] * r[k]; } q[j] = t; } } delete c; return m; } int main(int argc, char *argv[]) { int n = 300; n = (n/2) * 2; Matrix* a = Matrix_Gen(n); Matrix* b = Matrix_Gen(n); Matrix* m = Matrix_Mul(a, b); delete a; delete b; delete m; return 0; } ================================================ FILE: benchmarks/Matmul/matmul_cs.cs ================================================ // Written by Attractive Chaos; distributed under the MIT license // To compile: mcs -optimize+ -out:matmul_v1.run matmul_v1.cs // Translated from Java with reference to: http://code.wikia.com/wiki/Matrix_multiplication using System; class matmul_v1 { public double[,] matgen(int n) { double[,] a = new double[n,n]; double tmp = 1.0 / n / n; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) a[i,j] = tmp * (i - j) * (i + j); return a; } public double[,] matmul(double[,] a, double[,] b) { int m = a.GetLength(0), n = a.GetLength(1), p = b.GetLength(0); double[,] x = new double[m,p]; double[,] c = new double[p,n]; for (int i = 0; i < n; ++i) // transpose for (int j = 0; j < p; ++j) c[j,i] = b[i,j]; for (int i = 0; i < m; ++i) for (int j = 0; j < p; ++j) { double s = 0.0; for (int k = 0; k < n; ++k) s += a[i,k] * c[j,k]; x[i,j] = s; } return x; } public static void Main(String[] args) { int n = 100; if (args.GetLength(0) >= 1) n = int.Parse(args[0]); n = n / 2 * 2; matmul_v1 m = new matmul_v1(); double[,] a, b, x; a = m.matgen(n); b = m.matgen(n); x = m.matmul(a, b); Console.WriteLine(x[n/2,n/2]); } } ================================================ FILE: benchmarks/Matmul/matmul_d.d ================================================ // Originally written by Attractive Chaos; distributed under the MIT license (D V.2 code) // Contributed by leonardo and then modified by Attractive Chaos to remove D 2.0 features import std.stdio, std.string; double[][] matGen(in int n) { double tmp = 1.0 / n / n; auto a = new double[][](n, n); foreach (int i, row; a) foreach (int j, ref x; row) x = tmp * (i - j) * (i + j); return a; } double[][] matMul(in double[][] a, in double[][] b) { int m = a.length, n = a[0].length, p = b[0].length; // transpose auto c = new double[][](p, n); foreach (i, brow; b) foreach (j, bx; brow) c[j][i] = bx; auto x = new double[][](m, p); foreach (i, arow; a) foreach (j, crow; c) { // x[i][j] = std.numeric.dotProduct(arow, crow); // right way double s = 0.0; foreach (k, arowk; arow) s += arowk * crow[k]; x[i][j] = s; } return x; } void main(in string[] args) { int n = 100; if (args.length >= 2) n = atoi(args[1]) / 2 * 2; auto a = matGen(n); auto b = matGen(n); auto x = matMul(a, b); writefln(x[n / 2][n / 2]); } ================================================ FILE: benchmarks/Matmul/matmul_dart.dart ================================================ import 'dart:scalarlist'; mat_transpose(a) { int m = a.length, n = a[0].length; // m rows and n cols var b = new List(n); for (int j = 0; j < n; ++j) b[j] = new Float64List(m); for (int i = 0; i < m; ++i) for (int j = 0; j < n; ++j) b[j][i] = a[i][j]; return b; } mat_mul(a, b) { inner_loop(t, n, ai, c) { var xi = new Float64List(t); for (int j = 0; j < t; ++j) { double sum = 0.0; for (int k = 0; k < n; ++k) sum += ai[k] * c[j][k]; xi[j] = sum; } return xi; } int m = a.length, n = a[0].length, s = b.length, t = b[0].length; if (n != s) return null; var x = new List(m), c = mat_transpose(b); for (int i = 0; i < m; ++i) x[i] = inner_loop(t, n, a[i], c); return x; } mat_gen(int n) { var a = new List(n); double t = 1.0 / n / n; for (int i = 0; i < n; ++i) { a[i] = new Float64List(n); for (int j = 0; j < n; ++j) a[i][j] = t * (i - j) * (i + j); } return a; } main() { List argv = new Options().arguments; int n = argv.length != 0? int.parse(argv[0]) : 1000; var a = mat_gen(n), b = mat_gen(n); var c = mat_mul(a, b); print(c[n~/2][n~/2]); } ================================================ FILE: benchmarks/Matmul/matmul_go.go ================================================ // Written by Attractive Chaos; distributed under the MIT license package main import "fmt" import "flag" import "strconv" func matgen(n int) [][]float64 { a := make([][]float64, n) tmp := float64(1.0) / float64(n) / float64(n) // pretty silly... for i := 0; i < n; i++ { a[i] = make([]float64, n) for j := 0; j < n; j++ { a[i][j] = tmp * float64(i-j) * float64(i+j) } } return a } func matmul(a [][]float64, b [][]float64) [][]float64 { m := len(a) n := len(a[0]) p := len(b[0]) x := make([][]float64, m) c := make([][]float64, p) for i := 0; i < p; i++ { c[i] = make([]float64, n) for j := 0; j < n; j++ { c[i][j] = b[j][i] } } for i, am := range a { x[i] = make([]float64, p) for j, cm := range c { s := float64(0) for k, m := range am { s += m*cm[k] } x[i][j] = s } } return x } func main() { n := int(100) flag.Parse() if flag.NArg() > 0 { n,_ = strconv.Atoi(flag.Arg(0)) } a := matgen(n) b := matgen(n) x := matmul(a, b) fmt.Printf("%f\n", x[n/2][n/2]) } ================================================ FILE: benchmarks/Matmul/matmul_java.java ================================================ // Written by Attractive Chaos; distributed under the MIT license class matmul_java { public double[][] matgen(int n) { double[][] a = new double[n][n]; double tmp = 1. / n / n; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) a[i][j] = tmp * (i - j) * (i + j); return a; } public double[][] matmul(double[][] a, double[][] b) { int m = a.length, n = a[0].length, p = b[0].length; double[][] x = new double[m][p]; double[][] c = new double[p][n]; for (int i = 0; i < n; ++i) // transpose for (int j = 0; j < p; ++j) c[j][i] = b[i][j]; for (int i = 0; i < m; ++i) for (int j = 0; j < p; ++j) { double s = 0.0; for (int k = 0; k < n; ++k) s += a[i][k] * c[j][k]; x[i][j] = s; } return x; } public static void main(String[] args) { int n = 300; n = n / 2 * 2; matmul_java m = new matmul_java(); double[][] a, b, x; a = m.matgen(n); b = m.matgen(n); x = m.matmul(a, b); //System.out.println(x[n/2][n/2]); } } ================================================ FILE: benchmarks/Matmul/matmul_javascript.js ================================================ // Writen by Attractive Chaos; distributed under the MIT license Math.m = {}; Math.m.T = function(a) { // matrix transpose var b = [], m = a.length, n = a[0].length; // m rows and n cols for (var j = 0; j < n; ++j) b[j] = []; for (var i = 0; i < m; ++i) for (var j = 0; j < n; ++j) b[j].push(a[i][j]); return b; } Math.m.mul = function(a, b) { // matrix mul var m = a.length, n = a[0].length, s = b.length, t = b[0].length; if (n != s) return null; var x = [], c = Math.m.T(b); for (var i = 0; i < m; ++i) { x[i] = []; for (var j = 0; j < t; ++j) { var sum = 0; var ai = a[i], cj = c[j]; for (var k = 0; k < n; ++k) sum += ai[k] * cj[k]; x[i].push(sum); } } return x; } function matgen(n) { var a = [], tmp = 1. / n / n; for (i = 0; i < n; ++i) { a[i] = [] for (j = 0; j < n; ++j) a[i][j] = tmp * (i - j) * (i + j); } return a; } var n = 300; var a = matgen(n); var b = matgen(n); var c = Math.m.mul(a, b); //print(c[n/2][n/2]); ================================================ FILE: benchmarks/Matmul/matmul_lua.lua ================================================ -- Writen by Attractive Chaos; distributed under the MIT license matrix = {} function matrix.T(a) local m, n, x = #a, #a[1], {}; for i = 1, n do x[i] = {}; for j = 1, m do x[i][j] = a[j][i] end end return x; end function matrix.mul(a, b) assert(#a[1] == #b); local m, n, p, x = #a, #a[1], #b[1], {}; local c = matrix.T(b); -- transpose for efficiency for i = 1, m do x[i] = {} local xi = x[i]; for j = 1, p do local sum, ai, cj = 0, a[i], c[j]; -- for luajit, caching c[j] or not makes no difference; lua is not so clever for k = 1, n do sum = sum + ai[k] * cj[k] end xi[j] = sum; end end return x; end function matgen(n) local a, tmp = {}, 1. / n / n; for i = 1, n do a[i] = {} for j = 1, n do a[i][j] = tmp * (i - j) * (i + j - 2) end end return a; end local n = 300; n = math.floor(n/2) * 2; local a = matrix.mul(matgen(n), matgen(n)); --print(a[n/2+1][n/2+1]); ================================================ FILE: benchmarks/Matmul/matmul_perl.pl ================================================ #!/usr/bin/perl -w # Writen by Attractive Chaos; distributed under the MIT license use strict; use warnings; &main; sub main { my $n = $ARGV[0] || 100; $n = int($n/2) * 2; my (@a, @b, @x); &matgen($n, \@a); &matgen($n, \@b); &mul(\@a, \@b, \@x); print $x[$n/2][$n/2], "\n"; } sub transpose { my ($a, $b) = @_; my $m = @$a; my $n = @{$a->[0]}; @$b = (); for my $i (0 .. $n - 1) { @{$b->[$i]} = (); for my $j (0 .. $m - 1) { push(@{$b->[$i]}, $a->[$j][$i]); } } } sub mul { my ($a, $b, $x) = @_; my $m = @$a; my $n = @{$a->[0]}; my $p = @{$b->[0]}; my @c; &transpose($b, \@c); for my $i (0 .. $m - 1) { @{$x->[$i]} = (); for my $j (0 .. $p - 1) { my $sum = 0; my ($ai, $cj) = ($a->[$i], $c[$j]); for my $k (0 .. $n - 1) { $sum += $ai->[$k] * $cj->[$k]; } push(@{$x->[$i]}, $sum); } } } sub matgen { my ($n, $a) = @_; @$a = (); my $tmp = 1. / $n / $n; for my $i (0 .. $n - 1) { for my $j (0 .. $n - 1) { $a->[$i][$j] = $tmp * ($i - $j) * ($i + $j); } } } ================================================ FILE: benchmarks/Matmul/matmul_python.py ================================================ import sys # Writen by Attractive Chaos; distributed under the MIT license # reference: http://www.syntagmatic.net/matrix-multiplication-in-python/ def matmul(a, b): # FIXME: no error checking c = [[b[j][i] for j in range(len(b))] for i in range(len(b[0]))] d = [[0 for j in range(len(b[0]))] for i in range(len(a))] # transpose for i in range(len(a)): for j in range(len(b[0])): s = 0 ai = a[i] cj = c[j] for k in range(len(a[0])): s += ai[k] * cj[k] d[i][j] = s return d def main(): n = 300 n = int(float(n)/2) * 2 # FIXME: I am sure there are better ways to do this... tmp = 1. / n / n a = [[tmp * (i - j) * (i + j) for j in range(n)] for i in range(n)] b = [[tmp * (i - j) * (i + j) for j in range(n)] for i in range(n)] d = matmul(a, b) #print(d[int(n/2)][int(n/2)]) if __name__ == '__main__': main() ================================================ FILE: benchmarks/Matmul/matmul_r.R ================================================ matgen <- function(n) { y0 <- matrix(rep(seq(0, n-1), n), n) y1 <- t(y0) z <- 1 / n / n (y0 - y1) * (y0 + y1) * z } matmul <- function(a, b) { # transposing or not seems to make no difference m <- dim(a)[1] n <- dim(a)[2] p <- dim(b)[2] x <- matrix(rep(0, m * p), m) for (i in seq(1, m)) { for (j in seq(1, p)) { #x[i,j] <- sum(a[i,], b[,j]) # using this line: 43.3 sec, 64.6 MB s <- 0; ai <- a[i,]; bj <- b[,j]; for (k in seq(1, n)) s <- s + ai[k] * bj[k]; x[i,j] <- s } } x } n <- as.integer(commandArgs(trailingOnly = T)[1]) if (is.na(n)) n <- 100 # x <- matgen(n) %*% matgen(n) # using this line: 2.7 sec, 53.0 MB x <- matmul(matgen(n), matgen(n)) x[n/2+1,n/2+1] ================================================ FILE: benchmarks/Matmul/matmul_ruby.rb ================================================ # Writen by Attractive Chaos; distributed under the MIT license # This version does not use the built-in Matrix object # reference: http://vikhyat.net/articles/matrix_multiplication_ruby/ def matmul(a, b) m = a.length n = a[0].length p = b[0].length # transpose b2 = Array.new(n) { Array.new(p) { 0 } } for i in 0 .. n-1 for j in 0 .. p-1 b2[j][i] = b[i][j] end end # multiplication c = Array.new(m) { Array.new(p) { 0 } } for i in 0 .. m-1 for j in 0 .. p-1 s = 0 ai, b2j = a[i], b2[j] for k in 0 .. n-1 s += ai[k] * b2j[k] end c[i][j] = s end end return c end def matgen(n) tmp = 1.0 / n / n a = Array.new(n) { Array.new(n) { 0 } } for i in 0 .. n-1 for j in 0 .. n-1 a[i][j] = tmp * (i - j) * (i + j) end end return a end n = 300 n = n / 2 * 2 a = matgen(n) b = matgen(n) c = matmul(a, b) #puts c[n/2][n/2] ================================================ FILE: benchmarks/Nbodies/nbodies_c.c ================================================ #include #include #undef M_PI #define M_PI 3.14159265359 static const double solar_mass = 4 * M_PI * M_PI; static const double days_per_year = 365.24; struct Body { double x, y, z; double vx, vy, vz; double mass; }; static void Body_Offset_Momentum(struct Body* b, double px, double py, double pz) { b->vx = -px / solar_mass; b->vy = -py / solar_mass; b->vz = -pz / solar_mass; } static void Bodies_Advance(struct Body* bodies, size_t nbodies, double dt) { for(size_t i = 0; i < nbodies; i++) { struct Body* body0 = &bodies[i]; for(size_t j = i+1; j < nbodies; j++) { struct Body* body1 = &bodies[j]; double dx = body0->x - body1->x; double dy = body0->y - body1->y; double dz = body0->z - body1->z; double dsquared = dx * dx + dy * dy + dz * dz; double distance = sqrt(dsquared); double mag = dt / (dsquared * distance); body0->vx -= dx * body1->mass * mag; body0->vy -= dy * body1->mass * mag; body0->vz -= dz * body1->mass * mag; body1->vx += dx * body0->mass * mag; body1->vy += dy * body0->mass * mag; body1->vz += dz * body0->mass * mag; } } for(size_t i = 0; i < nbodies; i++) { struct Body* b = &bodies[i]; b->x += dt * b->vx; b->y += dt * b->vy; b->z += dt * b->vz; } } static double Bodies_Energy(struct Body* bodies, size_t nbodies) { double dx = 0.0; double dy = 0.0; double dz = 0.0; double distance = 0.0; double e = 0.0; for(size_t i = 0; i < nbodies; i++) { struct Body* body0 = &bodies[i]; e += (0.5 * body0->mass * ( body0->vx * body0->vx + body0->vy * body0->vy + body0->vz * body0->vz)); for(size_t j = i+1; j < nbodies; j++) { struct Body* body1 = &bodies[j]; dx = body0->x - body1->x; dy = body0->y - body1->y; dz = body0->z - body1->z; distance = sqrt(dx * dx + dy * dy + dz * dz); e -= (body0->mass * body1->mass) / distance; } } return e; } int main(int argc, char** argv) { struct Body jupiter = (struct Body){ 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-00, days_per_year * 1.66007664274403694e-03, days_per_year * 7.69901118419740425e-03, days_per_year * -6.90460016972063023e-05, solar_mass * 9.54791938424326609e-04}; struct Body saturn = (struct Body){ 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, days_per_year * -2.76742510726862411e-03, days_per_year * 4.99852801234917238e-03, days_per_year * 2.30417297573763929e-05, solar_mass * 2.85885980666130812e-04}; struct Body uranus = (struct Body){ 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, days_per_year * 2.96460137564761618e-03, days_per_year * 2.37847173959480950e-03, days_per_year * -2.96589568540237556e-05, solar_mass * 4.36624404335156298e-05}; struct Body neptune = (struct Body){ 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, days_per_year * 2.68067772490389322e-03, days_per_year * 1.62824170038242295e-03, days_per_year * -9.51592254519715870e-05, solar_mass * 5.15138902046611451e-05}; struct Body sun = (struct Body){ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, solar_mass}; size_t nbodies = 5; struct Body* bodies = (struct Body[5]){ jupiter, saturn, uranus, neptune, sun}; double px = 0.0; double py = 0.0; double pz = 0.0; for(size_t i = 0; i < nbodies; i++) { struct Body* b = &bodies[i]; px += b->vx * b->mass; py += b->vy * b->mass; pz += b->vz * b->mass; } Body_Offset_Momentum(&bodies[0], px, py, pz); for(size_t i = 0; i < 100000; i++) { Bodies_Advance(bodies, nbodies, 1e-5); } } ================================================ FILE: benchmarks/Nbodies/nbodies_cello.c ================================================ #include "Cello.h" static const double solar_mass = 4 * M_PI * M_PI; static const double days_per_year = 365.24; struct Body { double x, y, z; double vx, vy, vz; double mass; }; static void Body_Offset_Momentum(struct Body* self, double px, double py, double pz) { self->vx = -px / solar_mass; self->vy = -py / solar_mass; self->vz = -pz / solar_mass; } var Body = Cello(Body); static void Bodies_Advance(var bodies, double dt) { size_t nbodies = len(bodies); foreach(i in range($I(nbodies))) { struct Body* body0 = get(bodies, i); foreach(j in range($I(c_int(i)+1), $I(nbodies))) { struct Body* body1 = get(bodies, j); double dx = body0->x - body1->x; double dy = body0->y - body1->y; double dz = body0->z - body1->z; double dsquared = dx * dx + dy * dy + dz * dz; double mag = dt / (dsquared * sqrt(dsquared)); body0->vx -= dx * body1->mass * mag; body0->vy -= dy * body1->mass * mag; body0->vz -= dz * body1->mass * mag; body1->vx += dx * body0->mass * mag; body1->vy += dy * body0->mass * mag; body1->vz += dz * body0->mass * mag; } } foreach (body in bodies) { struct Body* b = body; b->x += dt * b->vx; b->y += dt * b->vy; b->z += dt * b->vz; } } static double Bodies_Energy(var bodies) { double dx = 0.0; double dy = 0.0; double dz = 0.0; double distance = 0.0; double e = 0.0; size_t nbodies = len(bodies); foreach(i in range($I(nbodies))) { struct Body* body0 = get(bodies, i); e += (0.5 * body0->mass * ( body0->vx * body0->vx + body0->vy * body0->vy + body0->vz * body0->vz)); foreach(j in range($I(c_int(i)+1), $I(nbodies))) { struct Body* body1 = get(bodies, j); dx = body0->x - body1->x; dy = body0->y - body1->y; dz = body0->z - body1->z; distance = sqrt(dx * dx + dy * dy + dz * dz); e -= (body0->mass * body1->mass) / distance; } } return e; } int main(int argc, char** argv) { var jupiter = $(Body, 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-00, days_per_year * 1.66007664274403694e-03, days_per_year * 7.69901118419740425e-03, days_per_year * -6.90460016972063023e-05, solar_mass * 9.54791938424326609e-04); var saturn = $(Body, 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, days_per_year * -2.76742510726862411e-03, days_per_year * 4.99852801234917238e-03, days_per_year * 2.30417297573763929e-05, solar_mass * 2.85885980666130812e-04); var uranus = $(Body, 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, days_per_year * 2.96460137564761618e-03, days_per_year * 2.37847173959480950e-03, days_per_year * -2.96589568540237556e-05, solar_mass * 4.36624404335156298e-05); var neptune = $(Body, 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, days_per_year * 2.68067772490389322e-03, days_per_year * 1.62824170038242295e-03, days_per_year * -9.51592254519715870e-05, solar_mass * 5.15138902046611451e-05); var sun = $(Body, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, solar_mass); var bodies = new(Array, Body, jupiter, saturn, uranus, neptune, sun); double px = 0.0; double py = 0.0; double pz = 0.0; foreach(body in bodies) { struct Body* b = body; px += b->vx * b->mass; py += b->vy * b->mass; pz += b->vz * b->mass; } Body_Offset_Momentum(get(bodies, $I(0)), px, py, pz); foreach(i in range($I(100000))) { Bodies_Advance(bodies, 1e-5); } del(bodies); return 0; } ================================================ FILE: benchmarks/Nbodies/nbodies_cpp.cpp ================================================ #include #include #undef M_PI #define M_PI 3.14159265358979323846 static const double solar_mass = 4 * M_PI * M_PI; static const double days_per_year = 365.24; class Body { public: Body(double x, double y, double z, double vx, double vy, double vz, double mass) : x(x), y(y), z(z), vx(vx), vy(vy), vz(vz), mass(mass) {} double x, y, z; double vx, vy, vz; double mass; void Offset_Momentum(double px, double py, double pz) { vx = -px / solar_mass; vy = -py / solar_mass; vz = -pz / solar_mass; } }; static void Bodies_Advance(std::vector bodies, double dt) { for(int i = 0; i < bodies.size(); i++) { Body* body0 = bodies[i]; for(int j = i+1; j < bodies.size(); j++) { Body* body1 = bodies[j]; double dx = body0->x - body1->x; double dy = body0->y - body1->y; double dz = body0->z - body1->z; double dsquared = dx * dx + dy * dy + dz * dz; double distance = sqrt(dsquared); double mag = dt / (dsquared * distance); body0->vx -= dx * body1->mass * mag; body0->vy -= dy * body1->mass * mag; body0->vz -= dz * body1->mass * mag; body1->vx += dx * body0->mass * mag; body1->vy += dy * body0->mass * mag; body1->vz += dz * body0->mass * mag; } } for (auto &b : bodies) { b->x += dt * b->vx; b->y += dt * b->vy; b->z += dt * b->vz; } } static double Bodies_Energy(std::vector bodies) { double dx = 0.0; double dy = 0.0; double dz = 0.0; double distance = 0.0; double e = 0.0; for(int i = 0; i < bodies.size(); i++) { Body* body0 = bodies[i]; e += (0.5 * body0->mass * ( body0->vx * body0->vx + body0->vy * body0->vy + body0->vz * body0->vz)); for(int j = i+1; j < bodies.size(); j++) { Body* body1 = bodies[j]; dx = body0->x - body1->x; dy = body0->y - body1->y; dz = body0->z - body1->z; distance = sqrt(dx * dx + dy * dy + dz * dz); e -= (body0->mass * body1->mass) / distance; } } return e; } int main(int argc, char** argv) { Body jupiter = Body( 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-00, days_per_year * 1.66007664274403694e-03, days_per_year * 7.69901118419740425e-03, days_per_year * -6.90460016972063023e-05, solar_mass * 9.54791938424326609e-04); Body saturn = Body( 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, days_per_year * -2.76742510726862411e-03, days_per_year * 4.99852801234917238e-03, days_per_year * 2.30417297573763929e-05, solar_mass * 2.85885980666130812e-04); Body uranus = Body( 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, days_per_year * 2.96460137564761618e-03, days_per_year * 2.37847173959480950e-03, days_per_year * -2.96589568540237556e-05, solar_mass * 4.36624404335156298e-05); Body neptune = Body( 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, days_per_year * 2.68067772490389322e-03, days_per_year * 1.62824170038242295e-03, days_per_year * -9.51592254519715870e-05, solar_mass * 5.15138902046611451e-05); Body sun = Body( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, solar_mass); auto bodies = std::vector(); bodies.push_back(&jupiter); bodies.push_back(&saturn); bodies.push_back(&uranus); bodies.push_back(&neptune); bodies.push_back(&sun); double px = 0.0; double py = 0.0; double pz = 0.0; for(auto &b : bodies) { px += b->vx * b->mass; py += b->vy * b->mass; pz += b->vz * b->mass; } bodies[0]->Offset_Momentum(px, py, pz); for(int i = 0; i < 100000; i++) { Bodies_Advance(bodies, 1e-5); } } ================================================ FILE: benchmarks/Nbodies/nbodies_java.java ================================================ public final class nbodies_java { public static void main(String[] args) { NBodySystem bodies = new NBodySystem(); for (int i=0; i<100000; ++i) { bodies.advance(1e-5); } } } final class NBodySystem { private Body[] bodies; public NBodySystem(){ bodies = new Body[]{ Body.sun(), Body.jupiter(), Body.saturn(), Body.uranus(), Body.neptune() }; double px = 0.0; double py = 0.0; double pz = 0.0; for(int i=0; i < bodies.length; ++i) { px += bodies[i].vx * bodies[i].mass; py += bodies[i].vy * bodies[i].mass; pz += bodies[i].vz * bodies[i].mass; } bodies[0].offsetMomentum(px,py,pz); } public void advance(double dt) { for(int i=0; i < bodies.length; ++i) { Body iBody = bodies[i]; for(int j=i+1; j < bodies.length; ++j) { double dx = iBody.x - bodies[j].x; double dy = iBody.y - bodies[j].y; double dz = iBody.z - bodies[j].z; double dSquared = dx * dx + dy * dy + dz * dz; double distance = Math.sqrt(dSquared); double mag = dt / (dSquared * distance); iBody.vx -= dx * bodies[j].mass * mag; iBody.vy -= dy * bodies[j].mass * mag; iBody.vz -= dz * bodies[j].mass * mag; bodies[j].vx += dx * iBody.mass * mag; bodies[j].vy += dy * iBody.mass * mag; bodies[j].vz += dz * iBody.mass * mag; } } for ( Body body : bodies) { body.x += dt * body.vx; body.y += dt * body.vy; body.z += dt * body.vz; } } public double energy(){ double dx, dy, dz, distance; double e = 0.0; for (int i=0; i < bodies.length; ++i) { Body iBody = bodies[i]; e += 0.5 * iBody.mass * ( iBody.vx * iBody.vx + iBody.vy * iBody.vy + iBody.vz * iBody.vz ); for (int j=i+1; j < bodies.length; ++j) { Body jBody = bodies[j]; dx = iBody.x - jBody.x; dy = iBody.y - jBody.y; dz = iBody.z - jBody.z; distance = Math.sqrt(dx*dx + dy*dy + dz*dz); e -= (iBody.mass * jBody.mass) / distance; } } return e; } } final class Body { static final double PI = 3.141592653589793; static final double SOLAR_MASS = 4 * PI * PI; static final double DAYS_PER_YEAR = 365.24; public double x, y, z, vx, vy, vz, mass; public Body(){} static Body jupiter(){ Body p = new Body(); p.x = 4.84143144246472090e+00; p.y = -1.16032004402742839e+00; p.z = -1.03622044471123109e-01; p.vx = 1.66007664274403694e-03 * DAYS_PER_YEAR; p.vy = 7.69901118419740425e-03 * DAYS_PER_YEAR; p.vz = -6.90460016972063023e-05 * DAYS_PER_YEAR; p.mass = 9.54791938424326609e-04 * SOLAR_MASS; return p; } static Body saturn(){ Body p = new Body(); p.x = 8.34336671824457987e+00; p.y = 4.12479856412430479e+00; p.z = -4.03523417114321381e-01; p.vx = -2.76742510726862411e-03 * DAYS_PER_YEAR; p.vy = 4.99852801234917238e-03 * DAYS_PER_YEAR; p.vz = 2.30417297573763929e-05 * DAYS_PER_YEAR; p.mass = 2.85885980666130812e-04 * SOLAR_MASS; return p; } static Body uranus(){ Body p = new Body(); p.x = 1.28943695621391310e+01; p.y = -1.51111514016986312e+01; p.z = -2.23307578892655734e-01; p.vx = 2.96460137564761618e-03 * DAYS_PER_YEAR; p.vy = 2.37847173959480950e-03 * DAYS_PER_YEAR; p.vz = -2.96589568540237556e-05 * DAYS_PER_YEAR; p.mass = 4.36624404335156298e-05 * SOLAR_MASS; return p; } static Body neptune(){ Body p = new Body(); p.x = 1.53796971148509165e+01; p.y = -2.59193146099879641e+01; p.z = 1.79258772950371181e-01; p.vx = 2.68067772490389322e-03 * DAYS_PER_YEAR; p.vy = 1.62824170038242295e-03 * DAYS_PER_YEAR; p.vz = -9.51592254519715870e-05 * DAYS_PER_YEAR; p.mass = 5.15138902046611451e-05 * SOLAR_MASS; return p; } static Body sun(){ Body p = new Body(); p.mass = SOLAR_MASS; return p; } Body offsetMomentum(double px, double py, double pz){ vx = -px / SOLAR_MASS; vy = -py / SOLAR_MASS; vz = -pz / SOLAR_MASS; return this; } } ================================================ FILE: benchmarks/Nbodies/nbodies_javascript.js ================================================ var pi = 3.141592653589793 var solar_mass = 4 * pi * pi var days_per_year = 365.24 var jupiter = { x: 4.84143144246472090e+00, y: -1.16032004402742839e+00, z: -1.03622044471123109e-01, vx: days_per_year * 1.66007664274403694e-03, vy: days_per_year * 7.69901118419740425e-03, vz: days_per_year * -6.90460016972063023e-05, mass: solar_mass * 9.54791938424326609e-04} var saturn = { x: 8.34336671824457987e+00, y: 4.12479856412430479e+00, z: -4.03523417114321381e-01, vx: days_per_year * -2.76742510726862411e-03, vy: days_per_year * 4.99852801234917238e-03, vz: days_per_year * 2.30417297573763929e-05, mass: solar_mass * 2.85885980666130812e-04} var uranus = { x: 1.28943695621391310e+01, y: -1.51111514016986312e+01, z: -2.23307578892655734e-01, vx: days_per_year * 2.96460137564761618e-03, vy: days_per_year * 2.37847173959480950e-03, vz: days_per_year * -2.96589568540237556e-05, mass: solar_mass * 4.36624404335156298e-05} var neptune = { x: 1.53796971148509165e+01, y: -2.59193146099879641e+01, z: 1.79258772950371181e-01, vx: days_per_year * 2.68067772490389322e-03, vy: days_per_year * 1.62824170038242295e-03, vz: days_per_year * -9.51592254519715870e-05, mass: solar_mass * 5.15138902046611451e-05} var sun = { x: 0.0, y: 0.0, z: 0.0, vx: 0.0, vy: 0.0, vz: 0.0, mass: solar_mass} function body_offset_momentum(b, px, py, pz) { b.vx = -px / solar_mass b.vy = -py / solar_mass b.vz = -pz / solar_mass } function bodies_new() { bodies = [jupiter, saturn, uranus, neptune, sun]; px = 0.0; py = 0.0; pz = 0.0; for (var i = 0; i < bodies.length; i++) { body = bodies[i]; px += body.vx * body.mass; py += body.vy * body.mass; pz += body.vz * body.mass; } body_offset_momentum(bodies[0], px, py, pz); return bodies; } function bodies_advance(bodies, dt) { for (var i = 0; i < bodies.length; i++) { var body0 = bodies[i] for (var j = i+1; j < bodies.length; j++) { var body1 = bodies[j]; var dx = body0.x - body1.x; var dy = body0.y - body1.y; var dz = body0.z - body1.z; var dsquared = dx * dx + dy * dy + dz * dz; var distance = Math.sqrt(dsquared); var mag = dt / (dsquared * distance); body0.vx -= dx * body1.mass * mag; body0.vy -= dy * body1.mass * mag; body0.vz -= dz * body1.mass * mag; body1.vx += dx * body0.mass * mag; body1.vy += dy * body0.mass * mag; body1.vz += dz * body0.mass * mag; } } for (var i = 0; i < bodies.length; i++) { body = bodies[i]; body.x += dt * body.vx; body.y += dt * body.vy; body.z += dt * body.vz; } } function bodies_energy(bodies) { var dx = 0.0; var dy = 0.0; var dz = 0.0; var distance = 0.0; var e = 0.0; for (var i = 0; i < bodies.length; i++) { var body0 = bodies[i]; e += (0.5 * body0.mass * ( body0.vx * body0.vx + body0.vy * body0.vy + body0.vz * body0.vz)); for (var j = i+1; j < bodies.length; j++) { var body1 = bodies[j]; dx = body0.x - body1.x; dy = body0.y - body1.y; dz = body0.z - body1.z; distance = Math.sqrt(dx * dx + dy * dy + dz * dz); e -= (body0.mass * body1.mass) / distance; } } return e; } var bodies = bodies_new() for (var i = 0; i < 100000; i++) { bodies_advance(bodies, 1e-5) } ================================================ FILE: benchmarks/Nbodies/nbodies_lua.lua ================================================ local pi = 3.141592653589793 local solar_mass = 4 * pi * pi local days_per_year = 365.24 local jupiter = { x=4.84143144246472090e+00, y=-1.16032004402742839e+00, z=-1.03622044471123109e-01, vx=days_per_year * 1.66007664274403694e-03, vy=days_per_year * 7.69901118419740425e-03, vz=days_per_year * -6.90460016972063023e-05, mass=solar_mass * 9.54791938424326609e-04} local saturn = { x=8.34336671824457987e+00, y=4.12479856412430479e+00, z=-4.03523417114321381e-01, vx=days_per_year * -2.76742510726862411e-03, vy=days_per_year * 4.99852801234917238e-03, vz=days_per_year * 2.30417297573763929e-05, mass=solar_mass * 2.85885980666130812e-04} local uranus = { x=1.28943695621391310e+01, y=-1.51111514016986312e+01, z=-2.23307578892655734e-01, vx=days_per_year * 2.96460137564761618e-03, vy=days_per_year * 2.37847173959480950e-03, vz=days_per_year * -2.96589568540237556e-05, mass=solar_mass * 4.36624404335156298e-05} local neptune = { x=1.53796971148509165e+01, y=-2.59193146099879641e+01, z=1.79258772950371181e-01, vx=days_per_year * 2.68067772490389322e-03, vy=days_per_year * 1.62824170038242295e-03, vz=days_per_year * -9.51592254519715870e-05, mass=solar_mass * 5.15138902046611451e-05} local sun = { x=0.0, y=0.0, z=0.0, vx=0.0, vy=0.0, vz=0.0, mass=solar_mass} function body_offset_momentum (b, px, py, pz) b.vx = -px / solar_mass b.vy = -py / solar_mass b.vz = -pz / solar_mass end function bodies_advance(bodies, dt) for i = 1, #bodies do local body0 = bodies[i] for j = i+1, #bodies do local body1 = bodies[j]; local dx = body0.x - body1.x; local dy = body0.y - body1.y; local dz = body0.z - body1.z; local dsquared = dx * dx + dy * dy + dz * dz; local distance = math.sqrt(dsquared); local mag = dt / (dsquared * distance); body0.vx = body0.vx - dx * body1.mass * mag; body0.vy = body0.vy -dy * body1.mass * mag; body0.vz = body0.vz -dz * body1.mass * mag; body1.vx = body1.vx + dx * body0.mass * mag; body1.vy = body1.vy + dy * body0.mass * mag; body1.vz = body1.vz + dz * body0.mass * mag; end end for i = 1, #bodies do body = bodies[i]; body.x = body.x + dt * body.vx; body.y = body.y + dt * body.vy; body.z = body.z + dt * body.vz; end end function bodies_energy(bodies) local dx = 0.0; local dy = 0.0; local dz = 0.0; local distance = 0.0; local e = 0.0; for i = 1, #bodies.length do local body0 = bodies[i]; e = e + (0.5 * body0.mass * ( body0.vx * body0.vx + body0.vy * body0.vy + body0.vz * body0.vz)); for j = i+1, #bodies do local body1 = bodies[j]; dx = body0.x - body1.x; dy = body0.y - body1.y; dz = body0.z - body1.z; distance = sqrt(dx * dx + dy * dy + dz * dz); e = e - (body0.mass * body1.mass) / distance; end end return e; end local bodies = {jupiter, saturn, uranus, neptune, sun}; local px = 0.0; local py = 0.0; local pz = 0.0; for i = 1, #bodies do local body = bodies[i]; px = px + body.vx * body.mass; py = py + body.vy * body.mass; pz = pz + body.vz * body.mass; end body_offset_momentum(bodies[1], px, py, pz); for i = 1, 100000 do bodies_advance(bodies, 1e-5) end ================================================ FILE: benchmarks/Nbodies/nbodies_python.py ================================================ import math import sys # Constants pi = 3.141592653589793 solar_mass = 4 * pi * pi days_per_year = 365.24 # Bodies class Body: def __init__(self, x, y, z, vx, vy, vz, mass): self.x = x self.y = y self.z = z self.vx = vx self.vy = vy self.vz = vz self.mass = mass jupiter = Body( 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, days_per_year * 1.66007664274403694e-03, days_per_year * 7.69901118419740425e-03, days_per_year * -6.90460016972063023e-05, solar_mass * 9.54791938424326609e-04 ) saturn = Body( 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, days_per_year * -2.76742510726862411e-03, days_per_year * 4.99852801234917238e-03, days_per_year * 2.30417297573763929e-05, solar_mass * 2.85885980666130812e-04 ) uranus = Body( 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, days_per_year * 2.96460137564761618e-03, days_per_year * 2.37847173959480950e-03, days_per_year * -2.96589568540237556e-05, solar_mass * 4.36624404335156298e-05 ) neptune = Body( 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, days_per_year * 2.68067772490389322e-03, days_per_year * 1.62824170038242295e-03, days_per_year * -9.51592254519715870e-05, solar_mass * 5.15138902046611451e-05 ) sun = Body( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, solar_mass ) def body_offset_momentum(b, px, py, pz): b.vx = -px / solar_mass b.vy = -py / solar_mass b.vz = -pz / solar_mass def bodies_new(): bodies = [jupiter, saturn, uranus, neptune, sun] px = 0.0 py = 0.0 pz = 0.0 for body in bodies: px += body.vx * body.mass py += body.vy * body.mass pz += body.vz * body.mass body_offset_momentum(bodies[0], px, py, pz) return bodies # Simulation def bodies_advance(bodies, dt): for i in xrange(len(bodies)): body0 = bodies[i] for j in xrange(i+1, len(bodies)): body1 = bodies[j] dx = body0.x - body1.x dy = body0.y - body1.y dz = body0.z - body1.z dsquared = dx * dx + dy * dy + dz * dz distance = math.sqrt(dsquared) mag = dt / (dsquared * distance) body0.vx -= dx * body1.mass * mag body0.vy -= dy * body1.mass * mag body0.vz -= dz * body1.mass * mag body1.vx += dx * body0.mass * mag body1.vy += dy * body0.mass * mag body1.vz += dz * body0.mass * mag for body in bodies: body.x += dt * body.vx body.y += dt * body.vy body.z += dt * body.vz def bodies_energy(bodies): dx = 0.0 dy = 0.0 dz = 0.0 distance = 0.0 e = 0.0 for i in xrange(len(bodies)): body0 = bodies[i] e += (0.5 * body0.mass * ( body0.vx * body0.vx + body0.vy * body0.vy + body0.vz * body0.vz)) for j in xrange(i+1, len(bodies)): body1 = bodies[j] dx = body0.x - body1.x dy = body0.y - body1.y dz = body0.z - body1.z distance = math.sqrt(dx * dx + dy * dy + dz * dz) e -= (body0.mass * body1.mass) / distance return e # Run bodies = bodies_new() for i in xrange(100000): bodies_advance(bodies, 1e-5) ================================================ FILE: benchmarks/Nbodies/nbodies_ruby.rb ================================================ SOLAR_MASS = 4 * Math::PI**2 DAYS_PER_YEAR = 365.24 class Planet attr_accessor :x, :y, :z, :vx, :vy, :vz, :mass def initialize(x, y, z, vx, vy, vz, mass) @x, @y, @z = x, y, z @vx, @vy, @vz = vx * DAYS_PER_YEAR, vy * DAYS_PER_YEAR, vz * DAYS_PER_YEAR @mass = mass * SOLAR_MASS end def move_from_i(bodies, nbodies, dt, i) while i < nbodies b2 = bodies[i] dx = @x - b2.x dy = @y - b2.y dz = @z - b2.z distance = Math.sqrt(dx * dx + dy * dy + dz * dz) mag = dt / (distance * distance * distance) b_mass_mag, b2_mass_mag = @mass * mag, b2.mass * mag @vx -= dx * b2_mass_mag @vy -= dy * b2_mass_mag @vz -= dz * b2_mass_mag b2.vx += dx * b_mass_mag b2.vy += dy * b_mass_mag b2.vz += dz * b_mass_mag i += 1 end @x += dt * @vx @y += dt * @vy @z += dt * @vz end end def energy(bodies) e = 0.0 nbodies = bodies.size for i in 0 ... nbodies b = bodies[i] e += 0.5 * b.mass * (b.vx * b.vx + b.vy * b.vy + b.vz * b.vz) for j in (i + 1) ... nbodies b2 = bodies[j] dx = b.x - b2.x dy = b.y - b2.y dz = b.z - b2.z distance = Math.sqrt(dx * dx + dy * dy + dz * dz) e -= (b.mass * b2.mass) / distance end end e end def offset_momentum(bodies) px, py, pz = 0.0, 0.0, 0.0 for b in bodies m = b.mass px += b.vx * m py += b.vy * m pz += b.vz * m end b = bodies[0] b.vx = - px / SOLAR_MASS b.vy = - py / SOLAR_MASS b.vz = - pz / SOLAR_MASS end BODIES = [ # sun Planet.new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0), # jupiter Planet.new( 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, 1.66007664274403694e-03, 7.69901118419740425e-03, -6.90460016972063023e-05, 9.54791938424326609e-04), # saturn Planet.new( 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, -2.76742510726862411e-03, 4.99852801234917238e-03, 2.30417297573763929e-05, 2.85885980666130812e-04), # uranus Planet.new( 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, 2.96460137564761618e-03, 2.37847173959480950e-03, -2.96589568540237556e-05, 4.36624404335156298e-05), # neptune Planet.new( 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, 2.68067772490389322e-03, 1.62824170038242295e-03, -9.51592254519715870e-05, 5.15138902046611451e-05) ] offset_momentum(BODIES) 100000.times do i = 0 while i < BODIES.size b = BODIES[i] b.move_from_i(BODIES, BODIES.size, 1e-5, i + 1) i += 1 end end ================================================ FILE: benchmarks/Sudoku/sudoku_c.c ================================================ /* The MIT License Copyright (c) 2011 by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // This file implements an improved algorithm of Guenter Stertenbrink's suexco.c // (http://magictour.free.fr/suexco.txt). #include #include #include #include /* For Sudoku, there are 9x9x9=729 possible choices (9 numbers to choose for each cell in a 9x9 grid), and 4x9x9=324 constraints with each constraint representing a set of choices that are mutually conflictive with each other. The 324 constraints are classified into 4 categories: 1. row-column where each cell contains only one number 2. box-number where each number appears only once in one 3x3 box 3. row-number where each number appears only once in one row 4. col-number where each number appears only once in one column Each category consists of 81 constraints. We number these constraints from 0 to 323. In this program, for example, constraint 0 requires that the (0,0) cell contains only one number; constraint 81 requires that number 1 appears only once in the upper-left 3x3 box; constraint 162 requires that number 1 appears only once in row 1; constraint 243 requires that number 1 appears only once in column 1. Noting that a constraint is a subset of choices, we may represent a constraint with a binary vector of 729 elements. Thus we have a 729x324 binary matrix M with M(r,c)=1 indicating the constraint c involves choice r. Solving a Sudoku is reduced to finding a subset of choices such that no choices are present in the same constaint. This is equivalent to finding the minimal subset of choices intersecting all constraints, a minimum hitting set problem or a eqivalence of the exact cover problem. The 729x324 binary matrix is a sparse matrix, with each row containing 4 non-zero elements and each column 9 non-zero elements. In practical implementation, we store the coordinate of non-zero elements instead of the binary matrix itself. We use a binary row vector to indicate the constraints that have not been used and use a column vector to keep the number of times a choice has been forbidden. When we set a choice, we will use up 4 constraints and forbid other choices in the 4 constraints. When we make wrong choices, we will find an unused constraint with all choices forbidden, in which case, we have to backtrack to make new choices. Once we understand what the 729x324 matrix represents, the backtracking algorithm itself is easy. A major difference between the algorithm implemented here and Guenter Stertenbrink's suexco.c lies in how to count the number of the available choices for each constraint. Suexco.c computes the count with a loop, while the algorithm here keeps the count in an array. The latter is a little more complex to implement as we have to keep the counts synchronized all the time, but it is 50-100% faster, depending on the input. */ // the sparse representation of the binary matrix typedef struct { uint16_t r[324][9]; // M(r[c][i], c) is a non-zero element uint16_t c[729][4]; // M(r, c[r][j]) is a non-zero element } sdaux_t; // generate the sparse representation of the binary matrix sdaux_t *sd_genmat() { sdaux_t *a; int i, j, k, r, c, c2, r2; int8_t nr[324]; a = calloc(1, sizeof(sdaux_t)); for (i = r = 0; i < 9; ++i) // generate c[729][4] for (j = 0; j < 9; ++j) for (k = 0; k < 9; ++k) // this "9" means each cell has 9 possible numbers a->c[r][0] = 9 * i + j, // row-column constraint a->c[r][1] = (i/3*3 + j/3) * 9 + k + 81, // box-number constraint a->c[r][2] = 9 * i + k + 162, // row-number constraint a->c[r][3] = 9 * j + k + 243, // col-number constraint ++r; for (c = 0; c < 324; ++c) nr[c] = 0; for (r = 0; r < 729; ++r) // generate r[][] from c[][] for (c2 = 0; c2 < 4; ++c2) k = a->c[r][c2], a->r[k][nr[k]++] = r; return a; } // update the state vectors when we pick up choice r; v=1 for setting choice; v=-1 for reverting static inline int sd_update(const sdaux_t *aux, int8_t sr[729], uint8_t sc[324], int r, int v) { int c2, min = 10, min_c = 0; for (c2 = 0; c2 < 4; ++c2) sc[aux->c[r][c2]] += v<<7; for (c2 = 0; c2 < 4; ++c2) { // update # available choices int r2, rr, cc2, c = aux->c[r][c2]; if (v > 0) { // move forward for (r2 = 0; r2 < 9; ++r2) { if (sr[rr = aux->r[c][r2]]++ != 0) continue; // update the row status for (cc2 = 0; cc2 < 4; ++cc2) { int cc = aux->c[rr][cc2]; if (--sc[cc] < min) // update # allowed choices min = sc[cc], min_c = cc; // register the minimum number } } } else { // revert const uint16_t *p; for (r2 = 0; r2 < 9; ++r2) { if (--sr[rr = aux->r[c][r2]] != 0) continue; // update the row status p = aux->c[rr]; ++sc[p[0]]; ++sc[p[1]]; ++sc[p[2]]; ++sc[p[3]]; // update the count array } } } return min<<16 | min_c; // return the col that has been modified and with the minimal available choices } // solve a Sudoku; _s is the standard dot/number representation int sd_solve(const sdaux_t *aux, const char *_s) { int i, j, r, c, r2, dir, cand, n = 0, min, hints = 0; // dir=1: forward; dir=-1: backtrack int8_t sr[729], cr[81]; // sr[r]: # times the row is forbidden by others; cr[i]: row chosen at step i uint8_t sc[324]; // bit 1-7: # allowed choices; bit 8: the constraint has been used or not int16_t cc[81]; // cc[i]: col chosen at step i char out[82]; for (r = 0; r < 729; ++r) sr[r] = 0; // no row is forbidden for (c = 0; c < 324; ++c) sc[c] = 0<<7|9; // 9 allowed choices; no constraint has been used for (i = 0; i < 81; ++i) { int a = _s[i] >= '1' && _s[i] <= '9'? _s[i] - '1' : -1; // number from -1 to 8 if (a >= 0) sd_update(aux, sr, sc, i * 9 + a, 1); // set the choice if (a >= 0) ++hints; // count the number of hints cr[i] = cc[i] = -1, out[i] = _s[i]; } for (i = 0, dir = 1, cand = 10<<16|0, out[81] = 0;;) { while (i >= 0 && i < 81 - hints) { // maximum 81-hints steps if (dir == 1) { min = cand>>16, cc[i] = cand&0xffff; if (min > 1) { for (c = 0; c < 324; ++c) { if (sc[c] < min) { min = sc[c], cc[i] = c; // choose the top constraint if (min <= 1) break; // this is for acceleration; slower without this line } } } if (min == 0 || min == 10) cr[i--] = dir = -1; // backtrack } c = cc[i]; if (dir == -1 && cr[i] >= 0) sd_update(aux, sr, sc, aux->r[c][cr[i]], -1); // revert the choice for (r2 = cr[i] + 1; r2 < 9; ++r2) // search for the choice to make if (sr[aux->r[c][r2]] == 0) break; // found if the state equals 0 if (r2 < 9) { cand = sd_update(aux, sr, sc, aux->r[c][r2], 1); // set the choice cr[i++] = r2; dir = 1; // moving forward } else cr[i--] = dir = -1; // backtrack } if (i < 0) break; for (j = 0; j < i; ++j) r = aux->r[cc[j]][cr[j]], out[r/9] = r%9 + '1'; // print //puts(out); ++n; --i; dir = -1; // backtrack } return n; // return the number of solutions } int main() { sdaux_t *a = sd_genmat(); char buf[1024]; while (fgets(buf, 1024, stdin) != 0) { if (strlen(buf) < 81) continue; sd_solve(a, buf); //putchar('\n'); } free(a); return 0; } ================================================ FILE: benchmarks/Sudoku/sudoku_cello.c ================================================ #include "Cello.h" struct Sudoku { uint16_t r[324][9]; uint16_t c[729][4]; }; static void Sudoku_New(var self, var args) { struct Sudoku* a = self; int i, j, k, r, c, c2; int8_t nr[324]; for (i = r = 0; i < 9; ++i) // generate c[729][4] for (j = 0; j < 9; ++j) for (k = 0; k < 9; ++k) // this "9" means each cell has 9 possible numbers a->c[r][0] = 9 * i + j, // row-column constraint a->c[r][1] = (i/3*3 + j/3) * 9 + k + 81, // box-number constraint a->c[r][2] = 9 * i + k + 162, // row-number constraint a->c[r][3] = 9 * j + k + 243, // col-number constraint ++r; for (c = 0; c < 324; ++c) nr[c] = 0; for (r = 0; r < 729; ++r) // generate r[][] from c[][] for (c2 = 0; c2 < 4; ++c2) k = a->c[r][c2], a->r[k][nr[k]++] = r; } static var Sudoku = Cello(Sudoku, Instance(New, Sudoku_New, NULL)); static int Sudoku_Update( struct Sudoku* aux, int8_t sr[729], uint8_t sc[324], int r, int v) { int c2, min = 10, min_c = 0; for (c2 = 0; c2 < 4; ++c2) sc[aux->c[r][c2]] += v<<7; for (c2 = 0; c2 < 4; ++c2) { // update # available choices int r2, rr, cc2, c = aux->c[r][c2]; if (v > 0) { // move forward for (r2 = 0; r2 < 9; ++r2) { if (sr[rr = aux->r[c][r2]]++ != 0) continue; // update the row status for (cc2 = 0; cc2 < 4; ++cc2) { int cc = aux->c[rr][cc2]; if (--sc[cc] < min) // update # allowed choices min = sc[cc], min_c = cc; // register the minimum number } } } else { // revert const uint16_t *p; for (r2 = 0; r2 < 9; ++r2) { if (--sr[rr = aux->r[c][r2]] != 0) continue; // update the row status p = aux->c[rr]; ++sc[p[0]]; ++sc[p[1]]; ++sc[p[2]]; ++sc[p[3]]; // update the count array } } } return min<<16 | min_c; // return the col that has been modified and with the minimal available choices } // solve a Sudoku; _s is the standard dot/number representation static int Sudoku_Solve(struct Sudoku* aux, const char *_s) { int i, j, r, c, r2, dir, cand, n = 0, min, hints = 0; // dir=1: forward; dir=-1: backtrack int8_t sr[729], cr[81]; // sr[r]: # times the row is forbidden by others; cr[i]: row chosen at step i uint8_t sc[324]; // bit 1-7: # allowed choices; bit 8: the constraint has been used or not int16_t cc[81]; // cc[i]: col chosen at step i char out[82]; for (r = 0; r < 729; ++r) sr[r] = 0; // no row is forbidden for (c = 0; c < 324; ++c) sc[c] = 0<<7|9; // 9 allowed choices; no constraint has been used for (i = 0; i < 81; ++i) { int a = _s[i] >= '1' && _s[i] <= '9'? _s[i] - '1' : -1; // number from -1 to 8 if (a >= 0) Sudoku_Update(aux, sr, sc, i * 9 + a, 1); // set the choice if (a >= 0) ++hints; // count the number of hints cr[i] = cc[i] = -1, out[i] = _s[i]; } for (i = 0, dir = 1, cand = 10<<16|0, out[81] = 0;;) { while (i >= 0 && i < 81 - hints) { // maximum 81-hints steps if (dir == 1) { min = cand>>16, cc[i] = cand&0xffff; if (min > 1) { for (c = 0; c < 324; ++c) { if (sc[c] < min) { min = sc[c], cc[i] = c; // choose the top constraint if (min <= 1) break; // this is for acceleration; slower without this line } } } if (min == 0 || min == 10) cr[i--] = dir = -1; // backtrack } c = cc[i]; if (dir == -1 && cr[i] >= 0) Sudoku_Update(aux, sr, sc, aux->r[c][cr[i]], -1); // revert the choice for (r2 = cr[i] + 1; r2 < 9; ++r2) // search for the choice to make if (sr[aux->r[c][r2]] == 0) break; // found if the state equals 0 if (r2 < 9) { cand = Sudoku_Update(aux, sr, sc, aux->r[c][r2], 1); // set the choice cr[i++] = r2; dir = 1; // moving forward } else cr[i--] = dir = -1; // backtrack } if (i < 0) break; for (j = 0; j < i; ++j) r = aux->r[cc[j]][cr[j]], out[r/9] = r%9 + '1'; // print //puts(out); ++n; --i; dir = -1; // backtrack } return n; // return the number of solutions } int main(int argc, char** argv) { struct Sudoku* a = new(Sudoku); char buf[1024]; while (fgets(buf, 1024, stdin) != 0) { if (strlen(buf) < 81) continue; Sudoku_Solve(a, buf); //putchar('\n'); } del(a); return 0; } ================================================ FILE: benchmarks/Sudoku/sudoku_cpp.cpp ================================================ /* The MIT License Copyright (c) 2011 by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // This file implements an improved algorithm of Guenter Stertenbrink's suexco.c // (http://magictour.free.fr/suexco.txt). #include #include #include #include /* For Sudoku, there are 9x9x9=729 possible choices (9 numbers to choose for each cell in a 9x9 grid), and 4x9x9=324 constraints with each constraint representing a set of choices that are mutually conflictive with each other. The 324 constraints are classified into 4 categories: 1. row-column where each cell contains only one number 2. box-number where each number appears only once in one 3x3 box 3. row-number where each number appears only once in one row 4. col-number where each number appears only once in one column Each category consists of 81 constraints. We number these constraints from 0 to 323. In this program, for example, constraint 0 requires that the (0,0) cell contains only one number; constraint 81 requires that number 1 appears only once in the upper-left 3x3 box; constraint 162 requires that number 1 appears only once in row 1; constraint 243 requires that number 1 appears only once in column 1. Noting that a constraint is a subset of choices, we may represent a constraint with a binary vector of 729 elements. Thus we have a 729x324 binary matrix M with M(r,c)=1 indicating the constraint c involves choice r. Solving a Sudoku is reduced to finding a subset of choices such that no choices are present in the same constaint. This is equivalent to finding the minimal subset of choices intersecting all constraints, a minimum hitting set problem or a eqivalence of the exact cover problem. The 729x324 binary matrix is a sparse matrix, with each row containing 4 non-zero elements and each column 9 non-zero elements. In practical implementation, we store the coordinate of non-zero elements instead of the binary matrix itself. We use a binary row vector to indicate the constraints that have not been used and use a column vector to keep the number of times a choice has been forbidden. When we set a choice, we will use up 4 constraints and forbid other choices in the 4 constraints. When we make wrong choices, we will find an unused constraint with all choices forbidden, in which case, we have to backtrack to make new choices. Once we understand what the 729x324 matrix represents, the backtracking algorithm itself is easy. A major difference between the algorithm implemented here and Guenter Stertenbrink's suexco.c lies in how to count the number of the available choices for each constraint. Suexco.c computes the count with a loop, while the algorithm here keeps the count in an array. The latter is a little more complex to implement as we have to keep the counts synchronized all the time, but it is 50-100% faster, depending on the input. */ // the sparse representation of the binary matrix class sdaux_t { uint16_t r[324][9]; // M(r[c][i], c) is a non-zero element uint16_t c[729][4]; // M(r, c[r][j]) is a non-zero element public: sdaux_t(); int update(int8_t sr[729], uint8_t sc[324], int r, int v); int solve(const char *_s); }; // generate the sparse representation of the binary matrix sdaux_t::sdaux_t() { int i, j, k, r, c, c2, r2; int8_t nr[324]; for (i = r = 0; i < 9; ++i) // generate c[729][4] for (j = 0; j < 9; ++j) for (k = 0; k < 9; ++k) // this "9" means each cell has 9 possible numbers this->c[r][0] = 9 * i + j, // row-column constraint this->c[r][1] = (i/3*3 + j/3) * 9 + k + 81, // box-number constraint this->c[r][2] = 9 * i + k + 162, // row-number constraint this->c[r][3] = 9 * j + k + 243, // col-number constraint ++r; for (c = 0; c < 324; ++c) nr[c] = 0; for (r = 0; r < 729; ++r) // generate r[][] from c[][] for (c2 = 0; c2 < 4; ++c2) k = this->c[r][c2], this->r[k][nr[k]++] = r; } // update the state vectors when we pick up choice r; v=1 for setting choice; v=-1 for reverting int sdaux_t::update(int8_t sr[729], uint8_t sc[324], int r, int v) { int c2, min = 10, min_c = 0; for (c2 = 0; c2 < 4; ++c2) sc[this->c[r][c2]] += v<<7; for (c2 = 0; c2 < 4; ++c2) { // update # available choices int r2, rr, cc2, c = this->c[r][c2]; if (v > 0) { // move forward for (r2 = 0; r2 < 9; ++r2) { if (sr[rr = this->r[c][r2]]++ != 0) continue; // update the row status for (cc2 = 0; cc2 < 4; ++cc2) { int cc = this->c[rr][cc2]; if (--sc[cc] < min) // update # allowed choices min = sc[cc], min_c = cc; // register the minimum number } } } else { // revert const uint16_t *p; for (r2 = 0; r2 < 9; ++r2) { if (--sr[rr = this->r[c][r2]] != 0) continue; // update the row status p = this->c[rr]; ++sc[p[0]]; ++sc[p[1]]; ++sc[p[2]]; ++sc[p[3]]; // update the count array } } } return min<<16 | min_c; // return the col that has been modified and with the minimal available choices } // solve a Sudoku; _s is the standard dot/number representation int sdaux_t::solve(const char *_s) { int i, j, r, c, r2, dir, cand, n = 0, min, hints = 0; // dir=1: forward; dir=-1: backtrack int8_t sr[729], cr[81]; // sr[r]: # times the row is forbidden by others; cr[i]: row chosen at step i uint8_t sc[324]; // bit 1-7: # allowed choices; bit 8: the constraint has been used or not int16_t cc[81]; // cc[i]: col chosen at step i char out[82]; for (r = 0; r < 729; ++r) sr[r] = 0; // no row is forbidden for (c = 0; c < 324; ++c) sc[c] = 0<<7|9; // 9 allowed choices; no constraint has been used for (i = 0; i < 81; ++i) { int a = _s[i] >= '1' && _s[i] <= '9'? _s[i] - '1' : -1; // number from -1 to 8 if (a >= 0) this->update(sr, sc, i * 9 + a, 1); // set the choice if (a >= 0) ++hints; // count the number of hints cr[i] = cc[i] = -1, out[i] = _s[i]; } for (i = 0, dir = 1, cand = 10<<16|0, out[81] = 0;;) { while (i >= 0 && i < 81 - hints) { // maximum 81-hints steps if (dir == 1) { min = cand>>16, cc[i] = cand&0xffff; if (min > 1) { for (c = 0; c < 324; ++c) { if (sc[c] < min) { min = sc[c], cc[i] = c; // choose the top constraint if (min <= 1) break; // this is for acceleration; slower without this line } } } if (min == 0 || min == 10) cr[i--] = dir = -1; // backtrack } c = cc[i]; if (dir == -1 && cr[i] >= 0) this->update(sr, sc, this->r[c][cr[i]], -1); // revert the choice for (r2 = cr[i] + 1; r2 < 9; ++r2) // search for the choice to make if (sr[this->r[c][r2]] == 0) break; // found if the state equals 0 if (r2 < 9) { cand = this->update(sr, sc, this->r[c][r2], 1); // set the choice cr[i++] = r2; dir = 1; // moving forward } else cr[i--] = dir = -1; // backtrack } if (i < 0) break; for (j = 0; j < i; ++j) r = this->r[cc[j]][cr[j]], out[r/9] = r%9 + '1'; // print //puts(out); ++n; --i; dir = -1; // backtrack } return n; // return the number of solutions } int main() { sdaux_t *a = new sdaux_t(); char buf[1024]; while (fgets(buf, 1024, stdin) != 0) { if (strlen(buf) < 81) continue; a->solve(buf); //putchar('\n'); } delete a; return 0; } ================================================ FILE: benchmarks/Sudoku/sudoku_cs.cs ================================================ using System; class sudoku_v1 { int[,] R, C; public void genmat() { R = new int[324,9]; C = new int[729,4]; int[] nr = new int[324]; int i, j, k, r, c, c2; for (i = r = 0; i < 9; ++i) // generate c[729][4] for (j = 0; j < 9; ++j) for (k = 0; k < 9; ++k) { // this "9" means each cell has 9 possible numbers C[r,0] = 9 * i + j; // row-column constraint C[r,1] = (i/3*3 + j/3) * 9 + k + 81; // box-number constraint C[r,2] = 9 * i + k + 162; // row-number constraint C[r,3] = 9 * j + k + 243; // col-number constraint ++r; } for (c = 0; c < 324; ++c) nr[c] = 0; for (r = 0; r < 729; ++r) // generate r[][] from c[][] for (c2 = 0; c2 < 4; ++c2) { k = C[r,c2]; R[k,nr[k]] = r; nr[k]++; } } private int sd_update(int[] sr, int[] sc, int r, int v) { int c2, min = 10, min_c = 0; for (c2 = 0; c2 < 4; ++c2) sc[C[r,c2]] += v<<7; for (c2 = 0; c2 < 4; ++c2) { // update # available choices int r2, rr, cc2, c = C[r,c2]; if (v > 0) { // move forward for (r2 = 0; r2 < 9; ++r2) { if (sr[rr = R[c,r2]]++ != 0) continue; // update the row status for (cc2 = 0; cc2 < 4; ++cc2) { int cc = C[rr,cc2]; if (--sc[cc] < min) { // update # allowed choices min = sc[cc]; min_c = cc; // register the minimum number } } } } else { // revert for (r2 = 0; r2 < 9; ++r2) { if (--sr[rr = R[c,r2]] != 0) continue; // update the row status ++sc[C[rr,0]]; ++sc[C[rr,1]]; ++sc[C[rr,2]]; ++sc[C[rr,3]]; // update the count array } } } return min<<16 | min_c; // return the col that has been modified and with the minimal available choices } // solve a Sudoku; _s is the standard dot/number representation public int solve(String _s) { int i, j, r, c, r2, dir, cand, n = 0, min, hints = 0; // dir=1: forward; dir=-1: backtrack int[] sr = new int[729]; int[] cr = new int[81]; int[] sc = new int[324]; int[] cc = new int[81]; int[] o = new int[81]; for (r = 0; r < 729; ++r) sr[r] = 0; // no row is forbidden for (c = 0; c < 324; ++c) sc[c] = 0<<7|9; // 9 allowed choices; no constraint has been used for (i = 0; i < 81; ++i) { int a = _s[i] >= '1' && _s[i] <= '9'? _s[i] - '1' : -1; // number from -1 to 8 if (a >= 0) sd_update(sr, sc, i * 9 + a, 1); // set the choice if (a >= 0) ++hints; // count the number of hints cr[i] = cc[i] = -1; o[i] = a; } i = 0; dir = 1; cand = 10<<16|0; for (;;) { while (i >= 0 && i < 81 - hints) { // maximum 81-hints steps if (dir == 1) { min = cand>>16; cc[i] = cand&0xffff; if (min > 1) { for (c = 0; c < 324; ++c) { if (sc[c] < min) { min = sc[c]; cc[i] = c; // choose the top constraint if (min <= 1) break; // this is for acceleration; slower without this line } } } if (min == 0 || min == 10) cr[i--] = dir = -1; // backtrack } c = cc[i]; if (dir == -1 && cr[i] >= 0) sd_update(sr, sc, R[c,cr[i]], -1); // revert the choice for (r2 = cr[i] + 1; r2 < 9; ++r2) // search for the choice to make if (sr[R[c,r2]] == 0) break; // found if the state equals 0 if (r2 < 9) { cand = sd_update(sr, sc, R[c,r2], 1); // set the choice cr[i++] = r2; dir = 1; // moving forward } else cr[i--] = dir = -1; // backtrack } if (i < 0) break; char[] y = new char[81]; for (j = 0; j < 81; ++j) y[i] = (char)(o[i] + '1'); for (j = 0; j < i; ++j) { r = R[cc[j],cr[j]]; y[r/9] = (char)(r%9 + '1'); } Console.WriteLine(new String(y)); ++n; --i; dir = -1; // backtrack } return n; } public static void Main(String[] args) { sudoku_v1 a = new sudoku_v1(); String l; a.genmat(); while ((l = Console.ReadLine()) != null) if (l.Length >= 81) { a.solve(l); Console.WriteLine(); } } } ================================================ FILE: benchmarks/Sudoku/sudoku_d.d ================================================ // Works with LDC1 // Compile with: ldc -O3 -release -inline // Contributed by leonardo /* The MIT License Copyright (c) 2011 by Attractive Chaos leonardo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // This implements an improved algorithm of Guenter Stertenbrink's suexco.c // (http://magictour.free.fr/suexco.txt). import tango.stdc.stdio: puts, fgets, stdin, putchar; import tango.stdc.string: strlen; /// the sparse representation of the binary matrix struct Sdaux { int[9][324] r; // M(r[c][i], c) is a non-zero element int[4][729] c; // M(r, c[r][j]) is a non-zero element /// generate the sparse representation of the binary matrix void initialize() { // static this() is slower // generate this.c { int r1 = 0; for (int i = 0; i < 9; i++) for (int j = 0; j < 9; j++) for (int k = 0; k < 9; k++) { // this "9" means each cell has 9 possible numbers c[r1][] = [9 * i + j, (i/3*3 + j/3) * 9 + k + 81, 9 * i + k + 162, 9 * j + k + 243]; r1++; } } // generate this.r from this.c byte[r.length] nr; foreach (ushort r2, ref rowc; this.c) foreach (c2, int k; rowc) { this.r[k][nr[k]] = r2; nr[k]++; } } } /// update the state vectors when we pick up choice r; v=1 for setting choice; v=-1 for reverting int sdUpdate(in Sdaux* aux, byte[Sdaux.c.length] sr, ubyte[Sdaux.r.length] sc, in int r, in int v) { int min = 10, min_c; for (size_t c2 = 0; c2 < aux.c[0].length; c2++) sc[aux.c[r][c2]] += v << 7; for (size_t c2 = 0; c2 < aux.c[0].length; c2++) { // update # available choices int c = aux.c[r][c2]; int rr; if (v > 0) { // move forward for (size_t r2 = 0; r2 < 9; r2++) { if (sr[rr = aux.r[c][r2]]++ != 0) continue; // update the row status for (size_t cc2 = 0; cc2 < aux.c[0].length; cc2++) { int cc = aux.c[rr][cc2]; sc[cc]--; if (sc[cc] < min) { // update # allowed choices min = sc[cc]; // register the minimum number min_c = cc; } } } } else { // revert for (size_t r2 = 0; r2 < 9; r2++) { if (--sr[rr = aux.r[c][r2]] != 0) continue; // update the row status auto p = aux.c[rr].ptr; sc[p[0]]++; // update the count array sc[p[1]]++; sc[p[2]]++; sc[p[3]]++; } } } return (min << 16) | min_c; // return the col that has been modified and // with the minimal available choices } /// solve a Sudoku; _s is the standard dot/number representation int sdSolve(in Sdaux* aux, in char* _s) { int hints; byte[Sdaux.c.length] sr; // sr[r]: # times the row is forbidden by others byte[81] cr = -1; // cr[i]: row chosen at step i ubyte[Sdaux.r.length] sc = 9; // bit 1-7: # allowed choices; // bit 8: the constraint has been used or not // 9 allowed choices; no constraint has been used short[cr.length] cc = -1; // cc[i]: col chosen at step i char[82] outs; for (int i = 0; i < cr.length; i++) { int a = (_s[i] >= '1' && _s[i] <= '9') ? (_s[i] - '1') : -1; // number from -1 to 8 if (a >= 0) sdUpdate(aux, sr, sc, i * 9 + a, 1); // set the choice if (a >= 0) hints++; // count the number of hints outs[i] = _s[i]; } int dir; // dir=1: forward; dir=-1: backtrack int i, r, cand, n, min; for (i = 0, dir = 1, cand = (10 << 16) | 0, outs[81] = 0; ; ) { while (i >= 0 && i < 81 - hints) { // maximum 81-hints steps if (dir == 1) { min = cand >> 16; cc[i] = cand & 0xFFFF; if (min > 1) { for (size_t c = 0; c < sc.length; c++) { // performance-critical loop if (sc[c] < min) { min = sc[c]; // choose the top constraint cc[i] = cast(short)c; if (min <= 1) break; // this is for acceleration; slower without this line } } } if (min == 0 || min == 10) { dir = cr[i] = -1; // backtrack i--; } } int c = cc[i]; if (dir == -1 && cr[i] >= 0) sdUpdate(aux, sr, sc, aux.r[c][cr[i]], -1); // revert the choice int r2; for (r2 = cr[i] + 1; r2 < 9; r2++) // search for the choice to make if (sr[aux.r[c][r2]] == 0) break; // found if the state equals 0 if (r2 < 9) { cand = sdUpdate(aux, sr, sc, aux.r[c][r2], 1); // set the choice cr[i] = cast(byte)r2; // moving forward. r2 < 9 i++; dir = 1; } else { dir = cr[i] = -1; // backtrack i--; } } if (i < 0) break; for (size_t j = 0; j < i; j++) { r = aux.r[cc[j]][cr[j]]; outs[r / 9] = cast(char)(r % 9 + '1'); // print } puts(outs.ptr); n++; // backtrack i--; dir = -1; } return n; // return the number of solutions } void main() { Sdaux a; a.initialize(); char[1024] buf; while (fgets(buf.ptr, buf.length, stdin) != null) { if (strlen(buf.ptr) < 81) continue; sdSolve(&a, buf.ptr); putchar('\n'); } } ================================================ FILE: benchmarks/Sudoku/sudoku_dart.dart ================================================ import 'dart:core'; import 'dart:io'; import 'dart:typeddata'; class Sudoku { final _R = new List(324); final _C = new List>(729); Sudoku() { final Uint8List nr = new Uint8List(324); for (int i = 0, r = 0; i < 9; ++i) for (int j = 0; j < 9; ++j) for (int k = 0; k < 9; ++k) _C[r++] = [ 9 * i + j, (i~/3*3 + j~/3) * 9 + k + 81, 9 * i + k + 162, 9 * j + k + 243 ]; for (int c = 0; c < 324; ++c) { _R[c] = new Uint16List(9); nr[c] = 0; } for (int r = 0; r < 729; ++r) for (int c2 = 0; c2 < 4; ++c2) { int k = _C[r][c2]; _R[k][nr[k]++] = r; } } _update(sr, sc, r, v) { int min = 10, min_c = 0; for (int c2 = 0; c2 < 4; ++c2) sc[_C[r][c2]] += v<<7; for (int c2 = 0; c2 < 4; ++c2) { int r2, rr, cc2, c = _C[r][c2]; if (v > 0) { for (r2 = 0; r2 < 9; ++r2) { if (sr[rr = _R[c][r2]]++ != 0) continue; for (cc2 = 0; cc2 < 4; ++cc2) { var cc = _C[rr][cc2]; if (--sc[cc] < min) { min = sc[cc]; min_c = cc; } } } } else { // revert for (r2 = 0; r2 < 9; ++r2) { if (--sr[rr = _R[c][r2]] != 0) continue; var p = _C[rr]; ++sc[p[0]]; ++sc[p[1]]; ++sc[p[2]]; ++sc[p[3]]; } } } return min<<16 | min_c; // return the col that has been modified and with the minimal available choices } solve(s_) { int j, r, c, r2, min, hints = 0; var sr = new Int16List(729), sc = new Int16List(324); var cr = new Int16List(81), cc = new Int16List(81), out = new Int16List(81); List ret = []; for (r = 0; r < 729; ++r) sr[r] = 0; for (c = 0; c < 324; ++c) sc[c] = 9; for (int i = 0; i < 81; ++i) { int a = s_.codeUnitAt(i) >= 49 && s_.codeUnitAt(i) <= 57? s_.codeUnitAt(i) - 49 : -1; if (a >= 0) _update(sr, sc, i * 9 + a, 1); if (a >= 0) ++hints; cr[i] = cc[i] = -1; out[i] = a + 1; } for (int i = 0, dir = 1, cand = 10<<16|0;;) { while (i >= 0 && i < 81 - hints) { if (dir == 1) { min = cand>>16; cc[i] = cand&0xffff; if (min > 1) { for (c = 0; c < 324; ++c) { if (sc[c] < min) { min = sc[c]; cc[i] = c; if (min <= 1) break; } } } if (min == 0 || min == 10) cr[i--] = dir = -1; } c = cc[i]; if (dir == -1 && cr[i] >= 0) _update(sr, sc, _R[c][cr[i]], -1); for (r2 = cr[i] + 1; r2 < 9; ++r2) if (sr[_R[c][r2]] == 0) break; if (r2 < 9) { cand = _update(sr, sc, _R[c][r2], 1); cr[i++] = r2; dir = 1; } else cr[i--] = dir = -1; } if (i < 0) break; Uint8List y = new Uint8List(81); for (j = 0; j < 81; ++j) y[j] = out[j]; for (j = 0; j < i; ++j) { r = _R[cc[j]][cr[j]]; y[r~/9] = r%9 + 1; } ret.add(y.join()); --i; dir = -1; } return ret; } } main() { final List argv = new Options().arguments; final fp = new File(argv[0]); final lines = fp.readAsLinesSync(encoding: Encoding.ASCII); final Sudoku s = new Sudoku(); for (int i = 0; i < lines.length; ++i) { var rst = s.solve(lines[i]); for (final x in rst) print(x); print(""); } } ================================================ FILE: benchmarks/Sudoku/sudoku_go.go ================================================ package main import ( "bufio" "fmt" "os" ) type sdaux_t struct { r [324][9]uint16 c [729][4]uint16 } func sd_genmat() *sdaux_t { a := new(sdaux_t) nr := make([]int8, 324) r := 0 for i := 0; i < 9; i++ { for j := 0; j < 9; j++ { for k := 0; k < 9; k++ { a.c[r][0] = uint16(9*i + j) a.c[r][1] = uint16((i/3*3+j/3)*9 + k + 81) a.c[r][2] = uint16(9*i + k + 162) a.c[r][3] = uint16(9*j + k + 243) r++ } } } for c := 0; c < 324; c++ { nr[c] = 0 } for r := 0; r < 729; r++ { for c2 := 0; c2 < 4; c2++ { k := a.c[r][c2] a.r[k][nr[k]] = uint16(r) nr[k]++ } } return a } func sd_update_forward(aux *sdaux_t, sr []int8, sc []uint8, r uint16) int { min, min_c := uint8(10), uint16(0) rows := &aux.c[r] for _, c := range rows { sc[c] |= 0x80 } for _, c := range rows { for _, rr := range &aux.r[c] { sr[rr]++ if sr[rr] != 1 { continue } for _, cc := range &aux.c[rr] { v := sc[cc] - 1 sc[cc] = v if v < min { min, min_c = v, cc } } } } return int(min)<<16 | int(min_c) } func sd_update_revert(aux *sdaux_t, sr []int8, sc []uint8, r uint16) { rows := &aux.c[r] for _, c := range rows { sc[c] &= 0x7f } for _, c := range rows { for _, rr := range &aux.r[c] { sr[rr]-- if sr[rr] != 0 { continue } for _, cc := range &aux.c[rr] { sc[cc]++ } // unroll this loop makes no difference } } } func sd_solve(aux *sdaux_t, _s []byte) int { sr := make([]int8, 729) cr := make([]int8, 81) sc := make([]uint8, 324) cc := make([]int16, 81) out := make([]byte, 81) n, hints := 0, 0 for c := 0; c < 324; c++ { sc[c] = 9 } for i := 0; i < 81; i++ { a := -1 if _s[i] >= '1' && _s[i] <= '9' { a = int(_s[i] - '1') } if a >= 0 { sd_update_forward(aux, sr, sc, uint16(i*9+a)) hints++ } cr[i], cc[i], out[i] = -1, -1, _s[i] } i, dir, cand := 0, 1, 10<<16 for { for i >= 0 && i < 81-hints { if dir == 1 { min := uint8(cand >> 16) cc[i] = int16(cand & 0xffff) if min > 1 { for c, scc := range sc { if scc < min { min, cc[i] = scc, int16(c) if min <= 1 { break } } } } if min == 0 || min == 10 { cr[i], dir = -1, -1 i-- } } c := cc[i] if dir == -1 && cr[i] >= 0 { sd_update_revert(aux, sr, sc, aux.r[c][cr[i]]) } r2_ := 9 for r2 := cr[i] + 1; r2 < 9; r2++ { if sr[aux.r[c][r2]] == 0 { r2_ = int(r2) break } } if r2_ < 9 { cand = sd_update_forward(aux, sr, sc, aux.r[c][r2_]) cr[i], dir = int8(r2_), 1 i++ } else { cr[i], dir = -1, -1 i-- } } if i < 0 { break } for j := 0; j < i; j++ { r := aux.r[cc[j]][cr[j]] out[r/9] = byte(r%9) + '1' } fmt.Println(string(out)) n++ i-- dir = -1 } return n } func main() { a := sd_genmat() r := bufio.NewReader(os.Stdin) for { l, e := r.ReadSlice('\n') if e != nil { break } if len(l) > 81 { sd_solve(a, l) fmt.Println("") } } } ================================================ FILE: benchmarks/Sudoku/sudoku_java.java ================================================ import java.io.*; class sudoku_java { int[][] R, C; public void genmat() { R = new int[324][9]; C = new int[729][4]; int[] nr = new int[324]; int i, j, k, r, c, c2, r2; for (i = r = 0; i < 9; ++i) // generate c[729][4] for (j = 0; j < 9; ++j) for (k = 0; k < 9; ++k) { // this "9" means each cell has 9 possible numbers C[r][0] = 9 * i + j; // row-column constraint C[r][1] = (i/3*3 + j/3) * 9 + k + 81; // box-number constraint C[r][2] = 9 * i + k + 162; // row-number constraint C[r][3] = 9 * j + k + 243; // col-number constraint ++r; } for (c = 0; c < 324; ++c) nr[c] = 0; for (r = 0; r < 729; ++r) // generate r[][] from c[][] for (c2 = 0; c2 < 4; ++c2) { k = C[r][c2]; R[k][nr[k]++] = r; } } private int sd_update(int[] sr, int[] sc, int r, int v) { int c2, min = 10, min_c = 0; for (c2 = 0; c2 < 4; ++c2) sc[C[r][c2]] += v<<7; for (c2 = 0; c2 < 4; ++c2) { // update # available choices int r2, rr, cc2, c = C[r][c2]; if (v > 0) { // move forward for (r2 = 0; r2 < 9; ++r2) { if (sr[rr = R[c][r2]]++ != 0) continue; // update the row status for (cc2 = 0; cc2 < 4; ++cc2) { int cc = C[rr][cc2]; if (--sc[cc] < min) { // update # allowed choices min = sc[cc]; min_c = cc; // register the minimum number } } } } else { // revert int[] p; for (r2 = 0; r2 < 9; ++r2) { if (--sr[rr = R[c][r2]] != 0) continue; // update the row status p = C[rr]; ++sc[p[0]]; ++sc[p[1]]; ++sc[p[2]]; ++sc[p[3]]; // update the count array } } } return min<<16 | min_c; // return the col that has been modified and with the minimal available choices } // solve a Sudoku; _s is the standard dot/number representation public int solve(String _s) { int i, j, r, c, r2, dir, cand, n = 0, min, hints = 0; // dir=1: forward; dir=-1: backtrack int[] sr = new int[729]; int[] cr = new int[81]; int[] sc = new int[324]; int[] cc = new int[81]; int[] out = new int[81]; for (r = 0; r < 729; ++r) sr[r] = 0; // no row is forbidden for (c = 0; c < 324; ++c) sc[c] = 0<<7|9; // 9 allowed choices; no constraint has been used for (i = 0; i < 81; ++i) { int a = _s.charAt(i) >= '1' && _s.charAt(i) <= '9'? _s.codePointAt(i) - '1' : -1; // number from -1 to 8 if (a >= 0) sd_update(sr, sc, i * 9 + a, 1); // set the choice if (a >= 0) ++hints; // count the number of hints cr[i] = cc[i] = -1; out[i] = a; } i = 0; dir = 1; cand = 10<<16|0; for (;;) { while (i >= 0 && i < 81 - hints) { // maximum 81-hints steps if (dir == 1) { min = cand>>16; cc[i] = cand&0xffff; if (min > 1) { for (c = 0; c < 324; ++c) { if (sc[c] < min) { min = sc[c]; cc[i] = c; // choose the top constraint if (min <= 1) break; // this is for acceleration; slower without this line } } } if (min == 0 || min == 10) cr[i--] = dir = -1; // backtrack } c = cc[i]; if (dir == -1 && cr[i] >= 0) sd_update(sr, sc, R[c][cr[i]], -1); // revert the choice for (r2 = cr[i] + 1; r2 < 9; ++r2) // search for the choice to make if (sr[R[c][r2]] == 0) break; // found if the state equals 0 if (r2 < 9) { cand = sd_update(sr, sc, R[c][r2], 1); // set the choice cr[i++] = r2; dir = 1; // moving forward } else cr[i--] = dir = -1; // backtrack } if (i < 0) break; char[] y = new char[81]; for (j = 0; j < 81; ++j) y[j] = (char)(out[j] + '1'); for (j = 0; j < i; ++j) { r = R[cc[j]][cr[j]]; y[r/9] = (char)(r%9 + '1'); } //System.out.println(new String(y)); ++n; --i; dir = -1; // backtrack } return n; } public static void main(String[] args) throws Exception { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); sudoku_java a = new sudoku_java(); String l; a.genmat(); while ((l = stdin.readLine()) != null) if (l.length() >= 81) { a.solve(l); //System.out.println(); } } } ================================================ FILE: benchmarks/Sudoku/sudoku_javascript.js ================================================ function Sudoku() { var C = [], R = []; function sd_genmat() { // precompute matrix var i, j, r, c, c2; for (i = r = 0; i < 9; ++i) for (j = 0; j < 9; ++j) for (k = 0; k < 9; ++k) C[r++] = [ 9 * i + j, (Math.floor(i/3)*3 + Math.floor(j/3)) * 9 + k + 81, 9 * i + k + 162, 9 * j + k + 243 ] for (c = 0; c < 324; ++c) R[c] = [] for (r = 0; r < 729; ++r) for (c2 = 0; c2 < 4; ++c2) R[C[r][c2]].push(r) } function sd_update(sr, sc, r, v) { // update var min = 10, min_c = 0; for (var c2 = 0; c2 < 4; ++c2) sc[C[r][c2]] += v<<7; for (var c2 = 0; c2 < 4; ++c2) { var r2, rr, cc2, c = C[r][c2]; if (v > 0) { for (r2 = 0; r2 < 9; ++r2) { if (sr[rr = R[c][r2]]++ != 0) continue; for (cc2 = 0; cc2 < 4; ++cc2) { var cc = C[rr][cc2]; if (--sc[cc] < min) min = sc[cc], min_c = cc; } } } else { // revert for (r2 = 0; r2 < 9; ++r2) { if (--sr[rr = R[c][r2]] != 0) continue; var p = C[rr] ++sc[p[0]]; ++sc[p[1]]; ++sc[p[2]]; ++sc[p[3]]; } } } return min<<16 | min_c; // return the col that has been modified and with the minimal available choices } sd_genmat(); return function(_s) { // closure, the actual solver var i, j, r, c, r2, min, cand, dir, hints = 0; var sr = [], sc = [], cr = [], cc = [], out = [], ret = []; for (r = 0; r < 729; ++r) sr[r] = 0; for (c = 0; c < 324; ++c) sc[c] = 9; for (i = 0; i < 81; ++i) { var a = _s.charAt(i) >= '1' && _s.charAt(i) <= '9'? _s.charCodeAt(i) - 49 : -1; if (a >= 0) sd_update(sr, sc, i * 9 + a, 1); if (a >= 0) ++hints; cr[i] = cc[i] = -1, out[i] = a + 1; } for (i = 0, dir = 1, cand = 10<<16|0;;) { while (i >= 0 && i < 81 - hints) { if (dir == 1) { min = cand>>16, cc[i] = cand&0xffff; if (min > 1) { for (c = 0; c < 324; ++c) { if (sc[c] < min) { min = sc[c], cc[i] = c; if (min <= 1) break; } } } if (min == 0 || min == 10) cr[i--] = dir = -1; } c = cc[i]; if (dir == -1 && cr[i] >= 0) sd_update(sr, sc, R[c][cr[i]], -1); for (r2 = cr[i] + 1; r2 < 9; ++r2) if (sr[R[c][r2]] == 0) break; if (r2 < 9) { cand = sd_update(sr, sc, R[c][r2], 1); cr[i++] = r2; dir = 1; } else cr[i--] = dir = -1; } if (i < 0) break; var y = []; for (j = 0; j < 81; ++j) y[j] = out[j]; for (j = 0; j < i; ++j) r = R[cc[j]][cr[j]], y[Math.floor(r/9)] = r%9 + 1; ret.push(y); --i; dir = -1; } return ret; } } var solver = Sudoku(); function processLine (l) { if (l.length >= 81) { var r = solver(l) //for (var i = 0; i < r.length; ++i) print(r[i].join('')+'\n') } } var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); rl.on('line', function(line){ processLine(line); }) ================================================ FILE: benchmarks/Sudoku/sudoku_lua.lua ================================================ function sd_genmat() local R, C, r = {}, {}, 0 for i = 0, 8 do for j = 0, 8 do for k = 0, 8 do C[r], r = { 9 * i + j, math.floor(i/3)*27 + math.floor(j/3)*9 + k + 81, 9 * i + k + 162, 9 * j + k + 243 }, r + 1 end end end for c = 0, 323 do R[c] = {} end for r = 0, 728 do for c2 = 1, 4 do table.insert(R[C[r][c2]], r) end end return R, C end function sd_update(R, C, sr, sc, r, v) local min, min_c = 10, 0 for c2 = 1, 4 do if v > 0 then sc[C[r][c2]] = sc[C[r][c2]] + 128 else sc[C[r][c2]] = sc[C[r][c2]] - 128 end end for c2 = 1, 4 do local c = C[r][c2] if v > 0 then for r2 = 1, 9 do local rr = R[c][r2] sr[rr] = sr[rr] + 1 if sr[rr] == 1 then for cc2 = 1, 4 do local cc = C[rr][cc2] sc[cc] = sc[cc] - 1 if sc[cc] < min then min, min_c = sc[cc], cc end end end end else for r2 = 1, 9 do local rr = R[c][r2] sr[rr] = sr[rr] - 1 if sr[rr] == 0 then local p = C[rr] sc[p[1]], sc[p[2]], sc[p[3]], sc[p[4]] = sc[p[1]]+1, sc[p[2]]+1, sc[p[3]]+1, sc[p[4]]+1 end end end end return min, min_c end function sd_solve(R, C, s) local sr, sc, cr, cc, hints = {}, {}, {}, {}, 0 for r = 0, 728 do sr[r] = 0 end for c = 0, 323 do sc[c] = 9 end for i = 0, 80 do local t = s:byte(i+1) local a = t >= 49 and t <= 57 and t - 49 or -1 if a >= 0 then sd_update(R, C, sr, sc, i * 9 + a, 1); hints = hints + 1 end cr[i], cc[i] = 0, 0 end local i, min, dir, ret = 0, 10, 1, {} while true do while i >= 0 and i < 81 - hints do if dir == 1 then if min > 1 then for c = 0, 323 do if sc[c] < min then min, cc[i] = sc[c], c if min < 2 then break end end end end if min == 0 or min == 10 then cr[i], dir, i = 0, -1, i - 1 end end local c, r2_ = cc[i], 10 if dir == -1 and cr[i] > 0 then sd_update(R, C, sr, sc, R[c][cr[i]], -1) end for r2 = cr[i] + 1, 9 do if sr[R[c][r2]] == 0 then r2_ = r2; break end end if r2_ < 10 then min, cc[i+1] = sd_update(R, C, sr, sc, R[c][r2_], 1) cr[i], dir, i = r2_, 1, i + 1 else cr[i], dir, i = 0, -1, i - 1 end end if i < 0 then break end local y = {} for j = 1, 81 do y[j] = s:byte(j) - 48 end for j = 0, i - 1 do r = R[cc[j]][cr[j]] y[math.floor(r/9)+1] = math.fmod(r, 9) + 1 end ret[#ret+1] = y dir, i = -1, i - 1 end return ret end local R, C = sd_genmat() for l in io.lines() do if #l >= 81 then local ret = sd_solve(R, C, l) --for _, v in ipairs(ret) do print(table.concat(v)) end --print() end end ================================================ FILE: benchmarks/Sudoku/sudoku_perl.pl ================================================ use strict; use warnings; my @aux = &sd_genmat(); while (<>) { chomp; if (length($_) >= 81) { my $ret = &sd_solve($aux[0], $aux[1], $_); print($_, "\n") for (@$ret); print("\n"); } } sub sd_genmat() { my (@C, @R); for my $i (0..8) { for my $j (0..8) { for my $k (0..8) { push(@C, [9*$i+$j, int($i/3)*27+int($j/3)*9+$k+81, 9*$i+$k+162, 9*$j+$k+243]) } } } for my $r (0..728) { for my $k (@{$C[$r]}) { push(@{$R[$k]}, $r); } } return (\@R, \@C); } sub sd_update() { my ($R, $C, $sr, $sc, $r, $v) = @_; my ($min, $min_c) = (10, 0); for my $c (@{$C->[$r]}) { if ($v > 0) { $sc->[$c] += 128; } else { $sc->[$c] -= 128; } } for my $c (@{$C->[$r]}) { if ($v > 0) { for my $rr (@{$R->[$c]}) { ++$sr->[$rr]; next if ($sr->[$rr] != 1); for my $cc (@{$C->[$rr]}) { if (--$sc->[$cc] < $min) { $min = $sc->[$cc]; $min_c = $cc; } } } } else { for my $rr (@{$R->[$c]}) { --$sr->[$rr]; next if ($sr->[$rr] != 0); my $p = $C->[$rr]; ++$sc->[$p->[0]]; ++$sc->[$p->[1]]; ++$sc->[$p->[2]]; ++$sc->[$p->[3]]; } } } return $min<<16 | $min_c; } sub sd_solve() { my ($R, $C, $s) = @_; my (@sr, @sc, @cr, @cc, @ret); my $hints = 0; for my $r (0..728) { $sr[$r] = 0; } for my $c (0..323) { $sc[$c] = 9; } for my $i (0..80) { my $a = substr($s, $i, 1) =~ /[1-9]/? ord(substr($s, $i, 1)) - 49 : -1; if ($a >= 0) { &sd_update($R, $C, \@sr, \@sc, $i*9+$a, 1); ++$hints; } $cr[$i] = $cc[$i] = -1; } #for my $i (0..323) {print("$i\t$sc[$i]\n")} my ($i, $dir, $cand) = (0, 1, 10<<16); for (;;) { while ($i >= 0 && $i < 81 - $hints) { if ($dir == 1) { my $min = $cand>>16; $cc[$i] = $cand&0xffff; if ($min > 1) { for my $c (0..323) { if ($sc[$c] < $min) { $min = $sc[$c]; $cc[$i] = $c; last if ($min <= 1); } } } if ($min == 0 || $min == 10) { $cr[$i--] = $dir = -1; } } my $c = $cc[$i]; &sd_update($R, $C, \@sr, \@sc, $R->[$c][$cr[$i]], -1) if ($dir == -1 && $cr[$i] >= 0); my $r2; for ($r2 = $cr[$i] + 1; $r2 < 9; ++$r2) { last if ($sr[$R->[$c][$r2]] == 0); } if ($r2 < 9) { $cand = &sd_update($R, $C, \@sr, \@sc, $R->[$c][$r2], 1); $cr[$i++] = $r2; $dir = 1; } else { $cr[$i--] = $dir = -1; } } last if ($i < 0); for my $j (0..$i-1) { my $r = $R->[$cc[$j]][$cr[$j]]; substr($s, int($r/9), 1) = $r%9 + 1; } push(@ret, $s); --$i; $dir = -1; } return \@ret; } ================================================ FILE: benchmarks/Sudoku/sudoku_python.py ================================================ import sys, string try: xrange(1) except NameError: xrange = range def sd_genmat(): C = [[n/9, n/81*9 + n%9 + 81, n%81 + 162, n%9*9 + n/243*3 + n/27%3 + 243] for n in range(729)] R = [[] for c in xrange(324)] for r in xrange(729): for c2 in xrange(4): R[C[r][c2]].append(r) return R, C def sd_update(R, C, sr, sc, r, v): m = 10 m_c = 0 for c in C[r]: sc[c] += v<<7 for c in C[r]: if v > 0: for rr in R[c]: sr[rr] += 1 if sr[rr] == 1: for cc in C[rr]: sc[cc] -= 1 if sc[cc] < m: m, m_c = sc[cc], cc else: for rr in R[c]: sr[rr] -= 1 if sr[rr] == 0: p = C[rr] sc[p[0]] += 1; sc[p[1]] += 1; sc[p[2]] += 1; sc[p[3]] += 1; return m, m_c def sd_solve(R, C, s): ret, out, hints = [], [], 0 sr = [0] * 729 sc = [9] * 324 cr = [-1] * 81 cc = [-1] * 81 for i in xrange(81): if ord(s[i]) >= 49 and ord(s[i]) <= 57: a = ord(s[i]) - 49 else: a = -1 if a >= 0: sd_update(R, C, sr, sc, i * 9 + a, 1) hints += 1 out.append(a + 1) i, m, d = 0, 10, 1 while True: while i >= 0 and i < 81 - hints: if d == 1: if m > 1: for c in xrange(324): # using enumerate() here is slower if sc[c] < m: m, cc[i] = sc[c], c if m < 2: break if m == 0 or m == 10: cr[i], d = -1, -1 i -= 1 c = cc[i] if d == -1 and cr[i] >= 0: sd_update(R, C, sr, sc, R[c][cr[i]], -1) r2_ = 9 for r2 in xrange(cr[i] + 1, 9): if sr[R[c][r2]] == 0: r2_ = r2; break if r2_ < 9: m, cc[i+1] = sd_update(R, C, sr, sc, R[c][r2], 1) cr[i], d = r2, 1 i += 1 else: cr[i], d = -1, -1 i -= 1 if i < 0: break y = out[:81] for j in xrange(i): r = R[cc[j]][cr[j]] y[r//9] = r%9 + 1 ret.append(y) i -= 1 d = -1 return ret R, C = sd_genmat() for line in sys.stdin: if len(line) >= 81: ret = sd_solve(R, C, line) #for j in ret: # print(''.join(map(str, j))) #print('') ================================================ FILE: benchmarks/Sudoku/sudoku_ruby.rb ================================================ def sd_genmat() mr = Array.new(324) { [] } mc = Array.new(729) { [] } r = 0 (0...9).each do |i| (0...9).each do |j| (0...9).each do |k| mc[r] = [ 9 * i + j, (i/3*3 + j/3) * 9 + k + 81, 9 * i + k + 162, 9 * j + k + 243 ] r += 1 end end end (0...729).each do |r| (0...4).each do |c2| mr[mc[r][c2]].push(r) end end return mr, mc end def sd_update(mr, mc, sr, sc, r, v) min, min_c = 10, 0 (0...4).each do |c2| if v > 0 then sc[mc[r][c2]] += 128 else sc[mc[r][c2]] -= 128 end end (0...4).each do |c2| c = mc[r][c2] if v > 0 then (0...9).each do |r2| rr = mr[c][r2] sr[rr] += + 1 if sr[rr] == 1 then p = mc[rr] sc[p[0]] -= 1; sc[p[1]] -= 1; sc[p[2]] -= 1; sc[p[3]] -= 1 if sc[p[0]] < min then min, min_c = sc[p[0]], p[0] end if sc[p[1]] < min then min, min_c = sc[p[1]], p[1] end if sc[p[2]] < min then min, min_c = sc[p[2]], p[2] end if sc[p[3]] < min then min, min_c = sc[p[3]], p[3] end end end else (0...9).each do |r2| rr = mr[c][r2] sr[rr] -= 1 if sr[rr] == 0 then p = mc[rr] sc[p[0]] += 1; sc[p[1]] += 1; sc[p[2]] += 1; sc[p[3]] += 1 end end end end return min, min_c end def sd_solve(mr, mc, s) ret, sr, sc, hints = [], Array.new(729) { 0 }, Array.new(324) { 9 }, 0 (0...81).each do |i| a = (s[i].chr >= '1' and s[i].chr <= '9')? s[i].ord - 49 : -1 if a >= 0 then sd_update(mr, mc, sr, sc, i * 9 + a, 1); hints += 1 end end cr, cc = Array.new(81) { -1 }, Array.new(81) { 0 } i, min, dir = 0, 10, 1 loop do while i >= 0 and i < 81 - hints do if dir == 1 then if min > 1 then (0...324).each do |c| if sc[c] < min then min, cc[i] = sc[c], c if min < 2 then break end end end end if min == 0 or min == 10 then cr[i], dir, i = -1, -1, i - 1 end end c = cc[i] if dir == -1 and cr[i] >= 0 then sd_update(mr, mc, sr, sc, mr[c][cr[i]], -1) end r2_ = 9 (cr[i]+1...9).each do |r2| if sr[mr[c][r2]] == 0 then r2_ = r2; break end end if r2_ < 9 then min, cc[i+1] = sd_update(mr, mc, sr, sc, mr[c][r2_], 1) cr[i], dir, i = r2_, 1, i + 1 else cr[i], dir, i = -1, -1, i - 1 end end if i < 0 then break end o = [] (0...81).each do |j| o.push(s[j].ord - 49) end (0...i).each do |j| r = mr[cc[j]][cr[j]] o[r/9] = r % 9 + 1 end ret.push(o) i, dir = i - 1, -1 end return ret end mr, mc = sd_genmat() STDIN.each do |line| if line.length >= 81 then ret = sd_solve(mr, mc, line) #ret.each do |s| puts s.join end #puts end end ================================================ FILE: benchmarks/benchmark ================================================ gcc ./ext/genint.c -o ./ext/genint gcc Nbodies/nbodies_c.c -std=c99 -O3 -lm -o Nbodies/nbodies_c g++ Nbodies/nbodies_cpp.cpp -std=c++11 -O3 -lm -o Nbodies/nbodies_cpp gcc Nbodies/nbodies_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -std=gnu99 -pg -O3 -lm -lpthread -o Nbodies/nbodies_cello javac Nbodies/nbodies_java.java gcc List/list_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o List/list_c g++ List/list_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o List/list_cpp gcc List/list_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -pg -O3 -lm -lpthread -o List/list_cello javac List/list_java.java gcc Dict/dict_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Dict/dict_c g++ Dict/dict_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Dict/dict_cpp gcc Dict/dict_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -pg -O3 -lm -lpthread -o Dict/dict_cello javac Dict/dict_java.java gcc Map/map_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Map/map_c g++ Map/map_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Map/map_cpp gcc Map/map_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -pg -O3 -lm -lpthread -o Map/map_cello javac Map/map_java.java gcc Sudoku/sudoku_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Sudoku/sudoku_c g++ Sudoku/sudoku_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Sudoku/sudoku_cpp gcc Sudoku/sudoku_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -pg -O3 -lm -lpthread -o Sudoku/sudoku_cello javac Sudoku/sudoku_java.java gcc Matmul/matmul_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Matmul/matmul_c g++ Matmul/matmul_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Matmul/matmul_cpp gcc Matmul/matmul_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -pg -O3 -lm -lpthread -o Matmul/matmul_cello javac Matmul/matmul_java.java gcc GC/gc_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o GC/gc_c g++ GC/gc_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o GC/gc_cpp gcc GC/gc_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -std=gnu99 -O3 -lm -lpthread -o GC/gc_cello javac GC/gc_java.java echo echo "## Garbage Collection" echo echo -n "* C: " time -f "%e" ./GC/gc_c echo -n "* C++: " time -f "%e" ./GC/gc_cpp echo -n "* Cello: " time -f "%e" ./GC/gc_cello echo -n "* Java: " time -f "%e" java -cp ./GC gc_java echo -n "* Javascript: " time -f "%e" nodejs GC/gc_javascript.js echo -n "* Python: " time -f "%e" python GC/gc_python.py echo -n "* Ruby: " time -f "%e" ruby GC/gc_ruby.rb echo -n "* Lua: " time -f "%e" lua GC/gc_lua.lua echo -n "* Lua JIT: " time -f "%e" luajit GC/gc_lua.lua gprof GC/gc_cello > GC/profile.txt rm gmon.out echo echo "## List" echo echo -n "* C: " time -f "%e" ./List/list_c echo -n "* C++: " time -f "%e" ./List/list_cpp echo -n "* Cello: " time -f "%e" ./List/list_cello echo -n "* Java: " time -f "%e" java -cp ./List list_java echo -n "* Javascript: " time -f "%e" nodejs List/list_javascript.js echo -n "* Python: " time -f "%e" python List/list_python.py echo -n "* Ruby: " time -f "%e" ruby List/list_ruby.rb echo -n "* Lua: " time -f "%e" lua List/list_lua.lua echo -n "* Lua JIT: " time -f "%e" luajit List/list_lua.lua gprof List/list_cello > List/profile.txt rm gmon.out echo echo "## Map" echo echo -n "* C: " time -f "%e" sh -c './ext/genint | ./Map/map_c' echo -n "* C++: " time -f "%e" sh -c './ext/genint | ./Map/map_cpp' echo -n "* Cello: " time -f "%e" sh -c './ext/genint | ./Map/map_cello' echo -n "* Java: " time -f "%e" sh -c './ext/genint | java -cp ./Map map_java' echo -n "* Javascript: " time -f "%e" sh -c './ext/genint | nodejs Map/map_javascript.js' echo -n "* Python: " time -f "%e" sh -c './ext/genint | python Map/map_python.py' echo -n "* Ruby: " time -f "%e" sh -c './ext/genint | ruby Map/map_ruby.rb' echo -n "* Lua: " time -f "%e" sh -c './ext/genint | lua Map/map_lua.lua' echo -n "* Lua JIT: " time -f "%e" sh -c './ext/genint | luajit Map/map_lua.lua' gprof Map/map_cello > Map/profile.txt rm gmon.out echo echo "## NBodies" echo echo -n "* C: " time -f "%e" ./Nbodies/nbodies_c echo -n "* C++: " time -f "%e" ./Nbodies/nbodies_cpp echo -n "* Cello: " time -f "%e" ./Nbodies/nbodies_cello echo -n "* Java: " time -f "%e" java -cp ./Nbodies nbodies_java echo -n "* Javascript: " time -f "%e" nodejs Nbodies/nbodies_javascript.js echo -n "* Python: " time -f "%e" python Nbodies/nbodies_python.py echo -n "* Ruby: " time -f "%e" ruby Nbodies/nbodies_ruby.rb echo -n "* Lua: " time -f "%e" lua Nbodies/nbodies_lua.lua echo -n "* Lua JIT: " time -f "%e" luajit Nbodies/nbodies_lua.lua gprof Nbodies/nbodies_cello > Nbodies/profile.txt rm gmon.out echo echo "## Dict" echo echo -n "* C: " time -f "%e" sh -c './ext/genint | ./Dict/dict_c' echo -n "* C++: " time -f "%e" sh -c './ext/genint | ./Dict/dict_cpp' echo -n "* Cello: " time -f "%e" sh -c './ext/genint | ./Dict/dict_cello' echo -n "* Java: " time -f "%e" sh -c './ext/genint | java -cp ./Dict dict_java' echo -n "* Javascript: " time -f "%e" sh -c './ext/genint | nodejs Dict/dict_javascript.js' echo -n "* Python: " time -f "%e" sh -c './ext/genint | python Dict/dict_python.py' echo -n "* Ruby: " time -f "%e" sh -c './ext/genint | ruby Dict/dict_ruby.rb' echo -n "* Lua: " time -f "%e" sh -c './ext/genint | lua Dict/dict_lua.lua' echo -n "* Lua JIT: " time -f "%e" sh -c './ext/genint | luajit Dict/dict_lua.lua' gprof Dict/dict_cello > Dict/profile.txt rm gmon.out echo echo "## Sudoku" echo echo -n "* C: " time -f "%e" sh -c './ext/sudoku | ./Sudoku/sudoku_c' echo -n "* C++: " time -f "%e" sh -c './ext/sudoku | ./Sudoku/sudoku_cpp' echo -n "* Cello: " time -f "%e" sh -c './ext/sudoku | ./Sudoku/sudoku_cello' echo -n "* Java: " time -f "%e" sh -c './ext/sudoku | java -cp ./Sudoku sudoku_java' echo -n "* Javascript: " time -f "%e" sh -c './ext/sudoku | nodejs Sudoku/sudoku_javascript.js' echo -n "* Python: " time -f "%e" sh -c './ext/sudoku | python Sudoku/sudoku_python.py' echo -n "* Ruby: " time -f "%e" sh -c './ext/sudoku | ruby Sudoku/sudoku_ruby.rb' echo -n "* Lua: " time -f "%e" sh -c './ext/sudoku | lua Sudoku/sudoku_lua.lua' echo -n "* Lua JIT: " time -f "%e" sh -c './ext/sudoku | luajit Sudoku/sudoku_lua.lua' gprof Sudoku/sudoku_cello > Sudoku/profile.txt rm gmon.out echo echo "## Matmul" echo echo -n "* C: " time -f "%e" ./Matmul/matmul_c echo -n "* C++: " time -f "%e" ./Matmul/matmul_cpp echo -n "* Cello: " time -f "%e" ./Matmul/matmul_cello echo -n "* Java: " time -f "%e" java -cp ./Matmul matmul_java echo -n "* Javascript: " time -f "%e" nodejs Matmul/matmul_javascript.js echo -n "* Python: " time -f "%e" python Matmul/matmul_python.py echo -n "* Ruby: " time -f "%e" ruby Matmul/matmul_ruby.rb echo -n "* Lua: " time -f "%e" lua Matmul/matmul_lua.lua echo -n "* Lua JIT: " time -f "%e" luajit Matmul/matmul_lua.lua gprof Matmul/matmul_cello > Matmul/profile.txt rm gmon.out ================================================ FILE: benchmarks/benchmark.sh ================================================ #!/bin/sh gcc ./ext/genint.c -o ./ext/genint gcc Nbodies/nbodies_c.c -std=c99 -O3 -lm -o Nbodies/nbodies_c g++ Nbodies/nbodies_cpp.cpp -std=c++11 -O3 -lm -o Nbodies/nbodies_cpp cc Nbodies/nbodies_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -std=gnu99 -O3 -lm -lpthread -o Nbodies/nbodies_cello javac Nbodies/nbodies_java.java gcc List/list_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o List/list_c g++ List/list_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o List/list_cpp cc List/list_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -O3 -lm -lpthread -o List/list_cello javac List/list_java.java gcc Dict/dict_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Dict/dict_c g++ Dict/dict_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Dict/dict_cpp cc Dict/dict_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -O3 -lm -lpthread -o Dict/dict_cello javac Dict/dict_java.java gcc Map/map_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Map/map_c g++ Map/map_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Map/map_cpp cc Map/map_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -O3 -lm -lpthread -o Map/map_cello javac Map/map_java.java gcc Sudoku/sudoku_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Sudoku/sudoku_c g++ Sudoku/sudoku_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Sudoku/sudoku_cpp cc Sudoku/sudoku_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -O3 -lm -lpthread -o Sudoku/sudoku_cello javac Sudoku/sudoku_java.java gcc Matmul/matmul_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o Matmul/matmul_c g++ Matmul/matmul_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o Matmul/matmul_cpp cc Matmul/matmul_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -Wno-unused-result -std=gnu99 -O3 -lm -lpthread -o Matmul/matmul_cello javac Matmul/matmul_java.java gcc GC/gc_c.c -Wno-unused-result -I./ext -std=c99 -O3 -lm -o GC/gc_c g++ GC/gc_cpp.cpp -Wno-unused-result -std=c++11 -O3 -lm -o GC/gc_cpp cc GC/gc_cello.c -DCELLO_NDEBUG ../libCello.a -I../include -std=gnu99 -O3 -lm -lpthread -o GC/gc_cello javac GC/gc_java.java echo echo "## Garbage Collection" echo printf "* C: " gtime -f "%e" -f "%e" ./GC/gc_c printf "* C++: " gtime -f "%e" -f "%e" ./GC/gc_cpp printf "* Cello: " gtime -f "%e" -f "%e" ./GC/gc_cello printf "* Java: " gtime -f "%e" -f "%e" java -cp ./GC gc_java printf "* Javascript: " gtime -f "%e" -f "%e" node GC/gc_javascript.js printf "* Python: " gtime -f "%e" -f "%e" python GC/gc_python.py printf "* Ruby: " gtime -f "%e" -f "%e" ruby GC/gc_ruby.rb printf "* Lua: " gtime -f "%e" -f "%e" lua GC/gc_lua.lua # printf "* Lua JIT: " # gtime -f "%e" -f "%e" luajit GC/gc_lua.lua # gprof GC/gc_cello > GC/profile.txt # rm gmon.out echo echo "## List" echo printf "* C: " gtime -f "%e" -f "%e" ./List/list_c printf "* C++: " gtime -f "%e" -f "%e" ./List/list_cpp printf "* Cello: " gtime -f "%e" -f "%e" ./List/list_cello printf "* Java: " gtime -f "%e" -f "%e" java -cp ./List list_java printf "* Javascript: " gtime -f "%e" -f "%e" node List/list_javascript.js printf "* Python: " gtime -f "%e" -f "%e" python List/list_python.py printf "* Ruby: " gtime -f "%e" -f "%e" ruby List/list_ruby.rb printf "* Lua: " gtime -f "%e" -f "%e" lua List/list_lua.lua # printf "* Lua JIT: " # gtime -f "%e" -f "%e" luajit List/list_lua.lua # gprof List/list_cello > List/profile.txt # rm gmon.out echo echo "## Map" echo printf "* C: " gtime -f "%e" -f "%e" sh -c './ext/genint | ./Map/map_c' printf "* C++: " gtime -f "%e" -f "%e" sh -c './ext/genint | ./Map/map_cpp' printf "* Cello: " gtime -f "%e" -f "%e" sh -c './ext/genint | ./Map/map_cello' printf "* Java: " gtime -f "%e" -f "%e" sh -c './ext/genint | java -cp ./Map map_java' printf "* Javascript: " gtime -f "%e" -f "%e" sh -c './ext/genint | node Map/map_javascript.js' printf "* Python: " gtime -f "%e" -f "%e" sh -c './ext/genint | python Map/map_python.py' printf "* Ruby: " gtime -f "%e" -f "%e" sh -c './ext/genint | ruby Map/map_ruby.rb' printf "* Lua: " gtime -f "%e" -f "%e" sh -c './ext/genint | lua Map/map_lua.lua' # printf "* Lua JIT: " # gtime -f "%e" -f "%e" sh -c './ext/genint | luajit Map/map_lua.lua' # gprof Map/map_cello > Map/profile.txt # rm gmon.out echo echo "## NBodies" echo printf "* C: " gtime -f "%e" -f "%e" ./Nbodies/nbodies_c printf "* C++: " gtime -f "%e" -f "%e" ./Nbodies/nbodies_cpp printf "* Cello: " gtime -f "%e" -f "%e" ./Nbodies/nbodies_cello printf "* Java: " gtime -f "%e" -f "%e" java -cp ./Nbodies nbodies_java printf "* Javascript: " gtime -f "%e" -f "%e" node Nbodies/nbodies_javascript.js printf "* Python: " gtime -f "%e" -f "%e" python Nbodies/nbodies_python.py printf "* Ruby: " gtime -f "%e" -f "%e" ruby Nbodies/nbodies_ruby.rb printf "* Lua: " gtime -f "%e" -f "%e" lua Nbodies/nbodies_lua.lua # printf "* Lua JIT: " # gtime -f "%e" -f "%e" luajit Nbodies/nbodies_lua.lua # gprof Nbodies/nbodies_cello > Nbodies/profile.txt # rm gmon.out echo echo "## Dict" echo printf "* C: " gtime -f "%e" -f "%e" sh -c './ext/genint | ./Dict/dict_c' printf "* C++: " gtime -f "%e" -f "%e" sh -c './ext/genint | ./Dict/dict_cpp' printf "* Cello: " gtime -f "%e" -f "%e" sh -c './ext/genint | ./Dict/dict_cello' printf "* Java: " gtime -f "%e" -f "%e" sh -c './ext/genint | java -cp ./Dict dict_java' printf "* Javascript: " gtime -f "%e" -f "%e" sh -c './ext/genint | node Dict/dict_javascript.js' printf "* Python: " gtime -f "%e" -f "%e" sh -c './ext/genint | python Dict/dict_python.py' printf "* Ruby: " gtime -f "%e" -f "%e" sh -c './ext/genint | ruby Dict/dict_ruby.rb' printf "* Lua: " gtime -f "%e" -f "%e" sh -c './ext/genint | lua Dict/dict_lua.lua' # printf "* Lua JIT: " # gtime -f "%e" -f "%e" sh -c './ext/genint | luajit Dict/dict_lua.lua' # gprof Dict/dict_cello > Dict/profile.txt # rm gmon.out echo echo "## Sudoku" echo printf "* C: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | ./Sudoku/sudoku_c' printf "* C++: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | ./Sudoku/sudoku_cpp' printf "* Cello: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | ./Sudoku/sudoku_cello' printf "* Java: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | java -cp ./Sudoku sudoku_java' printf "* Javascript: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | node Sudoku/sudoku_javascript.js' printf "* Python: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | python Sudoku/sudoku_python.py' printf "* Ruby: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | ruby Sudoku/sudoku_ruby.rb' printf "* Lua: " gtime -f "%e" -f "%e" sh -c './ext/sudoku | lua Sudoku/sudoku_lua.lua' # printf "* Lua JIT: " # gtime -f "%e" -f "%e" sh -c './ext/sudoku | luajit Sudoku/sudoku_lua.lua' # gprof Sudoku/sudoku_cello > Sudoku/profile.txt # rm gmon.out echo echo "## Matmul" echo printf "* C: " gtime -f "%e" -f "%e" ./Matmul/matmul_c printf "* C++: " gtime -f "%e" -f "%e" ./Matmul/matmul_cpp printf "* Cello: " gtime -f "%e" -f "%e" ./Matmul/matmul_cello printf "* Java: " gtime -f "%e" -f "%e" java -cp ./Matmul matmul_java printf "* Javascript: " gtime -f "%e" -f "%e" node Matmul/matmul_javascript.js printf "* Python: " gtime -f "%e" -f "%e" python Matmul/matmul_python.py printf "* Ruby: " gtime -f "%e" -f "%e" ruby Matmul/matmul_ruby.rb printf "* Lua: " gtime -f "%e" -f "%e" lua Matmul/matmul_lua.lua # printf "* Lua JIT: " # gtime -f "%e" -f "%e" luajit Matmul/matmul_lua.lua # gprof Matmul/matmul_cello > Matmul/profile.txt # rm gmon.out ================================================ FILE: benchmarks/ext/cleantxt.c ================================================ #include #include #include int main(int argc, char *argv[]) { FILE *fp; int c = EOF; if (argc == 1) { fprintf(stderr, "Usage: cleantxt \n"); return 1; } fp = strcmp(argv[1], "-")? fopen(argv[1], "rb") : stdin; if (fp == 0) { fprintf(stderr, "ERROR: fail to open the input file.\n"); return 1; } while (!feof(fp)) { c = fgetc(fp); if (isgraph(c) || c == '\t' || c == '\n' || c == ' ') fputc(c, stdout); } if (c != '\n') fputc('\n', stdout); fclose(fp); return 0; } ================================================ FILE: benchmarks/ext/genint.c ================================================ /* This program generates 5 million recurrent integers. Each distinct integer * occurs 4 times in average. */ #include #include int main(int argc, char *argv[]) { int i, n = 500000; // by default, output 5 million integers if (argc > 1) n = atoi(argv[1]); srand(11); for (i = 0; i < n; ++i) printf("%u\n", (unsigned)((rand() % (n/4)) * 271828183u)); // 4 times in average return 0; } ================================================ FILE: benchmarks/ext/kbtree.h ================================================ /*- * Copyright 1997-1999, 2001, John-Mark Gurney. * 2008-2009, Attractive Chaos * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __AC_KBTREE_H #define __AC_KBTREE_H #include #include #include typedef struct { int32_t is_internal:1, n:31; } kbnode_t; #define __KB_KEY(type, x) ((type*)((char*)x + 4)) #define __KB_PTR(btr, x) ((kbnode_t**)((char*)x + btr->off_ptr)) #define __KB_TREE_T(name) \ typedef struct { \ kbnode_t *root; \ int off_key, off_ptr, ilen, elen; \ int n, t; \ int n_keys, n_nodes; \ } kbtree_##name##_t; #define __KB_INIT(name, key_t) \ kbtree_##name##_t *kb_init_##name(int size) \ { \ kbtree_##name##_t *b; \ b = (kbtree_##name##_t*)calloc(1, sizeof(kbtree_##name##_t)); \ b->t = ((size - 4 - sizeof(void*)) / (sizeof(void*) + sizeof(key_t)) + 1) >> 1; \ if (b->t < 2) { \ free(b); return 0; \ } \ b->n = 2 * b->t - 1; \ b->off_ptr = 4 + b->n * sizeof(key_t); \ b->ilen = (4 + sizeof(void*) + b->n * (sizeof(void*) + sizeof(key_t)) + 3) >> 2 << 2; \ b->elen = (b->off_ptr + 3) >> 2 << 2; \ b->root = (kbnode_t*)calloc(1, b->ilen); \ ++b->n_nodes; \ return b; \ } #define __kb_destroy(b) do { \ int i, max = 8; \ kbnode_t *x, **top, **stack = 0; \ if (b) { \ top = stack = (kbnode_t**)calloc(max, sizeof(kbnode_t*)); \ *top++ = (b)->root; \ while (top != stack) { \ x = *--top; \ if (x->is_internal == 0) { free(x); continue; } \ for (i = 0; i <= x->n; ++i) \ if (__KB_PTR(b, x)[i]) { \ if (top - stack == max) { \ max <<= 1; \ stack = (kbnode_t**)realloc(stack, max * sizeof(kbnode_t*)); \ top = stack + (max>>1); \ } \ *top++ = __KB_PTR(b, x)[i]; \ } \ free(x); \ } \ } \ free(b); free(stack); \ } while (0) #define __kb_get_first(key_t, b, ret) do { \ kbnode_t *__x = (b)->root; \ while (__KB_PTR(b, __x)[0] != 0) \ __x = __KB_PTR(b, __x)[0]; \ (ret) = __KB_KEY(key_t, __x)[0]; \ } while (0) #define __KB_GET_AUX0(name, key_t, __cmp) \ static inline int __kb_get_aux_##name(const kbnode_t * __restrict x, const key_t * __restrict k, int *r) \ { \ int tr, *rr, begin, end, n = x->n >> 1; \ if (x->n == 0) return -1; \ if (__cmp(*k, __KB_KEY(key_t, x)[n]) < 0) { \ begin = 0; end = n; \ } else { begin = n; end = x->n - 1; } \ rr = r? r : &tr; \ n = end; \ while (n >= begin && (*rr = __cmp(*k, __KB_KEY(key_t, x)[n])) < 0) --n; \ return n; \ } #define __KB_GET_AUX1(name, key_t, __cmp) \ static inline int __kb_getp_aux_##name(const kbnode_t * __restrict x, const key_t * __restrict k, int *r) \ { \ int tr, *rr, begin = 0, end = x->n; \ if (x->n == 0) return -1; \ rr = r? r : &tr; \ while (begin < end) { \ int mid = (begin + end) >> 1; \ if (__cmp(__KB_KEY(key_t, x)[mid], *k) < 0) begin = mid + 1; \ else end = mid; \ } \ if (begin == x->n) { *rr = 1; return x->n - 1; } \ if ((*rr = __cmp(*k, __KB_KEY(key_t, x)[begin])) < 0) --begin; \ return begin; \ } #define __KB_GET(name, key_t) \ static key_t *kb_getp_##name(kbtree_##name##_t *b, const key_t * __restrict k) \ { \ int i, r = 0; \ kbnode_t *x = b->root; \ while (x) { \ i = __kb_getp_aux_##name(x, k, &r); \ if (i >= 0 && r == 0) return &__KB_KEY(key_t, x)[i]; \ if (x->is_internal == 0) return 0; \ x = __KB_PTR(b, x)[i + 1]; \ } \ return 0; \ } \ static inline key_t *kb_get_##name(kbtree_##name##_t *b, const key_t k) \ { \ return kb_getp_##name(b, &k); \ } #define __KB_INTERVAL(name, key_t) \ static void kb_intervalp_##name(kbtree_##name##_t *b, const key_t * __restrict k, key_t **lower, key_t **upper) \ { \ int i, r = 0; \ kbnode_t *x = b->root; \ *lower = *upper = 0; \ while (x) { \ i = __kb_getp_aux_##name(x, k, &r); \ if (i >= 0 && r == 0) { \ *lower = *upper = &__KB_KEY(key_t, x)[i]; \ return; \ } \ if (i >= 0) *lower = &__KB_KEY(key_t, x)[i]; \ if (i < x->n - 1) *upper = &__KB_KEY(key_t, x)[i + 1]; \ if (x->is_internal == 0) return; \ x = __KB_PTR(b, x)[i + 1]; \ } \ } \ static inline void kb_interval_##name(kbtree_##name##_t *b, const key_t k, key_t **lower, key_t **upper) \ { \ kb_intervalp_##name(b, &k, lower, upper); \ } #define __KB_PUT(name, key_t, __cmp) \ /* x must be an internal node */ \ static void __kb_split_##name(kbtree_##name##_t *b, kbnode_t *x, int i, kbnode_t *y) \ { \ kbnode_t *z; \ z = (kbnode_t*)calloc(1, y->is_internal? b->ilen : b->elen); \ ++b->n_nodes; \ z->is_internal = y->is_internal; \ z->n = b->t - 1; \ memcpy(__KB_KEY(key_t, z), __KB_KEY(key_t, y) + b->t, sizeof(key_t) * (b->t - 1)); \ if (y->is_internal) memcpy(__KB_PTR(b, z), __KB_PTR(b, y) + b->t, sizeof(void*) * b->t); \ y->n = b->t - 1; \ memmove(__KB_PTR(b, x) + i + 2, __KB_PTR(b, x) + i + 1, sizeof(void*) * (x->n - i)); \ __KB_PTR(b, x)[i + 1] = z; \ memmove(__KB_KEY(key_t, x) + i + 1, __KB_KEY(key_t, x) + i, sizeof(key_t) * (x->n - i)); \ __KB_KEY(key_t, x)[i] = __KB_KEY(key_t, y)[b->t - 1]; \ ++x->n; \ } \ static void __kb_putp_aux_##name(kbtree_##name##_t *b, kbnode_t *x, const key_t * __restrict k) \ { \ int i = x->n - 1; \ if (x->is_internal == 0) { \ i = __kb_getp_aux_##name(x, k, 0); \ if (i != x->n - 1) \ memmove(__KB_KEY(key_t, x) + i + 2, __KB_KEY(key_t, x) + i + 1, (x->n - i - 1) * sizeof(key_t)); \ __KB_KEY(key_t, x)[i + 1] = *k; \ ++x->n; \ } else { \ i = __kb_getp_aux_##name(x, k, 0) + 1; \ if (__KB_PTR(b, x)[i]->n == 2 * b->t - 1) { \ __kb_split_##name(b, x, i, __KB_PTR(b, x)[i]); \ if (__cmp(*k, __KB_KEY(key_t, x)[i]) > 0) ++i; \ } \ __kb_putp_aux_##name(b, __KB_PTR(b, x)[i], k); \ } \ } \ static void kb_putp_##name(kbtree_##name##_t *b, const key_t * __restrict k) \ { \ kbnode_t *r, *s; \ ++b->n_keys; \ r = b->root; \ if (r->n == 2 * b->t - 1) { \ ++b->n_nodes; \ s = (kbnode_t*)calloc(1, b->ilen); \ b->root = s; s->is_internal = 1; s->n = 0; \ __KB_PTR(b, s)[0] = r; \ __kb_split_##name(b, s, 0, r); \ r = s; \ } \ __kb_putp_aux_##name(b, r, k); \ } \ static inline void kb_put_##name(kbtree_##name##_t *b, const key_t k) \ { \ kb_putp_##name(b, &k); \ } #define __KB_DEL(name, key_t) \ static key_t __kb_delp_aux_##name(kbtree_##name##_t *b, kbnode_t *x, const key_t * __restrict k, int s) \ { \ int yn, zn, i, r = 0; \ kbnode_t *xp, *y, *z; \ key_t kp; \ if (x == 0) return *k; \ if (s) { /* s can only be 0, 1 or 2 */ \ r = x->is_internal == 0? 0 : s == 1? 1 : -1; \ i = s == 1? x->n - 1 : -1; \ } else i = __kb_getp_aux_##name(x, k, &r); \ if (x->is_internal == 0) { \ if (s == 2) ++i; \ kp = __KB_KEY(key_t, x)[i]; \ memmove(__KB_KEY(key_t, x) + i, __KB_KEY(key_t, x) + i + 1, (x->n - i - 1) * sizeof(key_t)); \ --x->n; \ return kp; \ } \ if (r == 0) { \ if ((yn = __KB_PTR(b, x)[i]->n) >= b->t) { \ xp = __KB_PTR(b, x)[i]; \ kp = __KB_KEY(key_t, x)[i]; \ __KB_KEY(key_t, x)[i] = __kb_delp_aux_##name(b, xp, 0, 1); \ return kp; \ } else if ((zn = __KB_PTR(b, x)[i + 1]->n) >= b->t) { \ xp = __KB_PTR(b, x)[i + 1]; \ kp = __KB_KEY(key_t, x)[i]; \ __KB_KEY(key_t, x)[i] = __kb_delp_aux_##name(b, xp, 0, 2); \ return kp; \ } else if (yn == b->t - 1 && zn == b->t - 1) { \ y = __KB_PTR(b, x)[i]; z = __KB_PTR(b, x)[i + 1]; \ __KB_KEY(key_t, y)[y->n++] = *k; \ memmove(__KB_KEY(key_t, y) + y->n, __KB_KEY(key_t, z), z->n * sizeof(key_t)); \ if (y->is_internal) memmove(__KB_PTR(b, y) + y->n, __KB_PTR(b, z), (z->n + 1) * sizeof(void*)); \ y->n += z->n; \ memmove(__KB_KEY(key_t, x) + i, __KB_KEY(key_t, x) + i + 1, (x->n - i - 1) * sizeof(key_t)); \ memmove(__KB_PTR(b, x) + i + 1, __KB_PTR(b, x) + i + 2, (x->n - i - 1) * sizeof(void*)); \ --x->n; \ free(z); \ return __kb_delp_aux_##name(b, y, k, s); \ } \ } \ ++i; \ if ((xp = __KB_PTR(b, x)[i])->n == b->t - 1) { \ if (i > 0 && (y = __KB_PTR(b, x)[i - 1])->n >= b->t) { \ memmove(__KB_KEY(key_t, xp) + 1, __KB_KEY(key_t, xp), xp->n * sizeof(key_t)); \ if (xp->is_internal) memmove(__KB_PTR(b, xp) + 1, __KB_PTR(b, xp), (xp->n + 1) * sizeof(void*)); \ __KB_KEY(key_t, xp)[0] = __KB_KEY(key_t, x)[i - 1]; \ __KB_KEY(key_t, x)[i - 1] = __KB_KEY(key_t, y)[y->n - 1]; \ if (xp->is_internal) __KB_PTR(b, xp)[0] = __KB_PTR(b, y)[y->n]; \ --y->n; ++xp->n; \ } else if (i < x->n && (y = __KB_PTR(b, x)[i + 1])->n >= b->t) { \ __KB_KEY(key_t, xp)[xp->n++] = __KB_KEY(key_t, x)[i]; \ __KB_KEY(key_t, x)[i] = __KB_KEY(key_t, y)[0]; \ if (xp->is_internal) __KB_PTR(b, xp)[xp->n] = __KB_PTR(b, y)[0]; \ --y->n; \ memmove(__KB_KEY(key_t, y), __KB_KEY(key_t, y) + 1, y->n * sizeof(key_t)); \ if (y->is_internal) memmove(__KB_PTR(b, y), __KB_PTR(b, y) + 1, (y->n + 1) * sizeof(void*)); \ } else if (i > 0 && (y = __KB_PTR(b, x)[i - 1])->n == b->t - 1) { \ __KB_KEY(key_t, y)[y->n++] = __KB_KEY(key_t, x)[i - 1]; \ memmove(__KB_KEY(key_t, y) + y->n, __KB_KEY(key_t, xp), xp->n * sizeof(key_t)); \ if (y->is_internal) memmove(__KB_PTR(b, y) + y->n, __KB_PTR(b, xp), (xp->n + 1) * sizeof(void*)); \ y->n += xp->n; \ memmove(__KB_KEY(key_t, x) + i - 1, __KB_KEY(key_t, x) + i, (x->n - i) * sizeof(key_t)); \ memmove(__KB_PTR(b, x) + i, __KB_PTR(b, x) + i + 1, (x->n - i) * sizeof(void*)); \ --x->n; \ free(xp); \ xp = y; \ } else if (i < x->n && (y = __KB_PTR(b, x)[i + 1])->n == b->t - 1) { \ __KB_KEY(key_t, xp)[xp->n++] = __KB_KEY(key_t, x)[i]; \ memmove(__KB_KEY(key_t, xp) + xp->n, __KB_KEY(key_t, y), y->n * sizeof(key_t)); \ if (xp->is_internal) memmove(__KB_PTR(b, xp) + xp->n, __KB_PTR(b, y), (y->n + 1) * sizeof(void*)); \ xp->n += y->n; \ memmove(__KB_KEY(key_t, x) + i, __KB_KEY(key_t, x) + i + 1, (x->n - i - 1) * sizeof(key_t)); \ memmove(__KB_PTR(b, x) + i + 1, __KB_PTR(b, x) + i + 2, (x->n - i - 1) * sizeof(void*)); \ --x->n; \ free(y); \ } \ } \ return __kb_delp_aux_##name(b, xp, k, s); \ } \ static key_t kb_delp_##name(kbtree_##name##_t *b, const key_t * __restrict k) \ { \ kbnode_t *x; \ key_t ret; \ ret = __kb_delp_aux_##name(b, b->root, k, 0); \ --b->n_keys; \ if (b->root->n == 0 && b->root->is_internal) { \ --b->n_nodes; \ x = b->root; \ b->root = __KB_PTR(b, x)[0]; \ free(x); \ } \ return ret; \ } \ static inline key_t kb_del_##name(kbtree_##name##_t *b, const key_t k) \ { \ return kb_delp_##name(b, &k); \ } typedef struct { kbnode_t *x; int i; } __kbstack_t; #define __kb_traverse(key_t, b, __func) do { \ int __kmax = 8; \ __kbstack_t *__kstack, *__kp; \ __kp = __kstack = (__kbstack_t*)calloc(__kmax, sizeof(__kbstack_t)); \ __kp->x = (b)->root; __kp->i = 0; \ for (;;) { \ while (__kp->x && __kp->i <= __kp->x->n) { \ if (__kp - __kstack == __kmax - 1) { \ __kmax <<= 1; \ __kstack = (__kbstack_t*)realloc(__kstack, __kmax * sizeof(__kbstack_t)); \ __kp = __kstack + (__kmax>>1) - 1; \ } \ (__kp+1)->i = 0; (__kp+1)->x = __kp->x->is_internal? __KB_PTR(b, __kp->x)[__kp->i] : 0; \ ++__kp; \ } \ --__kp; \ if (__kp >= __kstack) { \ if (__kp->x && __kp->i < __kp->x->n) __func(&__KB_KEY(key_t, __kp->x)[__kp->i]); \ ++__kp->i; \ } else break; \ } \ free(__kstack); \ } while (0) #define KBTREE_INIT(name, key_t, __cmp) \ __KB_TREE_T(name) \ __KB_INIT(name, key_t) \ __KB_GET_AUX1(name, key_t, __cmp) \ __KB_GET(name, key_t) \ __KB_INTERVAL(name, key_t) \ __KB_PUT(name, key_t, __cmp) \ __KB_DEL(name, key_t) #define KB_DEFAULT_SIZE 512 #define kbtree_t(name) kbtree_##name##_t #define kb_init(name, s) kb_init_##name(s) #define kb_destroy(name, b) __kb_destroy(b) #define kb_get(name, b, k) kb_get_##name(b, k) #define kb_put(name, b, k) kb_put_##name(b, k) #define kb_del(name, b, k) kb_del_##name(b, k) #define kb_interval(name, b, k, l, u) kb_interval_##name(b, k, l, u) #define kb_getp(name, b, k) kb_getp_##name(b, k) #define kb_putp(name, b, k) kb_putp_##name(b, k) #define kb_delp(name, b, k) kb_delp_##name(b, k) #define kb_intervalp(name, b, k, l, u) kb_intervalp_##name(b, k, l, u) #define kb_size(b) ((b)->n_keys) #define kb_generic_cmp(a, b) (((b) < (a)) - ((a) < (b))) #define kb_str_cmp(a, b) strcmp(a, b) #endif ================================================ FILE: benchmarks/ext/khash.h ================================================ /* The MIT License Copyright (c) 2008, 2009, 2011 by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* An example: #include "khash.h" KHASH_MAP_INIT_INT(32, char) int main() { int ret, is_missing; khiter_t k; khash_t(32) *h = kh_init(32); k = kh_put(32, h, 5, &ret); if (!ret) kh_del(32, h, k); kh_value(h, k) = 10; k = kh_get(32, h, 10); is_missing = (k == kh_end(h)); k = kh_get(32, h, 5); kh_del(32, h, k); for (k = kh_begin(h); k != kh_end(h); ++k) if (kh_exist(h, k)) kh_value(h, k) = 1; kh_destroy(32, h); return 0; } */ /* 2011-02-14 (0.2.5): * Allow to declare global functions. 2009-09-26 (0.2.4): * Improve portability 2008-09-19 (0.2.3): * Corrected the example * Improved interfaces 2008-09-11 (0.2.2): * Improved speed a little in kh_put() 2008-09-10 (0.2.1): * Added kh_clear() * Fixed a compiling error 2008-09-02 (0.2.0): * Changed to token concatenation which increases flexibility. 2008-08-31 (0.1.2): * Fixed a bug in kh_get(), which has not been tested previously. 2008-08-31 (0.1.1): * Added destructor */ #ifndef __AC_KHASH_H #define __AC_KHASH_H /*! @header Generic hash table library. @copyright Heng Li */ #define AC_VERSION_KHASH_H "0.2.5" #include #include #include /* compipler specific configuration */ #if UINT_MAX == 0xffffffffu typedef unsigned int khint32_t; #elif ULONG_MAX == 0xffffffffu typedef unsigned long khint32_t; #endif #if ULONG_MAX == ULLONG_MAX typedef unsigned long khint64_t; #else typedef unsigned long long khint64_t; #endif #ifdef _MSC_VER #define inline __inline #endif typedef khint32_t khint_t; typedef khint_t khiter_t; #define __ac_HASH_PRIME_SIZE 32 static const khint32_t __ac_prime_list[__ac_HASH_PRIME_SIZE] = { 0ul, 3ul, 11ul, 23ul, 53ul, 97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul }; #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) #define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) #define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) #define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) #define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) #define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) static const double __ac_HASH_UPPER = 0.77; #define KHASH_DECLARE(name, khkey_t, khval_t) \ typedef struct { \ khint_t n_buckets, size, n_occupied, upper_bound; \ khint32_t *flags; \ khkey_t *keys; \ khval_t *vals; \ } kh_##name##_t; \ extern kh_##name##_t *kh_init_##name(); \ extern void kh_destroy_##name(kh_##name##_t *h); \ extern void kh_clear_##name(kh_##name##_t *h); \ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ extern void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ extern void kh_del_##name(kh_##name##_t *h, khint_t x); #define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ typedef struct { \ khint_t n_buckets, size, n_occupied, upper_bound; \ khint32_t *flags; \ khkey_t *keys; \ khval_t *vals; \ } kh_##name##_t; \ SCOPE kh_##name##_t *kh_init_##name() { \ return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t)); \ } \ SCOPE void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ free(h->keys); free(h->flags); \ free(h->vals); \ free(h); \ } \ } \ SCOPE void kh_clear_##name(kh_##name##_t *h) \ { \ if (h && h->flags) { \ memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(khint32_t)); \ h->size = h->n_occupied = 0; \ } \ } \ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ { \ if (h->n_buckets) { \ khint_t inc, k, i, last; \ k = __hash_func(key); i = k % h->n_buckets; \ inc = 1 + k % (h->n_buckets - 1); last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ else i += inc; \ if (i == last) return h->n_buckets; \ } \ return __ac_iseither(h->flags, i)? h->n_buckets : i; \ } else return 0; \ } \ SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ { \ khint32_t *new_flags = 0; \ khint_t j = 1; \ { \ khint_t t = __ac_HASH_PRIME_SIZE - 1; \ while (__ac_prime_list[t] > new_n_buckets) --t; \ new_n_buckets = __ac_prime_list[t+1]; \ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; \ else { \ new_flags = (khint32_t*)malloc(((new_n_buckets>>4) + 1) * sizeof(khint32_t)); \ memset(new_flags, 0xaa, ((new_n_buckets>>4) + 1) * sizeof(khint32_t)); \ if (h->n_buckets < new_n_buckets) { \ h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ if (kh_is_map) \ h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ } \ } \ } \ if (j) { \ for (j = 0; j != h->n_buckets; ++j) { \ if (__ac_iseither(h->flags, j) == 0) { \ khkey_t key = h->keys[j]; \ khval_t val; \ if (kh_is_map) val = h->vals[j]; \ __ac_set_isdel_true(h->flags, j); \ while (1) { \ khint_t inc, k, i; \ k = __hash_func(key); \ i = k % new_n_buckets; \ inc = 1 + k % (new_n_buckets - 1); \ while (!__ac_isempty(new_flags, i)) { \ if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \ else i += inc; \ } \ __ac_set_isempty_false(new_flags, i); \ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ __ac_set_isdel_true(h->flags, i); \ } else { \ h->keys[i] = key; \ if (kh_is_map) h->vals[i] = val; \ break; \ } \ } \ } \ } \ if (h->n_buckets > new_n_buckets) { \ h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ if (kh_is_map) \ h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ } \ free(h->flags); \ h->flags = new_flags; \ h->n_buckets = new_n_buckets; \ h->n_occupied = h->size; \ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ } \ } \ SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ { \ khint_t x; \ if (h->n_occupied >= h->upper_bound) { \ if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); \ else kh_resize_##name(h, h->n_buckets + 1); \ } \ { \ khint_t inc, k, i, site, last; \ x = site = h->n_buckets; k = __hash_func(key); i = k % h->n_buckets; \ if (__ac_isempty(h->flags, i)) x = i; \ else { \ inc = 1 + k % (h->n_buckets - 1); last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (__ac_isdel(h->flags, i)) site = i; \ if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ else i += inc; \ if (i == last) { x = site; break; } \ } \ if (x == h->n_buckets) { \ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ else x = i; \ } \ } \ } \ if (__ac_isempty(h->flags, x)) { \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; ++h->n_occupied; \ *ret = 1; \ } else if (__ac_isdel(h->flags, x)) { \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; \ *ret = 2; \ } else *ret = 0; \ return x; \ } \ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ { \ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ __ac_set_isdel_true(h->flags, x); \ --h->size; \ } \ } #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) /* --- BEGIN OF HASH FUNCTIONS --- */ /*! @function @abstract Integer hash function @param key The integer [khint32_t] @return The hash value [khint_t] */ #define kh_int_hash_func(key) (khint32_t)(key) /*! @function @abstract Integer comparison function */ #define kh_int_hash_equal(a, b) ((a) == (b)) /*! @function @abstract 64-bit integer hash function @param key The integer [khint64_t] @return The hash value [khint_t] */ #define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) /*! @function @abstract 64-bit integer comparison function */ #define kh_int64_hash_equal(a, b) ((a) == (b)) /*! @function @abstract const char* hash function @param s Pointer to a null terminated string @return The hash value */ static inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = *s; if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; return h; } /*! @function @abstract Another interface to const char* hash function @param key Pointer to a null terminated string [const char*] @return The hash value [khint_t] */ #define kh_str_hash_func(key) __ac_X31_hash_string(key) /*! @function @abstract Const char* comparison function */ #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) /* --- END OF HASH FUNCTIONS --- */ /* Other necessary macros... */ /*! @abstract Type of the hash table. @param name Name of the hash table [symbol] */ #define khash_t(name) kh_##name##_t /*! @function @abstract Initiate a hash table. @param name Name of the hash table [symbol] @return Pointer to the hash table [khash_t(name)*] */ #define kh_init(name) kh_init_##name() /*! @function @abstract Destroy a hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] */ #define kh_destroy(name, h) kh_destroy_##name(h) /*! @function @abstract Reset a hash table without deallocating memory. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] */ #define kh_clear(name, h) kh_clear_##name(h) /*! @function @abstract Resize a hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param s New size [khint_t] */ #define kh_resize(name, h, s) kh_resize_##name(h, s) /*! @function @abstract Insert a key to the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Key [type of keys] @param r Extra return code: 0 if the key is present in the hash table; 1 if the bucket is empty (never used); 2 if the element in the bucket has been deleted [int*] @return Iterator to the inserted element [khint_t] */ #define kh_put(name, h, k, r) kh_put_##name(h, k, r) /*! @function @abstract Retrieve a key from the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Key [type of keys] @return Iterator to the found element, or kh_end(h) is the element is absent [khint_t] */ #define kh_get(name, h, k) kh_get_##name(h, k) /*! @function @abstract Remove a key from the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Iterator to the element to be deleted [khint_t] */ #define kh_del(name, h, k) kh_del_##name(h, k) /*! @function @abstract Test whether a bucket contains data. @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return 1 if containing data; 0 otherwise [int] */ #define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) /*! @function @abstract Get key given an iterator @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return Key [type of keys] */ #define kh_key(h, x) ((h)->keys[x]) /*! @function @abstract Get value given an iterator @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return Value [type of values] @discussion For hash sets, calling this results in segfault. */ #define kh_val(h, x) ((h)->vals[x]) /*! @function @abstract Alias of kh_val() */ #define kh_value(h, x) ((h)->vals[x]) /*! @function @abstract Get the start iterator @param h Pointer to the hash table [khash_t(name)*] @return The start iterator [khint_t] */ #define kh_begin(h) (khint_t)(0) /*! @function @abstract Get the end iterator @param h Pointer to the hash table [khash_t(name)*] @return The end iterator [khint_t] */ #define kh_end(h) ((h)->n_buckets) /*! @function @abstract Get the number of elements in the hash table @param h Pointer to the hash table [khash_t(name)*] @return Number of elements in the hash table [khint_t] */ #define kh_size(h) ((h)->size) /*! @function @abstract Get the number of buckets in the hash table @param h Pointer to the hash table [khash_t(name)*] @return Number of buckets in the hash table [khint_t] */ #define kh_n_buckets(h) ((h)->n_buckets) /* More conenient interfaces */ /*! @function @abstract Instantiate a hash set containing integer keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_INT(name) \ KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) /*! @function @abstract Instantiate a hash map containing integer keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_INT(name, khval_t) \ KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) /*! @function @abstract Instantiate a hash map containing 64-bit integer keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_INT64(name) \ KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) /*! @function @abstract Instantiate a hash map containing 64-bit integer keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_INT64(name, khval_t) \ KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) typedef const char *kh_cstr_t; /*! @function @abstract Instantiate a hash map containing const char* keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_STR(name) \ KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) /*! @function @abstract Instantiate a hash map containing const char* keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_STR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) #endif /* __AC_KHASH_H */ ================================================ FILE: benchmarks/ext/kvec.h ================================================ /* The MIT License Copyright (c) 2008, by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* An example: #include "kvec.h" int main() { kvec_t(int) array; kv_init(array); kv_push(int, array, 10); // append kv_a(int, array, 20) = 5; // dynamic kv_A(array, 20) = 4; // static kv_destroy(array); return 0; } */ /* 2008-09-22 (0.1.0): * The initial version. */ #ifndef AC_KVEC_H #define AC_KVEC_H #include #define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #define kvec_t(type) struct { size_t n, m; type *a; } #define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) #define kv_destroy(v) free((v).a) #define kv_A(v, i) ((v).a[(i)]) #define kv_pop(v) ((v).a[--(v).n]) #define kv_size(v) ((v).n) #define kv_max(v) ((v).m) #define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) #define kv_copy(type, v1, v0) do { \ if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ (v1).n = (v0).n; \ memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ } while (0) \ #define kv_push(type, v, x) do { \ if ((v).n == (v).m) { \ (v).m = (v).m? (v).m<<1 : 2; \ (v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \ } \ (v).a[(v).n++] = (x); \ } while (0) #define kv_pushp(type, v) (((v).n == (v).m)? \ ((v).m = ((v).m? (v).m<<1 : 2), \ (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ : 0), ((v).a + ((v).n++)) #define kv_a(type, v, i) (((v).m <= (size_t)(i)? \ ((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \ (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ : (v).n <= (size_t)(i)? (v).n = (i) + 1 \ : 0), (v).a[(i)]) #endif ================================================ FILE: benchmarks/ext/regexp9.c ================================================ /* Copyright information from the original package: */ /* The authors of this software is Rob Pike. Copyright (c) 2002 by Lucent Technologies. Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice is included in all copies of any software which is or includes a copy or modification of this software and in all copies of the supporting documentation for such software. THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. This is a Unix port of the Plan 9 regular expression library. Please send comments about the packaging to Russ Cox . ---- This software is also made available under the Lucent Public License version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html */ /* This software was packaged for Unix by Russ Cox. Please send comments to rsc@swtch.com. http://swtch.com/plan9port/unix */ #include #include #include #include "regexp9.h" #define nil 0 #define exits(x) exit(x && *x ? 1 : 0) /**************************************************** * Routines from rune.c, runestrchr.c and utfrune.c * ****************************************************/ #include #include enum { Bit1 = 7, Bitx = 6, Bit2 = 5, Bit3 = 4, Bit4 = 3, T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ Maskx = (1< T1 */ c = *(uchar*)str; if(c < Tx) { *rune = c; return 1; } /* * two character sequence * 0080-07FF => T2 Tx */ c1 = *(uchar*)(str+1) ^ Tx; if(c1 & Testx) goto bad; if(c < T3) { if(c < T2) goto bad; l = ((c << Bitx) | c1) & Rune2; if(l <= Rune1) goto bad; *rune = l; return 2; } /* * three character sequence * 0800-FFFF => T3 Tx Tx */ c2 = *(uchar*)(str+2) ^ Tx; if(c2 & Testx) goto bad; if(c < T4) { l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; if(l <= Rune2) goto bad; *rune = l; return 3; } /* * bad decoding */ bad: *rune = Bad; return 1; } Rune* runestrchr(const Rune *s, Rune c) { Rune c0 = c; Rune c1; if(c == 0) { while(*s++) ; return (Rune*)s-1; } while((c1 = *s++)) if(c1 == c0) return (Rune*)s-1; return 0; } char* utfrune(char *s, long c) { long c1; Rune r; int n; if(c < Runesync) /* not part of utf sequence */ return strchr(s, c); for(;;) { c1 = *(uchar*)s; if(c1 < Runeself) { /* one byte rune */ if(c1 == 0) return 0; if(c1 == c) return s; s++; continue; } n = chartorune(&r, s); if(r == c) return s; s += n; } } /************ * regaux.c * ************/ /* * save a new match in mp */ static void _renewmatch(Resub *mp, int ms, Resublist *sp) { int i; if(mp==0 || ms<=0) return; if(mp[0].s.sp==0 || sp->m[0].s.spm[0].s.sp==mp[0].s.sp && sp->m[0].e.ep>mp[0].e.ep)){ for(i=0; im[i]; for(; iinst; p++){ if(p->inst == ip){ if(sep->m[0].s.sp < p->se.m[0].s.sp){ if(ms > 1) p->se = *sep; else p->se.m[0] = sep->m[0]; } return 0; } } p->inst = ip; if(ms > 1) p->se = *sep; else p->se.m[0] = sep->m[0]; (++p)->inst = 0; return p; } /* * same as renewthread, but called with * initial empty start pointer. */ static Relist* _renewemptythread(Relist *lp, /* _relist to add to */ Reinst *ip, /* instruction to add */ int ms, char *sp) /* pointers to subexpressions */ { Relist *p; for(p=lp; p->inst; p++){ if(p->inst == ip){ if(sp < p->se.m[0].s.sp) { if(ms > 1) memset(&p->se, 0, sizeof(p->se)); p->se.m[0].s.sp = sp; } return 0; } } p->inst = ip; if(ms > 1) memset(&p->se, 0, sizeof(p->se)); p->se.m[0].s.sp = sp; (++p)->inst = 0; return p; } static Relist* _rrenewemptythread(Relist *lp, /* _relist to add to */ Reinst *ip, /* instruction to add */ int ms, Rune *rsp) /* pointers to subexpressions */ { Relist *p; for(p=lp; p->inst; p++){ if(p->inst == ip){ if(rsp < p->se.m[0].s.rsp) { if(ms > 1) memset(&p->se, 0, sizeof(p->se)); p->se.m[0].s.rsp = rsp; } return 0; } } p->inst = ip; if(ms > 1) memset(&p->se, 0, sizeof(p->se)); p->se.m[0].s.rsp = rsp; (++p)->inst = 0; return p; } /************* * regcomp.c * *************/ #define TRUE 1 #define FALSE 0 /* * Parser Information */ typedef struct Node { Reinst* first; Reinst* last; }Node; #define NSTACK 20 static Node andstack[NSTACK]; static Node *andp; static int atorstack[NSTACK]; static int* atorp; static int cursubid; /* id of current subexpression */ static int subidstack[NSTACK]; /* parallel to atorstack */ static int* subidp; static int lastwasand; /* Last token was operand */ static int nbra; static char* exprp; /* pointer to next character in source expression */ static int lexdone; static int nclass; static Reclass*classp; static Reinst* freep; static int errors; static Rune yyrune; /* last lex'd rune */ static Reclass*yyclassp; /* last lex'd class */ /* predeclared crap */ static void operator(int); static void pushand(Reinst*, Reinst*); static void pushator(int); static void evaluntil(int); static int bldcclass(void); static jmp_buf regkaboom; static void rcerror(char *s) { errors++; regerror9(s); longjmp(regkaboom, 1); } static Reinst* newinst(int t) { freep->type = t; freep->u2.left = 0; freep->u1.right = 0; return freep++; } static void operand(int t) { Reinst *i; if(lastwasand) operator(CAT); /* catenate is implicit */ i = newinst(t); if(t == CCLASS || t == NCCLASS) i->u1.cp = yyclassp; if(t == RUNE) i->u1.r = yyrune; pushand(i, i); lastwasand = TRUE; } static void operator(int t) { if(t==RBRA && --nbra<0) rcerror("unmatched right paren"); if(t==LBRA){ if(++cursubid >= NSUBEXP) rcerror ("too many subexpressions"); nbra++; if(lastwasand) operator(CAT); } else evaluntil(t); if(t != RBRA) pushator(t); lastwasand = FALSE; if(t==STAR || t==QUEST || t==PLUS || t==RBRA) lastwasand = TRUE; /* these look like operands */ } static void regerr2(char *s, int c) { char buf[100]; char *cp = buf; while(*s) *cp++ = *s++; *cp++ = c; *cp = '\0'; rcerror(buf); } static void cant(char *s) { char buf[100]; strcpy(buf, "can't happen: "); strcat(buf, s); rcerror(buf); } static void pushand(Reinst *f, Reinst *l) { if(andp >= &andstack[NSTACK]) cant("operand stack overflow"); andp->first = f; andp->last = l; andp++; } static void pushator(int t) { if(atorp >= &atorstack[NSTACK]) cant("operator stack overflow"); *atorp++ = t; *subidp++ = cursubid; } static Node* popand(int op) { Reinst *inst; if(andp <= &andstack[0]){ regerr2("missing operand for ", op); inst = newinst(NOP); pushand(inst,inst); } return --andp; } static int popator(void) { if(atorp <= &atorstack[0]) cant("operator stack underflow"); --subidp; return *--atorp; } static void evaluntil(int pri) { Node *op1, *op2; Reinst *inst1, *inst2; while(pri==RBRA || atorp[-1]>=pri){ switch(popator()){ default: rcerror("unknown operator in evaluntil"); break; case LBRA: /* must have been RBRA */ op1 = popand('('); inst2 = newinst(RBRA); inst2->u1.subid = *subidp; op1->last->u2.next = inst2; inst1 = newinst(LBRA); inst1->u1.subid = *subidp; inst1->u2.next = op1->first; pushand(inst1, inst2); return; case OR: op2 = popand('|'); op1 = popand('|'); inst2 = newinst(NOP); op2->last->u2.next = inst2; op1->last->u2.next = inst2; inst1 = newinst(OR); inst1->u1.right = op1->first; inst1->u2.left = op2->first; pushand(inst1, inst2); break; case CAT: op2 = popand(0); op1 = popand(0); op1->last->u2.next = op2->first; pushand(op1->first, op2->last); break; case STAR: op2 = popand('*'); inst1 = newinst(OR); op2->last->u2.next = inst1; inst1->u1.right = op2->first; pushand(inst1, inst1); break; case PLUS: op2 = popand('+'); inst1 = newinst(OR); op2->last->u2.next = inst1; inst1->u1.right = op2->first; pushand(op2->first, inst1); break; case QUEST: op2 = popand('?'); inst1 = newinst(OR); inst2 = newinst(NOP); inst1->u2.left = inst2; inst1->u1.right = op2->first; op2->last->u2.next = inst2; pushand(inst1, inst2); break; } } } static Reprog* optimize(Reprog *pp) { Reinst *inst, *target; int size; Reprog *npp; Reclass *cl; int diff; /* * get rid of NOOP chains */ for(inst=pp->firstinst; inst->type!=END; inst++){ target = inst->u2.next; while(target->type == NOP) target = target->u2.next; inst->u2.next = target; } /* * The original allocation is for an area larger than * necessary. Reallocate to the actual space used * and then relocate the code. */ size = sizeof(Reprog) + (freep - pp->firstinst)*sizeof(Reinst); npp = realloc(pp, size); if(npp==0 || npp==pp) return pp; diff = (char *)npp - (char *)pp; freep = (Reinst *)((char *)freep + diff); for(inst=npp->firstinst; insttype){ case OR: case STAR: case PLUS: case QUEST: inst->u1.right = (void*)((char*)inst->u1.right + diff); break; case CCLASS: case NCCLASS: inst->u1.right = (void*)((char*)inst->u1.right + diff); cl = inst->u1.cp; cl->end = (void*)((char*)cl->end + diff); break; } inst->u2.left = (void*)((char*)inst->u2.left + diff); } npp->startinst = (void*)((char*)npp->startinst + diff); return npp; } #ifdef DEBUG static void dumpstack(void){ Node *stk; int *ip; print("operators\n"); for(ip=atorstack; ipfirst->type, stk->last->type); } static void dump(Reprog *pp) { Reinst *l; Rune *p; l = pp->firstinst; do{ print("%d:\t0%o\t%d\t%d", l-pp->firstinst, l->type, l->u2.left-pp->firstinst, l->u1.right-pp->firstinst); if(l->type == RUNE) print("\t%C\n", l->u1.r); else if(l->type == CCLASS || l->type == NCCLASS){ print("\t["); if(l->type == NCCLASS) print("^"); for(p = l->u1.cp->spans; p < l->u1.cp->end; p += 2) if(p[0] == p[1]) print("%C", p[0]); else print("%C-%C", p[0], p[1]); print("]\n"); } else print("\n"); }while(l++->type); } #endif static Reclass* newclass(void) { if(nclass >= NCLASS) regerr2("too many character classes; limit", NCLASS+'0'); return &(classp[nclass++]); } static int nextc(Rune *rp) { if(lexdone){ *rp = 0; return 1; } exprp += chartorune(rp, exprp); if(*rp == '\\'){ exprp += chartorune(rp, exprp); return 1; } if(*rp == 0) lexdone = 1; return 0; } static int lex(int literal, int dot_type) { int quoted; quoted = nextc(&yyrune); if(literal || quoted){ if(yyrune == 0) return END; return RUNE; } switch(yyrune){ case 0: return END; case '*': return STAR; case '?': return QUEST; case '+': return PLUS; case '|': return OR; case '.': return dot_type; case '(': return LBRA; case ')': return RBRA; case '^': return BOL; case '$': return EOL; case '[': return bldcclass(); } return RUNE; } static int bldcclass(void) { int type; Rune r[NCCRUNE]; Rune *p, *ep, *np; Rune rune; int quoted; /* we have already seen the '[' */ type = CCLASS; yyclassp = newclass(); /* look ahead for negation */ /* SPECIAL CASE!!! negated classes don't match \n */ ep = r; quoted = nextc(&rune); if(!quoted && rune == '^'){ type = NCCLASS; quoted = nextc(&rune); *ep++ = '\n'; *ep++ = '\n'; } /* parse class into a set of spans */ for(; ep<&r[NCCRUNE];){ if(rune == 0){ rcerror("malformed '[]'"); return 0; } if(!quoted && rune == ']') break; if(!quoted && rune == '-'){ if(ep == r){ rcerror("malformed '[]'"); return 0; } quoted = nextc(&rune); if((!quoted && rune == ']') || rune == 0){ rcerror("malformed '[]'"); return 0; } *(ep-1) = rune; } else { *ep++ = rune; *ep++ = rune; } quoted = nextc(&rune); } /* sort on span start */ for(p = r; p < ep; p += 2){ for(np = p; np < ep; np += 2) if(*np < *p){ rune = np[0]; np[0] = p[0]; p[0] = rune; rune = np[1]; np[1] = p[1]; p[1] = rune; } } /* merge spans */ np = yyclassp->spans; p = r; if(r == ep) yyclassp->end = np; else { np[0] = *p++; np[1] = *p++; for(; p < ep; p += 2) if(p[0] <= np[1]){ if(p[1] > np[1]) np[1] = p[1]; } else { np += 2; np[0] = p[0]; np[1] = p[1]; } yyclassp->end = np+2; } return type; } static Reprog* regcomp1(char *s, int literal, int dot_type) { int token; Reprog *volatile pp; /* get memory for the program */ pp = malloc(sizeof(Reprog) + 6*sizeof(Reinst)*strlen(s)); if(pp == 0){ regerror9("out of memory"); return 0; } freep = pp->firstinst; classp = pp->class; errors = 0; if(setjmp(regkaboom)) goto out; /* go compile the sucker */ lexdone = 0; exprp = s; nclass = 0; nbra = 0; atorp = atorstack; andp = andstack; subidp = subidstack; lastwasand = FALSE; cursubid = 0; /* Start with a low priority operator to prime parser */ pushator(START-1); while((token = lex(literal, dot_type)) != END){ if((token&0300) == OPERATOR) operator(token); else operand(token); } /* Close with a low priority operator */ evaluntil(START); /* Force END */ operand(END); evaluntil(START); #ifdef DEBUG dumpstack(); #endif if(nbra) rcerror("unmatched left paren"); --andp; /* points to first and only operand */ pp->startinst = andp->first; #ifdef DEBUG dump(pp); #endif pp = optimize(pp); #ifdef DEBUG print("start: %d\n", andp->first-pp->firstinst); dump(pp); #endif out: if(errors){ free(pp); pp = 0; } return pp; } extern Reprog* regcomp9(char *s) { return regcomp1(s, 0, ANY); } extern Reprog* regcomplit9(char *s) { return regcomp1(s, 1, ANY); } extern Reprog* regcompnl9(char *s) { return regcomp1(s, 0, ANYNL); } /************* * regexec.c * *************/ /* * return 0 if no match * >0 if a match * <0 if we ran out of _relist space */ static int regexec1(const Reprog *progp, /* program to run */ char *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms, /* number of elements at mp */ Reljunk *j ) { int flag=0; Reinst *inst; Relist *tlp; char *s; int i, checkstart; Rune r, *rp, *ep; int n; Relist* tl; /* This list, next list */ Relist* nl; Relist* tle; /* ends of this and next list */ Relist* nle; int match; char *p; match = 0; checkstart = j->starttype; if(mp) for(i=0; irelist[0][0].inst = 0; j->relist[1][0].inst = 0; /* Execute machine once for each character, including terminal NUL */ s = j->starts; do{ /* fast check for first char */ if(checkstart) { switch(j->starttype) { case RUNE: p = utfrune(s, j->startchar); if(p == 0 || s == j->eol) return match; s = p; break; case BOL: if(s == bol) break; p = utfrune(s, '\n'); if(p == 0 || s == j->eol) return match; s = p+1; break; } } r = *(uchar*)s; if(r < Runeself) n = 1; else n = chartorune(&r, s); /* switch run lists */ tl = j->relist[flag]; tle = j->reliste[flag]; nl = j->relist[flag^=1]; nle = j->reliste[flag]; nl->inst = 0; /* Add first instruction to current list */ if(match == 0) _renewemptythread(tl, progp->startinst, ms, s); /* Execute machine until current list is empty */ for(tlp=tl; tlp->inst; tlp++){ /* assignment = */ for(inst = tlp->inst; ; inst = inst->u2.next){ switch(inst->type){ case RUNE: /* regular character */ if(inst->u1.r == r){ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; } break; case LBRA: tlp->se.m[inst->u1.subid].s.sp = s; continue; case RBRA: tlp->se.m[inst->u1.subid].e.ep = s; continue; case ANY: if(r != '\n') if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; case ANYNL: if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; case BOL: if(s == bol || *(s-1) == '\n') continue; break; case EOL: if(s == j->eol || r == 0 || r == '\n') continue; break; case CCLASS: ep = inst->u1.cp->end; for(rp = inst->u1.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]){ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; } break; case NCCLASS: ep = inst->u1.cp->end; for(rp = inst->u1.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]) break; if(rp == ep) if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; case OR: /* evaluate right choice later */ if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle) return -1; /* efficiency: advance and re-evaluate */ continue; case END: /* Match! */ match = 1; tlp->se.m[0].e.ep = s; if(mp != 0) _renewmatch(mp, ms, &tlp->se); break; } break; } } if(s == j->eol) break; checkstart = j->starttype && nl->inst==0; s += n; }while(r); return match; } static int regexec2(const Reprog *progp, /* program to run */ char *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms, /* number of elements at mp */ Reljunk *j ) { int rv; Relist *relist0, *relist1; /* mark space */ relist0 = malloc(BIGLISTSIZE*sizeof(Relist)); if(relist0 == nil) return -1; relist1 = malloc(BIGLISTSIZE*sizeof(Relist)); if(relist1 == nil){ free(relist1); return -1; } j->relist[0] = relist0; j->relist[1] = relist1; j->reliste[0] = relist0 + BIGLISTSIZE - 2; j->reliste[1] = relist1 + BIGLISTSIZE - 2; rv = regexec1(progp, bol, mp, ms, j); free(relist0); free(relist1); return rv; } extern int regexec9(const Reprog *progp, /* program to run */ char *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms) /* number of elements at mp */ { Reljunk j; Relist relist0[LISTSIZE], relist1[LISTSIZE]; int rv; /* * use user-specified starting/ending location if specified */ j.starts = bol; j.eol = 0; if(mp && ms>0){ if(mp->s.sp) j.starts = mp->s.sp; if(mp->e.ep) j.eol = mp->e.ep; } j.starttype = 0; j.startchar = 0; if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) { j.starttype = RUNE; j.startchar = progp->startinst->u1.r; } if(progp->startinst->type == BOL) j.starttype = BOL; /* mark space */ j.relist[0] = relist0; j.relist[1] = relist1; j.reliste[0] = relist0 + nelem(relist0) - 2; j.reliste[1] = relist1 + nelem(relist1) - 2; rv = regexec1(progp, bol, mp, ms, &j); if(rv >= 0) return rv; rv = regexec2(progp, bol, mp, ms, &j); if(rv >= 0) return rv; return -1; } /************ * regsub.c * ************/ /* substitute into one string using the matches from the last regexec() */ extern void regsub9(char *sp, /* source string */ char *dp, /* destination string */ int dlen, Resub *mp, /* subexpression elements */ int ms) /* number of elements pointed to by mp */ { char *ssp, *ep; int i; ep = dp+dlen-1; while(*sp != '\0'){ if(*sp == '\\'){ switch(*++sp){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = *sp-'0'; if(mp[i].s.sp != 0 && mp!=0 && ms>i) for(ssp = mp[i].s.sp; ssp < mp[i].e.ep; ssp++) if(dp < ep) *dp++ = *ssp; break; case '\\': if(dp < ep) *dp++ = '\\'; break; case '\0': sp--; break; default: if(dp < ep) *dp++ = *sp; break; } }else if(*sp == '&'){ if(mp[0].s.sp != 0 && mp!=0 && ms>0) if(mp[0].s.sp != 0) for(ssp = mp[0].s.sp; ssp < mp[0].e.ep; ssp++) if(dp < ep) *dp++ = *ssp; }else{ if(dp < ep) *dp++ = *sp; } sp++; } *dp = '\0'; } /************** * regerror.c * **************/ void regerror9(char *s) { char buf[132]; strcpy(buf, "regerror: "); strcat(buf, s); strcat(buf, "\n"); write(2, buf, strlen(buf)); exits("regerr"); } /************** * rregexec.c * **************/ /* * return 0 if no match * >0 if a match * <0 if we ran out of _relist space */ static int rregexec1(const Reprog *progp, /* program to run */ Rune *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms, /* number of elements at mp */ Reljunk *j) { int flag=0; Reinst *inst; Relist *tlp; Rune *s; int i, checkstart; Rune r, *rp, *ep; Relist* tl; /* This list, next list */ Relist* nl; Relist* tle; /* ends of this and next list */ Relist* nle; int match; Rune *p; match = 0; checkstart = j->startchar; if(mp) for(i=0; irelist[0][0].inst = 0; j->relist[1][0].inst = 0; /* Execute machine once for each character, including terminal NUL */ s = j->rstarts; do{ /* fast check for first char */ if(checkstart) { switch(j->starttype) { case RUNE: p = runestrchr(s, j->startchar); if(p == 0 || p == j->reol) return match; s = p; break; case BOL: if(s == bol) break; p = runestrchr(s, '\n'); if(p == 0 || s == j->reol) return match; s = p+1; break; } } r = *s; /* switch run lists */ tl = j->relist[flag]; tle = j->reliste[flag]; nl = j->relist[flag^=1]; nle = j->reliste[flag]; nl->inst = 0; /* Add first instruction to current list */ _rrenewemptythread(tl, progp->startinst, ms, s); /* Execute machine until current list is empty */ for(tlp=tl; tlp->inst; tlp++){ for(inst=tlp->inst; ; inst = inst->u2.next){ switch(inst->type){ case RUNE: /* regular character */ if(inst->u1.r == r) if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; case LBRA: tlp->se.m[inst->u1.subid].s.rsp = s; continue; case RBRA: tlp->se.m[inst->u1.subid].e.rep = s; continue; case ANY: if(r != '\n') if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; case ANYNL: if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; case BOL: if(s == bol || *(s-1) == '\n') continue; break; case EOL: if(s == j->reol || r == 0 || r == '\n') continue; break; case CCLASS: ep = inst->u1.cp->end; for(rp = inst->u1.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]){ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; } break; case NCCLASS: ep = inst->u1.cp->end; for(rp = inst->u1.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]) break; if(rp == ep) if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) return -1; break; case OR: /* evaluate right choice later */ if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle) return -1; /* efficiency: advance and re-evaluate */ continue; case END: /* Match! */ match = 1; tlp->se.m[0].e.rep = s; if(mp != 0) _renewmatch(mp, ms, &tlp->se); break; } break; } } if(s == j->reol) break; checkstart = j->startchar && nl->inst==0; s++; }while(r); return match; } static int rregexec2(const Reprog *progp, /* program to run */ Rune *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms, /* number of elements at mp */ Reljunk *j ) { Relist relist0[5*LISTSIZE], relist1[5*LISTSIZE]; /* mark space */ j->relist[0] = relist0; j->relist[1] = relist1; j->reliste[0] = relist0 + nelem(relist0) - 2; j->reliste[1] = relist1 + nelem(relist1) - 2; return rregexec1(progp, bol, mp, ms, j); } extern int rregexec9(const Reprog *progp, /* program to run */ Rune *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms) /* number of elements at mp */ { Reljunk j; Relist relist0[LISTSIZE], relist1[LISTSIZE]; int rv; /* * use user-specified starting/ending location if specified */ j.rstarts = bol; j.reol = 0; if(mp && ms>0){ if(mp->s.sp) j.rstarts = mp->s.rsp; if(mp->e.ep) j.reol = mp->e.rep; } j.starttype = 0; j.startchar = 0; if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) { j.starttype = RUNE; j.startchar = progp->startinst->u1.r; } if(progp->startinst->type == BOL) j.starttype = BOL; /* mark space */ j.relist[0] = relist0; j.relist[1] = relist1; j.reliste[0] = relist0 + nelem(relist0) - 2; j.reliste[1] = relist1 + nelem(relist1) - 2; rv = rregexec1(progp, bol, mp, ms, &j); if(rv >= 0) return rv; rv = rregexec2(progp, bol, mp, ms, &j); if(rv >= 0) return rv; return -1; } /************* * rregsub.c * *************/ /* substitute into one string using the matches from the last regexec() */ extern void rregsub9(Rune *sp, /* source string */ Rune *dp, /* destination string */ int dlen, Resub *mp, /* subexpression elements */ int ms) /* number of elements pointed to by mp */ { Rune *ssp, *ep; int i; ep = dp+(dlen/sizeof(Rune))-1; while(*sp != '\0'){ if(*sp == '\\'){ switch(*++sp){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = *sp-'0'; if(mp[i].s.rsp != 0 && mp!=0 && ms>i) for(ssp = mp[i].s.rsp; ssp < mp[i].e.rep; ssp++) if(dp < ep) *dp++ = *ssp; break; case '\\': if(dp < ep) *dp++ = '\\'; break; case '\0': sp--; break; default: if(dp < ep) *dp++ = *sp; break; } }else if(*sp == '&'){ if(mp[0].s.rsp != 0 && mp!=0 && ms>0) if(mp[0].s.rsp != 0) for(ssp = mp[0].s.rsp; ssp < mp[0].e.rep; ssp++) if(dp < ep) *dp++ = *ssp; }else{ if(dp < ep) *dp++ = *sp; } sp++; } *dp = '\0'; } ================================================ FILE: benchmarks/ext/regexp9.h ================================================ #ifndef _REGEXP9_H_ #define _REGEXP9_H_ 1 /********* * utf.h * *********/ typedef unsigned short Rune; typedef struct Resub Resub; typedef struct Reclass Reclass; typedef struct Reinst Reinst; typedef struct Reprog Reprog; enum { UTFmax = 3, /* maximum bytes per rune */ Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ Runeself = 0x80, /* rune and UTF sequences are the same (<) */ Runeerror = 0xFFFD /* decoding error in UTF */ }; /************* * regexp9.h * *************/ #if defined(__cplusplus) extern "C" { #endif #ifdef AUTOLIB AUTOLIB(regexp9) #endif /* * Sub expression matches */ struct Resub{ union { char *sp; Rune *rsp; }s; union { char *ep; Rune *rep; }e; }; /* * character class, each pair of rune's defines a range */ struct Reclass{ Rune *end; Rune spans[64]; }; /* * Machine instructions */ struct Reinst{ int type; union { Reclass *cp; /* class pointer */ Rune r; /* character */ int subid; /* sub-expression id for RBRA and LBRA */ Reinst *right; /* right child of OR */ }u1; union { /* regexp relies on these two being in the same union */ Reinst *left; /* left child of OR */ Reinst *next; /* next instruction for CAT & LBRA */ }u2; }; /* * Reprogram definition */ struct Reprog{ Reinst *startinst; /* start pc */ Reclass class[16]; /* .data */ Reinst firstinst[5]; /* .text */ }; extern Reprog *regcomp9(char*); extern Reprog *regcomplit9(char*); extern Reprog *regcompnl9(char*); extern void regerror9(char*); extern int regexec9(const Reprog*, char*, Resub*, int); extern void regsub9(char*, char*, int, Resub*, int); extern int rregexec9(const Reprog*, Rune*, Resub*, int); extern void rregsub9(Rune*, Rune*, int, Resub*, int); extern int chartorune(Rune *rune, const char *str); extern Rune* runestrchr(const Rune *s, Rune c); extern char* utfrune(char *s, long c); #if defined(__cplusplus) } #endif /************* * regcomp.h * *************/ /* * substitution list */ #define uchar __reuchar typedef unsigned char uchar; #define nelem(x) (sizeof(x)/sizeof((x)[0])) #define NSUBEXP 32 typedef struct Resublist Resublist; struct Resublist { Resub m[NSUBEXP]; }; /* max character classes per program */ extern Reprog RePrOg; #define NCLASS (sizeof(RePrOg.class)/sizeof(Reclass)) /* max rune ranges per character class */ #define NCCRUNE (sizeof(Reclass)/sizeof(Rune)) /* * Actions and Tokens (Reinst types) * * 02xx are operators, value == precedence * 03xx are tokens, i.e. operands for operators */ #define RUNE 0177 #define OPERATOR 0200 /* Bitmask of all operators */ #define START 0200 /* Start, used for marker on stack */ #define RBRA 0201 /* Right bracket, ) */ #define LBRA 0202 /* Left bracket, ( */ #define OR 0203 /* Alternation, | */ #define CAT 0204 /* Concatentation, implicit operator */ #define STAR 0205 /* Closure, * */ #define PLUS 0206 /* a+ == aa* */ #define QUEST 0207 /* a? == a|nothing, i.e. 0 or 1 a's */ #define ANY 0300 /* Any character except newline, . */ #define ANYNL 0301 /* Any character including newline, . */ #define NOP 0302 /* No operation, internal use only */ #define BOL 0303 /* Beginning of line, ^ */ #define EOL 0304 /* End of line, $ */ #define CCLASS 0305 /* Character class, [] */ #define NCCLASS 0306 /* Negated character class, [] */ #define END 0377 /* Terminate: match found */ /* * regexec execution lists */ #define LISTSIZE 10 #define BIGLISTSIZE (10*LISTSIZE) typedef struct Relist Relist; struct Relist { Reinst* inst; /* Reinstruction of the thread */ Resublist se; /* matched subexpressions in this thread */ }; typedef struct Reljunk Reljunk; struct Reljunk { Relist* relist[2]; Relist* reliste[2]; int starttype; Rune startchar; char* starts; char* eol; Rune* rstarts; Rune* reol; }; #endif ================================================ FILE: benchmarks/ext/sudoku ================================================ cat ./ext/sudoku.txt ================================================ FILE: benchmarks/ext/sudoku.txt ================================================ ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9 near worst case for brute-force solver (wiki) .......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6... gsf's sudoku q1 (Platinum Blonde) .2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9.. (Cheese) ........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........ (Fata Morgana) 12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8 (Red Dwarf) 1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1 (Easter Monster) .......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7..... Nicolas Juillerat's Sudoku explainer 1.2.1 (top 5) 12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8 ..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5. 1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6. ..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7.. ....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7 dukuso's suexrat9 (top 1) 4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........ from http://magictour.free.fr/topn87 (top 3) 7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5........... 3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6..... ........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3 dukuso's suexratt (top 1) .......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6... first 2 from sudoku17 .......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6.. 1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1 2 from http://www.setbb.com/phpbb/viewtopic.php?p=10478 .....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67..... ================================================ FILE: benchmarks/graphs.py ================================================ import numpy as np from matplotlib import pyplot as plt languages = np.array([ 'C', 'C++', 'Cello', 'Java', 'Javascript', 'Python', 'Ruby', 'Lua', 'Lua JIT']) experiments = [ 'Array', 'Map', 'N-Bodies', 'Dictionary', 'Sudoku', 'Matrix', 'Garbage Collection'] results_array = np.array([ 0.02, 0.02, 0.10, 0.14, 0.07, 0.11, 0.07, 2.34, 0.24]) results_map = np.array([ 0.24, 0.05, 0.54, 0.60, 1.92, 9.73, 1.24, 5.46, 2.01]) results_nbodies = np.array([ 0.01, 0.01, 0.07, 0.07, 0.09, 1.52, 1.45, 0.66, 0.02]) results_dict = np.array([ 0.09, 0.13, 0.25, 0.24, 0.46, 0.18, 0.44, 0.23, 0.18]) results_sudoku = np.array([ 0.14, 0.14, 0.15, 0.29, 0.44, 5.30, 9.64, 6.34, 0.49]) results_matrix = np.array([ 0.03, 0.01, 0.02, 0.11, 0.23, 2.33, 7.62, 0.94, 0.03]) results_gc = np.array([ 0.01, 0.01, 0.26, 0.06, 0.25, 3.34, 5.37, 8.02, 0.31]) results = [ results_array, results_map, results_nbodies, results_dict, results_sudoku, results_matrix, results_gc] #cols = [ # '#006666', '#FF6600', '#991F00', # '#339933', '#009999', '#006666', # '#FF6600', '#991F00', '#339933'] cols = [ '#006666', '#FF6600', '#991F00', '#006666', '#FF6600', '#991F00', '#006666', '#FF6600', '#991F00'] ylims = [ 3.25, 13.5, 2.2, 0.65, 13.5, 11, 11 ] for exp, result, ylim in zip(experiments, results, ylims): fig = plt.figure(figsize=(3, 2.5)) ax = fig.add_subplot(1, 1, 1, axisbg='#FCFCFC') bars = ax.bar(np.arange(len(result)), result) for bar, col in zip(bars, cols): bar.set_color(col) bar.set_edgecolor('#555555') height = bar.get_height() ax.text( bar.get_x()+bar.get_width()/1.5, height + (ylim/20), '%0.2f' % height, ha='center', va='bottom', rotation='vertical', color='#555555') plt.xticks(np.arange(len(result)) + 0.75 / 2, languages, rotation='vertical') plt.gca().xaxis.grid(False) plt.gca().yaxis.grid(False) plt.ylim((0, ylim)) plt.tight_layout() plt.show() ================================================ FILE: examples/cello_world.c ================================================ #include "Cello.h" int main(int argc, char** argv) { println("Cello World!"); return 0; } ================================================ FILE: examples/help.c ================================================ #include "Cello.h" int main(int argc, char** argv) { help(Range); return 0; } ================================================ FILE: examples/iteration.c ================================================ #include "Cello.h" int main(int argc, char** argv) { /* Stack objects are created using "$" */ var i0 = $(Int, 5); var i1 = $(Int, 3); var i2 = $(Int, 4); /* Heap objects are created using "new" */ var items = new(Array, Int, i0, i1, i2); /* Collections can be looped over */ foreach (item in items) { print("Object %$ is of type %$\n", item, type_of(item)); } return 0; } ================================================ FILE: examples/newtype.c ================================================ #include "Cello.h" /* Type Variable */ static var ExampleType; /* Type Struct */ struct ExampleType { int value; }; /* Constructor Function */ static void ExampleType_New(var self, var args) { struct ExampleType* h = self; h->value = c_int(get(args, $I(0))); } /* Comparison Function */ static int ExampleType_Cmp(var self, var obj) { struct ExampleType* lhs = self; struct ExampleType* rhs = cast(obj, ExampleType); return lhs->value - rhs->value; } int main(int argc, char** argv) { /* Construct `ExampleType` type dynamically */ ExampleType = new_root(Type, $S("ExampleType"), /* Type Name */ $I(sizeof(struct ExampleType)), /* Type Size */ $(New, ExampleType_New, NULL), /* Type Interfaces */ $(Cmp, ExampleType_Cmp)); /* ... */ print("%$ is a %$!\n", ExampleType, type_of(ExampleType)); /* We can now make `ExampleType` objects */ var obj0 = new(ExampleType, $I(1)); var obj1 = new(ExampleType, $I(2)); /* Test for comparison etc... */ print("Is %$ less Than %$? %s\n", obj0, obj1, lt(obj0, obj1) ? $S("Yes") : $S("No")); /* Type objects must remain around longer than their instances */ del(obj0); del(obj1); del_root(ExampleType); return 0; } ================================================ FILE: examples/object.c ================================================ #include "Cello.h" /* Define a normal C structure */ struct Point { float x, y; }; /* Make it compatible with Cello */ var Point = Cello(Point); int main(int argc, char** argv) { /* Create on Stack or Heap */ var p0 = $(Point, 0.0, 1.0); var p1 = new(Point, $(Point, 0.0, 2.0)); /* It can be shown, compared, hashed, etc... ** ** p0: <'Point' At 0x000000000022FC58> ** p1: <'Point' At 0x00000000004C7CC8> ** cmp: 1 ** hash: 2849275892l */ print("p0: %$\np1: %$\ncmp: %i\nhash: %ul\n", p0, p1, $I(cmp(p0, p1)), $I(hash(p0))); /* And collected by the GC when out of scope */ return 0; } ================================================ FILE: examples/ranges.c ================================================ #include "Cello.h" int main(int argc, char** argv) { var items = new(Array, Int, $I( 8), $I( 5), $I(20), $I(15), $I(16), $I(98)); /* Iterate over indices using "range" */ foreach (i in range($I(len(items)))) { print("Item Range %i is %i\n", i, get(items, i)); } /* Iterate over every other item with "slice" */ foreach (item in slice(items, _, _, $I(2))) { print("Item Slice %i\n", item); } return 0; } ================================================ FILE: examples/table.c ================================================ #include "Cello.h" int main(int argc, char** argv) { /* Shorthand $ can be used for basic types */ var prices = new(Table, String, Int); set(prices, $S("Apple"), $I(12)); set(prices, $S("Banana"), $I( 6)); set(prices, $S("Pear"), $I(55)); /* Tables also support iteration */ foreach (key in prices) { var val = get(prices, key); print("Price of %$ is %$\n", key, val); } return 0; } ================================================ FILE: examples/threads.c ================================================ #include "Cello.h" /* Threaded Callback */ var say_hello(var args) { with (mutex in get(args, $I(0))) { println("Hello from %$!", current(Thread)); } return NULL; } int main(int argc, char** argv) { /* Create a Mutex */ var mutex = new(Mutex); /* Create Several Threads */ var threads = new(Array, Thread, new(Thread, $(Function, say_hello)), new(Thread, $(Function, say_hello)), new(Thread, $(Function, say_hello)), new(Thread, $(Function, say_hello))); /* Call each Thread */ foreach (t in threads) { call(t, mutex); } /* Wait for each Thread to finish */ foreach (t in threads) { join(t); } return 0; } ================================================ FILE: include/Cello.h ================================================ /* ** $$===================================================$$ ** || || ** || ,;, ___ _ _ || ** || +($)+ / __|___| | |___ || ** || +($)+ | (__/ -_) | / _ \ || ** || ||| lib \___\___|_|_\___/ || ** || ||| || ** || ||| /# High Level || ** || _|||_ /' Programming in C || ** || .' ||| '/ || ** || / ||| / \ http://libcello.org/ || ** || | |||/ | || ** || )_ ||/ _( Licensed under BSD || ** || _) |/| (_ || ** || ) /|| ( || ** || / }/+++ { \ || ** || | {/ ||| } | || ** || | / ~~~ | || ** || \/ \ / / || ** || /`-.__Y__.-` || ** || '- | Daniel Holden || ** || | || ** || ! contact@theorangeduck.com || ** || || ** || https://github.com/orangeduck/libCello || ** || || ** $$===================================================$$ */ #ifndef CELLO_H #define CELLO_H /* Settings */ #ifdef CELLO_NDEBUG #define CELLO_BOUND_CHECK 0 #define CELLO_MAGIC_CHECK 0 #define CELLO_ALLOC_CHECK 0 #define CELLO_NULL_CHECK 0 #define CELLO_METHOD_CHECK 0 #define CELLO_MEMORY_CHECK 0 #else #define CELLO_BOUND_CHECK 1 #define CELLO_MAGIC_CHECK 1 #define CELLO_ALLOC_CHECK 1 #define CELLO_NULL_CHECK 1 #define CELLO_METHOD_CHECK 1 #define CELLO_MEMORY_CHECK 1 #endif #if CELLO_ALLOC_CHECK == 1 #define CELLO_ALLOC_HEADER (var)AllocStatic, #else #define CELLO_ALLOC_HEADER #endif #if CELLO_MAGIC_CHECK == 1 #define CELLO_MAGIC_NUM 0xCe110 #define CELLO_MAGIC_HEADER ((var)CELLO_MAGIC_NUM), #else #define CELLO_MAGIC_HEADER #endif #ifndef CELLO_CACHE #define CELLO_CACHE 1 #define CELLO_CACHE_HEADER \ NULL, NULL, NULL, NULL, NULL, NULL, \ NULL, NULL, NULL, NULL, NULL, NULL, \ NULL, NULL, NULL, NULL, NULL, NULL, #define CELLO_CACHE_NUM 18 #else #define CELLO_CACHE 0 #define CELLO_CACHE_HEADER #define CELLO_CACHE_NUM 0 #endif #ifdef _WIN32 #define CELLO_WINDOWS #endif #ifdef __unix__ #define CELLO_UNIX #define CELLO_LINUX #endif #ifdef __APPLE__ #define CELLO_UNIX #define CELLO_MAC #endif #ifdef _MSC_VER #define CELLO_MSC #define popen _popen #define pclose _pclose #define __func__ __FUNCTION__ #ifndef CELLO_NSTRACE #pragma comment(lib, "DbgHelp.lib") #endif #endif #if defined __has_feature # if __has_feature(address_sanitizer) # if defined __clang__ # define CELLO_NASAN __attribute__((no_sanitize("address"))) # else # define CELLO_NASAN # endif # else # define CELLO_NASAN # endif #else # define CELLO_NASAN #endif /* Includes */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CELLO_WINDOWS #include #ifndef CELLO_NSTRACE #include #endif #endif #ifdef CELLO_UNIX #include #ifndef CELLO_NSTRACE #include #endif #endif /* Syntax */ typedef void* var; #define is == #define isnt != #define not ! #define and && #define or || #define in , /* Declaration */ #define Cello(T, ...) CelloStruct(T, ##__VA_ARGS__) #define CelloStruct(T, ...) CelloObject(T, sizeof(struct T), ##__VA_ARGS__) #define CelloEmpty(T, ...) CelloObject(T, 0, ##__VA_ARGS__) #define CelloObject(T, S, ...) (var)((char*)((var[]){ NULL, \ CELLO_ALLOC_HEADER \ CELLO_MAGIC_HEADER \ CELLO_CACHE_HEADER \ NULL, "__Name", #T, \ NULL, "__Size", (var)S, \ ##__VA_ARGS__, \ NULL, NULL, NULL}) + \ sizeof(struct Header)) #define Instance(I, ...) NULL, #I, &((struct I){__VA_ARGS__}) /* Types */ extern var Type; extern var Tuple; extern var Ref; extern var Box; extern var Int; extern var Float; extern var String; extern var Tree; extern var List; extern var Array; extern var Table; extern var Range; extern var Slice; extern var Zip; extern var Filter; extern var Map; extern var Terminal; extern var _; extern var File; extern var Mutex; extern var Thread; extern var Process; extern var Function; extern var Exception; extern var IOError; extern var KeyError; extern var BusyError; extern var TypeError; extern var ValueError; extern var ClassError; extern var FormatError; extern var ResourceError; extern var OutOfMemoryError; extern var IndexOutOfBoundsError; extern var SegmentationError; extern var ProgramAbortedError; extern var DivisionByZeroError; extern var IllegalInstructionError; extern var ProgramInterruptedError; extern var ProgramTerminationError; /* Data */ enum { AllocStatic = 0x01, AllocStack = 0x02, AllocHeap = 0x03, AllocData = 0x04 }; struct Header { var type; #if CELLO_ALLOC_CHECK == 1 var alloc; #endif #if CELLO_MAGIC_CHECK == 1 var magic; #endif }; struct Type { var cls; var name; var inst; }; struct Ref { var val; }; struct Box { var val; }; struct Int { int64_t val; }; struct Float { double val; }; struct String { char* val; }; struct Tuple { var* items; }; struct Range { var value; int64_t start; int64_t stop; int64_t step; }; struct Slice { var iter; var range; }; struct Zip { var iters; var values; }; struct Filter { var iter; var func; }; struct Map { var iter; var curr; var func; }; struct File { FILE* file; }; struct Process { FILE* proc; }; struct Function { var (*func)(var); }; /* Classes */ extern var Doc; extern var Help; extern var Cast; extern var Size; extern var Alloc; extern var New; extern var Copy; extern var Assign; extern var Swap; extern var Cmp; extern var Hash; extern var Len; extern var Iter; extern var Push; extern var Concat; extern var Get; extern var Sort; extern var Resize; extern var C_Str; extern var C_Int; extern var C_Float; extern var Stream; extern var Pointer; extern var Call; extern var Format; extern var Show; extern var Current; extern var Start; extern var Lock; extern var Mark; /* Signatures */ struct Example { const char* name; const char* body; }; struct Method { const char* name; const char* definition; const char* description; }; struct Doc { const char* (*name)(void); const char* (*brief)(void); const char* (*description)(void); const char* (*definition)(void); struct Example* (*examples)(void); struct Method* (*methods)(void); }; struct Help { int (*help_to)(var, var, int); }; struct Cast { var (*cast)(var, var); }; struct Size { size_t (*size)(void); }; struct Alloc { var (*alloc)(void); void (*dealloc)(var); }; struct New { void (*construct_with)(var, var); void (*destruct)(var); }; struct Copy { var (*copy)(var); }; struct Assign { void (*assign)(var, var); }; struct Swap { void (*swap)(var, var); }; struct Cmp { int (*cmp)(var, var); }; struct Hash { uint64_t (*hash)(var); }; struct Len { size_t (*len)(var); }; struct Push { void (*push)(var, var); void (*pop)(var); void (*push_at)(var, var, var); void (*pop_at)(var, var); }; struct Concat { void (*concat)(var, var); void (*append)(var, var); }; struct Get { var (*get)(var, var); void (*set)(var, var, var); bool (*mem)(var, var); void (*rem)(var, var); var (*key_type)(var); var (*val_type)(var); }; struct Iter { var (*iter_init)(var); var (*iter_next)(var, var); var (*iter_last)(var); var (*iter_prev)(var, var); var (*iter_type)(var); }; struct Sort { void (*sort_by)(var,bool(*f)(var,var)); }; struct Resize { void (*resize)(var, size_t); }; struct C_Str { char* (*c_str)(var); }; struct C_Int { int64_t (*c_int)(var); }; struct C_Float { double (*c_float)(var); }; struct Stream { var (*sopen)(var,var,var); void (*sclose)(var); void (*sseek)(var,int64_t,int); int64_t (*stell)(var); void (*sflush)(var); bool (*seof)(var); size_t (*sread)(var,void*,size_t); size_t (*swrite)(var,void*,size_t); }; struct Pointer { void (*ref)(var, var); var (*deref)(var); }; struct Call { var (*call_with)(var, var); }; struct Format { int (*format_to)(var,int,const char*,va_list); int (*format_from)(var,int,const char*,va_list); }; struct Show { int (*show)(var, var, int); int (*look)(var, var, int); }; struct Current { var (*current)(void); }; struct Start { void (*start)(var); void (*stop)(var); void (*join)(var); bool (*running)(var); }; struct Lock { void (*lock)(var); void (*unlock)(var); bool (*trylock)(var); }; struct Mark { void (*mark)(var, var, void(*)(var,void*)); }; /* Functions */ const char* name(var type); const char* brief(var type); const char* description(var type); const char* definition(var type); void help(var self); int help_to(var out, int pos, var self); var type_of(var self); var cast(var self, var type); var instance(var self, var cls); bool implements(var self, var cls); var type_instance(var type, var cls); bool type_implements(var type, var cls); #define method(X, C, M, ...) \ ((struct C*)method_at_offset(X, C, \ offsetof(struct C, M), #M))->M(X, ##__VA_ARGS__) #define implements_method(X, C, M) \ implements_method_at_offset(X, C, offsetof(struct C, M)) #define type_method(T, C, M, ...) \ ((struct C*)type_method_at_offset(T, C, \ offsetof(struct C, M), #M))->M(__VA_ARGS__) #define type_implements_method(T, C, M) \ type_implements_method_at_offset(T, C, offsetof(struct C, M)) var method_at_offset(var self, var cls, size_t offset, const char* method); bool implements_method_at_offset(var self, var cls, size_t offset); var type_method_at_offset(var self, var cls, size_t offset, const char* method); bool type_implements_method_at_offset(var self, var cls, size_t offset); struct Header* header(var self); var header_init(var head, var type, int alloc); size_t size(var type); var alloc(var type); var alloc_raw(var type); var alloc_root(var type); #define alloc_stack(T) ((struct T*)header_init( \ (char[sizeof(struct Header) + sizeof(struct T)]){0}, T, AllocStack)) void dealloc(var self); void dealloc_raw(var self); void dealloc_root(var self); #define $(T, ...) ((struct T*)memcpy( \ alloc_stack(T), &((struct T){__VA_ARGS__}), sizeof(struct T))) #define $I(X) $(Int, X) #define $F(X) $(Float, X) #define $S(X) $(String, X) #define $R(X) $(Ref, X) #define $B(X) $(Box, X) #define tuple(...) tuple_xp(tuple_in, (_, ##__VA_ARGS__, Terminal)) #define tuple_xp(X, A) X A #define tuple_in(_, ...) $(Tuple, (var[]){ __VA_ARGS__ }) #define construct(self, ...) construct_with(self, tuple(__VA_ARGS__)) var construct_with(var self, var args); var destruct(var self); #define new(T, ...) ((struct T*)new_with(T, tuple(__VA_ARGS__))) #define new_raw(T, ...) ((struct T*)new_raw_with(T, tuple(__VA_ARGS__))) #define new_root(T, ...) ((struct T*)new_root_with(T, tuple(__VA_ARGS__))) var new_with(var type, var args); var new_raw_with(var type, var args); var new_root_with(var type, var args); void del(var self); void del_raw(var self); void del_root(var self); var assign(var self, var obj); var copy(var self); void swap(var self, var obj); int cmp(var self, var obj); bool eq(var self, var obj); bool neq(var self, var obj); bool gt(var self, var obj); bool lt(var self, var obj); bool ge(var self, var obj); bool le(var self, var obj); uint64_t hash(var self); uint64_t hash_data(const void* data, size_t num); var iter_init(var self); var iter_next(var self, var curr); var iter_prev(var self, var curr); var iter_last(var self); var iter_type(var self); #define foreach(...) foreach_xp(foreach_in, (__VA_ARGS__)) #define foreach_xp(X, A) X A #define foreach_in(X, S) for(var \ __##X = (S), \ __Iter##X = instance(__##X, Iter), \ X = ((struct Iter*)(__Iter##X))->iter_init(__##X); \ X isnt Terminal; \ X = ((struct Iter*)(__Iter##X))->iter_next(__##X, X)) void push(var self, var obj); void pop(var self); void push_at(var self, var obj, var key); void pop_at(var self, var key); void sort(var self); void sort_by(var self, bool(*f)(var,var)); void append(var self, var obj); void concat(var self, var obj); var get(var self, var key); void set(var self, var key, var val); bool mem(var self, var key); void rem(var self, var key); var key_type(var self); var val_type(var self); void resize(var self, size_t n); size_t len(var self); bool empty(var self); char* c_str(var self); int64_t c_int(var self); double c_float(var self); #define range(...) range_stack($(Range, $I(0), 0, 0, 0), tuple(__VA_ARGS__)) #define slice(I, ...) slice_xp(slice_in, (I, ##__VA_ARGS__)) #define slice_xp(X, A) X A #define slice_in(...) slice_stack( \ $(Slice, NULL, $(Range, $I(0), 0, 0, 0)), tuple(__VA_ARGS__)) #define reverse(I) slice(I, _, _, $I(-1)) #define filter(I, F) $(Filter, I, F) #define map(I, F) $(Map, I, NULL, F) #define zip(...) zip_stack( \ $(Zip, tuple(__VA_ARGS__), \ $(Tuple, (var[(sizeof((var[]){__VA_ARGS__})/sizeof(var))+1]){0}))) #define enumerate(I) enumerate_stack(zip(range(), I)) var range_stack(var self, var args); var slice_stack(var self, var args); var zip_stack(var self); var enumerate_stack(var self); var sopen(var self, var resource, var options); void sclose(var self); void sseek(var self, int64_t pos, int origin); int64_t stell(var self); void sflush(var self); bool seof(var self); size_t sread(var self, void* output, size_t size); size_t swrite(var self, void* input, size_t size); void ref(var self, var item); var deref(var self); #define call(self, ...) call_with(self, tuple(__VA_ARGS__)) var call_with(var self, var args); int format_to_va(var self, int pos, const char* fmt, va_list va); int format_from_va(var self, int pos, const char* fmt, va_list va); int format_to(var self, int pos, const char* fmt, ...); int format_from(var self, int pos, const char* fmt, ...); int show(var self); int show_to(var self, var out, int pos); #define print(fmt, ...) print_with(fmt, tuple(__VA_ARGS__)) #define println(fmt, ...) println_with(fmt, tuple(__VA_ARGS__)) #define print_to(out, pos, fmt, ...) \ print_to_with(out, pos, fmt, tuple(__VA_ARGS__)) int print_with(const char* fmt, var args); int println_with(const char* fmt, var args); int print_to_with(var out, int pos, const char* fmt, var args); int look(var self); int look_from(var self, var input, int pos); #define scan(fmt, ...) scan_with(fmt, tuple(__VA_ARGS__)) #define scanln(fmt, ...) scanln_with(fmt, tuple(__VA_ARGS__)) #define scan_from(input, pos, fmt, ...) \ scan_from_with(input, pos, fmt, tuple(__VA_ARGS__)) int scan_with(const char* fmt, var args); int scanln_with(const char* fmt, var args); int scan_from_with(var input, int pos, const char* fmt, var args); var current(var type); void start(var self); void stop(var self); void join(var self); bool running(var self); var start_in(var self); var stop_in(var self); #define with(...) with_xp(with_in, (__VA_ARGS__)) #define with_xp(X, A) X A #define with_in(X, S) for(var X = start_in(S); X isnt NULL; X = stop_in(X)) void lock(var self); bool trylock(var self); void unlock(var self); #define try { jmp_buf __env; exception_try(&__env); if (!setjmp(__env)) #define catch(...) catch_xp(catch_in, (__VA_ARGS__)) #define catch_xp(X, A) X A #define catch_in(X, ...) else { exception_try_fail(); } exception_try_end(); } \ for (var X = exception_catch(tuple(__VA_ARGS__)); \ X isnt NULL; X = NULL) #define throw(E, F, ...) exception_throw(E, F, tuple(__VA_ARGS__)) void exception_try(jmp_buf* env); void exception_try_end(void); void exception_try_fail(void); var exception_throw(var obj, const char* fmt, var args); var exception_catch(var args); void exception_signals(void); var exception_object(void); var exception_message(void); void mark(var self, var gc, void(*f)(var,void*)); #ifndef CELLO_NGC extern var GC; int Cello_Main(int argc, char** argv); void Cello_Exit(void); #define main(...) \ main(int argc, char** argv) { \ var bottom = NULL; \ new_raw(GC, $R(&bottom)); \ atexit(Cello_Exit); \ return Cello_Main(argc, argv); \ }; \ int Cello_Main(__VA_ARGS__) #endif #endif ================================================ FILE: src/Alloc.c ================================================ #include "Cello.h" struct Header* header(var self) { return (struct Header*)((char*)self - sizeof(struct Header)); } var header_init(var head, var type, int alloc) { struct Header* self = head; self->type = type; #if CELLO_ALLOC_CHECK == 1 self->alloc = (var)(intptr_t)alloc; #endif #if CELLO_MAGIC_CHECK == 1 self->magic = (var)CELLO_MAGIC_NUM; #endif return ((char*)self) + sizeof(struct Header); } static const char* Alloc_Name(void) { return "Alloc"; } static const char* Alloc_Brief(void) { return "Memory Allocation"; } static const char* Alloc_Description(void) { return "The `Alloc` class can be used to override how memory is allocated for a " "given data type. By default memory is allocated using `calloc` along with " "the `Size` class to determine the amount of memory to allocate." "\n\n" "A custom allocator should be careful to also initialise the header for " "the allocated memory using the function `header_init`. Cello objects " "without a header wont be recognised as such as so will throw errors when " "used with Cello functions." "\n\n" "Allocated memory is automatically registered with the garbage collector " "unless the functions `alloc_raw` and `dealloc_raw` are used." ; } static const char* Alloc_Definition(void) { return "struct Alloc {\n" " var (*alloc)(void);\n" " void (*dealloc)(var);\n" "};"; } static struct Example* Alloc_Examples(void) { static struct Example examples[] = { { "Usage", "/* Allocation deallocated by Garbage Collector */\n" "var x = alloc(Int);\n" "construct(x, $I(10));\n", }, { "Avoid Garbage Collection", "/* Allocation must be manually deallocated */\n" "var x = alloc_raw(Int);\n" "construct(x, $I(10));\n" "destruct(x);\n" "dealloc_raw(x);\n", }, {NULL, NULL} }; return examples; } static struct Method* Alloc_Methods(void) { static struct Method methods[] = { { "$", "#define $(T, ...)\n" "#define $I(X)\n" "#define $F(X)\n" "#define $S(X)\n" "#define $R(X)\n" "#define $B(X)", "Allocate memory for the given type `T` on the stack and copy in the " "given arguments `...` as struct members. Shorthand constructors exist " "for native types:\n\n* `$I -> Int` `$F -> Float` `$S -> String`\n*" " `$R -> Ref` `$B -> Box`\n\n" }, { "alloc", "#define alloc_stack(T)\n" "var alloc(var type);\n" "var alloc_raw(var type);\n" "var alloc_root(var type);", "Allocate memory for a given `type`. To avoid the Garbage Collector " "completely use `alloc_raw`, to register the allocation as a root use " "`alloc_root`. In the case of raw or root allocations the corresponding " "`dealloc` function should be used when done. Memory allocated with " "`alloc_stack` is not managed by the Garbage Collector." }, { "dealloc", "void dealloc(var self);\n" "void dealloc_raw(var self);\n" "void dealloc_root(var self);", "Deallocate memory for object `self` manually. If registered with the " "Garbage Collector then entry will be removed. If the `raw` variation is " "used memory will be deallocated without going via the Garbage Collector." }, {NULL, NULL, NULL} }; return methods; } var Alloc = Cello(Alloc, Instance(Doc, Alloc_Name, Alloc_Brief, Alloc_Description, Alloc_Definition, Alloc_Examples, Alloc_Methods)); enum { ALLOC_STANDARD, ALLOC_RAW, ALLOC_ROOT }; static var alloc_by(var type, int method) { struct Alloc* a = type_instance(type, Alloc); var self; if (a and a->alloc) { self = a->alloc(); } else { struct Header* head = calloc(1, sizeof(struct Header) + size(type)); #if CELLO_MEMORY_CHECK == 1 if (head is NULL) { throw(OutOfMemoryError, "Cannot create new '%s', out of memory!", type); } #endif self = header_init(head, type, AllocHeap); } switch (method) { case ALLOC_STANDARD: #ifndef CELLO_NGC set(current(GC), self, $I(0)); #endif break; case ALLOC_RAW: break; case ALLOC_ROOT: #ifndef CELLO_NGC set(current(GC), self, $I(1)); #endif break; } return self; } var alloc(var type) { return alloc_by(type, ALLOC_STANDARD); } var alloc_raw(var type) { return alloc_by(type, ALLOC_RAW); } var alloc_root(var type) { return alloc_by(type, ALLOC_ROOT); } void dealloc(var self) { struct Alloc* a = instance(self, Alloc); if (a and a->dealloc) { a->dealloc(self); return; } #if CELLO_ALLOC_CHECK == 1 if (self is NULL) { throw(ResourceError, "Attempt to deallocate NULL!"); } if (header(self)->alloc is (var)AllocStatic) { throw(ResourceError, "Attempt to deallocate %$ " "which was allocated statically!", self); } if (header(self)->alloc is (var)AllocStack) { throw(ResourceError, "Attempt to deallocate %$ " "which was allocated on the stack!", self); } if (header(self)->alloc is (var)AllocData) { throw(ResourceError, "Attempt to deallocate %$ " "which was allocated inside a data structure!", self); } #endif #if CELLO_ALLOC_CHECK == 1 size_t s = size(type_of(self)); for (size_t i = 0; i < (sizeof(struct Header) + s) / sizeof(var); i++) { ((var*)header(self))[i] = (var)0xDeadCe110; } #endif free(((char*)self) - sizeof(struct Header)); } void dealloc_raw(var self) { dealloc(self); } void dealloc_root(var self) { dealloc(self); } static const char* New_Name(void) { return "New"; } static const char* New_Brief(void) { return "Construction and Destruction"; } static const char* New_Description(void) { return "The `New` class allows the user to define constructors and destructors " "for a type, accessible via `new` and `del`. Objects allocated with `new` " "are allocated on the heap and also registered with the Garbage Collector " "this means technically it isn't required to call `del` on them as they " "will be cleaned up at a later date." "\n\n" "The `new_root` function can be called to register a variable with the " "Garbage Collector but to indicate that it will be manually destructed " "with `del_root` by the user. This should be used for variables that wont " "be reachable by the Garbage Collector such as those in the data segment " "or only accessible via vanilla C structures." "\n\n" "The `new_raw` and `del_raw` functions can be called to construct and " "destruct objects without going via the Garbage Collector." "\n\n" "It is also possible to simply call the `construct` and `destruct` " "functions if you wish to construct an already allocated object." "\n\n" "Constructors should assume that memory is zero'd for an object but " "nothing else." ; } static const char* New_Definition(void) { return "struct New {\n" " void (*construct_with)(var, var);\n" " void (*destruct)(var);\n" "};\n"; } static struct Example* New_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Int, $I(1));\n" "show(x); /* 1 */\n" "show(type_of(x)); /* Int */\n" "\n" "var y = alloc(Float);\n" "construct(y, $F(1.0));\n" "show(y); /* 1.0 */\n" "destruct(y);\n" }, {NULL, NULL} }; return examples; } static struct Method* New_Methods(void) { static struct Method methods[] = { { "new", "#define new(T, ...)\n" "#define new_raw(T, ...)\n" "#define new_root(T, ...)\n" "var new_with(var type, var args);\n" "var new_raw_with(var type, var args);\n" "var new_root_with(var type, var args);", "Construct a new object of a given `type`. Use `new_raw` to avoid the " "Garbage Collector completely, and `new_root` to register the allocation " "as a Garbage Collection root. In the case of raw and root allocations " "they must be destructed with the corresponding deletion functions." }, { "del", "void del(var self);\n" "void del_raw(var self);\n" "void del_root(var self);", "Destruct the object `self` manually. If registered with the " "Garbage Collector then entry will be removed. If `del_raw` is used then" "the destruction will be done without going via the Garbage Collector." }, { "construct", "#define construct(self, ...)\n" "var construct_with(var self, var args);", "Call the constructor on object `self` which has already been allocated." }, { "destruct", "var destruct(var self);", "Call the destructor on object `self` without deallocating the memory " "for it." }, {NULL, NULL, NULL} }; return methods; } var New = Cello(New, Instance(Doc, New_Name, New_Brief, New_Description, New_Definition, New_Examples, New_Methods)); var construct_with(var self, var args) { struct New* n = instance(self, New); if (n and n->construct_with) { n->construct_with(self, args); } else if (len(args) == 1) { assign(self, get(args, $I(0))); } return self; } var destruct(var self) { struct New* n = instance(self, New); if (n and n->destruct) { n->destruct(self); } return self; } var new_with(var type, var args) { return construct_with(alloc(type), args); } var new_raw_with(var type, var args) { return construct_with(alloc_raw(type), args); } var new_root_with(var type, var args) { return construct_with(alloc_root(type), args); } static void del_by(var self, int method) { switch (method) { case ALLOC_STANDARD: case ALLOC_ROOT: #ifndef CELLO_NGC rem(current(GC), self); return; #endif break; case ALLOC_RAW: break; } dealloc(destruct(self)); } void del(var self) { del_by(self, ALLOC_STANDARD); } void del_raw(var self) { del_by(self, ALLOC_RAW); } void del_root(var self) { del_by(self, ALLOC_ROOT); } static const char* Copy_Name(void) { return "Copy"; } static const char* Copy_Brief(void) { return "Copyable"; } static const char* Copy_Description(void) { return "The `Copy` class can be used to override the behaviour of an object when " "a copy is made of it. By default the `Copy` class allocates a new empty " "object of the same type and uses the `Assign` class to set the " "contents. The copy is then registered with the Garbage Collector as if it " "had been constructed with `new`. This means when using manual memory " "management a copy must be deleted manually." "\n\n" "If the `copy` class is overridden then the implementer may manually have " "to register the object with the Garbage Collector if they wish for it to " "be tracked." "\n\n" "By convention `copy` follows the semantics of `Assign`, which typically " "means a _deep copy_ should be made, and that an object will create a " "copy of all of the sub-objects it references or contains - although this " "could vary depending on the type's overridden behaviours."; } static const char* Copy_Definition(void) { return "struct Copy {\n" " var (*copy)(var);\n" "};\n"; } static struct Example* Copy_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(String, $S(\"Hello\"));\n" "var y = copy(x);\n" "show(x); /* Hello */\n" "show(y); /* Hello */\n" "show($I(eq(x, y))); /* 1 */\n" "show($I(x is y)); /* 0 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Copy_Methods(void) { static struct Method methods[] = { { "copy", "var copy(var self);", "Make a copy of the object `self`." }, {NULL, NULL, NULL} }; return methods; } var Copy = Cello(Copy, Instance(Doc, Copy_Name, Copy_Brief, Copy_Description, Copy_Definition, Copy_Examples, Copy_Methods)); var copy(var self) { struct Copy* c = instance(self, Copy); if (c and c->copy) { return c->copy(self); } return assign(alloc(type_of(self)), self); } ================================================ FILE: src/Array.c ================================================ #include "Cello.h" static const char* Array_Name(void) { return "Array"; } static const char* Array_Brief(void) { return "Sequential Container"; } static const char* Array_Description(void) { return "" "The `Array` type is data structure containing a sequence of a single type " "of object. It can dynamically grow and shrink in size depending on how " "many elements it contains. It allocates storage for the type specified. " "It also deallocates and destroys the objects inside upon destruction." "\n\n" "Elements are copied into an Array using `assign` and will initially have " "zero'd memory." "\n\n" "Elements are ordered linearly. Elements are accessed by their position in " "this sequence directly. Addition and removal of elements at the end of " "the sequence is fast, with memory movement required for elements in the " "middle of the sequence." "\n\n" "This is largely equivalent to the C++ construct " "[std::vector](http://www.cplusplus.com/reference/vector/vector/)"; } static struct Example* Array_Examples(void) { static struct Example examples[] = { { "Construction & Deletion", "var x = new(Array, Int);\n" "push(x, $I(32));\n" "push(x, $I(6));\n" "\n" "/* <'Array' At 0x0000000000414603 [32, 6]> */\n" "show(x);\n", }, { "Element Access", "var x = new(Array, Float, $F(0.01), $F(5.12));\n" "\n" "show(get(x, $I(0))); /* 0.01 */\n" "show(get(x, $I(1))); /* 5.12 */\n" "\n" "set(x, $I(0), $F(500.1));\n" "show(get(x, $I(0))); /* 500.1 */\n", }, { "Membership", "var x = new(Array, Int, $I(1), $I(2), $I(3), $I(4));\n" "\n" "show($I(mem(x, $I(1)))); /* 1 */\n" "show($I(len(x))); /* 4 */\n" "\n" "rem(x, $I(3));\n" "\n" "show($I(mem(x, $I(3)))); /* 0 */\n" "show($I(len(x))); /* 3 */\n" "show($I(empty(x))); /* 0 */\n" "\n" "resize(x, 0);\n" "\n" "show($I(empty(x))); /* 1 */\n", }, { "Iteration", "var greetings = new(Array, String, \n" " $S(\"Hello\"), $S(\"Bonjour\"), $S(\"Hej\"));\n" "\n" "foreach(greet in greetings) {\n" " show(greet);\n" "}\n", }, {NULL, NULL} }; return examples; } struct Array { var type; var data; size_t tsize; size_t nitems; size_t nslots; }; static size_t Array_Step(struct Array* a) { return a->tsize + sizeof(struct Header); } static var Array_Item(struct Array* a, size_t i) { return (char*)a->data + Array_Step(a) * i + sizeof(struct Header); } static void Array_Alloc(struct Array* a, size_t i) { memset((char*)a->data + Array_Step(a) * i, 0, Array_Step(a)); struct Header* head = (struct Header*)((char*)a->data + Array_Step(a) * i); header_init(head, a->type, AllocData); } static size_t Array_Size_Round(size_t s) { return ((s + sizeof(var) - 1) / sizeof(var)) * sizeof(var); } static void Array_New(var self, var args) { struct Array* a = self; a->type = cast(get(args, $I(0)), Type); a->tsize = Array_Size_Round(size(a->type)); a->nitems = len(args)-1; a->nslots = a->nitems; if (a->nslots is 0) { a->data = NULL; return; } a->data = malloc(a->nslots * Array_Step(a)); #if CELLO_MEMORY_CHECK == 1 if (a->data is NULL) { throw(OutOfMemoryError, "Cannot allocate Array, out of memory!"); } #endif for(size_t i = 0; i < a->nitems; i++) { Array_Alloc(a, i); assign(Array_Item(a, i), get(args, $I(i+1))); } } static void Array_Del(var self) { struct Array* a = self; for(size_t i = 0; i < a->nitems; i++) { destruct(Array_Item(a, i)); } free(a->data); } static void Array_Clear(var self) { struct Array* a = self; for(size_t i = 0; i < a->nitems; i++) { destruct(Array_Item(a, i)); } free(a->data); a->data = NULL; a->nitems = 0; a->nslots = 0; } static void Array_Push(var self, var obj); static void Array_Assign(var self, var obj) { struct Array* a = self; Array_Clear(self); a->type = implements_method(obj, Iter, iter_type) ? iter_type(obj) : Ref; a->tsize = Array_Size_Round(size(a->type)); a->nitems = 0; a->nslots = 0; if (implements_method(obj, Len, len) and implements_method(obj, Get, get)) { a->nitems = len(obj); a->nslots = a->nitems; if (a->nslots is 0) { a->data = NULL; return; } a->data = malloc(a->nslots * Array_Step(a)); #if CELLO_MEMORY_CHECK == 1 if (a->data is NULL) { throw(OutOfMemoryError, "Cannot allocate Array, out of memory!"); } #endif for(size_t i = 0; i < a->nitems; i++) { Array_Alloc(a, i); assign(Array_Item(a, i), get(obj, $I(i))); } } else { foreach (item in obj) { Array_Push(self, item); } } } static void Array_Reserve_More(struct Array* a) { if (a->nitems > a->nslots) { a->nslots = a->nitems + a->nitems / 2; a->data = realloc(a->data, Array_Step(a) * a->nslots); #if CELLO_MEMORY_CHECK == 1 if (a->data is NULL) { throw(OutOfMemoryError, "Cannot grow Array, out of memory!"); } #endif } } static void Array_Concat(var self, var obj) { struct Array* a = self; size_t i = 0; size_t olen = len(obj); a->nitems += olen; Array_Reserve_More(a); foreach (item in obj) { Array_Alloc(a, a->nitems-olen+i); assign(Array_Item(a, a->nitems-olen+i), item); i++; } } static var Array_Iter_Init(var self); static var Array_Iter_Next(var self, var curr); static int Array_Cmp(var self, var obj) { var item0 = Array_Iter_Init(self); var item1 = iter_init(obj); while (true) { if (item0 is Terminal and item1 is Terminal) { return 0; } if (item0 is Terminal) { return -1; } if (item1 is Terminal) { return 1; } int c = cmp(item0, item1); if (c < 0) { return -1; } if (c > 0) { return 1; } item0 = Array_Iter_Next(self, item0); item1 = iter_next(obj, item1); } return 0; } static uint64_t Array_Hash(var self) { struct Array* a = self; uint64_t h = 0; for (size_t i = 0; i < a->nitems; i++) { h ^= hash(Array_Item(a, i)); } return h; } static size_t Array_Len(var self) { struct Array* a = self; return a->nitems; } static bool Array_Mem(var self, var obj) { struct Array* a = self; for(size_t i = 0; i < a->nitems; i++) { if (eq(Array_Item(a, i), obj)) { return true; } } return false; } static void Array_Reserve_Less(struct Array* a) { if (a->nslots > a->nitems + a->nitems / 2) { a->nslots = a->nitems; a->data = realloc(a->data, Array_Step(a) * a->nslots); } } static void Array_Pop_At(var self, var key) { struct Array* a = self; int64_t i = c_int(key); i = i < 0 ? a->nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)a->nitems) { throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Array of size %i.", key, $I(a->nitems)); return; } #endif destruct(Array_Item(a, i)); memmove((char*)a->data + Array_Step(a) * (i+0), (char*)a->data + Array_Step(a) * (i+1), Array_Step(a) * ((a->nitems-1) - i)); a->nitems--; Array_Reserve_Less(a); } static void Array_Rem(var self, var obj) { struct Array* a = self; for(size_t i = 0; i < a->nitems; i++) { if (eq(Array_Item(a, i), obj)) { Array_Pop_At(a, $I(i)); return; } } throw(ValueError, "Object %$ not in Array!", obj); } static void Array_Push(var self, var obj) { struct Array* a = self; a->nitems++; Array_Reserve_More(a); Array_Alloc(a, a->nitems-1); assign(Array_Item(a, a->nitems-1), obj); } static void Array_Push_At(var self, var obj, var key) { struct Array* a = self; a->nitems++; Array_Reserve_More(a); int64_t i = c_int(key); i = i < 0 ? a->nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)a->nitems) { throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Array of size %i.", key, $I(a->nitems)); return; } #endif memmove((char*)a->data + Array_Step(a) * (i+1), (char*)a->data + Array_Step(a) * (i+0), Array_Step(a) * ((a->nitems-1) - i)); Array_Alloc(self, i); assign(Array_Item(a, i), obj); } static void Array_Pop(var self) { struct Array* a = self; #if CELLO_BOUND_CHECK == 1 if (a->nitems is 0) { throw(IndexOutOfBoundsError, "Cannot pop. Array is empty!"); return; } #endif destruct(Array_Item(a, a->nitems-1)); a->nitems--; Array_Reserve_Less(a); } static var Array_Get(var self, var key) { struct Array* a = self; int64_t i = c_int(key); i = i < 0 ? a->nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)a->nitems) { return throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Array of size %i.", key, $I(a->nitems)); } #endif return Array_Item(a, i); } static void Array_Set(var self, var key, var val) { struct Array* a = self; int64_t i = c_int(key); i = i < 0 ? a->nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)a->nitems) { throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Array of size %i.", key, $I(a->nitems)); return; } #endif assign(Array_Item(a, i), val); } static var Array_Iter_Init(var self) { struct Array* a = self; if (a->nitems is 0) { return Terminal; } return Array_Item(a, 0); } static var Array_Iter_Next(var self, var curr) { struct Array* a = self; if (curr >= Array_Item(a, a->nitems-1)) { return Terminal; } else { return (char*)curr + Array_Step(a); } } static var Array_Iter_Last(var self) { struct Array* a = self; if (a->nitems is 0) { return Terminal; } return Array_Item(a, a->nitems-1); } static var Array_Iter_Prev(var self, var curr) { struct Array* a = self; if (curr < Array_Item(a, 0)) { return Terminal; } else { return (char*)curr - Array_Step(a); } } static var Array_Iter_Type(var self) { struct Array* a = self; return a->type; } static size_t Array_Sort_Partition( struct Array* a, int64_t l, int64_t r, bool(*f)(var,var)) { int64_t p = l + (r - l) / 2; swap(Array_Item(a, p), Array_Item(a, r)); int64_t s = l; for (int64_t i = l; i < r; i++) { if (f(Array_Get(a, $I(i)), Array_Item(a, r))) { swap(Array_Item(a, i), Array_Item(a, s)); s++; } } swap(Array_Item(a, s), Array_Item(a, r)); return s; } static void Array_Sort_Part(struct Array* a, int64_t l, int64_t r, bool(*f)(var,var)) { if (l < r) { int64_t s = Array_Sort_Partition(a, l, r, f); Array_Sort_Part(a, l, s-1, f); Array_Sort_Part(a, s+1, r, f); } } static void Array_Sort_By(var self, bool(*f)(var,var)) { Array_Sort_Part(self, 0, Array_Len(self)-1, f); } static int Array_Show(var self, var output, int pos) { struct Array* a = self; pos = print_to(output, pos, "<'Array' At 0x%p [", self); for (size_t i = 0; i < a->nitems; i++) { pos = print_to(output, pos, "%$", Array_Item(a, i)); if (i < a->nitems-1) { pos = print_to(output, pos, ", "); } } return print_to(output, pos, "]>"); } static void Array_Resize(var self, size_t n) { struct Array* a = self; if (n is 0) { Array_Clear(self); return; } while (n < a->nitems) { destruct(Array_Item(a, a->nitems-1)); a->nitems--; } a->nslots = n; a->data = realloc(a->data, Array_Step(a) * a->nslots); #if CELLO_MEMORY_CHECK == 1 if (a->data is NULL) { throw(OutOfMemoryError, "Cannot grow Array, out of memory!"); } #endif } static void Array_Mark(var self, var gc, void(*f)(var,void*)) { struct Array* a = self; for (size_t i = 0; i < a->nitems; i++) { f(gc, Array_Item(a, i)); } } var Array = Cello(Array, Instance(Doc, Array_Name, Array_Brief, Array_Description, NULL, Array_Examples, NULL), Instance(New, Array_New, Array_Del), Instance(Assign, Array_Assign), Instance(Mark, Array_Mark), Instance(Cmp, Array_Cmp), Instance(Hash, Array_Hash), Instance(Push, Array_Push, Array_Pop, Array_Push_At, Array_Pop_At), Instance(Concat, Array_Concat, Array_Push), Instance(Len, Array_Len), Instance(Get, Array_Get, Array_Set, Array_Mem, Array_Rem), Instance(Iter, Array_Iter_Init, Array_Iter_Next, Array_Iter_Last, Array_Iter_Prev, Array_Iter_Type), Instance(Sort, Array_Sort_By), Instance(Show, Array_Show, NULL), Instance(Resize, Array_Resize)); ================================================ FILE: src/Assign.c ================================================ #include "Cello.h" static const char* Assign_Name(void) { return "Assign"; } static const char* Assign_Brief(void) { return "Assignment"; } static const char* Assign_Description(void) { return "`Assign` is potentially the most important class in Cello. It is used " "throughout Cello to initialise objects using other objects. In C++ this is " "called the _copy constructor_ and it is used to assign the value of one " "object to another." "\n\n" "By default the `Assign` class uses the `Size` class to copy the memory " "from one object to another. But for more complex objects which maintain " "their own behaviours and state this may need to be overridden." "\n\n" "The most important thing about the `Assign` class is that it must work on " "the assumption that the target object may not have had it's constructor " "called and could be uninitialised with just zero'd memory. This is often " "the case when copying contents into containers."; } static const char* Assign_Definition(void) { return "struct Assign {\n" " void (*assign)(var, var);\n" "};\n"; } static struct Example* Assign_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Int, $I(10));\n" "var y = new(Int, $I(20));\n" "\n" "show(x); /* 10 */\n" "show(y); /* 20 */\n" "\n" "assign(x, y);\n" "\n" "show(x); /* 20 */\n" "show(y); /* 20 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Assign_Methods(void) { static struct Method methods[] = { { "assign", "var assign(var self, var obj);", "Assign the object `obj` to the object `self`. The assigned object " "`self` is returned." }, {NULL, NULL, NULL} }; return methods; } var Assign = Cello(Assign, Instance(Doc, Assign_Name, Assign_Brief, Assign_Description, Assign_Definition, Assign_Examples, Assign_Methods)); var assign(var self, var obj) { struct Assign* a = instance(self, Assign); if (a and a->assign) { a->assign(self, obj); return self; } size_t s = size(type_of(self)); if (type_of(self) is type_of(obj) and s) { return memcpy(self, obj, s); } return throw(TypeError, "Cannot assign type %s to type %s", type_of(obj), type_of(self)); } static const char* Swap_Name(void) { return "Swap"; } static const char* Swap_Brief(void) { return "Swapable"; } static const char* Swap_Description(void) { return "The `Swap` class can be used to override the behaviour of swapping two " "objects. By default the `Swap` class simply swaps the memory of the " "two objects passed in as parameters making use of the `Size` class. " "In almost all cases this default behaviour should be fine, even if the " "objects have custom assignment functions." "\n\n" "Swapping can be used internally by various collections and algorithms."; } static const char* Swap_Definition(void) { return "struct Swap {\n" " void (*swap)(var, var);\n" "};\n"; } static struct Example* Swap_Examples(void) { static struct Example examples[] = { { "Usage", "var x = $S(\"Hello\");\n" "var y = $S(\"World\");\n" "show(x); /* Hello */\n" "show(y); /* World */\n" "swap(x, y);\n" "show(x); /* World */\n" "show(y); /* Hello */\n" }, {NULL, NULL} }; return examples; } static struct Method* Swap_Methods(void) { static struct Method methods[] = { { "swap", "void swap(var self, var obj);", "Swap the object `self` for the object `obj`." }, {NULL, NULL, NULL} }; return methods; } var Swap = Cello(Swap, Instance(Doc, Swap_Name, Swap_Brief, Swap_Description, Swap_Definition, Swap_Examples, Swap_Methods)); static void memswap(void* p0, void* p1, size_t s) { if (p0 == p1) { return; } for (size_t i = 0; i < s; i++) { char t = ((char*)p0)[i]; ((char*)p0)[i] = ((char*)p1)[i]; ((char*)p1)[i] = t; } } void swap(var self, var obj) { struct Swap* s = instance(self, Swap); if (s and s->swap) { s->swap(self, obj); return; } size_t n = size(type_of(self)); if (type_of(self) is type_of(obj) and n) { memswap(self, obj, n); return; } throw(TypeError, "Cannot swap type %s and type %s", type_of(obj), type_of(self)); } ================================================ FILE: src/Cmp.c ================================================ #include "Cello.h" static const char* Cmp_Name(void) { return "Cmp"; } static const char* Cmp_Brief(void) { return "Comparison"; } static const char* Cmp_Description(void) { return "The `Cmp` class is used to define comparison between two object values. " "This class is important as it is used by many data structures to test " "equality or ordering of objects." "\n\n" "By default, if passed two objects of the same type, the `Cmp` class will " "simply compare the raw memory of both objects, using the `Size` " "class." "\n\n" "To implement this class a `cmp` function must be provided which returns " "`< 0` if the first object is _less than_ the second, `> 0` if the first " "object is _greater than_ the second, and `0` if they are _equal_. " "\n\n" "For objects that manage their own data this class may need to be " "overridden to ensure that objects of the same _value_ are still treated " "as equal. E.G. for string types." "\n\n" "This class to used to test for _value_ equality between objects, I.E. if " "they represent the same thing. For _object_ equality the `is` keyword can " "be used, which will return `true` only if two variables are pointing to " "the same object in memory."; } static const char* Cmp_Definition(void) { return "struct Cmp {\n" " int (*cmp)(var, var);\n" "};\n"; } static struct Example* Cmp_Examples(void) { static struct Example examples[] = { { "Usage 1", "show($I( eq($I(1), $I( 1)))); /* 1 */\n" "show($I(neq($I(2), $I(20)))); /* 1 */\n" "show($I(neq($S(\"Hello\"), $S(\"Hello\")))); /* 0 */\n" "show($I( eq($S(\"Hello\"), $S(\"There\")))); /* 0 */\n" "\n" "var a = $I(1); var b = $I(1);\n" "\n" "show($I(eq(a, b))); /* 1 */\n" "show($I(a is b)); /* 0 */\n" "show($I(a isnt b)); /* 1 */\n" }, { "Usage 2", "show($I(gt($I(15), $I(3 )))); /* 1 */\n" "show($I(lt($I(70), $I(81)))); /* 1 */\n" "show($I(lt($I(71), $I(71)))); /* 0 */\n" "show($I(ge($I(78), $I(71)))); /* 1 */\n" "show($I(gt($I(32), $I(32)))); /* 0 */\n" "show($I(le($I(21), $I(32)))); /* 1 */\n" "\n" "show($I(cmp($I(20), $I(20)))); /* 0 */\n" "show($I(cmp($I(21), $I(20)))); /* 1 */\n" "show($I(cmp($I(20), $I(21)))); /* -1 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Cmp_Methods(void) { static struct Method methods[] = { { "cmp", "int cmp(var self, var obj);", "The return value of `cmp` is `< 0` if `self` is less than `obj`, `> 0` " "if `self` is greater than `obj` and `0` if they are equal." }, { "eq", "bool eq(var self, var obj);", "Returns true if the object `self` is equal to the object `obj`." }, { "neq", "bool neq(var self, var obj);", "Returns false if the object `self` is equal to the object `obj`." }, { "gt", "bool gt(var self, var obj);", "Returns true if the object `self` is greater than the object `obj`." }, { "lt", "bool lt(var self, var obj);", "Returns false if the object `self` is less than the object `obj`." }, { "ge", "bool ge(var self, var obj);", "Returns false if the object `self` is greater than or equal to the " "object `obj`." }, { "le", "bool le(var self, var obj);", "Returns false if the object `self` is less than or equal to the " "object `obj`." }, {NULL, NULL, NULL} }; return methods; } var Cmp = Cello(Cmp, Instance(Doc, Cmp_Name, Cmp_Brief, Cmp_Description, Cmp_Definition, Cmp_Examples, Cmp_Methods)); int cmp(var self, var obj) { struct Cmp* c = instance(self, Cmp); if (c and c->cmp) { return c->cmp(self, obj); } size_t s = size(type_of(self)); if (type_of(self) is type_of(obj) and s) { return memcmp(self, obj, s); } throw(TypeError, "Cannot compare type %s to type %s", type_of(obj), type_of(self)); return 0; } bool eq(var self, var obj) { return cmp(self, obj) is 0; } bool neq(var self, var obj) { return not eq(self, obj); } bool gt(var self, var obj) { return cmp(self, obj) > 0; } bool lt(var self, var obj) { return cmp(self, obj) < 0; } bool ge(var self, var obj) { return not lt(self, obj); } bool le(var self, var obj) { return not gt(self, obj); } static const char* Sort_Name(void) { return "Sort"; } static const char* Sort_Brief(void) { return "Sortable"; } static const char* Sort_Description(void) { return "The `Sort` class can be implemented by types which can be sorted in some " "way such as `Array`. By default the sorting function uses the `lt` method " "to compare elements, but a custom function can also be provided."; } static const char* Sort_Definition(void) { return "struct Sort {\n" " void (*sort_by)(var,bool(*f)(var,var));\n" "};"; } static struct Example* Sort_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Array, Float, \n" " $F(5.2), $F(7.1), $F(2.2));\n" "\n" "show(x); /* <'Array' At 0x00414603 [5.2, 7.1, 2.2]> */\n" "sort(x);\n" "show(x); /* <'Array' At 0x00414603 [2.2, 5.2, 7.1]> */\n" }, {NULL, NULL} }; return examples; } static struct Method* Sort_Methods(void) { static struct Method methods[] = { { "sort", "void sort(var self);", "Sorts the object `self`." }, { "sort_by", "void sort_by(var self, bool(*f)(var,var));", "Sorts the object `self` using the function `f`." }, {NULL, NULL, NULL} }; return methods; } var Sort = Cello(Sort, Instance(Doc, Sort_Name, Sort_Brief, Sort_Description, Sort_Definition, Sort_Examples, Sort_Methods)); void sort(var self) { method(self, Sort, sort_by, lt); } void sort_by(var self, bool(*f)(var,var)) { method(self, Sort, sort_by, f); } ================================================ FILE: src/Concat.c ================================================ #include "Cello.h" static const char* Concat_Name(void) { return "Concat"; } static const char* Concat_Brief(void) { return "Concatenate Objects"; } static const char* Concat_Description(void) { return "The `Concat` class is implemented by objects that can have other objects " "either _appended_ to their, on _concatenated_ to them. For example " "collections or strings."; } static const char* Concat_Definition(void) { return "struct Concat {\n" " void (*concat)(var, var);\n" " void (*append)(var, var);\n" "};\n"; } static struct Example* Concat_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Array, Float, $F(9.9), $F(2.8));\n" "var y = new(Array, Float, $F(1.1), $F(6.5));\n" "\n" "show(x); /* <'Array' At 0x00414603 [9.9, 2.8]> */\n" "show(y); /* <'Array' At 0x00414603 [1.1, 6.5]> */\n" "append(x, $F(2.5));\n" "show(x); /* <'Array' At 0x00414603 [9.9, 2.8, 2.5]> */\n" "concat(x, y);\n" "show(x); /* <'Array' At 0x00414603 [9.9, 2.8, 2.5, 1.1, 6.5]> */\n" }, {NULL, NULL} }; return examples; } static struct Method* Concat_Methods(void) { static struct Method methods[] = { { "append", "void append(var self, var obj);", "Append the object `obj` to the object `self`." }, { "concat", "void concat(var self, var obj);", "Concatenate the object `obj` to the object `self`." }, {NULL, NULL, NULL} }; return methods; } var Concat = Cello(Concat, Instance(Doc, Concat_Name, Concat_Brief, Concat_Description, Concat_Definition, Concat_Examples, Concat_Methods)); void append(var self, var obj) { method(self, Concat, append, obj); } void concat(var self, var obj) { method(self, Concat, concat, obj); } ================================================ FILE: src/Doc.c ================================================ #include "Cello.h" static const char* Doc_Name(void) { return "Doc"; } static const char* Doc_Brief(void) { return "Provides Documentation"; } static const char* Doc_Description(void) { return "The `Doc` class can be used to give documentation to a certain class or " "type. This documentation can then be accessed using the `help` function " "or by other tools used to generate documentation such as for the Cello " "website. Documentation can be written in Markdown." "\n\n" "The `examples` and `methods` entries should be provided as `NULL` " "terminated arrays allocated statically." ; } static const char* Doc_Definition(void) { return "struct Example {\n" " const char* name;\n" " const char* body;\n" "};\n" "\n" "struct Method {\n" " const char* name;\n" " const char* definition;\n" " const char* description;\n" "};\n" "\n" "struct Doc {\n" " const char* (*name)(void);\n" " const char* (*brief)(void);\n" " const char* (*description)(void);\n" " const char* (*definition)(void);\n" " struct Example* (*examples)(void);\n" " struct Method* (*methods)(void);\n" "};\n"; } static struct Method* Doc_Methods(void) { static struct Method methods[] = { { "name", "const char* name(var type);", "Return the name of a given `type`." }, { "brief", "const char* brief(var type);", "Return a brief description of a given `type`." }, { "description", "const char* description(var type);", "Return a longer description of a given `type`." }, { "definition", "const char* definition(var type);", "Return the C definition of a given `type`." }, {NULL, NULL, NULL} }; return methods; } static struct Example* Doc_Examples(void) { static struct Example examples[] = { { "Usage", "show($S(name(Int))); /* Int */\n" "show($S(brief(Int))); /* Integer Object */\n" }, {NULL, NULL} }; return examples; } var Doc = Cello(Doc, Instance(Doc, Doc_Name, Doc_Brief, Doc_Description, Doc_Definition, Doc_Examples, Doc_Methods)); const char* name(var type) { struct Doc* doc = type_instance(type, Doc); if (doc and doc->name) { return doc->name(); } return c_str(type); } const char* brief(var type) { return type_method(type, Doc, brief); } const char* description(var type) { return type_method(type, Doc, description); } const char* definition(var type) { return type_method(type, Doc, definition); } static const char* Help_Name(void) { return "Help"; } static const char* Help_Brief(void) { return "Usage information"; } static const char* Help_Description(void) { return "The `Help` class can be implemented to let an object provide helpful " "information about itself. In the standard library this class is " "implemented by `Type` and it prints out the documentation provided " "by the `Doc` class in a friendly way."; } static const char* Help_Definition(void) { return "struct Help {\n" " int (*help_to)(var, int);\n" "};\n"; } static struct Method* Help_Methods(void) { static struct Method methods[] = { { "help", "void help(var self);\n" "int help_to(var out, int pos, var self);", "Print help information about the object `self` either to `stdout` or " "to the object `out` at some position `pos`." }, {NULL, NULL, NULL} }; return methods; } static struct Example* Help_Examples(void) { static struct Example examples[] = { { "Usage", "help(Int);\n" }, {NULL, NULL} }; return examples; } var Help = Cello(Help, Instance(Doc, Help_Name, Help_Brief, Help_Description, Help_Definition, Help_Examples, Help_Methods)); int help_to(var out, int pos, var self) { return method(self, Help, help_to, out, pos); } void help(var self) { help_to($(File, stdout), 0, self); } ================================================ FILE: src/Exception.c ================================================ #include "Cello.h" #define EXCEPTION_TLS_KEY "__Exception" enum { EXCEPTION_MAX_DEPTH = 2048, EXCEPTION_MAX_STRACE = 25 }; var TypeError = CelloEmpty(TypeError); var ValueError = CelloEmpty(ValueError); var ClassError = CelloEmpty(ClassError); var IndexOutOfBoundsError = CelloEmpty(IndexOutOfBoundsError); var KeyError = CelloEmpty(KeyError); var OutOfMemoryError = CelloEmpty(OutOfMemoryError); var IOError = CelloEmpty(IOError); var FormatError = CelloEmpty(FormatError); var BusyError = CelloEmpty(BusyError); var ResourceError = CelloEmpty(ResourceError); var ProgramAbortedError = CelloEmpty(ProgramAbortedError); var DivisionByZeroError = CelloEmpty(DivisionByZeroError); var IllegalInstructionError = CelloEmpty(IllegalInstructionError); var ProgramInterruptedError = CelloEmpty(ProgramInterruptedError); var SegmentationError = CelloEmpty(SegmentationError); var ProgramTerminationError = CelloEmpty(ProgramTerminationError); struct Exception { var obj; var msg; size_t depth; bool active; jmp_buf* buffers[EXCEPTION_MAX_DEPTH]; }; static const char* Exception_Name(void) { return "Exception"; } static const char* Exception_Brief(void) { return "Exception Object"; } static const char* Exception_Description(void) { return "The `Exception` type provides an interface to the Cello Exception System. " "One instance of this type is created for each `Thread` and stores the " "various bits of data required for the exception system. It can be " "retrieved using the `current` function, although not much can be done " "with it." "\n\n" "Exceptions are available via the `try`, `catch` and `throw` macros. It is " "important that the `catch` part of the exception block is always " "evaluated otherwise the internal state of the exception system can go out " "of sync. For this reason please never use `return` inside a `try` block. " "\n\n" "The `exception_signals` method can be used to register some exception to " "be thrown for any of the " "[standard C signals](https://en.wikipedia.org/wiki/C_signal_handling)." "\n\n" "To get the current exception object or message use the " "`exception_message` or `exception_object` methods."; } static struct Method* Exception_Methods(void) { static struct Method methods[] = { { "try", "#define try", "Start an exception `try` block." }, { "catch", "#define catch(...)", "Start an exception `catch` block, catching any objects listed in `...` " "as the first name given. To catch any exception object leave argument " "list empty other than caught variable name." }, { "#define throw", "throw(E, F, ...)", "Throw exception object `E` with format string `F` and arguments `...`." }, { "exception_signals", "void exception_signals(void);", "Register the standard C signals to throw corresponding exceptions." }, { "exception_object", "void exception_object(void);\n", "Retrieve the current exception object." }, { "exception_message", "void exception_message(void);\n", "Retrieve the current exception message." }, {NULL, NULL, NULL} }; return methods; } static struct Example* Exception_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Table, String, Int);\n" "set(x, $S(\"Hello\"), $I(1));\n" "set(x, $S(\"World\"), $I(2));\n" "\n" "try {\n" " get(x, $S(\"Missing\"));\n" "} catch (e in KeyError) {\n" " println(\"Got Exception: %$\", e);\n" "}\n" }, {NULL, NULL} }; return examples; } static void Exception_New(var self, var args) { struct Exception* e = self; e->active = false; e->depth = 0; e->obj = NULL; e->msg = new_raw(String); memset(e->buffers, 0, sizeof(jmp_buf*) * EXCEPTION_MAX_DEPTH); set(current(Thread), $S(EXCEPTION_TLS_KEY), self); } static void Exception_Del(var self) { struct Exception* e = self; del_raw(e->msg); rem(current(Thread), $S(EXCEPTION_TLS_KEY)); } static void Exception_Assign(var self, var obj) { struct Exception* e = self; struct Exception* o = cast(obj, Exception); e->obj = o->obj; assign(e->msg, o->msg); e->depth = o->depth; e->active = o->active; memcpy(e->buffers, o->buffers, sizeof(jmp_buf*) * EXCEPTION_MAX_DEPTH); } static var Exception_Current(void) { return get(current(Thread), $S(EXCEPTION_TLS_KEY)); } static void Exception_Signal(int sig) { switch(sig) { case SIGABRT: throw(ProgramAbortedError, "Program Aborted"); case SIGFPE: throw(DivisionByZeroError, "Division by Zero"); case SIGILL: throw(IllegalInstructionError, "Illegal Instruction"); case SIGINT: throw(ProgramInterruptedError, "Program Interrupted"); case SIGSEGV: throw(SegmentationError, "Segmentation fault"); case SIGTERM: throw(ProgramTerminationError, "Program Terminated"); } } static jmp_buf* Exception_Buffer(struct Exception* e) { if (e->depth == 0) { fprintf(stderr, "Cello Fatal Error: Exception Buffer Out of Bounds!\n"); abort(); } return e->buffers[e->depth-1]; } static size_t Exception_Len(var self) { struct Exception* e = self; return e->depth; } static bool Exception_Running(var self) { struct Exception* e = self; return e->active; } #ifndef CELLO_NSTRACE #if defined(CELLO_UNIX) static void Exception_Backtrace(void) { var trace[EXCEPTION_MAX_STRACE]; size_t trace_count = backtrace(trace, EXCEPTION_MAX_STRACE); char** symbols = backtrace_symbols(trace, trace_count); print_to($(File, stderr), 0, "!!\tStack Trace: \n"); print_to($(File, stderr), 0, "!!\t\n"); for (size_t i = 0; i < trace_count; i++) { print_to($(File, stderr), 0, "!!\t\t[%i] %s\n", $(Int, i), $(String, symbols[i])); } print_to($(File, stderr), 0, "!!\t\n"); free(symbols); } #elif defined(CELLO_WINDOWS) static void Exception_Backtrace(void) { HANDLE process = GetCurrentProcess(); HANDLE thread = GetCurrentThread(); CONTEXT context; memset(&context, 0, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_FULL; RtlCaptureContext(&context); SymSetOptions(SYMOPT_UNDNAME|SYMOPT_LOAD_LINES); SymInitialize(process, NULL, TRUE); DWORD image; STACKFRAME64 stackframe; ZeroMemory(&stackframe, sizeof(STACKFRAME64)); #ifdef _M_IX86 image = IMAGE_FILE_MACHINE_I386; stackframe.AddrPC.Offset = context.Eip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Ebp; stackframe.AddrFrame.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Esp; stackframe.AddrStack.Mode = AddrModeFlat; #elif _M_X64 image = IMAGE_FILE_MACHINE_AMD64; stackframe.AddrPC.Offset = context.Rip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Rsp; stackframe.AddrFrame.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrStack.Mode = AddrModeFlat; #elif _M_IA64 image = IMAGE_FILE_MACHINE_IA64; stackframe.AddrPC.Offset = context.StIIP; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.IntSp; stackframe.AddrFrame.Mode = AddrModeFlat; stackframe.AddrBStore.Offset = context.RsBSP; stackframe.AddrBStore.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.IntSp; stackframe.AddrStack.Mode = AddrModeFlat; #endif print_to($(File, stderr), 0, "!!\tStack Trace: \n"); print_to($(File, stderr), 0, "!!\t\n"); for (size_t i = 0; i < EXCEPTION_MAX_STRACE; i++) { BOOL result = StackWalk64( image, process, thread, &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL); if (!result) { break; } char* filename = ""; char* symbolname = "???"; int lineno = 0; char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; DWORD64 displacement = 0; if (SymFromAddr(process, stackframe.AddrPC.Offset, &displacement, symbol)) { symbolname = symbol->Name; } else { symbolname = "???"; } IMAGEHLP_LINE64 line; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); DWORD displacementline = 0; if (SymGetLineFromAddr64(process, stackframe.AddrPC.Offset, &displacementline, &line)) { lineno = line.LineNumber; filename = line.FileName; } else { lineno = 0; filename = ""; } if (strcmp(filename, "") == 0) { print_to($(File, stderr), 0, "!!\t\t[%i] %s\n", $I(i), $S(symbolname)); } else { print_to($(File, stderr), 0, "!!\t\t[%i] %s:%i %s\n", $I(i), $S(filename), $I(lineno), $S(symbolname)); } } print_to($(File, stderr), 0, "!!\t\n"); SymCleanup(process); } #else static void Exception_Backtrace(void) {} #endif #else static void Exception_Backtrace(void) {} #endif static void Exception_Error(struct Exception* e) { print_to($(File, stderr), 0, "\n"); print_to($(File, stderr), 0, "!!\t\n"); print_to($(File, stderr), 0, "!!\tUncaught %$\n", e->obj); print_to($(File, stderr), 0, "!!\t\n"); print_to($(File, stderr), 0, "!!\t\t %s\n", e->msg); print_to($(File, stderr), 0, "!!\t\n"); Exception_Backtrace(); exit(EXIT_FAILURE); } static int Exception_Show(var self, var out, int pos) { struct Exception* e = self; return print_to(out, pos, "<'Exception' At 0x%p %$ - %$>", self, e->obj, e->msg); } var Exception = Cello(Exception, Instance(Doc, Exception_Name, Exception_Brief, Exception_Description, NULL, Exception_Examples, Exception_Methods), Instance(New, Exception_New, Exception_Del), Instance(Assign, Exception_Assign), Instance(Len, Exception_Len), Instance(Current, Exception_Current), Instance(Start, NULL, NULL, NULL, Exception_Running), Instance(Show, Exception_Show, NULL)); void exception_signals(void) { signal(SIGABRT, Exception_Signal); signal(SIGFPE, Exception_Signal); signal(SIGILL, Exception_Signal); signal(SIGINT, Exception_Signal); signal(SIGSEGV, Exception_Signal); signal(SIGTERM, Exception_Signal); } void exception_try(jmp_buf* env) { struct Exception* e = current(Exception); if (e->depth is EXCEPTION_MAX_DEPTH) { fprintf(stderr, "Cello Fatal Error: Exception Buffer Overflow!\n"); abort(); } e->depth++; e->active = false; e->buffers[e->depth-1] = env; } var exception_throw(var obj, const char* fmt, var args) { struct Exception* e = current(Exception); e->obj = obj; print_to_with(e->msg, 0, fmt, args); if (Exception_Len(e) >= 1) { longjmp(*Exception_Buffer(e), 1); } else { Exception_Error(e); } return NULL; } var exception_catch(var args) { struct Exception* e = current(Exception); if (not e->active) { return NULL; } /* If no Arguments catch all */ if (len(args) is 0) { return e->obj; } /* Check Exception against Arguments */ foreach(arg in args) { if (eq(arg, e->obj)) { return e->obj; } } /* No matches found. Propagate to outward block */ if (e->depth >= 1) { longjmp(*Exception_Buffer(e), 1); } else { Exception_Error(e); } return NULL; } void exception_try_end(void) { struct Exception* e = current(Exception); if (e->depth == 0) { fprintf(stderr, "Cello Fatal Error: Exception Buffer Underflow!\n"); abort(); } e->depth--; } void exception_try_fail(void) { struct Exception* e = current(Exception); e->active = true; } ================================================ FILE: src/File.c ================================================ #include "Cello.h" static const char* Stream_Name(void) { return "Stream"; } static const char* Stream_Brief(void) { return "File-like"; } static const char* Stream_Description(void) { return "The `Stream` class represents an abstract set of operations that can be " "performed on File-like objects."; } static const char* Stream_Definition(void) { return "struct Stream {\n" " var (*sopen)(var,var,var);\n" " void (*sclose)(var);\n" " void (*sseek)(var,int64_t,int);\n" " int64_t (*stell)(var);\n" " void (*sflush)(var);\n" " bool (*seof)(var);\n" " size_t (*sread)(var,void*,size_t);\n" " size_t (*swrite)(var,void*,size_t);\n" "};\n"; } static struct Example* Stream_Examples(void) { static struct Example examples[] = { { "Usage", "var f = sopen($(File, NULL), $S(\"test.bin\"), $S(\"r\"));\n" "\n" "char c;\n" "while (!seof(f)) {\n" " sread(f, &c, 1);\n" " putc(c, stdout);\n" "}\n" "\n" "sclose(f);\n" }, {NULL, NULL} }; return examples; } static struct Method* Stream_Methods(void) { static struct Method methods[] = { { "sopen", "var sopen(var self, var resource, var options);", "Open the stream `self` with a given `resource` and `options`." }, { "sclose", "void sclose(var self);", "Close the stream `self`." }, { "sseek", "void sseek(var self, int64_t pos, int origin);", "Seek to the position `pos` from some `origin` in the stream `self`." }, { "stell", "int64_t stell(var self);", "Return the current position of the stream `stell`." }, { "sflush", "void sflush(var self);", "Flush the buffered contents of stream `self`." }, { "seof", "bool seof(var self);", "Returns true if there is no more information in the stream." }, { "sread", "size_t sread(var self, void* output, size_t size);", "Read `size` bytes from the stream `self` and write them to `output`." }, { "swrite", "size_t swrite(var self, void* input, size_t size);", "Write `size` bytes to the stream `self` and read them from `input`." }, {NULL, NULL, NULL} }; return methods; } var Stream = Cello(Stream, Instance(Doc, Stream_Name, Stream_Brief, Stream_Description, Stream_Definition, Stream_Examples, Stream_Methods)); var sopen(var self, var resource, var options) { return method(self, Stream, sopen, resource, options); } void sclose(var self) { method(self, Stream, sclose); } void sseek(var self, int64_t pos, int origin) { method(self, Stream, sseek, pos, origin); } int64_t stell(var self) { return method(self, Stream, stell); } void sflush(var self) { method(self, Stream, sflush); } bool seof(var self) { return method(self, Stream, seof); } size_t sread(var self, void* output, size_t size) { return method(self, Stream, sread, output, size); } size_t swrite(var self, void* input, size_t size) { return method(self, Stream, swrite, input, size); } static const char* File_Name(void) { return "File"; } static const char* File_Brief(void) { return "Operating System File"; } static const char* File_Description(void) { return "The `File` type is a wrapper of the native C `FILE` type representing a " "file in the operating system."; } static const char* File_Definition(void) { return "struct File {\n" " FILE* file;\n" "};\n"; } static struct Example* File_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(File, $S(\"test.bin\"), $S(\"wb\"));\n" "char* data = \"hello\";\n" "swrite(x, data, strlen(data));\n" "sclose(x);\n" }, { "Formatted Printing", "var x = $(File, NULL);\n" "sopen(x, $S(\"test.txt\"), $S(\"w\"));\n" "print_to(x, 0, \"%$ is %$ \", $S(\"Dan\"), $I(23));\n" "print_to(x, 0, \"%$ is %$ \", $S(\"Chess\"), $I(24));\n" "sclose(x);\n" }, { "Automatic Closing", "with(f in new(File, $S(\"test.txt\"), $S(\"r\"))) {\n" " var k = new(String); resize(k, 100);\n" " var v = new(Int, $I(0));\n" " foreach (i in range($I(2))) {\n" " scan_from(f, 0, \"%$ is %$ \", k, v);\n" " show(k); show(v);\n" " }\n" "}\n" }, {NULL, NULL} }; return examples; } static var File_Open(var self, var filename, var access); static void File_Close(var self); static void File_New(var self, var args) { struct File* f = self; if (len(args) > 0) { File_Open(self, get(args, $I(0)), get(args, $I(1))); } } static void File_Del(var self) { struct File* f = self; if (f->file isnt NULL) { File_Close(self); } } static var File_Open(var self, var filename, var access) { struct File* f = self; if (f->file isnt NULL) { File_Close(self); } f->file = fopen(c_str(filename), c_str(access)); if (f->file is NULL) { throw(IOError, "Could not open file: %s", filename); } return self; } static void File_Close(var self) { struct File* f = self; int err = fclose(f->file); if (err != 0) { throw(IOError, "Failed to close file: %i", $I(err)); } f->file = NULL; } static void File_Seek(var self, int64_t pos, int origin) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot seek file - no file open."); } int err = fseek(f->file, pos, origin); if (err != 0) { throw(IOError, "Failed to seek in file: %i", $I(err)); } } static int64_t File_Tell(var self) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot tell file - no file open."); } int64_t i = ftell(f->file); if (i == -1) { throw(IOError, "Failed to tell file: %i", $I(i)); } return i; } static void File_Flush(var self) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot flush file - no file open."); } int err = fflush(f->file); if (err != 0) { throw(IOError, "Failed to flush file: %i", $I(err)); } } static bool File_EOF(var self) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot eof file - no file open."); } return feof(f->file); } static size_t File_Read(var self, void* output, size_t size) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot read file - no file open."); } size_t num = fread(output, size, 1, f->file); if (num isnt 1 and size isnt 0 and not feof(f->file)) { throw(IOError, "Failed to read from file: %i", $I(num)); return num; } return num; } static size_t File_Write(var self, void* input, size_t size) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot write file - no file open."); } size_t num = fwrite(input, size, 1, f->file); if (num isnt 1 and size isnt 0) { throw(IOError, "Failed to write to file: %i", $I(num)); } return num; } static int File_Format_To(var self, int pos, const char* fmt, va_list va) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot format to file - no file open."); } return vfprintf(f->file, fmt, va); } static int File_Format_From(var self, int pos, const char* fmt, va_list va) { struct File* f = self; if (f->file is NULL) { throw(IOError, "Cannot format from file - no file open."); } return vfscanf(f->file, fmt, va); } var File = Cello(File, Instance(Doc, File_Name, File_Brief, File_Description, File_Definition, File_Examples, NULL), Instance(New, File_New, File_Del), Instance(Start, NULL, File_Close, NULL), Instance(Stream, File_Open, File_Close, File_Seek, File_Tell, File_Flush, File_EOF, File_Read, File_Write), Instance(Format, File_Format_To, File_Format_From)); static const char* Process_Name(void) { return "Process"; } static const char* Process_Brief(void) { return "Operating System Process"; } static const char* Process_Description(void) { return "The `Process` type is a wrapper for an operating system process as " "constructed by the unix-like call `popen`. In this sense it is much like " "a standard file in the operating system but that instead of writing data " "to a location you are writing it as input to a process."; } static const char* Process_Definition(void) { return "struct Process {\n" " FILE* proc;\n" "};\n"; } static struct Example* Process_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Process, $S(\"ls\"), $S(\"r\"));\n" "char c;\n" "while (not seof(x)) {\n" " sread(x, &c, 1);\n" " print(\"%c\", $I(c));\n" "}\n" "sclose(x);\n" }, {NULL, NULL} }; return examples; } static var Process_Open(var self, var filename, var access); static void Process_Close(var self); static void Process_New(var self, var args) { struct Process* p = self; p->proc = NULL; Process_Open(self, get(args, $I(0)), get(args, $I(1))); } static void Process_Del(var self) { struct Process* p = self; if (p->proc isnt NULL) { Process_Close(self); } } static var Process_Open(var self, var filename, var access) { struct Process* p = self; if (p->proc isnt NULL) { Process_Close(self); } p->proc = popen(c_str(filename), c_str(access)); if (p->proc is NULL) { throw(IOError, "Could not open process: %s", filename); } return self; } static void Process_Close(var self) { struct Process* p = self; int err = pclose(p->proc); if (err != 0) { throw(IOError, "Failed to close process: %i", $I(err)); } p->proc = NULL; } static void Process_Seek(var self, int64_t pos, int origin) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot seek process - no process open."); } int err = fseek(p->proc, pos, origin); if (err != 0) { throw(IOError, "Failed to seek in process: %i", $I(err)); } } static int64_t Process_Tell(var self) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot tell process - no process open."); } int64_t i = ftell(p->proc); if (i == -1) { throw(IOError, "Failed to tell process: %i", $I(i)); } return i; } static void Process_Flush(var self) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot flush process - no process open."); } int err = fflush(p->proc); if (err != 0) { throw(IOError, "Failed to flush process: %i", $I(err)); } } static bool Process_EOF(var self) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot eof process - no process open."); } return feof(p->proc); } static size_t Process_Read(var self, void* output, size_t size) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot read process - no process open."); } size_t num = fread(output, size, 1, p->proc); if (num isnt 1 and size isnt 0 and not feof(p->proc)) { throw(IOError, "Failed to read from process: %i", $I(num)); return num; } return num; } static size_t Process_Write(var self, void* input, size_t size) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot write process - no process open."); } size_t num = fwrite(input, size, 1, p->proc); if (num isnt 1 and size isnt 0) { throw(IOError, "Failed to write to process: %i", $I(num)); } return num; } static int Process_Format_To(var self, int pos, const char* fmt, va_list va) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot format to process - no process open."); } return vfprintf(p->proc, fmt, va); } static int Process_Format_From(var self, int pos, const char* fmt, va_list va) { struct Process* p = self; if (p->proc is NULL) { throw(IOError, "Cannot format from process - no process open."); } return vfscanf(p->proc, fmt, va); } var Process = Cello(Process, Instance(Doc, Process_Name, Process_Brief, Process_Description, Process_Definition, Process_Examples, NULL), Instance(New, Process_New, Process_Del), Instance(Start, NULL, Process_Close, NULL), Instance(Stream, Process_Open, Process_Close, Process_Seek, Process_Tell, Process_Flush, Process_EOF, Process_Read, Process_Write), Instance(Format, Process_Format_To, Process_Format_From)); ================================================ FILE: src/Function.c ================================================ #include "Cello.h" static const char* Call_Name(void) { return "Call"; } static const char* Call_Brief(void) { return "Callable"; } static const char* Call_Description(void) { return "The `Call` class is used by types which can be called as functions."; } static const char* Call_Definition(void) { return "struct Call {\n" " var (*call_with)(var, var);\n" "};\n"; } static struct Example* Call_Examples(void) { static struct Example examples[] = { { "Usage", "var increment(var args) {\n" " struct Int* i = get(args, $I(0));\n" " i->val++;\n" " return NULL;\n" "}\n" "\n" "var x = $I(0);\n" "show(x); /* 0 */\n" "call($(Function, increment), x);\n" "show(x); /* 1 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Call_Methods(void) { static struct Method methods[] = { { "call", "#define call(self, ...)\n" "var call_with(var self, var args);", "Call the object `self` with arguments `args`." }, {NULL, NULL, NULL} }; return methods; } var Call = Cello(Call, Instance(Doc, Call_Name, Call_Brief, Call_Description, Call_Definition, Call_Examples, Call_Methods)); var call_with(var self, var args) { return method(self, Call, call_with, args); } static const char* Function_Name(void) { return "Function"; } static const char* Function_Brief(void) { return "Function Object"; } static const char* Function_Description(void) { return "The `Function` type allows C function pointers to be treated as " "Cello objects. They can be passed around, stored, and manipulated. Only C " "functions of the type `var(*)(var)` can be stored as a `Function` type " "and when called the arguments will be wrapped into an iterable and passed " "as the first argument, typically in the form of a `tuple`."; } static struct Example* Function_Examples(void) { static struct Example examples[] = { { "Usage", "var increment(var args) {\n" " struct Int* i = get(args, $I(0));\n" " i->val++;\n" " return NULL;\n" "}\n" "\n" "var x = $I(0);\n" "show(x); /* 0 */\n" "call($(Function, increment), x);\n" "show(x); /* 1 */\n" }, { "Usage 2", "var hello_person(var args) {\n" " print(\"Hello %$!\", get(args, $I(0)));\n" " return NULL;\n" "}\n" "\n" "call($(Function, hello_person), $S(\"Dan\"));\n" }, { "Usage 3", "var add_print(var args) {\n" " int64_t fst = c_int(get(args, $I(0)));\n" " int64_t snd = c_int(get(args, $I(1)));\n" " println(\"%i + %i = %i\", $I(fst), $I(snd), $I(fst+snd));\n" " return NULL;\n" "}\n" "\n" "call($(Function, add_print), $I(10), $I(21));\n" }, {NULL, NULL} }; return examples; } static const char* Function_Definition(void) { return "struct Function {\n" " var (*func)(var);\n" "};\n"; } static var Function_Call(var self, var args) { struct Function* f = self; return f->func(args); } var Function = Cello(Function, Instance(Doc, Function_Name, Function_Brief, Function_Description, Function_Definition, Function_Examples, NULL), Instance(Call, Function_Call)); ================================================ FILE: src/GC.c ================================================ #include "Cello.h" static const char* Mark_Name(void) { return "Mark"; } static const char* Mark_Brief(void) { return "Markable by GC"; } static const char* Mark_Description(void) { return "The `Mark` class can be overridden to customize the behaviour of the " "Cello Garbage Collector on encountering a given type. By default the " "allocated memory for a structure is scanned for pointers to other Cello " "objects, but if a type does its own memory allocation it may store " "pointers to Cello objects in other locations." "\n\n" "If this is the case the `Mark` class can be overridden and the callback " "function `f` must be called on all pointers which might be Cello objects " "which are managed by the class. Alternately the `mark` function can be " "called on any sub object to start a chain of recursive marking."; } static const char* Mark_Definition(void) { return "struct Mark {\n" " void (*mark)(var, var, void(*)(var,void*));\n" "};\n"; } static struct Method* Mark_Methods(void) { static struct Method methods[] = { { "mark", "void mark(var self, var gc, void(*f)(var,void*));", "Mark the object `self` with the Garbage Collector `gc` and the callback " "function `f`." }, {NULL, NULL, NULL} }; return methods; } var Mark = Cello(Mark, Instance(Doc, Mark_Name, Mark_Brief, Mark_Description, Mark_Definition, NULL, Mark_Methods)); void mark(var self, var gc, void(*f)(var,void*)) { if (self is NULL) { return; } struct Mark* m = instance(self, Mark); if (m and m->mark) { m->mark(self, gc, f); } } #ifndef CELLO_NGC #define GC_TLS_KEY "__GC" enum { GC_PRIMES_COUNT = 24 }; static const size_t GC_Primes[GC_PRIMES_COUNT] = { 0, 1, 5, 11, 23, 53, 101, 197, 389, 683, 1259, 2417, 4733, 9371, 18617, 37097, 74093, 148073, 296099, 592019, 1100009, 2200013, 4400021, 8800019 }; static const char* GC_Name(void) { return "GC"; } static const char* GC_Brief(void) { return "Garbage Collector"; } static const char* GC_Description(void) { return "The `GC` type provides an interface to the Cello Garbage Collector. One " "instance of this type is created for each thread and can be retrieved " "using the `current` function. The Garbage Collector can be stopped and " "started using `start` and `stop` and objects can be added or removed from " "the Garbage Collector using `set` and `rem`."; } static struct Example* GC_Examples(void) { static struct Example examples[] = { { "Starting & Stopping", "var gc = current(GC);\n" "stop(gc);\n" "var x = new(Int, $I(10)); /* Not added to GC */\n" "show($I(running(gc))); /* 0 */\n" "del(x); /* Must be deleted when done */\n" "start(gc);\n" }, {NULL, NULL} }; return examples; } struct GCEntry { var ptr; uint64_t hash; bool root; bool marked; }; struct GC { struct GCEntry* entries; size_t nslots; size_t nitems; size_t mitems; uintptr_t maxptr; uintptr_t minptr; var bottom; bool running; uintptr_t freenum; var* freelist; }; static uint64_t GC_Probe(struct GC* gc, uint64_t i, uint64_t h) { int64_t v = i - (h-1); if (v < 0) { v = gc->nslots + v; } return v; } static const double GC_Load_Factor = 0.9; static size_t GC_Ideal_Size(size_t size) { size = (size_t)((double)(size+1) / GC_Load_Factor); for (size_t i = 0; i < GC_PRIMES_COUNT; i++) { if (GC_Primes[i] >= size) { return GC_Primes[i]; } } size_t last = GC_Primes[GC_PRIMES_COUNT-1]; for (size_t i = 0;; i++) { if (last * i >= size) { return last * i; } } } static void GC_Set_Ptr(struct GC* gc, var ptr, bool root); static void GC_Rehash(struct GC* gc, size_t new_size) { struct GCEntry* old_entries = gc->entries; size_t old_size = gc->nslots; gc->nslots = new_size; gc->entries = calloc(gc->nslots, sizeof(struct GCEntry)); #if CELLO_MEMORY_CHECK == 1 if (gc->entries is NULL) { throw(OutOfMemoryError, "Cannot allocate GC Pointer Table, out of memory!"); return; } #endif for (size_t i = 0; i < old_size; i++) { if (old_entries[i].hash isnt 0) { GC_Set_Ptr(gc, old_entries[i].ptr, old_entries[i].root); } } free(old_entries); } static void GC_Resize_More(struct GC* gc) { size_t new_size = GC_Ideal_Size(gc->nitems); size_t old_size = gc->nslots; if (new_size > old_size) { GC_Rehash(gc, new_size); } } static void GC_Resize_Less(struct GC* gc) { size_t new_size = GC_Ideal_Size(gc->nitems); size_t old_size = gc->nslots; if (new_size < old_size) { GC_Rehash(gc, new_size); } } static uint64_t GC_Hash(var ptr) { return ((uintptr_t)ptr) >> 3; } static void GC_Set_Ptr(struct GC* gc, var ptr, bool root) { uint64_t i = GC_Hash(ptr) % gc->nslots; uint64_t j = 0; uint64_t ihash = i+1; struct GCEntry entry = { ptr, ihash, root, 0 }; while (true) { uint64_t h = gc->entries[i].hash; if (h is 0) { gc->entries[i] = entry; return; } if (gc->entries[i].ptr == entry.ptr) { return; } uint64_t p = GC_Probe(gc, i, h); if (j >= p) { struct GCEntry tmp = gc->entries[i]; gc->entries[i] = entry; entry = tmp; j = p; } i = (i+1) % gc->nslots; j++; } } static bool GC_Mem_Ptr(struct GC* gc, var ptr) { if (gc->nslots is 0) { return false; } uint64_t i = GC_Hash(ptr) % gc->nslots; uint64_t j = 0; while (true) { uint64_t h = gc->entries[i].hash; if (h is 0 or j > GC_Probe(gc, i, h)) { return false; } if (gc->entries[i].ptr == ptr) { return true; } i = (i+1) % gc->nslots; j++; } } static void GC_Rem_Ptr(struct GC* gc, var ptr) { if (gc->nslots is 0) { return; } for (size_t i = 0; i < gc->freenum; i++) { if (gc->freelist[i] is ptr) { gc->freelist[i] = NULL; } } uint64_t i = GC_Hash(ptr) % gc->nslots; uint64_t j = 0; while (true) { uint64_t h = gc->entries[i].hash; if (h is 0 or j > GC_Probe(gc, i, h)) { return; } if (gc->entries[i].ptr is ptr) { var freeitem = gc->entries[i].ptr; memset(&gc->entries[i], 0, sizeof(struct GCEntry)); j = i; while (true) { uint64_t nj = (j+1) % gc->nslots; uint64_t nh = gc->entries[nj].hash; if (nh isnt 0 and GC_Probe(gc, nj, nh) > 0) { memcpy(&gc->entries[j], &gc->entries[nj], sizeof(struct GCEntry)); memset(&gc->entries[nj], 0, sizeof(struct GCEntry)); j = nj; } else { break; } } gc->nitems--; dealloc(destruct(freeitem)); return; } i = (i+1) % gc->nslots; j++; } } static void GC_Mark_Item(void* _gc, void* ptr); static void GC_Recurse(struct GC* gc, var ptr); static void GC_Mark_And_Recurse(void* _gc, void* ptr) { struct GC* gc = _gc; GC_Mark_Item(gc, ptr); GC_Recurse(gc, ptr); } static void GC_Recurse(struct GC* gc, var ptr) { var type = type_of(ptr); if (type is Int or type is Float or type is String or type is Type or type is File or type is Process or type is Function) { return; } struct Mark* m = type_instance(type, Mark); if (m and m->mark) { m->mark(ptr, gc, (void(*)(var,void*))GC_Mark_And_Recurse); return; } for (size_t i = 0; i+sizeof(var) <= size(type); i += sizeof(var)) { var p = ((char*)ptr) + i; GC_Mark_Item(gc, *((var*)p)); } } static void GC_Print(struct GC* gc); static void GC_Mark_Item(void* _gc, void* ptr) { struct GC* gc = _gc; uintptr_t pval = (uintptr_t)ptr; if (pval % sizeof(var) isnt 0 or pval < gc->minptr or pval > gc->maxptr) { return; } uint64_t i = GC_Hash(ptr) % gc->nslots; uint64_t j = 0; while (true) { uint64_t h = gc->entries[i].hash; if (h is 0 or j > GC_Probe(gc, i, h)) { return; } if (gc->entries[i].ptr is ptr and not gc->entries[i].marked) { gc->entries[i].marked = true; GC_Recurse(gc, gc->entries[i].ptr); return; } i = (i+1) % gc->nslots; j++; } } static void CELLO_NASAN GC_Mark_Stack(struct GC* gc) { var stk = NULL; var bot = gc->bottom; var top = &stk; if (bot == top) { return; } if (bot < top) { for (var p = top; p >= bot; p = ((char*)p) - sizeof(var)) { GC_Mark_Item(gc, *((var*)p)); } } if (bot > top) { for (var p = top; p <= bot; p = ((char*)p) + sizeof(var)) { GC_Mark_Item(gc, *((var*)p)); } } } static void GC_Mark_Stack_Fake(struct GC* gc) { } void GC_Mark(struct GC* gc) { if (gc is NULL or gc->nitems is 0) { return; } /* Mark Thread Local Storage */ mark(current(Thread), gc, (void(*)(var,void*))GC_Mark_Item); /* Mark Roots */ for (size_t i = 0; i < gc->nslots; i++) { if (gc->entries[i].hash is 0) { continue; } if (gc->entries[i].marked) { continue; } if (gc->entries[i].root) { gc->entries[i].marked = true; GC_Recurse(gc, gc->entries[i].ptr); } } volatile int noinline = 1; /* Flush Registers to Stack */ if (noinline) { jmp_buf env; memset(&env, 0, sizeof(jmp_buf)); setjmp(env); } /* Avoid Inlining function call */ void (*mark_stack)(struct GC* gc) = noinline ? GC_Mark_Stack : (void(*)(struct GC* gc))(NULL); /* Mark Stack */ mark_stack(gc); } static int GC_Show(var self, var out, int pos) { struct GC* gc = self; pos = print_to(out, pos, "<'GC' At 0x%p\n", self); for (size_t i = 0; i < gc->nslots; i++) { if (gc->entries[i].hash is 0) { pos = print_to(out, pos, "| %i : \n", $I(i)); continue; } pos = print_to(out, pos, "| %i : %15s %p %s %s\n", $I(i), type_of(gc->entries[i].ptr), gc->entries[i].ptr, gc->entries[i].root ? $S("root") : $S("auto"), gc->entries[i].marked ? $S("*") : $S(" ")); } return print_to(out, pos, "+------------------->\n"); } void GC_Sweep(struct GC* gc) { gc->freelist = realloc(gc->freelist, sizeof(var) * gc->nitems); gc->freenum = 0; size_t i = 0; while (i < gc->nslots) { if (gc->entries[i].hash is 0) { i++; continue; } if (gc->entries[i].marked) { i++; continue; } if (not gc->entries[i].root and not gc->entries[i].marked) { gc->freelist[gc->freenum] = gc->entries[i].ptr; gc->freenum++; memset(&gc->entries[i], 0, sizeof(struct GCEntry)); uint64_t j = i; while (true) { uint64_t nj = (j+1) % gc->nslots; uint64_t nh = gc->entries[nj].hash; if (nh isnt 0 and GC_Probe(gc, nj, nh) > 0) { memcpy(&gc->entries[j], &gc->entries[nj], sizeof(struct GCEntry)); memset(&gc->entries[nj], 0, sizeof(struct GCEntry)); j = nj; } else { break; } } gc->nitems--; continue; } i++; } for (size_t i = 0; i < gc->nslots; i++) { if (gc->entries[i].hash is 0) { continue; } if (gc->entries[i].marked) { gc->entries[i].marked = false; continue; } } GC_Resize_Less(gc); gc->mitems = gc->nitems + gc->nitems / 2 + 1; for (size_t i = 0; i < gc->freenum; i++) { if (gc->freelist[i]) { dealloc(destruct(gc->freelist[i])); } } free(gc->freelist); gc->freelist = NULL; gc->freenum = 0; } static var GC_Current(void) { return get(current(Thread), $S(GC_TLS_KEY)); } static void GC_New(var self, var args) { struct GC* gc = self; struct Ref* bt = cast(get(args, $I(0)), Ref); gc->bottom = bt->val; gc->maxptr = 0; gc->minptr = UINTPTR_MAX; gc->running = true; gc->freelist = NULL; gc->freenum = 0; set(current(Thread), $S(GC_TLS_KEY), gc); } static void GC_Del(var self) { struct GC* gc = self; GC_Sweep(gc); free(gc->entries); free(gc->freelist); rem(current(Thread), $S(GC_TLS_KEY)); } static void GC_Set(var self, var key, var val) { struct GC* gc = self; if (not gc->running) { return; } gc->nitems++; gc->maxptr = (uintptr_t)key > gc->maxptr ? (uintptr_t)key : gc->maxptr; gc->minptr = (uintptr_t)key < gc->minptr ? (uintptr_t)key : gc->minptr; GC_Resize_More(gc); GC_Set_Ptr(gc, key, (bool)c_int(val)); if (gc->nitems > gc->mitems) { GC_Mark(gc); GC_Sweep(gc); } } static void GC_Rem(var self, var key) { struct GC* gc = self; if (not gc->running) { return; } GC_Rem_Ptr(gc, key); GC_Resize_Less(gc); gc->mitems = gc->nitems + gc->nitems / 2 + 1; } static bool GC_Mem(var self, var key) { return GC_Mem_Ptr(self, key); } static void GC_Start(var self) { struct GC* gc = self; gc->running = true; } static void GC_Stop(var self) { struct GC* gc = self; gc->running = false; } static bool GC_Running(var self) { struct GC* gc = self; return gc->running; } var GC = Cello(GC, Instance(Doc, GC_Name, GC_Brief, GC_Description, NULL, GC_Examples, NULL), Instance(New, GC_New, GC_Del), Instance(Get, NULL, GC_Set, GC_Mem, GC_Rem), Instance(Start, GC_Start, GC_Stop, NULL, GC_Running), Instance(Show, GC_Show, NULL), Instance(Current, GC_Current)); void Cello_Exit(void) { del_raw(current(GC)); } #endif ================================================ FILE: src/Get.c ================================================ #include "Cello.h" static const char* Get_Name(void) { return "Get"; } static const char* Get_Brief(void) { return "Gettable or Settable"; } static const char* Get_Description(void) { return "The `Get` class provides a method to _get_ or _set_ certain properties " "of an object using keys and value. Typically it is implemented by " "data lookup structures such as `Table` or `Map` but it is also used " "more generally such as using indices to look up items in `Array`, or " "as thread local storage for the `Thread` object."; } static const char* Get_Definition(void) { return "struct Get {\n" " var (*get)(var, var);\n" " void (*set)(var, var, var);\n" " bool (*mem)(var, var);\n" " void (*rem)(var, var);\n" " var (*key_type)(var);\n" " var (*val_type)(var);\n" "};\n"; } static struct Example* Get_Examples(void) { static struct Example examples[] = { { "Usage 1", "var x = new(Array, String, \n" " $S(\"Hello\"), $S(\"There\"));\n" "\n" "show(get(x, $I(0))); /* Hello */\n" "show(get(x, $I(1))); /* There */\n" "set(x, $I(1), $S(\"Blah\"));\n" "show(get(x, $I(1))); /* Blah */\n" }, { "Usage 2", "var prices = new(Table, String, Int, \n" " $S(\"Apple\"), $I(12),\n" " $S(\"Banana\"), $I( 6),\n" " $S(\"Pear\"), $I(55));\n" "\n" "var pear_price = get(prices, $S(\"Pear\"));\n" "var banana_price = get(prices, $S(\"Banana\"));\n" "var apple_price = get(prices, $S(\"Apple\"));\n" "\n" "show(pear_price); /* 55 */\n" "show(banana_price); /* 6 */\n" "show(apple_price); /* 12 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Get_Methods(void) { static struct Method methods[] = { { "get", "var get(var self, var key);", "Get the value at a given `key` for object `self`." }, { "set", "void set(var self, var key, var val);", "Set the value at a given `key` for object `self`." }, { "mem", "bool mem(var self, var key);", "Returns true if `key` is a member of the object `self`." }, { "rem", "void rem(var self, var key);", "Removes the `key` from object `self`." }, { "key_type", "var key_type(var self);", "Returns the key type for the object `self`." }, { "val_type", "var val_type(var self);", "Returns the value type for the object `self`." }, {NULL, NULL, NULL} }; return methods; } var Get = Cello(Get, Instance(Doc, Get_Name, Get_Brief, Get_Description, Get_Definition, Get_Examples, Get_Methods)); var get(var self, var key) { return method(self, Get, get, key); } void set(var self, var key, var val) { method(self, Get, set, key, val); } bool mem(var self, var key) { return method(self, Get, mem, key); } void rem(var self, var key) { method(self, Get, rem, key); } var key_type(var self) { return method(self, Get, key_type); } var val_type(var self) { return method(self, Get, val_type); } ================================================ FILE: src/Hash.c ================================================ #include "Cello.h" static const char* Hash_Name(void) { return "Hash"; } static const char* Hash_Brief(void) { return "Hashable"; } static const char* Hash_Description(void) { return "The `Hash` class provides a mechanism for hashing an object. This hash " "value should remain the same across objects that are also considered " "equal by the `Cmp` class. For objects that are not considered equal this " "value should aim to be evenly distributed across integers." "\n\n" "This is not a cryptographic hash. It is used for various objects or " "data structures that require fast hashing such as the `Table` type. Due " "to this it should not be used for cryptography or security." "\n\n" "By default an object is hashed by using its raw memory with the " "[Murmurhash](http://en.wikipedia.org/wiki/MurmurHash) algorithm. Due to " "the link between them it is recommended to only override `Hash` and " "`Cmp` in conjunction."; } static const char* Hash_Definition(void) { return "struct Hash {\n" " uint64_t (*hash)(var);\n" "};\n"; } static struct Example* Hash_Examples(void) { static struct Example examples[] = { { "Usage", "println(\"%li\", $I(hash($I( 1)))); /* 1 */\n" "println(\"%li\", $I(hash($I(123)))); /* 123 */\n" "\n" "/* 866003103 */\n" "println(\"%li\", $I(hash_data($I(123), size(Int))));\n" "\n" "println(\"%li\", $I(hash($S(\"Hello\")))); /* -1838682532 */\n" "println(\"%li\", $I(hash($S(\"There\")))); /* 961387266 */\n" "println(\"%li\", $I(hash($S(\"People\")))); /* 697467069 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Hash_Methods(void) { static struct Method methods[] = { { "hash", "uint64_t hash(var self);", "Get the hash value for the object `self`." }, { "hash_data", "uint64_t hash_data(void* data, size_t num);", "Hash `num` bytes pointed to by `data` using " "[Murmurhash](http://en.wikipedia.org/wiki/MurmurHash)." }, {NULL, NULL, NULL} }; return methods; } var Hash = Cello(Hash, Instance(Doc, Hash_Name, Hash_Brief, Hash_Description, Hash_Definition, Hash_Examples, Hash_Methods)); uint64_t hash_data(const void* data, size_t size) { const uint64_t m = 0xc6a4a7935bd1e995; const int r = 47; const uint8_t * d = (const uint8_t*)data; const uint8_t * end = d + (size & ~7ULL); uint64_t h = 0xCe110 ^ (size * m); while (d != end) { uint64_t k; memcpy(&k, d, sizeof(uint64_t)); d += sizeof(uint64_t); k *= m; k ^= k >> r; k *= m; h ^= k; h *= m; } switch (size & 7) { case 7: h ^= (uint64_t)(d[6]) << 48; case 6: h ^= (uint64_t)(d[5]) << 40; case 5: h ^= (uint64_t)(d[4]) << 32; case 4: h ^= (uint64_t)(d[3]) << 24; case 3: h ^= (uint64_t)(d[2]) << 16; case 2: h ^= (uint64_t)(d[1]) << 8; case 1: h ^= (uint64_t)(d[0]); h *= m; }; h ^= h >> r; h *= m; h ^= h >> r; return h; } uint64_t hash(var self) { struct Hash* h = instance(self, Hash); if (h and h->hash) { return h->hash(self); } return hash_data(self, size(type_of(self))); } ================================================ FILE: src/Iter.c ================================================ #include "Cello.h" var _ = CelloEmpty(_); var Terminal = CelloEmpty(Terminal); static const char* Iter_Name(void) { return "Iter"; } static const char* Iter_Brief(void) { return "Iterable"; } static const char* Iter_Description(void) { return "The `Iter` class is implemented by types which can be looped over. This " "allows them to be used in conjunction with the `foreach` macro as well " "as various other components of Cello." "\n\n" "To signal that an interation has finished an iteration should return the " "Cello object `Terminal`. Due to this - the `Terminal` object cannot be " "placed inside of Tuples because it artificially shortens their length."; } static const char* Iter_Definition(void) { return "struct Iter {\n" " var (*iter_init)(var);\n" " var (*iter_next)(var, var);\n" " var (*iter_prev)(var, var);\n" " var (*iter_last)(var);\n" " var (*iter_type)(var);\n" "};\n"; } static struct Example* Iter_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Array, Int, $I(1), $I(2), $I(5));\n" "\n" "foreach(o in x) {\n" " show(o); /* 1, 2, 5 */\n" "}\n" }, { "Table", "var prices = new(Table, String, Int);\n" "set(prices, $S(\"Apple\"), $I(12));\n" "set(prices, $S(\"Banana\"), $I( 6));\n" "set(prices, $S(\"Pear\"), $I(55));\n" "\n" "foreach(key in prices) {\n" " var price = get(prices, key);\n" " print(\"Price of %$ is %$\\n\", key, price);\n" "}\n" }, {NULL, NULL} }; return examples; } static struct Method* Iter_Methods(void) { static struct Method methods[] = { { "foreach", "#define foreach(...)\n", "Iterate over elements in a loop." }, { "iter_init", "var iter_init(var self);\n" "var iter_last(var self);", "Return the initial item (or final item) in the iteration over `self`." }, { "iter_next", "var iter_next(var self, var curr);\n" "var iter_prev(var self, var curr);", "Given the current item `curr`, return the next (or previous) item in " "the iteration over `self`." }, { "iter_type", "var iter_type(var self);", "Returns the type of item that can be expected to be returned by the " "iterable." }, {NULL, NULL, NULL} }; return methods; } var Iter = Cello(Iter, Instance(Doc, Iter_Name, Iter_Brief, Iter_Description, Iter_Definition, Iter_Examples, Iter_Methods)); var iter_init(var self) { return method(self, Iter, iter_init); } var iter_next(var self, var curr) { return method(self, Iter, iter_next, curr); } var iter_last(var self) { return method(self, Iter, iter_last); } var iter_prev(var self, var curr) { return method(self, Iter, iter_prev, curr); } var iter_type(var self) { return method(self, Iter, iter_type); } static const char* Range_Name(void) { return "Range"; } static const char* Range_Brief(void) { return "Integer Sequence"; } static const char* Range_Description(void) { return "The `Range` type is a basic iterable which acts as a virtual " "sequence of integers, starting from some value, stopping at some value " "and incrementing by some step." "\n\n" "This can be a useful replacement for the standard C `for` loop with " "decent performance but returning a Cello `Int`. It is constructable on " "the stack with the `range` macro which makes it practical and easy to " "use."; } static const char* Range_Definition(void) { return "struct Range {\n" " var value;\n" " int64_t start;\n" " int64_t stop;\n" " int64_t step;\n" "};\n"; } static struct Example* Range_Examples(void) { static struct Example examples[] = { { "Usage", "/* Iterate 0 to 10 */\n" "foreach (i in range($I(10))) {\n" " print(\"%i\\n\", i);\n" "}\n" "\n" "/* Iterate 10 to 20 */\n" "foreach (i in range($I(10), $I(20))) {\n" " print(\"%i\\n\", i);\n" "}\n" "\n" "/* Iterate 10 to 20 with a step of 5 */\n" "foreach (i in range($I(10), $I(20), $I(5))) {\n" " print(\"%i\\n\", i);\n" "}\n" "\n" "/* Iterate 20 to 10 */\n" "foreach (i in range($I(10), $I(20), $I(-1))) {\n" " print(\"%i\\n\", i);\n" "}\n" }, {NULL, NULL} }; return examples; } static struct Method* Range_Methods(void) { static struct Method methods[] = { { "range", "#define range(...)", "Construct a `Range` object on the stack." }, {NULL, NULL, NULL} }; return methods; } var range_stack(var self, var args) { struct Range* r = self; size_t nargs = len(args); if (nargs > 3) { throw(FormatError, "Received too many arguments to Range constructor"); } switch (nargs) { case 0: r->start = 0; r->stop = 0; r->step = 1; break; case 1: r->start = 0; r->stop = c_int(get(args, $I(0))); r->step = 1; break; case 2: r->start = get(args, $I(0)) is _ ? 0 : c_int(get(args, $I(0))); r->stop = c_int(get(args, $I(1))); r->step = 1; break; case 3: r->start = get(args, $I(0)) is _ ? 0 : c_int(get(args, $I(0))); r->stop = c_int(get(args, $I(1))); r->step = get(args, $I(2)) is _ ? 1 : c_int(get(args, $I(2))); break; } return self; } static void Range_New(var self, var args) { struct Range* r = self; r->value = new(Int); range_stack(self, args); } static void Range_Del(var self) { struct Range* r = self; del(r->value); } static void Range_Assign(var self, var obj) { struct Range* r = self; struct Range* o = cast(obj, Range); assign(r->value, o->value); r->start = o->start; r->stop = o->stop; r->step = o->step; } static int Range_Cmp(var self, var obj) { struct Range* r = self; struct Range* o = cast(obj, Range); return memcmp(&r->start, &o->start, sizeof(int64_t) * 3); } static var Range_Iter_Init(var self) { struct Range* r = self; struct Int* i = r->value; if (r->step == 0) { return Terminal; } if (r->step > 0) { i->val = r->start; } if (r->step < 0) { i->val = r->stop-1; } if (r->step > 0 and i->val >= r->stop) { return Terminal; } if (r->step < 0 and i->val < r->start) { return Terminal; } return i; } static var Range_Iter_Last(var self) { struct Range* r = self; struct Int* i = r->value; if (r->step == 0) { return Terminal; } if (r->step > 0) { i->val = r->stop-1; } if (r->step < 0) { i->val = r->start; } if (r->step > 0 and i->val < r->start) { return Terminal; } if (r->step < 0 and i->val >= r->stop) { return Terminal; } return i; } static var Range_Iter_Next(var self, var curr) { struct Range* r = self; struct Int* i = r->value; i->val += r->step; if (r->step == 0) { return Terminal; } if (r->step > 0 and i->val >= r->stop) { return Terminal; } if (r->step < 0 and i->val < r->start) { return Terminal; } return i; } static var Range_Iter_Prev(var self, var curr) { struct Range* r = self; struct Int* i = r->value; i->val -= r->step; if (r->step == 0) { return Terminal; } if (r->step > 0 and i->val < r->start) { return Terminal; } if (r->step < 0 and i->val >= r->stop) { return Terminal; } return i; } static var Range_Iter_Type(var self) { return Int; } static size_t Range_Len(var self) { struct Range* r = self; if (r->step == 0) { return 0; } if (r->step > 0) { return ((r->stop-1) - r->start) / r->step + 1; } if (r->step < 0) { return ((r->stop-1) - r->start) / -r->step + 1; } return 0; } static var Range_Get(var self, var key) { struct Range* r = self; struct Int* x = r->value; int64_t i = c_int(key); i = i < 0 ? Range_Len(r)+i : i; if (r->step == 0) { x->val = 0; return x; } if (r->step > 0 and (r->start + r->step * i) < r->stop) { x->val = r->start + r->step * i; return x; } if (r->step < 0 and (r->stop-1 + r->step * i) >= r->start) { x->val = r->stop-1 + r->step * i; return x; } return throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Range of start %i, stop %i and step %i.", key, $I(r->start), $I(r->stop), $I(r->step)); } static bool Range_Mem(var self, var key) { struct Range* r = self; int64_t i = c_int(key); i = i < 0 ? Range_Len(r)+i : i; if (r->step == 0) { return false; } if (r->step > 0) { return i >= r->start and i < r->stop and (i - r->start) % r->step is 0; } if (r->step < 0) { return i >= r->start and i < r->stop and (i - (r->stop-1)) % -r->step is 0; } return false; } static int Range_Show(var self, var output, int pos) { struct Range* r = self; pos = print_to(output, pos, "<'Range' At 0x%p [", self); var curr = Range_Iter_Init(self); while (curr isnt Terminal) { pos = print_to(output, pos, "%i", curr); curr = Range_Iter_Next(self, curr); if (curr isnt Terminal) { pos = print_to(output, pos, ", "); } } return print_to(output, pos, "]>"); } var Range = Cello(Range, Instance(Doc, Range_Name, Range_Brief, Range_Description, Range_Definition, Range_Examples, Range_Methods), Instance(New, Range_New, Range_Del), Instance(Assign, Range_Assign), Instance(Cmp, Range_Cmp), Instance(Len, Range_Len), Instance(Get, Range_Get, NULL, Range_Mem, NULL), Instance(Show, Range_Show, NULL), Instance(Iter, Range_Iter_Init, Range_Iter_Next, Range_Iter_Last, Range_Iter_Prev, Range_Iter_Type)); static const char* Slice_Name(void) { return "Slice"; } static const char* Slice_Brief(void) { return "Partial Iterable"; } static const char* Slice_Description(void) { return "The `Slice` type is an iterable that allows one to only iterate over " "part of another iterable. Given some start, stop and step, only " "those entries described by the `Slice` are returned in the iteration." "\n\n" "Under the hood the `Slice` object still iterates over the whole iterable " "but it only returns those values in the range given."; } static const char* Slice_Definition(void) { return "struct Slice {\n" " var iter;\n" " var range;\n" "};\n"; } static struct Example* Slice_Examples(void) { static struct Example examples[] = { { "Usage", "var x = tuple(\n" " $S(\"Hello\"), $S(\"There\"), $S(\"World\"), $S(\"!\"));\n" "\n" "/* Iterate over elements 0 to 2 */\n" "foreach (s in slice(x, $I(2))) {\n" " print(\"%s\\n\", s);\n" "}\n" "\n" "/* Iterate over elements 1 to 2 */\n" "foreach (s in slice(x, $I(1), $I(2))) {\n" " print(\"%s\\n\", s);\n" "}\n" "\n" "/* Iterate over every other element */\n" "foreach (s in slice(x, _, _, $I(2))) {\n" " print(\"%s\\n\", s);\n" "}\n" "\n" "/* Iterate backwards, starting from element 3 */\n" "foreach (s in slice(x, _, $I(2), $I(-1))) {\n" " print(\"%s\\n\", s);\n" "}\n" }, {NULL, NULL} }; return examples; } static struct Method* Slice_Methods(void) { static struct Method methods[] = { { "slice", "#define slice(I, ...)", "Construct a `Slice` object on the stack over iterable `I`." }, { "reverse", "#define reverse(I)", "Construct a `Slice` object that iterates over iterable `I` in reverse " "order." }, {NULL, NULL, NULL} }; return methods; } static int64_t Slice_Arg(int part, size_t n, var arg) { if (arg is _) { if (part is 0) { return 0; } if (part is 1) { return n; } if (part is 2) { return 1; } } int64_t a = c_int(arg); if (part isnt 2) { a = a < 0 ? n+a : a; a = a > n ? n : a; a = a < 0 ? 0 : a; } return a; } var slice_stack(var self, var args) { size_t nargs = len(args); if (nargs > 4) { throw(FormatError, "Received too many arguments to Slice constructor"); } if (nargs < 1) { throw(FormatError, "Received too few arguments to Slice constructor"); } struct Slice* s = self; s->iter = get(args, $I(0)); struct Range* r = s->range; size_t n = len(s->iter); switch (nargs) { case 1: r->start = 0; r->stop = n; r->step = 1; break; case 2: r->start = 0; r->stop = Slice_Arg(1, n, get(args, $I(1))); r->step = 1; break; case 3: r->start = Slice_Arg(0, n, get(args, $I(1))); r->stop = Slice_Arg(1, n, get(args, $I(2))); r->step = 1; break; case 4: r->start = Slice_Arg(0, n, get(args, $I(1))); r->stop = Slice_Arg(1, n, get(args, $I(2))); r->step = Slice_Arg(2, n, get(args, $I(3))); break; } return self; } static void Slice_New(var self, var args) { struct Slice* s = self; s->range = new(Range); slice_stack(self, args); } static void Slice_Del(var self) { struct Slice* s = self; del(s->range); } static void Slice_Assign(var self, var obj) { struct Slice* s = self; struct Slice* o = cast(obj, Slice); s->iter = o->iter; assign(s->range, o->range); } static int Slice_Cmp(var self, var obj) { struct Slice* s = self; struct Slice* o = cast(obj, Slice); if (s->iter > o->iter) { return 1; } if (s->iter < o->iter) { return -1; } return cmp(s->range, o->range); } static var Slice_Iter_Init(var self) { struct Slice* s = self; struct Range* r = s->range; if (r->step > 0) { var curr = iter_init(s->iter); for(int64_t i = 0; i < r->start; i++) { curr = iter_next(s->iter, curr); } return curr; } if (r->step < 0) { var curr = iter_last(s->iter); for (int64_t i = 0; i < (int64_t)len(s->iter)-r->stop; i++) { curr = iter_prev(s->iter, curr); } return curr; } return Terminal; } static var Slice_Iter_Next(var self, var curr) { struct Slice* s = self; struct Range* r = s->range; if (r->step > 0) { for (int64_t i = 0; i < r->step; i++) { curr = iter_next(s->iter, curr); } } if (r->step < 0) { for (int64_t i = 0; i < -r->step; i++) { curr = iter_prev(s->iter, curr); } } return curr; } static var Slice_Iter_Type(var self) { struct Slice* s = self; return iter_type(s->iter); } static var Slice_Iter_Last(var self) { struct Slice* s = self; struct Range* r = s->range; if (r->step > 0) { var curr = iter_last(s->iter); for(int64_t i = 0; i < (int64_t)len(s->iter)-r->stop; i++) { curr = iter_prev(s->iter, curr); } return curr; } if (r->step < 0) { var curr = iter_init(s->iter); for(int64_t i = 0; i < r->start; i++) { curr = iter_next(s->iter, curr); } return curr; } return Terminal; } static var Slice_Iter_Prev(var self, var curr) { struct Slice* s = self; struct Range* r = s->range; if (r->step > 0) { for (int64_t i = 0; i < r->step; i++) { curr = iter_prev(s->iter, curr); } } if (r->step < 0) { for (int64_t i = 0; i < -r->step; i++) { curr = iter_next(s->iter, curr); } } return curr; } static size_t Slice_Len(var self) { struct Slice* s = self; return Range_Len(s->range); } static var Slice_Get(var self, var key) { struct Slice* s = self; return get(s->iter, Range_Get(s->range, key)); } static bool Slice_Mem(var self, var key) { var curr = Slice_Iter_Init(self); while (curr) { if (eq(curr, key)) { return true; } curr = Slice_Iter_Next(self, curr); } return false; } static int Slice_Show(var self, var output, int pos) { struct Slice* s = self; pos = print_to(output, pos, "<'Slice' At 0x%p [", self); var curr = Slice_Iter_Init(self); while (curr isnt Terminal) { pos = print_to(output, pos, "%$", curr); curr = Slice_Iter_Next(self, curr); if (curr isnt Terminal) { pos = print_to(output, pos, ", "); } } return print_to(output, pos, "]>"); } var Slice = Cello(Slice, Instance(Doc, Slice_Name, Slice_Brief, Slice_Description, Slice_Definition, Slice_Examples, Slice_Methods), Instance(New, Slice_New, Slice_Del), Instance(Assign, Slice_Assign), Instance(Cmp, Slice_Cmp), Instance(Len, Slice_Len), Instance(Get, Slice_Get, NULL, Slice_Mem, NULL), Instance(Iter, Slice_Iter_Init, Slice_Iter_Next, Slice_Iter_Last, Slice_Iter_Prev, Slice_Iter_Type), Instance(Show, Slice_Show, NULL)); static const char* Zip_Name(void) { return "Zip"; } static const char* Zip_Brief(void) { return "Multiple Iterator"; } static const char* Zip_Description(void) { return "The `Zip` type can be used to combine multiple iterables into one which " "is then iterated over all at once and returned as a Tuple. The Zip object " "only iterates when all of it's sub iterators have valid items. More " "specifically the Zip iteration will terminate if _any_ of the sub " "iterators terminate."; } static const char* Zip_Definition(void) { return "struct Zip {\n" " var iters;\n" " var values;\n" "};\n"; } static struct Example* Zip_Examples(void) { static struct Example examples[] = { { "Usage", "/* Iterate over two iterables at once */\n" "var x = new(Array, Int, $I(100), $I(200), $I(130));\n" "var y = new(Array, Float, $F(0.1), $F(0.2), $F(1.3));\n" "foreach (pair in zip(x, y)) {\n" " print(\"x: %$\\n\", get(pair, $I(0)));\n" " print(\"y: %$\\n\", get(pair, $I(1)));\n" "}\n" "\n" "/* Iterate over iterable with count */\n" "foreach (pair in enumerate(x)) {\n" " print(\"%i: %$\\n\", get(pair, $I(0)), get(pair, $I(1)));\n" "}\n" }, {NULL, NULL} }; return examples; } static struct Method* Zip_Methods(void) { static struct Method methods[] = { { "zip", "#define zip(...)", "Construct a `Zip` object on the stack." }, { "enumerate", "#define enumerate(I)", "Zip the iterable `I` with a `Range` object of the same length." }, {NULL, NULL, NULL} }; return methods; } var zip_stack(var self) { struct Zip* z = self; size_t nargs = len(z->iters); struct Tuple* t = z->values; for (size_t i = 0; i < nargs; i++) { t->items[i] = _; } t->items[nargs] = Terminal; return z; } static void Zip_New(var self, var args) { struct Zip* z = self; z->iters = new(Tuple); z->values = new(Tuple); assign(z->iters, args); for (size_t i = 0; i < len(args); i++) { push(z->values, _); } } static void Zip_Del(var self) { struct Zip* z = self; del(z->iters); del(z->values); } static void Zip_Assign(var self, var obj) { struct Zip* z = self; struct Zip* o = cast(obj, Zip); assign(z->iters, o->iters); assign(z->values, o->values); } static var Zip_Iter_Init(var self) { struct Zip* z = self; struct Tuple* values = z->values; struct Tuple* iters = z->iters; size_t num = len(iters); if (num is 0) { return Terminal; } for (size_t i = 0; i < num; i++) { var init = iter_init(iters->items[i]); if (init is Terminal) { return Terminal; } values->items[i] = init; } return values; } static var Zip_Iter_Last(var self) { struct Zip* z = self; struct Tuple* values = z->values; struct Tuple* iters = z->iters; size_t num = len(iters); if (num is 0) { return Terminal; } for (size_t i = 0; i < num; i++) { var last = iter_last(iters->items[i]); if (last is Terminal) { return Terminal; } values->items[i] = last; } return values; } static var Zip_Iter_Next(var self, var curr) { struct Zip* z = self; struct Tuple* values = z->values; struct Tuple* iters = z->iters; size_t num = len(iters); if (num is 0) { return Terminal; } for (size_t i = 0; i < num; i++) { var next = iter_next(iters->items[i], get(curr, $I(i))); if (next is Terminal) { return Terminal; } values->items[i] = next; } return values; } static var Zip_Iter_Prev(var self, var curr) { struct Zip* z = self; struct Tuple* values = z->values; struct Tuple* iters = z->iters; size_t num = len(iters); if (num is 0) { return Terminal; } for (size_t i = 0; i < num; i++) { var prev = iter_prev(iters->items[i], get(curr, $I(i))); if (prev is Terminal) { return Terminal; } values->items[i] = prev; } return values; } static var Zip_Iter_Type(var self) { return Tuple; } static size_t Zip_Len(var self) { struct Zip* z = self; struct Tuple* values = z->values; struct Tuple* iters = z->iters; size_t num = len(iters); if (num is 0) { return 0; } size_t mlen = len(iters->items[0]); for (size_t i = 1; i < num; i++) { size_t num = len(iters->items[i]); mlen = num < mlen ? num : mlen; } return mlen; } static var Zip_Get(var self, var key) { struct Zip* z = self; struct Tuple* values = z->values; struct Tuple* iters = z->iters; size_t num = len(iters); for (size_t i = 0; i < num; i++) { values->items[i] = get(iters->items[i], key); } return values; } static bool Zip_Mem(var self, var key) { foreach (item in self) { if (eq(item, key)) { return true; } } return false; } var Zip = Cello(Zip, Instance(Doc, Zip_Name, Zip_Brief, Zip_Description, Zip_Definition, Zip_Examples, Zip_Methods), Instance(New, Zip_New, Zip_Del), Instance(Assign, Zip_Assign), Instance(Len, Zip_Len), Instance(Get, Zip_Get, NULL, Zip_Mem, NULL), Instance(Iter, Zip_Iter_Init, Zip_Iter_Next, Zip_Iter_Last, Zip_Iter_Prev, Zip_Iter_Type)); var enumerate_stack(var self) { struct Zip* z = self; struct Range* r = get(z->iters, $I(0)); r->stop = len(get(z->iters, $I(1))); return self; } static const char* Filter_Name(void) { return "Filter"; } static const char* Filter_Brief(void) { return "Filtered Iterable"; } static const char* Filter_Description(void) { return "The `Filter` type can be used to filter the results of some iterable. " "Given a callable object `Filter` iterable returns only those items in " "the original iterable for where calling the function returns a " "non-`NULL` value."; } static const char* Filter_Definition(void) { return "struct Filter {\n" " var iter;\n" " var func;\n" "};\n"; } static struct Example* Filter_Examples(void) { static struct Example examples[] = { { "Usage", "var greater_than_two(var x) {\n" " return c_int(x) > 2 ? x : NULL;\n" "}\n" "\n" "var x = new(Array, Int, $I(0), $I(5), $I(2), $I(9));\n" "\n" "foreach (n in filter(x, $(Function, greater_than_two))) {\n" " show(n); /* 5, 9 */\n" "}\n" }, { "Usage 2", "var mem_hello(var x) {\n" " return mem(x, $S(\"Hello\")) ? x : NULL;\n" "}\n" "\n" "var x = new(Tuple, \n" " $S(\"Hello World\"), $S(\"Hello Dan\"), \n" " $S(\"Bonjour\"));\n" "\n" "var y = new(Tuple);\n" "assign(y, filter(x, $(Function, mem_hello)));\n" "show(y); /* tuple(\"Hello World\", \"Hello Dan\") */\n" }, {NULL, NULL} }; return examples; } static struct Method* Filter_Methods(void) { static struct Method methods[] = { { "filter", "#define filter(I, F)", "Construct a `Filter` object on the stack over iterable `I` with " "filter function `F`." }, {NULL, NULL, NULL} }; return methods; } static void Filter_New(var self, var args) { struct Filter* f = self; f->iter = get(args, $I(0)); f->func = get(args, $I(1)); } static var Filter_Iter_Init(var self) { struct Filter* f = self; var curr = iter_init(f->iter); while (true) { if (curr is Terminal or call_with(f->func, curr)) { return curr; } else { curr = iter_next(f->iter, curr); } } return Terminal; } static var Filter_Iter_Last(var self) { struct Filter* f = self; var curr = iter_last(f->iter); while (true) { if (curr is Terminal or call_with(f->func, curr)) { return curr; } else { curr = iter_prev(f->iter, curr); } } return Terminal; } static var Filter_Iter_Next(var self, var curr) { struct Filter* f = self; curr = iter_next(f->iter, curr); while (true) { if (curr is Terminal or call_with(f->func, curr)) { return curr; } else { curr = iter_next(f->iter, curr); } } return Terminal; } static var Filter_Iter_Prev(var self, var curr) { struct Filter* f = self; curr = iter_prev(f->iter, curr); while (true) { if (curr is Terminal or call_with(f->func, curr)) { return curr; } else { curr = iter_prev(f->iter, curr); } } return Terminal; } static var Filter_Iter_Type(var self) { struct Filter* f = self; return iter_type(f->iter); } static bool Filter_Mem(var self, var key) { foreach (item in self) { if (eq(item, key)) { return true; } } return false; } var Filter = Cello(Filter, Instance(Doc, Filter_Name, Filter_Brief, Filter_Description, Filter_Definition, Filter_Examples, Filter_Methods), Instance(New, Filter_New, NULL), Instance(Get, NULL, NULL, Filter_Mem, NULL), Instance(Iter, Filter_Iter_Init, Filter_Iter_Next, Filter_Iter_Last, Filter_Iter_Prev, Filter_Iter_Type)); static const char* Map_Name(void) { return "Map"; } static const char* Map_Brief(void) { return "Apply Function to Iterable"; } static const char* Map_Description(void) { return "The `Map` type is an iterable that applies some callable to to each " "item in another iterable and returns the result. This can be useful to " "make more concise iteration when there are callback functions available." "\n\n" "If the mapping callable is a purely side-effect callable it is possible " "to use the `call` function on the `Map` object directly for a quick way " "to perform the iteration." "\n\n" "One downside of `Map` is that the `iter_type` becomes unknown (there is " "no way to know what type the callable will return so some objects such " "as `Array`s may revert to using `Ref` as the object type when assigned a " "`Map`."; } static const char* Map_Definition(void) { return "struct Map {\n" " var iter;\n" " var curr;\n" " var func;\n" "};\n"; } static struct Method* Map_Methods(void) { static struct Method methods[] = { { "map", "#define map(I, F)", "Construct a `Map` object on the stack over iterable `I` applying " "function `F`." }, {NULL, NULL, NULL} }; return methods; } static struct Example* Map_Examples(void) { static struct Example examples[] = { { "Usage", "var convert_to_int(var x) {\n" " var y = new(Int);\n" " look_from(y, x, 0);\n" " return y;\n" "}\n" "\n" "var x = tuple($S(\"1\"), $S(\"2\"), $S(\"3\"));\n" "\n" "foreach (y in map(x, $(Function, convert_to_int))) {\n" " show(y); /* 1, 2, 3 */\n" "};\n" }, { "Usage 2", "var print_object(var x) {\n" " println(\"Object %$ is of type %$\", x, type_of(x));\n" " return NULL;\n" "}\n" "\n" "var x = tuple($I(0), $S(\"Hello!\"), $F(2.4));\n" "\n" "call(map(x, $(Function, print_object)));\n" }, {NULL, NULL} }; return examples; } static void Map_New(var self, var args) { struct Map* m = self; m->iter = get(args, $I(0)); m->func = get(args, $I(1)); } static var Map_Iter_Init(var self) { struct Map* m = self; m->curr = iter_init(m->iter); if (m->curr is Terminal) { return m->curr; } else { return call_with(m->func, m->curr); } } static var Map_Iter_Last(var self) { struct Map* m = self; m->curr = iter_last(m->iter); if (m->curr is Terminal) { return m->curr; } else { return call_with(m->func, m->curr); } } static var Map_Iter_Next(var self, var curr) { struct Map* m = self; m->curr = iter_next(m->iter, m->curr); if (m->curr is Terminal) { return m->curr; } else { return call_with(m->func, m->curr); } } static var Map_Iter_Prev(var self, var curr) { struct Map* m = self; m->curr = iter_prev(m->iter, m->curr); if (m->curr is Terminal) { return m->curr; } else { return call_with(m->func, m->curr); } } static size_t Map_Len(var self) { struct Map* m = self; return len(m->iter); } static var Map_Get(var self, var key) { struct Map* m = self; m->curr = get(m->iter, key); if (m->curr is Terminal) { return m->curr; } else { return call_with(m->func, m->curr); } } static bool Map_Mem(var self, var key) { foreach (item in self) { if (eq(item, key)) { return true; } } return false; } static var Map_Call(var self, var args) { foreach (item in self); return Terminal; } var Map = Cello(Map, Instance(Doc, Map_Name, Map_Brief, Map_Description, Map_Definition, Map_Examples, Map_Methods), Instance(New, Map_New, NULL), Instance(Len, Map_Len), Instance(Get, Map_Get, NULL, Map_Mem, NULL), Instance(Call, Map_Call), Instance(Iter, Map_Iter_Init, Map_Iter_Next, Map_Iter_Last, Map_Iter_Prev, NULL)); ================================================ FILE: src/Len.c ================================================ #include "Cello.h" static const char* Len_Name(void) { return "Len"; } static const char* Len_Brief(void) { return "Has a length"; } static const char* Len_Description(void) { return "The `Len` class can be implemented by any type that has a length " "associated with it. It is typically implemented by collections " "and is often used in conjunction with `Iter` or `Get`."; } static const char* Len_Definition(void) { return "struct Len {\n" " size_t (*len)(var);\n" "};\n"; } static struct Example* Len_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Array, Int, $I(1), $I(2), $I(5));\n" "show($I(len(x))); /* 3 */\n" "var y = $S(\"Test\");\n" "show($I(len(y))); /* 4 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Len_Methods(void) { static struct Method methods[] = { { "len", "size_t len(var self);", "Returns the length of object `self`." }, {NULL, NULL, NULL} }; return methods; } var Len = Cello(Len, Instance(Doc, Len_Name, Len_Brief, Len_Description, Len_Definition, Len_Examples, Len_Methods)); size_t len(var self) { return method(self, Len, len); } bool empty(var self) { return len(self) is 0; } ================================================ FILE: src/List.c ================================================ #include "Cello.h" static const char* List_Name(void) { return "List"; } static const char* List_Brief(void) { return "Linked List"; } static const char* List_Description(void) { return "The `List` type is a linked list data structure. Elements can be added " "and removed from the list and their memory is allocated and deallocated " "by the structure. Additionally destructors will be called on objects " "once removed." "\n\n" "Elements are copied into the List using `assign` and will initially have " "zero'd memory." "\n\n" "Lists can provide fast insertion and removal at arbitrary locations " "although most other operations will be slow due to having to traverse " "the linked list data structure." "\n\n" "This is largely equivalent to the C++ construct " "[std::list](http://www.cplusplus.com/reference/list/list/)"; } static struct Example* List_Examples(void) { static struct Example examples[] = { { "Construction & Deletion", "var x = new(List, Int);\n" "push(x, $I(32));\n" "push(x, $I(6));\n" "\n" "/* <'List' At 0x0000000000414603 [32, 6]> */\n" "show(x);\n", }, { "Element Access", "var x = new(List, Float, $F(0.01), $F(5.12));\n" "\n" "show(get(x, $I(0))); /* 0.01 */\n" "show(get(x, $I(1))); /* 5.12 */\n" "\n" "set(x, $I(0), $F(500.1));\n" "show(get(x, $I(0))); /* 500.1 */\n", }, { "Membership", "var x = new(List, Int, $I(1), $I(2), $I(3), $I(4));\n" "\n" "show($I(mem(x, $I(1)))); /* 1 */\n" "show($I(len(x))); /* 4 */\n" "\n" "rem(x, $I(3));\n" "\n" "show($I(mem(x, $I(3)))); /* 0 */\n" "show($I(len(x))); /* 3 */\n" "show($I(empty(x))); /* 0 */\n" "\n" "resize(x, 0);\n" "\n" "show($I(empty(x))); /* 1 */\n", }, { "Iteration", "var greetings = new(List, String, \n" " $S(\"Hello\"), $S(\"Bonjour\"), $S(\"Hej\"));\n" "\n" "foreach(greet in greetings) {\n" " show(greet);\n" "}\n", }, {NULL, NULL} }; return examples; } struct List { var type; var head; var tail; size_t tsize; size_t nitems; }; static var List_Alloc(struct List* l) { var item = calloc(1, 2 * sizeof(var) + sizeof(struct Header) + l->tsize); #if CELLO_MEMORY_CHECK == 1 if (item is NULL) { throw(OutOfMemoryError, "Cannot allocate List entry, out of memory!"); } #endif return header_init((struct Header*)( (char*)item + 2 * sizeof(var)), l->type, AllocData); } static void List_Free(struct List* l, var self) { free((char*)self - sizeof(struct Header) - 2 * sizeof(var)); } static var* List_Next(struct List* l, var self) { return (var*)((char*)self - sizeof(struct Header) - 1 * sizeof(var)); } static var* List_Prev(struct List* l, var self) { return (var*)((char*)self - sizeof(struct Header) - 2 * sizeof(var)); } static var List_At(struct List* l, int64_t i) { i = i < 0 ? l->nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)l->nitems) { return throw(IndexOutOfBoundsError, "Index '%i' out of bounds for List of size %i.", $(Int, i), $(Int, l->nitems)); } #endif var item; if (i <= (int64_t)(l->nitems / 2)) { item = l->head; while (i) { item = *List_Next(l, item); i--; } } else { i = l->nitems-i-1; item = l->tail; while (i) { item = *List_Prev(l, item); i--; } } return item; } static void List_Push(var self, var obj); static void List_Rem(var self, var obj); static void List_New(var self, var args) { struct List* l = self; l->type = cast(get(args, $I(0)), Type); l->tsize = size(l->type); l->nitems = 0; l->head = NULL; l->tail = NULL; size_t nargs = len(args); for(size_t i = 0; i < nargs-1; i++) { List_Push(self, get(args, $I(i+1))); } } static void List_Clear(var self) { struct List* l = self; var item = l->head; while (item) { var next = *List_Next(l, item); destruct(item); List_Free(l, item); item = next; } l->tail = NULL; l->head = NULL; l->nitems = 0; } static void List_Del(var self) { struct List* l = self; List_Clear(self); } static void List_Assign(var self, var obj) { struct List* l = self; List_Clear(self); l->type = implements_method(obj, Iter, iter_type) ? iter_type(obj) : Ref; l->tsize = size(l->type); size_t nargs = len(obj); for (size_t i = 0; i < nargs; i++) { List_Push(self, get(obj, $I(i))); } } static void List_Concat(var self, var obj) { foreach (item in obj) { List_Push(self, item); } } static var List_Iter_Init(var self); static var List_Iter_Next(var self, var curr); static int List_Cmp(var self, var obj) { var item0 = List_Iter_Init(self); var item1 = iter_init(obj); while (true) { if (item0 is Terminal and item1 is Terminal) { return 0; } if (item0 is Terminal) { return -1; } if (item1 is Terminal) { return 1; } int c = cmp(item0, item1); if (c < 0) { return -1; } if (c > 0) { return 1; } item0 = List_Iter_Next(self, item0); item1 = iter_next(obj, item1); } return 0; } static uint64_t List_Hash(var self) { struct List* l = self; uint64_t h = 0; var item = l->head; for (size_t i = 0; i < l->nitems; i++) { h ^= hash(item); item = *List_Next(l, item); } return h; } static size_t List_Len(var self) { struct List* l = self; return l->nitems; } static bool List_Mem(var self, var obj) { struct List* l = self; var item = l->head; while (item) { if (eq(item, obj)) { return true; } item = *List_Next(l, item); } return false; } static void List_Unlink(struct List* l, var item) { var next = *List_Next(l, item); var prev = *List_Prev(l, item); if (item is l->head and item is l->tail) { l->head = NULL; l->tail = NULL; } else if (item is l->head) { l->head = next; *List_Prev(l, next) = NULL; } else if (item is l->tail) { l->tail = prev; *List_Next(l, prev) = NULL; } else { *List_Next(l, prev) = next; *List_Prev(l, next) = prev; } } static void List_Link(struct List* l, var item, var prev, var next) { if (prev is NULL) { l->head = item; } else { *List_Next(l, prev) = item; } if (next is NULL) { l->tail = item; } else { *List_Prev(l, next) = item; } *List_Next(l, item) = next; *List_Prev(l, item) = prev; } static void List_Pop_At(var self, var key) { struct List* l = self; int64_t i = c_int(key); var item = List_At(l, i); List_Unlink(l, item); destruct(item); List_Free(l, item); l->nitems--; } static void List_Rem(var self, var obj) { struct List* l = self; var item = l->head; while (item) { if (eq(item, obj)) { List_Unlink(l, item); destruct(item); List_Free(l, item); l->nitems--; return; } item = *List_Next(l, item); } throw(ValueError, "Object %$ not in List!", obj); } static void List_Push(var self, var obj) { struct List* l = self; var item = List_Alloc(l); assign(item, obj); List_Link(l, item, l->tail, NULL); l->nitems++; } static void List_Push_At(var self, var obj, var key) { struct List* l = self; var item = List_Alloc(l); assign(item, obj); int64_t i = c_int(key); if (i is 0) { List_Link(l, item, NULL, l->head); } else { var curr = List_At(l, i); List_Link(l, item, *List_Prev(l, curr), curr); } l->nitems++; } static void List_Pop(var self) { struct List* l = self; #if CELLO_BOUND_CHECK == 1 if (l->nitems is 0) { throw(IndexOutOfBoundsError, "Cannot pop. List is empty!"); return; } #endif var item = l->tail; List_Unlink(l, item); destruct(item); List_Free(l, item); l->nitems--; } static var List_Get(var self, var key) { struct List* l = self; return List_At(l, c_int(key)); } static void List_Set(var self, var key, var val) { struct List* l = self; assign(List_At(l, c_int(key)), val); } static var List_Iter_Init(var self) { struct List* l = self; if (l->nitems is 0) { return Terminal; } return l->head; } static var List_Iter_Next(var self, var curr) { struct List* l = self; curr = *List_Next(l, curr); return curr ? curr : Terminal; } static var List_Iter_Last(var self) { struct List* l = self; if (l->nitems is 0) { return Terminal; } return l->tail; } static var List_Iter_Prev(var self, var curr) { struct List* l = self; curr = *List_Prev(l, curr); return curr ? curr : Terminal; } static var List_Iter_Type(var self) { struct List* l = self; return l->type; } static int List_Show(var self, var output, int pos) { struct List* l = self; pos = print_to(output, pos, "<'List' At 0x%p [", self); var item = l->head; while (item) { pos = print_to(output, pos, "%$", item); item = *List_Next(l, item); if (item) { pos = print_to(output, pos, ", "); } } return print_to(output, pos, "]>"); } static void List_Resize(var self, size_t n) { struct List* l = self; if (n is 0) { List_Clear(self); return; } while (n < l->nitems) { var item = l->tail; List_Unlink(l, item); destruct(item); List_Free(l, item); l->nitems--; } while (n > l->nitems) { var item = List_Alloc(l); List_Link(l, item, l->tail, NULL); l->nitems++; } } static void List_Mark(var self, var gc, void(*f)(var,void*)) { struct List* l = self; var item = l->head; while (item) { f(gc, item); item = *List_Next(l, item); } } var List = Cello(List, Instance(Doc, List_Name, List_Brief, List_Description, NULL, List_Examples, NULL), Instance(New, List_New, List_Del), Instance(Assign, List_Assign), Instance(Mark, List_Mark), Instance(Cmp, List_Cmp), Instance(Hash, List_Hash), Instance(Push, List_Push, List_Pop, List_Push_At, List_Pop_At), Instance(Concat, List_Concat, List_Push), Instance(Len, List_Len), Instance(Get, List_Get, List_Set, List_Mem, List_Rem), Instance(Iter, List_Iter_Init, List_Iter_Next, List_Iter_Last, List_Iter_Prev, List_Iter_Type), Instance(Show, List_Show, NULL), Instance(Resize, List_Resize)); ================================================ FILE: src/Num.c ================================================ #include "Cello.h" static const char* C_Int_Name(void) { return "C_Int"; } static const char* C_Int_Brief(void) { return "Interpret as C Integer"; } static const char* C_Int_Description(void) { return "The `C_Int` class should be overridden by types which are representable " "as a C style Integer of the type `int64_t`."; } static const char* C_Int_Definition(void) { return "struct C_Int {\n" " int64_t (*c_int)(var);\n" "};\n"; } static struct Example* C_Int_Examples(void) { static struct Example examples[] = { { "Usage", "printf(\"%li\", c_int($I(5))); /* 5 */\n" "printf(\"%li\", c_int($I(6))); /* 6 */\n" }, {NULL, NULL} }; return examples; } static struct Method* C_Int_Methods(void) { static struct Method methods[] = { { "c_int", "int64_t c_int(var self);", "Returns the object `self` represented as a `int64_t`." }, {NULL, NULL, NULL} }; return methods; } var C_Int = Cello(C_Int, Instance(Doc, C_Int_Name, C_Int_Brief, C_Int_Description, C_Int_Definition, C_Int_Examples, C_Int_Methods)); static const char* C_Float_Name(void) { return "C_Float"; } static const char* C_Float_Brief(void) { return "Interpret as C Float"; } static const char* C_Float_Description(void) { return "The `C_Float` class should be overridden by types which are representable " "as a C style Float of the type `double`."; } static const char* C_Float_Definition(void) { return "struct C_Float {\n" " double (*c_float)(var);\n" "};\n"; } static struct Example* C_Float_Examples(void) { static struct Example examples[] = { { "Usage", "printf(\"%f\", c_float($F(5.1))); /* 5.1 */\n" "printf(\"%f\", c_float($F(6.2))); /* 6.2 */\n" }, {NULL, NULL} }; return examples; } static struct Method* C_Float_Methods(void) { static struct Method methods[] = { { "c_float", "double c_float(var self);", "Returns the object `self` represented as a `double`." }, {NULL, NULL, NULL} }; return methods; } var C_Float = Cello(C_Float, Instance(Doc, C_Float_Name, C_Float_Brief, C_Float_Description, C_Float_Definition, C_Float_Examples, C_Float_Methods)); int64_t c_int(var self) { if (type_of(self) is Int) { return ((struct Int*)self)->val; } return method(self, C_Int, c_int); } double c_float(var self) { if (type_of(self) is Float) { return ((struct Float*)self)->val; } return method(self, C_Float, c_float); } static const char* Int_Name(void) { return "Int"; } static const char* Int_Brief(void) { return "Integer Object"; } static const char* Int_Description(void) { return "64-bit signed integer Object."; } static const char* Int_Definition(void) { return "struct Int {\n" " int64_t val;\n" "};\n"; } static struct Example* Int_Examples(void) { static struct Example examples[] = { { "Usage", "var i0 = $(Int, 1);\n" "var i1 = new(Int, $I(24313));\n" "var i2 = copy(i0);\n" "\n" "show(i0); /* 1 */\n" "show(i1); /* 24313 */\n" "show(i2); /* 1 */\n" }, {NULL, NULL} }; return examples; } static void Int_Assign(var self, var obj) { struct Int* i = self; i->val = c_int(obj); } static int64_t Int_C_Int(var self) { struct Int* i = self; return i->val; } static int Int_Cmp(var self, var obj) { return (int)(Int_C_Int(self) - c_int(obj)); } static uint64_t Int_Hash(var self) { return (uint64_t)c_int(self); } static int Int_Show(var self, var output, int pos) { return print_to(output, pos, "%li", self); } static int Int_Look(var self, var input, int pos) { return scan_from(input, pos, "%li", self); } var Int = Cello(Int, Instance(Doc, Int_Name, Int_Brief, Int_Description, Int_Definition, Int_Examples, NULL), Instance(Assign, Int_Assign), Instance(Cmp, Int_Cmp), Instance(Hash, Int_Hash), Instance(C_Int, Int_C_Int), Instance(Show, Int_Show, Int_Look)); static const char* Float_Name(void) { return "Float"; } static const char* Float_Brief(void) { return "Floating Point Object"; } static const char* Float_Description(void) { return "64-bit double precision float point Object."; } static const char* Float_Definition(void) { return "struct Float {\n" " double val;\n" "};\n"; } static struct Example* Float_Examples(void) { static struct Example examples[] = { { "Usage", "var f0 = $(Float, 1.0);\n" "var f1 = new(Float, $F(24.313));\n" "var f2 = copy(f0);\n" "\n" "show(f0); /* 1.000 */\n" "show(f1); /* 24.313 */\n" "show(f2); /* 1.000 */\n" }, {NULL, NULL} }; return examples; } static void Float_Assign(var self, var obj) { struct Float* f = self; f->val = c_float(obj); } static double Float_C_Float(var self) { struct Float* f = self; return f->val; } static int Float_Cmp(var self, var obj) { double c = Float_C_Float(self) - c_float(obj); return c > 0 ? 1 : c < 0 ? -1 : 0; } union interp_cast { double as_flt; uint64_t as_int; }; static uint64_t Float_Hash(var self) { union interp_cast ic; ic.as_flt = c_float(self); return ic.as_int; } int Float_Show(var self, var output, int pos) { return print_to(output, pos, "%f", self); } int Float_Look(var self, var input, int pos) { return scan_from(input, pos, "%f", self); } var Float = Cello(Float, Instance(Doc, Float_Name, Float_Brief, Float_Description, Float_Definition, Float_Examples, NULL), Instance(Assign, Float_Assign), Instance(Cmp, Float_Cmp), Instance(Hash, Float_Hash), Instance(C_Float, Float_C_Float), Instance(Show, Float_Show, Float_Look)); ================================================ FILE: src/Pointer.c ================================================ #include "Cello.h" static const char* Pointer_Name(void) { return "Pointer"; } static const char* Pointer_Brief(void) { return "Reference to other object"; } static const char* Pointer_Description(void) { return "The `Pointer` class is implemented by types which act as references to " "other objects. Primarily this class is implemented by `Ref` and `Box` " "which provide the two main pointer types in Cello."; } static const char* Pointer_Definition(void) { return "struct Pointer {\n" " void (*ref)(var, var);\n" " var (*deref)(var);\n" "};\n"; } static struct Example* Pointer_Examples(void) { static struct Example examples[] = { { "Usage", "var obj0 = $F(1.0), obj1 = $F(2.0);\n" "var r = $(Ref, obj0);\n" "show(r);\n" "show(deref(r)); /* 1.0 */\n" "ref(r, obj1);\n" "show(deref(r)); /* 2.0 */\n" "assign(r, obj0);\n" "show(deref(r)); /* 1.0 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Pointer_Methods(void) { static struct Method methods[] = { { "ref", "void ref(var self, var item);", "Set the object `self` to reference the object `item`." }, { "deref", "var deref(var self);", "Get the object referenced by `self`." }, {NULL, NULL, NULL} }; return methods; } var Pointer = Cello(Pointer, Instance(Doc, Pointer_Name, Pointer_Brief, Pointer_Description, Pointer_Definition, Pointer_Examples, Pointer_Methods)); void ref(var self, var item) { method(self, Pointer, ref, item); } var deref(var self) { return method(self, Pointer, deref); } static const char* Ref_Name(void) { return "Ref"; } static const char* Ref_Brief(void) { return "Shared Pointer"; } static const char* Ref_Description(void) { return "The `Ref` type is a basic wrapper around a C pointer. It can be used " "as a type argument to collections to allow them to store generic types. " "It may also be useful in various circumstances where another level of " "indirection or mutability is required."; } static const char* Ref_Definition(void) { return "struct Ref {\n" " var val;\n" "};\n"; } static struct Example* Ref_Examples(void) { static struct Example examples[] = { { "Usage", "var obj0 = $F(1.0), obj1 = $F(2.0);\n" "var r = $(Ref, obj0);\n" "show(r);\n" "show(deref(r)); /* 1.0 */\n" "ref(r, obj1);\n" "show(deref(r)); /* 2.0 */\n" "assign(r, obj0);\n" "show(deref(r)); /* 1.0 */\n" }, { "Collections", "var i0 = new(Int, $I(100));\n" "var i1 = new(Int, $I(200));\n" "var x = new(Array, Ref, i0, i1);\n" "\n" "print(deref(get(x, $I(0)))); /* 100 */" "\n" "del(x); /* Contents of `x` still alive */\n" }, {NULL, NULL} }; return examples; } static void Ref_Ref(var self, var val); static var Ref_Deref(var self); static void Ref_Assign(var self, var obj); static void Ref_Assign(var self, var obj) { struct Pointer* p = instance(obj, Pointer); if (p and p->deref) { Ref_Ref(self, p->deref(obj)); } else { Ref_Ref(self, obj); } } static void Ref_Ref(var self, var val) { struct Ref* r = self; r->val = val; } static var Ref_Deref(var self) { struct Ref* r = self; return r->val; } var Ref = Cello(Ref, Instance(Doc, Ref_Name, Ref_Brief, Ref_Description, Ref_Definition, Ref_Examples, NULL), Instance(Assign, Ref_Assign), Instance(Pointer, Ref_Ref, Ref_Deref)); static const char* Box_Name(void) { return "Box"; } static const char* Box_Brief(void) { return "Unique Pointer"; } static const char* Box_Description(void) { return "The `Box` type is another wrapper around a C pointer with one additional " "behaviour as compared to `Ref`. When a `Box` object is deleted it will " "also call `del` on the object it points to. The means a `Box` is " "considered a pointer type that _owns_ the object it points to, and so is " "responsible for it's destruction. Due to this `Box`s must point to valid " "Cello objects and so can't be initalised with `NULL` or anything else " "invalid. " "\n\n" "While this might not seem that useful when there is Garbage Collection " "this can be very useful when Garbage Collection is turned off, and when " "used in conjunction with collections."; } static const char* Box_Definition(void) { return "struct Box {\n" " var val;\n" "};\n"; } static struct Example* Box_Examples(void) { static struct Example examples[] = { { "Usage", "var obj0 = $F(1.0), obj1 = $F(2.0);\n" "var r = $(Box, obj0);\n" "show(r);\n" "show(deref(r)); /* 1.0 */\n" "ref(r, obj1);\n" "show(deref(r)); /* 2.0 */\n" "assign(r, obj0);\n" "show(deref(r)); /* 1.0 */\n" }, { "Lifetimes", "var quote = $S(\"Life is long\");\n" "\n" "with (r in $B(new(String, quote))) {\n" " println(\"This reference is: %$\", r);\n" " println(\"This string is alive: '%s'\", deref(r));\n" "}\n" "\n" "print(\"Now it has been cleared up!\\n\");\n" }, { "Collection", "/* Multiple Types in one Collection */\n" "var x = new(Array, Box, \n" " new(String, $S(\"Hello\")), \n" " new(String, $S(\"There\")), \n" " new(Int, $I(10)));\n" "\n" "print(deref(get(x, $I(0)))); /* Hello */ \n" "\n" "del(x); /* Contents of `x` deleted with it */\n" }, {NULL, NULL} }; return examples; } static void Box_Ref(var self, var val); static var Box_Deref(var self); static void Box_Assign(var self, var obj); static void Box_New(var self, var args) { Box_Assign(self, get(args, $I(0))); } static void Box_Del(var self) { var obj = Box_Deref(self); if (obj) { del(obj); } Box_Ref(self, NULL); } static void Box_Assign(var self, var obj) { struct Pointer* p = instance(obj, Pointer); if (p and p->deref) { Box_Ref(self, p->deref(obj)); } else { Box_Ref(self, obj); } } static int Box_Show(var self, var output, int pos) { return print_to(output, pos, "<'Box' at 0x%p (%$)>", self, Box_Deref(self)); } static void Box_Ref(var self, var val) { struct Box* b = self; b->val = val; } static var Box_Deref(var self) { struct Box* b = self; return b->val; } var Box = Cello(Box, Instance(Doc, Box_Name, Box_Brief, Box_Description, Box_Definition, Box_Examples, NULL), Instance(New, Box_New, Box_Del), Instance(Assign, Box_Assign), Instance(Show, Box_Show, NULL), Instance(Pointer, Box_Ref, Box_Deref)); ================================================ FILE: src/Push.c ================================================ #include "Cello.h" static const char* Push_Name(void) { return "Push"; } static const char* Push_Brief(void) { return "Pushable and Popable object"; } static const char* Push_Description(void) { return "" "The `Push` class provides an interface for the addition and removal of " "objects from another in a positional sense." "\n\n" "`push` can be used to add new objects to a collection and `pop` to remove " "them. Usage of `push` can require `assign` to be defined on the argument."; } static const char* Push_Definition(void) { return "struct Push {\n" " void (*push)(var, var);\n" " void (*pop)(var);\n" " void (*push_at)(var, var, var);\n" " void (*pop_at)(var, var);\n" "};\n"; } static struct Example* Push_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Array, Int);\n" "\n" "push(x, $I( 0));\n" "push(x, $I( 5));\n" "push(x, $I(10));\n" "\n" "show(get(x, $I(0))); /* 0 */\n" "show(get(x, $I(1))); /* 5 */\n" "show(get(x, $I(2))); /* 10 */\n" "\n" "pop_at(x, $I(1));\n" "\n" "show(get(x, $I(0))); /* 0 */\n" "show(get(x, $I(1))); /* 10 */\n" }, {NULL, NULL} }; return examples; } static struct Method* Push_Methods(void) { static struct Method methods[] = { { "push", "void push(var self, var obj);", "Push the object `obj` onto the top of object `self`." }, { "pop", "void pop(var self);", "Pop the top item from the object `self`." }, { "push_at", "void push_at(var self, var obj, var key);", "Push the object `obj` onto the object `self` at a given `key`." }, { "pop_at", "void pop_at(var self, var key);", "Pop the object from the object `self` at a given `key`." }, {NULL, NULL, NULL} }; return methods; } var Push = Cello(Push, Instance(Doc, Push_Name, Push_Brief, Push_Description, Push_Definition, Push_Examples, Push_Methods)); void push(var self, var val) { method(self, Push, push, val); } void push_at(var self, var val, var i) { method(self, Push, push_at, val, i); } void pop(var self) { method(self, Push, pop); } void pop_at(var self, var i) { method(self, Push, pop_at, i); } ================================================ FILE: src/Resize.c ================================================ #include "Cello.h" static const char* Resize_Name(void) { return "Reserve"; } static const char* Resize_Brief(void) { return "Object can be resized"; } static const char* Resize_Description(void) { return "The `Resize` class can be implemented by objects which can be resized in " "some way. Resizing to a larger size than the current may allow for some " "resource or other to be preallocated or reserved. For example this class " "is implemented by `Array` and `Table` to either remove a number of items " "quickly or to preallocate memory space if it is known that many items are " "going to be added at a later date."; } static const char* Resize_Definition(void) { return "struct Resize {\n" " void (*resize)(var, size_t);\n" "};\n"; } static struct Method* Resize_Methods(void) { static struct Method methods[] = { { "resize", "void resize(var self, size_t n);", "Resize to some size `n`, perhaps reserving some resource for object " "`self`." }, {NULL, NULL, NULL} }; return methods; } static struct Example* Resize_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Array, Int);\n" "resize(x, 10000); /* Reserve space in Array */ \n" "for (size_t i = 0; i < 10000; i++) {\n" " push(x, $I(i));\n" "}\n" }, { "Usage 2", "var x = new(Array, Int, $I(0), $I(1), $I(2));\n" "resize(x, 0); /* Clear Array of items */\n" }, {NULL, NULL} }; return examples; } var Resize = Cello(Resize, Instance(Doc, Resize_Name, Resize_Brief, Resize_Description, Resize_Definition, Resize_Examples, Resize_Methods)); void resize(var self, size_t n) { method(self, Resize, resize, n); } ================================================ FILE: src/Show.c ================================================ #include "Cello.h" static const char* Format_Name(void) { return "Format"; } static const char* Format_Brief(void) { return "Read or Write with Format String"; } static const char* Format_Description(void) { return "Format abstracts the class of operations such as `scanf`, `sprintf` and " "`fprintf` with matching semantics. It provides general `printf` and " "`scanf` functionality for several different types objects in a " "uniform way. This class is essentially an in-between class, used by the " "`Show` class to read and write output." "\n\n" "It is important to note that the semantics of these operations match " "`printf` and not the newly defined `Show` class. For example it is " "perfectly valid to pass a C `int` to these functions, while the `println` " "function from `Show` must be passed only `var` objects."; } static const char* Format_Definition(void) { return "struct Format {\n" " int (*format_to)(var,int,const char*,va_list);\n" " int (*format_from)(var,int,const char*,va_list);\n" "};\n"; } static struct Example* Format_Examples(void) { static struct Example examples[] = { { "Usage", "/* printf(\"Hello my name is %s, I'm %i\\n\", \"Dan\", 23); */\n" "format_to($(File, stdout), 0, \n" " \"Hello my name is %s, I'm %i\\n\", \"Dan\", 23);\n" }, {NULL, NULL} }; return examples; } static struct Method* Format_Methods(void) { static struct Method methods[] = { { "format_to", "int format_to(var self, int pos, const char* fmt, ...);\n" "int format_to_va(var self, int pos, const char* fmt, va_list va);", "Write a formatted string `fmt` to the object `self` at position `pos`." }, { "format_from", "int format_from(var self, int pos, const char* fmt, ...);\n" "int format_from_va(var self, int pos, const char* fmt, va_list va);", "Read a formatted string `fmt` from the object `self` at position `pos`." }, {NULL, NULL, NULL} }; return methods; } var Format = Cello(Format, Instance(Doc, Format_Name, Format_Brief, Format_Description, Format_Definition, Format_Examples, Format_Methods)); int format_to_va(var self, int pos, const char* fmt, va_list va) { return method(self, Format, format_to, pos, fmt, va); } int format_from_va(var self, int pos, const char* fmt, va_list va) { return method(self, Format, format_from, pos, fmt, va); } int format_to(var self, int pos, const char* fmt, ...) { va_list va; va_start(va, fmt); int ret = format_to_va(self, pos, fmt, va); va_end(va); return ret; } int format_from(var self, int pos, const char* fmt, ...) { va_list va; va_start(va, fmt); int ret = format_from_va(self, pos, fmt, va); va_end(va); return ret; } static const char* Show_Name(void) { return "Show"; } static const char* Show_Brief(void) { return "Convert To or From String"; } static const char* Show_Description(void) { return "The `Show` class is used to convert objects to, and from, a `String` " "representation. Objects which implement `Show` should expect the " "input/output object to be one that support the `Format` class, such as " "`File` or `String`." "\n\n" "The `print`, `println` and `print_to` functions provide a mechanism for " "writing formatted strings with Cello objects. To do this they provide a " "new format specifier `%$` which uses an object's `Show` functionality to " "write that part of the string. All objects which don't support `Show` can " "still be shown via a default implementation." "\n\n" "All the Show methods which are variable arguments only take `var` objects " "as input. To print native C types wrap them in Cello types using `$`." "\n\n" "Standard format specifiers such as `%f` and `%d` will call functions such " "as `c_float` and `c_int` on their passed arguments to convert objects to " "C types before performing the standard C formatting behaviour." "\n\n" "See [printf](http://www.cplusplus.com/reference/cstdio/printf/) for more " "information on format specifiers."; } static const char* Show_Definition(void) { return "struct Show {\n" " int (*show)(var, var, int);\n" " int (*look)(var, var, int);\n" "};\n"; } static struct Example* Show_Examples(void) { static struct Example examples[] = { { "Hello World", "println(\"Hello %s!\", $S(\"World\"));\n" }, { "File Writing", "with (f in new(File, $S(\"prices.txt\"), $S(\"wb\"))) {\n" " print_to(f, 0, \"%$ :: %$\\n\", $S(\"Banana\"), $I(57));\n" " print_to(f, 0, \"%$ :: %$\\n\", $S(\"Apple\"), $I(22));\n" " print_to(f, 0, \"%$ :: %$\\n\", $S(\"Pear\"), $I(16));\n" "}\n" }, { "String Scanning", "var input = $S(\"1 and 52 then 78\");\n" "\n" "var i0 = $I(0), i1 = $I(0), i2 = $I(0);\n" "scan_from(input, 0, \"%i and %i then %i\", i0, i1, i2);\n" "\n" "/* i0: 1, i1: 52, i2: 78 */\n" "println(\"i0: %$, i1: %$, i2: %$\", i0, i1, i2);\n" }, { "String Printing", "var greeting = new(String);\n" "print_to(greeting, 0, \"Hello %s %s, %s?\", \n" " $S(\"Mr\"), $S(\"Johnson\"), $S(\"how are you?\"));\n" "\n" "/* Hello Mr Johnson, how are you? */\n" "show(greeting);\n" }, {NULL, NULL} }; return examples; } static struct Method* Show_Methods(void) { static struct Method methods[] = { { "show", "int show(var self);\n" "int show_to(var self, var out, int pos);", "Show the object `self` either to `stdout` or to the object `output`." }, { "look", "int look(var self);\n" "int look_from(var self, var input, int pos);", "Read the object `self` either from `stdout` or from the object `input`." }, { "print", "#define print(fmt, ...)\n" "#define println(fmt, ...)\n" "#define print_to(out, pos, fmt, ...)\n" "int print_with(const char* fmt, var args);\n" "int println_with(const char* fmt, var args);\n" "int print_to_with(var out, int pos, const char* fmt, var args);", "Print the format string `fmt` either to `stdout` or to the object `out` " "at positions `pos`. Returns new position in output." }, { "scan", "#define scan(fmt, ...)\n" "#define scanln(fmt, ...)\n" "#define scan_from(input, pos, fmt, ...)\n" "int scan_with(const char* fmt, var args);\n" "int scanln_with(const char* fmt, var args);\n" "int scan_from_with(var input, int pos, const char* fmt, var args);", "Scan the format string `fmt` either from `stdin` or from the object " "`input` at position `pos`. Returns new position in output." }, {NULL, NULL, NULL} }; return methods; } var Show = Cello(Show, Instance(Doc, Show_Name, Show_Brief, Show_Description, Show_Definition, Show_Examples, Show_Methods)); int show(var self) { return show_to(self, $(File, stdout), 0); } int show_to(var self, var out, int pos) { if (self is NULL) { return print_to(out, pos, ""); } struct Show* s = instance(self, Show); if (s and s->show) { return s->show(self, out, pos); } return print_to(out, pos, "<'%s' At 0x%p>", type_of(self), self); } int print_with(const char* fmt, var args) { return print_to_with($(File, stdout), 0, fmt, args); } int println_with(const char* fmt, var args) { int pos = 0; pos = print_to_with($(File, stdout), pos, fmt, args); pos = print_to($(File, stdout), pos, "\n"); return pos; } int print_to_with(var out, int pos, const char* fmt, var args) { char* fmt_buf = malloc(strlen(fmt)+1); size_t index = 0; while (true) { if (*fmt is '\0') { break; } const char* start = fmt; /* Match String */ while(*fmt isnt '\0' and *fmt isnt '%') { fmt++; } if (start isnt fmt) { memcpy(fmt_buf, start, fmt - start); fmt_buf[fmt - start] = '\0'; int off = format_to(out, pos, fmt_buf); if (off < 0) { throw(FormatError, "Unable to output format!"); } pos += off; continue; } /* Match %% */ if (*fmt is '%' && *(fmt+1) is '%') { int off = format_to(out, pos, "%%"); if (off < 0) { throw(FormatError, "Unable to output '%%%%'!"); } pos += off; fmt += 2; continue; } /* Match Format Specifier */ while(not strchr("diuoxXfFeEgGaAxcsp$", *fmt)) { fmt++; } if (start isnt fmt) { memcpy(fmt_buf, start, fmt - start + 1); fmt_buf[fmt - start + 1] = '\0'; if (index >= len(args)) { throw(FormatError, "Not enough arguments to Format String!"); } var a = get(args, $I(index)); index++; if (*fmt is '$') { pos = show_to(a, out, pos); } if (*fmt is 's') { int off = format_to(out, pos, fmt_buf, c_str(a)); if (off < 0) { throw(FormatError, "Unable to output String!"); } pos += off; } if (strchr("diouxX", *fmt)) { int off = format_to(out, pos, fmt_buf, c_int(a)); if (off < 0) { throw(FormatError, "Unable to output Int!"); } pos += off; } if (strchr("fFeEgGaA", *fmt)) { int off = format_to(out, pos, fmt_buf, c_float(a)); if (off < 0) { throw(FormatError, "Unable to output Real!"); } pos += off; } if (*fmt is 'c') { int off = format_to(out, pos, fmt_buf, c_int(a)); if (off < 0) { throw(FormatError, "Unable to output Char!"); } pos += off; } if (*fmt is 'p') { int off = format_to(out, pos, fmt_buf, a); if (off < 0) { throw(FormatError, "Unable to output Object!"); } pos += off; } fmt++; continue; } throw(FormatError, "Invalid Format String!"); } free(fmt_buf); return pos; } int look(var self) { return look_from(self, $(File, stdin), 0); } int look_from(var self, var input, int pos) { return method(self, Show, look, input, pos); } int scan_with(const char* fmt, var args) { return scan_from_with($(File, stdin), 0, fmt, args); } int scanln_with(const char* fmt, var args) { int pos = 0; pos = scan_from_with($(File, stdin), pos, fmt, args); pos = scan_from($(File, stdin), pos, "\n"); return pos; } int scan_from_with(var input, int pos, const char* fmt, var args) { char* fmt_buf = malloc(strlen(fmt)+4); size_t index = 0; while (true) { if (*fmt is '\0') { break; } const char* start = fmt; /* Match String */ while (*fmt isnt '\0' and *fmt isnt '%') { fmt++; } if (start isnt fmt) { memcpy(fmt_buf, start, fmt - start); fmt_buf[fmt - start] = '\0'; format_from(input, pos, fmt_buf); pos += (int)(fmt - start); continue; } /* Match %% */ if (*fmt is '%' and *(fmt+1) is '%') { int err = format_from(input, pos, "%%"); if (err < 0) { throw(FormatError, "Unable to input '%%%%'!"); } pos += 2; fmt += 2; continue; } /* Match Format Specifier */ while (not strchr("diuoxXfFeEgGaAxcsp$[^]", *fmt)) { fmt++; } if (start isnt fmt) { int off = 0; memcpy(fmt_buf, start, fmt - start + 1); fmt_buf[fmt - start + 1] = '\0'; strcat(fmt_buf, "%n"); if (index >= len(args)) { throw(FormatError, "Not enough arguments to Format String!"); } var a = get(args, $I(index)); index++; if (*fmt is '$') { pos = look_from(a, input, pos); } else if (*fmt is 's') { int err = format_from(input, pos, fmt_buf, c_str(a), &off); if (err < 1) { throw(FormatError, "Unable to input String!"); } pos += off; } /* TODO: Test */ else if (*fmt is ']') { int err = format_from(input, pos, fmt_buf, c_str(a), &off); if (err < 1) { throw(FormatError, "Unable to input Scanset!"); } pos += off; } else if (strchr("diouxX", *fmt)) { long tmp = 0; int err = format_from(input, pos, fmt_buf, &tmp, &off); if (err < 1) { throw(FormatError, "Unable to input Int!"); } pos += off; assign(a, $I(tmp)); } else if (strchr("fFeEgGaA", *fmt)) { if (strchr(fmt_buf, 'l')) { double tmp = 0; int err = format_from(input, pos, fmt_buf, &tmp, &off); if (err < 1) { throw(FormatError, "Unable to input Float!"); } pos += off; assign(a, $F(tmp)); } else { float tmp = 0; int err = format_from(input, pos, fmt_buf, &tmp, &off); if (err < 1) { throw(FormatError, "Unable to input Float!"); } pos += off; assign(a, $F(tmp)); } } else if (*fmt is 'c') { char tmp = '\0'; int err = format_from(input, pos, fmt_buf, &tmp, &off); if (err < 1) { throw(FormatError, "Unable to input Char!"); } pos += off; assign(a, $I(tmp)); } else if (*fmt is 'p') { void* tmp = NULL; int err = format_from(input, pos, fmt_buf, &tmp, &off); if (err < 1) { throw(FormatError, "Unable to input Ref!"); } pos += off; assign(a, $R(tmp)); } else { /* TODO: Report Better */ throw(FormatError, "Invalid Format Specifier!"); } fmt++; continue; } } free(fmt_buf); return pos; } ================================================ FILE: src/Start.c ================================================ #include "Cello.h" static const char* Start_Name(void) { return "Start"; } static const char* Start_Brief(void) { return "Can be started or stopped"; } static const char* Start_Description(void) { return "The `Start` class can be implemented by types which provide an abstract " "notion of a started and stopped state. This can be real processes such " "as `Thread`, or something like `File` where the on/off correspond to " "if the file is open or not." "\n\n" "The main nicety of the `Start` class is that it allows use of the `with` " "macro which performs the `start` function at the opening of a scope block " "and the `stop` function at the end."; } static const char* Start_Definition(void) { return "struct Start {\n" " void (*start)(var);\n" " void (*stop)(var);\n" " void (*join)(var);\n" " bool (*running)(var);\n" "};\n"; } static struct Example* Start_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Mutex);\n" "start(x); /* Lock Mutex */ \n" "print(\"Inside Mutex!\\n\");\n" "stop(x); /* unlock Mutex */" }, { "Scoped", "var x = new(Mutex);\n" "with (mut in x) { /* Lock Mutex */ \n" " print(\"Inside Mutex!\\n\");\n" "} /* unlock Mutex */" }, {NULL, NULL} }; return examples; } static struct Method* Start_Methods(void) { static struct Method methods[] = { { "with", "#define with(...)", "Perform operations in between `start` and `stop`." }, { "start", "void start(var self);", "Start the object `self`." }, { "stop", "void stop(var self);", "Stop the object `self`." }, { "join", "void join(var self);", "Block and wait for the object `self` to stop." }, { "running", "bool running(var self);", "Check if the object `self` is running." }, {NULL, NULL, NULL} }; return methods; } var Start = Cello(Start, Instance(Doc, Start_Name, Start_Brief, Start_Description, Start_Definition, Start_Examples, Start_Methods)); void start(var self) { method(self, Start, start); } void stop(var self) { method(self, Start, stop); } void join(var self) { method(self, Start, join); } bool running(var self) { return method(self, Start, running); } var start_in(var self) { struct Start* s = instance(self, Start); if (s and s->start) { s->start(self); } return self; } var stop_in(var self) { struct Start* s = instance(self, Start); if (s and s->stop) { s->stop(self); } return NULL; } ================================================ FILE: src/String.c ================================================ #include "Cello.h" static const char* C_Str_Name(void) { return "C_Str"; } static const char* C_Str_Brief(void) { return "Interpret as C String"; } static const char* C_Str_Description(void) { return "The `C_Str` class should be overridden by types which are representable " "as a C style String."; } static const char* C_Str_Definition(void) { return "struct C_Str {\n" " char* (*c_str)(var);\n" "};\n"; } static struct Example* C_Str_Examples(void) { static struct Example examples[] = { { "Usage", "puts(c_str($S(\"Hello\"))); /* Hello */\n" "puts(c_str($S(\"There\"))); /* There */\n" }, {NULL, NULL} }; return examples; } static struct Method* C_Str_Methods(void) { static struct Method methods[] = { { "c_str", "char* c_str(var self);", "Returns the object `self` represented as a `char*`." }, {NULL, NULL, NULL} }; return methods; } var C_Str = Cello(C_Str, Instance(Doc, C_Str_Name, C_Str_Brief, C_Str_Description, C_Str_Definition, C_Str_Examples, C_Str_Methods)); char* c_str(var self) { if (type_of(self) is String) { return ((struct String*)self)->val; } return method(self, C_Str, c_str); } static const char* String_Name(void) { return "String"; } static const char* String_Brief(void) { return "String Object"; } static const char* String_Description(void) { return "The `String` type is a wrapper around the native C string type. This " "includes strings that are allocated on either the Stack or the Heap." "\n\n" "For strings allocated on the heap a number of extra operations are " "provided overs standard C strings such as concatenation."; } static const char* String_Definition(void) { return "struct String {\n" " char* val;\n" "};\n"; } static struct Example* String_Examples(void) { static struct Example examples[] = { { "Usage", "var s0 = $(String, \"Hello\");\n" "var s1 = new(String, $S(\"Hello\"));\n" "append(s1, $S(\" There\"));\n" "show(s0); /* Hello */\n" "show(s1); /* Hello There */\n" }, { "Manipulation", "var s0 = new(String, $S(\"Balloons\"));\n" "\n" "show($I(len(s0))); /* 8 */\n" "show($I(mem(s0, $S(\"Ball\")))); /* 1 */\n" "show($I(mem(s0, $S(\"oon\")))); /* 1 */\n" "show($I(mem(s0, $S(\"Balloons\")))); /* 1 */\n" "show($I(mem(s0, $S(\"l\")))); /* 1 */\n" "\n" "rem(s0, $S(\"oons\"));\n" "\n" "show($I(eq(s0, $S(\"Ball\")))); /* 1 */\n" "\n" "resize(s0, 0);\n" "\n" "show($I(len(s0))); /* 0 */\n" "show($I(eq(s0, $S(\"\")))); /* 1 */\n" }, {NULL, NULL} }; return examples; } static void String_Assign(var self, var obj); static void String_New(var self, var args) { struct String* s = self; if (len(args) > 0) { String_Assign(self, get(args, $I(0))); } else { s->val = calloc(1, 1); } #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif } static void String_Del(var self) { struct String* s = self; #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot destruct String, not on heap!"); } #endif free(s->val); } static void String_Assign(var self, var obj) { struct String* s = self; char* val = c_str(obj); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate String, not on heap!"); } #endif s->val = realloc(s->val, strlen(val) + 1); #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif strcpy(s->val, val); } static char* String_C_Str(var self) { struct String* s = self; return s->val; } static int String_Cmp(var self, var obj) { return strcmp(String_C_Str(self), c_str(obj)); } static size_t String_Len(var self) { struct String* s = self; return strlen(s->val); } static void String_Clear(var self) { struct String* s = self; #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate String, not on heap!"); } #endif s->val = realloc(s->val, 1); #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif s->val[0] = '\0'; } static bool String_Mem(var self, var obj) { struct C_Str* c = instance(obj, C_Str); if (c and c->c_str) { return strstr(String_C_Str(self), c->c_str(obj)); } return false; } static void String_Rem(var self, var obj) { struct C_Str* c = instance(obj, C_Str); if (c and c->c_str) { char* pos = strstr(String_C_Str(self), c->c_str(obj)); size_t count = strlen(String_C_Str(self)) - strlen(pos) - strlen(c->c_str(obj)) + 1; memmove((char*)pos, pos + strlen(c->c_str(obj)), count); } } static uint64_t String_Hash(var self) { struct String* s = self; return hash_data(s->val, strlen(s->val)); } static void String_Concat(var self, var obj) { struct String* s = self; #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate String, not on heap!"); } #endif s->val = realloc(s->val, strlen(s->val) + strlen(c_str(obj)) + 1); #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif strcat(s->val, c_str(obj)); } static void String_Resize(var self, size_t n) { struct String* s = self; #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate String, not on heap!"); } #endif size_t m = String_Len(self); s->val = realloc(s->val, n+1); if (n > m) { memset(&s->val[m], 0, n - m); } else { s->val[n] = '\0'; } #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif } static int String_Format_To(var self, int pos, const char* fmt, va_list va) { struct String* s = self; #ifdef CELLO_WINDOWS va_list va_tmp; va_copy(va_tmp, va); int size = _vscprintf(fmt, va_tmp); va_end(va_tmp); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate String, not on heap!"); } #endif s->val = realloc(s->val, pos + size + 1); #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif return vsprintf(s->val + pos, fmt, va); #elif defined(CELLO_MAC) va_list va_tmp; va_copy(va_tmp, va); char* tmp = NULL; int size = vasprintf(&tmp, fmt, va_tmp); va_end(va_tmp); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate String, not on heap!"); } #endif s->val = realloc(s->val, pos + size + 1); #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif s->val[pos] = '\0'; strcat(s->val, tmp); free(tmp); return size; #else va_list va_tmp; va_copy(va_tmp, va); int size = vsnprintf(NULL, 0, fmt, va_tmp); va_end(va_tmp); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate String, not on heap!"); } #endif s->val = realloc(s->val, pos + size + 1); #if CELLO_MEMORY_CHECK == 1 if (s->val is NULL) { throw(OutOfMemoryError, "Cannot allocate String, out of memory!"); } #endif return vsprintf(s->val + pos, fmt, va); #endif } static int String_Format_From(var self, int pos, const char* fmt, va_list va) { struct String* s = self; return vsscanf(s->val + pos, fmt, va); } static int String_Show(var self, var out, int pos) { struct String* s = self; pos = print_to(out, pos, "\"", self); char* v = s->val; while (*v) { switch (*v) { case '\a': pos = print_to(out, pos, "\\a"); break; case '\b': pos = print_to(out, pos, "\\b"); break; case '\f': pos = print_to(out, pos, "\\f"); break; case '\n': pos = print_to(out, pos, "\\n"); break; case '\r': pos = print_to(out, pos, "\\r"); break; case '\t': pos = print_to(out, pos, "\\t"); break; case '\v': pos = print_to(out, pos, "\\v"); break; case '\\': pos = print_to(out, pos, "\\\\"); break; case '\'': pos = print_to(out, pos, "\\'"); break; case '\"': pos = print_to(out, pos, "\\\""); break; case '\?': pos = print_to(out, pos, "\\?"); break; default: pos = print_to(out, pos, "%c", $I(*v)); } v++; } return print_to(out, pos, "\"", self); } static int String_Look(var self, var input, int pos) { String_Clear(self); var chr = $I(0); pos = scan_from(input, pos, "%c", chr); if (c_int(chr) isnt '\"') { throw(FormatError, "String literal does not start with quotation marks!"); } while (true) { pos = scan_from(input, pos, "%c", chr); if (c_int(chr) == '"') { break; } if (c_int(chr) == '\\') { pos = scan_from(input, pos, "%c", chr); switch (c_int(chr)) { case 'a': String_Concat(self, $S("\a")); break; case 'b': String_Concat(self, $S("\b")); break; case 'f': String_Concat(self, $S("\f")); break; case 'n': String_Concat(self, $S("\n")); break; case 'r': String_Concat(self, $S("\r")); break; case 't': String_Concat(self, $S("\t")); break; case 'v': String_Concat(self, $S("\v")); break; case '\\': String_Concat(self, $S("\\")); break; case '\'': String_Concat(self, $S("\'")); break; case '"': String_Concat(self, $S("\"")); break; case '?': String_Concat(self, $S("\?")); break; default: throw(FormatError, "Unknown Escape Sequence '\\%c'!", chr); } } char buffer[2]; buffer[0] = (char)c_int(chr); buffer[1] = '\0'; String_Concat(self, $S(buffer)); } return pos; } var String = Cello(String, Instance(Doc, String_Name, String_Brief, String_Description, String_Definition, String_Examples, NULL), Instance(New, String_New, String_Del), Instance(Assign, String_Assign), Instance(Cmp, String_Cmp), Instance(Hash, String_Hash), Instance(Len, String_Len), Instance(Get, NULL, NULL, String_Mem, String_Rem), Instance(Resize, String_Resize), Instance(Concat, String_Concat, String_Concat), Instance(C_Str, String_C_Str), Instance(Format, String_Format_To, String_Format_From), Instance(Show, String_Show, String_Look)); ================================================ FILE: src/Table.c ================================================ #include "Cello.h" static const char* Table_Name(void) { return "Table"; } static const char* Table_Brief(void) { return "Hash table"; } static const char* Table_Description(void) { return "The `Table` type is a hash table data structure that maps keys to values. " "It uses an open-addressing robin-hood hashing scheme which requires " "`Hash` and `Cmp` to be defined on the key type. Keys and values are " "copied into the collection using the `Assign` class and intially have " "zero'd memory." "\n\n" "Hash tables provide `O(1)` lookup, insertion and removal can but require " "long pauses when the table must be _rehashed_ and all entries processed." "\n\n" "This is largely equivalent to the C++ construct " "[std::unordered_map](http://www.cplusplus.com/reference/unordered_map/unordered_map/)"; } static struct Example* Table_Examples(void) { static struct Example examples[] = { { "Usage", "var prices = new(Table, String, Int);\n" "set(prices, $S(\"Apple\"), $I(12));\n" "set(prices, $S(\"Banana\"), $I( 6));\n" "set(prices, $S(\"Pear\"), $I(55));\n" "\n" "foreach (key in prices) {\n" " var price = get(prices, key);\n" " println(\"Price of %$ is %$\", key, price);\n" "}\n" }, { "Manipulation", "var t = new(Table, String, Int);\n" "set(t, $S(\"Hello\"), $I(2));\n" "set(t, $S(\"There\"), $I(5));\n" "\n" "show($I(len(t))); /* 2 */\n" "show($I(mem(t, $S(\"Hello\")))); /* 1 */\n" "\n" "rem(t, $S(\"Hello\"));\n" "\n" "show($I(len(t))); /* 1 */\n" "show($I(mem(t, $S(\"Hello\")))); /* 0 */\n" "show($I(mem(t, $S(\"There\")))); /* 1 */\n" "\n" "resize(t, 0);\n" "\n" "show($I(len(t))); /* 0 */\n" "show($I(mem(t, $S(\"Hello\")))); /* 0 */\n" "show($I(mem(t, $S(\"There\")))); /* 0 */\n" }, {NULL, NULL} }; return examples; } struct Table { var data; var ktype; var vtype; size_t ksize; size_t vsize; size_t nslots; size_t nitems; var sspace0; var sspace1; }; enum { TABLE_PRIMES_COUNT = 24 }; static const size_t Table_Primes[TABLE_PRIMES_COUNT] = { 0, 1, 5, 11, 23, 53, 101, 197, 389, 683, 1259, 2417, 4733, 9371, 18617, 37097, 74093, 148073, 296099, 592019, 1100009, 2200013, 4400021, 8800019 }; static const double Table_Load_Factor = 0.9; static size_t Table_Ideal_Size(size_t size) { size = (size_t)((double)(size+1) / Table_Load_Factor); for (size_t i = 0; i < TABLE_PRIMES_COUNT; i++) { if (Table_Primes[i] >= size) { return Table_Primes[i]; } } size_t last = Table_Primes[TABLE_PRIMES_COUNT-1]; for (size_t i = 0;; i++) { if (last * i >= size) { return last * i; } } } static size_t Table_Step(struct Table* t) { return sizeof(uint64_t) + sizeof(struct Header) + t->ksize + sizeof(struct Header) + t->vsize; } static uint64_t Table_Key_Hash(struct Table* t, uint64_t i) { return *(uint64_t*)((char*)t->data + i * Table_Step(t)); } static var Table_Key(struct Table* t, uint64_t i) { return (char*)t->data + i * Table_Step(t) + sizeof(uint64_t) + sizeof(struct Header); } static var Table_Val(struct Table* t, uint64_t i) { return (char*)t->data + i * Table_Step(t) + sizeof(uint64_t) + sizeof(struct Header) + t->ksize + sizeof(struct Header); } static uint64_t Table_Probe(struct Table* t, uint64_t i, uint64_t h) { int64_t v = i - (h-1); if (v < 0) { v = t->nslots + v; } return v; } static void Table_Set(var self, var key, var val); static void Table_Set_Move(var self, var key, var val, bool move); static size_t Table_Size_Round(size_t s) { return ((s + sizeof(var) - 1) / sizeof(var)) * sizeof(var); } static void Table_New(var self, var args) { struct Table* t = self; t->ktype = cast(get(args, $(Int, 0)), Type); t->vtype = cast(get(args, $(Int, 1)), Type); t->ksize = Table_Size_Round(size(t->ktype)); t->vsize = Table_Size_Round(size(t->vtype)); size_t nargs = len(args); if (nargs % 2 isnt 0) { throw(FormatError, "Received non multiple of two argument count to Table constructor."); } t->nslots = Table_Ideal_Size((nargs-2)/2); t->nitems = 0; if (t->nslots is 0) { t->data = NULL; return; } t->data = calloc(t->nslots, Table_Step(t)); t->sspace0 = calloc(1, Table_Step(t)); t->sspace1 = calloc(1, Table_Step(t)); #if CELLO_MEMORY_CHECK == 1 if (t->data is NULL or t->sspace0 is NULL or t->sspace1 is NULL) { throw(OutOfMemoryError, "Cannot allocate Table, out of memory!"); } #endif for(size_t i = 0; i < (nargs-2)/2; i++) { var key = get(args, $(Int, 2+(i*2)+0)); var val = get(args, $(Int, 2+(i*2)+1)); Table_Set_Move(t, key, val, false); } } static void Table_Del(var self) { struct Table* t = self; for (size_t i = 0; i < t->nslots; i++) { if (Table_Key_Hash(t, i) isnt 0) { destruct(Table_Key(t, i)); destruct(Table_Val(t, i)); } } free(t->data); free(t->sspace0); free(t->sspace1); } static var Table_Key_Type(var self) { struct Table* t = self; return t->ktype; } static var Table_Val_Type(var self) { struct Table* t = self; return t->vtype; } static void Table_Clear(var self) { struct Table* t = self; for (size_t i = 0; i < t->nslots; i++) { if (Table_Key_Hash(t, i) isnt 0) { destruct(Table_Key(t, i)); destruct(Table_Val(t, i)); } } free(t->data); t->nslots = 0; t->nitems = 0; t->data = NULL; } static void Table_Assign(var self, var obj) { struct Table* t = self; Table_Clear(t); t->ktype = implements_method(obj, Get, key_type) ? key_type(obj) : Ref; t->vtype = implements_method(obj, Get, val_type) ? val_type(obj) : Ref; t->ksize = Table_Size_Round(size(t->ktype)); t->vsize = Table_Size_Round(size(t->vtype)); t->nitems = 0; t->nslots = Table_Ideal_Size(len(obj)); if (t->nslots is 0) { t->data = NULL; return; } t->data = calloc(t->nslots, Table_Step(t)); t->sspace0 = realloc(t->sspace0, Table_Step(t)); t->sspace1 = realloc(t->sspace1, Table_Step(t)); #if CELLO_MEMORY_CHECK == 1 if (t->data is NULL or t->sspace0 is NULL or t->sspace1 is NULL) { throw(OutOfMemoryError, "Cannot allocate Table, out of memory!"); } #endif memset(t->sspace0, 0, Table_Step(t)); memset(t->sspace1, 0, Table_Step(t)); foreach(key in obj) { Table_Set_Move(t, key, get(obj, key), false); } } static var Table_Iter_Init(var self); static var Table_Iter_Next(var self, var curr); static bool Table_Mem(var self, var key); static var Table_Get(var self, var key); static int Table_Cmp(var self, var obj) { int c; var item0 = Table_Iter_Init(self); var item1 = iter_init(obj); while (true) { if (item0 is Terminal and item1 is Terminal) { return 0; } if (item0 is Terminal) { return -1; } if (item1 is Terminal) { return 1; } c = cmp(item0, item1); if (c < 0) { return -1; } if (c > 0) { return 1; } c = cmp(Table_Get(self, item0), get(obj, item1)); if (c < 0) { return -1; } if (c > 0) { return 1; } item0 = Table_Iter_Next(self, item0); item1 = iter_next(obj, item1); } return 0; } static uint64_t Table_Hash(var self) { struct Table* t = self; uint64_t h = 0; var curr = Table_Iter_Init(self); while (curr isnt Terminal) { var vurr = (char*)curr + t->ksize + sizeof(struct Header); h = h ^ hash(curr) ^ hash(vurr); curr = Table_Iter_Next(self, curr); } return h; } static size_t Table_Len(var self) { struct Table* t = self; return t->nitems; } static uint64_t Table_Swapspace_Hash(struct Table* t, var space) { return *((uint64_t*)space); } static var Table_Swapspace_Key(struct Table* t, var space) { return (char*)space + sizeof(uint64_t) + sizeof(struct Header); } static var Table_Swapspace_Val(struct Table* t, var space) { return (char*)space + sizeof(uint64_t) + sizeof(struct Header) + t->ksize + sizeof(struct Header); } static void Table_Set_Move(var self, var key, var val, bool move) { struct Table* t = self; key = cast(key, t->ktype); val = cast(val, t->vtype); uint64_t i = hash(key) % t->nslots; uint64_t j = 0; memset(t->sspace0, 0, Table_Step(t)); memset(t->sspace1, 0, Table_Step(t)); if (move) { uint64_t ihash = i+1; memcpy((char*)t->sspace0, &ihash, sizeof(uint64_t)); memcpy((char*)t->sspace0 + sizeof(uint64_t), (char*)key - sizeof(struct Header), t->ksize + sizeof(struct Header)); memcpy((char*)t->sspace0 + sizeof(uint64_t) + sizeof(struct Header) + t->ksize, (char*)val - sizeof(struct Header), t->vsize + sizeof(struct Header)); } else { struct Header* khead = (struct Header*) ((char*)t->sspace0 + sizeof(uint64_t)); struct Header* vhead = (struct Header*) ((char*)t->sspace0 + sizeof(uint64_t) + sizeof(struct Header) + t->ksize); header_init(khead, t->ktype, AllocData); header_init(vhead, t->vtype, AllocData); uint64_t ihash = i+1; memcpy((char*)t->sspace0, &ihash, sizeof(uint64_t)); assign((char*)t->sspace0 + sizeof(uint64_t) + sizeof(struct Header), key); assign((char*)t->sspace0 + sizeof(uint64_t) + sizeof(struct Header) + t->ksize + sizeof(struct Header), val); } while (true) { uint64_t h = Table_Key_Hash(t, i); if (h is 0) { memcpy((char*)t->data + i * Table_Step(t), t->sspace0, Table_Step(t)); t->nitems++; return; } if (eq(Table_Key(t, i), Table_Swapspace_Key(t, t->sspace0))) { destruct(Table_Key(t, i)); destruct(Table_Val(t, i)); memcpy((char*)t->data + i * Table_Step(t), t->sspace0, Table_Step(t)); return; } uint64_t p = Table_Probe(t, i, h); if (j >= p) { memcpy((char*)t->sspace1, (char*)t->data + i * Table_Step(t), Table_Step(t)); memcpy((char*)t->data + i * Table_Step(t), (char*)t->sspace0, Table_Step(t)); memcpy((char*)t->sspace0, (char*)t->sspace1, Table_Step(t)); j = p; } i = (i+1) % t->nslots; j++; } } static void Table_Rehash(struct Table* t, size_t new_size) { var old_data = t->data; size_t old_size = t->nslots; t->nslots = new_size; t->nitems = 0; t->data = calloc(t->nslots, Table_Step(t)); #if CELLO_MEMORY_CHECK == 1 if (t->data is NULL) { throw(OutOfMemoryError, "Cannot allocate Table, out of memory!"); } #endif for (size_t i = 0; i < old_size; i++) { uint64_t h = *(uint64_t*)((char*)old_data + i * Table_Step(t)); if (h isnt 0) { var key = (char*)old_data + i * Table_Step(t) + sizeof(uint64_t) + sizeof(struct Header); var val = (char*)old_data + i * Table_Step(t) + sizeof(uint64_t) + sizeof(struct Header) + t->ksize + sizeof(struct Header); Table_Set_Move(t, key, val, true); } } free(old_data); } static void Table_Resize_More(struct Table* t) { size_t new_size = Table_Ideal_Size(t->nitems); size_t old_size = t->nslots; if (new_size > old_size) { Table_Rehash(t, new_size); } } static void Table_Resize_Less(struct Table* t) { size_t new_size = Table_Ideal_Size(t->nitems); size_t old_size = t->nslots; if (new_size < old_size) { Table_Rehash(t, new_size); } } static bool Table_Mem(var self, var key) { struct Table* t = self; key = cast(key, t->ktype); if (t->nslots is 0) { return false; } uint64_t i = hash(key) % t->nslots; uint64_t j = 0; while (true) { uint64_t h = Table_Key_Hash(t, i); if (h is 0 or j > Table_Probe(t, i, h)) { return false; } if (eq(Table_Key(t, i), key)) { return true; } i = (i+1) % t->nslots; j++; } return false; } static void Table_Rem(var self, var key) { struct Table* t = self; key = cast(key, t->ktype); if (t->nslots is 0) { throw(KeyError, "Key %$ not in Table!", key); } uint64_t i = hash(key) % t->nslots; uint64_t j = 0; while (true) { uint64_t h = Table_Key_Hash(t, i); if (h is 0 or j > Table_Probe(t, i, h)) { throw(KeyError, "Key %$ not in Table!", key); } if (eq(Table_Key(t, i), key)) { destruct(Table_Key(t, i)); destruct(Table_Val(t, i)); memset((char*)t->data + i * Table_Step(t), 0, Table_Step(t)); while (true) { uint64_t ni = (i+1) % t->nslots; uint64_t nh = Table_Key_Hash(t, ni); if (nh isnt 0 and Table_Probe(t, ni, nh) > 0) { memcpy( (char*)t->data + i * Table_Step(t), (char*)t->data + ni * Table_Step(t), Table_Step(t)); memset((char*)t->data + ni * Table_Step(t), 0, Table_Step(t)); i = ni; } else { break; } } t->nitems--; Table_Resize_Less(t); return; } i = (i+1) % t->nslots; j++; } } static var Table_Get(var self, var key) { struct Table* t = self; if (key >= t->data and ((char*)key) < ((char*)t->data) + t->nslots * Table_Step(self)) { return Table_Val(self, (((char*)key) - ((char*)t->data)) / Table_Step(self)); } key = cast(key, t->ktype); if (t->nslots is 0) { throw(KeyError, "Key %$ not in Table!", key); } uint64_t i = hash(key) % t->nslots; uint64_t j = 0; while (true) { uint64_t h = Table_Key_Hash(t, i); if (h is 0 or j > Table_Probe(t, i, h)) { throw(KeyError, "Key %$ not in Table!", key); } if (eq(Table_Key(t, i), key)) { return Table_Val(t, i); } i = (i+1) % t->nslots; j++; } return NULL; } static void Table_Set(var self, var key, var val) { Table_Set_Move(self, key, val, false); Table_Resize_More(self); } static var Table_Iter_Init(var self) { struct Table* t = self; if (t->nitems is 0) { return Terminal; } for (size_t i = 0; i < t->nslots; i++) { if (Table_Key_Hash(t, i) isnt 0) { return Table_Key(t, i); } } return Terminal; } static var Table_Iter_Next(var self, var curr) { struct Table* t = self; curr = (char*)curr + Table_Step(t); while (true) { if (curr > Table_Key(t, t->nslots-1)) { return Terminal;} uint64_t h = *(uint64_t*)((char*)curr - sizeof(struct Header) - sizeof(uint64_t)); if (h isnt 0) { return curr; } curr = (char*)curr + Table_Step(t); } return Terminal; } static var Table_Iter_Last(var self) { struct Table* t = self; if (t->nitems is 0) { return Terminal; } size_t i = t->nslots-1; while (true) { if (Table_Key_Hash(t, i) isnt 0) { return Table_Key(t, i); } if (i == 0) { break; } i--; } return Terminal; } static var Table_Iter_Prev(var self, var curr) { struct Table* t = self; curr = (char*)curr - Table_Step(t); while (true) { if (curr < Table_Key(t, 0)) { return Terminal; } uint64_t h = *(uint64_t*)((char*)curr - sizeof(struct Header) - sizeof(uint64_t)); if (h isnt 0) { return curr; } curr = (char*)curr - Table_Step(t); } return Terminal; } static var Table_Iter_Type(var self) { struct Table* t = self; return t->ktype; } static int Table_Show(var self, var output, int pos) { struct Table* t = self; pos = print_to(output, pos, "<'Table' At 0x%p {", self); size_t j =0; for(size_t i = 0; i < t->nslots; i++) { if (Table_Key_Hash(t, i) isnt 0) { pos = print_to(output, pos, "%$:%$", Table_Key(t, i), Table_Val(t, i)); if (j < Table_Len(t)-1) { pos = print_to(output, pos, ", "); } j++; } } return print_to(output, pos, "}>"); } static void Table_Resize(var self, size_t n) { struct Table* t = self; if (n is 0) { Table_Clear(t); return; } #if CELLO_BOUND_CHECK == 1 if (n < t->nitems) { throw(FormatError, "Cannot resize Table to make it smaller than %li items", $I(t->nitems)); } #endif Table_Rehash(t, Table_Ideal_Size(n)); } static void Table_Mark(var self, var gc, void(*f)(var,void*)) { struct Table* t = self; for(size_t i = 0; i < t->nslots; i++) { if (Table_Key_Hash(t, i) isnt 0) { f(gc, Table_Key(t, i)); f(gc, Table_Val(t, i)); } } } var Table = Cello(Table, Instance(Doc, Table_Name, Table_Brief, Table_Description, NULL, Table_Examples, NULL), Instance(New, Table_New, Table_Del), Instance(Assign, Table_Assign), Instance(Mark, Table_Mark), Instance(Cmp, Table_Cmp), Instance(Hash, Table_Hash), Instance(Len, Table_Len), Instance(Get, Table_Get, Table_Set, Table_Mem, Table_Rem, Table_Key_Type, Table_Val_Type), Instance(Iter, Table_Iter_Init, Table_Iter_Next, Table_Iter_Last, Table_Iter_Prev, Table_Iter_Type), Instance(Show, Table_Show, NULL), Instance(Resize, Table_Resize)); ================================================ FILE: src/Thread.c ================================================ #include "Cello.h" static const char* Current_Name(void) { return "Current"; } static const char* Current_Brief(void) { return "Implicit Object"; } static const char* Current_Description(void) { return "The `Current` class can be implemented by types which have implicit " "instances associated with them. For example it can be used to retrieve " "the _current_ `Thread`, or it could be used to get the _current_ Garbage " "Collector." "\n\n" "This class may be implemented by types which express the [Singleton " "Design Pattern](http://en.wikipedia.org/wiki/Singleton_pattern)"; } static const char* Current_Definition(void) { return "struct Current {\n" " var (*current)(void);\n" "};\n"; } static struct Example* Current_Examples(void) { static struct Example examples[] = { { "Usage", "var gc = current(GC);\n" "show(gc);\n" "var thread = current(Thread);\n" "show(thread);\n" }, {NULL, NULL} }; return examples; } static struct Method* Current_Methods(void) { static struct Method methods[] = { { "current", "var current(var type);", "Returns the current active object of the given `type`." }, {NULL, NULL, NULL} }; return methods; } var Current = Cello(Current, Instance(Doc, Current_Name, Current_Brief, Current_Description, Current_Definition, Current_Examples, Current_Methods)); var current(var type) { return type_method(type, Current, current); } struct Thread { var func; var args; var tls; bool is_main; bool is_running; #if defined(CELLO_UNIX) pthread_t thread; #elif defined(CELLO_WINDOWS) DWORD id; HANDLE thread; #endif }; static const char* Thread_Name(void) { return "Thread"; } static const char* Thread_Brief(void) { return "Concurrent Execution"; } static const char* Thread_Description(void) { return "The `Thread` type provides a basic primitive for concurrent " "execution. It acts as a basic wrapper around operating system threads, " "using WinThreads on Windows and pthreads otherwise."; } static struct Example* Thread_Examples(void) { static struct Example examples[] = { { "Usage", "var set_value(var args) {\n" " assign(get(args, $I(0)), $I(1));\n" " return NULL;\n" "}\n" "\n" "var i = $I(0);\n" "\n" "var x = new(Thread, $(Function, set_value));\n" "call(x, i);\n" "join(x);\n" "\n" "show(i); /* 1 */\n" }, { "Exclusive Resource", "var increment(var args) {\n" " var mut = get(args, $I(0));\n" " var tot = get(args, $I(1));\n" " lock(mut);\n" " assign(tot, $I(c_int(tot)+1));\n" " unlock(mut);\n" " return NULL;\n" "}\n" "\n" "var mutex = new(Mutex);\n" "var total = $I(0);\n" "\n" "var threads = new(Array, Box,\n" " new(Thread, $(Function, increment)),\n" " new(Thread, $(Function, increment)),\n" " new(Thread, $(Function, increment)));\n" "\n" "show(total); /* 0 */\n" "\n" "foreach (t in threads) {\n" " call(deref(t), mutex, total);\n" "}\n" "\n" "foreach (t in threads) {\n" " join(deref(t));\n" "}\n" "\n" "show(total); /* 3 */\n" }, {NULL, NULL} }; return examples; } static void Thread_New(var self, var args) { struct Thread* t = self; t->func = empty(args) ? NULL : get(args, $I(0)); t->args = NULL; t->is_main = false; t->is_running = false; t->tls = new_raw(Table, String, Ref); } static void Thread_Del(var self) { struct Thread* t = self; #ifdef CELLO_WINDOWS CloseHandle(t->thread); #endif if (t->args isnt NULL) { del_raw(t->args); } del_raw(t->tls); } static int64_t Thread_C_Int(var self) { struct Thread* t = self; if (not t->is_running) { throw(ValueError, "Cannot get thread ID, thread not running!"); } #if defined(CELLO_UNIX) return (int64_t)t->thread; #elif defined(CELLO_WINDOWS) return (int64_t)t->id; #else return 0; #endif } static void Thread_Assign(var self, var obj) { struct Thread* t = self; struct Thread* o = cast(obj, Thread); t->func = o->func; t->tls = t->tls ? t->tls : alloc_raw(Table); assign(t->tls, o->tls); } static int Thread_Cmp(var self, var obj) { return (int)(Thread_C_Int(self) - c_int(obj)); } static uint64_t Thread_Hash(var self) { return Thread_C_Int(self); } static bool Thread_TLS_Key_Created = false; #if defined(CELLO_UNIX) static pthread_key_t Thread_Key_Wrapper; static void Thread_TLS_Key_Create(void) { pthread_key_create(&Thread_Key_Wrapper, NULL); } static void Thread_TLS_Key_Delete(void) { pthread_key_delete(Thread_Key_Wrapper); } static var Thread_Init_Run(var self) { struct Thread* t = self; pthread_setspecific(Thread_Key_Wrapper, t); t->is_running = true; #ifndef CELLO_NGC var bottom = NULL; var gc = new_raw(GC, $R(&bottom)); #endif var exc = new_raw(Exception); var x = call_with(t->func, t->args); del_raw(t->args); t->args = NULL; del_raw(exc); #ifndef CELLO_NGC del_raw(gc); #endif return x; } #elif defined(CELLO_WINDOWS) static DWORD Thread_Key_Wrapper; static void Thread_TLS_Key_Create(void) { Thread_Key_Wrapper = TlsAlloc(); } static void Thread_TLS_Key_Delete(void) { TlsFree(Thread_Key_Wrapper); } static DWORD Thread_Init_Run(var self) { struct Thread* t = self; TlsSetValue(Thread_Key_Wrapper, t); t->is_running = true; var ex = new_raw(Exception); #ifndef CELLO_NGC var bottom = NULL; var gc = new_raw(GC, $R(&bottom)); #endif call_with(t->func, t->args); del_raw(t->args); t->args = NULL; #ifndef CELLO_NGC del_raw(gc); #endif del_raw(ex); return 0; } #endif static var Thread_Call(var self, var args) { struct Thread* t = self; t->args = assign(alloc_raw(type_of(args)), args); /* Call Init Thread & Run */ #if defined(CELLO_UNIX) /* Setup Thread Local Storage */ if (not Thread_TLS_Key_Created) { Thread_TLS_Key_Create(); Thread_TLS_Key_Created = true; atexit(Thread_TLS_Key_Delete); } int err = pthread_create(&t->thread, NULL, Thread_Init_Run, t); if (err is EINVAL) { throw(ValueError, "Invalid Argument to Thread Creation"); } if (err is EAGAIN) { throw(OutOfMemoryError, "Not enough resources to create another Thread"); } if (err is EBUSY) { throw(BusyError, "System is too busy to create thread"); } #elif defined(CELLO_WINDOWS) /* Setup Thread Local Storage */ if (not Thread_TLS_Key_Created) { Thread_TLS_Key_Create(); Thread_TLS_Key_Created = true; atexit(Thread_TLS_Key_Delete); } t->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Thread_Init_Run, t, 0, &t->id); if (t->thread is NULL) { throw(ValueError, "Unable to Create WinThread"); } #else throw(ResourceError, "Unsupported Threading Environment"); #endif return self; } static var Thread_Main = NULL; static var Exception_Main = NULL; static void Thread_Main_Del(void) { del_raw(Exception_Main); del_raw(Thread_Main); } static var Thread_Current(void) { if (not Thread_TLS_Key_Created) { Thread_TLS_Key_Create(); Thread_TLS_Key_Created = true; atexit(Thread_TLS_Key_Delete); } #if defined(CELLO_UNIX) var wrapper = pthread_getspecific(Thread_Key_Wrapper); #elif defined(CELLO_WINDOWS) var wrapper = TlsGetValue(Thread_Key_Wrapper); #else var wrapper = NULL; #endif /* ** Here is a nasty one. On OSX instead of ** returning NULL for an unset key it ** decides to return uninitialized rubbish ** (even though the spec says otherwise). ** ** Luckily we can test directly for the main ** thread on OSX using this non-portable method */ #ifdef CELLO_MAC if (pthread_main_np()) { wrapper = NULL; } #endif if (wrapper is NULL) { if (Thread_Main is NULL) { Thread_Main = new_raw(Thread); Exception_Main = new_raw(Exception); atexit(Thread_Main_Del); } struct Thread* t = Thread_Main; t->is_main = true; t->is_running = true; #if defined(CELLO_UNIX) t->thread = pthread_self(); #elif defined(CELLO_WINDOWS) t->thread = GetCurrentThread(); #endif return Thread_Main; } return wrapper; } static void Thread_Start(var self) { call(self); } static void Thread_Stop(var self) { struct Thread* t = self; #if defined(CELLO_UNIX) if (not t->thread) { return; } int err = pthread_kill(t->thread, SIGINT); if (err is EINVAL) { throw(ValueError, "Invalid Argument to Thread Stop"); } if (err is ESRCH) { throw(ValueError, "Invalid Thread"); } #elif defined(CELLO_WINDOWS) if (not t->thread) { return; } TerminateThread(t->thread, FALSE); #endif } static void Thread_Join(var self) { struct Thread* t = self; #if defined(CELLO_UNIX) if (not t->thread) { return; } int err = pthread_join(t->thread, NULL); if (err is EINVAL) { throw(ValueError, "Invalid Argument to Thread Join"); } if (err is ESRCH) { throw(ValueError, "Invalid Thread"); } #elif defined(CELLO_WINDOWS) if (not t->thread) { return; } WaitForSingleObject(t->thread, INFINITE); #endif } static bool Thread_Running(var self) { struct Thread* t = self; return t->is_running; } static var Thread_Get(var self, var key) { struct Thread* t = self; return deref(get(t->tls, key)); } static void Thread_Set(var self, var key, var val) { struct Thread* t = self; set(t->tls, key, $R(val)); } static bool Thread_Mem(var self, var key) { struct Thread* t = self; return mem(t->tls, key); } static void Thread_Rem(var self, var key) { struct Thread* t = self; rem(t->tls, key); } static var Thread_Key_Type(var self) { struct Thread* t = self; return key_type(t->tls); } static var Thread_Val_Type(var self) { struct Thread* t = self; return val_type(t->tls); } static void Thread_Mark(var self, var gc, void(*f)(var,void*)) { struct Thread* t = self; mark(t->tls, gc, f); } var Thread = Cello(Thread, Instance(Doc, Thread_Name, Thread_Brief, Thread_Description, NULL, Thread_Examples, NULL), Instance(New, Thread_New, Thread_Del), Instance(Assign, Thread_Assign), Instance(Cmp, Thread_Cmp), Instance(Hash, Thread_Hash), Instance(Call, Thread_Call), Instance(Current, Thread_Current), Instance(Mark, Thread_Mark), Instance(Start, Thread_Start, Thread_Stop, Thread_Join, Thread_Running), Instance(C_Int, Thread_C_Int), Instance(Get, Thread_Get, Thread_Set, Thread_Mem, Thread_Rem)); static const char* Lock_Name(void) { return "Lock"; } static const char* Lock_Brief(void) { return "Exclusive Resource"; } static const char* Lock_Description(void) { return "The `Lock` class can be implemented by types to limit the access to them. " "For example this class is implemented by the `Mutex` type to provide " "mutual exclusion across Threads."; } static const char* Lock_Definition(void) { return "struct Lock {\n" " void (*lock)(var);\n" " void (*unlock)(var);\n" " bool (*trylock)(var);\n" "};\n"; } static struct Method* Lock_Methods(void) { static struct Method methods[] = { { "lock", "void lock(var self);", "Wait until a lock can be aquired on object `self`." }, { "trylock", "bool trylock(var self);", "Try to acquire a lock on object `self`. Returns `true` on success and " "`false` if the resource is busy." }, { "unlock", "void unlock(var self);", "Release lock on object `self`." }, {NULL, NULL, NULL} }; return methods; } static struct Example* Lock_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Mutex);\n" "lock(x); /* Lock Mutex */ \n" "print(\"Inside Mutex!\\n\");\n" "unlock(x); /* Unlock Mutex */" }, {NULL, NULL} }; return examples; } var Lock = Cello(Lock, Instance(Doc, Lock_Name, Lock_Brief, Lock_Description, Lock_Definition, Lock_Examples, Lock_Methods)); void lock(var self) { method(self, Lock, lock); } void unlock(var self) { method(self, Lock, unlock); } bool trylock(var self) { return method(self, Lock, trylock); } struct Mutex { #if defined(CELLO_UNIX) pthread_mutex_t mutex; #elif defined(CELLO_WINDOWS) HANDLE mutex; #endif }; static const char* Mutex_Name(void) { return "Mutex"; } static const char* Mutex_Brief(void) { return "Mutual Exclusion Lock"; } static const char* Mutex_Description(void) { return "The `Mutex` type can be used to gain mutual exclusion across Threads for " "access to some resource."; } static struct Example* Mutex_Examples(void) { static struct Example examples[] = { { "Usage", "var x = new(Mutex);\n" "with (mut in x) { /* Lock Mutex */ \n" " print(\"Inside Mutex!\\n\");\n" "} /* Unlock Mutex */" }, {NULL, NULL} }; return examples; } static void Mutex_New(var self, var args) { struct Mutex* m = self; #if defined(CELLO_UNIX) pthread_mutex_init(&m->mutex, NULL); #elif defined(CELLO_WINDOWS) m->mutex = CreateMutex(NULL, false, NULL); #endif } static void Mutex_Del(var self) { struct Mutex* m = self; #if defined(CELLO_UNIX) pthread_mutex_destroy(&m->mutex); #elif defined(CELLO_WINDOWS) CloseHandle(m->mutex); #endif } static void Mutex_Lock(var self) { struct Mutex* m = self; #if defined(CELLO_UNIX) int err = pthread_mutex_lock(&m->mutex); if (err is EINVAL) { throw(ValueError, "Invalid Argument to Mutex Lock"); } if (err is EDEADLK) { throw(ResourceError, "Attempt to relock already held mutex"); } #elif defined(CELLO_WINDOWS) WaitForSingleObject(m->mutex, INFINITE); #endif } static bool Mutex_Trylock(var self) { struct Mutex* m = self; #if defined(CELLO_UNIX) int err = pthread_mutex_trylock(&m->mutex); if (err == EBUSY) { return false; } if (err is EINVAL) { throw(ValueError, "Invalid Argument to Mutex Lock Try"); } return true; #elif defined(CELLO_WINDOWS) return not (WaitForSingleObject(m->mutex, 0) is WAIT_TIMEOUT); #else return true; #endif } static void Mutex_Unlock(var self) { struct Mutex* m = cast(self, Mutex); #if defined(CELLO_UNIX) int err = pthread_mutex_unlock(&m->mutex); if (err is EINVAL) { throw(ValueError, "Invalid Argument to Mutex Unlock"); } if (err is EPERM) { throw(ResourceError, "Mutex cannot be held by caller"); } #elif defined(CELLO_WINDOWS) ReleaseMutex(m->mutex); #endif } var Mutex = Cello(Mutex, Instance(Doc, Mutex_Name, Mutex_Brief, Mutex_Description, NULL, Mutex_Examples, NULL), Instance(New, Mutex_New, Mutex_Del), Instance(Lock, Mutex_Lock, Mutex_Unlock, Mutex_Trylock), Instance(Start, Mutex_Lock, Mutex_Unlock, NULL)); ================================================ FILE: src/Tree.c ================================================ #include "Cello.h" static const char* Tree_Name(void) { return "Tree"; } static const char* Tree_Brief(void) { return "Balanced Binary Tree"; } static const char* Tree_Description(void) { return "The `Tree` type is a self balancing binary tree implemented as a red-black " "tree. It provides key-value access and requires the `Cmp` class to be " "defined on the key type." "\n\n" "Element lookup and insertion are provided as an `O(log(n))` operation. " "This means in general a `Tree` is slower than a `Table` but it has several " "other nice properties such as being able to iterate over the items in " "order and not having large pauses for rehashing on some insertions." "\n\n" "This is largely equivalent to the C++ construct " "[std::map](http://www.cplusplus.com/reference/map/map/)"; } static struct Example* Tree_Examples(void) { static struct Example examples[] = { { "Usage", "var prices = new(Tree, String, Int);\n" "set(prices, $S(\"Apple\"), $I(12));\n" "set(prices, $S(\"Banana\"), $I( 6));\n" "set(prices, $S(\"Pear\"), $I(55));\n" "\n" "foreach (key in prices) {\n" " var price = get(prices, key);\n" " println(\"Price of %$ is %$\", key, price);\n" "}\n" }, { "Manipulation", "var t = new(Tree, String, Int);\n" "set(t, $S(\"Hello\"), $I(2));\n" "set(t, $S(\"There\"), $I(5));\n" "\n" "show($I(len(t))); /* 2 */\n" "show($I(mem(t, $S(\"Hello\")))); /* 1 */\n" "\n" "rem(t, $S(\"Hello\"));\n" "\n" "show($I(len(t))); /* 1 */\n" "show($I(mem(t, $S(\"Hello\")))); /* 0 */\n" "show($I(mem(t, $S(\"There\")))); /* 1 */\n" "\n" "resize(t, 0);\n" "\n" "show($I(len(t))); /* 0 */\n" "show($I(mem(t, $S(\"Hello\")))); /* 0 */\n" "show($I(mem(t, $S(\"There\")))); /* 0 */\n" }, {NULL, NULL} }; return examples; } struct Tree { var root; var ktype; var vtype; size_t ksize; size_t vsize; size_t nitems; }; static bool Tree_Is_Red(struct Tree* m, var node); static var* Tree_Left(struct Tree* m, var node) { return (var*)((char*)node + 0 * sizeof(var)); } static var* Tree_Right(struct Tree* m, var node) { return (var*)((char*)node + 1 * sizeof(var)); } static var Tree_Get_Parent(struct Tree* m, var node) { var ptr = *(var*)((char*)node + 2 * sizeof(var)); return (var)(((uintptr_t)ptr) & (~1)); } static void Tree_Set_Parent(struct Tree* m, var node, var ptr) { if (Tree_Is_Red(m, node)) { *(var*)((char*)node + 2 * sizeof(var)) = (var)(((uintptr_t)ptr) | 1); } else { *(var*)((char*)node + 2 * sizeof(var)) = ptr; } } static var Tree_Key(struct Tree* m, var node) { return (char*)node + 3 * sizeof(var) + sizeof(struct Header); } static var Tree_Val(struct Tree* m, var node) { return (char*)node + 3 * sizeof(var) + sizeof(struct Header) + m->ksize + sizeof(struct Header); } static void Tree_Set_Color(struct Tree* m, var node, bool col) { var ptr = Tree_Get_Parent(m, node); if (col) { *(var*)((char*)node + 2 * sizeof(var)) = (var)(((uintptr_t)ptr) | 1); } else { *(var*)((char*)node + 2 * sizeof(var)) = ptr; } } static bool Tree_Get_Color(struct Tree* m, var node) { if (node is NULL) { return 0; } var ptr = *(var*)((char*)node + 2 * sizeof(var)); return ((uintptr_t)ptr) & 1; } static void Tree_Set_Black(struct Tree* m, var node) { Tree_Set_Color(m, node, false); } static void Tree_Set_Red(struct Tree* m, var node) { Tree_Set_Color(m, node, true); } static bool Tree_Is_Red(struct Tree* m, var node) { return Tree_Get_Color(m, node); } static bool Tree_Is_Black(struct Tree* m, var node) { return not Tree_Get_Color(m, node); } static var Tree_Alloc(struct Tree* m) { var node = calloc(1, 3 * sizeof(var) + sizeof(struct Header) + m->ksize + sizeof(struct Header) + m->vsize); #if CELLO_MEMORY_CHECK == 1 if (node is NULL) { throw(OutOfMemoryError, "Cannot allocate Tree entry, out of memory!"); } #endif var key = header_init((struct Header*)( (char*)node + 3 * sizeof(var)), m->ktype, AllocData); var val = header_init((struct Header*)( (char*)node + 3 * sizeof(var) + sizeof(struct Header) + m->ksize), m->vtype, AllocData); *Tree_Left(m, node) = NULL; *Tree_Right(m, node) = NULL; Tree_Set_Parent(m, node, NULL); Tree_Set_Red(m, node); return node; } static void Tree_Set(var self, var key, var val); static void Tree_New(var self, var args) { struct Tree* m = self; m->ktype = get(args, $I(0)); m->vtype = get(args, $I(1)); m->ksize = size(m->ktype); m->vsize = size(m->vtype); m->nitems = 0; m->root = NULL; size_t nargs = len(args); if (nargs % 2 isnt 0) { throw(FormatError, "Received non multiple of two argument count to Tree constructor."); } for(size_t i = 0; i < (nargs-2)/2; i++) { var key = get(args, $I(2+(i*2)+0)); var val = get(args, $I(2+(i*2)+1)); Tree_Set(m, key, val); } } static void Tree_Clear_Entry(struct Tree* m, var node) { if (node isnt NULL) { Tree_Clear_Entry(m, *Tree_Left(m, node)); Tree_Clear_Entry(m, *Tree_Right(m, node)); destruct(Tree_Key(m, node)); destruct(Tree_Val(m, node)); free(node); } } static void Tree_Clear(var self) { struct Tree* m = self; Tree_Clear_Entry(m, m->root); m->nitems = 0; m->root = NULL; } static void Tree_Del(var self) { struct Tree* m = self; Tree_Clear(self); } static void Tree_Assign(var self, var obj) { struct Tree* m = self; Tree_Clear(self); m->ktype = implements_method(obj, Get, key_type) ? key_type(obj) : Ref; m->vtype = implements_method(obj, Get, val_type) ? val_type(obj) : Ref; m->ksize = size(m->ktype); m->vsize = size(m->vtype); foreach (key in obj) { Tree_Set(self, key, get(obj, key)); } } static var Tree_Iter_Init(var self); static var Tree_Iter_Next(var self, var curr); static bool Tree_Mem(var self, var key); static var Tree_Get(var self, var key); static int Tree_Cmp(var self, var obj) { int c; var item0 = Tree_Iter_Init(self); var item1 = iter_init(obj); while (true) { if (item0 is Terminal and item1 is Terminal) { return 0; } if (item0 is Terminal) { return -1; } if (item1 is Terminal) { return 1; } c = cmp(item0, item1); if (c < 0) { return -1; } if (c > 0) { return 1; } c = cmp(Tree_Get(self, item0), get(obj, item1)); if (c < 0) { return -1; } if (c > 0) { return 1; } item0 = Tree_Iter_Next(self, item0); item1 = iter_next(obj, item1); } return 0; } static uint64_t Tree_Hash(var self) { struct Tree* m = self; uint64_t h = 0; var curr = Tree_Iter_Init(self); while (curr isnt Terminal) { var node = (char*)curr - sizeof(struct Header) - 3 * sizeof(var); h = h ^ hash(Tree_Key(m, node)) ^ hash(Tree_Val(m, node)); curr = Tree_Iter_Next(self, curr); } return h; } static size_t Tree_Len(var self) { struct Tree* m = self; return m->nitems; } static bool Tree_Mem(var self, var key) { struct Tree* m = self; key = cast(key, m->ktype); var node = m->root; while (node isnt NULL) { int c = cmp(Tree_Key(m, node), key); if (c is 0) { return true; } node = c < 0 ? *Tree_Left(m, node) : *Tree_Right(m, node); } return false; } static var Tree_Get(var self, var key) { struct Tree* m = self; key = cast(key, m->ktype); var node = m->root; while (node isnt NULL) { int c = cmp(Tree_Key(m, node), key); if (c is 0) { return Tree_Val(m, node); } node = c < 0 ? *Tree_Left(m, node) : *Tree_Right(m, node); } return throw(KeyError, "Key %$ not in Tree!", key); } static var Tree_Key_Type(var self) { struct Tree* m = self; return m->ktype; } static var Tree_Val_Type(var self) { struct Tree* m = self; return m->vtype; } static var Tree_Maximum(struct Tree* m, var node) { while (*Tree_Right(m, node) isnt NULL) { node = *Tree_Right(m, node); } return node; } static var Tree_Sibling(struct Tree* m, var node) { if (node is NULL or Tree_Get_Parent(m, node) is NULL) { return NULL; } if (node is *Tree_Left(m, Tree_Get_Parent(m, node))) { return *Tree_Right(m, Tree_Get_Parent(m, node)); } else { return *Tree_Left(m, Tree_Get_Parent(m, node)); } } static var Tree_Grandparent(struct Tree* m, var node) { if ((node isnt NULL) and (Tree_Get_Parent(m, node) isnt NULL)) { return Tree_Get_Parent(m, Tree_Get_Parent(m, node)); } else { return NULL; } } static var Tree_Uncle(struct Tree* m, var node) { var gpar = Tree_Grandparent(m, node); if (gpar is NULL) { return NULL; } if (Tree_Get_Parent(m, node) is *Tree_Left(m, gpar)) { return *Tree_Right(m, gpar); } else { return *Tree_Left(m, gpar); } } void Tree_Replace(struct Tree* m, var oldn, var newn) { if (Tree_Get_Parent(m, oldn) is NULL) { m->root = newn; } else { if (oldn is *Tree_Left(m, Tree_Get_Parent(m, oldn))) { *Tree_Left(m, Tree_Get_Parent(m, oldn)) = newn; } else { *Tree_Right(m, Tree_Get_Parent(m, oldn)) = newn; } } if (newn isnt NULL) { Tree_Set_Parent(m, newn, Tree_Get_Parent(m, oldn)); } } static void Tree_Rotate_Left(struct Tree* m, var node) { var r = *Tree_Right(m, node); Tree_Replace(m, node, r); *Tree_Right(m, node) = *Tree_Left(m, r); if (*Tree_Left(m, r) isnt NULL) { Tree_Set_Parent(m, *Tree_Left(m, r), node); } *Tree_Left(m, r) = node; Tree_Set_Parent(m, node, r); } static void Tree_Rotate_Right(struct Tree* m, var node) { var l = *Tree_Left(m, node); Tree_Replace(m, node, l); *Tree_Left(m, node) = *Tree_Right(m, l); if (*Tree_Right(m, l) isnt NULL) { Tree_Set_Parent(m, *Tree_Right(m, l), node); } *Tree_Right(m, l) = node; Tree_Set_Parent(m, node, l); } static void Tree_Set_Fix(struct Tree* m, var node) { while (true) { if (Tree_Get_Parent(m, node) is NULL) { Tree_Set_Black(m, node); return; } if (Tree_Is_Black(m, Tree_Get_Parent(m, node))) { return; } if ((Tree_Uncle(m, node) isnt NULL) and (Tree_Is_Red(m, Tree_Uncle(m, node)))) { Tree_Set_Black(m, Tree_Get_Parent(m, node)); Tree_Set_Black(m, Tree_Uncle(m, node)); Tree_Set_Red(m, Tree_Grandparent(m, node)); node = Tree_Grandparent(m, node); continue; } if ((node is *Tree_Right(m, Tree_Get_Parent(m, node))) and (Tree_Get_Parent(m, node) is *Tree_Left(m, Tree_Grandparent(m, node)))) { Tree_Rotate_Left(m, Tree_Get_Parent(m, node)); node = *Tree_Left(m, node); } else if ((node is *Tree_Left(m, Tree_Get_Parent(m, node))) and (Tree_Get_Parent(m, node) is *Tree_Right(m, Tree_Grandparent(m, node)))) { Tree_Rotate_Right(m, Tree_Get_Parent(m, node)); node = *Tree_Right(m, node); } Tree_Set_Black(m, Tree_Get_Parent(m, node)); Tree_Set_Red(m, Tree_Grandparent(m, node)); if (node is *Tree_Left(m, Tree_Get_Parent(m, node))) { Tree_Rotate_Right(m, Tree_Grandparent(m, node)); } else { Tree_Rotate_Left(m, Tree_Grandparent(m, node)); } return; } } static void Tree_Set(var self, var key, var val) { struct Tree* m = self; key = cast(key, m->ktype); val = cast(val, m->vtype); var node = m->root; if (node is NULL) { var node = Tree_Alloc(m); assign(Tree_Key(m, node), key); assign(Tree_Val(m, node), val); m->root = node; m->nitems++; Tree_Set_Fix(m, node); return; } while (true) { int c = cmp(Tree_Key(m, node), key); if (c is 0) { assign(Tree_Key(m, node), key); assign(Tree_Val(m, node), val); return; } if (c < 0) { if (*Tree_Left(m, node) is NULL) { var newn = Tree_Alloc(m); assign(Tree_Key(m, newn), key); assign(Tree_Val(m, newn), val); *Tree_Left(m, node) = newn; Tree_Set_Parent(m, newn, node); Tree_Set_Fix(m, newn); m->nitems++; return; } node = *Tree_Left(m, node); } if (c > 0) { if (*Tree_Right(m, node) is NULL) { var newn = Tree_Alloc(m); assign(Tree_Key(m, newn), key); assign(Tree_Val(m, newn), val); *Tree_Right(m, node) = newn; Tree_Set_Parent(m, newn, node); Tree_Set_Fix(m, newn); m->nitems++; return; } node = *Tree_Right(m, node); } } } static void Tree_Rem_Fix(struct Tree* m, var node) { while (true) { if (Tree_Get_Parent(m, node) is NULL) { return; } if (Tree_Is_Red(m, Tree_Sibling(m, node))) { Tree_Set_Red(m, Tree_Get_Parent(m, node)); Tree_Set_Black(m, Tree_Sibling(m, node)); if (node is *Tree_Left(m, Tree_Get_Parent(m, node))) { Tree_Rotate_Left(m, Tree_Get_Parent(m, node)); } else { Tree_Rotate_Right(m, Tree_Get_Parent(m, node)); } } if (Tree_Is_Black(m, Tree_Get_Parent(m, node)) and Tree_Is_Black(m, Tree_Sibling(m, node)) and Tree_Is_Black(m, *Tree_Left(m, Tree_Sibling(m, node))) and Tree_Is_Black(m, *Tree_Right(m, Tree_Sibling(m, node)))) { Tree_Set_Red(m, Tree_Sibling(m, node)); node = Tree_Get_Parent(m, node); continue; } if (Tree_Is_Red(m, Tree_Get_Parent(m, node)) and Tree_Is_Black(m, Tree_Sibling(m, node)) and Tree_Is_Black(m, *Tree_Left(m, Tree_Sibling(m, node))) and Tree_Is_Black(m, *Tree_Right(m, Tree_Sibling(m, node)))) { Tree_Set_Red(m, Tree_Sibling(m, node)); Tree_Set_Black(m, Tree_Get_Parent(m, node)); return; } if (Tree_Is_Black(m, Tree_Sibling(m, node))) { if (node is *Tree_Left(m, Tree_Get_Parent(m, node)) and Tree_Is_Red(m, *Tree_Left(m, Tree_Sibling(m, node))) and Tree_Is_Black(m, *Tree_Right(m, Tree_Sibling(m, node)))) { Tree_Set_Red(m, Tree_Sibling(m, node)); Tree_Set_Black(m, *Tree_Left(m, Tree_Sibling(m, node))); Tree_Rotate_Right(m, Tree_Sibling(m, node)); } else if (node is *Tree_Right(m, Tree_Get_Parent(m, node)) and Tree_Is_Red(m, *Tree_Right(m, Tree_Sibling(m, node))) and Tree_Is_Black(m, *Tree_Left(m, Tree_Sibling(m, node)))) { Tree_Set_Red(m, Tree_Sibling(m, node)); Tree_Set_Black(m, *Tree_Right(m, Tree_Sibling(m, node))); Tree_Rotate_Left(m, Tree_Sibling(m, node)); } } Tree_Set_Color(m, Tree_Sibling(m, node), Tree_Get_Color(m, Tree_Get_Parent(m, node))); Tree_Set_Black(m, Tree_Get_Parent(m, node)); if (node is *Tree_Left(m, Tree_Get_Parent(m, node))) { Tree_Set_Black(m, *Tree_Right(m, Tree_Sibling(m, node))); Tree_Rotate_Left(m, Tree_Get_Parent(m, node)); } else { Tree_Set_Black(m, *Tree_Left(m, Tree_Sibling(m, node))); Tree_Rotate_Right(m, Tree_Get_Parent(m, node)); } return; } } static void Tree_Rem(var self, var key) { struct Tree* m = self; key = cast(key, m->ktype); bool found = false; var node = m->root; while (node isnt NULL) { int c = cmp(Tree_Key(m, node), key); if (c is 0) { found = true; break; } node = c < 0 ? *Tree_Left(m, node) : *Tree_Right(m, node); } if (not found) { throw(KeyError, "Key %$ not in Tree!", key); return; } destruct(Tree_Key(m, node)); destruct(Tree_Val(m, node)); if ((*Tree_Left(m, node) isnt NULL) and (*Tree_Right(m, node) isnt NULL)) { var pred = Tree_Maximum(m, *Tree_Left(m, node)); bool ncol = Tree_Get_Color(m, node); memcpy((char*)node + 3 * sizeof(var), (char*)pred + 3 * sizeof(var), sizeof(struct Header) + m->ksize + sizeof(struct Header) + m->vsize); Tree_Set_Color(m, node, ncol); node = pred; } var chld = *Tree_Right(m, node) is NULL ? *Tree_Left(m, node) : *Tree_Right(m, node); if (Tree_Is_Black(m, node)) { Tree_Set_Color(m, node, Tree_Get_Color(m, chld)); Tree_Rem_Fix(m, node); } Tree_Replace(m, node, chld); if ((Tree_Get_Parent(m, node) is NULL) and (chld isnt NULL)) { Tree_Set_Black(m, chld); } m->nitems--; free(node); } static var Tree_Iter_Init(var self) { struct Tree* m = self; if (m->nitems is 0) { return Terminal; } var node = m->root; while (*Tree_Left(m, node) isnt NULL) { node = *Tree_Left(m, node); } return Tree_Key(m, node); } static var Tree_Iter_Next(var self, var curr) { struct Tree* m = self; var node = (char*)curr - sizeof(struct Header) - 3 * sizeof(var); var prnt = Tree_Get_Parent(m, node); if (*Tree_Right(m, node) isnt NULL) { node = *Tree_Right(m, node); while (*Tree_Left(m, node) isnt NULL) { node = *Tree_Left(m, node); } return Tree_Key(m, node); } while (true) { if (prnt is NULL) { return Terminal; } if (node is *Tree_Left(m, prnt)) { return Tree_Key(m, prnt); } if (node is *Tree_Right(m, prnt)) { prnt = Tree_Get_Parent(m, prnt); node = Tree_Get_Parent(m, node); } } return Terminal; } static var Tree_Iter_Last(var self) { struct Tree* m = self; if (m->nitems is 0) { return Terminal; } var node = m->root; while (*Tree_Right(m, node) isnt NULL) { node = *Tree_Right(m, node); } return Tree_Key(m, node); } static var Tree_Iter_Prev(var self, var curr) { struct Tree* m = self; var node = (char*)curr - sizeof(struct Header) - 3 * sizeof(var); var prnt = Tree_Get_Parent(m, node); if (*Tree_Left(m, node) isnt NULL) { node = *Tree_Left(m, node); while (*Tree_Right(m, node) isnt NULL) { node = *Tree_Right(m, node); } return Tree_Key(m, node); } while (true) { if (prnt is NULL) { return Terminal; } if (node is *Tree_Right(m, prnt)) { return Tree_Key(m, prnt); } if (node is *Tree_Left(m, prnt)) { prnt = Tree_Get_Parent(m, prnt); node = Tree_Get_Parent(m, node); } } return Terminal; } static var Tree_Iter_Type(var self) { struct Tree* m = self; return m->ktype; } static int Tree_Show(var self, var output, int pos) { struct Tree* m = self; pos = print_to(output, pos, "<'Tree' At 0x%p {", self); var curr = Tree_Iter_Init(self); while (curr isnt Terminal) { var node = (char*)curr - sizeof(struct Header) - 3 * sizeof(var); pos = print_to(output, pos, "%$:%$", Tree_Key(m, node), Tree_Val(m, node)); curr = Tree_Iter_Next(self, curr); if (curr isnt Terminal) { pos = print_to(output, pos, ", "); } } return print_to(output, pos, "}>"); } static void Tree_Mark(var self, var gc, void(*f)(var,void*)) { struct Tree* m = self; var curr = Tree_Iter_Init(self); while (curr isnt Terminal) { var node = (char*)curr - sizeof(struct Header) - 3 * sizeof(var); f(gc, Tree_Key(m, node)); f(gc, Tree_Val(m, node)); curr = Tree_Iter_Next(self, curr); } } static void Tree_Resize(var self, size_t n) { if (n is 0) { Tree_Clear(self); } else { throw(FormatError, "Cannot resize Tree to %li items. Trees can only be resized to 0 items.", $I(n)); } } var Tree = Cello(Tree, Instance(Doc, Tree_Name, Tree_Brief, Tree_Description, NULL, Tree_Examples, NULL), Instance(New, Tree_New, Tree_Del), Instance(Assign, Tree_Assign), Instance(Mark, Tree_Mark), Instance(Cmp, Tree_Cmp), Instance(Hash, Tree_Hash), Instance(Len, Tree_Len), Instance(Get, Tree_Get, Tree_Set, Tree_Mem, Tree_Rem, Tree_Key_Type, Tree_Val_Type), Instance(Resize, Tree_Resize), Instance(Iter, Tree_Iter_Init, Tree_Iter_Next, Tree_Iter_Last, Tree_Iter_Prev, Tree_Iter_Type), Instance(Show, Tree_Show, NULL)); ================================================ FILE: src/Tuple.c ================================================ #include "Cello.h" static const char* Tuple_Name(void) { return "Tuple"; } static const char* Tuple_Brief(void) { return "Basic Collection"; } static const char* Tuple_Description(void) { return "The `Tuple` type provides a basic way to create a simple collection of " "objects. Its main use is the fact that it can be constructed on the " "stack using the `tuple` macro. This makes it suitable for a number of " "purposes such as use in functions that take a variable number of " "arguments." "\n\n" "Tuples can also be constructed on the heap and stored in collections. " "This makes them also useful as a simple _untyped_ list of objects." "\n\n" "Internally Tuples are just an array of pointers terminated with a pointer " "to the Cello `Terminal` object. This makes positional access fast, but " "many other operations slow including iteration and counting the number of " "elements. Due to this it is only recommended Tuples are used for small " "collections. " "\n\n" "Because Tuples are terminated with the Cello `Terminal` object this can't " "naturally be included within them. This object should therefore only be " "returned from iteration functions."; } static const char* Tuple_Definition(void) { return "struct Tuple {\n" " var* items;\n" "};\n"; } static struct Example* Tuple_Examples(void) { static struct Example examples[] = { { "Usage", "var x = tuple($I(100), $I(200), $S(\"Hello\"));\n" "show(x);\n" "var y = tuple(Int, $I(10), $I(20));\n" "var z = new_with(Array, y);\n" "show(z);\n" "\n" "foreach (item in x) {\n" " println(\"%$\", item);\n" "}\n" }, {NULL, NULL} }; return examples; } static struct Method* Tuple_Methods(void) { static struct Method methods[] = { { "tuple", "#define tuple(...)", "Construct a `Tuple` object on the stack." }, {NULL, NULL, NULL} }; return methods; } static void Tuple_New(var self, var args) { struct Tuple* t = self; size_t nargs = len(args); t->items = malloc(sizeof(var) * (nargs+1)); #if CELLO_MEMORY_CHECK == 1 if (t->items is NULL) { throw(OutOfMemoryError, "Cannot create Tuple, out of memory!"); } #endif for (size_t i = 0; i < nargs; i++) { t->items[i] = get(args, $I(i)); } t->items[nargs] = Terminal; } static void Tuple_Del(var self) { struct Tuple* t = self; #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot destruct Tuple, not on heap!"); } #endif free(t->items); } static void Tuple_Push(var self, var obj); static void Tuple_Assign(var self, var obj) { struct Tuple* t = self; if (implements_method(obj, Len, len) and implements_method(obj, Get, get)) { size_t nargs = len(obj); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate Tuple, not on heap!"); } #endif t->items = realloc(t->items, sizeof(var) * (nargs+1)); #if CELLO_MEMORY_CHECK == 1 if (t->items is NULL) { throw(OutOfMemoryError, "Cannot allocate Tuple, out of memory!"); } #endif for (size_t i = 0; i < nargs; i++) { t->items[i] = get(obj, $I(i)); } t->items[nargs] = Terminal; } else { foreach (item in obj) { Tuple_Push(self, item); } } } static size_t Tuple_Len(var self) { struct Tuple* t = self; size_t i = 0; while (t->items and t->items[i] isnt Terminal) { i++; } return i; } static var Tuple_Iter_Init(var self) { struct Tuple* t = self; return t->items[0]; } static var Tuple_Iter_Next(var self, var curr) { struct Tuple* t = self; size_t i = 0; while (t->items[i] isnt Terminal) { if (t->items[i] is curr) { return t->items[i+1]; } i++; } return Terminal; } static var Tuple_Iter_Last(var self) { struct Tuple* t = self; return t->items[Tuple_Len(t)-1]; } static var Tuple_Iter_Prev(var self, var curr) { struct Tuple* t = self; if (curr is t->items[0]) { return Terminal; } size_t i = 0; while (t->items[i] isnt Terminal) { if (t->items[i] is curr) { return t->items[i-1]; } i++; } return Terminal; } static var Tuple_Get(var self, var key) { struct Tuple* t = self; size_t nitems = Tuple_Len(t); int64_t i = c_int(key); i = i < 0 ? nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)nitems) { return throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Tuple of size %i.", key, $I(Tuple_Len(t))); } #endif return t->items[i]; } static void Tuple_Set(var self, var key, var val) { struct Tuple* t = self; size_t nitems = Tuple_Len(t); int64_t i = c_int(key); i = i < 0 ? nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)nitems) { throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Tuple of size %i.", key, $I(Tuple_Len(t))); return; } #endif t->items[i] = val; } static bool Tuple_Mem(var self, var item) { foreach (obj in self) { if (eq(obj, item)) { return true; } } return false; } static void Tuple_Pop_At(var self, var key); static void Tuple_Rem(var self, var item) { struct Tuple* t = self; size_t i = 0; while (t->items[i] isnt Terminal) { if (eq(item, t->items[i])) { Tuple_Pop_At(self, $I(i)); return; } i++; } } static int Tuple_Show(var self, var output, int pos) { struct Tuple* t = self; pos = print_to(output, pos, "tuple(", self); size_t i = 0; while (t->items[i] isnt Terminal) { pos = print_to(output, pos, "%$", t->items[i]); if (t->items[i+1] isnt Terminal) { pos = print_to(output, pos, ", "); } i++; } return print_to(output, pos, ")"); } static void Tuple_Push(var self, var obj) { struct Tuple* t = self; size_t nitems = Tuple_Len(t); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate Tuple, not on heap!"); } #endif t->items = realloc(t->items, sizeof(var) * (nitems+2)); #if CELLO_MEMORY_CHECK == 1 if (t->items is NULL) { throw(OutOfMemoryError, "Cannot grow Tuple, out of memory!"); } #endif t->items[nitems+0] = obj; t->items[nitems+1] = Terminal; } static void Tuple_Pop(var self) { struct Tuple* t = self; size_t nitems = Tuple_Len(t); #if CELLO_BOUND_CHECK == 1 if (nitems is 0) { throw(IndexOutOfBoundsError, "Cannot pop. Tuple is empty!"); return; } #endif #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate Tuple, not on heap!"); } #endif t->items = realloc(t->items, sizeof(var) * nitems); t->items[nitems-1] = Terminal; } static void Tuple_Push_At(var self, var obj, var key) { struct Tuple* t = self; size_t nitems = Tuple_Len(t); int64_t i = c_int(key); i = i < 0 ? nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)nitems) { throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Tuple of size %i.", key, $I(nitems)); } #endif #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate Tuple, not on heap!"); } #endif t->items = realloc(t->items, sizeof(var) * (nitems+2)); #if CELLO_MEMORY_CHECK == 1 if (t->items is NULL) { throw(OutOfMemoryError, "Cannot grow Tuple, out of memory!"); } #endif memmove(&t->items[i+1], &t->items[i+0], sizeof(var) * (nitems - (size_t)i + 1)); t->items[i] = obj; } static void Tuple_Pop_At(var self, var key) { struct Tuple* t = self; size_t nitems = Tuple_Len(t); int64_t i = c_int(key); i = i < 0 ? nitems+i : i; #if CELLO_BOUND_CHECK == 1 if (i < 0 or i >= (int64_t)nitems) { throw(IndexOutOfBoundsError, "Index '%i' out of bounds for Tuple of size %i.", key, $I(nitems)); } #endif memmove(&t->items[i+0], &t->items[i+1], sizeof(var) * (nitems - (size_t)i)); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate Tuple, not on heap!"); } #endif t->items = realloc(t->items, sizeof(var) * nitems); } static void Tuple_Concat(var self, var obj) { struct Tuple* t = self; size_t nitems = Tuple_Len(t); size_t objlen = len(obj); #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate Tuple, not on heap!"); } #endif t->items = realloc(t->items, sizeof(var) * (nitems+1+objlen)); #if CELLO_MEMORY_CHECK == 1 if (t->items is NULL) { throw(OutOfMemoryError, "Cannot grow Tuple, out of memory!"); } #endif size_t i = nitems; foreach (item in obj) { t->items[i] = item; i++; } t->items[nitems+objlen] = Terminal; } static void Tuple_Resize(var self, size_t n) { struct Tuple* t = self; #if CELLO_ALLOC_CHECK == 1 if (header(self)->alloc is (var)AllocStack or header(self)->alloc is (var)AllocStatic) { throw(ValueError, "Cannot reallocate Tuple, not on heap!"); } #endif size_t m = Tuple_Len(self); if (n < m) { t->items = realloc(t->items, sizeof(var) * (n+1)); t->items[n] = Terminal; } else { throw(FormatError, "Cannot resize Tuple to %li as it only contains %li items", $I(n), $I(m)); } } static void Tuple_Mark(var self, var gc, void(*f)(var,void*)) { struct Tuple* t = self; size_t i = 0; if (t->items is NULL) { return; } while (t->items[i] isnt Terminal) { f(gc, t->items[i]); i++; } } static void Tuple_Swap(struct Tuple* t, size_t i, size_t j) { var tmp = t->items[i]; t->items[i] = t->items[j]; t->items[j] = tmp; } static size_t Tuple_Sort_Partition( struct Tuple* t, int64_t l, int64_t r, bool(*f)(var,var)) { int64_t p = l + (r - l) / 2; Tuple_Swap(t, p, r); int64_t s = l; for (int64_t i = l; i < r; i++) { if (f(t->items[i], t->items[r])) { Tuple_Swap(t, i, s); s++; } } Tuple_Swap(t, s, r); return s; } static void Tuple_Sort_Part( struct Tuple* t, int64_t l, int64_t r, bool(*f)(var,var)) { if (l < r) { int64_t s = Tuple_Sort_Partition(t, l, r, f); Tuple_Sort_Part(t, l, s-1, f); Tuple_Sort_Part(t, s+1, r, f); } } static void Tuple_Sort_By(var self, bool(*f)(var,var)) { Tuple_Sort_Part(self, 0, Tuple_Len(self)-1, f); } static int Tuple_Cmp(var self, var obj) { struct Tuple* t = self; size_t i = 0; var item0 = t->items[i]; var item1 = iter_init(obj); while (true) { if (item0 is Terminal and item1 is Terminal) { return 0; } if (item0 is Terminal) { return -1; } if (item1 is Terminal) { return 1; } int c = cmp(item0, item1); if (c < 0) { return -1; } if (c > 0) { return 1; } i++; item0 = t->items[i]; item1 = iter_next(obj, item1); } return 0; } static uint64_t Tuple_Hash(var self) { struct Tuple* t = self; uint64_t h = 0; size_t n = Tuple_Len(self); for (size_t i = 0; i < n; i++) { h ^= hash(t->items[i]); } return h; } var Tuple = Cello(Tuple, Instance(Doc, Tuple_Name, Tuple_Brief, Tuple_Description, Tuple_Definition, Tuple_Examples, Tuple_Methods), Instance(New, Tuple_New, Tuple_Del), Instance(Assign, Tuple_Assign), Instance(Cmp, Tuple_Cmp), Instance(Hash, Tuple_Hash), Instance(Len, Tuple_Len), Instance(Get, Tuple_Get, Tuple_Set, Tuple_Mem, Tuple_Rem), Instance(Push, Tuple_Push, Tuple_Pop, Tuple_Push_At, Tuple_Pop_At), Instance(Concat, Tuple_Concat, Tuple_Push), Instance(Resize, Tuple_Resize), Instance(Iter, Tuple_Iter_Init, Tuple_Iter_Next, Tuple_Iter_Last, Tuple_Iter_Prev, NULL), Instance(Mark, Tuple_Mark), Instance(Sort, Tuple_Sort_By), Instance(Show, Tuple_Show, NULL)); ================================================ FILE: src/Type.c ================================================ #include "Cello.h" static const char* Cast_Name(void) { return "Cast"; } static const char* Cast_Brief(void) { return "Runtime Type Checking"; } static const char* Cast_Description(void) { return "The `Cast` class provides a rudimentary run-time type checking. By " "default it simply checks that the passed in object is of a given type " "but it can be overridden by types which have to do more complex checking " "to ensure the types are correct."; } static const char* Cast_Definition(void) { return "struct Cast {\n" " var (*cast)(var, var);\n" "};\n"; } static struct Example* Cast_Examples(void) { static struct Example examples[] = { { "Usage", "var x = $I(100);\n" "struct Int* y = cast(x, Int);\n" "show(y);\n" }, {NULL, NULL} }; return examples; } static struct Method* Cast_Methods(void) { static struct Method methods[] = { { "cast", "var cast(var self, var type);", "Ensures the object `self` is of the given `type` and returns it if it " "is." }, {NULL, NULL, NULL} }; return methods; } var Cast = Cello(Cast, Instance(Doc, Cast_Name, Cast_Brief, Cast_Description, Cast_Definition, Cast_Examples, Cast_Methods)); var cast(var self, var type) { struct Cast* c = instance(self, Cast); if (c and c->cast) { return c->cast(self, type); } if (type_of(self) is type) { return self; } else { return throw(ValueError, "cast expected type %s, got type %s", type_of(self), type); } } static const char* Type_Name(void) { return "Type"; } static const char* Type_Brief(void) { return "Metadata Object"; } static const char* Type_Description(void) { return "The `Type` type is one of the most important types in Cello. It is the " "object which specifies the meta-data associated with a particular object. " "Most importantly this says what classes an object implements and what " "their instances are." "\n\n" "One can get the type of an object using the `type_of` function." "\n\n" "To see if an object implements a class `implements` can be used. To " "call a member of a class with an object `method` can be used." "\n\n" "To see if a type implements a class `type_implements` can be used. To " "call a member of a class, implemented `type_method` can be used."; } static struct Example* Type_Examples(void) { static struct Example examples[] = { { "Usage", "var t = type_of($I(5));\n" "show(t); /* Int */\n" "\n" "show($I(type_implements(t, New))); /* 1 */\n" "show($I(type_implements(t, Cmp))); /* 1 */\n" "show($I(type_implements(t, Hash))); /* 1 */\n" "\n" "show($I(type_method(t, Cmp, cmp, $I(5), $I(6))));\n" }, {NULL, NULL} }; return examples; } static struct Method* Type_Methods(void) { static struct Method methods[] = { { "type_of", "var type_of(var self);", "Returns the `Type` of an object `self`." }, { "instance", "var instance(var self, var cls);\n" "var type_instance(var type, var cls);", "Returns the instance of class `cls` implemented by object `self` or " "type `type`. If class is not implemented then returns `NULL`." }, { "implements", "bool implements(var self, var cls);\n" "bool type_implements(var type, var cls);", "Returns if the object `self` or type `type` implements the class `cls`." }, { "method", "#define method(X, C, M, ...)\n" "#define type_method(T, C, M, ...)", "Returns the result of the call to method `M` of class `C` for object `X`" "or type `T`. If class is not implemented then an error is thrown." }, { "implements_method", "#define implements_method(X, C, M)\n" "#define type_implements_method(T, C, M)", "Returns if the type `T` or object `X` implements the method `M` of " "class C." }, {NULL, NULL, NULL} }; return methods; } enum { CELLO_NBUILTINS = 2 + (CELLO_CACHE_NUM / 3), CELLO_MAX_INSTANCES = 256 }; static var Type_Alloc(void) { struct Header* head = calloc(1, sizeof(struct Header) + sizeof(struct Type) * (CELLO_NBUILTINS + CELLO_MAX_INSTANCES + 1)); #if CELLO_MEMORY_CHECK == 1 if (head is NULL) { throw(OutOfMemoryError, "Cannot create new 'Type', out of memory!"); } #endif return header_init(head, Type, AllocHeap); } static void Type_New(var self, var args) { struct Type* t = self; var name = get(args, $I(0)); var size = get(args, $I(1)); #if CELLO_MEMORY_CHECK == 1 if (len(args) - 2 > CELLO_MAX_INSTANCES) { throw(OutOfMemoryError, "Cannot construct 'Type' with %i instances, maximum is %i.", $I(len(args)), $I(CELLO_MAX_INSTANCES)); } #endif size_t cache_entries = CELLO_CACHE_NUM / 3; for (size_t i = 0; i < cache_entries; i++) { t[i] = (struct Type){ NULL, NULL, NULL }; } t[cache_entries+0] = (struct Type){ NULL, "__Name", (var)c_str(name) }; t[cache_entries+1] = (struct Type){ NULL, "__Size", (var)(uintptr_t)c_int(size) }; for(size_t i = 2; i < len(args); i++) { var ins = get(args, $I(i)); t[CELLO_NBUILTINS-2+i] = (struct Type){ NULL, (var)c_str(type_of(ins)), ins }; } t[CELLO_NBUILTINS+len(args)-2] = (struct Type){ NULL, NULL, NULL }; } static char* Type_Builtin_Name(struct Type* t) { return t[(CELLO_CACHE_NUM / 3)+0].inst; } static size_t Type_Builtin_Size(struct Type* t) { return (size_t)t[(CELLO_CACHE_NUM / 3)+1].inst; } static int Type_Show(var self, var output, int pos) { return format_to(output, pos, "%s", Type_Builtin_Name(self)); } static int Type_Cmp(var self, var obj) { struct Type* objt = cast(obj, Type); return strcmp(Type_Builtin_Name(self), Type_Builtin_Name(objt)); } static uint64_t Type_Hash(var self) { const char* name = Type_Builtin_Name(self); return hash_data(name, strlen(name)); } static char* Type_C_Str(var self) { return Type_Builtin_Name(self); } static void Type_Assign(var self, var obj) { throw(ValueError, "Type objects cannot be assigned."); } static var Type_Copy(var self) { return throw(ValueError, "Type objects cannot be copied."); } static int print_indent(var out, int pos, const char* str) { pos = print_to(out, pos, " "); while (*str) { if (*str is '\n') { pos = print_to(out, pos, "\n "); } else { pos = print_to(out, pos, "%c", $I(*str)); } str++; } return pos; } static int Type_Help_To(var self, var out, int pos) { struct Doc* doc = type_instance(self, Doc); if (doc is NULL) { return print_to(out, pos, "\nNo Documentation Found for Type %s\n", self); } pos = print_to(out, pos, "\n"); pos = print_to(out, pos, "# %s ", self); if (doc->brief) { pos = print_to(out, pos, " - %s\n\n", $S((char*)doc->brief())); } if (doc->description) { pos = print_to(out, pos, "%s\n\n", $S((char*)doc->description())); } if (doc->definition) { pos = print_to(out, pos, "\n### Definition\n\n"); pos = print_indent(out, pos, doc->definition()); pos = print_to(out, pos, "\n\n"); } if (doc->methods) { pos = print_to(out, pos, "\n### Methods\n\n"); struct Method* methods = doc->methods(); while (methods[0].name) { pos = print_to(out, pos, "__%s__\n\n", $S((char*)methods[0].name)); pos = print_indent(out, pos, methods[0].definition); pos = print_to(out, pos, "\n\n%s\n\n", $S((char*)methods[0].description)); methods++; } } if (doc->examples) { pos = print_to(out, pos, "\n### Examples\n\n"); struct Example* examples = doc->examples(); while (examples[0].name) { pos = print_to(out, pos, "__%s__\n\n", $S((char*)examples[0].name)); pos = print_indent(out, pos, examples[0].body); pos = print_to(out, pos, "\n\n"); examples++; } pos = print_to(out, pos, "\n\n"); } return pos; } var Type = CelloEmpty(Type, Instance(Doc, Type_Name, Type_Brief, Type_Description, NULL, Type_Examples, Type_Methods), Instance(Assign, Type_Assign), Instance(Copy, Type_Copy), Instance(Alloc, Type_Alloc, NULL), Instance(New, Type_New, NULL), Instance(Cmp, Type_Cmp), Instance(Hash, Type_Hash), Instance(Show, Type_Show, NULL), Instance(C_Str, Type_C_Str), Instance(Help, Type_Help_To)); static var Type_Scan(var self, var cls) { #if CELLO_METHOD_CHECK == 1 if (type_of(self) isnt Type) { return throw(TypeError, "Method call got non type '%s'", type_of(self)); } #endif struct Type* t; t = (struct Type*)self + CELLO_NBUILTINS; while (t->name) { if (t->cls is cls) { return t->inst; } t++; } t = (struct Type*)self + CELLO_NBUILTINS; while (t->name) { if (strcmp(t->name, Type_Builtin_Name(cls)) is 0) { t->cls = cls; return t->inst; } t++; } return NULL; } static bool Type_Implements(var self, var cls) { return Type_Scan(self, cls) isnt NULL; } bool type_implements(var self, var cls) { return Type_Implements(self, cls); } static var Type_Instance(var self, var cls); static var Type_Method_At_Offset( var self, var cls, size_t offset, const char* method_name) { var inst = Type_Instance(self, cls); #if CELLO_METHOD_CHECK == 1 if (inst is NULL) { return throw(ClassError, "Type '%s' does not implement class '%s'", self, cls); } #endif #if CELLO_METHOD_CHECK == 1 var meth = *((var*)(((char*)inst) + offset)); if (meth is NULL) { return throw(ClassError, "Type '%s' implements class '%s' but not the method '%s' required", self, cls, $(String, (char*)method_name)); } #endif return inst; } var type_method_at_offset( var self, var cls, size_t offset, const char* method_name) { return Type_Method_At_Offset(self, cls, offset, method_name); } static bool Type_Implements_Method_At_Offset(var self, var cls, size_t offset) { var inst = Type_Scan(self, cls); if (inst is NULL) { return false; } var meth = *((var*)(((char*)inst) + offset)); if (meth is NULL) { return false; } return true; } bool type_implements_method_at_offset(var self, var cls, size_t offset) { return Type_Implements_Method_At_Offset(self, cls, offset); } /* ** Doing the lookup of a class instances is fairly fast ** but still too slow to be done inside a tight inner loop. ** This is because there could be any number of instances ** and they could be in any order, so each time a linear ** search must be done to find the correct instance. ** ** We can remove the need for a linear search by placing ** some common class instances at known locations. These ** are the _Type Cache Entries_ and are located at some ** preallocated space at the beginning of every type object. ** ** The only problem is that these instances are not filled ** at compile type, so we must dynamically fill them if they ** are empty. But this can be done with a standard call to ** `Type_Scan` the first time. ** ** The main advantage of this method is that it gives the compiler ** a better chance of inlining the code up to the call of the ** instance function pointer, and removes the overhead ** associated with setting up the call to `Type_Scan` which is ** too complex a call to be effectively inlined. ** */ #define Type_Cache_Entry(i, lit) \ if (cls is lit) { \ var inst = ((var*)self)[i]; \ if (inst is NULL) { \ inst = Type_Scan(self, lit); \ ((var*)self)[i] = inst; \ } \ return inst; \ } static var Type_Instance(var self, var cls) { #if CELLO_CACHE == 1 Type_Cache_Entry( 0, Size); Type_Cache_Entry( 1, Alloc); Type_Cache_Entry( 2, New); Type_Cache_Entry( 3, Assign); Type_Cache_Entry( 4, Cmp); Type_Cache_Entry( 5, Mark); Type_Cache_Entry( 6, Hash); Type_Cache_Entry( 7, Len); Type_Cache_Entry( 8, Iter); Type_Cache_Entry( 9, Push); Type_Cache_Entry(10, Concat); Type_Cache_Entry(11, Get); Type_Cache_Entry(12, C_Str); Type_Cache_Entry(13, C_Int); Type_Cache_Entry(14, C_Float); Type_Cache_Entry(15, Current); Type_Cache_Entry(16, Cast); Type_Cache_Entry(17, Pointer); #endif return Type_Scan(self, cls); } #undef Type_Cache_Entry var type_instance(var self, var cls) { return Type_Instance(self, cls); } static var Type_Of(var self) { /* ** The type of a Type object is just `Type` again. But because `Type` is ** extern it isn't a constant expression. This means it cannot be set at ** compile time. ** ** But we really want to be able to construct types statically. So by ** convention at compile time the type of a Type object is set to `NULL`. ** So if we access a statically allocated object and it tells us `NULL` ** is the type, we assume the type is `Type`. */ #if CELLO_NULL_CHECK == 1 if (self is NULL) { return throw(ValueError, "Received NULL as value to 'type_of'"); } #endif struct Header* head = (struct Header*)((char*)self - sizeof(struct Header)); #if CELLO_MAGIC_CHECK == 1 if (head->magic is (var)0xDeadCe110) { throw(ValueError, "Pointer '%p' passed to 'type_of' " "has bad magic number, it looks like it was already deallocated.", self); } if (head->magic isnt ((var)CELLO_MAGIC_NUM)) { throw(ValueError, "Pointer '%p' passed to 'type_of' " "has bad magic number, perhaps it wasn't allocated by Cello.", self); } #endif if (head->type is NULL) { head->type = Type; } return head->type; } var type_of(var self) { return Type_Of(self); } var instance(var self, var cls) { return Type_Instance(Type_Of(self), cls); } bool implements(var self, var cls) { return Type_Implements(Type_Of(self), cls); } var method_at_offset( var self, var cls, size_t offset, const char* method_name) { return Type_Method_At_Offset(Type_Of(self), cls, offset, method_name); } bool implements_method_at_offset(var self, var cls, size_t offset) { return Type_Implements_Method_At_Offset(Type_Of(self), cls, offset); } static const char* Size_Name(void) { return "Size"; } static const char* Size_Brief(void) { return "Type Size"; } static const char* Size_Description(void) { return "The `Size` class is a very important class in Cello because it gives the " "size in bytes you can expect an object of a given type to be. This is " "used by many methods to allocate, assign, or compare various objects." "\n\n" "By default this size is automatically found and recorded by the `Cello` " "macro, but if the type does it's own allocation, or the size cannot be " "found naturally then it may be necessary to override this method."; } static const char* Size_Definition(void) { return "struct Size {\n" " size_t (*size)(void);\n" "};\n"; } static struct Example* Size_Examples(void) { static struct Example examples[] = { { "Usage", "show($I(size(Int)));\n" "show($I(size(Float)));\n" "show($I(size(Array)));\n" }, {NULL, NULL} }; return examples; } static struct Method* Size_Methods(void) { static struct Method methods[] = { { "size", "size_t size(var type);", "Returns the associated size of a given `type` in bytes." }, {NULL, NULL, NULL} }; return methods; } var Size = Cello(Size, Instance(Doc, Size_Name, Size_Brief, Size_Description, Size_Definition, Size_Examples, Size_Methods)); size_t size(var type) { struct Size* s = type_instance(type, Size); if (s and s->size) { return s->size(); } return Type_Builtin_Size(type); } ================================================ FILE: tests/ptest.c ================================================ #include "ptest.h" #include #include #include #include #if defined(CELLO_UNIX) # include #endif /* Globals */ enum { MAX_NAME = 512 }; enum { MAX_ERROR = 2048 }; enum { MAX_TESTS = 2048 }; static int test_passing = 0; static int suite_passing = 0; /* Colors */ enum { BLACK = 0, BLUE = 1, GREEN = 2, AQUA = 3, RED = 4, PURPLE = 5, YELLOW = 6, WHITE = 7, GRAY = 8, LIGHT_BLUE = 9, LIGHT_GREEN = 10, LIGHT_AQUA = 11, LIGHT_RED = 12, LIGHT_PURPLE = 13, LIGHT_YELLOW = 14, LIGHT_WHITE = 15, DEFAULT = 16, }; #ifdef _WIN32 #include static WORD defaults; static int defaults_loaded = 0; static void pt_color(int color) { HANDLE cnsl = GetStdHandle(STD_OUTPUT_HANDLE); if (!defaults_loaded) { CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(cnsl, &info); defaults = info.wAttributes; defaults_loaded = 1; } SetConsoleTextAttribute(cnsl, color == DEFAULT ? defaults : color); } #else static const char* colors[] = { "\x1B[0m", "\x1B[34m", "\x1B[32m", "\x1B[36m", "\x1B[31m", "\x1B[35m", "\x1B[33m", "\x1B[37m", "", "\x1B[34m", "\x1B[32m", "\x1B[36m", "\x1B[31m", "\x1B[35m", "\x1B[33m", "\x1B[37m", "\x1B[39m", }; static void pt_color(int color) { printf("%s", colors[color]); } #endif /* Asserts */ static int num_asserts = 0; static int num_assert_passes = 0; static int num_assert_fails = 0; static char assert_err[MAX_ERROR]; static char assert_err_buff[MAX_ERROR]; static int assert_err_num = 0; void pt_assert_run( int result, const char* expr, const char* func, const char* file, int line) { num_asserts++; test_passing = test_passing && result; if (result) { num_assert_passes++; } else { sprintf(assert_err_buff, " %i. Assert [ %s ] (%s:%i)\n", assert_err_num+1, expr, file, line ); strcat(assert_err, assert_err_buff); assert_err_num++; num_assert_fails++; } } static void ptest_signal(int sig) { test_passing = 0; switch( sig ) { case SIGFPE: sprintf(assert_err_buff, " %i. Division by Zero\n", assert_err_num+1); break; case SIGILL: sprintf(assert_err_buff, " %i. Illegal Instruction\n", assert_err_num+1); break; case SIGSEGV: sprintf(assert_err_buff, " %i. Segmentation Fault\n", assert_err_num+1); break; } assert_err_num++; strcat(assert_err, assert_err_buff); pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(DEFAULT); puts(" | Stopping Execution."); fflush(stdout); exit(0); } /* Tests */ static void pt_title_case(char* output, const char* input) { int space = 1; unsigned int i; strcpy(output, input); for(i = 0; i < strlen(output); i++) { if (output[i] == '_' || output[i] == ' ') { space = 1; output[i] = ' '; continue; } if (space && output[i] >= 'a' && output[i] <= 'z') { space = 0; output[i] = output[i] - 32; continue; } space = 0; } } typedef struct { void (*func)(void); char name[MAX_NAME]; char suite[MAX_NAME]; } test_t; static test_t tests[MAX_TESTS]; static int num_tests = 0; static int num_tests_passes = 0; static int num_tests_fails = 0; void pt_add_test(void (*func)(void), const char* name, const char* suite) { test_t test; if (num_tests == MAX_TESTS) { printf("ERROR: Exceeded maximum test count of %i!\n", MAX_TESTS); abort(); } if (strlen(name) >= MAX_NAME) { printf("ERROR: Test name '%s' too long (Maximum is %i characters)\n", name, MAX_NAME); abort(); } if (strlen(suite) >= MAX_NAME) { printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n", suite, MAX_NAME); abort(); } test.func = func; pt_title_case(test.name, name); pt_title_case(test.suite, suite); tests[num_tests] = test; num_tests++; } /* Suites */ static int num_suites = 0; static int num_suites_passes = 0; static int num_suites_fails = 0; void pt_add_suite(void (*func)(void)) { num_suites++; func(); } /* Running */ static clock_t start, end; static char current_suite[MAX_NAME]; int pt_run(void) { unsigned int i; double total; test_t test; puts(""); puts(" +-------------------------------------------+"); puts(" | ptest MicroTesting Magic for C |"); puts(" | |"); puts(" | http://github.com/orangeduck/ptest |"); puts(" | |"); puts(" | Daniel Holden (contact@theorangeduck.com) |"); puts(" +-------------------------------------------+"); signal(SIGFPE, ptest_signal); signal(SIGILL, ptest_signal); signal(SIGSEGV, ptest_signal); start = clock(); strcpy(current_suite, ""); for(i = 0; i < num_tests; i++) { test = tests[i]; /* Check for transition to a new suite */ if (strcmp(test.suite, current_suite)) { /* Don't increment any counter for first entrance */ if (strcmp(current_suite, "")) { if (suite_passing) { num_suites_passes++; } else { num_suites_fails++; } } suite_passing = 1; strcpy(current_suite, test.suite); printf("\n\n ===== %s =====\n\n", current_suite); } /* Run Test */ test_passing = 1; strcpy(assert_err, ""); strcpy(assert_err_buff, ""); assert_err_num = 0; printf(" | %s ... ", test.name); fflush(stdout); test.func(); suite_passing = suite_passing && test_passing; if (test_passing) { num_tests_passes++; pt_color(GREEN); puts("Passed!"); pt_color(DEFAULT); } else { num_tests_fails++; pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(DEFAULT); } } if (suite_passing) { num_suites_passes++; } else { num_suites_fails++; } end = clock(); puts(""); puts(" +---------------------------------------------------+"); puts(" | Summary |"); puts(" +---------++------------+-------------+-------------+"); printf(" | Suites ||"); pt_color(YELLOW); printf(" Total %4d ", num_suites); pt_color(DEFAULT); putchar('|'); pt_color(GREEN); printf(" Passed %4d ", num_suites_passes); pt_color(DEFAULT); putchar('|'); pt_color(RED); printf(" Failed %4d ", num_suites_fails); pt_color(DEFAULT); puts("|"); printf(" | Tests ||"); pt_color(YELLOW); printf(" Total %4d ", num_tests); pt_color(DEFAULT); putchar('|'); pt_color(GREEN); printf(" Passed %4d ", num_tests_passes); pt_color(DEFAULT); putchar('|'); pt_color(RED); printf(" Failed %4d ", num_tests_fails); pt_color(DEFAULT); puts("|"); printf(" | Asserts ||"); pt_color(YELLOW); printf(" Total %4d ", num_asserts); pt_color(DEFAULT); putchar('|'); pt_color(GREEN); printf(" Passed %4d ", num_assert_passes); pt_color(DEFAULT); putchar('|'); pt_color(RED); printf(" Failed %4d ", num_assert_fails); pt_color(DEFAULT); puts("|"); puts(" +---------++------------+-------------+-------------+"); puts(""); total = (double)(end - start) / CLOCKS_PER_SEC; printf(" Total Running Time: %0.3fs\n\n", total); if (num_suites_fails > 0) { return 1; } else { return 0; } } ================================================ FILE: tests/ptest.h ================================================ #ifndef ptest_h #define ptest_h #include #define PT_SUITE(name) void name(void) #define PT_FUNC(name) static void name(void) #define PT_REG(name) pt_add_test(name, #name, __func__) #define PT_TEST(name) auto void name(void); PT_REG(name); void name(void) #define PT_ASSERT(expr) pt_assert_run((int)((expr) != 0), #expr, __func__, __FILE__, __LINE__) #define PT_ASSERT_STR_EQ(fst, snd) pt_assert_run(strcmp(fst, snd) == 0, "strcmp( " #fst ", " #snd " ) == 0", __func__, __FILE__, __LINE__) void pt_assert_run(int result, const char* expr, const char* func, const char* file, int line); void pt_add_test(void (*func)(void), const char* name, const char* suite); void pt_add_suite(void (*func)(void)); int pt_run(void); #endif ================================================ FILE: tests/test.c ================================================ #include "../include/Cello.h" #include "ptest.h" /* Array */ PT_FUNC(test_array_new) { var a0 = new(Array, Int, $I(1), $I(5), $I(10)); var a1 = new(Array, Float, $F(1.1), $F(2.2)); var a2 = copy(a0); PT_ASSERT(a0); PT_ASSERT(a1); PT_ASSERT(a2); PT_ASSERT(a0 isnt a1); PT_ASSERT(a0 isnt a2); PT_ASSERT(a1 isnt a2); PT_ASSERT( eq(get(a0, $I(0)), $I(1)) ); PT_ASSERT( eq(get(a1, $I(0)), $F(1.1)) ); PT_ASSERT( eq(get(a2, $I(0)), $I(1)) ); del(a0); del(a1); del(a2); } PT_FUNC(test_array_assign) { var a0 = new(Array, Int, $I(1), $I(2), $I(3)); var l0 = new(List, Int, $I(4), $I(5), $I(6), $I(7)); PT_ASSERT(eq(get(a0, $I(0)), $I(1))); PT_ASSERT(eq(get(a0, $I(1)), $I(2))); PT_ASSERT(eq(get(a0, $I(2)), $I(3))); assign(a0, l0); PT_ASSERT(eq(get(a0, $I(0)), $I(4))); PT_ASSERT(eq(get(a0, $I(1)), $I(5))); PT_ASSERT(eq(get(a0, $I(2)), $I(6))); PT_ASSERT(eq(get(a0, $I(3)), $I(7))); assign(a0, range($I(5))); PT_ASSERT(eq(get(a0, $I(0)), $I(0))); PT_ASSERT(eq(get(a0, $I(1)), $I(1))); PT_ASSERT(eq(get(a0, $I(2)), $I(2))); PT_ASSERT(eq(get(a0, $I(3)), $I(3))); PT_ASSERT(eq(get(a0, $I(4)), $I(4))); del(a0); del(l0); } PT_FUNC(test_array_concat) { var a0 = new(Array, Int, $I(1), $I(2), $I(3)); var a1 = new(Array, Int, $I(4), $I(5), $I(6)); concat(a0, a1); PT_ASSERT(eq(get(a0, $I(0)), $I(1))); PT_ASSERT(eq(get(a0, $I(1)), $I(2))); PT_ASSERT(eq(get(a0, $I(2)), $I(3))); PT_ASSERT(eq(get(a0, $I(3)), $I(4))); PT_ASSERT(eq(get(a0, $I(4)), $I(5))); PT_ASSERT(eq(get(a0, $I(5)), $I(6))); append(a0, $I(10)); PT_ASSERT(eq(get(a0, $I(6)), $I(10))); del(a0); del(a1); } PT_FUNC(test_array_cmp) { var a0 = new(Array, Int, $I(1), $I(5), $I(10)); var a1 = new(Array, Int, $I(1), $I(5), $I(10)); var a2 = new(Array, Int, $I(2), $I(5), $I(10)); PT_ASSERT(eq(a0, a1)); PT_ASSERT(neq(a0, a2)); PT_ASSERT(gt(a2, a1)); PT_ASSERT(lt(a1, a2)); del(a0); del(a1); del(a2); } PT_FUNC(test_array_get) { var a0 = new(Array, Int, $I(1), $I(5), $I(10)); PT_ASSERT(len(a0) is 3); PT_ASSERT(mem(a0, $I(1))); PT_ASSERT(mem(a0, $I(5))); rem(a0, $I(5)); PT_ASSERT(len(a0) is 2); PT_ASSERT(mem(a0, $I(1))); PT_ASSERT(not mem(a0, $I(5))); resize(a0, 0); PT_ASSERT(len(a0) is 0); PT_ASSERT(not mem(a0, $I(1))); del(a0); var a1 = new(Array, String, $S("Hello"), $S("There"), $S("People")); PT_ASSERT( eq(get(a1, $I(0)), $S("Hello")) ); PT_ASSERT( eq(get(a1, $I(1)), $S("There")) ); PT_ASSERT( eq(get(a1, $I(2)), $S("People")) ); PT_ASSERT( eq(get(a1, $I(-1)), $S("People")) ); PT_ASSERT( eq(get(a1, $I(-2)), $S("There")) ); PT_ASSERT( eq(get(a1, $I(-3)), $S("Hello")) ); set(a1, $I(1), $S("Blah")); PT_ASSERT( eq(get(a1, $I(0)), $S("Hello")) ); PT_ASSERT( eq(get(a1, $I(1)), $S("Blah")) ); PT_ASSERT( eq(get(a1, $I(2)), $S("People")) ); set(a1, $I(0), $S("Foo")); set(a1, $I(2), $S("Bar")); PT_ASSERT( eq(get(a1, $I(0)), $S("Foo")) ); PT_ASSERT( eq(get(a1, $I(1)), $S("Blah")) ); PT_ASSERT( eq(get(a1, $I(2)), $S("Bar")) ); del(a1); } PT_FUNC(test_array_hash) { var a0 = new(Array, String, $S("Hello"), $S("World")); PT_ASSERT(hash(a0) is (hash($S("Hello")) ^ hash($S("World")))); del(a0); } PT_FUNC(test_array_iter) { var a0 = new(Array, Int); var a1 = new(Array, Float); PT_ASSERT(iter_type(a0) is Int); PT_ASSERT(iter_type(a1) is Float); del(a0); del(a1); a0 = new(Array, String, $S("Hello"), $S("There"), $S("People")); int counter = 0; foreach(item in a0) { switch(counter) { case 0: PT_ASSERT( eq(item, $S("Hello")) ); break; case 1: PT_ASSERT( eq(item, $S("There")) ); break; case 2: PT_ASSERT( eq(item, $S("People")) ); break; } counter++; } PT_ASSERT(counter is 3); counter = 0; foreach(item0 in a0) { foreach(item1 in a0) { counter++; } } PT_ASSERT(counter is 9); del(a0); } PT_FUNC(test_array_len) { var a0 = new(Array, String, $S("Hello"), $S("World")); PT_ASSERT(len(a0) is 2); append(a0, $S("!")); PT_ASSERT(len(a0) is 3); resize(a0, 0); PT_ASSERT(len(a0) is 0); del(a0); } PT_FUNC(test_array_push) { var a0 = new(Array, Int); PT_ASSERT(len(a0) is 0); push(a0, $I(1)); PT_ASSERT(len(a0) is 1); PT_ASSERT( eq(get(a0, $I(0)), $I(1)) ); push(a0, $I(3)); PT_ASSERT(len(a0) is 2); PT_ASSERT( eq(get(a0, $I(0)), $I(1)) ); PT_ASSERT( eq(get(a0, $I(1)), $I(3)) ); push_at(a0, $I(10), $I(0)); PT_ASSERT(len(a0) is 3); PT_ASSERT( eq(get(a0, $I(0)), $I(10)) ); PT_ASSERT( eq(get(a0, $I(1)), $I(1)) ); PT_ASSERT( eq(get(a0, $I(2)), $I(3)) ); push_at(a0, $I(20), $I(1)); PT_ASSERT(len(a0) is 4); PT_ASSERT( eq(get(a0, $I(0)), $I(10)) ); PT_ASSERT( eq(get(a0, $I(1)), $I(20)) ); PT_ASSERT( eq(get(a0, $I(2)), $I(1)) ); PT_ASSERT( eq(get(a0, $I(3)), $I(3)) ); pop_at(a0, $I(2)); PT_ASSERT(len(a0) is 3); PT_ASSERT( eq(get(a0, $I(0)), $I(10)) ); PT_ASSERT( eq(get(a0, $I(1)), $I(20)) ); PT_ASSERT( eq(get(a0, $I(2)), $I(3)) ); pop_at(a0, $I(0)); PT_ASSERT(len(a0) is 2); PT_ASSERT( eq(get(a0, $I(0)), $I(20)) ); PT_ASSERT( eq(get(a0, $I(1)), $I(3)) ); pop(a0); PT_ASSERT(len(a0) is 1); PT_ASSERT( eq(get(a0, $I(0)), $I(20)) ); pop(a0); PT_ASSERT(len(a0) is 0); del(a0); } PT_FUNC(test_array_resize) { var a0 = new(Array, Int); resize(a0, 1000); del(a0); a0 = new(Array, Int, $I(1), $I(2), $I(3)); PT_ASSERT(len(a0) is 3); resize(a0, 0); PT_ASSERT(len(a0) is 0); PT_ASSERT(empty(a0)); del(a0); } PT_FUNC(test_array_show) { var a0 = new(Array, Int, $I(1), $I(5), $I(9)); var s0 = new(String); show_to(a0, s0, 0); del(a0); del(s0); } PT_FUNC(test_array_sort) { var a0 = new(Array, Int, $I(100), $I(1233), $I(1), $I(2312), $I(21)); var a1 = new(Array, Int, $I(1), $I(21), $I(100), $I(1233), $I(2312)); var a2 = new(Array, Int, $I(2312), $I(1233), $I(100), $I(21), $I(1)); var a3 = copy(a0); var a4 = copy(a0); sort(a3); sort_by(a4, gt); PT_ASSERT(eq(a3, a1)); PT_ASSERT(eq(a4, a2)); del(a0); del(a1); del(a2); del(a3); del(a4); var a5 = new(Array, String, $S("a"), $S("b"), $S("c"), $S("d"), $S("e")); var a6 = new(Array, String, $S("c"), $S("b"), $S("e"), $S("a"), $S("d")); PT_ASSERT(neq(a5, a6)); sort(a6); PT_ASSERT(eq(a5, a6)); del(a5); del(a6); } PT_SUITE(suite_array) { PT_REG(test_array_new); PT_REG(test_array_assign); PT_REG(test_array_concat); PT_REG(test_array_cmp); PT_REG(test_array_get); PT_REG(test_array_hash); PT_REG(test_array_iter); PT_REG(test_array_len); PT_REG(test_array_push); PT_REG(test_array_resize); PT_REG(test_array_show); PT_REG(test_array_sort); } /* Box */ PT_FUNC(test_box_new) { var s0 = new(String, $S("Hello World!")); var b1 = new(Box, s0); PT_ASSERT(b1); PT_ASSERT(deref(b1) is s0); del(b1); } PT_FUNC(test_box_assign) { var s0 = new(String, $S("Hello World!")); var b0 = assign($(Box, NULL), s0); PT_ASSERT(b0); PT_ASSERT(deref(b0) is s0); destruct(b0); } PT_FUNC(test_box_pointer) { var s0 = new(String, $S("Hello World 1!")); var s1 = new(String, $S("Hello World 2!")); var b0 = $(Box, s0); var b1 = $(Box, s1); PT_ASSERT(deref(b0) is s0); PT_ASSERT(deref(b1) is s1); del(s0); del(s1); } PT_FUNC(test_box_show) { var b0 = new(Box, new(Int, $I(1))); var s0 = new(String); show_to(b0, s0, 0); del(b0); del(s0); } PT_SUITE(suite_box) { PT_REG(test_box_new); PT_REG(test_box_assign); PT_REG(test_box_pointer); PT_REG(test_box_show); } /* File */ PT_FUNC(test_file_format) { var f0 = $(File, NULL); sopen(f0, $S("./tests/test.txt"), $S("w")); format_to(f0, 0, "%s", "Hello World!"); sclose(f0); } PT_FUNC(test_file_new) { var f0 = new(File, $S("./tests/test.bin"), $S("w")); var f1 = $(File, NULL); PT_ASSERT(f0); PT_ASSERT(f1); PT_ASSERT(f0 isnt f1); del(f0); } PT_FUNC(test_file_start) { struct File* file = new(File, $S("./tests/test.bin"), $S("w")); with (f0 in file) { PT_ASSERT(f0); }; PT_ASSERT(file->file is NULL); del(file); } PT_FUNC(test_file_stream) { static char testoutput1[] = "This is a test\n"; static char testoutput2[] = "This is a sample\n"; static char testinput[512]; var f0 = $(File, NULL); sopen(f0, $S("./tests/test.txt"), $S("w")); swrite(f0, testoutput1, sizeof(testoutput1)); sclose(f0); sopen(f0, $S("./tests/test.txt"), $S("r")); sread(f0, testinput, sizeof(testoutput1)); sclose(f0); PT_ASSERT_STR_EQ(testinput, testoutput1); } PT_SUITE(suite_file) { PT_REG(test_file_format); PT_REG(test_file_new); PT_REG(test_file_start); PT_REG(test_file_stream); } /* Float */ PT_FUNC(test_float_assign) { var float0 = $F(1.0); var float1 = new(Float, $F(24.313)); var float2 = copy(float0); PT_ASSERT(float0); PT_ASSERT(float1); PT_ASSERT(float2); PT_ASSERT(c_float(float0) is 1.0); PT_ASSERT(c_float(float1) is 24.313); PT_ASSERT(c_float(float2) is 1.0); assign(float2, float1); PT_ASSERT( not(float2 is float1) ); PT_ASSERT(c_float(float2) is 24.313); del(float1); del(float2); } PT_FUNC(test_float_c_float) { PT_ASSERT(c_float($F( 10.0)) is 10.0); PT_ASSERT(c_float($F( 531.1231)) is 531.1231); PT_ASSERT(c_float($F(21312.4535)) is 21312.4535); PT_ASSERT(c_float($F( 1352.21)) is 1352.21); PT_ASSERT(c_float($F( 676.342)) is 676.342); } PT_FUNC(test_float_cmp) { PT_ASSERT( gt($F(5.0), $F(0.0)) ); PT_ASSERT( gt($F(51.33), $F(2.32)) ); PT_ASSERT( lt($F(23.32), $F(99.92)) ); PT_ASSERT( lt($F(31.0), $F(32.3)) ); PT_ASSERT( eq($F(1.11), $F(1.11)) ); PT_ASSERT( eq($F(23.55), $F(23.55)) ); PT_ASSERT( ge($F(2.89), $F(2.89)) ); PT_ASSERT( ge($F(87.34), $F(2.89)) ); PT_ASSERT( le($F(16.6), $F(16.6)) ); PT_ASSERT( le($F(1.1), $F(88.8)) ); PT_ASSERT( neq($F(3.24), $F(6.85)) ); PT_ASSERT( neq($F(3.4), $F(5.4)) ); } union type_interp { double c_float; int64_t c_int; }; PT_FUNC(test_float_hash) { union type_interp r0 = { 34.0 }; union type_interp r1 = { 11.0 }; union type_interp r2 = { 0.6 }; union type_interp r3 = { 82.13 }; PT_ASSERT( hash($F(34.0)) is r0.c_int ); PT_ASSERT( hash($F(11.0)) is r1.c_int ); PT_ASSERT( hash($F(0.6)) is r2.c_int ); PT_ASSERT( hash($F(82.13)) is r3.c_int ); } PT_FUNC(test_float_show) { var s0 = new(String); show_to($F(213.12), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "213.120000"); show_to($F(3532.11), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "3532.110000"); show_to($F(1023.1), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "1023.100000"); del(s0); } PT_SUITE(suite_float) { PT_REG(test_float_assign); PT_REG(test_float_c_float); PT_REG(test_float_cmp); PT_REG(test_float_hash); PT_REG(test_float_show); } /* Filter */ var greater_than_two(var x) { return c_int(x) > 2 ? x : NULL; } var mem_hello(var x) { return mem(x, $S("Hello")) ? x : NULL; } PT_FUNC(test_filter_get) { var x = new(Array, Int, $I(0), $I(5), $I(2), $I(9)); var f = filter(x, $(Function, greater_than_two)); PT_ASSERT(mem(f, $I(5))); PT_ASSERT(mem(f, $I(9))); PT_ASSERT(not mem(f, $I(0))); PT_ASSERT(not mem(f, $I(1))); del(x); } PT_FUNC(test_filter_iter) { var x = new(Tuple, $S("Hello World"), $S("Hello Dan"), $S("Bonjour")); var y = new(Tuple); foreach (item in filter(x, $(Function, mem_hello))) { push(y, item); } PT_ASSERT(eq(get(y, $I(0)), $S("Hello World"))); PT_ASSERT(eq(get(y, $I(1)), $S("Hello Dan"))); del(x); del(y); } PT_FUNC(test_filter_new) { var x = new(Array, Int, $I(0), $I(5), $I(2), $I(9)); var f = new(Filter, x, $(Function, greater_than_two)); PT_ASSERT(mem(f, $I(5))); PT_ASSERT(mem(f, $I(9))); PT_ASSERT(not mem(f, $I(0))); PT_ASSERT(not mem(f, $I(1))); del(x); del(f); } PT_SUITE(suite_filter) { PT_REG(test_filter_get); PT_REG(test_filter_iter); PT_REG(test_filter_new); } /* Function */ var return_fst(var args) { return get(args, $I(0)); } var return_snd(var args) { return get(args, $I(1)); } PT_FUNC(test_function_call) { PT_ASSERT(eq(call($(Function, return_fst), $I(100), $I(200)), $I(100))); PT_ASSERT(eq(call($(Function, return_snd), $I(100), $I(200)), $I(200))); } PT_SUITE(suite_function) { PT_REG(test_function_call); } /* Int */ PT_FUNC(test_int_assign) { var int0 = $I(1); var int1 = new(Int, $I(24313)); var int2 = copy(int0); PT_ASSERT(int0); PT_ASSERT(int1); PT_ASSERT(int2); PT_ASSERT(c_int(int0) is 1); PT_ASSERT(c_int(int1) is 24313); PT_ASSERT(c_int(int2) is 1); assign(int2, int1); PT_ASSERT( not(int2 is int1) ); PT_ASSERT(c_int(int2) is 24313); del(int1); del(int2); } PT_FUNC(test_int_c_int) { PT_ASSERT( c_int($I(34)) is 34 ); PT_ASSERT( c_int($I(11)) is 11 ); PT_ASSERT( c_int($I(06)) is 06 ); PT_ASSERT( c_int($I(8213)) is 8213 ); } PT_FUNC(test_int_cmp) { PT_ASSERT( gt($I(5), $I(0)) ); PT_ASSERT( gt($I(5133), $I(232)) ); PT_ASSERT( lt($I(2332), $I(9992)) ); PT_ASSERT( lt($I(34), $I(323)) ); PT_ASSERT( eq($I(111), $I(111)) ); PT_ASSERT( eq($I(23), $I(23)) ); PT_ASSERT( ge($I(289), $I(289)) ); PT_ASSERT( ge($I(8734), $I(289)) ); PT_ASSERT( le($I(166), $I(166)) ); PT_ASSERT( le($I(11), $I(888)) ); PT_ASSERT( neq($I(324), $I(685)) ); PT_ASSERT( neq($I(34), $I(54)) ); } PT_FUNC(test_int_hash) { PT_ASSERT( hash($I(34)) is 34 ); PT_ASSERT( hash($I(11)) is 11 ); PT_ASSERT( hash($I(06)) is 06 ); PT_ASSERT( hash($I(8213)) is 8213 ); } PT_FUNC(test_int_show) { var s0 = new(String); show_to($I(34), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "34"); show_to($I(06), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "6"); show_to($I(8213), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "8213"); del(s0); } PT_SUITE(suite_int) { PT_REG(test_int_assign); PT_REG(test_int_c_int); PT_REG(test_int_cmp); PT_REG(test_int_hash); PT_REG(test_int_show); } /* List */ PT_FUNC(test_list_new) { var l0 = new(List, Int, $I(1), $I(5), $I(10)); var l1 = new(List, Float, $F(1.1), $F(2.2)); var l2 = copy(l0); PT_ASSERT(l0); PT_ASSERT(l1); PT_ASSERT(l2); PT_ASSERT(l0 isnt l1); PT_ASSERT(l0 isnt l2); PT_ASSERT(l1 isnt l2); PT_ASSERT( eq(get(l0, $I(0)), $I(1)) ); PT_ASSERT( eq(get(l1, $I(0)), $F(1.1)) ); PT_ASSERT( eq(get(l2, $I(0)), $I(1)) ); del(l0); del(l1); del(l2); } PT_FUNC(test_list_assign) { var l0 = new(List, Int, $I(1), $I(2), $I(3)); var a0 = new(Array, Int, $I(4), $I(5), $I(6), $I(7)); PT_ASSERT(eq(get(l0, $I(0)), $I(1))); PT_ASSERT(eq(get(l0, $I(1)), $I(2))); PT_ASSERT(eq(get(l0, $I(2)), $I(3))); assign(l0, a0); PT_ASSERT(eq(get(l0, $I(0)), $I(4))); PT_ASSERT(eq(get(l0, $I(1)), $I(5))); PT_ASSERT(eq(get(l0, $I(2)), $I(6))); PT_ASSERT(eq(get(l0, $I(3)), $I(7))); assign(l0, range($I(5))); PT_ASSERT(eq(get(l0, $I(0)), $I(0))); PT_ASSERT(eq(get(l0, $I(1)), $I(1))); PT_ASSERT(eq(get(l0, $I(2)), $I(2))); PT_ASSERT(eq(get(l0, $I(3)), $I(3))); PT_ASSERT(eq(get(l0, $I(4)), $I(4))); del(l0); del(a0); } PT_FUNC(test_list_concat) { var l0 = new(List, Int, $I(1), $I(2), $I(3)); var l1 = new(List, Int, $I(4), $I(5), $I(6)); concat(l0, l1); PT_ASSERT(eq(get(l0, $I(0)), $I(1))); PT_ASSERT(eq(get(l0, $I(1)), $I(2))); PT_ASSERT(eq(get(l0, $I(2)), $I(3))); PT_ASSERT(eq(get(l0, $I(3)), $I(4))); PT_ASSERT(eq(get(l0, $I(4)), $I(5))); PT_ASSERT(eq(get(l0, $I(5)), $I(6))); append(l0, $I(10)); PT_ASSERT(eq(get(l0, $I(6)), $I(10))); del(l0); del(l1); } PT_FUNC(test_list_cmp) { var a0 = new(Array, Int, $I(1), $I(5), $I(10)); var a1 = new(Array, Int, $I(1), $I(5), $I(10)); var a2 = new(Array, Int, $I(2), $I(5), $I(10)); PT_ASSERT(eq(a0, a1)); PT_ASSERT(neq(a0, a2)); PT_ASSERT(gt(a2, a1)); PT_ASSERT(lt(a1, a2)); del(a0); del(a1); del(a2); } PT_FUNC(test_list_get) { var l0 = new(List, Int, $I(1), $I(5), $I(10)); PT_ASSERT(len(l0) is 3); PT_ASSERT(mem(l0, $I(1))); PT_ASSERT(mem(l0, $I(5))); rem(l0, $I(5)); PT_ASSERT(len(l0) is 2); PT_ASSERT(mem(l0, $I(1))); PT_ASSERT(not mem(l0, $I(5))); resize(l0, 0); PT_ASSERT(len(l0) is 0); PT_ASSERT(not mem(l0, $I(1))); del(l0); var l1 = new(List, String, $S("Hello"), $S("There"), $S("People")); PT_ASSERT( eq(get(l1, $I(0)), $S("Hello")) ); PT_ASSERT( eq(get(l1, $I(1)), $S("There")) ); PT_ASSERT( eq(get(l1, $I(2)), $S("People")) ); PT_ASSERT( eq(get(l1, $I(-1)), $S("People")) ); PT_ASSERT( eq(get(l1, $I(-2)), $S("There")) ); PT_ASSERT( eq(get(l1, $I(-3)), $S("Hello")) ); set(l1, $I(1), $S("Blah")); PT_ASSERT( eq(get(l1, $I(0)), $S("Hello")) ); PT_ASSERT( eq(get(l1, $I(1)), $S("Blah")) ); PT_ASSERT( eq(get(l1, $I(2)), $S("People")) ); set(l1, $I(0), $S("Foo")); set(l1, $I(2), $S("Bar")); PT_ASSERT( eq(get(l1, $I(0)), $S("Foo")) ); PT_ASSERT( eq(get(l1, $I(1)), $S("Blah")) ); PT_ASSERT( eq(get(l1, $I(2)), $S("Bar")) ); del(l1); } PT_FUNC(test_list_hash) { var l0 = new(List, String, $S("Hello"), $S("World")); PT_ASSERT(hash(l0) is (hash($S("Hello")) ^ hash($S("World")))); del(l0); } PT_FUNC(test_list_iter) { var l0 = new(List, Int); var l1 = new(List, Float); PT_ASSERT(iter_type(l0) is Int); PT_ASSERT(iter_type(l1) is Float); del(l0); del(l1); l0 = new(List, String, $S("Hello"), $S("There"), $S("People")); int counter = 0; foreach(item in l0) { switch(counter) { case 0: PT_ASSERT( eq(item, $S("Hello")) ); break; case 1: PT_ASSERT( eq(item, $S("There")) ); break; case 2: PT_ASSERT( eq(item, $S("People")) ); break; } counter++; } PT_ASSERT(counter is 3); counter = 0; foreach(item0 in l0) { foreach(item1 in l0) { counter++; } } PT_ASSERT(counter is 9); del(l0); } PT_FUNC(test_list_len) { var l0 = new(List, String, $S("Hello"), $S("World")); PT_ASSERT(len(l0) is 2); append(l0, $S("!")); PT_ASSERT(len(l0) is 3); resize(l0, 0); PT_ASSERT(len(l0) is 0); del(l0); } PT_FUNC(test_list_push) { var l0 = new(List, Int); PT_ASSERT(len(l0) is 0); push(l0, $I(1)); PT_ASSERT(len(l0) is 1); PT_ASSERT( eq(get(l0, $I(0)), $I(1)) ); push(l0, $I(3)); PT_ASSERT(len(l0) is 2); PT_ASSERT( eq(get(l0, $I(0)), $I(1)) ); PT_ASSERT( eq(get(l0, $I(1)), $I(3)) ); push_at(l0, $I(10), $I(0)); PT_ASSERT(len(l0) is 3); PT_ASSERT( eq(get(l0, $I(0)), $I(10)) ); PT_ASSERT( eq(get(l0, $I(1)), $I(1)) ); PT_ASSERT( eq(get(l0, $I(2)), $I(3)) ); push_at(l0, $I(20), $I(1)); PT_ASSERT(len(l0) is 4); PT_ASSERT( eq(get(l0, $I(0)), $I(10)) ); PT_ASSERT( eq(get(l0, $I(1)), $I(20)) ); PT_ASSERT( eq(get(l0, $I(2)), $I(1)) ); PT_ASSERT( eq(get(l0, $I(3)), $I(3)) ); pop_at(l0, $I(2)); PT_ASSERT(len(l0) is 3); PT_ASSERT( eq(get(l0, $I(0)), $I(10)) ); PT_ASSERT( eq(get(l0, $I(1)), $I(20)) ); PT_ASSERT( eq(get(l0, $I(2)), $I(3)) ); pop_at(l0, $I(0)); PT_ASSERT(len(l0) is 2); PT_ASSERT( eq(get(l0, $I(0)), $I(20)) ); PT_ASSERT( eq(get(l0, $I(1)), $I(3)) ); pop(l0); PT_ASSERT(len(l0) is 1); PT_ASSERT( eq(get(l0, $I(0)), $I(20)) ); pop(l0); PT_ASSERT(len(l0) is 0); del(l0); } PT_FUNC(test_list_resize) { var l0 = new(List, Int); resize(l0, 1000); del(l0); l0 = new(List, Int, $I(1), $I(2), $I(3)); PT_ASSERT(len(l0) is 3); resize(l0, 0); PT_ASSERT(len(l0) is 0); PT_ASSERT(empty(l0)); del(l0); } PT_FUNC(test_list_show) { var l0 = new(List, Int, $I(1), $I(5), $I(9)); var s0 = new(String); show_to(l0, s0, 0); del(l0); del(s0); } PT_SUITE(suite_list) { PT_REG(test_list_new); PT_REG(test_list_assign); PT_REG(test_list_concat); PT_REG(test_list_cmp); PT_REG(test_list_get); PT_REG(test_list_hash); PT_REG(test_list_iter); PT_REG(test_list_len); PT_REG(test_list_push); PT_REG(test_list_resize); PT_REG(test_list_show); } /* Map */ var convert_to_int(var x) { var y = new(Int); look_from(y, x, 0); return y; } var assert_value(var x) { PT_ASSERT(x); return NULL; } PT_FUNC(test_map_call) { var x = tuple($I(0), $S("Hello!"), $F(2.4)); call(map(x, $(Function, assert_value))); } PT_FUNC(test_map_get) { var x = tuple($S("1"), $S("2"), $S("3")); var m = map(x, $(Function, convert_to_int)); var i0 = get(m, $I(0)); var i1 = get(m, $I(1)); var i2 = get(m, $I(2)); PT_ASSERT(eq(i0, $I(1))); PT_ASSERT(eq(i1, $I(2))); PT_ASSERT(eq(i2, $I(3))); del(i0); del(i1); del(i2); } PT_FUNC(test_map_iter) { var x = tuple($I(0), $S("Hello!"), $F(2.4)); foreach (_ in map(x, $(Function, assert_value))); } PT_FUNC(test_map_len) { var x = tuple($S("1"), $S("2"), $S("3")); var y = tuple($S("1"), $S("2"), $S("3"), $S("4")); var m = map(x, $(Function, convert_to_int)); var n = map(y, $(Function, convert_to_int)); PT_ASSERT(len(m) is len(x)); PT_ASSERT(len(n) is len(y)); } PT_FUNC(test_map_new) { var x = tuple($S("1"), $S("2"), $S("3")); var m = new(Map, x, $(Function, convert_to_int)); var i0 = get(m, $I(0)); var i1 = get(m, $I(1)); var i2 = get(m, $I(2)); PT_ASSERT(eq(i0, $I(1))); PT_ASSERT(eq(i1, $I(2))); PT_ASSERT(eq(i2, $I(3))); del(i0); del(i1); del(i2); del(m); } PT_SUITE(suite_map) { PT_REG(test_map_call); PT_REG(test_map_get); PT_REG(test_map_iter); PT_REG(test_map_len); PT_REG(test_map_new); } /* Mutex */ static var increment(var args) { var mut = get(args, $I(0)); var tot = get(args, $I(1)); lock(mut); assign(tot, $I(c_int(tot)+1)); unlock(mut); return NULL; } static var increment2(var args) { var mut = get(args, $I(0)); var tot = get(args, $I(1)); with (m in mut) { assign(tot, $I(c_int(tot)+1)); } return NULL; } PT_FUNC(test_mutex_lock) { var mutex = new(Mutex); var total = $I(0); var threads = new(Array, Box, new(Thread, $(Function, increment)), new(Thread, $(Function, increment)), new(Thread, $(Function, increment)), new(Thread, $(Function, increment)), new(Thread, $(Function, increment))); PT_ASSERT(eq(total, $I(0))); foreach (t in threads) { call(deref(t), mutex, total); } foreach (t in threads) { join(deref(t)); } PT_ASSERT(eq(total, $I(5))); del(threads); del(mutex); } PT_FUNC(test_mutex_new) { var mutex = new(Mutex); start(mutex); del(mutex); } PT_FUNC(test_mutex_start) { var mutex = new(Mutex); var total = $I(0); var threads = new(Array, Box, new(Thread, $(Function, increment2)), new(Thread, $(Function, increment2)), new(Thread, $(Function, increment2)), new(Thread, $(Function, increment2)), new(Thread, $(Function, increment2))); PT_ASSERT(eq(total, $I(0))); foreach (t in threads) { call(deref(t), mutex, total); } foreach (t in threads) { join(deref(t)); } PT_ASSERT(eq(total, $I(5))); del(threads); del(mutex); } PT_SUITE(suite_mutex) { PT_REG(test_mutex_lock); PT_REG(test_mutex_new); PT_REG(test_mutex_start); } /* Range */ PT_FUNC(test_range_assign) { var x = new(Range, $I(1), $I(5), $I(2)); var y = assign(new(Range), x); PT_ASSERT(eq(x, y)); PT_ASSERT(c_int(get(y, $I(0))) is 1); PT_ASSERT(c_int(get(y, $I(1))) is 3); PT_ASSERT(len(y) is 2); del(x); del(y); } PT_FUNC(test_range_cmp) { var x = range($I(100)); var y = range($I(0), $I(100)); var z = range($I(0), $I(100), $I(1)); PT_ASSERT(eq(x, y)); PT_ASSERT(eq(y, z)); PT_ASSERT(eq(x, z)); var a = new(Range, $I(100)); PT_ASSERT(eq(x, a)); PT_ASSERT(eq(y, a)); PT_ASSERT(eq(z, a)); del(a); } PT_FUNC(test_range_get) { var x = range($I(1000)); var y = range($I(1), $I(20)); var z = range($I(3), $I(21), $I(2)); PT_ASSERT(c_int(get(x, $I(100))) is 100); PT_ASSERT(c_int(get(x, $I(10))) is 10); PT_ASSERT(c_int(get(x, $I(5))) is 5); PT_ASSERT(c_int(get(x, $I(-1))) is 999); PT_ASSERT(c_int(get(x, $I(-2))) is 998); PT_ASSERT(c_int(get(x, $I(-3))) is 997); PT_ASSERT(c_int(get(y, $I(15))) is 16); PT_ASSERT(c_int(get(y, $I(10))) is 11); PT_ASSERT(c_int(get(y, $I(5))) is 6); PT_ASSERT(c_int(get(z, $I(0))) is 3); PT_ASSERT(c_int(get(z, $I(1))) is 5); PT_ASSERT(c_int(get(z, $I(3))) is 9); } PT_FUNC(test_range_iter) { PT_ASSERT(iter_type(range($I(10))) is Int); size_t j; var x = range($I(1000)); var y = range($I(1), $I(20)); var z = range($I(3), $I(21), $I(2)); j = 0; foreach (i in x) { PT_ASSERT(c_int(i) is j); j++; } j = 1; foreach (i in y) { PT_ASSERT(c_int(i) is j); j++; } j = 3; foreach (i in z) { PT_ASSERT(c_int(i) is j); j+=2; } } PT_FUNC(test_range_len) { var x = range($I(1000)); var y = range($I(1), $I(20)); var z = range($I(3), $I(21), $I(2)); PT_ASSERT(len(x) is 1000); PT_ASSERT(len(y) is 19); PT_ASSERT(len(z) is 9); } PT_FUNC(test_range_new) { var x = new(Range, $I(1), $I(5), $I(2)); del(x); } PT_FUNC(test_range_show) { var r0 = new(Range, $I(1), $I(5), $I(2)); var s0 = new(String); show_to(r0, s0, 0); del(r0); del(s0); } PT_SUITE(suite_range) { PT_REG(test_range_assign); PT_REG(test_range_cmp); PT_REG(test_range_get); PT_REG(test_range_iter); PT_REG(test_range_len); PT_REG(test_range_new); PT_REG(test_range_show); } /* Ref */ PT_FUNC(test_ref_assign) { var s0 = new(String, $S("Hello World!")); var r0 = assign($(Ref, NULL), s0); PT_ASSERT(r0); PT_ASSERT(deref(r0) is s0); del(s0); } PT_FUNC(test_ref_pointer) { var s0 = new(String, $S("Hello World 1!")); var s1 = NULL; var r0 = $(Ref, s0); var r1 = $(Ref, s1); PT_ASSERT(deref(r0) is s0); PT_ASSERT(deref(r1) is NULL); del(s0); } PT_SUITE(suite_ref) { PT_REG(test_ref_assign); PT_REG(test_ref_pointer); } /* Slice */ PT_FUNC(test_slice_assign) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var s0 = new(Slice, x, _, _, _); var s1 = assign(new(Slice, x), s0); PT_ASSERT(eq(s0, s1)); PT_ASSERT(c_int(get(s1, $I(0))) is 100); PT_ASSERT(c_int(get(s1, $I(1))) is 10); PT_ASSERT(c_int(get(s1, $I(2))) is 30); del(s0); del(s1); } PT_FUNC(test_slice_cmp) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var s0 = slice(x, $I(4)); var s1 = slice(x, $I(0), $I(4)); var s2 = slice(x, $I(0), $I(4), $I(1)); var s3 = slice(x, _, $I(4)); PT_ASSERT(eq(s0, s1)); PT_ASSERT(eq(s0, s2)); PT_ASSERT(eq(s0, s3)); } PT_FUNC(test_slice_get) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var s0 = slice(x, $I(4)); var s1 = slice(x, $I(1), $I(4)); var s2 = slice(x, $I(0), $I(4), $I(2)); var s3 = slice(x, _, _, $I(2)); PT_ASSERT(c_int(get(s0, $I(0))) is 100); PT_ASSERT(c_int(get(s0, $I(1))) is 10); PT_ASSERT(c_int(get(s1, $I(0))) is 10); PT_ASSERT(c_int(get(s1, $I(1))) is 30); PT_ASSERT(c_int(get(s2, $I(0))) is 100); PT_ASSERT(c_int(get(s2, $I(1))) is 30); PT_ASSERT(c_int(get(s3, $I(0))) is 100); PT_ASSERT(c_int(get(s3, $I(1))) is 30); var s4 = slice(x, $I(-2)); var s5 = slice(x, $I(-3), $I(-2)); var s6 = slice(x, _, _, $I(-1)); var s7 = slice(x, $I(-4), _, $I(1)); PT_ASSERT(c_int(get(s4, $I(-1))) is 50); PT_ASSERT(c_int(get(s4, $I(-2))) is 30); PT_ASSERT(c_int(get(s5, $I(0))) is 50); PT_ASSERT(c_int(get(s6, $I(0))) is 1); PT_ASSERT(c_int(get(s6, $I(1))) is 6); PT_ASSERT(c_int(get(s6, $I(2))) is 50); PT_ASSERT(c_int(get(s6, $I(3))) is 30); PT_ASSERT(c_int(get(s6, $I(4))) is 10); PT_ASSERT(c_int(get(s6, $I(5))) is 100); PT_ASSERT(c_int(get(s7, $I(0))) is 30); PT_ASSERT(c_int(get(s7, $I(1))) is 50); PT_ASSERT(c_int(get(s7, $I(2))) is 6); PT_ASSERT(c_int(get(s0, $I(-1))) is 50); PT_ASSERT(c_int(get(s1, $I(-1))) is 50); PT_ASSERT(c_int(get(s6, $I(-1))) is 100); } PT_FUNC(test_slice_iter) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var s0 = slice(x, $I(4)); var s1 = slice(x, $I(1), $I(4)); var s2 = slice(x, $I(0), $I(4), $I(2)); var s3 = slice(x, _, _, $I(2)); /* TODO: Improve */ foreach (i in s0) {} foreach (i in s1) {} foreach (i in s2) {} foreach (i in s3) {} x = new(Array, Int); var y = slice(x); PT_ASSERT(iter_type(y) is iter_type(x)); del(x); } PT_FUNC(test_slice_len) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var s0 = slice(x, $I(4)); var s1 = slice(x, $I(1), $I(4)); var s2 = slice(x, $I(0), $I(4), $I(2)); var s3 = slice(x, _, _, $I(2)); PT_ASSERT(len(s0) is 4); PT_ASSERT(len(s1) is 3); PT_ASSERT(len(s2) is 2); PT_ASSERT(len(s3) is 3); } PT_FUNC(test_slice_new) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var s0 = new(Slice, x, _, _, _); var s1 = new(Slice, x, $I(1), _); del(s0); del(s1); } PT_SUITE(suite_slice) { PT_REG(test_slice_assign); PT_REG(test_slice_cmp); PT_REG(test_slice_get); PT_REG(test_slice_iter); PT_REG(test_slice_len); PT_REG(test_slice_new); } /* String */ PT_FUNC(test_string_assign) { var s0 = new(String, $S("Hello")); PT_ASSERT_STR_EQ(c_str(s0), "Hello"); assign(s0, $S("There")); PT_ASSERT_STR_EQ(c_str(s0), "There"); del(s0); } PT_FUNC(test_string_c_str) { PT_ASSERT_STR_EQ(c_str($S("Ball")), "Ball"); PT_ASSERT_STR_EQ(c_str($S("dog")), "dog"); PT_ASSERT_STR_EQ(c_str($S("great")), "great"); PT_ASSERT_STR_EQ(c_str($S("Yellow")), "Yellow"); PT_ASSERT_STR_EQ(c_str($S("Hello")), "Hello"); } PT_FUNC(test_string_cmp) { PT_ASSERT( gt($S("Ball"), $S("Apple")) ); PT_ASSERT( gt($S("dog"), $S("cat")) ); PT_ASSERT( lt($S("great"), $S("hello")) ); PT_ASSERT( lt($S("Yellow"), $S("Zoo")) ); PT_ASSERT( eq($S("Hello"), $S("Hello")) ); PT_ASSERT( eq($S("there"), $S("there")) ); PT_ASSERT( ge($S("Hello"), $S("Hello")) ); PT_ASSERT( ge($S("tooting"), $S("red")) ); PT_ASSERT( le($S("guard"), $S("guardian")) ); PT_ASSERT( le($S("keep"), $S("keep")) ); PT_ASSERT(neq($S("Hello"), $S("hello")) ); PT_ASSERT(neq($S("group"), $S("GROUP")) ); } PT_FUNC(test_string_concat) { var s0 = new(String, $S("Hello")); PT_ASSERT_STR_EQ(c_str(s0), "Hello"); concat(s0, $S(" ")); PT_ASSERT_STR_EQ(c_str(s0), "Hello "); concat(s0, $S("World")); PT_ASSERT_STR_EQ(c_str(s0), "Hello World"); append(s0, $S("!")); PT_ASSERT_STR_EQ(c_str(s0), "Hello World!"); del(s0); } PT_FUNC(test_string_format) { var s0 = new(String); resize(s0, 1000); format_to(s0, 0, "%s", "Hello"); PT_ASSERT_STR_EQ(c_str(s0), "Hello"); format_to(s0, 0, ""); PT_ASSERT_STR_EQ(c_str(s0), ""); print_to(s0, 0, ""); PT_ASSERT_STR_EQ(c_str(s0), ""); del(s0); } PT_FUNC(test_string_get) { var s0 = new(String, $S("Balloons")); PT_ASSERT(len(s0) is 8); PT_ASSERT(mem(s0, $S("Ball"))); PT_ASSERT(mem(s0, $S("oon"))); PT_ASSERT(mem(s0, $S("Balloons"))); rem(s0, $S("oons")); PT_ASSERT_STR_EQ(c_str(s0), "Ball"); resize(s0, 0); PT_ASSERT(len(s0) is 0); PT_ASSERT_STR_EQ(c_str(s0), ""); del(s0); } PT_FUNC(test_string_hash) { uint64_t v0 = 4771441285123272284ULL; uint64_t v1 = 17415363727859751682ULL; uint64_t v2 = 11867268813077774525ULL; PT_ASSERT(hash($S("Hello")) is v0); PT_ASSERT(hash($S("There")) is v1); PT_ASSERT(hash($S("People")) is v2); } PT_FUNC(test_string_len) { PT_ASSERT(len($S("tooting")) is 7); PT_ASSERT(len($S("guard")) is 5); PT_ASSERT(len($S("keep")) is 4); PT_ASSERT(len($S("Hello")) is 5); PT_ASSERT(len($S("group")) is 5); } PT_FUNC(test_string_new) { var s0 = $S("Hello"); var s1 = new(String, $S("There")); var s2 = copy(s0); PT_ASSERT(s0); PT_ASSERT(s1); PT_ASSERT(s2); PT_ASSERT_STR_EQ(c_str(s0), "Hello"); PT_ASSERT_STR_EQ(c_str(s1), "There"); PT_ASSERT_STR_EQ(c_str(s2), "Hello"); assign(s2, s1); PT_ASSERT( not(s1 is s2) ); PT_ASSERT_STR_EQ( c_str(s2), "There" ); del(s1); del(s2); } PT_FUNC(test_string_resize) { var s0 = new(String, $S("Hello")); resize(s0, 1000); del(s0); s0 = new(String, $S("Hello")); PT_ASSERT_STR_EQ(c_str(s0), "Hello"); resize(s0, 0); PT_ASSERT_STR_EQ(c_str(s0), ""); PT_ASSERT(len(s0) is 0); PT_ASSERT(empty(s0)); del(s0); } PT_FUNC(test_string_show) { var s0 = new(String); show_to($S("Hello"), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "\"Hello\""); show_to($S("Hello\n"), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "\"Hello\\n\""); show_to($S("Hel\"lo\n"), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "\"Hel\\\"lo\\n\""); show_to($S("Hel\"'lo\n"), s0, 0); PT_ASSERT_STR_EQ(c_str(s0), "\"Hel\\\"\\'lo\\n\""); del(s0); } PT_SUITE(suite_string) { PT_REG(test_string_assign); PT_REG(test_string_c_str); PT_REG(test_string_cmp); PT_REG(test_string_concat); PT_REG(test_string_format); PT_REG(test_string_get); PT_REG(test_string_hash); PT_REG(test_string_len); PT_REG(test_string_new); PT_REG(test_string_resize); PT_REG(test_string_show); } /* Table */ PT_FUNC(test_table_assign) { var t0 = new(Table, String, Int); var t1 = new(Table, String, Int, $S("asfa"), $I(0), $S("gasg"), $I(2), $S("asda"), $I(1), $S("dqga"), $I(0), $S("jaja"), $I(7), $S("sdfa"), $I(2), $S("jads"), $I(7), $S("kyad"), $I(2), $S("kyas"), $I(7), $S("hwdw"), $I(2), $S("awaa"), $I(7), $S("lkil"), $I(2)); PT_ASSERT(len(t0) is 0); assign(t0, t1); PT_ASSERT(len(t0) is 12); PT_ASSERT(eq(get(t0, $S("asfa")), $I(0))); PT_ASSERT(eq(get(t0, $S("kyad")), $I(2))); PT_ASSERT(eq(get(t0, $S("lkil")), $I(2))); PT_ASSERT(eq(get(t0, $S("jads")), $I(7))); del(t0); del(t1); } PT_FUNC(test_table_cmp) { var t0 = new(Table, String, Int, $S("Hello"), $I(2), $S("There"), $I(5)); var t1 = new(Table, String, Int, $S("Hello"), $I(2), $S("There"), $I(5)); var t2 = new(Table, String, Int, $S("Bello"), $I(2), $S("There"), $I(5)); var t3 = new(Table, String, Int, $S("Hello"), $I(2), $S("There"), $I(5), $S("World"), $I(6)); PT_ASSERT(eq(t0, t1)); PT_ASSERT(neq(t0, t2)); PT_ASSERT(lt(t2, t0)); PT_ASSERT(gt(t0, t2)); PT_ASSERT(gt(t3, t1)); PT_ASSERT(gt(t3, t0)); del(t0); del(t1); del(t2); del(t3); } PT_FUNC(test_table_get) { var t0 = new(Table, String, Int); set(t0, $S("Hello"), $I(2)); set(t0, $S("There"), $I(5)); PT_ASSERT(len(t0) is 2); PT_ASSERT(mem(t0, $S("Hello"))); rem(t0, $S("Hello")); PT_ASSERT(len(t0) is 1); PT_ASSERT(not mem(t0, $S("Hello"))); PT_ASSERT(mem(t0, $S("There"))); resize(t0, 0); PT_ASSERT(len(t0) is 0); PT_ASSERT(not mem(t0, $S("Hello"))); PT_ASSERT(not mem(t0, $S("There"))); del(t0); var t1 = new(Table, String, Int); set(t1, $S("Hello"), $I(2)); set(t1, $S("There"), $I(5)); var i0 = get(t1, $S("Hello")); var i1 = get(t1, $S("There")); PT_ASSERT( eq(i0, $I(2)) ); PT_ASSERT( eq(i1, $I(5)) ); set(t1, $S("Hello"), $I(6)); var i2 = get(t1, $S("Hello")); PT_ASSERT( eq(i2, $I(6)) ); del(t1); var t3 = new(Table, String, Int, $S("asfa"), $I(0), $S("gasg"), $I(2), $S("asda"), $I(1), $S("dqga"), $I(0), $S("jaja"), $I(7), $S("sdfa"), $I(2), $S("jads"), $I(7), $S("kyad"), $I(2), $S("kyas"), $I(7), $S("hwdw"), $I(2), $S("oaqv"), $I(7), $S("fgds"), $I(2), $S("awaa"), $I(7), $S("lkil"), $I(2)); rem(t3, $S("gasg")); rem(t3, $S("oaqv")); rem(t3, $S("fgds")); rem(t3, $S("awaa")); rem(t3, $S("kyad")); rem(t3, $S("dqga")); rem(t3, $S("jaja")); del(t3); } PT_FUNC(test_table_hash) { var t0 = new(Table, String, Int); set(t0, $S("Hello"), $I(2)); set(t0, $S("There"), $I(5)); PT_ASSERT(hash(t0) is ( hash($S("Hello")) ^ hash($I(2)) ^ hash($S("There")) ^ hash($I(5)))); del(t0); } PT_FUNC(test_table_iter) { var t0 = new(Table, String, Int); set(t0, $S("Hello"), $I(2)); set(t0, $S("There"), $I(5)); foreach(key in t0) { var val = get(t0, key); PT_ASSERT( (eq(key, $S("Hello")) and eq(val, $I(2))) or (eq(key, $S("There")) and eq(val, $I(5)))); } del(t0); } PT_FUNC(test_table_len) { var t0 = new(Table, String, Int); set(t0, $S("Hello"), $I(2)); set(t0, $S("There"), $I(5)); PT_ASSERT(len(t0) is 2); del(t0); } PT_FUNC(test_table_new) { var t0 = new(Table, String, Int); set(t0, $S("Hello"), $I(2)); set(t0, $S("There"), $I(5)); var t1 = new(Table, String, Int); set(t1, $S("Bonjour"), $I(9)); set(t1, $S("Where"), $I(5)); var t2 = copy(t0); PT_ASSERT(t0); PT_ASSERT(t1); PT_ASSERT(t2); PT_ASSERT(t0 isnt t2); PT_ASSERT(t0 isnt t1); PT_ASSERT(mem(t0, $S("Hello"))); PT_ASSERT(mem(t1, $S("Bonjour"))); PT_ASSERT(mem(t2, $S("There"))); assign(t2, t1); PT_ASSERT(mem(t2, $S("Where"))); PT_ASSERT(mem(t2, $S("Bonjour"))); PT_ASSERT(not mem(t2, $S("Hello"))); PT_ASSERT(not mem(t2, $S("There"))); del(t0); del(t1); del(t2); var t3 = new(Table, String, Int, $S("asfa"), $I(0), $S("gasg"), $I(2), $S("asda"), $I(1), $S("dqga"), $I(0), $S("jaja"), $I(7), $S("sdfa"), $I(2), $S("jads"), $I(7), $S("kyad"), $I(2), $S("kyas"), $I(7), $S("hwdw"), $I(2), $S("awaa"), $I(7), $S("lkil"), $I(2)); del(t3); t0 = new(Table, Int, Float); t1 = new(Table, String, Int); PT_ASSERT(key_type(t0) is Int); PT_ASSERT(val_type(t0) is Float); PT_ASSERT(key_type(t1) is String); PT_ASSERT(val_type(t1) is Int); del(t0); del(t1); } PT_FUNC(test_table_resize) { var t0 = new(Table, String, Int); resize(t0, 1000); del(t0); t0 = new(Table, String, Int); set(t0, $S("Hello"), $I(2)); set(t0, $S("There"), $I(5)); PT_ASSERT(len(t0) is 2); resize(t0, 0); PT_ASSERT(len(t0) is 0); PT_ASSERT(empty(t0)); del(t0); } PT_FUNC(test_table_show) { var t0 = new(Table, String, Int); set(t0, $S("Bonjour"), $I(9)); set(t0, $S("Where"), $I(5)); var s0 = new(String); show_to(t0, s0, 0); del(t0); del(s0); } PT_FUNC(test_table_rehash) { var t0 = new(Table, Int, Int); var value = $I(23); var test_key = NULL; size_t max = 1000; int64_t r = rand() % max; for (size_t i = 0; i < max; i++) { var key = $I(i); if (i == r) { test_key = key; } set(t0, key, value); } PT_ASSERT(test_key isnt NULL); PT_ASSERT(eq(get(t0, test_key), value)); PT_ASSERT(len(t0) is max); del(t0); } PT_SUITE(suite_table) { PT_REG(test_table_assign); PT_REG(test_table_cmp); PT_REG(test_table_get); PT_REG(test_table_hash); PT_REG(test_table_iter); PT_REG(test_table_len); PT_REG(test_table_new); PT_REG(test_table_resize); PT_REG(test_table_show); PT_REG(test_table_rehash); } /* Thread */ PT_FUNC(test_thread_c_int) { var x = new(Thread); int64_t cid = c_int(current(Thread)); del(x); } static var set_value(var args) { assign(get(args, $I(0)), $I(1)); return NULL; } PT_FUNC(test_thread_call) { var i = $I(0); var x = new(Thread, $(Function, set_value)); call(x, i); join(x); PT_ASSERT(c_int(i) is 1); del(x); } PT_FUNC(test_thread_cmp) { PT_ASSERT(eq(current(Thread), current(Thread))); } PT_FUNC(test_thread_current) { var x = current(Thread); PT_ASSERT(x); } PT_FUNC(test_thread_get) { set(current(Thread), $S("Hello"), $I(1)); PT_ASSERT(mem(current(Thread), $S("Hello"))); PT_ASSERT(c_int(get(current(Thread), $S("Hello"))) is 1); rem(current(Thread), $S("Hello")); PT_ASSERT(not mem(current(Thread), $S("Hello"))); } PT_FUNC(test_thread_hash) { var x = new(Thread); int64_t h0 = hash(current(Thread)); int64_t h1 = hash(current(Thread)); PT_ASSERT(h0 is h1); del(x); } PT_FUNC(test_thread_new) { var x = new(Thread); del(x); } PT_FUNC(test_thread_start) { /* ** TODO: Separate out Enter/Exit to Start/Stop. ** For Threads With should do `join` on stop */ } #ifdef CELLO_UNIX #include #endif void cello_sleep(int ms) { #if defined(CELLO_UNIX) usleep(ms * 1000); #elif defined(CELLO_WINDOWS) Sleep(ms); #endif } static var exception_sleep(var args) { try { cello_sleep(20); PT_ASSERT(len(current(Exception)) is 1); } catch(e) { } return NULL; } PT_FUNC(test_thread_exception) { var t = new(Thread, $(Function, exception_sleep)); call(t); cello_sleep(10); PT_ASSERT(len(current(Exception)) is 0); join(t); del(t); } PT_SUITE(suite_thread) { PT_REG(test_thread_c_int); PT_REG(test_thread_call); PT_REG(test_thread_cmp); PT_REG(test_thread_current); PT_REG(test_thread_get); PT_REG(test_thread_hash); PT_REG(test_thread_new); PT_REG(test_thread_start); PT_REG(test_thread_exception); } /* Tree */ PT_FUNC(test_tree_assign) { var m0 = new(Tree, String, Int); var m1 = new(Tree, String, Int, $S("asfa"), $I(0), $S("gasg"), $I(2), $S("asda"), $I(1), $S("dqga"), $I(0), $S("jaja"), $I(7), $S("sdfa"), $I(2), $S("jads"), $I(7), $S("kyad"), $I(2), $S("kyas"), $I(7), $S("hwdw"), $I(2), $S("awaa"), $I(7), $S("lkil"), $I(2)); PT_ASSERT(len(m0) is 0); assign(m0, m1); PT_ASSERT(len(m0) is 12); PT_ASSERT(eq(get(m0, $S("asfa")), $I(0))); PT_ASSERT(eq(get(m0, $S("kyad")), $I(2))); PT_ASSERT(eq(get(m0, $S("lkil")), $I(2))); PT_ASSERT(eq(get(m0, $S("jads")), $I(7))); del(m0); del(m1); } PT_FUNC(test_tree_resize) { var m0 = new(Tree, String, Int); set(m0, $S("Hello"), $I(2)); set(m0, $S("There"), $I(5)); PT_ASSERT(len(m0) is 2); resize(m0, 0); PT_ASSERT(len(m0) is 0); PT_ASSERT(empty(m0)); del(m0); } PT_FUNC(test_tree_cmp) { var m0 = new(Tree, String, Int, $S("Hello"), $I(2), $S("There"), $I(5)); var m1 = new(Tree, String, Int, $S("Hello"), $I(2), $S("There"), $I(5)); var m2 = new(Tree, String, Int, $S("Bello"), $I(2), $S("There"), $I(5)); var m3 = new(Table, String, Int, $S("Hello"), $I(2), $S("There"), $I(5), $S("World"), $I(6)); PT_ASSERT(eq(m0, m1)); PT_ASSERT(neq(m0, m2)); PT_ASSERT(lt(m2, m0)); PT_ASSERT(gt(m0, m2)); PT_ASSERT(gt(m3, m1)); PT_ASSERT(gt(m3, m0)); del(m0); del(m1); del(m2); del(m3); } PT_FUNC(test_tree_get) { var m0 = new(Tree, String, Int); set(m0, $S("Hello"), $I(2)); set(m0, $S("There"), $I(5)); PT_ASSERT(len(m0) is 2); PT_ASSERT(mem(m0, $S("Hello"))); rem(m0, $S("Hello")); PT_ASSERT(len(m0) is 1); PT_ASSERT(not mem(m0, $S("Hello"))); PT_ASSERT(mem(m0, $S("There"))); resize(m0, 0); PT_ASSERT(len(m0) is 0); PT_ASSERT(not mem(m0, $S("Hello"))); PT_ASSERT(not mem(m0, $S("There"))); del(m0); var m1 = new(Tree, String, Int); set(m1, $S("Hello"), $I(2)); set(m1, $S("There"), $I(5)); var i0 = get(m1, $S("Hello")); var i1 = get(m1, $S("There")); PT_ASSERT( eq(i0, $I(2)) ); PT_ASSERT( eq(i1, $I(5)) ); set(m1, $S("Hello"), $I(6)); var i2 = get(m1, $S("Hello")); PT_ASSERT( eq(i2, $I(6)) ); del(m1); var m3 = new(Tree, String, Int, $S("asfa"), $I(0), $S("gasg"), $I(2), $S("asda"), $I(1), $S("dqga"), $I(0), $S("jaja"), $I(7), $S("sdfa"), $I(2), $S("jads"), $I(7), $S("kyad"), $I(2), $S("kyas"), $I(7), $S("hwdw"), $I(2), $S("oaqv"), $I(7), $S("fgds"), $I(2), $S("awaa"), $I(7), $S("lkil"), $I(2)); rem(m3, $S("gasg")); rem(m3, $S("oaqv")); rem(m3, $S("fgds")); rem(m3, $S("awaa")); rem(m3, $S("kyad")); rem(m3, $S("dqga")); rem(m3, $S("jaja")); del(m3); m0 = new(Tree, Int, Float); m1 = new(Tree, String, Int); PT_ASSERT(key_type(m0) is Int); PT_ASSERT(val_type(m0) is Float); PT_ASSERT(key_type(m1) is String); PT_ASSERT(val_type(m1) is Int); del(m0); del(m1); } PT_FUNC(test_tree_hash) { var m0 = new(Tree, String, Int); set(m0, $S("Hello"), $I(2)); set(m0, $S("There"), $I(5)); PT_ASSERT(hash(m0) is ( hash($S("Hello")) ^ hash($I(2)) ^ hash($S("There")) ^ hash($I(5)))); del(m0); } PT_FUNC(test_tree_iter) { var m0 = new(Tree, String, Int); set(m0, $S("Hello"), $I(2)); set(m0, $S("There"), $I(5)); foreach(key in m0) { var val = get(m0, key); PT_ASSERT( (eq(key, $S("Hello")) and eq(val, $I(2))) or (eq(key, $S("There")) and eq(val, $I(5)))); } del(m0); } PT_FUNC(test_tree_len) { var m0 = new(Tree, String, Int); set(m0, $S("Hello"), $I(2)); set(m0, $S("There"), $I(5)); PT_ASSERT(len(m0) is 2); del(m0); } PT_FUNC(test_tree_new) { var m0 = new(Tree, String, Int); set(m0, $S("Hello"), $I(2)); set(m0, $S("There"), $I(5)); var m1 = new(Tree, String, Int); set(m1, $S("Bonjour"), $I(9)); set(m1, $S("Where"), $I(5)); var m2 = copy(m0); PT_ASSERT(m0); PT_ASSERT(m1); PT_ASSERT(m2); PT_ASSERT(m0 isnt m2); PT_ASSERT(m0 isnt m1); PT_ASSERT(mem(m0, $S("Hello"))); PT_ASSERT(mem(m1, $S("Bonjour"))); PT_ASSERT(mem(m2, $S("There"))); assign(m2, m1); PT_ASSERT(mem(m2, $S("Where"))); PT_ASSERT(mem(m2, $S("Bonjour"))); PT_ASSERT(not mem(m2, $S("Hello"))); PT_ASSERT(not mem(m2, $S("There"))); del(m0); del(m1); del(m2); var m3 = new(Tree, String, Int, $S("asfa"), $I(0), $S("gasg"), $I(2), $S("asda"), $I(1), $S("dqga"), $I(0), $S("jaja"), $I(7), $S("sdfa"), $I(2), $S("jads"), $I(7), $S("kyad"), $I(2), $S("kyas"), $I(7), $S("hwdw"), $I(2), $S("awaa"), $I(7), $S("lkil"), $I(2)); del(m3); } PT_FUNC(test_tree_show) { var m0 = new(Tree, String, Int); set(m0, $S("Bonjour"), $I(9)); set(m0, $S("Where"), $I(5)); var s0 = new(String); show_to(m0, s0, 0); del(m0); del(s0); } PT_SUITE(suite_tree) { PT_REG(test_tree_assign); PT_REG(test_tree_resize); PT_REG(test_tree_cmp); PT_REG(test_tree_get); PT_REG(test_tree_hash); PT_REG(test_tree_iter); PT_REG(test_tree_len); PT_REG(test_tree_new); PT_REG(test_tree_show); } /* Tuple */ PT_FUNC(test_tuple_assign) { var x = new(Tuple); var y = tuple($I(100), $I(200), $I(300)); assign(x, y); PT_ASSERT(c_int(get(x, $I(0))) is 100); PT_ASSERT(c_int(get(x, $I(1))) is 200); PT_ASSERT(c_int(get(x, $I(2))) is 300); PT_ASSERT(len(y) is 3); var z = new(Array, Ref); assign(z, x); PT_ASSERT(c_int(deref(get(z, $I(0)))) is 100); PT_ASSERT(c_int(deref(get(z, $I(1)))) is 200); PT_ASSERT(c_int(deref(get(z, $I(2)))) is 300); PT_ASSERT(len(z) is 3); del(z); del(x); } PT_FUNC(test_tuple_resize) { var x = new(Tuple, $I(100), $I(200), $I(300)); PT_ASSERT(len(x) is 3); resize(x, 0); PT_ASSERT(len(x) is 0); del(x); } PT_FUNC(test_tuple_cmp) { var x = tuple($I(100), $I(200), $I(300)); var y = tuple($I(100), $I(200), $I(300)); PT_ASSERT(eq(x, y)); set(y, $I(0), $I(0)); PT_ASSERT(neq(x, y)); } PT_FUNC(test_tuple_concat) { var x = new(Tuple, $I(100), $I(200), $I(300)); concat(x, tuple($I(10), $I(20))); PT_ASSERT(c_int(get(x, $I(0))) is 100); PT_ASSERT(c_int(get(x, $I(1))) is 200); PT_ASSERT(c_int(get(x, $I(2))) is 300); PT_ASSERT(c_int(get(x, $I(3))) is 10); PT_ASSERT(c_int(get(x, $I(4))) is 20); del(x); } PT_FUNC(test_tuple_get) { var x = new(Tuple, $I(100), $I(200), $I(300)); PT_ASSERT(c_int(get(x, $I(0))) is 100); PT_ASSERT(c_int(get(x, $I(1))) is 200); PT_ASSERT(c_int(get(x, $I(2))) is 300); PT_ASSERT(c_int(get(x, $I(-1))) is 300); PT_ASSERT(c_int(get(x, $I(-2))) is 200); PT_ASSERT(c_int(get(x, $I(-3))) is 100); set(x, $I(1), $I(10)); PT_ASSERT(c_int(get(x, $I(1))) is 10); PT_ASSERT(mem(x, $I(10))); PT_ASSERT(mem(x, $I(100))); PT_ASSERT(mem(x, $I(300))); rem(x, $I(10)); PT_ASSERT(c_int(get(x, $I(0))) is 100); PT_ASSERT(c_int(get(x, $I(1))) is 300); del(x); } PT_FUNC(test_tuple_hash) { var x = tuple($S("Hello"), $S("World"), $S("!")); PT_ASSERT(hash(x) is ( hash($S("Hello")) ^ hash($S("World")) ^ hash($S("!")))); } PT_FUNC(test_tuple_iter) { size_t i = 0; foreach (x in tuple($I(10), $I(20), $I(30))) { if (i is 0) { PT_ASSERT(eq(x, $I(10))); } if (i is 1) { PT_ASSERT(eq(x, $I(20))); } if (i is 2) { PT_ASSERT(eq(x, $I(30))); } i++; } } PT_FUNC(test_tuple_len) { var x = tuple($I(1), $I(2)); var y = tuple($I(1), $I(2), $I(3)); var z = tuple($I(1), $I(2), $I(3), $I(10)); PT_ASSERT(len(x) is 2); PT_ASSERT(len(y) is 3); PT_ASSERT(len(z) is 4); } PT_FUNC(test_tuple_new) { var x = new(Tuple, $I(100), $S("Test")); PT_ASSERT(x); del(x); } PT_FUNC(test_tuple_push) { var x = new(Tuple); push(x, $S("Hello")); PT_ASSERT(eq(get(x, $I(0)), $S("Hello"))); push(x, $S("World")); PT_ASSERT(eq(get(x, $I(0)), $S("Hello"))); PT_ASSERT(eq(get(x, $I(1)), $S("World"))); pop_at(x, $I(0)); PT_ASSERT(eq(get(x, $I(0)), $S("World"))); push_at(x, $S("Hello"), $I(0)); PT_ASSERT(eq(get(x, $I(0)), $S("Hello"))); PT_ASSERT(eq(get(x, $I(1)), $S("World"))); del(x); } PT_FUNC(test_tuple_show) { var a0 = tuple($I(1), $I(5), $I(9)); var s0 = new(String); show_to(a0, s0, 0); del(s0); } PT_FUNC(test_tuple_sort) { var a0 = tuple($I(100), $I(1233), $I(1), $I(2312), $I(21)); var a1 = tuple($I(1), $I(21), $I(100), $I(1233), $I(2312)); var a2 = tuple($I(2312), $I(1233), $I(100), $I(21), $I(1)); var a3 = copy(a0); var a4 = copy(a0); sort(a3); sort_by(a4, gt); PT_ASSERT(eq(a3, a1)); PT_ASSERT(eq(a4, a2)); del(a3); del(a4); } PT_SUITE(suite_tuple) { PT_REG(test_tuple_assign); PT_REG(test_tuple_resize); PT_REG(test_tuple_cmp); PT_REG(test_tuple_concat); PT_REG(test_tuple_get); PT_REG(test_tuple_hash); PT_REG(test_tuple_iter); PT_REG(test_tuple_len); PT_REG(test_tuple_new); PT_REG(test_tuple_push); PT_REG(test_tuple_show); PT_REG(test_tuple_sort); } /* Type */ static var TestType; struct TestType { int64_t test_data; }; static void TestType_New(var self, var args) { struct TestType* tt = self; tt->test_data = c_int(get(args, $I(0))); } static int TestType_Cmp(var self, var obj) { struct TestType* lhs = cast(self, TestType); struct TestType* rhs = cast(obj, TestType); return (int)(lhs->test_data - rhs->test_data); } PT_FUNC(test_type_new) { TestType = new_root(Type, $S("TestType"), $I(sizeof(struct TestType)), $(New, TestType_New, NULL), $(Cmp, TestType_Cmp)); PT_ASSERT(TestType); PT_ASSERT_STR_EQ(c_str(TestType), "TestType"); var test_obj1 = new(TestType, $I(1)); var test_obj2 = new(TestType, $I(1)); var test_obj3 = new(TestType, $I(4)); PT_ASSERT(test_obj1); PT_ASSERT(test_obj2); PT_ASSERT(test_obj3); PT_ASSERT(eq(test_obj1, test_obj2)); PT_ASSERT(neq(test_obj1, test_obj3)); del(test_obj1); del(test_obj2); del(test_obj3); del_root(TestType); } PT_FUNC(test_type_c_str) { PT_ASSERT_STR_EQ(c_str(Type), "Type"); PT_ASSERT_STR_EQ(c_str(Int), "Int"); PT_ASSERT_STR_EQ(c_str(Float), "Float"); PT_ASSERT_STR_EQ(c_str(Array), "Array"); } PT_FUNC(test_type_cmp) { PT_ASSERT(gt(Type, Int)); PT_ASSERT(lt(Array, Float)); } PT_FUNC(test_type_hash) { PT_ASSERT(hash(Type) is hash($S("Type"))); PT_ASSERT(hash(Int) is hash($S("Int"))); PT_ASSERT(hash(Array) is hash($S("Array"))); } PT_FUNC(test_type_help) { /* TODO: improve test cases, but for now just verify that they can be called without crashing */ try{ name(_); } catch(e) { } try{ brief(_); } catch(e) { } try{ description(_); } catch(e) { } try{ definition(_); } catch(e) { } try{ help(_); } catch(e) { } try{ help_to(_, 0,_);} catch(e) { } PT_ASSERT(1); } PT_FUNC(test_type_show) { var s = new(String); show_to(Int, s, 0); PT_ASSERT_STR_EQ(c_str(s), "Int"); show_to(Type, s, 0); PT_ASSERT_STR_EQ(c_str(s), "Type"); show_to(Array, s, 0); PT_ASSERT_STR_EQ(c_str(s), "Array"); del(s); } PT_SUITE(suite_type) { PT_REG(test_type_new); PT_REG(test_type_c_str); PT_REG(test_type_cmp); PT_REG(test_type_hash); PT_REG(test_type_help); PT_REG(test_type_show); } /* Zip */ PT_FUNC(test_zip_get) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var y = tuple($I(10), $I(213), $I(12), $I(88), $I(99)); var z = zip(x, y); PT_ASSERT(eq(get(z, $I(0)), tuple($I(100), $I( 10)))); PT_ASSERT(eq(get(z, $I(1)), tuple($I( 10), $I(213)))); PT_ASSERT(eq(get(z, $I(2)), tuple($I( 30), $I( 12)))); PT_ASSERT(eq(get(z, $I(3)), tuple($I( 50), $I( 88)))); PT_ASSERT(eq(get(z, $I(4)), tuple($I( 6), $I( 99)))); } PT_FUNC(test_zip_iter) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var y = tuple($I(10), $I(213), $I(12), $I(88), $I(99)); var z = tuple($I(10), $I(213)); /* TODO: Improve */ foreach (i in zip(x)) {} foreach (i in zip(x, y)) {} foreach (i in zip(x, y, z)) {} foreach (i in zip(y, z)) {} PT_ASSERT(iter_type(zip(x, y, z)) is Tuple); x = new(Array, Int, $I(100), $I(200), $I(130)); y = new(Array, Float, $F(0.1), $F(0.2), $F(1.3)); foreach (pair in zip(x, y)) {} foreach (pair in enumerate(x)) {} del(x); del(y); } PT_FUNC(test_zip_len) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var y = tuple($I(10), $I(213), $I(12), $I(88), $I(99)); var z = tuple($I(10), $I(213)); PT_ASSERT(len(zip(z)) is 2); PT_ASSERT(len(zip(z, y)) is 2); PT_ASSERT(len(zip(x, y, z)) is 2); PT_ASSERT(len(zip(x, y)) is 5); PT_ASSERT(len(zip(x)) is 6); } PT_FUNC(test_zip_new) { var x = tuple($I(100), $I(10), $I(30), $I(50), $I(6), $I(1)); var y = tuple($I(10), $I(213), $I(12), $I(88), $I(99)); var z = new(Zip, x, y); PT_ASSERT(eq(get(z, $I(0)), tuple($I(100), $I( 10)))); PT_ASSERT(eq(get(z, $I(1)), tuple($I( 10), $I(213)))); PT_ASSERT(eq(get(z, $I(2)), tuple($I( 30), $I( 12)))); del(z); } PT_SUITE(suite_zip) { PT_REG(test_zip_get); PT_REG(test_zip_iter); PT_REG(test_zip_len); PT_REG(test_zip_new); } /* Exception */ static var DivideByZeroError = CelloEmpty(DivideByZeroError); static var OtherError = CelloEmpty(OtherError); static int exception_divide(int x, int y) { if (y == 0) { throw(DivideByZeroError, "%i / %i", $I(x), $I(y)); return 0; } else { return x / y; } } PT_FUNC(test_exception_throw) { int r0 = exception_divide(2, 1); int r1 = exception_divide(4, 2); int r2 = exception_divide(9, 3); PT_ASSERT(r0 == 2); PT_ASSERT(r1 == 2); PT_ASSERT(r2 == 3); PT_ASSERT(len(current(Exception)) is 0); } PT_FUNC(test_exception_catch) { volatile bool reached0 = false; volatile bool reached1 = false; volatile bool reached2 = false; try { int r3 = exception_divide(2, 0); } catch (e in DivideByZeroError) { reached0 = true; } try { int r3 = exception_divide(2, 1); } catch (e in DivideByZeroError) { reached1 = true; } try { int r3 = exception_divide(2, 1); } catch (e) { reached2 = true; } PT_ASSERT(reached0); PT_ASSERT(not reached1); PT_ASSERT(not reached2); PT_ASSERT(len(current(Exception)) is 0); } PT_FUNC(test_exception_catch_all) { volatile bool reached0 = false; volatile bool reached1 = false; try { exception_divide(2, 0); } catch (e) { reached0 = true; } try { throw(OtherError, "Something else went wrong"); } catch (e) { reached1 = true; } PT_ASSERT(reached0); PT_ASSERT(reached1); PT_ASSERT(len(current(Exception)) is 0); } PT_FUNC(test_exception_catch_outer) { volatile bool reached0 = false; volatile bool reached1 = false; try { PT_ASSERT(len(current(Exception)) is 1); try { PT_ASSERT(len(current(Exception)) is 2); exception_divide(2, 0); } catch (e in TypeError) { reached0 = true; } PT_ASSERT(len(current(Exception)) is 1); } catch (e) { reached1 = true; } PT_ASSERT(not reached0); PT_ASSERT(reached1); PT_ASSERT(len(current(Exception)) is 0); } PT_SUITE(suite_exception) { PT_REG(test_exception_throw); PT_REG(test_exception_catch); PT_REG(test_exception_catch_all); PT_REG(test_exception_catch_outer); } int main(int argc, char** argv) { pt_add_suite(suite_array); pt_add_suite(suite_box); pt_add_suite(suite_file); pt_add_suite(suite_float); pt_add_suite(suite_filter); pt_add_suite(suite_function); pt_add_suite(suite_int); pt_add_suite(suite_list); pt_add_suite(suite_map); #if defined(CELLO_WINDOWS) || defined(CELLO_UNIX) pt_add_suite(suite_mutex); #endif pt_add_suite(suite_range); pt_add_suite(suite_ref); pt_add_suite(suite_slice); pt_add_suite(suite_string); pt_add_suite(suite_table); #if defined(CELLO_WINDOWS) || defined(CELLO_UNIX) pt_add_suite(suite_thread); #endif pt_add_suite(suite_tree); pt_add_suite(suite_tuple); pt_add_suite(suite_type); pt_add_suite(suite_zip); pt_add_suite(suite_exception); pt_run(); return 0; }