mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 06:44:07 +10:00
Began cleaning up this project for a soft reboot
This commit is contained in:
69
README.md
69
README.md
@@ -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).
|
||||
|
||||
50
makefile
50
makefile
@@ -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?"
|
||||
|
||||
@@ -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 $@
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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;
|
||||
```
|
||||
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
set breakpoint pending on
|
||||
|
||||
@@ -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 $<
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
set breakpoint pending on
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
@@ -1,9 +0,0 @@
|
||||
//these are allowed
|
||||
/* EMPTY */;
|
||||
if (true) { }
|
||||
if (true) pass;
|
||||
|
||||
|
||||
//these are not allowed
|
||||
// if (true) /* EMPTY */;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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";
|
||||
@@ -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];
|
||||
|
||||
@@ -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)";
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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";
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -1 +0,0 @@
|
||||
set breakpoint pending on
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 }}
|
||||
Reference in New Issue
Block a user