diff --git a/Toy b/Toy index 6a08639..3460967 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit 6a086395be0d7824b4a83efddb14d457eaaf299d +Subproject commit 3460967e3bd30df15a6cc2bc9f7b169d15b33abc diff --git a/core/engine.c b/core/engine.c index 7d4196d..47316e4 100644 --- a/core/engine.c +++ b/core/engine.c @@ -68,7 +68,9 @@ static void execStep(Engine* engine) { case SDL_WINDOWEVENT: { switch(event.window.event) { case SDL_WINDOWEVENT_RESIZED: - SDL_RenderSetLogicalSize(engine->renderer, event.window.data1, event.window.data2); + engine->screenWidth = event.window.data1; + engine->screenHeight = event.window.data2; + SDL_RenderSetLogicalSize(engine->renderer, engine->screenWidth, engine->screenHeight); break; } } diff --git a/core/engine_node.c b/core/engine_node.c index cffb571..d8ec209 100644 --- a/core/engine_node.c +++ b/core/engine_node.c @@ -1,2 +1,120 @@ #include "engine_node.h" +#include "memory.h" + +void initEngineNode(EngineNode* node, Interpreter* interpreter, void* tb, size_t size) { + //init + node->children = NULL; + node->capacity = 0; + node->count = 0; + node->functions = ALLOCATE(LiteralDictionary, 1); + initLiteralDictionary(node->functions); + + //run bytecode + runInterpreter(interpreter, tb, size); + + //grab all top-level function literals + LiteralDictionary* variablesPtr = &interpreter->scope->variables; + + for (int i = 0; i < variablesPtr->capacity; i++) { + //skip empties and tombstones + if (IS_NULL(variablesPtr->entries[i].key)) { + continue; + } + + //if this variable is a function + _entry* entry = &variablesPtr->entries[i]; + if (IS_FUNCTION(entry->value)) { + //save a copy + setLiteralDictionary(node->functions, entry->key, entry->value); + } + } +} + +void pushEngineNode(EngineNode* node, EngineNode* child) { + //push to the array (prune tombstones when expanding/copying) + if (node->count + 1 > node->capacity) { + int oldCapacity = node->capacity; + + node->capacity = GROW_CAPACITY(oldCapacity); + node->children = GROW_ARRAY(EngineNode, node->children, oldCapacity, node->capacity); + } + + //prune tombstones (experimental) + int counter = 0; + for (int i = 0; i < node->capacity; i++) { + if (i >= node->count) { + node->count = counter; + break; + } + + //move down + if (node->children[i].functions != NULL) { + node->children[counter++] = node->children[i]; + } + } + + //zero the rest + while (counter < node->capacity) { + node->children[counter].children = NULL; + node->children[counter].capacity = 0; + node->children[counter].count = 0; + node->children[counter].functions = NULL; + counter++; + } + + //assign + node->children[node->count++] = *child; +} + +void freeEngineNode(EngineNode* node) { + //free and tombstone this node + for (int i = 0; i < node->capacity; i++) { + freeEngineNode(&node->children[i]); + } + + FREE_ARRAY(EngineNode, node->children, node->capacity); + + if (node->functions != NULL) { + freeLiteralDictionary(node->functions); + } + + FREE(LiteralDictionary, node->functions); + + node->children = NULL; + node->capacity = -1; + node->count = -1; + node->functions = NULL; +} + +static void callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Literal key) { + //if this fn exists + if (existsLiteralDictionary(node->functions, key)) { + Literal fn = getLiteralDictionary(node->functions, key); + + LiteralArray dummyArray; + initLiteralArray(&dummyArray); + + callLiteralFn(interpreter, fn, &dummyArray, &dummyArray); + + freeLiteralArray(&dummyArray); + + freeLiteral(fn); + } + + //recurse to the (non-tomstone) children + for (int i = 0; i < node->count; i++) { + if (node->children[i].functions != NULL) { + callEngineNodeLiteral(&node->children[i], interpreter, key); + } + } +} + +void callEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName) { + //call "fnName" on this node, and all children, if it exists + Literal key = TO_IDENTIFIER_LITERAL(copyString(fnName, strlen(fnName)), strlen(fnName)); + + callEngineNodeLiteral(node, interpreter, key); + + freeLiteral(key); +} diff --git a/core/engine_node.h b/core/engine_node.h index 496c2fb..bf90745 100644 --- a/core/engine_node.h +++ b/core/engine_node.h @@ -2,22 +2,25 @@ #include "common.h" +#include "literal_dictionary.h" +#include "interpreter.h" + //forward declare typedef struct _engineNode EngineNode; -typedef struct _engine Engine; - -//the interface function -typedef void (*EngineNodeFn)(EngineNode* self, Engine* engine); //the node object, which forms a tree typedef struct _engineNode { //use Toy's memory model - void* children; + EngineNode* children; int capacity; - int count; + int count; //includes tombstones - EngineNodeFn onInit; - EngineNodeFn onStep; - EngineNodeFn onFree; + //toy functions, stored in a dict for flexibility + LiteralDictionary* functions; } EngineNode; +CORE_API void initEngineNode(EngineNode* node, Interpreter* interpreter, void* tb, size_t size); //run bytecode, then grab all top-level function literals +CORE_API void pushEngineNode(EngineNode* node, EngineNode* child); //push to the array (prune tombstones when expanding/copying) +CORE_API void freeEngineNode(EngineNode* node); //free and tombstone this node + +CORE_API void callEngineNode(EngineNode* node, Interpreter* interpreter, char* fnName); //call "fnName" on this node, and all children, if it exists diff --git a/core/makefile b/core/makefile index 826525c..d0e5adc 100644 --- a/core/makefile +++ b/core/makefile @@ -1,8 +1,8 @@ CC=gcc -IDIR+=. -CFLAGS+=$(addprefix -I,$(IDIR)) -DSDL_MAIN_HANDLED -g -Wall -W -pedantic -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable -LIBS+=-lSDL2 +IDIR+=. ../Toy/source +CFLAGS+=$(addprefix -I,$(IDIR)) -DSDL_MAIN_HANDLED -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable +LIBS+=-lSDL2 -ltoy ODIR = obj SRC = $(wildcard *.c) @@ -24,10 +24,10 @@ else endif library: $(OBJ) - $(CC) -DCORE_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE) $(LIBS) + $(CC) -DCORE_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE) -L../$(LIBDIR) $(LIBS) static: $(OBJ) - ar crs $(CORE_OUTDIR)/lib$(OUTNAME).a $(OBJ) $(LIBS) + ar crs $(CORE_OUTDIR)/lib$(OUTNAME).a $(OBJ) -L../$(LIBDIR) $(LIBS) $(OBJ): | $(ODIR) diff --git a/makefile b/makefile index b2c44f2..9c4df5c 100644 --- a/makefile +++ b/makefile @@ -13,6 +13,9 @@ toy: $(LIBDIR) core: $(LIBDIR) $(MAKE) -C core +test: clean $(OUTDIR) toy core + $(MAKE) -C test + $(OUTDIR): mkdir $(OUTDIR) diff --git a/source/main.c b/source/main.c index e42d183..2bf2c9a 100644 --- a/source/main.c +++ b/source/main.c @@ -1,11 +1,11 @@ #include "engine.h" int main(int argc, char* argv[]) { - Engine engine = { .screenWidth = 640, .screenHeight = 480 }; + Engine engine = { .screenWidth = 800, .screenHeight = 600 }; initEngine(&engine); execEngine(&engine); freeEngine(&engine); return 0; -} \ No newline at end of file +} diff --git a/source/makefile b/source/makefile index e7fbc96..afa9f4c 100644 --- a/source/makefile +++ b/source/makefile @@ -1,7 +1,7 @@ CC=gcc IDIR+=. ../Toy/source ../core -CFLAGS+=$(addprefix -I,$(IDIR)) -DSDL_MAIN_HANDLED -g -Wall -W -pedantic -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable +CFLAGS+=$(addprefix -I,$(IDIR)) -DSDL_MAIN_HANDLED -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable LIBS+=-lSDL2 -ltoy -lcore ODIR = obj diff --git a/test/makefile b/test/makefile new file mode 100644 index 0000000..851fc91 --- /dev/null +++ b/test/makefile @@ -0,0 +1,39 @@ +CC=gcc + +IDIR+=. ../Toy/source ../core +CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable +LIBS+=-lSDL2 -ltoy -lcore + +ODIR = obj +TARGETS = $(wildcard ../core/*.c) +TESTS = $(wildcard *.c) +OBJ = $(addprefix $(ODIR)/,$(TARGETS:../core/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o)) + +.PRECIOUS: $(TESTS:%.c=../$(OUTDIR)/%.exe) + +all: $(OBJ) $(TESTS:%.c=../$(OUTDIR)/%.exe) + +../$(OUTDIR)/%.exe: $(ODIR)/%.o + @$(CC) -o $@ $< $(TARGETS:../core/%.c=$(ODIR)/%.o) $(CFLAGS) -L../$(LIBDIR) $(LIBS) + cp ../$(LIBDIR)/*.dll ../$(OUTDIR) +ifeq ($(shell uname),Linux) + valgrind --leak-check=full --track-origins=yes $@ +else + $@ +endif + +$(OBJ): | $(ODIR) + +$(ODIR): + mkdir $(ODIR) + +$(ODIR)/%.o: %.c + @$(CC) -c -o $@ $< $(CFLAGS) + +$(ODIR)/%.o: ../core/%.c + @$(CC) -c -o $@ $< $(CFLAGS) + +.PHONY: clean + +clean: + $(RM) $(ODIR) diff --git a/test/scripts/child_engine_node.toy b/test/scripts/child_engine_node.toy new file mode 100644 index 0000000..46ef7b3 --- /dev/null +++ b/test/scripts/child_engine_node.toy @@ -0,0 +1,14 @@ +var tally: int = 0; + +fn onInit() { + print "child init called"; +} + +fn onStep() { + print "child step called"; + print ++tally; +} + +fn onFree() { + print "child free called"; +} \ No newline at end of file diff --git a/test/scripts/parent_engine_node.toy b/test/scripts/parent_engine_node.toy new file mode 100644 index 0000000..b78ceef --- /dev/null +++ b/test/scripts/parent_engine_node.toy @@ -0,0 +1,11 @@ +fn onInit() { + print "init called"; +} + +fn onStep() { + print "step called"; +} + +fn onFree() { + print "free called"; +} \ No newline at end of file diff --git a/test/test_engine_node.c b/test/test_engine_node.c new file mode 100644 index 0000000..fefaf60 --- /dev/null +++ b/test/test_engine_node.c @@ -0,0 +1,156 @@ +#include "engine_node.h" + +#include "lexer.h" +#include "parser.h" +#include "compiler.h" +#include "interpreter.h" +#include "console_colors.h" + +#include +#include +#include + +//compilation functions +char* readFile(char* path, size_t* fileSize) { + FILE* file = fopen(path, "rb"); + + if (file == NULL) { + fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path); + exit(-1); + } + + fseek(file, 0L, SEEK_END); + *fileSize = ftell(file); + rewind(file); + + char* buffer = (char*)malloc(*fileSize + 1); + + if (buffer == NULL) { + fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path); + exit(-1); + } + + size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file); + + buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this + + if (bytesRead < *fileSize) { + fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path); + exit(-1); + } + + fclose(file); + + return buffer; +} + +unsigned char* compileString(char* source, size_t* size) { + Lexer lexer; + Parser parser; + Compiler compiler; + + initLexer(&lexer, source); + initParser(&parser, &lexer); + initCompiler(&compiler); + + //run the parser until the end of the source + ASTNode* node = scanParser(&parser); + while(node != NULL) { + //pack up and leave + if (node->type == AST_NODEERROR) { + printf(ERROR "error node detected\n" RESET); + freeNode(node); + freeCompiler(&compiler); + freeParser(&parser); + return NULL; + } + + writeCompiler(&compiler, node); + freeNode(node); + node = scanParser(&parser); + } + + //get the bytecode dump + unsigned char* tb = collateCompiler(&compiler, (int*)(size)); + + //cleanup + freeCompiler(&compiler); + freeParser(&parser); + //no lexer to clean up + + //finally + return tb; +} + +int main() { + { + //setup interpreter + Interpreter interpreter; + initInterpreter(&interpreter); + + size_t size = 0; + + char* source = readFile("./scripts/parent_engine_node.toy", &size); + unsigned char* tb = compileString(source, &size); + + //create and test the engine node + EngineNode node; + + initEngineNode(&node, &interpreter, tb, size); + + callEngineNode(&node, &interpreter, "onInit"); + callEngineNode(&node, &interpreter, "onStep"); + callEngineNode(&node, &interpreter, "onFree"); + + freeEngineNode(&node); + + //free + free((void*)source); + freeInterpreter(&interpreter); + } + + { + //setup interpreter + Interpreter interpreter; + initInterpreter(&interpreter); + + size_t size = 0; + + char* source = readFile("./scripts/parent_engine_node.toy", &size); + unsigned char* tb = compileString(source, &size); + + //create and test the engine node + EngineNode node; + + initEngineNode(&node, &interpreter, tb, size); + resetInterpreter(&interpreter); + + for (int i = 0; i < 10; i++) { + char* source = readFile("./scripts/child_engine_node.toy", &size); + unsigned char* tb = compileString(source, &size); + + EngineNode child; + initEngineNode(&child, &interpreter, tb, size); + resetInterpreter(&interpreter); + + pushEngineNode(&node, &child); + } + + //test the calls + callEngineNode(&node, &interpreter, "onInit"); + + for (int i = 0; i < 10; i++) { + callEngineNode(&node, &interpreter, "onStep"); + } + + callEngineNode(&node, &interpreter, "onFree"); + + //free + freeEngineNode(&node); + free((void*)source); + freeInterpreter(&interpreter); + } + + printf(NOTICE "All good\n" RESET); + return 0; +}