Began cleaning up this project for a soft reboot

This commit is contained in:
2026-04-04 19:32:45 +11:00
parent 5a0012d73a
commit 98208f4bb5
49 changed files with 23 additions and 8655 deletions

View File

@@ -2,6 +2,8 @@
<image src="toylogo.png" />
</p>
<p style="color:red;font-weight: bold;">This is a work in progress, and is not fit for purpose. I hope I can get it to a useable state, but personal issues can often make dedicating myself to a project difficult. Your patience and support is greatly appreciated.</p>
# Toy v2.x
The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through text files.
@@ -14,69 +16,20 @@ This repository holds the reference implementation for Toy version 2.x, written
* Intermediate AST representation
* Strong, but optional type system
* First-class functions and closures
* Extensible with importable native code
* Extensible with imported native code
* Can re-direct output, error and assert failure messages
* Open-Source under the zlib license
# Syntax
The following examples aren't fully functional yet - see the ['features' label in the issue tracker](https://github.com/krgamestudios/Toy/issues?q=is%3Aissue%20state%3Aopen%20label%3Afeature) for more information.
Watch this space.
```toy
//fizzbuzz example
for (var counter: int = 1; counter <= 100; i++) {
var result: string = "";
if (counter % 3 == 0) {
result = result .. "fizz";
}
if (counter % 5 == 0) {
result = result .. "buzz";
}
if (result != "") {
print result;
}
else {
print counter;
}
}
```
```toy
//find the nth fibonacci number
fn fib(n: int) {
if (n < 2) return n;
return fib(n-1) + fib(n-2);
}
for (var i = 1; i <= 10; i++) {
print i.toString() .. ":" .. fib(i).toString();
}
```
```toy
//closures!
fn makeCounter() {
var count = 0;
fn next() {
return ++count;
}
return next;
}
var tally = makeCounter();
print tally(); //1
print tally(); //2
print tally(); //3
```
(The `scripts` or `tests` directory might help.)
# Building
TODO: Look into cmake
Supported platforms are: `linux-latest`, `windows-latest`, `macos-latest`, using [GitHub's standard runners](https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories).
Support for NetBSD is present, but not guaranteed.
@@ -87,13 +40,11 @@ To build and run the test suites, run `make tests` (`make tests-gdb` and `make t
# Tools
Information about the tools can be found in the [issue tracker](https://github.com/krgamestudios/Toy/issues?q=is%3Aissue%20state%3Aopen%20label%3Atooling).
Watch this space.
# Documentation
See [https://toylang.com/](https://toylang.com/) for Toy's documentation.
Further information about the documentation can be found in the [issue tracker](https://github.com/krgamestudios/Toy/issues?q=is%3Aissue%20state%3Aopen%20label%3Adocumentation).
Watch this space.
# License
@@ -113,6 +64,6 @@ Various Anons - Feedback
# Patreon Supporters
* Seth A. Robinson
Watch this space.
You can show your support and be listed here by joining my [Patreon](https://patreon.com/krgamestudios).

View File

View File

@@ -9,8 +9,6 @@
#directories
export TOY_SOURCEDIR=source
export TOY_REPLDIR=repl
export TOY_CASESDIR=tests/cases
export TOY_INTEGRATIONSDIR=tests/integrations
export TOY_OUTDIR=out
export TOY_OBJDIR=obj
@@ -25,48 +23,6 @@ source:
repl: source
$(MAKE) -C repl -k
#various kinds of available tests
.PHONY: tests
tests: clean test-cases test-integrations
.PHONY: test-cases
test-cases:
$(MAKE) -C $(TOY_CASESDIR) -k
.PHONY: test-integrations
test-integrations:
$(MAKE) -C $(TOY_INTEGRATIONSDIR) -k
#same as above, but with GDB
.PHONY: tests-gdb
tests-gdb: clean test-cases-gdb test-integrations-gdb
.PHONY: test-cases-gdb
test-cases-gdb:
$(MAKE) -C $(TOY_CASESDIR) gdb -k
.PHONY: test-integrations-gdb
test-integrations-gdb:
$(MAKE) -C $(TOY_INTEGRATIONSDIR) gdb -k
#same as above, but with valgrind
.PHONY: tests-valgrind
tests-valgrind: clean test-cases-valgrind test-integrations-valgrind
.PHONY: test-cases-valgrind
test-cases-valgrind:
$(MAKE) -C $(TOY_CASESDIR) valgrind -k
.PHONY: test-integrations-valgrind
test-integrations-valgrind:
$(MAKE) -C $(TOY_INTEGRATIONSDIR) valgrind -k
#Run all tests
.PHONY: tests-all
tests-all: clean tests tests-gdb tests-valgrind
#TODO: mustfail tests
#util targets
$(TOY_OUTDIR):
mkdir $(TOY_OUTDIR)
@@ -78,6 +34,7 @@ $(TOY_OBJDIR):
.PHONY: clean
clean:
ifeq ($(shell uname),Linux)
rm -r out
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.exe' -delete
@@ -85,9 +42,9 @@ ifeq ($(shell uname),Linux)
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else ifeq ($(shell uname),NetBSD)
rm -r out
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.exe' -delete
@@ -95,13 +52,13 @@ else ifeq ($(shell uname),NetBSD)
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib
$(RM) out
$(RM) obj
else ifeq ($(shell uname),Darwin)
rm -r out
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.exe' -delete
@@ -109,7 +66,6 @@ else ifeq ($(shell uname),Darwin)
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else
@echo "Deletion failed - what platform is this?"

View File

@@ -15,7 +15,14 @@ REPL_OBJDIR=obj
#file names
REPL_REPLFILES=$(wildcard $(REPL_REPLDIR)/*.c)
REPL_OBJFILES=$(addprefix $(REPL_OBJDIR)/,$(notdir $(REPL_REPLFILES:.c=.o)))
REPL_TARGETNAME=repl.exe
REPL_TARGETNAME=repl
#file extensions
ifeq ($(OS),Windows_NT)
REPL_TARGETEXT=.exe
else
REPL_TARGETEXT=
endif
#linker fix
LDFLAGS+=-L$(realpath $(REPL_OUTDIR))
@@ -28,7 +35,7 @@ all: build link
build: $(REPL_OBJDIR) $(REPL_OBJFILES)
.PHONY: link
link: $(REPL_OUTDIR) $(REPL_OUTDIR)/$(REPL_TARGETNAME)
link: $(REPL_OUTDIR) $(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT)
#util targets
$(REPL_OUTDIR):
@@ -41,7 +48,7 @@ $(REPL_OBJDIR):
$(REPL_OBJDIR)/%.o: $(REPL_REPLDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(REPL_REPLDIR)) $(addprefix -I,$(REPL_SOURCEDIR)) $(CFLAGS)
$(REPL_OUTDIR)/$(REPL_TARGETNAME): $(REPL_OBJFILES)
$(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT): $(REPL_OBJFILES)
$(CC) -DTOY_IMPORT $(CFLAGS) -o $@ $(REPL_OBJFILES) $(LDFLAGS) $(LIBS)
ifeq ($(shell uname),Darwin) #dylib fix
otool -L $@

View File

@@ -1,28 +0,0 @@
# Test Instructions
To run these tests, execute one of the following commands from the repo's root:
`make tests`
`make tests-gdb`
`make tests-valgrind`
## Benchmarks
For testing and comparing different potential solutions. These may be left in an incomplete state, so they might not work out of the box.
## Cases
For testing individual pieces of the source code in isolation. These are essentially the unit tests, and are used by the CI pipeline.
## Integrations
For testing Toy's processes as a complete whole. This will automatically build the repl, and is used by the CI pipeline.
## Mustfails
These have situations which will raise errors of some kind, to ensure that common user errors are handled gracefully. This is not yet implemented.
## Standalone
These are one-file programs that are not intended to test the source directly. Instead, these can cover a number of situations, such as the exact behavior of GitHub's workflow runners, or to generate repetitive code predictably, etc.

View File

@@ -1,40 +0,0 @@
#include "toy.h"
//util macros
#define TOY_ARRAY_EXPAND(array) ((array) = ((array) != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray((array), (array)->capacity * TOY_ARRAY_EXPANSION_RATE) : (array)))
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array), (array)->data[(array)->count++] = (value))
void stress_fillArray(Toy_Array** array) {
//Toy_Value is either 8 or 16 bytes
for (int i = 0; i < 10 * 1000 * 1000; i++) {
TOY_ARRAY_PUSHBACK(*array, TOY_VALUE_FROM_INTEGER(i));
}
}
int main() {
//Compare different memory strategies for Toy_Array
for (int i = 0; i < 100; i++) {
/*
//malloc
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
stress_fillArray(&array);
Toy_resizeArray(array, 0);
/*/
//Toy_Bucket
benchBucket = Toy_allocateBucket(1024 * 1024 * 200); //200MB
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
stress_fillArray(&array);
Toy_resizeArray(array, 0);
Toy_freeBucket(&benchBucket);
//*/
}
return 0;
}

View File

@@ -1,104 +0,0 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
ifeq ($(shell uname),Linux)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(OS),Windows_NT)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),Darwin)
LDFLAGS=-Wl,-dead_strip
else
@echo "LDFLAGS set failed - what platform is this?"
endif
#patched incl
TOY_SOURCEDIR=source
#directories
TEST_ROOTDIR=../../..
TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR)
TEST_CASESDIR=.
TEST_OUTDIR=out
TEST_OBJDIR=obj
#file names
TEST_SOURCEFILES=$(wildcard $(TEST_SOURCEDIR)/*.c)
TEST_CASESFILES=$(wildcard $(TEST_CASESDIR)/bench_*.c)
#build the object files, compile the test cases, and run
all: clean
$(MAKE) build-source
$(MAKE) build-cases
$(MAKE) build-link
$(MAKE) build-run
#targets for each step
.PHONY: build-source
build-source: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o)))
.PHONY: build-cases
build-cases: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_CASESFILES:.c=.o)))
.PHONY: build-link
build-link: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe)))
.PHONY: build-run
build-run: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run)))
#compilation steps
$(TEST_OBJDIR)/%.o: $(TEST_SOURCEDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OBJDIR)/%.o: $(TEST_CASESDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR) $(TEST_CASESDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OUTDIR)/%.exe: $(TEST_OBJDIR)/%.o
@$(CC) -o $@ $< $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) $(CFLAGS) $(LIBS) $(LDFLAGS)
strip $@
.PRECIOUS: $(TEST_OUTDIR)/%.run
$(TEST_OUTDIR)/%.run: $(TEST_OUTDIR)/%.exe
@/usr/bin/time --format "User System\n%U %E" $<
#util targets
$(TEST_OUTDIR):
mkdir $(TEST_OUTDIR)
$(TEST_OBJDIR):
mkdir $(TEST_OBJDIR)
#util commands
.PHONY: clean
clean:
ifeq ($(shell uname),Linux)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib
$(RM) out
$(RM) obj
else ifeq ($(shell uname),Darwin)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else
@echo "Deletion failed - what platform is this?"
endif

View File

@@ -1,48 +0,0 @@
These tests compared two memory solutions for `Toy_Array`, under different conditions. 'Pushes' is the number of iterations used in the following stress function:
```c
//defined in toy_value.h
#define TOY_VALUE_FROM_INTEGER(value) ((Toy_Value){{ .integer = value }, TOY_VALUE_INTEGER})
//util macros
#define TOY_ARRAY_EXPAND(array) ((array) = ((array) != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray((array), (array)->capacity * TOY_ARRAY_EXPANSION_RATE) : (array)))
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array), (array)->data[(array)->count++] = (value))
void stress_fillArray(Toy_Array** array) {
//Toy_Value is either 8 or 16 bytes
for (int i = 0; i < 10 * 1000 * 1000; i++) {
TOY_ARRAY_PUSHBACK(*array, TOY_VALUE_FROM_INTEGER(i));
}
}
```
'Memory' is the capacity of the `Toy_Bucket` when used. In the first set of results, the stress function was called once, while the second set called it 100 times, clearing the memory entirely each time. 'malloc' and 'bucket' shows the measured time taken for each situation.
```
1x run
pushes: 1000 * 1000
memory: 1024 * 1024 * 20
malloc: 0.01 0:00.01
bucket: 0.00 0:00.02
pushes: 10 * 1000 * 1000
memory: 1024 * 1024 * 200
malloc: 0.08 0:00.14
bucket: 0.13 0:00.29
```
```
100x looped runs
pushes: 1000 * 1000
memory: 1024 * 1024 * 20
malloc: 0.94 0:01.47
bucket: 1.02 0:02.60
pushes: 10 * 1000 * 1000
memory: 1024 * 1024 * 200
malloc: 8.28 0:15.77
bucket: 11.81 0:30.06
```

View File

@@ -1,63 +0,0 @@
#include "toy_array.h"
#include "toy_console_colors.h"
#include "toy_bucket.h"
#include <string.h>
Toy_Bucket* benchBucket = NULL;
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
//allow the array to be 'lost', and freed with the bucket
if (capacity == 0) {
return NULL;
}
//initial allocation
if (paramArray == NULL) {
Toy_Array* array = Toy_partitionBucket(&benchBucket, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
array->capacity = capacity;
array->count = 0;
return array;
}
//if your array is growing, partition more space, then copy over the data
if (paramArray->capacity < capacity) {
Toy_Array* array = Toy_partitionBucket(&benchBucket, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
memcpy(array, paramArray, paramArray->count * sizeof(Toy_Value) + sizeof(Toy_Array)); //doesn't copy any blank space
array->capacity = capacity;
array->count = paramArray->count;
return array;
}
//if some values will be removed, free them first, then return the result
if (paramArray->count > capacity) {
for (unsigned int i = capacity; i < paramArray->count; i++) {
Toy_freeValue(paramArray->data[i]);
}
paramArray->capacity = capacity; //don't worry about another allocation, this is faster
paramArray->count = capacity;
return paramArray;
}
//unreachable
return paramArray;
}
/*
Note: This needs to be pasted in the header:
```
struct Toy_Bucket;
extern struct Toy_Bucket* benchBucket;
```
*/

View File

@@ -1,34 +0,0 @@
#include "toy_array.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
if (capacity == 0) {
free(paramArray);
return NULL;
}
//if some values will be removed, free them first
if (paramArray != NULL && paramArray->count > capacity) {
for (unsigned int i = capacity; i < paramArray->count; i++) {
Toy_freeValue(paramArray->data[i]);
}
}
unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity;
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
if (array == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to resize a 'Toy_Array' from %d to %d capacity\n" TOY_CC_RESET, (int)originalCapacity, (int)capacity);
exit(-1);
}
array->capacity = capacity;
array->count = paramArray == NULL ? 0 :
(array->count > capacity ? capacity : array->count); //truncate lost data
return array;
}

View File

@@ -1,78 +0,0 @@
#include "toy.h"
#include <stdio.h>
#include <stdlib.h>
//generate an immense series of Toy_String instances to fill the buckets, thrn compare the time taken for each possible vale of TOY_BUCKET_IDEAL
static unsigned int hash(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x);
return x;
}
static unsigned int seed = 42;
static unsigned int rng() {
return seed = hash(seed);
}
#define MAX 9
const char* samples[] = { //9 entries
"the",
"quick",
"brown",
"fox",
"jumped",
"over",
"the",
"lazy",
"dog",
};
void stress_fillBucket(Toy_Bucket** bucketHandle) {
for (unsigned int i = 0; i < 10000000; i++) {
//create some leaf and node strings
Toy_String* a = Toy_createString(bucketHandle, samples[rng() % MAX]);
Toy_String* b = Toy_createString(bucketHandle, samples[rng() % MAX]);
Toy_String* c = Toy_createString(bucketHandle, samples[rng() % MAX]);
Toy_String* d = Toy_createString(bucketHandle, samples[rng() % MAX]);
Toy_String* l = Toy_concatStrings(bucketHandle, a, b);
Toy_String* r = Toy_concatStrings(bucketHandle, c, d);
Toy_concatStrings(bucketHandle, l, r);
// char* buffer = Toy_getStringRawBuffer(s);
// printf("%s\n", buffer);
// free(buffer);
}
}
static unsigned long long int measureDepth(Toy_Bucket* bucket) {
return bucket == NULL ? 0 : 1 + measureDepth(bucket->next);
}
static unsigned long long int measureCapacity(Toy_Bucket* bucket) {
return bucket == NULL ? 0 : bucket->capacity + measureCapacity(bucket->next);
}
static unsigned long long int measureCount(Toy_Bucket* bucket) {
return bucket == NULL ? 0 : bucket->count + measureCount(bucket->next);
}
int main() {
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
stress_fillBucket(&bucket);
unsigned long long int depth = measureDepth(bucket);
unsigned long long int capacity = measureCapacity(bucket);
unsigned long long int count = measureCount(bucket);
printf(TOY_CC_FONT_RED TOY_CC_BACK_YELLOW "Result: %u: %llu, %llu, %llu" TOY_CC_RESET "\n", TOY_BUCKET_IDEAL, depth, capacity, count);
Toy_freeBucket(&bucket);
return 0;
}

View File

@@ -1,111 +0,0 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
ifeq ($(shell uname),Linux)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(OS),Windows_NT)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),Darwin)
LDFLAGS=-Wl,-dead_strip
else
@echo "LDFLAGS set failed - what platform is this?"
endif
#patched incl
TOY_SOURCEDIR=source
#directories
TEST_ROOTDIR=../../..
TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR)
TEST_CASESDIR=.
TEST_OUTDIR=out
TEST_OBJDIR=obj
#file names
TEST_SOURCEFILES=$(wildcard $(TEST_SOURCEDIR)/*.c)
TEST_CASESFILES=$(wildcard $(TEST_CASESDIR)/bench_*.c)
#build the object files, compile the test cases, and run
all: clean
$(MAKE) TOY_BUCKET_IDEAL=16372 all-override
$(MAKE) TOY_BUCKET_IDEAL=32756 all-override
$(MAKE) TOY_BUCKET_IDEAL=65524 all-override
$(MAKE) TOY_BUCKET_IDEAL=131060 all-override
$(MAKE) TOY_BUCKET_IDEAL=262132 all-override
all-override: clean
$(MAKE) build-source
$(MAKE) build-cases
$(MAKE) build-link
$(MAKE) build-run
#targets for each step
.PHONY: build-source
build-source: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o)))
.PHONY: build-cases
build-cases: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_CASESFILES:.c=.o)))
.PHONY: build-link
build-link: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe)))
.PHONY: build-run
build-run: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run)))
#compilation steps
$(TEST_OBJDIR)/%.o: $(TEST_SOURCEDIR)/%.c
$(CC) -DTOY_BUCKET_IDEAL=$(TOY_BUCKET_IDEAL) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OBJDIR)/%.o: $(TEST_CASESDIR)/%.c
$(CC) -DTOY_BUCKET_IDEAL=$(TOY_BUCKET_IDEAL) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR) $(TEST_CASESDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OUTDIR)/%.exe: $(TEST_OBJDIR)/%.o
@$(CC) -DTOY_BUCKET_IDEAL=$(TOY_BUCKET_IDEAL) -o $@ $< $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) $(CFLAGS) $(LIBS) $(LDFLAGS)
strip $@
.PRECIOUS: $(TEST_OUTDIR)/%.run
$(TEST_OUTDIR)/%.run: $(TEST_OUTDIR)/%.exe
@/usr/bin/time --format "User System\n%U %E" $<
#util targets
$(TEST_OUTDIR):
mkdir $(TEST_OUTDIR)
$(TEST_OBJDIR):
mkdir $(TEST_OBJDIR)
#util commands
.PHONY: clean
clean:
ifeq ($(shell uname),Linux)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib
$(RM) out
$(RM) obj
else ifeq ($(shell uname),Darwin)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else
@echo "Deletion failed - what platform is this?"
endif

View File

@@ -1,2 +0,0 @@
set breakpoint pending on

View File

@@ -1,86 +0,0 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
ifeq ($(shell uname),Linux)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),NetBSD)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(OS),Windows_NT)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),Darwin)
LDFLAGS=-Wl,-dead_strip
else
@echo "LDFLAGS set failed - what platform is this?"
endif
#directories
TEST_ROOTDIR=../..
TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR)
TEST_CASESDIR=.
TEST_OUTDIR=out
TEST_OBJDIR=obj
#file names
TEST_SOURCEFILES=$(wildcard $(TEST_SOURCEDIR)/*.c)
TEST_CASESFILES=$(wildcard $(TEST_CASESDIR)/test_*.c)
#build the object files, compile the test cases, and run
all: build-source build-cases build-link build-run
#targets for each step
.PHONY: build-source
build-source: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o)))
.PHONY: build-cases
build-cases: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_CASESFILES:.c=.o)))
.PHONY: build-link
build-link: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe)))
.PHONY: build-run
build-run: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run)))
#util targets
$(TEST_OUTDIR):
mkdir $(TEST_OUTDIR)
$(TEST_OBJDIR):
mkdir $(TEST_OBJDIR)
#compilation steps
$(TEST_OBJDIR)/%.o: $(TEST_SOURCEDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OBJDIR)/%.o: $(TEST_CASESDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR) $(TEST_CASESDIR)) $(CFLAGS) -fdata-sections -ffunction-sections
$(TEST_OUTDIR)/%.exe: $(TEST_OBJDIR)/%.o
@$(CC) -o $@ $< $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) $(CFLAGS) $(LIBS) $(LDFLAGS)
.PRECIOUS: $(TEST_OUTDIR)/%.run
$(TEST_OUTDIR)/%.run: $(TEST_OUTDIR)/%.exe
$<
#debugging targets
gdb: build-source build-cases build-link build-run-gdb
.PHONY: build-run-gdb
build-run-gdb: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run-gdb)))
.PRECIOUS: $(TEST_OUTDIR)/%.run-gdb
$(TEST_OUTDIR)/%.run-gdb: $(TEST_OUTDIR)/%.exe
gdb $< -ix gdb_init -ex=run --batch --return-child-result --args "$<"
#valgrind targets
valgrind: build-source build-cases build-link build-run-valgrind
.PHONY: build-run-valgrind
build-run-valgrind: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run-valgrind)))
.PRECIOUS: $(TEST_OUTDIR)/%.run-valgrind
$(TEST_OUTDIR)/%.run-valgrind: $(TEST_OUTDIR)/%.exe
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes $<

View File

@@ -1,52 +0,0 @@
#include "toy_array.h"
#include "toy_console_colors.h"
#include <stdio.h>
int test_array(void) {
//test allocation and free
{
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
array = Toy_resizeArray(array, 0);
}
//test initial data
{
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
//check you can access the memory
array->data[1] = TOY_VALUE_FROM_INTEGER(42);
array = Toy_resizeArray(array, 0);
}
//test multiple arrays (no overlaps or conflicts)
{
Toy_Array* array1 = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
Toy_Array* array2 = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
array1->data[1] = TOY_VALUE_FROM_INTEGER(42);
array2->data[1] = TOY_VALUE_FROM_INTEGER(42);
Toy_resizeArray(array1, 0);
Toy_resizeArray(array2, 0);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_array();
total += res;
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
}
return total;
}

View File

@@ -1,497 +0,0 @@
#include "toy_ast.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <string.h>
#if TOY_BITNESS == 32
#define TEST_SIZEOF(type, bit32, bit64) \
if (sizeof(type) != bit32) { \
fprintf(stderr, TOY_CC_ERROR "ERROR: sizeof(" #type ") is %d, expected %d (bitness %d)\n" TOY_CC_RESET, (int)sizeof(type), bit32, TOY_BITNESS); \
++err; \
}
#elif TOY_BITNESS == 64
#define TEST_SIZEOF(type, bit32, bit64) \
if (sizeof(type) != bit64) { \
fprintf(stderr, TOY_CC_ERROR "ERROR: sizeof(" #type ") is %d, expected %d (bitness %d)\n" TOY_CC_RESET, (int)sizeof(type), bit64, TOY_BITNESS); \
++err; \
}
#else
#pragma message("Unable to test the size of Toy_Ast members, as TOY_BITNESS is not recognized")
#define TEST_SIZEOF(type, bit32, bit64)
#endif
int test_sizeof_ast(void) {
int err = 0;
//run for each type
TEST_SIZEOF(Toy_AstType, 4 , 4);
TEST_SIZEOF(Toy_AstBlock, 20 , 32);
TEST_SIZEOF(Toy_AstValue, 12 , 24);
TEST_SIZEOF(Toy_AstUnary, 12 , 16);
TEST_SIZEOF(Toy_AstBinary, 16 , 24);
TEST_SIZEOF(Toy_AstBinaryShortCircuit, 16 , 24);
TEST_SIZEOF(Toy_AstCompare, 16 , 24);
TEST_SIZEOF(Toy_AstGroup, 8 , 16);
TEST_SIZEOF(Toy_AstCompound, 12 , 16);
TEST_SIZEOF(Toy_AstAggregate, 16 , 24);
TEST_SIZEOF(Toy_AstAssert, 12 , 24);
TEST_SIZEOF(Toy_AstIfThenElse, 16 , 32);
TEST_SIZEOF(Toy_AstWhileThen, 12 , 24);
TEST_SIZEOF(Toy_AstBreak, 4 , 4);
TEST_SIZEOF(Toy_AstContinue, 4 , 4);
TEST_SIZEOF(Toy_AstReturn, 8 , 16);
TEST_SIZEOF(Toy_AstPrint, 8 , 16);
TEST_SIZEOF(Toy_AstVarDeclare, 12 , 24);
TEST_SIZEOF(Toy_AstVarAssign, 16 , 24);
TEST_SIZEOF(Toy_AstVarAccess, 8 , 16);
TEST_SIZEOF(Toy_AstFnDeclare, 16 , 32);
TEST_SIZEOF(Toy_AstFnInvoke, 12 , 24);
TEST_SIZEOF(Toy_AstPass, 4 , 4);
TEST_SIZEOF(Toy_AstError, 4 , 4);
TEST_SIZEOF(Toy_AstEnd, 4 , 4);
TEST_SIZEOF(Toy_Ast, 20 , 32);
return -err;
}
#undef TEST_SIZEOF
int test_type_emission(Toy_Bucket** bucketHandle) {
//emit value
{
//emit to an AST
Toy_Ast* ast = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->value.value) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a value as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit unary
{
//build the AST
Toy_Ast* ast = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstUnary(bucketHandle, &ast, TOY_AST_FLAG_NEGATE);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_UNARY ||
ast->unary.flag != TOY_AST_FLAG_NEGATE ||
ast->unary.child->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->unary.child->value.value) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a unary as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit binary
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BINARY ||
ast->binary.flag != TOY_AST_FLAG_ADD ||
ast->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->binary.left->value.value) != 42 ||
ast->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->binary.right->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a binary as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit compare
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); //technically, not a valid value
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstCompare(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_COMPARE ||
ast->compare.flag != TOY_AST_FLAG_ADD ||
ast->compare.left->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->compare.left->value.value) != 42 ||
ast->compare.right->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->compare.right->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a compare as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit group
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
Toy_private_emitAstGroup(bucketHandle, &ast);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_GROUP ||
ast->group.child == NULL ||
ast->group.child->type != TOY_AST_BINARY ||
ast->group.child->binary.flag != TOY_AST_FLAG_ADD ||
ast->group.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->group.child->binary.left->value.value) != 42 ||
ast->group.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->group.child->binary.right->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a group as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit aggregate
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstAggregate(bucketHandle, &ast, TOY_AST_FLAG_COLLECTION, right);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_AGGREGATE ||
ast->aggregate.left == NULL ||
ast->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->aggregate.left->value.value) != 42 ||
ast->aggregate.right == NULL ||
ast->aggregate.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->aggregate.right->value.value) != 69 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an aggregate as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit keyword assert
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* child = NULL;
Toy_Ast* msg = NULL;
Toy_private_emitAstValue(bucketHandle, &child, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &msg, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstAssert(bucketHandle, &ast, child, msg);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_ASSERT ||
ast->assert.child == NULL ||
ast->assert.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->assert.child->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->assert.child->value.value) != 42 ||
ast->assert.message == NULL ||
ast->assert.message->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->assert.message->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->assert.message->value.value) != 69 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'assert' as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit keyword if-then
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* condBranch = NULL;
Toy_Ast* thenBranch = NULL;
Toy_private_emitAstValue(bucketHandle, &condBranch, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &thenBranch, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstIfThenElse(bucketHandle, &ast, condBranch, thenBranch, NULL);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_IF_THEN_ELSE ||
ast->ifThenElse.condBranch == NULL ||
ast->ifThenElse.condBranch->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->ifThenElse.condBranch->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->ifThenElse.condBranch->value.value) != 42 ||
ast->ifThenElse.thenBranch == NULL ||
ast->ifThenElse.thenBranch->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->ifThenElse.thenBranch->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->ifThenElse.thenBranch->value.value) != 69 ||
ast->ifThenElse.elseBranch != NULL ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'if-then' as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit keyword if-then-else
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* condBranch = NULL;
Toy_Ast* thenBranch = NULL;
Toy_Ast* elseBranch = NULL;
Toy_private_emitAstValue(bucketHandle, &condBranch, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &thenBranch, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstValue(bucketHandle, &elseBranch, TOY_VALUE_FROM_INTEGER(420));
Toy_private_emitAstIfThenElse(bucketHandle, &ast, condBranch, thenBranch, elseBranch);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_IF_THEN_ELSE ||
ast->ifThenElse.condBranch == NULL ||
ast->ifThenElse.condBranch->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->ifThenElse.condBranch->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->ifThenElse.condBranch->value.value) != 42 ||
ast->ifThenElse.thenBranch == NULL ||
ast->ifThenElse.thenBranch->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->ifThenElse.thenBranch->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->ifThenElse.thenBranch->value.value) != 69 ||
ast->ifThenElse.elseBranch == NULL ||
ast->ifThenElse.elseBranch->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->ifThenElse.elseBranch->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->ifThenElse.elseBranch->value.value) != 420 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'if-then-else' as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit keyword print
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
Toy_private_emitAstPrint(bucketHandle, &ast);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_PRINT ||
ast->print.child == NULL ||
ast->print.child->type != TOY_AST_BINARY ||
ast->print.child->binary.flag != TOY_AST_FLAG_ADD ||
ast->print.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->print.child->binary.left->value.value) != 42 ||
ast->print.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->print.child->binary.right->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'print' as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit var declare
{
//build the AST
Toy_Ast* ast = NULL;
Toy_String* name = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_ANY, false);
Toy_private_emitAstVariableDeclaration(bucketHandle, &ast, name, NULL);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_VAR_DECLARE ||
ast->varDeclare.name == NULL ||
ast->varDeclare.name->info.type != TOY_STRING_NAME ||
strcmp(ast->varDeclare.name->name.data, "foobar") != 0 ||
ast->varDeclare.expr != NULL)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a var declare as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
Toy_freeString(name);
return -1;
}
//cleanup
Toy_freeString(name);
}
//emit assign
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_String* name = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_INTEGER, false);
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_STRING(name));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstVariableAssignment(bucketHandle, &ast, TOY_AST_FLAG_ASSIGN, right);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_VAR_ASSIGN ||
ast->varAssign.flag != TOY_AST_FLAG_ASSIGN ||
ast->varAssign.target == NULL ||
ast->varAssign.target->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->varAssign.target->value.value) != true ||
TOY_VALUE_AS_STRING(ast->varAssign.target->value.value)->info.type != TOY_STRING_NAME ||
strcmp(TOY_VALUE_AS_STRING(ast->varAssign.target->value.value)->name.data, "foobar") != 0 ||
TOY_VALUE_AS_STRING(ast->varAssign.target->value.value)->name.varType != TOY_VALUE_INTEGER ||
ast->varAssign.expr->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(ast->varAssign.expr->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an assign as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit access
{
//build the AST
Toy_Ast* ast = NULL;
Toy_String* name = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_INTEGER, false);
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_STRING(name));
Toy_private_emitAstVariableAccess(bucketHandle, &ast);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_VAR_ACCESS ||
ast->varAccess.child == NULL ||
ast->varAccess.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->varAccess.child->value.value) != true ||
TOY_VALUE_AS_STRING(ast->varAccess.child->value.value)->info.type != TOY_STRING_NAME ||
strcmp(TOY_VALUE_AS_STRING(ast->varAccess.child->value.value)->name.data, "foobar") != 0 ||
TOY_VALUE_AS_STRING(ast->varAccess.child->value.value)->name.varType != TOY_VALUE_INTEGER)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an access as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}
//emit and append blocks of code (at the bottom of this test function, so everything else is checked first)
{
//initialize the root block
Toy_Ast* block = NULL;
Toy_private_initAstBlock(bucketHandle, &block);
//loop over the ast emissions, appending each one as you go
for (int i = 0; i < 5; i++) {
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
Toy_private_emitAstGroup(bucketHandle, &ast);
Toy_private_appendAstBlock(bucketHandle, block, ast);
}
//check if it worked
Toy_Ast* iter = block;
while(iter != NULL) {
if (
iter->type != TOY_AST_BLOCK ||
iter->block.child == NULL ||
iter->block.child->type != TOY_AST_GROUP ||
iter->block.child->group.child == NULL ||
iter->block.child->group.child->type != TOY_AST_BINARY ||
iter->block.child->group.child->binary.flag != TOY_AST_FLAG_ADD ||
iter->block.child->group.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.left->value.value) != 42 ||
iter->block.child->group.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.right->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a block as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
iter = iter->block.next;
}
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_sizeof_ast();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucketHandle = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_type_emission(&bucketHandle);
Toy_freeBucket(&bucketHandle);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,89 +0,0 @@
#include "toy_bucket.h"
#include "toy_console_colors.h"
#include <stdio.h>
int test_buckets(void) {
//test initializing and freeing a bucket
{
//init
Toy_Bucket* bucket = Toy_allocateBucket(sizeof(int) * 32);
//check
if (bucket == NULL || bucket->capacity != 32 * sizeof(int)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to initialize 'Toy_Bucket'\n" TOY_CC_RESET);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//test partitioning a bucket, several times
{
//init
Toy_Bucket* bucket = Toy_allocateBucket(sizeof(int) * 32);
//grab some memory
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
//check
if (bucket == NULL || bucket->count != 4 * sizeof(int)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to partition 'Toy_Bucket' correctly: count is %d, expected %d\n" TOY_CC_RESET, (int)(bucket->count), (int)(4*sizeof(int)));
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//test partitioning a bucket, several times, with an internal expansion
{
//init
Toy_Bucket* bucket = Toy_allocateBucket(sizeof(int) * 4);
//grab some memory
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
Toy_partitionBucket(&bucket, sizeof(int));
//checks - please note that the top-most bucket is what is being filled - older buckets are further along
if (
bucket->capacity != 4 * sizeof(int) ||
bucket->count != 2 * sizeof(int) ||
bucket->next == NULL ||
bucket->next->capacity != 4 * sizeof(int) ||
bucket->next->count != 4 * sizeof(int))
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to expand 'Toy_Bucket' correctly\n" TOY_CC_RESET);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_buckets();
total += res;
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
}
return total;
}

View File

@@ -1,47 +0,0 @@
#include "toy_lexer.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <string.h>
int main(void) {
{
//source code sample to operate on
char* source = "print null;";
//test the lexer
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
//get each token
Toy_Token print = Toy_private_scanLexer(&lexer);
Toy_Token null = Toy_private_scanLexer(&lexer);
Toy_Token semi = Toy_private_scanLexer(&lexer);
Toy_Token eof = Toy_private_scanLexer(&lexer);
//test each token is correct
if (strncmp(print.lexeme, "print", print.length)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: print lexeme is wrong: %s" TOY_CC_RESET, print.lexeme);
return -1;
}
if (strncmp(null.lexeme, "null", null.length)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: null lexeme is wrong: %s" TOY_CC_RESET, null.lexeme);
return -1;
}
if (strncmp(semi.lexeme, ";", semi.length)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: semicolon lexeme is wrong: %s" TOY_CC_RESET, semi.lexeme);
return -1;
}
if (eof.type != TOY_TOKEN_EOF) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to find EOF token" TOY_CC_RESET);
return -1;
}
}
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
return 0;
}

View File

@@ -1,127 +0,0 @@
#include "toy_module_bundle.h"
#include "toy_console_colors.h"
#include "toy_opcodes.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include <stdio.h>
#include <string.h>
//tests
int test_bundle_header(Toy_Bucket** bucketHandle) {
//simple test to ensure the header looks right
{
//setup
Toy_Ast* ast = NULL;
Toy_private_emitAstPass(bucketHandle, &ast);
//run
Toy_ModuleBundle bundle;
Toy_initModuleBundle(&bundle);
Toy_appendModuleBundle(&bundle, ast);
if (bundle.count % 4 != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: module bundle size is not a multiple of 4, size is: %d\n" TOY_CC_RESET, (int)bundle.count);
//cleanup and return
Toy_freeModuleBundle(&bundle);
return -1;
}
//check
if (bundle.ptr[0] != TOY_VERSION_MAJOR ||
bundle.ptr[1] != TOY_VERSION_MINOR ||
bundle.ptr[2] != TOY_VERSION_PATCH ||
bundle.ptr[3] != 1 || //only one module
strcmp((char*)(bundle.ptr + 4), TOY_VERSION_BUILD) != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to write the module bundle header correctly:\n" TOY_CC_RESET);
fprintf(stderr, TOY_CC_ERROR "\t%d.%d.%d.%s, %d modules found\n" TOY_CC_RESET, (int)(bundle.ptr[0]), (int)(bundle.ptr[1]), (int)(bundle.ptr[2]), (char*)(bundle.ptr + 4), (int)(bundle.ptr[2]));
fprintf(stderr, TOY_CC_ERROR "\t%d.%d.%d.%s, %d modules expected\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD, 1);
//cleanup and return
Toy_freeModuleBundle(&bundle);
return -1;
}
//cleanup
Toy_freeModuleBundle(&bundle);
}
return 0;
}
int test_bundle_from_source(Toy_Bucket** bucketHandle) {
{
//setup
const char* source = "(1 + 2) * (3 + 4);";
Toy_Lexer lexer;
Toy_Parser parser;
Toy_bindLexer(&lexer, source);
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
//run
Toy_ModuleBundle bundle;
Toy_initModuleBundle(&bundle);
Toy_appendModuleBundle(&bundle, ast);
//check bytecode alignment
if (bundle.count % 4 != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: module bundle size is not a multiple of 4 (size is %d), source: %s\n" TOY_CC_RESET, (int)bundle.count, source);
//cleanup and return
Toy_freeModuleBundle(&bundle);
return -1;
}
//check bytecode header
//check
if (bundle.ptr[0] != TOY_VERSION_MAJOR ||
bundle.ptr[1] != TOY_VERSION_MINOR ||
bundle.ptr[2] != TOY_VERSION_PATCH ||
bundle.ptr[3] != 1 || //only one module
strcmp((char*)(bundle.ptr + 4), TOY_VERSION_BUILD) != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to write the module bundle header, source: %s\n" TOY_CC_RESET, source);
//cleanup and return
Toy_freeModuleBundle(&bundle);
return -1;
}
//cleanup
Toy_freeModuleBundle(&bundle);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_bundle_header(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_bundle_from_source(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,969 +0,0 @@
#include "toy_parser.h"
#include "toy_console_colors.h"
#include "toy_string.h"
#include <stdio.h>
#include <string.h>
//utils
Toy_Ast* makeAstFromSource(Toy_Bucket** bucketHandle, const char* source) {
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
return Toy_scanParser(bucketHandle, &parser);
}
//tests
int test_simple_empty_parsers(Toy_Bucket** bucketHandle) {
//simple parser setup and cleanup
{
//raw source code and lexer
const char* source = "";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
//parser
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_END)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with empty source\n" TOY_CC_RESET);
return -1;
}
}
//repeat above, but with one semicolon
{
//raw source code and lexer
const char* source = ";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
//parser
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_PASS)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with one semicolon\n" TOY_CC_RESET);
return -1;
}
}
//repeat above, but with multiple semicolons
{
//raw source code and lexer
const char* source = ";;;;;";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
//parser
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
Toy_Ast* iter = ast;
while(iter != NULL) {
//check each link and child
if (
iter == NULL ||
iter->type != TOY_AST_BLOCK ||
iter->block.child == NULL ||
iter->block.child->type != TOY_AST_PASS)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with multiple semicolons\n" TOY_CC_RESET);
return -1;
}
iter = iter->block.next;
}
}
return 0;
}
int test_var_declare(Toy_Bucket** bucketHandle) {
//declare with an initial value
{
const char* source = "var answer = 42;";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VAR_DECLARE ||
ast->block.child->varDeclare.name == NULL ||
ast->block.child->varDeclare.name->info.type != TOY_STRING_NAME ||
strcmp(ast->block.child->varDeclare.name->name.data, "answer") != 0 ||
ast->block.child->varDeclare.expr == NULL ||
ast->block.child->varDeclare.expr->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->varDeclare.expr->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->varDeclare.expr->value.value) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Declare AST failed, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
//declare without an initial value
{
const char* source = "var empty;";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VAR_DECLARE ||
ast->block.child->varDeclare.name == NULL ||
ast->block.child->varDeclare.name->info.type != TOY_STRING_NAME ||
strcmp(ast->block.child->varDeclare.name->name.data, "empty") != 0 ||
ast->block.child->varDeclare.expr == NULL ||
ast->block.child->varDeclare.expr->type != TOY_AST_VALUE ||
TOY_VALUE_IS_NULL(ast->block.child->varDeclare.expr->value.value) == false ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Declare AST failed, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
return 0;
}
int test_var_assign(Toy_Bucket** bucketHandle) {
//macro templates
#define TEST_VAR_ASSIGN(ARG_SOURCE, ARG_FLAG, ARG_NAME, ARG_VALUE) \
{ \
const char* source = ARG_SOURCE; \
Toy_Ast* ast = makeAstFromSource(bucketHandle, source); \
if ( \
ast == NULL || \
ast->type != TOY_AST_BLOCK || \
ast->block.child == NULL || \
ast->block.child->type != TOY_AST_VAR_ASSIGN || \
ast->block.child->varAssign.flag != ARG_FLAG || \
ast->block.child->varAssign.target == NULL || \
ast->block.child->varAssign.target->type != TOY_AST_VALUE || \
TOY_VALUE_IS_STRING(ast->block.child->varAssign.target->value.value) != true ||\
TOY_VALUE_AS_STRING(ast->block.child->varAssign.target->value.value)->info.type != TOY_STRING_NAME ||\
strcmp(TOY_VALUE_AS_STRING(ast->block.child->varAssign.target->value.value)->name.data, ARG_NAME) != 0 || \
ast->block.child->varAssign.expr == NULL || \
ast->block.child->varAssign.expr->type != TOY_AST_VALUE || \
TOY_VALUE_IS_INTEGER(ast->block.child->varAssign.expr->value.value) == false || \
TOY_VALUE_AS_INTEGER(ast->block.child->varAssign.expr->value.value) != ARG_VALUE) \
{ \
fprintf(stderr, TOY_CC_ERROR "ERROR: Assign AST failed, source: %s\n" TOY_CC_RESET, source); \
return -1; \
} \
}
//run tests
TEST_VAR_ASSIGN("answer = 42;", TOY_AST_FLAG_ASSIGN, "answer", 42);
TEST_VAR_ASSIGN("answer += 42;", TOY_AST_FLAG_ADD_ASSIGN, "answer", 42);
TEST_VAR_ASSIGN("answer -= 42;", TOY_AST_FLAG_SUBTRACT_ASSIGN, "answer", 42);
TEST_VAR_ASSIGN("answer *= 42;", TOY_AST_FLAG_MULTIPLY_ASSIGN, "answer", 42);
TEST_VAR_ASSIGN("answer /= 42;", TOY_AST_FLAG_DIVIDE_ASSIGN, "answer", 42);
TEST_VAR_ASSIGN("answer %= 42;", TOY_AST_FLAG_MODULO_ASSIGN, "answer", 42);
return 0;
}
int test_values(Toy_Bucket** bucketHandle) {
//test boolean true
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "true;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_BOOLEAN(ast->block.child->value.value) == false ||
TOY_VALUE_AS_BOOLEAN(ast->block.child->value.value) != true)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with boolean value true\n" TOY_CC_RESET);
return -1;
}
}
//test boolean false (just to be safe)
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "false;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_BOOLEAN(ast->block.child->value.value) == false ||
TOY_VALUE_AS_BOOLEAN(ast->block.child->value.value) != false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with boolean value false\n" TOY_CC_RESET);
return -1;
}
}
//test integer
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "42;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->value.value) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with integer value 42\n" TOY_CC_RESET);
return -1;
}
}
//test float
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "3.1415;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_FLOAT(ast->block.child->value.value) == false ||
TOY_VALUE_AS_FLOAT(ast->block.child->value.value) != 3.1415f)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with float value 3.1415\n" TOY_CC_RESET);
return -1;
}
}
//test integer with separators
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "1_234_567_890;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->value.value) != 1234567890)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with integer value 1_234_567_890\n" TOY_CC_RESET);
return -1;
}
}
//test float with separators
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "3.141_592_65;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_FLOAT(ast->block.child->value.value) == false ||
TOY_VALUE_AS_FLOAT(ast->block.child->value.value) != 3.14159265f)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with float value 3.141_592_65\n" TOY_CC_RESET);
return -1;
}
}
//test string
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "\"Hello world!\";");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->value.value) == false ||
TOY_VALUE_AS_STRING(ast->block.child->value.value)->info.type != TOY_STRING_LEAF ||
strcmp(TOY_VALUE_AS_STRING(ast->block.child->value.value)->leaf.data, "Hello world!") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with string value 'Hello world!'\n" TOY_CC_RESET);
return -1;
}
}
return 0;
}
int test_unary(Toy_Bucket** bucketHandle) {
//test unary integer negation
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "-42;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->value.value) != -42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with integer value -42 (unary negation)\n" TOY_CC_RESET);
return -1;
}
}
//test unary float negation
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "-3.1415;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_FLOAT(ast->block.child->value.value) == false ||
TOY_VALUE_AS_FLOAT(ast->block.child->value.value) != -3.1415f)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with float value -3.1415 (unary negation)\n" TOY_CC_RESET);
return -1;
}
}
//ensure unary negation doesn't occur with a group
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "-(42);");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type == TOY_AST_VALUE)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: unexpected successful unary negation in parser with grouped value -(42)\n" TOY_CC_RESET);
return -1;
}
}
//ensure unary negation doesn't occur with a space
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "- 42;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type == TOY_AST_VALUE)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: unexpected successful unary negation in parser with space character '- 42'\n" TOY_CC_RESET);
return -1;
}
}
return 0;
}
int test_binary(Toy_Bucket** bucketHandle) {
//test binary add (term); also covers subtract
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "1 + 2;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_BINARY ||
ast->block.child->binary.flag != TOY_AST_FLAG_ADD ||
ast->block.child->binary.left == NULL ||
ast->block.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 1 ||
ast->block.child->binary.right == NULL ||
ast->block.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 2)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary add '1 + 2' (term)\n" TOY_CC_RESET);
return -1;
}
}
//test binary multiply (factor); also covers divide and modulo
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "3 * 5;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_BINARY ||
ast->block.child->binary.flag != TOY_AST_FLAG_MULTIPLY ||
ast->block.child->binary.left == NULL ||
ast->block.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 3 ||
ast->block.child->binary.right == NULL ||
ast->block.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 5)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary multiply '3 * 5' (factor)\n" TOY_CC_RESET);
return -1;
}
}
return 0;
}
int test_aggregate(Toy_Bucket** bucketHandle) {
//test collections (do it first, because it's used below)
{
char* source = "1, 2, 3;";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.flag != TOY_AST_FLAG_COLLECTION ||
ast->block.child->aggregate.left == NULL ||
ast->block.child->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.left->value.value) != 1 ||
ast->block.child->aggregate.right == NULL ||
ast->block.child->aggregate.right->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.right->aggregate.flag != TOY_AST_FLAG_COLLECTION ||
ast->block.child->aggregate.right->aggregate.left == NULL ||
ast->block.child->aggregate.right->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != 2 ||
ast->block.child->aggregate.right->aggregate.right == NULL ||
ast->block.child->aggregate.right->aggregate.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != 3 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
//test index
{
char* source = "name[0];";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.flag != TOY_AST_FLAG_INDEX ||
ast->block.child->aggregate.left == NULL ||
ast->block.child->aggregate.left->type != TOY_AST_VAR_ACCESS ||
ast->block.child->aggregate.left->varAccess.child == NULL ||
ast->block.child->aggregate.left->varAccess.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->aggregate.left->varAccess.child->value.value) != true ||
TOY_VALUE_AS_STRING(ast->block.child->aggregate.left->varAccess.child->value.value)->info.type != TOY_STRING_NAME ||
strcmp(TOY_VALUE_AS_STRING(ast->block.child->aggregate.left->varAccess.child->value.value)->name.data, "name") != 0 ||
ast->block.child->aggregate.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->value.value) != 0 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
//test index, length
{
char* source = "name[0, 1];";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.flag != TOY_AST_FLAG_INDEX ||
ast->block.child->aggregate.left == NULL ||
ast->block.child->aggregate.left->type != TOY_AST_VAR_ACCESS ||
ast->block.child->aggregate.left->varAccess.child == NULL ||
ast->block.child->aggregate.left->varAccess.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->aggregate.left->varAccess.child->value.value) != true ||
TOY_VALUE_AS_STRING(ast->block.child->aggregate.left->varAccess.child->value.value)->info.type != TOY_STRING_NAME ||
strcmp(TOY_VALUE_AS_STRING(ast->block.child->aggregate.left->varAccess.child->value.value)->name.data, "name") != 0 ||
ast->block.child->aggregate.right->type != TOY_AST_AGGREGATE ||
ast->block.child->aggregate.right->aggregate.flag != TOY_AST_FLAG_COLLECTION ||
ast->block.child->aggregate.right->aggregate.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.left->value.value) != 0 ||
TOY_VALUE_IS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->block.child->aggregate.right->aggregate.right->value.value) != 1 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
return 0;
}
int test_keywords(Toy_Bucket** bucketHandle) {
//test assert
{
char* source = "assert true;";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_ASSERT ||
ast->block.child->assert.child == NULL ||
ast->block.child->assert.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_BOOLEAN(ast->block.child->assert.child->value.value) == false ||
TOY_VALUE_AS_BOOLEAN(ast->block.child->assert.child->value.value) != true ||
ast->block.child->assert.message != NULL)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
//test assert (with message)
{
char* source = "assert true, \"foobar\";";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_ASSERT ||
ast->block.child->assert.child == NULL ||
ast->block.child->assert.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_BOOLEAN(ast->block.child->assert.child->value.value) == false ||
TOY_VALUE_AS_BOOLEAN(ast->block.child->assert.child->value.value) != true ||
ast->block.child->assert.message == NULL ||
ast->block.child->assert.message->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->assert.message->value.value) == false ||
TOY_VALUE_AS_STRING(ast->block.child->assert.message->value.value)->info.type != TOY_STRING_LEAF ||
strncmp(TOY_VALUE_AS_STRING(ast->block.child->assert.message->value.value)->leaf.data, "foo", 3) != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
//test print (tested first, to I can use it later)
{
char* source = "print \"foo\";";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_PRINT ||
ast->block.child->print.child == NULL ||
ast->block.child->print.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->print.child->value.value) == false ||
TOY_VALUE_AS_STRING(ast->block.child->print.child->value.value)->info.type != TOY_STRING_LEAF ||
strncmp(TOY_VALUE_AS_STRING(ast->block.child->print.child->value.value)->leaf.data, "foo", 3) != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
//test if-then
{
char* source = "if (true) { print \"foo\"; }";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_IF_THEN_ELSE ||
ast->block.child->ifThenElse.condBranch == NULL ||
ast->block.child->ifThenElse.condBranch->type != TOY_AST_VALUE ||
TOY_VALUE_IS_BOOLEAN(ast->block.child->ifThenElse.condBranch->value.value) == false ||
TOY_VALUE_AS_BOOLEAN(ast->block.child->ifThenElse.condBranch->value.value) != true ||
ast->block.child->ifThenElse.thenBranch == NULL ||
ast->block.child->ifThenElse.thenBranch->type != TOY_AST_BLOCK ||
ast->block.child->ifThenElse.thenBranch->block.child == NULL ||
ast->block.child->ifThenElse.thenBranch->block.child->type != TOY_AST_PRINT ||
ast->block.child->ifThenElse.thenBranch->block.child->print.child == NULL ||
ast->block.child->ifThenElse.thenBranch->block.child->print.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->ifThenElse.thenBranch->block.child->print.child->value.value) == false ||
TOY_VALUE_AS_STRING(ast->block.child->ifThenElse.thenBranch->block.child->print.child->value.value)->info.type != TOY_STRING_LEAF ||
strncmp(TOY_VALUE_AS_STRING(ast->block.child->ifThenElse.thenBranch->block.child->print.child->value.value)->leaf.data, "foo", 3) != 0 ||
ast->block.child->ifThenElse.elseBranch != NULL)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
//test if-then-else
{
char* source = "if (true) { print \"foo\"; } else { print \"bar\"; }";
Toy_Ast* ast = makeAstFromSource(bucketHandle, source);
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_IF_THEN_ELSE ||
ast->block.child->ifThenElse.condBranch == NULL ||
ast->block.child->ifThenElse.condBranch->type != TOY_AST_VALUE ||
TOY_VALUE_IS_BOOLEAN(ast->block.child->ifThenElse.condBranch->value.value) == false ||
TOY_VALUE_AS_BOOLEAN(ast->block.child->ifThenElse.condBranch->value.value) != true ||
ast->block.child->ifThenElse.thenBranch == NULL ||
ast->block.child->ifThenElse.thenBranch->type != TOY_AST_BLOCK ||
ast->block.child->ifThenElse.thenBranch->block.child == NULL ||
ast->block.child->ifThenElse.thenBranch->block.child->type != TOY_AST_PRINT ||
ast->block.child->ifThenElse.thenBranch->block.child->print.child == NULL ||
ast->block.child->ifThenElse.thenBranch->block.child->print.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->ifThenElse.thenBranch->block.child->print.child->value.value) == false ||
TOY_VALUE_AS_STRING(ast->block.child->ifThenElse.thenBranch->block.child->print.child->value.value)->info.type != TOY_STRING_LEAF ||
strncmp(TOY_VALUE_AS_STRING(ast->block.child->ifThenElse.thenBranch->block.child->print.child->value.value)->leaf.data, "foo", 3) != 0 ||
ast->block.child->ifThenElse.elseBranch == NULL ||
ast->block.child->ifThenElse.elseBranch->type != TOY_AST_BLOCK ||
ast->block.child->ifThenElse.elseBranch->block.child == NULL ||
ast->block.child->ifThenElse.elseBranch->block.child->type != TOY_AST_PRINT ||
ast->block.child->ifThenElse.elseBranch->block.child->print.child == NULL ||
ast->block.child->ifThenElse.elseBranch->block.child->print.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_STRING(ast->block.child->ifThenElse.elseBranch->block.child->print.child->value.value) == false ||
TOY_VALUE_AS_STRING(ast->block.child->ifThenElse.elseBranch->block.child->print.child->value.value)->info.type != TOY_STRING_LEAF ||
strncmp(TOY_VALUE_AS_STRING(ast->block.child->ifThenElse.elseBranch->block.child->print.child->value.value)->leaf.data, "bar", 3) != 0 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser, source: %s\n" TOY_CC_RESET, source);
return -1;
}
}
return 0;
}
int test_precedence(Toy_Bucket** bucketHandle) {
//test term-factor precedence
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "1 * 2 + 3 * 4;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_BINARY ||
ast->block.child->binary.flag != TOY_AST_FLAG_ADD ||
ast->block.child->binary.left == NULL ||
ast->block.child->binary.left->type != TOY_AST_BINARY ||
ast->block.child->binary.left->binary.flag != TOY_AST_FLAG_MULTIPLY ||
ast->block.child->binary.left->binary.left == NULL ||
ast->block.child->binary.left->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->value.value) != 1 ||
ast->block.child->binary.left->binary.right == NULL ||
ast->block.child->binary.left->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.right->value.value) != 2 ||
ast->block.child->binary.right == NULL ||
ast->block.child->binary.right->type != TOY_AST_BINARY ||
ast->block.child->binary.right->binary.flag != TOY_AST_FLAG_MULTIPLY ||
ast->block.child->binary.right->binary.left == NULL ||
ast->block.child->binary.right->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->binary.left->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->binary.left->value.value) != 3 ||
ast->block.child->binary.right->binary.right == NULL ||
ast->block.child->binary.right->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->binary.right->value.value) != 4)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser precedence '1 * 2 + 3 * 4' (term-factor)\n" TOY_CC_RESET);
return -1;
}
}
//test left-recrusive precedence
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "1 + 2 + 3 + 4 + 5 + 6;");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_BINARY ||
ast->block.child->binary.flag != TOY_AST_FLAG_ADD ||
// start from the right and work backwards
ast->block.child->binary.right == NULL ||
ast->block.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 6 ||
ast->block.child->binary.left == NULL ||
ast->block.child->binary.left->type != TOY_AST_BINARY ||
ast->block.child->binary.left->binary.right == NULL ||
ast->block.child->binary.left->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.right->value.value) != 5 ||
ast->block.child->binary.left->binary.left == NULL ||
ast->block.child->binary.left->binary.left->type != TOY_AST_BINARY ||
ast->block.child->binary.left->binary.left->binary.right == NULL ||
ast->block.child->binary.left->binary.left->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.right->value.value) != 4 ||
ast->block.child->binary.left->binary.left->binary.left == NULL ||
ast->block.child->binary.left->binary.left->binary.left->type != TOY_AST_BINARY ||
ast->block.child->binary.left->binary.left->binary.left->binary.right == NULL ||
ast->block.child->binary.left->binary.left->binary.left->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.right->value.value) != 3 ||
ast->block.child->binary.left->binary.left->binary.left->binary.left == NULL ||
ast->block.child->binary.left->binary.left->binary.left->binary.left->type != TOY_AST_BINARY ||
ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right == NULL ||
ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.right->value.value) != 2 ||
ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left == NULL ||
ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->binary.left->binary.left->binary.left->binary.left->value.value) != 1)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser precedence '1 + 2 + 3 + 4 + 5 + 6' (left-recursive)\n" TOY_CC_RESET);
return -1;
}
}
//test group precedence
{
Toy_Ast* ast = makeAstFromSource(bucketHandle, "(1 + 2) * (3 + 4);");
//check if it worked
if (
ast == NULL ||
ast->type != TOY_AST_BLOCK ||
ast->block.child == NULL ||
ast->block.child->type != TOY_AST_BINARY ||
ast->block.child->binary.flag != TOY_AST_FLAG_MULTIPLY ||
ast->block.child->binary.left == NULL ||
ast->block.child->binary.left->type != TOY_AST_GROUP ||
ast->block.child->binary.left->group.child->type != TOY_AST_BINARY ||
ast->block.child->binary.left->group.child->binary.flag != TOY_AST_FLAG_ADD ||
ast->block.child->binary.left->group.child->binary.left == NULL ||
ast->block.child->binary.left->group.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->group.child->binary.left->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->group.child->binary.left->value.value) != 1 ||
ast->block.child->binary.left->group.child->binary.right == NULL ||
ast->block.child->binary.left->group.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->group.child->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->group.child->binary.right->value.value) != 2 ||
ast->block.child->binary.right == NULL ||
ast->block.child->binary.right->type != TOY_AST_GROUP ||
ast->block.child->binary.right->group.child->type != TOY_AST_BINARY ||
ast->block.child->binary.right->group.child->binary.flag != TOY_AST_FLAG_ADD ||
ast->block.child->binary.right->group.child->binary.left == NULL ||
ast->block.child->binary.right->group.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->group.child->binary.left->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->group.child->binary.left->value.value) != 3 ||
ast->block.child->binary.right->group.child->binary.right == NULL ||
ast->block.child->binary.right->group.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->group.child->binary.right->value.value) == false ||
TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->group.child->binary.right->value.value) != 4)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser precedence '(1 + 2) * (3 + 4)' (group)\n" TOY_CC_RESET);
return -1;
}
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_simple_empty_parsers(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_var_declare(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_values(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_unary(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_binary(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_aggregate(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_keywords(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_precedence(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,112 +0,0 @@
#include "toy_print.h"
#include "toy_console_colors.h"
#include <stdio.h>
int counter = 0;
void count(const char* msg) {
(void)msg;
counter++;
}
int test_callbacks(void) {
//set a custom print callback, invoke it, and reset
{
//setup
Toy_setPrintCallback(count);
//invoke
Toy_print("");
//check
if (counter != 1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to set print callback\n" TOY_CC_RESET);
return -1;
}
//reset and retry
Toy_resetPrintCallback();
Toy_print("");
if (counter != 1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reset print callback\n" TOY_CC_RESET);
return -1;
}
//cleanup
counter = 0;
}
//set a custom error callback, invoke it, and reset
{
//setup
Toy_setErrorCallback(count);
//invoke
Toy_error("");
//check
if (counter != 1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to set error callback\n" TOY_CC_RESET);
return -1;
}
//reset and retry
Toy_resetErrorCallback();
Toy_error("");
if (counter != 1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reset error callback\n" TOY_CC_RESET);
return -1;
}
//cleanup
counter = 0;
}
//set a custom assert failure callback, invoke it, and reset
{
//setup
Toy_setAssertFailureCallback(count);
//invoke
Toy_assertFailure("");
//check
if (counter != 1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to set assert failure callback\n" TOY_CC_RESET);
return -1;
}
//reset and retry
Toy_resetAssertFailureCallback();
Toy_assertFailure("");
if (counter != 1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reset assert failure callback\n" TOY_CC_RESET);
return -1;
}
//cleanup
counter = 0;
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_callbacks();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,463 +0,0 @@
#include "toy_scope.h"
#include "toy_console_colors.h"
#include "toy_bucket.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int test_scope_allocation(void) {
//allocate and free a scope
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Scope* scope = Toy_pushScope(&bucket, NULL);
//check
if (scope == NULL ||
scope->next != NULL ||
scope->table == NULL ||
scope->table->capacity != 8 ||
scope->refCount != 1 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a Toy_Scope\n" TOY_CC_RESET);
Toy_popScope(scope);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_popScope(scope);
Toy_freeBucket(&bucket);
}
//allocate and free a list of scopes
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
//run
Toy_Scope* scope = NULL;
for (int i = 0; i < 5; i++) {
scope = Toy_pushScope(&bucket, scope);
}
//check
if (
scope == NULL ||
scope->next == NULL ||
scope->table == NULL ||
scope->table->capacity != 8 ||
scope->refCount != 1 ||
scope->next->next == NULL ||
scope->next->table == NULL ||
scope->next->table->capacity != 8 ||
scope->next->refCount != 2 ||
scope->next->next->next == NULL ||
scope->next->next->table == NULL ||
scope->next->next->table->capacity != 8 ||
scope->next->next->refCount != 3 ||
scope->next->next->next->next == NULL ||
scope->next->next->next->table == NULL ||
scope->next->next->next->table->capacity != 8 ||
scope->next->next->next->refCount != 4 ||
scope->next->next->next->next->next != NULL ||
scope->next->next->next->next->table == NULL ||
scope->next->next->next->next->table->capacity != 8 ||
scope->next->next->next->next->refCount != 5 || //refCount includes all ancestors
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a list of Toy_Scope\n" TOY_CC_RESET);
while (scope) {
scope = Toy_popScope(scope);
}
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
while (scope) {
scope = Toy_popScope(scope);
}
Toy_freeBucket(&bucket);
}
//ensure pops work correctly
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
//run
Toy_Scope* scope = NULL;
for (int i = 0; i < 5; i++) {
scope = Toy_pushScope(&bucket, scope);
}
for (int i = 0; i < 2; i++) {
scope = Toy_popScope(scope);
}
//check
if (
scope == NULL ||
scope->next == NULL ||
scope->table == NULL ||
scope->table->capacity != 8 ||
scope->refCount != 1 ||
scope->next->next == NULL ||
scope->next->table == NULL ||
scope->next->table->capacity != 8 ||
scope->next->refCount != 2 ||
scope->next->next->next != NULL ||
scope->next->next->table == NULL ||
scope->next->next->table->capacity != 8 ||
scope->next->next->refCount != 3 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate and free a list of Toy_Scope\n" TOY_CC_RESET);
while (scope) {
scope = Toy_popScope(scope);
}
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
while (scope) {
scope = Toy_popScope(scope);
}
Toy_freeBucket(&bucket);
}
//branched scope references
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
//run
Toy_Scope* scopeBase = Toy_pushScope(&bucket, NULL);
Toy_Scope* scopeA = Toy_pushScope(&bucket, scopeBase);
Toy_Scope* scopeB = Toy_pushScope(&bucket, scopeBase);
//check
if (
scopeBase == NULL ||
scopeBase->next != NULL ||
scopeBase->table == NULL ||
scopeBase->table->capacity != 8 ||
scopeBase->refCount != 3 ||
scopeA == NULL ||
scopeA->next != scopeBase ||
scopeA->table == NULL ||
scopeA->table->capacity != 8 ||
scopeA->refCount != 1 ||
scopeB == NULL ||
scopeB->next != scopeBase ||
scopeB->table == NULL ||
scopeB->table->capacity != 8 ||
scopeB->refCount != 1 ||
scopeA->next != scopeB->next || //double check
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate branched scopes\n" TOY_CC_RESET);
Toy_popScope(scopeB);
Toy_popScope(scopeA);
Toy_popScope(scopeBase);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_popScope(scopeB);
Toy_popScope(scopeA);
Toy_popScope(scopeBase);
Toy_freeBucket(&bucket);
}
//deallocate ancestors correctly
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
//run
Toy_Scope* scopeA = Toy_pushScope(&bucket, NULL);
Toy_Scope* scopeB = Toy_pushScope(&bucket, scopeA);
Toy_Scope* scopeC = Toy_pushScope(&bucket, scopeB);
Toy_popScope(scopeB);
//check
if (
scopeA == NULL ||
scopeA->next != NULL ||
scopeA->table == NULL ||
scopeA->table->capacity != 8 ||
scopeA->refCount != 2 ||
//scopeB still exists in memory until scopeC is popped
scopeB == NULL ||
scopeB->next != scopeA ||
scopeB->table == NULL ||
scopeB->table->capacity != 8 ||
scopeB->refCount != 1 ||
scopeC == NULL ||
scopeC->next != scopeB ||
scopeC->table == NULL ||
scopeC->table->capacity != 8 ||
scopeC->refCount != 1 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to deallocate ancestors scopes correctly\n" TOY_CC_RESET);
Toy_popScope(scopeC);
Toy_popScope(scopeA);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_popScope(scopeC);
Toy_popScope(scopeA);
Toy_freeBucket(&bucket);
}
return 0;
}
int test_scope_elements(void) {
//allocate, access and assign an element
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Scope* scope = Toy_pushScope(&bucket, NULL);
Toy_String* hello1 = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_ANY, false);
Toy_String* hello2 = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_ANY, false);
//check nothing is here
if (Toy_isDeclaredScope(scope, hello2)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected entry found in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello2);
Toy_freeString(hello1);
Toy_popScope(scope);
Toy_freeBucket(&bucket);
return -1;
}
//declare and access values
Toy_declareScope(scope, hello1, TOY_VALUE_FROM_INTEGER(42));
if (!Toy_isDeclaredScope(scope, hello2)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected missing entry in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello2);
Toy_freeString(hello1);
Toy_popScope(scope);
Toy_freeBucket(&bucket);
return -1;
}
Toy_Value* result = Toy_accessScopeAsPointer(scope, hello2);
//check integer
if (scope == NULL ||
scope->next != NULL ||
scope->table == NULL ||
scope->table->capacity != 8 ||
scope->refCount != 1 ||
TOY_VALUE_IS_INTEGER(*result) != true ||
TOY_VALUE_AS_INTEGER(*result) != 42 ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to declare in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello2);
Toy_freeString(hello1);
Toy_popScope(scope);
Toy_freeBucket(&bucket);
return -1;
}
//assign values
Toy_assignScope(scope, hello1, TOY_VALUE_FROM_FLOAT(3.1415f));
Toy_Value* resultTwo = Toy_accessScopeAsPointer(scope, hello2);
//check float
if (scope == NULL ||
scope->next != NULL ||
scope->table == NULL ||
scope->table->capacity != 8 ||
scope->refCount != 1 ||
TOY_VALUE_IS_FLOAT(*resultTwo) != true ||
TOY_VALUE_AS_FLOAT(*resultTwo) != 3.1415f ||
false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to assign in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello2);
Toy_freeString(hello1);
Toy_popScope(scope);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeString(hello2);
Toy_freeString(hello1);
Toy_popScope(scope);
Toy_freeBucket(&bucket);
}
//find an entry in an ancestor scope
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Scope* scope = Toy_pushScope(&bucket, NULL);
Toy_String* hello = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_ANY, false);
//declare and push
Toy_declareScope(scope, hello, TOY_VALUE_FROM_INTEGER(42));
scope = Toy_pushScope(&bucket, scope);
scope = Toy_pushScope(&bucket, scope);
{
//check it's accessible
Toy_Value* result1 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(*result1) != true ||
TOY_VALUE_AS_INTEGER(*result1) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to access from an ancestor Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
while ((scope = Toy_popScope(scope)) != NULL) /* */;
Toy_freeBucket(&bucket);
return -1;
}
}
Toy_declareScope(scope, hello, TOY_VALUE_FROM_FLOAT(3.1415f));
{
//check it's shadowed correctly
Toy_Value* result2 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_FLOAT(*result2) != true ||
TOY_VALUE_AS_FLOAT(*result2) != 3.1415f)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to shadow an entry in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
while ((scope = Toy_popScope(scope)) != NULL) /* */;
Toy_freeBucket(&bucket);
return -1;
}
}
scope = Toy_popScope(scope);
{
//check it's recovered correctly
Toy_Value* result3 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(*result3) != true ||
TOY_VALUE_AS_INTEGER(*result3) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to recover an entry in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
while ((scope = Toy_popScope(scope)) != NULL) /* */;
Toy_freeBucket(&bucket);
return -1;
}
}
Toy_assignScope(scope, hello, TOY_VALUE_FROM_INTEGER(8891));
{
//check it's assigned correctly
Toy_Value* result4 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(*result4) != true ||
TOY_VALUE_AS_INTEGER(*result4) != 8891)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to assign to an ancestor in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
while ((scope = Toy_popScope(scope)) != NULL) /* */;
Toy_freeBucket(&bucket);
return -1;
}
}
scope = Toy_popScope(scope);
{
//check it's in the correct state
Toy_Value* result5 = Toy_accessScopeAsPointer(scope, hello);
if (TOY_VALUE_IS_INTEGER(*result5) != true ||
TOY_VALUE_AS_INTEGER(*result5) != 8891)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to access an altered entry of an ancestor in Toy_Scope\n" TOY_CC_RESET);
Toy_freeString(hello);
while ((scope = Toy_popScope(scope)) != NULL) /* */;
Toy_freeBucket(&bucket);
return -1;
}
}
//cleanup
Toy_freeString(hello);
while ((scope = Toy_popScope(scope)) != NULL) /* */;
Toy_freeBucket(&bucket);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_scope_allocation();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_scope_elements();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,150 +0,0 @@
#include "toy_stack.h"
#include "toy_console_colors.h"
#include <stdio.h>
int test_stack_basics(void) {
//allocate and free the stack
{
Toy_Stack* stack = Toy_allocateStack();
//check if it worked
if (
stack == NULL ||
stack->capacity != 8 ||
stack->count != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to allocate Toy_Stack\n" TOY_CC_RESET);
Toy_freeStack(stack);
return -1;
}
Toy_freeStack(stack);
}
//push, peek and pop stack
{
Toy_Stack* stack = Toy_allocateStack();
//check if it worked (push)
Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(42));
Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(69));
Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(420));
if (
stack == NULL ||
stack->capacity != 8 ||
stack->count != 3)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to push Toy_Stack\n" TOY_CC_RESET);
Toy_freeStack(stack);
return -1;
}
//check if it worked (peek)
Toy_Value top1 = Toy_peekStack(&stack);
if (
TOY_VALUE_IS_INTEGER(top1) != true ||
TOY_VALUE_AS_INTEGER(top1) != 420)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to peek Toy_Stack\n" TOY_CC_RESET);
Toy_freeStack(stack);
return -1;
}
//check if it worked (pop)
Toy_Value top2 = Toy_popStack(&stack);
if (
stack == NULL ||
stack->capacity != 8 ||
stack->count != 2 ||
TOY_VALUE_IS_INTEGER(top2) != true ||
TOY_VALUE_AS_INTEGER(top2) != 420)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop Toy_Stack\n" TOY_CC_RESET);
Toy_freeStack(stack);
return -1;
}
//check if it worked (post-pop peek)
Toy_Value top3 = Toy_peekStack(&stack);
if (
TOY_VALUE_IS_INTEGER(top3) != true ||
TOY_VALUE_AS_INTEGER(top3) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop then peek Toy_Stack\n" TOY_CC_RESET);
Toy_freeStack(stack);
return -1;
}
Toy_freeStack(stack);
}
return 0;
}
int test_stack_stress(void) {
//stress the stack
{
Toy_Stack* stack = Toy_allocateStack();
//push 500 values
for (int i = 0; i < 500; i++) {
Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(i));
}
//check if it worked
if (
stack == NULL ||
stack->capacity != 512 ||
stack->count != 500)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to stress push the Toy_Stack\n" TOY_CC_RESET);
Toy_freeStack(stack);
return -1;
}
//pop each value
while(stack->count > 0) {
Toy_popStack(&stack); //ignore the results
}
//check if it worked
if (
stack == NULL ||
stack->capacity != TOY_STACK_INITIAL_CAPACITY || //reset to initial capacity
stack->count != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to stress pop the Toy_Stack\n" TOY_CC_RESET);
Toy_freeStack(stack);
return -1;
}
//cleanup
Toy_freeStack(stack);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_stack_basics();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_stack_stress();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,889 +0,0 @@
#include "toy_string.h"
#include "toy_console_colors.h"
#include "toy_bucket.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int test_sizeof_string_64bit(void) {
//test for the correct size
{
if (sizeof(Toy_String) != 32) {
fprintf(stderr, TOY_CC_ERROR "ERROR: 'Toy_String' is an unexpected size in memory: expected 32, found %d \n" TOY_CC_RESET, (int)sizeof(Toy_String));
return -1;
}
}
return 0;
}
int test_sizeof_string_32bit(void) {
//test for the correct size
{
if (sizeof(Toy_String) != 24) {
fprintf(stderr, TOY_CC_ERROR "ERROR: 'Toy_String' is an unexpected size in memory: expected 24, found %d \n" TOY_CC_RESET, (int)sizeof(Toy_String));
return -1;
}
}
return 0;
}
int test_string_allocation(void) {
//allocate a single string from a c-string
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
const char* cstring = "Hello world";
Toy_String* str = Toy_createString(&bucket, cstring);
//check
if (str->info.type != TOY_STRING_LEAF ||
str->info.length != 11 ||
str->info.refCount != 1 ||
strcmp(str->leaf.data, "Hello world") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a Toy_String with a private bucket\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
//free the string
Toy_freeString(str);
//inspect the bucket
if (bucket->capacity != 1024 ||
bucket->count != sizeof(Toy_String) + 12 ||
bucket->next != NULL)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected bucket state after a string was freed\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
//inspect the (now freed) string's memory
if (Toy_getStringRefCount(str) != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected string state after it was freed\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
Toy_freeBucket(&bucket);
}
//copy and deep copy a string
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
const char* cstring = "Hello world";
Toy_String* str = Toy_createString(&bucket, cstring);
//shallow and deep
Toy_String* shallow = Toy_copyString(str);
Toy_String* deep = Toy_deepCopyString(&bucket, str);
if (str != shallow ||
str == deep ||
shallow->info.refCount != 2 ||
deep->info.refCount != 1 ||
strcmp(shallow->leaf.data, deep->leaf.data) != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to copy a string correctly\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
Toy_freeBucket(&bucket);
}
//copy and deep copy a name string
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
const char* cstring = "Hello world";
Toy_String* str = Toy_createNameStringLength(&bucket, cstring, strlen(cstring), TOY_VALUE_UNKNOWN, false);
//shallow and deep
Toy_String* shallow = Toy_copyString(str);
Toy_String* deep = Toy_deepCopyString(&bucket, str);
if (str != shallow ||
str == deep ||
shallow->info.refCount != 2 ||
deep->info.refCount != 1 ||
strcmp(shallow->name.data, deep->name.data) != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to copy a name string correctly\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
Toy_freeBucket(&bucket);
}
//allocate a zero-length string
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
const char* cstring = "";
Toy_String* str = Toy_createString(&bucket, cstring);
//check
if (str->info.type != TOY_STRING_LEAF ||
str->info.length != 0 ||
str->info.refCount != 1 ||
strcmp(str->leaf.data, "") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a Toy_String with zero length\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
//free the string
Toy_freeString(str);
Toy_freeBucket(&bucket);
}
return 0;
}
int test_string_concatenation(void) {
//one big bucket o' fun
Toy_Bucket* bucket = Toy_allocateBucket(1024);
//concatenate two strings, and check the refcounts
{
//setup
Toy_String* first = Toy_createString(&bucket, "Hello ");
Toy_String* second = Toy_createString(&bucket, "world");
//concatenate
Toy_String* result = Toy_concatStrings(&bucket, first, second);
//check the refcounts
if (first->info.refCount != 2 ||
second->info.refCount != 2 ||
result->info.refCount != 1 ||
result->info.length != 11)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state for string refcounts after concatenation\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
//clean up the separate strings
Toy_freeString(first);
Toy_freeString(second);
//check the refcounts again
if (first->info.refCount != 1 ||
second->info.refCount != 1 ||
result->info.refCount != 1 ||
result->info.length != 11)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state for string refcounts after concatenation and free\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
//clean up
Toy_freeString(result);
}
//concatenate two strings, and check the resulting buffer
{
//setup
Toy_String* first = Toy_createString(&bucket, "Hello ");
Toy_String* second = Toy_createString(&bucket, "world");
//concatenate
Toy_String* result = Toy_concatStrings(&bucket, first, second);
char* buffer = Toy_getStringRawBuffer(result);
//check the refcounts
if (strlen(buffer) != 11 ||
strncmp(buffer, "Hello world", 11) != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to get the raw buffer from concatenated string\n" TOY_CC_RESET);
free(buffer);
Toy_freeBucket(&bucket);
return -1;
}
free(buffer);
Toy_freeString(result);
Toy_freeString(first);
Toy_freeString(second);
}
Toy_freeBucket(&bucket);
return 0;
}
int test_string_with_stressed_bucket(void) {
//how much is that dog in the window?
{
//test data: 36 characters total, 44 with spaces
char* testData[] = {
"the",
"quick",
"brown",
"fox",
"jumped", //longest word: 6 characters
"over",
"the",
"lazy",
"dog", //9 entries long
NULL,
};
//setup
Toy_Bucket* bucket = Toy_allocateBucket(128);//deliberately too much data for one bucket
//stress
Toy_String* str = Toy_createString(&bucket, testData[0]);
Toy_String* ptr = str;
for (int i = 1; testData[i]; i++) {
str = Toy_concatStrings(&bucket, str, Toy_createString(&bucket, testData[i]));
}
//check
if (ptr->info.refCount != 9 ||
str->info.length != 36)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state of the string after stress test\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
//grab the buffer
char* buffer = Toy_getStringRawBuffer(str);
if (strncmp(buffer, "thequickbrownfoxjumpedoverthelazydog", 36) != 0 ||
strlen(buffer) != 36)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state of the raw buffer after string stress test: '%s'\n" TOY_CC_RESET, buffer);
free(buffer);
Toy_freeBucket(&bucket);
return -1;
}
if (bucket->next == NULL) //just to make sure
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state of the bucket after string stress test\n" TOY_CC_RESET);
free(buffer);
Toy_freeBucket(&bucket);
return -1;
}
//clean up
free(buffer);
Toy_freeBucket(&bucket);
}
//
return 0;
}
int test_string_equality(void) {
//simple string equality (no concats)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_createString(&bucket, "Hello world");
Toy_String* helloWorldTwo = Toy_createString(&bucket, "Hello world");
Toy_String* helloEveryone = Toy_createString(&bucket, "Hello everyone");
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch
if ((result = Toy_compareStrings(helloWorldOne, helloEveryone)) == 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality '%s' != '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check match (same object)
if ((result = Toy_compareStrings(helloWorldOne, helloWorldOne)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldOne);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality '%s' == '%s' is incorrect, found %s (these are the same object)\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//string equality (with concat left)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_concatStrings(&bucket, Toy_createString(&bucket, "Hello "), Toy_createString(&bucket, "world"));
Toy_String* helloWorldTwo = Toy_createString(&bucket, "Hello world");
Toy_String* helloEveryone = Toy_createString(&bucket, "Hello everyone");
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat left equality '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch
if ((result = Toy_compareStrings(helloWorldOne, helloEveryone)) == 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat left equality '%s' != '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check match (same object)
if ((result = Toy_compareStrings(helloWorldOne, helloWorldOne)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldOne);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat left equality '%s' == '%s' is incorrect, found %s (these are the same object)\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//string equality (with concat right)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_createString(&bucket, "Hello world");
Toy_String* helloWorldTwo = Toy_concatStrings(&bucket, Toy_createString(&bucket, "Hello "), Toy_createString(&bucket, "world"));
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat right equality '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//string equality (with concat both)
{
//setup - these concat points are deliberately different
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_concatStrings(&bucket, Toy_createString(&bucket, "Hello "), Toy_createString(&bucket, "world"));
Toy_String* helloWorldTwo = Toy_concatStrings(&bucket, Toy_createString(&bucket, "Hello"), Toy_createString(&bucket, " world"));
Toy_String* helloEveryone = Toy_concatStrings(&bucket, Toy_createString(&bucket, "Hell"), Toy_createString(&bucket, "world"));
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat both equality '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch
if ((result = Toy_compareStrings(helloWorldOne, helloEveryone)) == 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat both equality '%s' != '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//string equality (with concat arbitrary)
{
//setup - The quick brown fox jumps over the lazy dog.
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_concatStrings(&bucket,
Toy_createString(&bucket, "The quick brown "),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, "fox jumps o"),
Toy_createString(&bucket, "ver the lazy dog.")
)
);
Toy_String* helloWorldTwo = Toy_concatStrings(&bucket,
Toy_concatStrings(&bucket,
Toy_createString(&bucket, "The quick brown fox"),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, " jumps ove"),
Toy_createString(&bucket, "r th")
)
),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, "e lazy dog"),
Toy_createString(&bucket, ".")
)
);
Toy_String* helloEveryone = Toy_concatStrings(&bucket,
Toy_createString(&bucket, "The quick brown fox jumps over "),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, "the lazy "),
Toy_createString(&bucket, "reddit mod.")
)
);
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat arbitrary equality '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch
if ((result = Toy_compareStrings(helloWorldOne, helloEveryone)) == 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: String concat arbitrary equality '%s' != '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//string equality (empty strings, no concats)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_createString(&bucket, "");
Toy_String* helloWorldTwo = Toy_createString(&bucket, "");
Toy_String* helloEveryone = Toy_createString(&bucket, "Hello everyone");
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality empty '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch
if ((result = Toy_compareStrings(helloWorldOne, helloEveryone)) == 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality empty '%s' != '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch (same object)
if ((result = Toy_compareStrings(helloWorldOne, helloWorldOne)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldOne);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality empty '%s' == '%s' is incorrect, found %s (these are the same object)\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//string equality (empty strings, deep concats)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_concatStrings(&bucket,
Toy_createString(&bucket, ""),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, ""),
Toy_createString(&bucket, "")
)
);
Toy_String* helloWorldTwo = Toy_concatStrings(&bucket,
Toy_concatStrings(&bucket,
Toy_createString(&bucket, ""),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, ""),
Toy_createString(&bucket, "")
)
),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, ""),
Toy_createString(&bucket, "")
)
);
Toy_String* helloEveryone = Toy_concatStrings(&bucket,
Toy_createString(&bucket, "The quick brown fox jumps over "),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, "the lazy "),
Toy_createString(&bucket, "reddit mod.")
)
);
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality empty with concats '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch
if ((result = Toy_compareStrings(helloWorldOne, helloEveryone)) == 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality empty with concats '%s' != '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check match (same object)
if ((result = Toy_compareStrings(helloWorldOne, helloWorldOne)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldOne);
fprintf(stderr, TOY_CC_ERROR "ERROR: String equality empty with concats '%s' == '%s' is incorrect, found %s (these are the same object)\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//simple name equality
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloWorldOne = Toy_createNameStringLength(&bucket, "Hello world", strlen("Hello world"), TOY_VALUE_UNKNOWN, false);
Toy_String* helloWorldTwo = Toy_createNameStringLength(&bucket, "Hello world", strlen("Hello world"), TOY_VALUE_UNKNOWN, false);
Toy_String* helloEveryone = Toy_createNameStringLength(&bucket, "Hello everyone", strlen("Hello everyone"), TOY_VALUE_UNKNOWN, false);
int result = 0; //for print the errors
//check match
if ((result = Toy_compareStrings(helloWorldOne, helloWorldTwo)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldTwo);
fprintf(stderr, TOY_CC_ERROR "ERROR: Name string equality '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check mismatch
if ((result = Toy_compareStrings(helloWorldOne, helloEveryone)) == 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: Name string equality '%s' != '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check match (same object)
if ((result = Toy_compareStrings(helloWorldOne, helloWorldOne)) != 0)
{
char* leftBuffer = Toy_getStringRawBuffer(helloWorldOne);
char* rightBuffer = Toy_getStringRawBuffer(helloWorldOne);
fprintf(stderr, TOY_CC_ERROR "ERROR: Name string equality '%s' == '%s' is incorrect, found %s (these are the same object)\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
return 0;
}
int test_string_diffs(void) {
//simple string diffs (no concats)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* helloEveryone = Toy_createString(&bucket, "Hello everyone");
Toy_String* helloUniverse = Toy_createString(&bucket, "Hello universe");
int result = 0; //for print the errors
//check diff
if (((result = Toy_compareStrings(helloEveryone, helloUniverse)) < 0) == false)
{
char* leftBuffer = Toy_getStringRawBuffer(helloEveryone);
char* rightBuffer = Toy_getStringRawBuffer(helloUniverse);
fprintf(stderr, TOY_CC_ERROR "ERROR: String diff '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check diff (reversed)
if (((result = Toy_compareStrings(helloUniverse, helloEveryone)) > 0) == false)
{
char* leftBuffer = Toy_getStringRawBuffer(helloUniverse);
char* rightBuffer = Toy_getStringRawBuffer(helloEveryone);
fprintf(stderr, TOY_CC_ERROR "ERROR: String diff '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//string diffs (with concat arbitrary)
{
//setup - The quick brown fox jumps over the lazy dog.
Toy_Bucket* bucket = Toy_allocateBucket(1024);
Toy_String* pangram = Toy_concatStrings(&bucket,
Toy_createString(&bucket, "The quick brown "),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, "fox jumps o"),
Toy_createString(&bucket, "ver the lazy dog.")
)
);
Toy_String* neckbeard = Toy_concatStrings(&bucket,
Toy_createString(&bucket, "The quick brown fox jumps over "),
Toy_concatStrings(&bucket,
Toy_createString(&bucket, "the lazy "),
Toy_createString(&bucket, "reddit mod.")
)
);
int result = 0; //for print the errors
//check diff
if (((result = Toy_compareStrings(pangram, neckbeard)) < 0) == false)
{
char* leftBuffer = Toy_getStringRawBuffer(pangram);
char* rightBuffer = Toy_getStringRawBuffer(neckbeard);
fprintf(stderr, TOY_CC_ERROR "ERROR: String diff '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//check diff (reversed)
if (((result = Toy_compareStrings(neckbeard, pangram)) > 0) == false)
{
char* leftBuffer = Toy_getStringRawBuffer(neckbeard);
char* rightBuffer = Toy_getStringRawBuffer(pangram);
fprintf(stderr, TOY_CC_ERROR "ERROR: String diff '%s' == '%s' is incorrect, found %s\n" TOY_CC_RESET, leftBuffer, rightBuffer, result < 0 ? "<" : result == 0 ? "==" : ">");
free(leftBuffer);
free(rightBuffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
return 0;
}
int test_string_fragmenting(void) {
//allocate a long string
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(128); //deliberately too small for the cstring
//445 charaters
const char* cstring = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
Toy_String* str = Toy_createString(&bucket, cstring);
//check
if (str->info.type != TOY_STRING_NODE ||
str->info.length != 445 ||
str->info.refCount != 1)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to fragment a string within Toy_String\n" TOY_CC_RESET);
Toy_freeString(str);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeString(str);
Toy_freeBucket(&bucket);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
#if TOY_BITNESS == 64
res = test_sizeof_string_64bit();
#else
res = test_sizeof_string_32bit();
#endif
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_string_allocation();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_string_concatenation();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_string_with_stressed_bucket();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_string_equality();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_string_diffs();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_string_fragmenting();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,653 +0,0 @@
#include "toy_table.h"
#include "toy_console_colors.h"
#include <stdio.h>
int test_table_allocation(void) {
//allocate and free a table
{
//setup
Toy_Table* table = Toy_allocateTable();
//check
if (table == NULL)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a table\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
return 0;
}
int test_table_simple_insert_lookup_and_remove(void) {
//simple insert
{
//setup
Toy_Table* table = Toy_allocateTable();
Toy_Value key = TOY_VALUE_FROM_INTEGER(1);
Toy_Value value = TOY_VALUE_FROM_INTEGER(42);
//insert
Toy_insertTable(&table, key, value);
if (table == NULL ||
table->capacity != 8 ||
table->count != 1)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to insert into a table\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//lookup
Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(1));
//check lookup
if (table == NULL ||
table->capacity != 8 ||
table->count != 1 ||
TOY_VALUE_AS_INTEGER(result) != 42)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to lookup from a table\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//remove
Toy_removeTable(&table, TOY_VALUE_FROM_INTEGER(1));
//check remove
if (table == NULL ||
table->capacity != 8 ||
table->count != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to remove from a table\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
return 0;
}
//macros are a godsend
#define TEST_ENTRY_STATE(i, k, v, p) \
TOY_VALUE_IS_INTEGER(table->data[i].key) != true || \
TOY_VALUE_AS_INTEGER(table->data[i].key) != k || \
TOY_VALUE_IS_INTEGER(table->data[i].value) != true || \
TOY_VALUE_AS_INTEGER(table->data[i].value) != v || \
table->data[i].psl != p
int test_table_contents_no_expansion(void) {
//single insert
{
//setup
Toy_Table* table = Toy_allocateTable();
//insert a key and value
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42));
//check the state
if (table == NULL ||
table->capacity != 8 ||
table->count != 1 ||
TEST_ENTRY_STATE(7, 1, 42, 0)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, single insert {1:42}\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//multiple inserts, no collisions
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(2), TOY_VALUE_FROM_INTEGER(69)); //hash: 0
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(420)); //hash: 5
//check the state
if (table == NULL ||
table->capacity != 8 ||
table->count != 3 ||
TEST_ENTRY_STATE(7, 1, 42, 0) ||
TEST_ENTRY_STATE(0, 2, 69, 0) ||
TEST_ENTRY_STATE(5, 3, 420, 0)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, multiple inserts, no collisions\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//multiple inserts, with collisions
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(5), TOY_VALUE_FROM_INTEGER(42)); //hash: 2
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(69)); //hash: 2
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(37), TOY_VALUE_FROM_INTEGER(420)); //hash: 2
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(65), TOY_VALUE_FROM_INTEGER(8891)); //hash: 2
//check the state
if (table == NULL ||
table->capacity != 8 ||
table->count != 4 ||
TEST_ENTRY_STATE(2, 5, 42, 0) ||
TEST_ENTRY_STATE(3, 19, 69, 1) ||
TEST_ENTRY_STATE(4, 37, 420, 2) ||
TEST_ENTRY_STATE(5, 65, 8891, 3)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//multiple inserts, with collisions, modulo wrap
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(14), TOY_VALUE_FROM_INTEGER(69)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(76), TOY_VALUE_FROM_INTEGER(420)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(80), TOY_VALUE_FROM_INTEGER(8891)); //hash: 7
//check the state
if (table == NULL ||
table->capacity != 8 ||
table->count != 4 ||
TEST_ENTRY_STATE(7, 1, 42, 0) ||
TEST_ENTRY_STATE(0, 14, 69, 1) ||
TEST_ENTRY_STATE(1, 76, 420, 2) ||
TEST_ENTRY_STATE(2, 80, 8891, 3)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions, modulo wrap\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//lookup, with collisions, modulo wrap
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(69)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(420)); //hash: 7
//lookup
Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(33));
//check the state
if (table == NULL ||
table->capacity != 8 ||
table->count != 3 ||
TOY_VALUE_IS_INTEGER(result) != true ||
TOY_VALUE_AS_INTEGER(result) != 69
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Bad result from table lookup with collisions and modulo wrap\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//multiple inserts, with collisions, modulo wrap, psl overlap
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(69)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(420)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(8891)); //hash: 1
//check the state
if (table == NULL ||
table->capacity != 8 ||
table->count != 4 ||
TEST_ENTRY_STATE(7, 17, 42, 0) ||
TEST_ENTRY_STATE(0, 33, 69, 1) ||
TEST_ENTRY_STATE(1, 70, 420, 2) ||
TEST_ENTRY_STATE(2, 35, 8891, 1)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions, modulo wrap, psl overlap\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//multiple inserts, with collisions, modulo wrap, psl overlap, psl shift
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(69)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(420)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(8891)); //hash: 1
//remove
Toy_removeTable(&table, TOY_VALUE_FROM_INTEGER(33));
//check the state
if (table == NULL ||
table->capacity != 8 ||
table->count != 3 ||
TEST_ENTRY_STATE(7, 17, 42, 0) ||
TEST_ENTRY_STATE(0, 70, 420, 1) ||
TEST_ENTRY_STATE(1, 35, 8891, 0)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions, modulo wrap, psl overlap, psl shift\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
return 0;
}
int test_table_contents_with_expansions(void) {
//simple expansion
{
//setup
Toy_Table* table = Toy_allocateTable();
//insert a key and value
for (int i = 0; i < 20; i++) {
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(i), TOY_VALUE_FROM_INTEGER(42));
}
//check the state
if (table == NULL ||
table->capacity != 32 ||
table->count != 20
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to expand table capacity\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//expansion, multiple inserts, no collisions
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(0), TOY_VALUE_FROM_INTEGER(42)); //hash: 0
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(42)); //hash: 1
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(42)); //hash: 2
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(8), TOY_VALUE_FROM_INTEGER(42)); //hash: 3
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(10), TOY_VALUE_FROM_INTEGER(42)); //hash: 4
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(42)); //hash: 5
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(28), TOY_VALUE_FROM_INTEGER(42)); //hash: 6
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(6), TOY_VALUE_FROM_INTEGER(42)); //hash: 8
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(93), TOY_VALUE_FROM_INTEGER(42)); //hash: 9
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(85), TOY_VALUE_FROM_INTEGER(42)); //hash: 10
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(9), TOY_VALUE_FROM_INTEGER(42)); //hash: 11
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(11), TOY_VALUE_FROM_INTEGER(42)); //hash: 12
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(22), TOY_VALUE_FROM_INTEGER(42)); //hash: 13
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(13), TOY_VALUE_FROM_INTEGER(42)); //hash: 14
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 15
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(43), TOY_VALUE_FROM_INTEGER(42)); //hash: 16
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(4), TOY_VALUE_FROM_INTEGER(42)); //hash: 17
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(5), TOY_VALUE_FROM_INTEGER(42)); //hash: 18
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(7), TOY_VALUE_FROM_INTEGER(42)); //hash: 19
//check the state
if (table == NULL ||
table->capacity != 32 ||
table->count != 20 ||
//effectively, check that each key was placed in the correct position after the expansion
TEST_ENTRY_STATE(0, 0, 42, 0) ||
TEST_ENTRY_STATE(1, 35, 42, 0) ||
TEST_ENTRY_STATE(2, 19, 42, 0) ||
TEST_ENTRY_STATE(3, 8, 42, 0) ||
TEST_ENTRY_STATE(4, 10, 42, 0) ||
TEST_ENTRY_STATE(5, 3, 42, 0) ||
TEST_ENTRY_STATE(6, 28, 42, 0) ||
TEST_ENTRY_STATE(7, 1, 42, 0) ||
TEST_ENTRY_STATE(8, 6, 42, 0) ||
TEST_ENTRY_STATE(9, 93, 42, 0) ||
TEST_ENTRY_STATE(10, 85, 42, 0) ||
TEST_ENTRY_STATE(11, 9, 42, 0) ||
TEST_ENTRY_STATE(12, 11, 42, 0) ||
TEST_ENTRY_STATE(13, 22, 42, 0) ||
TEST_ENTRY_STATE(14, 13, 42, 0) ||
TEST_ENTRY_STATE(15, 17, 42, 0) ||
TEST_ENTRY_STATE(16, 43, 42, 0) ||
TEST_ENTRY_STATE(17, 4, 42, 0) ||
TEST_ENTRY_STATE(18, 5, 42, 0) ||
TEST_ENTRY_STATE(19, 7, 42, 0)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data after expansion, multiple inserts, no collisions\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//multiple inserts, with collisions
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(0), TOY_VALUE_FROM_INTEGER(42)); //hash: 0
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(42)); //hash: 1
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(42)); //hash: 2
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(8), TOY_VALUE_FROM_INTEGER(42)); //hash: 3
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(10), TOY_VALUE_FROM_INTEGER(42)); //hash: 4
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(42)); //hash: 5
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(28), TOY_VALUE_FROM_INTEGER(42)); //hash: 6
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(6), TOY_VALUE_FROM_INTEGER(42)); //hash: 8
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(93), TOY_VALUE_FROM_INTEGER(42)); //hash: 9
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(85), TOY_VALUE_FROM_INTEGER(42)); //hash: 10
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(9), TOY_VALUE_FROM_INTEGER(42)); //hash: 11
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(11), TOY_VALUE_FROM_INTEGER(42)); //hash: 12
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(22), TOY_VALUE_FROM_INTEGER(42)); //hash: 13
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(13), TOY_VALUE_FROM_INTEGER(42)); //hash: 14
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 15
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(43), TOY_VALUE_FROM_INTEGER(42)); //hash: 16
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(4), TOY_VALUE_FROM_INTEGER(42)); //hash: 17
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(5), TOY_VALUE_FROM_INTEGER(42)); //hash: 18
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(7), TOY_VALUE_FROM_INTEGER(42)); //hash: 19
//insert one more
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(42)); //hash: 15
//check the state
if (table == NULL ||
table->capacity != 32 ||
table->count != 21 ||
TEST_ENTRY_STATE(0, 0, 42, 0) ||
TEST_ENTRY_STATE(1, 35, 42, 0) ||
TEST_ENTRY_STATE(2, 19, 42, 0) ||
TEST_ENTRY_STATE(3, 8, 42, 0) ||
TEST_ENTRY_STATE(4, 10, 42, 0) ||
TEST_ENTRY_STATE(5, 3, 42, 0) ||
TEST_ENTRY_STATE(6, 28, 42, 0) ||
TEST_ENTRY_STATE(7, 1, 42, 0) ||
TEST_ENTRY_STATE(8, 6, 42, 0) ||
TEST_ENTRY_STATE(9, 93, 42, 0) ||
TEST_ENTRY_STATE(10, 85, 42, 0) ||
TEST_ENTRY_STATE(11, 9, 42, 0) ||
TEST_ENTRY_STATE(12, 11, 42, 0) ||
TEST_ENTRY_STATE(13, 22, 42, 0) ||
TEST_ENTRY_STATE(14, 13, 42, 0) ||
TEST_ENTRY_STATE(15, 17, 42, 0) ||
TEST_ENTRY_STATE(16, 70, 42, 1) || //the collision
TEST_ENTRY_STATE(17, 43, 42, 1) ||
TEST_ENTRY_STATE(18, 4, 42, 1) ||
TEST_ENTRY_STATE(19, 5, 42, 1) ||
TEST_ENTRY_STATE(20, 7, 42, 1)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data after expansion, muiltiple inserts, with collisions\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//multiple inserts, with collisions, modulo wrap
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(123), TOY_VALUE_FROM_INTEGER(42)); //hash: 20
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(42), TOY_VALUE_FROM_INTEGER(42)); //hash: 21
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(132), TOY_VALUE_FROM_INTEGER(42)); //hash: 22
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(34), TOY_VALUE_FROM_INTEGER(42)); //hash: 23
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(2), TOY_VALUE_FROM_INTEGER(42)); //hash: 24
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(32), TOY_VALUE_FROM_INTEGER(42)); //hash: 25
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(21), TOY_VALUE_FROM_INTEGER(42)); //hash: 26
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(44), TOY_VALUE_FROM_INTEGER(42)); //hash: 27
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(104), TOY_VALUE_FROM_INTEGER(42)); //hash: 28
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(15), TOY_VALUE_FROM_INTEGER(42)); //hash: 29
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(57), TOY_VALUE_FROM_INTEGER(42)); //hash: 30
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(42)); //hash: 31
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(0), TOY_VALUE_FROM_INTEGER(42)); //hash: 32
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(42)); //hash: 33
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(42)); //hash: 34
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(8), TOY_VALUE_FROM_INTEGER(42)); //hash: 35
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(10), TOY_VALUE_FROM_INTEGER(42)); //hash: 36
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(42)); //hash: 37
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(28), TOY_VALUE_FROM_INTEGER(42)); //hash: 38
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 39
//insert one more
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(79), TOY_VALUE_FROM_INTEGER(42)); //hash: 23
//check the state
if (table == NULL ||
table->capacity != 32 ||
table->count != 21 ||
TEST_ENTRY_STATE(20, 123, 42, 0) ||
TEST_ENTRY_STATE(21, 42, 42, 0) ||
TEST_ENTRY_STATE(22, 132, 42, 0) ||
TEST_ENTRY_STATE(23, 34, 42, 0) ||
TEST_ENTRY_STATE(24, 79, 42, 1) || //the collision
TEST_ENTRY_STATE(25, 2, 42, 1) ||
TEST_ENTRY_STATE(26, 32, 42, 1) ||
TEST_ENTRY_STATE(27, 21, 42, 1) ||
TEST_ENTRY_STATE(28, 44, 42, 1) ||
TEST_ENTRY_STATE(29, 104, 42, 1) ||
TEST_ENTRY_STATE(30, 15, 42, 1) ||
TEST_ENTRY_STATE(31, 57, 42, 1) ||
TEST_ENTRY_STATE(0, 33, 42, 1) ||
TEST_ENTRY_STATE(1, 0, 42, 1) ||
TEST_ENTRY_STATE(2, 35, 42, 1) ||
TEST_ENTRY_STATE(3, 19, 42, 1) ||
TEST_ENTRY_STATE(4, 8, 42, 1) ||
TEST_ENTRY_STATE(5, 10, 42, 1) ||
TEST_ENTRY_STATE(6, 3, 42, 1) ||
TEST_ENTRY_STATE(7, 28, 42, 1) ||
TEST_ENTRY_STATE(8, 1, 42, 1)
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data after expansion, muiltiple inserts, with collisions, modulo wrap\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//lookup, with collisions, modulo wrap
{
//setup
Toy_Table* table = Toy_allocateTable();
//inserts
for (int i = 0; i < 20; i++) { //enough to expand
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(i), TOY_VALUE_FROM_INTEGER(100 - i));
}
//lookup
Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(15));
//check the state
if (table == NULL ||
table->capacity != 32 ||
table->count != 20 ||
TOY_VALUE_IS_INTEGER(result) != true ||
TOY_VALUE_AS_INTEGER(result) != 85
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Bad result from table lookup after expansion, with collisions and modulo wrap\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
//Skipped: multiple inserts, with collisions, modulo wrap, psl overlap
//Skipped: multiple inserts, with collisions, modulo wrap, psl overlap, psl shift
//Note: since psl overlap and psl shift both work without expansion, I'm leaving these tests unimplemented.
return 0;
}
int test_table_expansions_under_stress(void) {
//multiple expansions, find one value
{
//setup
Toy_Table* table = Toy_allocateTable();
int top = 300;
//insert keys and values
for (int i = 0; i < 400; i++) {
Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(i), TOY_VALUE_FROM_INTEGER(top - i));
}
Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(265));
//check the state
if (table == NULL ||
table->capacity != 512 ||
table->count != 400 ||
TOY_VALUE_IS_INTEGER(result) != true ||
TOY_VALUE_AS_INTEGER(result) != 35
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Table expansions under stress failed\n" TOY_CC_RESET);
Toy_freeTable(table);
return -1;
}
//free
Toy_freeTable(table);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_table_allocation();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_table_simple_insert_lookup_and_remove();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_table_contents_no_expansion();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_table_contents_with_expansions();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_table_expansions_under_stress();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,601 +0,0 @@
#include "toy_value.h"
#include "toy_console_colors.h"
#include "toy_bucket.h"
#include "toy_string.h"
#include "toy_array.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//util macros
#define TOY_ARRAY_EXPAND(array) (array = (array != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray(array, (array)->capacity * TOY_ARRAY_EXPANSION_RATE) : array))
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array), (array)->data[(array)->count++] = (value))
int test_value_creation(void) {
//test for the correct size
{
#if TOY_BITNESS == 64
if (sizeof(Toy_Value) != 16)
#else
if (sizeof(Toy_Value) != 8)
#endif
{
fprintf(stderr, TOY_CC_ERROR "ERROR: 'Toy_Value' is an unexpected size in memory, expected %d found %d\n" TOY_CC_RESET, TOY_BITNESS, (int)sizeof(Toy_Value));
return -1;
}
}
//test creating a null
{
Toy_Value v = TOY_VALUE_FROM_NULL();
if (!TOY_VALUE_IS_NULL(v)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: creating a 'null' value failed\n" TOY_CC_RESET);
return -1;
}
}
//test creating booleans
{
Toy_Value t = TOY_VALUE_FROM_BOOLEAN(true);
Toy_Value f = TOY_VALUE_FROM_BOOLEAN(false);
if (!Toy_checkValueIsTruthy(t) || Toy_checkValueIsTruthy(f)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: 'boolean' value failed\n" TOY_CC_RESET);
return -1;
}
}
//test creating strings
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value greeting = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "Hello world!"));
if (TOY_VALUE_IS_STRING(greeting) == false ||
TOY_VALUE_AS_STRING(greeting)->info.type != TOY_STRING_LEAF ||
strcmp(TOY_VALUE_AS_STRING(greeting)->leaf.data, "Hello world!") != 0
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: 'string' value failed\n" TOY_CC_RESET);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//test creating arrays
{
//setup
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(42));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(69));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(8891));
Toy_Value v = TOY_VALUE_FROM_ARRAY(array);
if (TOY_VALUE_AS_ARRAY(v) == false ||
TOY_VALUE_AS_ARRAY(v)->capacity != 8 ||
TOY_VALUE_AS_ARRAY(v)->count != 3 ||
TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[0]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[0]) != 42 ||
TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[1]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[1]) != 69 ||
TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[2]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[2]) != 8891
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: 'array' value failed\n" TOY_CC_RESET);
Toy_resizeArray(array, 0);
return -1;
}
//cleanup
Toy_resizeArray(array, 0);
}
return 0;
}
int test_value_copying(void) {
//test simple integer copy
{
Toy_Value original = TOY_VALUE_FROM_INTEGER(42);
Toy_Value result = Toy_copyValue(original);
if (!TOY_VALUE_IS_INTEGER(result) ||
TOY_VALUE_AS_INTEGER(result) != 42
) {
fprintf(stderr, TOY_CC_ERROR "ERROR: copy an integer value failed\n" TOY_CC_RESET);
return -1;
}
}
//test string copy
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value original = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "Hello world!"));
Toy_Value result = Toy_copyValue(original);
if (TOY_VALUE_IS_STRING(result) == false ||
TOY_VALUE_AS_STRING(result)->info.type != TOY_STRING_LEAF ||
strcmp(TOY_VALUE_AS_STRING(result)->leaf.data, "Hello world!") != 0 ||
TOY_VALUE_AS_STRING(result)->info.refCount != 2
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: copy a string value failed\n" TOY_CC_RESET);
Toy_freeValue(original);
Toy_freeValue(result);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeValue(original);
Toy_freeValue(result);
Toy_freeBucket(&bucket);
}
//test copy arrays
{
//setup
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(42));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(69));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(8891));
Toy_Value original = TOY_VALUE_FROM_ARRAY(array);
Toy_Value result = Toy_copyValue(original);
if (TOY_VALUE_AS_ARRAY(result) == false ||
TOY_VALUE_AS_ARRAY(result)->capacity != 8 ||
TOY_VALUE_AS_ARRAY(result)->count != 3 ||
TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(result)->data[0]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(result)->data[0]) != 42 ||
TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(result)->data[1]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(result)->data[1]) != 69 ||
TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(result)->data[2]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(result)->data[2]) != 8891
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: copy an array value failed\n" TOY_CC_RESET);
Toy_freeValue(original);
Toy_freeValue(result);
return -1;
}
//cleanup
Toy_freeValue(original);
Toy_freeValue(result);
}
//arrays can't be compared
return 0;
}
int test_value_hashing(void) {
//test value hashing
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
//values
Toy_Value n = TOY_VALUE_FROM_NULL();
Toy_Value t = TOY_VALUE_FROM_BOOLEAN(true);
Toy_Value f = TOY_VALUE_FROM_BOOLEAN(false);
Toy_Value i = TOY_VALUE_FROM_INTEGER(42);
//skip float
Toy_Value s = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "Hello world"));
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(42));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(69));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(8891));
Toy_Value a = TOY_VALUE_FROM_ARRAY(array);
if (Toy_hashValue(n) != 0 ||
Toy_hashValue(t) != 1 ||
Toy_hashValue(f) != 0 ||
Toy_hashValue(i) != 4147366645 ||
Toy_hashValue(s) != 994097935 ||
TOY_VALUE_AS_STRING(s)->info.cachedHash == 0 ||
Toy_hashValue(a) != 2544446955
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected hash of a value\n" TOY_CC_RESET);
Toy_resizeArray(array, 0);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_resizeArray(array, 0);
Toy_freeBucket(&bucket);
}
return 0;
}
int test_value_equality(void) {
//test value equality
{
Toy_Value answer = TOY_VALUE_FROM_INTEGER(42);
Toy_Value question = TOY_VALUE_FROM_INTEGER(42);
Toy_Value nice = TOY_VALUE_FROM_INTEGER(69);
if (Toy_checkValuesAreEqual(answer, question) != true) {
fprintf(stderr, TOY_CC_ERROR "ERROR: value equality check failed, expected true\n" TOY_CC_RESET);
return -1;
}
if (Toy_checkValuesAreEqual(answer, nice) != false) {
fprintf(stderr, TOY_CC_ERROR "ERROR: value equality check failed, expected false\n" TOY_CC_RESET);
return -1;
}
}
//again with strings
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value answer = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both"));
Toy_Value question = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "why is a raven like a writing desk?"));
Toy_Value duplicate = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both"));
if (Toy_checkValuesAreEqual(answer, duplicate) != true) {
fprintf(stderr, TOY_CC_ERROR "ERROR: string value equality check failed, expected true\n" TOY_CC_RESET);
return -1;
}
if (Toy_checkValuesAreEqual(answer, question) != false) {
fprintf(stderr, TOY_CC_ERROR "ERROR: string value equality check failed, expected false\n" TOY_CC_RESET);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//again with arrays
{
//setup
Toy_Array* array1 = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
TOY_ARRAY_PUSHBACK(array1, TOY_VALUE_FROM_INTEGER(42));
TOY_ARRAY_PUSHBACK(array1, TOY_VALUE_FROM_INTEGER(69));
TOY_ARRAY_PUSHBACK(array1, TOY_VALUE_FROM_INTEGER(8891));
Toy_Value value1 = TOY_VALUE_FROM_ARRAY(array1);
Toy_Array* array2 = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
TOY_ARRAY_PUSHBACK(array2, TOY_VALUE_FROM_INTEGER(42));
TOY_ARRAY_PUSHBACK(array2, TOY_VALUE_FROM_INTEGER(69));
TOY_ARRAY_PUSHBACK(array2, TOY_VALUE_FROM_INTEGER(8891));
Toy_Value value2 = TOY_VALUE_FROM_ARRAY(array2);
if (Toy_checkValuesAreEqual(value1, value2) != true)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: array values are not equal\n" TOY_CC_RESET);
Toy_freeValue(value1);
Toy_freeValue(value2);
return -1;
}
//cleanup
Toy_freeValue(value1);
Toy_freeValue(value2);
}
return 0;
}
int test_value_comparison(void) {
//test value comparable
{
Toy_Value answer = TOY_VALUE_FROM_INTEGER(42);
Toy_Value question = TOY_VALUE_FROM_INTEGER(42);
Toy_Value nope = TOY_VALUE_FROM_NULL();
if (Toy_checkValuesAreComparable(answer, question) != true) {
fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison check failed, expected true\n" TOY_CC_RESET);
return -1;
}
if (Toy_checkValuesAreComparable(answer, nope) != false) {
fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison check failed, expected false\n" TOY_CC_RESET);
return -1;
}
}
//test comparison
{
Toy_Value answer = TOY_VALUE_FROM_INTEGER(42);
Toy_Value question = TOY_VALUE_FROM_INTEGER(42);
Toy_Value nice = TOY_VALUE_FROM_INTEGER(69);
if (Toy_compareValues(answer, question) != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison failed, expected 0\n" TOY_CC_RESET);
return -1;
}
if (Toy_compareValues(answer, nice) == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: value comparison failed, expected not 0\n" TOY_CC_RESET);
return -1;
}
}
//again with strings
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value answer = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both"));
Toy_Value question = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "why is a raven like a writing desk?"));
Toy_Value duplicate = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "poe wrote on both"));
if (Toy_compareValues(answer, duplicate) != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: string value comparison failed, expected 0\n" TOY_CC_RESET);
return -1;
}
if (Toy_compareValues(answer, question) == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: string value comparison failed, expected not 0\n" TOY_CC_RESET);
return -1;
}
//cleanup
Toy_freeBucket(&bucket);
}
//arrays can't be compared
return 0;
}
int test_value_stringify(void) {
//stringify null
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value value = TOY_VALUE_FROM_NULL();
//run
Toy_String* string = Toy_stringifyValue(&bucket, value);
//check
if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "<null>") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify 'null' failed\n" TOY_CC_RESET);
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
}
//stringify boolean (true)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value value = TOY_VALUE_FROM_BOOLEAN(true);
//run
Toy_String* string = Toy_stringifyValue(&bucket, value);
//check
if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "<true>") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'true' failed\n" TOY_CC_RESET);
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
}
//stringify boolean (false)
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value value = TOY_VALUE_FROM_BOOLEAN(false);
//run
Toy_String* string = Toy_stringifyValue(&bucket, value);
//check
if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "<false>") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify boolean 'false' failed\n" TOY_CC_RESET);
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
}
//stringify integer
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value value = TOY_VALUE_FROM_INTEGER(42);
//run
Toy_String* string = Toy_stringifyValue(&bucket, value);
//check
if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "42") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify integer '42' failed\n" TOY_CC_RESET);
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
}
//stringify float
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value value = TOY_VALUE_FROM_FLOAT(3.1415f);
//run
Toy_String* string = Toy_stringifyValue(&bucket, value);
//check
if (string->info.type != TOY_STRING_LEAF ||
strcmp(string->leaf.data, "3.1415") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify float '3.1415' failed\n" TOY_CC_RESET);
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
Toy_freeString(string);
Toy_freeValue(value);
Toy_freeBucket(&bucket);
}
//stringify strings
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Value value = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "Hello world!"));
Toy_String* string = Toy_stringifyValue(&bucket, value);
char* buffer = Toy_getStringRawBuffer(string);
if (buffer == NULL || strcmp(buffer, "Hello world!") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify string 'Hello world!' failed\n" TOY_CC_RESET);
free(buffer);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
free(buffer);
Toy_freeBucket(&bucket);
}
//stringify array
{
//setup
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
//setup
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY);
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(42));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(69));
TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(8891));
Toy_Value value = TOY_VALUE_FROM_ARRAY(array);
Toy_String* string = Toy_stringifyValue(&bucket, value);
char* buffer = Toy_getStringRawBuffer(string);
if (buffer == NULL || strcmp(buffer, "[42,69,8891]") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: stringify array '[42,69,8891]' failed\n" TOY_CC_RESET);
free(buffer);
array = Toy_resizeArray(array, 0);
Toy_freeBucket(&bucket);
return -1;
}
//cleanup
free(buffer);
Toy_resizeArray(array, 0);
Toy_freeBucket(&bucket);
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
res = test_value_creation();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_value_copying();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_value_hashing();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_value_equality();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_value_comparison();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
res = test_value_stringify();
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
//TODO: references
return total;
}

View File

@@ -1,911 +0,0 @@
#include "toy_vm.h"
#include "toy_console_colors.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_module_compiler.h"
#include "toy_print.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//utils
unsigned char* makeCodeFromSource(Toy_Bucket** bucketHandle, const char* source) {
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
return Toy_compileModule(ast);
}
//tests
int test_setup_and_teardown(Toy_Bucket** bucketHandle) {
//basic init & quit
{
//generate module for testing
const char* source = "(1 + 2) * (3 + 4);";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//check the module was loaded correctly
if (
vm.codeAddr != 24 || // module header is: total, jumps, prams, data, subs, codeAddr
vm.jumpsCount != 0 ||
vm.paramCount != 0 ||
vm.dataCount != 0 ||
vm.subsCount != 0
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to setup and teadown Toy_VM, source: %s\n" TOY_CC_RESET, source);
//cleanup and return
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//don't run it this time, simply teadown
Toy_freeVM(&vm);
free(buffer);
}
return 0;
}
int test_simple_execution(Toy_Bucket** bucketHandle) {
//test execution
{
//generate bytecode for testing
const char* source = "(1 + 2) * (3 + 4);";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check the final state of the stack
if (vm.stack == NULL ||
vm.stack->count != 1 ||
TOY_VALUE_IS_INTEGER( Toy_peekStack(&vm.stack) ) != true ||
TOY_VALUE_AS_INTEGER( Toy_peekStack(&vm.stack) ) != 21
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM', source: %s\n" TOY_CC_RESET, source);
//cleanup and return
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_freeVM(&vm);
free(buffer);
}
return 0;
}
int test_opcode_not_equal(Toy_Bucket** bucketHandle) {
//testing a specific opcode; '!=' is compressed into a single word, so lets check it works
{
//generate bytecode for testing
const char* source = "3 != 5;";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check the final state of the stack
if (vm.stack == NULL ||
vm.stack->count != 1 ||
TOY_VALUE_IS_BOOLEAN( Toy_peekStack(&vm.stack) ) != true ||
TOY_VALUE_AS_BOOLEAN( Toy_peekStack(&vm.stack) ) != true
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM', source: %s\n" TOY_CC_RESET, source);
//cleanup and return
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_freeVM(&vm);
free(buffer);
}
return 0;
}
static char* callbackUtilReceived = NULL;
static void callbackUtil(const char* msg) {
if (msg != NULL) {
free(callbackUtilReceived);
callbackUtilReceived = (char*)malloc(strlen(msg) + 1);
strcpy(callbackUtilReceived, msg);
}
}
int test_keyword_assert(Toy_Bucket** bucketHandle) {
//test assert true
{
//setup
Toy_setAssertFailureCallback(callbackUtil);
const char* source = "assert true;";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived != NULL)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetAssertFailureCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetAssertFailureCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
//test assert false
{
//setup
Toy_setAssertFailureCallback(callbackUtil);
const char* source = "assert false;";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "assertion failed") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert failure message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetAssertFailureCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetAssertFailureCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
//test assert false with message
{
//setup
Toy_setAssertFailureCallback(callbackUtil);
const char* source = "assert false, \"You passed a false to assert\";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "You passed a false to assert") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert failure message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetAssertFailureCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetAssertFailureCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
return 0;
}
int test_keyword_print(Toy_Bucket** bucketHandle) {
//test print
{
//setup
Toy_setPrintCallback(callbackUtil);
const char* source = "print 42;";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "42") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetPrintCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetPrintCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
//test print with a string
{
//setup
Toy_setPrintCallback(callbackUtil);
const char* source = "print \"Hello world!\";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "Hello world!") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetPrintCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetPrintCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
//test print with a string concat
{
//setup
Toy_setPrintCallback(callbackUtil);
const char* source = "print \"Hello\" .. \"world!\";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "Helloworld!") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetPrintCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetPrintCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
return 0;
}
int test_keyword_ifThenElse(Toy_Bucket** bucketHandle) {
//test if-then (truthy)
{
//setup
Toy_setPrintCallback(callbackUtil);
const char* source = "if (true) print \"hello world\";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "hello world") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetPrintCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetPrintCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
//test if-then (falsy)
{
//setup
Toy_setPrintCallback(callbackUtil);
const char* source = "if (false) print \"hello world\";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived != NULL)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetPrintCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetPrintCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
//test if-then-else (truthy)
{
//setup
Toy_setPrintCallback(callbackUtil);
const char* source = "if (true) print \"hello world\"; else print \"failed\";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "hello world") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetPrintCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetPrintCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
//test if-then-else (falsy)
{
//setup
Toy_setPrintCallback(callbackUtil);
const char* source = "if (false) print \"hello world\"; else print \"failed\";";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
if (callbackUtilReceived == NULL ||
strcmp(callbackUtilReceived, "failed") != 0)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
//cleanup and return
Toy_resetPrintCallback();
free(callbackUtilReceived);
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_resetPrintCallback();
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_freeVM(&vm);
free(buffer);
}
return 0;
}
int test_scope(Toy_Bucket** bucketHandle) {
//test declaration with initial value
{
//generate bytecode for testing
const char* source = "var foobar = 42;";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_ANY, false);
if (vm.stack == NULL ||
vm.stack->count != 0 ||
vm.scope == NULL ||
Toy_isDeclaredScope(vm.scope, key) == false ||
TOY_VALUE_IS_INTEGER(*Toy_accessScopeAsPointer(vm.scope, key)) != true ||
TOY_VALUE_AS_INTEGER(*Toy_accessScopeAsPointer(vm.scope, key)) != 42
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM' when testing scope, source: %s\n" TOY_CC_RESET, source);
//cleanup and return
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_freeVM(&vm);
free(buffer);
}
//test declaration with absent value
{
//generate bytecode for testing
const char* source = "var foobar;";
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
unsigned char* buffer = Toy_compileModule(ast);
Toy_Module module = Toy_parseModule(buffer);
//run the setup
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, &module, false);
//run
Toy_runVM(&vm);
//check
Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_UNKNOWN, false);
if (vm.stack == NULL ||
vm.stack->count != 0 ||
vm.scope == NULL ||
Toy_isDeclaredScope(vm.scope, key) == false ||
TOY_VALUE_IS_NULL(*Toy_accessScopeAsPointer(vm.scope, key)) != true
)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM' when testing scope, source: %s\n" TOY_CC_RESET, source);
//cleanup and return
Toy_freeVM(&vm);
free(buffer);
return -1;
}
//teadown
Toy_freeVM(&vm);
free(buffer);
}
return 0;
}
int test_vm_reuse(Toy_Bucket** bucketHandle) {
//run code in the same vm multiple times
{
Toy_setPrintCallback(callbackUtil);
Toy_VM vm;
Toy_initVM(&vm);
//run 1
unsigned char* buffer1 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
Toy_Module module1 = Toy_parseModule(buffer1);
Toy_bindVM(&vm, &module1, false);
Toy_runVM(&vm);
Toy_resetVM(&vm, true);
if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' found in VM reuse run 1\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL");
//cleanup and return
free(callbackUtilReceived);
callbackUtilReceived = NULL;
free(buffer1);
Toy_freeVM(&vm);
Toy_resetPrintCallback();
return -1;
}
free(buffer1);
//run 2
unsigned char* buffer2 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
Toy_Module module2 = Toy_parseModule(buffer2);
Toy_bindVM(&vm, &module2, true); //preserve during repeated calls
Toy_runVM(&vm);
Toy_resetVM(&vm, true);
if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' found in VM reuse run 2\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL");
//cleanup and return
free(callbackUtilReceived);
callbackUtilReceived = NULL;
free(buffer2);
Toy_freeVM(&vm);
Toy_resetPrintCallback();
return -1;
}
free(buffer2);
//run 3
unsigned char* buffer3 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
Toy_Module module3 = Toy_parseModule(buffer3);
Toy_bindVM(&vm, &module3, true); //preserve during repeated calls
Toy_runVM(&vm);
Toy_resetVM(&vm, true);
if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' found in VM reuse run 3\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL");
//cleanup and return
free(callbackUtilReceived);
callbackUtilReceived = NULL;
free(buffer3);
Toy_freeVM(&vm);
Toy_resetPrintCallback();
return -1;
}
free(buffer3);
//cleanup
Toy_freeVM(&vm);
free(callbackUtilReceived);
callbackUtilReceived = NULL;
Toy_resetPrintCallback();
}
return 0;
}
int main(void) {
//run each test set, returning the total errors given
int total = 0, res = 0;
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_setup_and_teardown(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_simple_execution(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_opcode_not_equal(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_keyword_print(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_keyword_assert(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_keyword_ifThenElse(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_scope(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
{
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_vm_reuse(&bucket);
Toy_freeBucket(&bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}
return total;
}

View File

@@ -1,2 +0,0 @@
set breakpoint pending on

View File

@@ -1,68 +0,0 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
ifeq ($(shell uname),Linux)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),NetBSD)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(OS),Windows_NT)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),Darwin)
LDFLAGS=-Wl,-dead_strip
else
@echo "LDFLAGS set failed - what platform is this?"
endif
#directories
TEST_ROOTDIR=../..
TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR)
TEST_REPLDIR=$(TEST_ROOTDIR)/$(TOY_REPLDIR)
TEST_SCRIPTDIR=.
TEST_OUTDIR=out
TEST_OBJDIR=obj
#file names
TEST_SCRIPTFILES=$(wildcard $(TEST_SCRIPTDIR)/test_*.toy)
TEST_REPLNAME=repl.exe
#build the source and repl, and run
all: source repl run
run: $(TEST_SCRIPTFILES:.toy=.toy-run)
%.toy-run: %.toy
$(TEST_OUTDIR)/$(TEST_REPLNAME) -f ../$< --verbose
#same as above, but with gdb
gdb: source repl run-gdb
run-gdb: $(TEST_SCRIPTFILES:.toy=.toy-gdb)
%.toy-gdb: %.toy
gdb $(TEST_OUTDIR)/$(TEST_REPLNAME) -ix gdb_init -ex=run --batch --return-child-result --args "$(TEST_OUTDIR)/$(TEST_REPLNAME)" "-f" "../$<" "--verbose"
#same as above, but with valgrind
valgrind: source repl run-valgrind
run-valgrind: $(TEST_SCRIPTFILES:.toy=.toy-valgrind)
%.toy-valgrind: %.toy
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes $(TEST_OUTDIR)/$(TEST_REPLNAME) -f ../$< --verbose
#compile the source and repl first
source: $(TEST_OBJDIR) $(TEST_OUTDIR)
$(MAKE) SRC_OUTDIR=../$(TOY_INTEGRATIONSDIR)/$(TEST_OUTDIR) -C $(TEST_SOURCEDIR)
repl: $(TEST_OBJDIR) $(TEST_OUTDIR) source
$(MAKE) REPL_TARGETNAME=$(TEST_REPLNAME) REPL_OUTDIR=../$(TOY_INTEGRATIONSDIR)/$(TEST_OUTDIR) -C $(TEST_REPLDIR)
#util targets
$(TEST_OUTDIR):
mkdir $(TEST_OUTDIR)
$(TEST_OBJDIR):
mkdir $(TEST_OBJDIR)

View File

@@ -1,26 +0,0 @@
//1-D array
var arr = [1, 2, 3];
arr[1] = 6;
assert arr == [1, 6, 3], "1-D array failed";
//we need to go deeper
var barr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
barr[1][1] = 99;
assert barr == [[1, 2, 3],[4,99,6],[7,8,9]], "2-D array failed";
//test trailing commas
var a = [1, 2, 3, ];
print a;
//test empty arrays
var b = [];
print b;

View File

@@ -1,9 +0,0 @@
//these are allowed
/* EMPTY */;
if (true) { }
if (true) pass;
//these are not allowed
// if (true) /* EMPTY */;

View File

@@ -1,14 +0,0 @@
//NOTE: these tests are all passing - failing tests can be found under the 'mustfails' directory
//basic assert statement
assert true;
//assert on a string (tests for it's truthiness)
assert "Hello world";
//assert on a condition
assert 1 < 2;
//assert with an optional message
assert true, "Assertion message";

View File

@@ -1,65 +0,0 @@
//literals
if (true) {
print "Success 1";
}
else {
print "Failure 1";
}
//false literals
if (false) {
print "Failure 2";
}
else {
print "Success 2";
}
//conditionals
if (1 < 2) {
print "Success 3";
}
if (1 > 2) {
print "Failure 3";
}
//variables
var a = 42;
if (a) {
print "Success 4";
}
else {
print "Failure 4";
}
if (a == 42) {
print "Success 5";
}
else {
print "Failure 5";
}
//concatenated strings
if ("foo" .. "bar" == "foobar") {
print "Success 6";
}
else {
print "Failure 6";
}
if ("foobar" == "foo" .. "bar") {
print "Success 7";
}
else {
print "Failure 7";
}
if ("fizz" .. "le" == "fi" .. "zzle") {
print "Success 8";
}
else {
print "Failure 8";
}

View File

@@ -1,46 +0,0 @@
//literals
if (true)
print "Success 1";
else
print "Failure 1";
//false literals
if (false)
print "Failure 2";
else
print "Success 2";
//conditionals
if (1 < 2)
print "Success 3";
if (1 > 2)
print "Failure 3";
//variables
var a = 42;
if (a)
print "Success 4";
else
print "Failure 4";
if (a == 42)
print "Success 5";
else
print "Failure 5";
//concatenated strings
if ("foo" .. "bar" == "foobar")
print "Success 6";
else
print "Failure 6";
if ("foobar" == "foo" .. "bar")
print "Success 7";
else
print "Failure 7";
if ("fizz" .. "le" == "fi" .. "zzle")
print "Success 8";
else
print "Failure 8";

View File

@@ -1,21 +0,0 @@
//basic print statement
print 42;
//print complex expressions
print 3 * 5;
//print a string
print "Hello world!";
//print a concatenated string
print "Hello" .. "world!";
//print with escaped characters
print "\tHello\nworld";
//print from a substring string
print "Hello world"[0,5];
//print from a substring, after a concat
print ("hello" .. "world")[2,6];

View File

@@ -1,132 +0,0 @@
//make sure it works with multiple repititions
//-------------------------
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag1: bool = true;
while (flag1) {
flag1 = false;
continue;
assert false, "continue failed";
}
//-------------------------
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag2: bool = true;
while (flag2) {
flag2 = false;
continue;
assert false, "continue failed";
}
//-------------------------
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag3: bool = true;
while (flag3) {
flag3 = false;
continue;
assert false, "continue failed";
}
//-------------------------
{
//test break
while (true) {
break;
assert false, "break failed";
}
//test continue
var flag4: bool = true;
while (flag4) {
flag4 = false;
continue;
assert false, "continue failed";
}
}
//-------------------------
{
//test break
while (true) {
{
break;
}
assert false, "break failed";
}
//test continue
var flag5: bool = true;
while (flag5) {
flag5 = false;
{
continue;
}
assert false, "continue failed";
}
}
//-------------------------
{
//iteration
var iteration = 0;
while(iteration < 10) {
print iteration;
iteration += 1;
}
}
{
//if and while work together
var count = 1;
while (count <= 10) {
if (count % 2 == 0) {
print "even";
}
else {
print "odd";
}
count += 1;
}
}
//-------------------------
{
//make sure break and continue point to the correct locations
var loops = 0;
while (true) {
if (++loops < 15532) {
continue;
}
break;
}
assert loops == 15532, "Yuki loop failed (break + continue)";
}

View File

@@ -1,17 +0,0 @@
//shadowing
var answer = 42;
print answer; //42
{
var answer = 7;
print answer; //7
}
print answer; //42
//rebinding
var question = 42;
print question; //42
{
var question = question;
print question; //42
}
print question; //42

View File

@@ -1,33 +0,0 @@
//1-D table
var a = ["alpha": 1, "beta": 2, "gamma": 3];
a["beta"] = 6;
print a;
assert a == ["alpha": 1, "beta": 6, "gamma": 3], "1-D tables failed";
//nested
var b = [
"outer": ["inner": true],
"alpha": 1,
"beta": 2,
"gamma": 3
];
print b;
assert b == ["alpha": 1, "beta": 2, "gamma": 3, "outer": ["inner": true]], "nested tables failed";
//test empty tables
var empty = [:];
print empty;
assert empty == [:], "empty tables failed";
//test trailing commas
var trailing = [
"alpha":1,
"beta":2,
"gamma":3,
];
print trailing;
assert trailing == ["alpha": 1, "beta": 2, "gamma": 3], "trailing tables failed";

View File

@@ -1,80 +0,0 @@
//booleans
{
var value = true;
if (value) {
print "correct";
}
else {
assert false;
}
}
{
var value = false;
if (value) {
assert false;
}
else {
print "correct";
}
}
//integers
{
var value: int = 42;
if (value) {
print "correct";
}
else {
assert false;
}
}
{
var value: int = 0;
if (value) {
assert false;
}
else {
print "correct";
}
}
//floats
{
var value: float = 42.8891;
if (value) {
print "correct";
}
else {
assert false;
}
}
{
var value: float = 0;
if (value) {
assert false;
}
else {
print "correct";
}
}
//everything else
{
var value: string = "foobar";
if (value) {
print "correct";
}
else {
assert false;
}
}

View File

@@ -1,160 +0,0 @@
//declare a variable with an initial value
var answer = 42;
//declare a variable without an initial value
var empty;
//assign a previously existing variable
answer = 6 * 9;
//access a variable
answer = answer + 1;
//compound assignments
answer += 5;
answer -= 5;
answer *= 9;
answer /= 2;
answer %= 10;
//equality checks
print 1 == 1; //true
print 1 != 1; //false
//comparison checks
print 1 < 2; //true
print "foo" > "bar"; //true
print 1 < 2; //true
print 1 > 2; //false
print 2 <= 2; //true
print 2 >= 2; //true
print 1 <= 2; //true
print 1 >= 2; //false
//logical checks
print true && true; //true
print true && false; //false
print false && true; //false
print false && false; //false
print true || true; //true
print true || false; //true
print false || true; //true
print false || false; //false
print !true; //false
print !false; //true
//logical AND short-circuits and chained assignments
{
var a = 1;
var b = 2;
var c = a + 1 && b + 2;
assert a == 1, "short circuit 1.1";
assert b == 2, "short circuit 1.2";
assert c == 4, "short circuit 1.3";
}
{
var a = 1;
var b = 2;
var c = a = (a + 1) && b + 2;
assert a == 4, "short circuit 2.1";
assert b == 2, "short circuit 2.2";
assert c == 4, "short circuit 2.3";
}
{
var a = 1;
var b = 2;
var c = a = a + 1 && b + 2;
assert a == 4, "short circuit 3.1";
assert b == 2, "short circuit 3.2";
assert c == 4, "short circuit 3.3";
}
//logical OR short-circuits and chained assignments
{
var a = 1;
var b = 2;
var c = a + 1 || b + 2;
assert a == 1, "short circuit 4.1";
assert b == 2, "short circuit 4.2";
assert c == 2, "short circuit 4.3";
}
{
var a = 1;
var b = 2;
var c = a = (a + 1) || b + 2;
assert a == 2, "short circuit 5.1";
assert b == 2, "short circuit 5.2";
assert c == 2, "short circuit 5.3";
}
{
var a = 1;
var b = 2;
var c = a = a + 1 || b + 2;
assert a == 2, "short circuit 6.1";
assert b == 2, "short circuit 6.2";
assert c == 2, "short circuit 6.3";
}
//types
{
var a: int;
var b: int = 42;
a = 69;
b = 8891;
print a;
print b;
}
//constants
{
var c: int const = 42;
print c;
}
//indexing
{
var s = "Hello" .. "world!";
print s[3, 3];
}
//increment & decrement (prefix)
{
var a = 42;
assert a == 42, "prefix increment & decrement 1.1";
assert ++a == 43, "prefix increment & decrement 1.2";
assert a == 43, "prefix increment & decrement 1.3";
assert --a == 42, "prefix increment & decrement 1.4";
assert a == 42, "prefix increment & decrement 1.5";
}
//increment & decrement (postfix)
{
var a = 42;
assert a == 42, "postfix increment & decrement 1.1";
assert a++ == 42, "postfix increment & decrement 1.2";
assert a == 43, "postfix increment & decrement 1.3";
assert a-- == 43, "postfix increment & decrement 1.4";
assert a == 42, "postfix increment & decrement 1.5";
print a;
}
//TODO: type casting

View File

@@ -1 +0,0 @@
set breakpoint pending on

View File

@@ -1,19 +0,0 @@
//https://www.programiz.com/c-programming/online-compiler/
#include <stdio.h>
static unsigned int hashUInt(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
int main(void) {
//print the index/hash pairs
for (unsigned int i = 0; i < 100; i++) {
unsigned int h = hashUInt(i);
printf("%u: %u %% 8 = %u\n", i, h, h % 8);
}
return 0;
}

View File

@@ -1,23 +0,0 @@
//https://www.programiz.com/c-programming/online-compiler/
#include <stdio.h>
static unsigned int hashUInt(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
int main(void) {
//find the first number with a specific hash, then print the c-code
for (unsigned int h = 0; h < 20; h++) {
for (unsigned int i = 0; i < 100; i++) {
if (hashUInt(i) % 32 == h) {
printf("Toy_insertTable(&table, TOY_VALUE_TO_INTEGER(%d), TOY_VALUE_TO_INTEGER(42)); //hash: %d\n", i, h);
break;
}
}
}
return 0;
}

View File

@@ -1,59 +0,0 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
ifeq ($(shell uname),Linux)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(OS),Windows_NT)
LDFLAGS=-Wl,--gc-sections
else ifeq ($(shell uname),Darwin)
LDFLAGS=-Wl,-dead_strip
else
@echo "LDFLAGS set failed - what platform is this?"
endif
#directories
TEST_SRCDIR=
TEST_OUTDIR=out/
TEST_OBJDIR=obj/
#file names
TEST_SRCFILES=$(wildcard $(TEST_SRCDIR)*.c)
#kick off
all: $(TEST_OBJDIR) $(TEST_OUTDIR) build run
gdb: $(TEST_OBJDIR) $(TEST_OUTDIR) build gdb-run
#build
build: $(TEST_OBJDIR)$(TEST_SRCFILES:.c=.o)
.PRECIOUS: $(TEST_OBJDIR)%.o
$(TEST_OBJDIR)%.o: $(TEST_SRCDIR)%.c
$(CC) -c -o $@ $< $(CFLAGS) -fdata-sections -ffunction-sections
.PRECIOUS: $(TEST_OUTDIR)%.exe
$(TEST_OUTDIR)%.exe: $(TEST_OBJDIR)%.o
$(CC) -o $@ $< $(CFLAGS) $(LIBS) $(LDFLAGS)
#run
run: $(addprefix $(TEST_OUTDIR),$(TEST_SRCFILES:.c=.exe-run))
$(TEST_OUTDIR)%.exe-run: $(TEST_OUTDIR)%.exe
$<
#gdb-run
gdb-run: $(addprefix $(TEST_OUTDIR),$(TEST_SRCFILES:.c=.exe-gdb-run))
$(TEST_OUTDIR)%.exe-gdb-run: $(TEST_OUTDIR)%.exe
gdb $< -ix gdb_init -ex=run --batch --return-child-result
#util targets
$(TEST_OUTDIR):
mkdir $(TEST_OUTDIR)
$(TEST_OBJDIR):
mkdir $(TEST_OBJDIR)

View File

@@ -1,147 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//utilities
#define APPEND(dest, src) \
strncpy((dest) + (strlen(dest)), (src), strlen((src)) + 1);
#if defined(_WIN32) || defined(_WIN64)
#define FLIPSLASH(str) for (int i = 0; str[i]; i++) str[i] = str[i] == '/' ? '\\' : str[i];
#else
#define FLIPSLASH(str) for (int i = 0; str[i]; i++) str[i] = str[i] == '\\' ? '/' : str[i];
#endif
unsigned char* readFile(char* path, int* size) {
//BUGFIX: fix the path based on platform - it might be slower, but it's better than dealing with platform crap
int pathLength = strlen(path);
char realPath[pathLength + 1];
strncpy(realPath, path, pathLength);
realPath[pathLength] = '\0';
FLIPSLASH(realPath);
//open the file
FILE* file = fopen(path, "rb");
if (file == NULL) {
*size = -1; //missing file error
return NULL;
}
//determine the file's length
fseek(file, 0L, SEEK_END);
*size = ftell(file);
rewind(file);
//make some space
unsigned char* buffer = malloc(*size + 1);
if (buffer == NULL) {
fclose(file);
return NULL;
}
//read the file
if (fread(buffer, sizeof(unsigned char), *size, file) < (unsigned int)(*size)) {
fclose(file);
*size = -2; //singal a read error
return NULL;
}
buffer[(*size)++] = '\0';
//clean up and return
fclose(file);
return buffer;
}
int getFilePath(char* dest, const char* src) {
char* p = NULL;
//find the last slash, regardless of platform
p = strrchr(src, '\\');
if (p == NULL) {
p = strrchr(src, '/');
}
if (p == NULL) {
int len = strlen(src);
strncpy(dest, src, len);
return len;
}
//determine length of the path
int len = p - src + 1;
//copy to the dest
strncpy(dest, src, len);
dest[len] = '\0';
return len;
}
int getFileName(char* dest, const char* src) {
char* p = NULL;
//find the last slash, regardless of platform
p = strrchr(src, '\\');
if (p == NULL) {
p = strrchr(src, '/');
}
if (p == NULL) {
int len = strlen(src);
strncpy(dest, src, len);
return len;
}
p++; //skip the slash
//determine length of the file name
int len = strlen(p);
//copy to the dest
strncpy(dest, p, len);
dest[len] = '\0';
return len;
}
int main(void) {
//check the platform
printf("Platform: ");
#if defined(__linux__)
printf("Linux");
#elif defined(_WIN64)
printf("Win64");
#elif defined(_WIN32)
printf("Win32");
#elif defined(__APPLE__)
printf("macOS");
#else
printf("Unknown");
#endif
printf("\n");
//run each test
{
char src[256] = "../folder/file.txt";
char dest[256];
getFilePath(dest, src);
printf("Path: %s\n", dest);
}
{
char src[256] = "../folder/file.txt";
char dest[256];
getFileName(dest, src);
printf("Name: %s\n", dest);
}
{
char src[256] = "../folder/file.txt";
char dest[256];
getFilePath(dest, src);
APPEND(dest, "target.txt");
printf("Target: %s\n", dest);
}
return 0;
}

View File

@@ -1,29 +0,0 @@
name: Standalone Tests
#trigger when these occur
on:
workflow_dispatch:
#These tests are more stand-alone than the others
jobs:
run-test-cases:
continue-on-error: true
strategy:
matrix:
platforms:
- { os: ubuntu-latest, preinstall: sudo apt-get install gdb, gdb_enabled: true }
- { os: windows-latest, preinstall: , gdb_enabled: true }
- { os: macos-latest, preinstall: , gdb_enabled: false }
commands:
- { exec: make -C tests/standalone -k, gdb: false }
- { exec: make -C tests/standalone gdb -k, gdb: true }
runs-on: ${{ matrix.platforms.os }}
steps:
- uses: actions/checkout@v4
- name: Preinstall dependencies
if: (matrix.commands.gdb == true && matrix.platforms.gdb_enabled == false) != true
run: ${{ matrix.platforms.preinstall }}
- name: run the tests
if: (matrix.commands.gdb == true && matrix.platforms.gdb_enabled == false) != true
run: ${{ matrix.commands.exec }}

View File