working version

This commit is contained in:
overflowerror 2020-04-17 23:25:56 +02:00
parent e00c1b4a90
commit 4e39ae709f
15 changed files with 1462 additions and 0 deletions

40
Makefile Normal file
View file

@ -0,0 +1,40 @@
CC = gcc
CFLAGS = -std=c99 -Wall -D_POSIX_C_SOURCE=201112L -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -static
LD = gcc
LDFLAGS = -static
LIBFLAGS = -Llibs -Ilibs
OBJS = obj/arrays.o obj/linked.o obj/streams.o obj/strings.o
DEPS = $(OBJS:%.o=%.d)
all: $(BIN_NAME) test
demo: obj/demo.o $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^
test: $(OBJS) obj/do-test.o obj/linked-test.o obj/streams-test.o obj/strings-test.o
$(LD) $(LDFLAGS) -o $@ $^ $(LIBFLAGS) -lviscocity
obj/%-test.o: src/%-test.c obj
$(CC) $(CFLAGS) $(LIBFLAGS) -MMD -c -o $@ $<
valgrind: CFLAGS += -static -g
valgrind: clean test
valgrind --leak-check=yes ./test
-include $(DEPS)
obj/%.o: src/%.c obj
$(CC) $(CFLAGS) -MMD -c -o $@ $<
obj:
@mkdir -p obj
clean:
@echo "Cleaning up..."
@rm -f obj/*.o
@rm -f obj/*.d
@rm -f test
@rm -f demo

65
src/arrays.c Normal file
View file

@ -0,0 +1,65 @@
#include "arrays.h"
#include "streams.h"
typedef struct {
stream_t stream;
raw_t* array;
size_t length;
size_t elementSize;
} arrayStream_t;
static inline arrayStream_t* getCurrentArrayStream() {
return (arrayStream_t*) getCurrentStream();
}
void ptrArrayStreamIterate(predicate_t predicate) {
arrayStream_t* stream = getCurrentArrayStream();
for (size_t i = 0; i < stream->length; i++) {
if (predicate(stream->array[i])) {
break;
}
}
}
void primitiveArrayStreamIterate(predicate_t predicate) {
arrayStream_t* stream = getCurrentArrayStream();
for (size_t i = 0; i < stream->length; i++) {
raw_t data = 0;
for (size_t j = 0; j < stream->elementSize; j++) {
data |= ((unsigned char*) stream->array)[i * stream->elementSize + j] << (j * 8);
}
if (predicate(data)) {
break;
}
}
}
stream_t arrayStream(void* array, size_t size, size_t elementSize) {
arrayStream_t* stream = malloc(sizeof(arrayStream_t));
if (stream == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
skeletonStream(stream);
stream->array = array;
stream->length = size;
stream->elementSize = elementSize;
stream->stream.__iterate = (elementSize == sizeof(void*)) ? &ptrArrayStreamIterate : &primitiveArrayStreamIterate;
return *((stream_t*) stream);
}
stream_t ptrStream(raw_t* array, size_t length) {
return arrayStream(array, length, sizeof(void*));
}
stream_t primitiveStream(void* array, size_t length, size_t primitiveSize) {
return arrayStream(array, length, primitiveSize);
}

17
src/arrays.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef ARRAYS_H
#define ARRAYS_H
#include "streams.h"
typedef struct arrayWithLength {
void* array;
size_t length;
} arrayWithLength_t;
stream_t arrayStream(void*, size_t, size_t);
stream_t ptrStream(raw_t*, size_t);
stream_t primitiveStream(void*, size_t, size_t);
#endif

33
src/demo.c Normal file
View file

@ -0,0 +1,33 @@
#include "functional.h"
#include "streams.h"
#include "strings.h"
#include "arrays.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>
#define ARRAY_LENGTH (5)
int main(int argc, char** argv) {
const char* array[ARRAY_LENGTH] = {
"Hello", "World", "with", "no", "Streams"
};
ptrStream((void*) array, ARRAY_LENGTH)
.peek(qConsumer(obj, printf("%s\n", (char*) obj)))
.filter(stringNotEqualsPredicate("no"))
.map(qFunction(obj, (raw_t) charStream((char*) obj)
.map(qFunction(c, toupper((char) c)))
.collect(stringCollector())
)).forEach(Consumer(obj, {
printf("%s\n", (char*) obj);
free((char*) obj);
}));
return 0;
}

5
src/do-test.c Normal file
View file

@ -0,0 +1,5 @@
#include <viscocity.h>
int main() {
return doTests();
}

76
src/functional.h Normal file
View file

@ -0,0 +1,76 @@
#ifndef FUNCTIONAL_H
#define FUNCTIONAL_H
#include <stdbool.h>
#include <stdlib.h>
#define lambda(r, f) ({r __fn__ f __fn__; })
typedef unsigned long long raw_t;
typedef struct supply {
bool hasMore;
raw_t data;
} supply_t;
typedef raw_t (*function_t)(raw_t);
typedef void (*consumer_t)(raw_t);
typedef void (*biconsumer_t)(raw_t, raw_t);
typedef bool (*predicate_t)(raw_t);
typedef raw_t (*supplier_t)(void);
typedef int (*comparator_t)(raw_t, raw_t);
typedef supply_t (*multisupplier_t)(void);
#define Function(i, f) lambda(raw_t, (raw_t i) f)
#define Consumer(i, f) lambda(void, (raw_t i) f)
#define Predicate(i, f) lambda(bool, (raw_t i) f)
#define BiConsumer(i1, i2, f) lambda(void, (raw_t i1, raw_t i2) f)
#define Supplier(f) lambda(raw_t, () f)
#define Comparator(i1, i2, f) lambda(int, (raw_t i1, raw_t i2) f)
#define qFunction(i, f) Function(i, { return f; })
#define qConsumer(i, f) Consumer(i, { f; })
#define qPredicate(i, f) Predicate(i, { return f; })
#define qBiConsumer(i1, i2, f) BiConsumer(i1, i2, { f; })
#define qSupplier(f) Supplier({ return f; })
#define qComparator(i1, i2, f) Comparator(i1, i2, { return f; })
typedef struct collector {
supplier_t supplier;
biconsumer_t accumulator;
function_t finisher;
} collector_t;
enum iteratorResultStepType {
CONTINUE,
BREAK
};
enum iteratorResultModifyType {
KEEP,
REPLACE,
DELETE
};
typedef struct iteratorResult {
enum iteratorResultStepType step;
enum iteratorResultModifyType modify;
raw_t data;
} iter_result_t;
#define ITERATOR_CONTINUE ((iter_result_t) { .step = CONTINUE, .modify = KEEP })
#define ITERATOR_BREAK ((iter_result_t) { .step = BREAK, .modify = KEEP })
#define ITERATOR_DELETE ((iter_result_t) { .step = CONTINUE, .modify = DELETE })
#define ITERATOR_REPLACE(d) ((iter_result_t) { .step = CONTINUE, .modify = REPLACE, .data = d })
typedef iter_result_t (*iterator_t)(size_t, raw_t);
#define Iterator(i, d, f) lambda(iter_result_t, (size_t i, raw_t d) f)
typedef struct optional {
raw_t value;
bool present;
} optional_t;
#endif

127
src/linked-test.c Normal file
View file

@ -0,0 +1,127 @@
#include <viscocity.h>
#include "functional.h"
#include "linked.h"
#include "arrays.h"
test(linked_empty, "linked_empty creates new linked_t with size 0") {
linked_t list = linked_empty();
assertTrue(linked_size(&list) == 0, "list has size 0");
}
test(linked_add_get_clear, "add, get and clear elements from list") {
linked_t list = linked_empty();
assertTrue(linked_size(&list) == 0, "list has size 0");
linked_add(&list, 42);
assertTrue(linked_size(&list) == 1, "list has size 1");
assertTrue(linked_get(&list, 0) == 42, "get correct value from list");
linked_add(&list, 1337);
assertTrue(linked_size(&list) == 2, "list has size 1");
assertTrue(linked_get(&list, 1) == 1337, "get correct value from list");
linked_clear(&list);
assertTrue(linked_size(&list) == 0, "list has size 0");
}
test(linked_get_empty, "get non-existing entry returns 0") {
linked_t list = linked_empty();
assertTrue(linked_size(&list) == 0, "list has size 0");
assertTrue(linked_get(&list, 0) == 0, "get correct value from list");
}
test(linked_fromArray_get, "create list from array and get elements") {
const char* array[] = {"Hello", "World", "foobar"};
linked_t list = linked_fromArray((void**) array, 3);
assertTrue(linked_size(&list) == 3, "list has size 3");
assertStringEqual((char*) linked_get(&list, 0), "Hello", "first element in list is \"Hello\"");
assertStringEqual((char*) linked_get(&list, 1), "World", "second element in list is \"World\"");
assertStringEqual((char*) linked_get(&list, 2), "foobar", "third element in list is \"foobar\"");
linked_clear(&list);
}
test(linked_fromSupplier_get, "create list from supplier and get elements") {
const char* array[] = {"Hello", "World", "foobar"};
int i = 0;
assertCallExact(4, "supplier call assertion");
linked_t list = linked_fromSupplier(lambda(supply_t, () {
assertCallExact(4, "supplier call assertion");
supply_t supply;
supply.data = (raw_t) array[i];
supply.hasMore = ++i < 3;
return supply;
}));
assertTrue(linked_size(&list) == 3, "list has size 3");
assertStringEqual((char*) linked_get(&list, 0), "Hello", "first element in list is \"Hello\"");
assertStringEqual((char*) linked_get(&list, 1), "World", "second element in list is \"World\"");
assertStringEqual((char*) linked_get(&list, 2), "foobar", "third element in list is \"foobar\"");
linked_clear(&list);
}
test(linked_toArray, "get array from list") {
const char* originalArray[] = {"Hello", "World", "foobar"};
linked_t list = linked_fromArray((void**) originalArray, 3);
size_t size;
const char** array = linked_toArray(&list, &size);
assertTrue(size == 3, "size of new array is correct");
for (int i = 0; i < size; i++) {
assertStringEqual(array[0], originalArray[0], "value in new array is correct");
}
linked_clear(&list);
free(array);
}
test(linked_toPrimitiveArray, "get primitive array from list") {
linked_t list = linked_empty();
linked_add(&list, 42);
linked_add(&list, 1337);
linked_add(&list, 420);
size_t size;
int* array = linked_toPrimitiveArray(&list, ofType(int), &size);
assertTrue(size == 3, "size of new array is correct");
assertTrue(array[0] == 42, "value in array is correct");
assertTrue(array[1] == 1337, "value in array is correct");
assertTrue(array[2] == 420, "value in array is correct");
linked_clear(&list);
free(array);
}
test(linked_iterate_continue, "iterator gets called for all elements in list on continue") {
const char* array[] = {"Hello", "World", "foobar"};
linked_t list = linked_fromArray((void**) array, 3);
assertCallExact(4, "iterator call assertion");
linked_iterate(&list, lambda(iter_result_t, (size_t i, raw_t data) {
assertCallExact(4, "iterator call assertion");
assertStringEqual((const char*) data, array[i], "value in array is correct");
return ITERATOR_CONTINUE;
}));
}

194
src/linked.c Normal file
View file

@ -0,0 +1,194 @@
#include "linked.h"
#include <stdlib.h>
#include <stdio.h>
struct linkedElement* linked_createElement(raw_t data) {
struct linkedElement* element = malloc(sizeof(struct linkedElement));
if (element == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
element->data = data;
element->next = NULL;
return element;
}
linked_t linked_empty() {
return (linked_t) { .head = NULL };
}
linked_t linked_fromArray(void** array, size_t length) {
linked_t list = linked_empty();
struct linkedElement** nextRef = &list.head;
for (size_t i = 0; i < length; i++) {
*nextRef = linked_createElement((raw_t) array[i]);
nextRef = &(*nextRef)->next;
}
return list;
}
linked_t linked_fromSupplier(multisupplier_t supplier) {
linked_t list = linked_empty();
struct linkedElement** nextRef = &list.head;
supply_t supply = { .hasMore = true, .data = (raw_t) NULL };
while(supply.hasMore) {
supply = supplier();
*nextRef = linked_createElement(supply.data);
nextRef = &((*nextRef)->next);
}
return list;
}
size_t linked_add(linked_t* list, raw_t data) {
struct linkedElement** nextRef = &(list->head);
size_t position = 0;
while (*nextRef != NULL) {
nextRef = &((*nextRef)->next);
position++;
}
*nextRef = linked_createElement(data);
return position;
}
size_t linked_size(linked_t* list) {
struct linkedElement* current = list->head;
size_t size = 0;
while (current != NULL) {
size++;
current = current->next;
}
return size;
}
raw_t linked_get(linked_t* list, size_t index) {
raw_t result = 0;
linked_iterate(list, Iterator(i, d, {
if (i == index) {
result = d;
return ITERATOR_BREAK;
} else {
return ITERATOR_CONTINUE;
}
}));
return result;
}
void linked_iterate(linked_t* list, iterator_t iterator) {
struct linkedElement** refToCurrent = &list->head;
struct linkedElement* current = list->head;
size_t index = 0;
while(current != NULL) {
iter_result_t result = iterator(index, current->data);
bool skipContinue = false;
switch(result.modify) {
case DELETE:
*refToCurrent = current->next;
struct linkedElement* next = current->next;
free(current);
current = next;
skipContinue = true;
break;
case REPLACE:
current->data = result.data;
break;
case KEEP:
break;
}
switch(result.step) {
case BREAK:
return;
case CONTINUE:
if (skipContinue)
break;
refToCurrent = &current->next;
current = current->next;
break;
}
index++;
}
}
void linked_clear(linked_t* list) {
struct linkedElement* current = list->head;
while (current != NULL) {
struct linkedElement* next = current->next;
free(current);
current = next;
}
list->head = NULL;
}
void* linked_toArray(linked_t* list, size_t* length) {
size_t size = linked_size(list);
if (length != NULL)
*length = size;
raw_t* array = malloc(sizeof(raw_t) * size);
if (array == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
linked_iterate(list, Iterator(i, data, {
array[i] = data;
return ITERATOR_CONTINUE;
}));
return array;
}
void* linked_toPrimitiveArray(linked_t* list, size_t primitiveSize, size_t* length) {
size_t size = linked_size(list);
if (length != NULL)
*length = size;
unsigned char* array = malloc(primitiveSize * size);
if (array == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
linked_iterate(list, Iterator(i, data, {
for(size_t j = 0; j < primitiveSize; j++) {
array[i * primitiveSize + j] = (data >> (j * 8)) & 0xff;
}
return ITERATOR_CONTINUE;
}));
return array;
}

29
src/linked.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef LINKED_H
#define LINKED_H
#include "functional.h"
struct linkedElement {
struct linkedElement* next;
raw_t data;
};
typedef struct linked {
struct linkedElement* head;
} linked_t;
linked_t linked_empty();
linked_t linked_fromArray(void**, size_t);
linked_t linked_fromSupplier(multisupplier_t);
size_t linked_add(linked_t* list, raw_t data);
size_t linked_size(linked_t* list);
raw_t linked_get(linked_t*, size_t);
void linked_iterate(linked_t* list, iterator_t iterator);
void linked_clear(linked_t* list);
void* linked_toArray(linked_t*, size_t*);
void* linked_toPrimitiveArray(linked_t*, size_t, size_t*);
#endif

283
src/streams-test.c Normal file
View file

@ -0,0 +1,283 @@
#include <viscocity.h>
#include "streams.h"
#include "strings.h"
#include "arrays.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
test(stream_forEach, "forEach get's called for every element in stream") {
const char* strings[] = {"Hello", "World"};
assertCallExact(3, "forEach call assertion");
ptrStream((void*) strings, 2)
.forEach(Consumer(s, {
assertCallExact(3, "forEach call assertion");
assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream");
}));
}
test(stream_peek, "peek get's called for every element in stream") {
const char* strings[] = {"Hello", "World"};
assertCallExact(3, "peek call assertion");
ptrStream((void*) strings, 2)
.peek(Consumer(s, {
assertCallExact(3, "peek call assertion");
assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream");
})).forEach(Consumer(s, {}));
}
test(stream_map, "map sees and replaces all elements in the stream") {
int numbers[] = {1, 2, 3};
assertCallExact(4, "forEach call assertion");
primitiveStream((void*) numbers, 3, ofType(int))
.map(qFunction(i, i*i))
.forEach(Consumer(i, {
assertCallExact(4, "forEach call assertion");
assertTrue(i == 1 || i == 4 || i == 9, "i is one of the elements in the stream");
}));
}
test(stream_filter, "filter removes non-matching elements from stream") {
const char* strings[] = {"Hello", "World"};
assertCallExact(2, "forEach call assertion");
ptrStream((void*) strings, 2)
.filter(qPredicate(s, ((char*) s)[0] == 'W'))
.forEach(Consumer(s, {
assertStringEqual((char*) s, "World", NULL);
assertCallExact(2, "forEach call assertion");
}));
}
test(stream_limit, "limit aborts a stream after a certain number of elements") {
const char* strings[] = {"Hello", "World", "this", "is", "a", "test"};
assertCallExact(3, "forEach call assertion");
ptrStream((void*) strings, 6)
.limit(2)
.forEach(Consumer(s, {
assertCallExact(3, "forEach call assertion");
assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream");
}));
}
test(stream_skip, "skip over a certain number of elements") {
const char* strings[] = {"Hello", "World", "this", "is", "a", "test"};
assertCallExact(3, "forEach call assertion");
ptrStream((void*) strings, 6)
.skip(4)
.forEach(Consumer(s, {
assertCallExact(3, "forEach call assertion");
assertTrue(strcmp((char*) s, "a") == 0 || strcmp((char*) s, "test") == 0, "s is one of the elements in the stream");
}));
}
test(stream_count, "count reports correct number") {
const char* strings[] = {"Hello", "World", "this", "is", "a", "test"};
size_t count = ptrStream((void*) strings, 6)
.count();
assertTrue(count == 6, "count is correct");
}
test(stream_count_zero, "count reports correct number on empty stream") {
const char* strings[] = {};
size_t count = ptrStream((void*) strings, 0)
.count();
assertTrue(count == 0, "count is correct");
}
test(stream_max, "get max value of stream") {
int numbers[] = {42, 1337, 420};
optional_t max = primitiveStream((void*) numbers, 3, ofType(int))
.max(qComparator(i1, i2, i1 > i2));
assertTrue(max.present, "there is a max value");
assertTrue(max.value == 1337, "max is correct");
}
test(stream_max_empty, "get max value of empty stream") {
int numbers[] = {};
optional_t max = primitiveStream((void*) numbers, 0, ofType(int))
.max(qComparator(i1, i2, i1 > i2));
assertFalse(max.present, "there is no max value");
}
test(stream_min, "get min value of stream") {
int numbers[] = {42, 1337, 420};
optional_t min = primitiveStream((void*) numbers, 3, ofType(int))
.min(qComparator(i1, i2, i1 > i2));
assertTrue(min.present, "there is a min value");
assertTrue(min.value == 42, "max is correct");
}
test(stream_min_empty, "get min value of empty stream") {
int numbers[] = {};
optional_t min = primitiveStream((void*) numbers, 0, ofType(int))
.min(qComparator(i1, i2, i1 > i2));
assertFalse(min.present, "there is no min value");
}
test(stream_allMatch_positive, "allMatch correctly reports positive matches") {
int numbers[] = {2, 2, 3};
assertTrue(primitiveStream((void*) numbers, 3, ofType(int))
.allMatch(qPredicate(i, i > 1)), "are all numbers greater than 1");
}
test(stream_allMatch_negative, "allMatch correctly reports negative matches") {
int numbers[] = {2, 1, 3};
assertFalse(primitiveStream((void*) numbers, 3, ofType(int))
.allMatch(qPredicate(i, i > 1)), "are not all numbers greater than 1");
}
test(stream_anyMatch_positive, "anyMatch correctly reports positive matches") {
int numbers[] = {2, 2, 3};
assertTrue(primitiveStream((void*) numbers, 3, ofType(int))
.anyMatch(qPredicate(i, i > 2)), "one of the elements is > 2");
}
test(stream_anyMatch_negative, "anyMatch correctly reports negative matches") {
int numbers[] = {2, 1, 3};
assertFalse(primitiveStream((void*) numbers, 3, ofType(int))
.allMatch(qPredicate(i, i > 3)), "none of the elements is > 3");
}
test(stream_collect_array, "arrayCollector correctly builds an array of all elements in the stream") {
int numbers[] = {42, 1337, 420};
arrayWithLength_t* array = (void*) primitiveStream((void*) numbers, 3, ofType(int))
.collect(arrayCollector(ofType(int)));
assertTrue(array->length == 3, "resulting array length is okay");
for (int i = 0; i < array->length; i++) {
assertTrue(((int*) (array->array))[i] == numbers[i], "checking values in array");
}
free(array->array);
free(array);
}
test(stream_collect_string, "stringCollector correctly build a string of all elements in the stream") {
char characters[] = "Hello World";
char* string = (char*) charStream(characters)
.collect(stringCollector());
assertStringEqual(characters, string, "strings match");
free(string);
}
test(stream_forEach_empty, "forEach doesn't get called on empty stream") {
const char* strings[] = {};
assertCallExact(1, "forEach call assertion");
ptrStream((void*) strings, 0)
.forEach(Consumer(s, {
assertCallExact(1, "forEach call assertion");
}));
}
test(stream_allMatch_empty, "allMatch on empty stream returns true") {
const char* strings[] = {};
assertTrue(ptrStream((void*) strings, 0)
.allMatch(qPredicate(s, true)), "allMatch returns true");
}
test(stream_collect_array_empty, "arrayCollector returns empty array on empty stream") {
int numbers[] = {};
arrayWithLength_t* array = (void*) primitiveStream((void*) numbers, 0, ofType(int))
.collect(arrayCollector(ofType(int)));
assertTrue(array->length == 0, "array length is 0");
free(array->array);
free(array);
}
test(stream_collect_string_empty, "stringCollector returns empty string on empty stream") {
char characters[] = "";
char* string = (char*) charStream(characters)
.collect(stringCollector());
assertNotNull(string, "string is not null");
assertStringEqual(string, "", "string is empty");
free(string);
}
test(stream_iterate, "stream from iterate works") {
assertCallExact(4, "forEach call assertion");
Stream.iterate(1, qFunction(last, 2*last))
.limit(3)
.forEach(Consumer(i, {
assertCallExact(4, "forEach call assertion");
assertTrue(i == 1 || i == 2 || i == 4, "correct values in stream");
}));
}
test(stream_generate, "stream from generate works") {
assertCallExact(5, "supplier call assertion");
assertCallExact(4, "forEach call assertion");
int i = 1;
Stream.generate(Supplier({
assertCallExact(5, "supplier call assertion");
i *= 2;
return i;
})).limit(3)
.forEach(Consumer(i, {
assertCallExact(4, "forEach call assertion");
assertTrue(i == 2 || i == 4 || i == 8, "correct values in stream");
}));
}
test(stream_empty, "empty stream") {
size_t count = Stream.empty().count();
assertTrue(count == 0, "count is correct");
}

378
src/streams.c Normal file
View file

@ -0,0 +1,378 @@
#include "streams.h"
#include "arrays.h"
#include <stdio.h>
#include <stdlib.h>
#define MAX_STREAMS (1<<5)
stream_t* streams[MAX_STREAMS];
int currentStream = -1;
stream_t* getCurrentStream() {
return streams[currentStream];
}
// true for break; false for continue
bool streamApply(stream_t* stream, raw_t data, size_t elementIndex, size_t operationIndex, consumer_t finally) {
if (operationIndex >= stream->_operationsLength) {
finally(data);
return false;
} else {
operation_t operation = stream->_operations[operationIndex];
switch(operation.type) {
case map:
data = ((function_t) operation.action)(data);
break;
case peek:
((consumer_t) operation.action)(data);
break;
case filter:
if (!((predicate_t) operation.action)(data)) {
return false; // abort for this piece of data
}
break;
case limit:
if (elementIndex >= (size_t) operation.action)
return true;
break;
case skip:
if (elementIndex < (size_t) operation.action)
return false;
break;
}
stream->_apply(stream, data, elementIndex, operationIndex + 1, finally);
}
return false;
}
#define OPERATION_CHUNK_SIZE (8)
void streamAddOperation(enum operationType type, void* action) {
stream_t* stream = getCurrentStream();
if (stream->_operationsLength % OPERATION_CHUNK_SIZE == 0) {
operation_t* operations = realloc(stream->_operations, sizeof(operation_t) * ((stream->_operationsLength) / OPERATION_CHUNK_SIZE + 1) * OPERATION_CHUNK_SIZE);
if (operations == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
stream->_operations = operations;
}
stream->_operations[stream->_operationsLength++] = (operation_t) {
.action = action,
.type = type
};
}
stream_t streamMap(function_t function) {
stream_t* stream = getCurrentStream();
stream->_addOperation(map, function);
return *stream;
}
stream_t streamPeek(consumer_t consumer) {
stream_t* stream = getCurrentStream();
stream->_addOperation(peek, consumer);
return *stream;
}
stream_t streamFilter(predicate_t predicate) {
stream_t* stream = getCurrentStream();
stream->_addOperation(filter, predicate);
return *stream;
}
stream_t streamLimit(size_t maxSize) {
stream_t* stream = getCurrentStream();
stream->_addOperation(limit, (void*) maxSize);
return *stream;
}
stream_t streamSkip(size_t n) {
stream_t* stream = getCurrentStream();
stream->_addOperation(skip, (void*) n);
return *stream;
}
bool streamAllMatch(predicate_t predicate) {
stream_t* stream = getCurrentStream();
bool allMatch = true;
size_t i = 0;
stream->__iterate(lambda(bool, (raw_t data) {
bool abort = stream->_apply(stream, data, i++, 0, lambda(void, (raw_t data) {
if (!predicate(data)) {
allMatch = false;
}
}));
if (!allMatch)
abort = true;
return abort;
}));
stream->__destroy();
return allMatch;
}
bool streamAnyMatch(predicate_t predicate) {
stream_t* stream = getCurrentStream();
bool anyMatch = false;
size_t i = 0;
stream->__iterate(lambda(bool, (raw_t data) {
bool abort = stream->_apply(stream, data, i++, 0, lambda(void, (raw_t data) {
if (predicate(data)) {
anyMatch = true;
}
}));
if (anyMatch)
abort = true;
return abort;
}));
stream->__destroy();
return anyMatch;
}
void streamForEach(consumer_t consumer) {
stream_t* stream = getCurrentStream();
size_t i = 0;
stream->__iterate(lambda(bool, (raw_t data) {
return stream->_apply(stream, data, i++, 0, consumer);
}));
stream->__destroy();
}
size_t streamCount() {
stream_t* stream = getCurrentStream();
size_t i = 0;
stream->__iterate(lambda(bool, (raw_t data) {
return stream->_apply(stream, data, i++, 0, Consumer(obj, {}));
}));
stream->__destroy();
return i;
}
optional_t streamMin(comparator_t comparator) {
stream_t* stream = getCurrentStream();
raw_t current;
bool present = false;
stream->__iterate(lambda(bool, (raw_t data) {
if (!present) {
current = data;
present = true;
} else if (comparator(data, current) < 0) {
current = data;
}
return false;
}));
stream->__destroy();
return (optional_t) {
.value = current,
.present = present,
};
}
optional_t streamMax(comparator_t comparator) {
stream_t* stream = getCurrentStream();
raw_t current;
bool present = false;
stream->__iterate(lambda(bool, (raw_t data) {
if (!present) {
current = data;
present = true;
} else if (comparator(data, current) > 0) {
current = data;
}
return false;
}));
stream->__destroy();
return (optional_t) {
.value = current,
.present = present,
};
}
raw_t streamCollect(collector_t collector) {
stream_t* stream = getCurrentStream();
raw_t result = collector.supplier();
size_t i = 0;
stream->__iterate(lambda(bool, (raw_t data) {
return stream->_apply(stream, data, i++, 0, lambda(void, (raw_t data) {
collector.accumulator(result, data);
}));
}));
result = collector.finisher(result);
stream->__destroy();
return result;
}
void streamDestroy() {
stream_t* stream = getCurrentStream();
if (stream->_operations != NULL) {
free(stream->_operations);
}
free(stream);
currentStream--;
}
void skeletonStream(void* _stream) {
stream_t* stream = (stream_t*) _stream;
stream->_operations = NULL;
stream->_operationsLength = 0;
stream->_addOperation = &streamAddOperation;
stream->_destroy = &streamDestroy;
stream->_apply = &streamApply;
stream->__destroy = &streamDestroy;
stream->map = &streamMap;
stream->peek = &streamPeek;
stream->filter = &streamFilter;
stream->limit = &streamLimit;
stream->skip = &streamSkip;
stream->allMatch = &streamAllMatch;
stream->anyMatch = &streamAnyMatch;
stream->forEach = &streamForEach;
stream->count = &streamCount;
stream->min = &streamMin;
stream->max = &streamMax;
stream->collect = &streamCollect;
streams[++currentStream] = stream;
}
typedef struct {
stream_t stream;
raw_t seed;
function_t function;
} baseIterateStream_t;
typedef struct {
stream_t stream;
supplier_t supplier;
} baseGenerateStream_t;
typedef struct {
stream_t stream;
stream_t source1;
stream_t source2;
} baseConcatStream_t;
void iterateStream_iterate(predicate_t predicate) {
baseIterateStream_t* stream = (baseIterateStream_t*) getCurrentStream();
raw_t current = stream->seed;
while(true) {
if (predicate(current)) {
break;
}
current = stream->function(current);
}
}
stream_t stream_iterate(raw_t seed, function_t function) {
baseIterateStream_t* stream = malloc(sizeof(baseIterateStream_t));
if (stream == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
skeletonStream(stream);
stream->seed = seed;
stream->function = function;
stream->stream.__iterate = &iterateStream_iterate;
return *((stream_t*) stream);
}
void generateStream_iterate(predicate_t predicate) {
baseGenerateStream_t* stream = (baseGenerateStream_t*) getCurrentStream();
while(true) {
if (predicate(stream->supplier())) {
break;
}
}
}
stream_t stream_generate(supplier_t supplier) {
baseGenerateStream_t* stream = malloc(sizeof(baseGenerateStream_t));
if (stream == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
skeletonStream(stream);
stream->supplier = supplier;
stream->stream.__iterate = &generateStream_iterate;
return *((stream_t*) stream);
}
void emptyStream_iterate(predicate_t predicate) {
// do nothing
}
stream_t stream_empty(supplier_t supplier) {
stream_t* stream = malloc(sizeof(stream_t));
if (stream == NULL) {
fprintf(stderr, "Fuck\n");
exit(1);
}
skeletonStream(stream);
stream->__iterate = &emptyStream_iterate;
return *stream;
}
const struct globalStream Stream = {
.iterate = &stream_iterate,
.generate = &stream_generate,
.empty = &stream_empty,
//.concat = &stream_concat,
};

108
src/streams.h Normal file
View file

@ -0,0 +1,108 @@
#ifndef STREAMS_H
#define STREAMS_H
#include "functional.h"
#include <stdlib.h>
#include <stdio.h>
#define ARRAY_COLLECTOR_CHUNK_SIZE (8)
#define ofType(t) (sizeof(t))
#define arrayCollector(s) ((collector_t) {\
.supplier = Supplier({\
arrayWithLength_t* internal = malloc(sizeof(struct arrayWithLength));\
if (internal == NULL) {\
fprintf(stderr, "Fuck\n");\
exit(1);\
}\
internal->array = NULL;\
internal->length = 0;\
return (raw_t) internal;\
}),\
.accumulator = BiConsumer(_internal, data, {\
arrayWithLength_t* internal = (void*) _internal;\
if (internal->length % ARRAY_COLLECTOR_CHUNK_SIZE == 0) {\
void* array = realloc(internal->array, s * ((internal->length / ARRAY_COLLECTOR_CHUNK_SIZE) + 1) * ARRAY_COLLECTOR_CHUNK_SIZE);\
if (array == NULL) {\
fprintf(stderr, "Fuck\n");\
exit(1);\
}\
internal->array = array;\
}\
for (size_t i = 0; i < s; i++) {\
((char*) (internal->array))[internal->length * s + i] = (data >> (i * 8)) & 0xff;\
}\
internal->length++;\
}),\
.finisher = qFunction(_internal, _internal)\
})
#define stringCollector() ({\
collector_t collector = arrayCollector(ofType(char));\
collector.finisher = Function(_internal, {\
arrayWithLength_t* internal = (void*) _internal;\
if (internal->length == 0 || ((char*) (internal->array))[internal->length - 1] != '\0') {\
collector.accumulator(_internal, '\0');\
}\
raw_t result = (raw_t) internal->array;\
free(internal);\
return result;\
});\
collector;\
})
#define sumCollector() ((collector_t) {\
.supplier = qSupplier(0),\
.accumulator = qBiConsumer(sum, element, sum + element),\
.finisher = qFunction(sum, sum)\
})
enum operationType {
peek, map, filter, limit, skip
};
typedef struct operation {
enum operationType type;
void* action;
} operation_t;
typedef struct stream {
operation_t* _operations;
size_t _operationsLength;
void (*_addOperation)(enum operationType, void*);
bool (*_apply)(struct stream*, raw_t, size_t, size_t, consumer_t);
void (*_destroy)();
void (*__destroy)();
void (*__iterate)(predicate_t);
struct stream (*map)(function_t);
struct stream (*peek)(consumer_t);
struct stream (*filter)(predicate_t);
struct stream (*limit)(size_t);
struct stream (*skip)(size_t);
optional_t (*min)(comparator_t);
optional_t (*max)(comparator_t);
bool (*allMatch)(predicate_t);
bool (*anyMatch)(predicate_t);
void (*forEach)(consumer_t);
size_t (*count)();
raw_t (*collect)(collector_t);
} stream_t;
const extern struct globalStream {
stream_t (*iterate)(raw_t, function_t);
stream_t (*generate)(supplier_t);
stream_t (*empty)();
} Stream;
stream_t* getCurrentStream();
void skeletonStream(void*);
#endif

72
src/strings-test.c Normal file
View file

@ -0,0 +1,72 @@
#include <viscocity.h>
#include "streams.h"
#include "strings.h"
#include "arrays.h"
test(string_strclone, "strclone creates copy of string") {
const char* string = "Hello World";
char* clone = strclone(string);
assertNotNull(clone, "clone is not null");
assertStringEqual(clone, string, "strings are equal");
free(clone);
}
test(string_stringEqualsPredicate, "stringEqualsPredicate") {
const char* array[] = {"Hello", "World"};
assertCallExact(2, "forEach call assertion");
ptrStream((void*) array, 2)
.filter(stringEqualsPredicate("World"))
.forEach(Consumer(s, {
assertCallExact(2, "forEach call assertion");
assertStringEqual((char*) s, "World", "string is \"World\"");
}));
}
test(string_stringNotEqualsPredicate, "stringNotEqualsPredicate") {
const char* array[] = {"Hello", "World"};
assertCallExact(2, "forEach call assertion");
ptrStream((void*) array, 2)
.filter(stringNotEqualsPredicate("World"))
.forEach(Consumer(s, {
assertCallExact(2, "forEach call assertion");
assertStringEqual((char*) s, "Hello", "string is \"Hello\"");
}));
}
test(string_stringEmptyPredicate, "stringEmptyPredicate") {
const char* array[] = {"Hello", "", "World"};
assertCallExact(2, "forEach call assertion");
ptrStream((void*) array, 3)
.filter(stringEmptyPredicate())
.forEach(Consumer(s, {
assertCallExact(2, "forEach call assertion");
assertStringEqual((char*) s, "", "string is empty");
}));
}
test(string_stringNotEmptyPredicate, "stringNotEmptyPredicate") {
const char* array[] = {"Hello", "", "World"};
assertCallExact(3, "forEach call assertion");
ptrStream((void*) array, 3)
.filter(stringNotEmptyPredicate())
.forEach(Consumer(s, {
assertCallExact(3, "forEach call assertion");
assertTrue(strcmp((char*) s, "Hello") == 0 || strcmp((char*) s, "World") == 0, "s is one of the elements in the stream");
}));
}

20
src/strings.c Normal file
View file

@ -0,0 +1,20 @@
#include "strings.h"
#include "arrays.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
stream_t charStream(const char* string) {
return primitiveStream((char*) string, strlen(string), ofType(char));
}
char* strclone(const char* string) {
char* clone = malloc(strlen(string) + 1);
if (clone == NULL) {
fprintf(stderr, "Fuck!\n");
exit(1);
}
strcpy(clone, string);
return clone;
}

15
src/strings.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef STRINGS_H
#define STRINGS_H
#include "streams.h"
char* strclone(const char*);
stream_t charStream(const char*);
#define stringEqualsPredicate(s) qPredicate(obj, strcmp((char*) obj, s) == 0)
#define stringNotEqualsPredicate(s) qPredicate(obj, strcmp((char*) obj, s) != 0)
#define stringEmptyPredicate(s) qPredicate(obj, strcmp((char*) obj, "") == 0)
#define stringNotEmptyPredicate(s) qPredicate(obj, strcmp((char*) obj, "") != 0)
#endif