Basic script-based engine init is working - performance is poor
This commit is contained in:
5
assets/scripts/init.toy
Normal file
5
assets/scripts/init.toy
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import engine;
|
||||||
|
|
||||||
|
initWindow("Airport Game", 800, 600, false);
|
||||||
|
|
||||||
|
loadRootNode("assets/scripts/root.toy");
|
||||||
14
assets/scripts/root.toy
Normal file
14
assets/scripts/root.toy
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//root node can load the whole scene, and essentially act as the scene object
|
||||||
|
fn onInit() {
|
||||||
|
print "root.toy:onInit() called";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onStep() {
|
||||||
|
import standard;
|
||||||
|
print "root.toy:onStep() called";
|
||||||
|
print clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onFree() {
|
||||||
|
print "root.toy:onFree() called";
|
||||||
|
}
|
||||||
198
core/engine.c
198
core/engine.c
@@ -1,66 +1,160 @@
|
|||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
|
||||||
|
#include "lib_engine.h"
|
||||||
|
#include "lib_standard.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//define the engine object
|
||||||
|
Engine engine;
|
||||||
|
|
||||||
//errors here should be fatal
|
//errors here should be fatal
|
||||||
static void error(Engine* engine, char* message) {
|
static void fatalError(char* message) {
|
||||||
fprintf(stderr, message);
|
fprintf(stderr, message);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//compilation functions
|
||||||
|
//TODO: move these to their own file
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
//exposed functions
|
//exposed functions
|
||||||
void initEngine(Engine* engine) {
|
void initEngine() {
|
||||||
//clear
|
//clear
|
||||||
engine->root = NULL;
|
engine.rootNode = NULL;
|
||||||
engine->running = true;
|
engine.running = false;
|
||||||
|
engine.window = NULL;
|
||||||
|
engine.renderer = NULL;
|
||||||
|
|
||||||
//init SDL
|
//init SDL
|
||||||
if (SDL_Init(0) != 0) {
|
if (SDL_Init(0) != 0) {
|
||||||
error(engine, "Failed to initialize SDL2");
|
fatalError("Failed to initialize SDL2");
|
||||||
}
|
}
|
||||||
|
|
||||||
//init the window
|
//init Toy
|
||||||
engine->window = SDL_CreateWindow(
|
initInterpreter(&engine.interpreter);
|
||||||
"Caption",
|
injectNativeHook(&engine.interpreter, "engine", hookEngine);
|
||||||
SDL_WINDOWPOS_UNDEFINED,
|
injectNativeHook(&engine.interpreter, "standard", hookStandard);
|
||||||
SDL_WINDOWPOS_UNDEFINED,
|
|
||||||
engine->screenWidth,
|
|
||||||
engine->screenHeight,
|
|
||||||
SDL_WINDOW_RESIZABLE
|
|
||||||
);
|
|
||||||
|
|
||||||
if (engine->window == NULL) {
|
size_t size = 0;
|
||||||
error(engine, "Failed to initialize the window");
|
char* source = readFile("./assets/scripts/init.toy", &size);
|
||||||
|
unsigned char* tb = compileString(source, &size);
|
||||||
|
free((void*)source);
|
||||||
|
|
||||||
|
runInterpreter(&engine.interpreter, tb, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
//init the renderer
|
void freeEngine() {
|
||||||
engine->renderer = SDL_CreateRenderer(engine->window, -1, 0);
|
SDL_DestroyRenderer(engine.renderer);
|
||||||
|
SDL_DestroyWindow(engine.window);
|
||||||
if (engine->renderer == NULL) {
|
|
||||||
error(engine, "Failed to initialize the renderer");
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
|
||||||
SDL_RenderSetLogicalSize(engine->renderer, engine->screenWidth, engine->screenHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeEngine(Engine* engine) {
|
|
||||||
SDL_DestroyRenderer(engine->renderer);
|
|
||||||
SDL_DestroyWindow(engine->window);
|
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
engine->renderer = NULL;
|
//clear existing root node
|
||||||
engine->window = NULL;
|
if (engine.rootNode != NULL) {
|
||||||
|
callEngineNode(engine.rootNode, &engine.interpreter, "onFree");
|
||||||
|
|
||||||
|
freeEngineNode(engine.rootNode);
|
||||||
|
FREE(EngineNode, engine.rootNode);
|
||||||
|
|
||||||
|
engine.rootNode = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execStep(Engine* engine) {
|
freeInterpreter(&engine.interpreter);
|
||||||
//DEBUG: for now, just poll events
|
|
||||||
|
engine.renderer = NULL;
|
||||||
|
engine.window = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execStep() {
|
||||||
|
//call onStep
|
||||||
|
callEngineNode(engine.rootNode, &engine.interpreter, "onStep");
|
||||||
|
|
||||||
|
//poll events
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch(event.type) {
|
switch(event.type) {
|
||||||
//quit
|
//quit
|
||||||
case SDL_QUIT: {
|
case SDL_QUIT: {
|
||||||
engine->running = false;
|
engine.running = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -68,9 +162,9 @@ static void execStep(Engine* engine) {
|
|||||||
case SDL_WINDOWEVENT: {
|
case SDL_WINDOWEVENT: {
|
||||||
switch(event.window.event) {
|
switch(event.window.event) {
|
||||||
case SDL_WINDOWEVENT_RESIZED:
|
case SDL_WINDOWEVENT_RESIZED:
|
||||||
engine->screenWidth = event.window.data1;
|
engine.screenWidth = event.window.data1;
|
||||||
engine->screenHeight = event.window.data2;
|
engine.screenHeight = event.window.data2;
|
||||||
SDL_RenderSetLogicalSize(engine->renderer, engine->screenWidth, engine->screenHeight);
|
SDL_RenderSetLogicalSize(engine.renderer, engine.screenWidth, engine.screenHeight);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,25 +176,29 @@ static void execStep(Engine* engine) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//the heart of the engine
|
//the heart of the engine
|
||||||
void execEngine(Engine* engine) {
|
void execEngine() {
|
||||||
|
if (!engine.running) {
|
||||||
|
fatalError("Can't execute the engine (did you forget to initialize the screen?)");
|
||||||
|
}
|
||||||
|
|
||||||
//set up time
|
//set up time
|
||||||
gettimeofday(&engine->realTime, NULL);
|
gettimeofday(&engine.realTime, NULL);
|
||||||
engine->simTime = engine->realTime;
|
engine.simTime = engine.realTime;
|
||||||
struct timeval delta = { .tv_sec = 0, .tv_usec = 1000 * 1000 / 60 }; //60 frames per second
|
struct timeval delta = { .tv_sec = 0, .tv_usec = 1000 * 1000 / 60 }; //60 frames per second
|
||||||
|
|
||||||
while (engine->running) {
|
while (engine.running) {
|
||||||
//calc the time passed
|
//calc the time passed
|
||||||
gettimeofday(&engine->realTime, NULL);
|
gettimeofday(&engine.realTime, NULL);
|
||||||
|
|
||||||
//if not enough time has passed
|
//if not enough time has passed
|
||||||
if (engine->simTime.tv_sec < engine->realTime.tv_sec && engine->simTime.tv_usec < engine->realTime.tv_usec) {
|
if (engine.simTime.tv_sec < engine.realTime.tv_sec && engine.simTime.tv_usec < engine.realTime.tv_usec) {
|
||||||
//while not enough time has passed
|
//while not enough time has passed
|
||||||
while(engine->simTime.tv_sec < engine->realTime.tv_sec && engine->simTime.tv_usec < engine->realTime.tv_usec) {
|
while(engine.simTime.tv_sec < engine.realTime.tv_sec && engine.simTime.tv_usec < engine.realTime.tv_usec) {
|
||||||
//simulate the world
|
//simulate the world
|
||||||
execStep(engine);
|
execStep();
|
||||||
|
|
||||||
//calc the time simulation
|
//calc the time simulation
|
||||||
timeradd(&delta, &engine->simTime, &engine->simTime);
|
timeradd(&delta, &engine.simTime, &engine.simTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -108,8 +206,8 @@ void execEngine(Engine* engine) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//render the world
|
//render the world
|
||||||
SDL_SetRenderDrawColor(engine->renderer, 0, 0, 0, 255); //NOTE: This line can be disabled later
|
SDL_SetRenderDrawColor(engine.renderer, 0, 0, 0, 255); //NOTE: This line can be disabled later
|
||||||
SDL_RenderClear(engine->renderer); //NOTE: This line can be disabled later
|
SDL_RenderClear(engine.renderer); //NOTE: This line can be disabled later
|
||||||
SDL_RenderPresent(engine->renderer);
|
SDL_RenderPresent(engine.renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "engine_node.h"
|
#include "engine_node.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
@@ -10,11 +11,14 @@
|
|||||||
//the base engine object, which represents the state of the game
|
//the base engine object, which represents the state of the game
|
||||||
typedef struct _engine {
|
typedef struct _engine {
|
||||||
//engine stuff
|
//engine stuff
|
||||||
EngineNode* root;
|
EngineNode* rootNode;
|
||||||
struct timeval simTime;
|
struct timeval simTime;
|
||||||
struct timeval realTime;
|
struct timeval realTime;
|
||||||
bool running;
|
bool running;
|
||||||
|
|
||||||
|
//Toy stuff
|
||||||
|
Interpreter interpreter;
|
||||||
|
|
||||||
//SDL stuff
|
//SDL stuff
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
SDL_Renderer* renderer;
|
SDL_Renderer* renderer;
|
||||||
@@ -22,9 +26,11 @@ typedef struct _engine {
|
|||||||
int screenHeight;
|
int screenHeight;
|
||||||
} Engine;
|
} Engine;
|
||||||
|
|
||||||
//APIs for initializing the engine
|
//extern singleton
|
||||||
CORE_API void initEngine(Engine* engine);
|
extern Engine engine;
|
||||||
CORE_API void freeEngine(Engine* engine);
|
|
||||||
|
|
||||||
CORE_API void execEngine(Engine* engine);
|
//APIs for running the engine in main()
|
||||||
|
CORE_API void initEngine();
|
||||||
|
CORE_API void execEngine();
|
||||||
|
CORE_API void freeEngine();
|
||||||
|
|
||||||
|
|||||||
258
core/lib_engine.c
Normal file
258
core/lib_engine.c
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
#include "lib_engine.h"
|
||||||
|
|
||||||
|
#include "engine.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "literal_array.h"
|
||||||
|
|
||||||
|
//errors here should be fatal
|
||||||
|
static void fatalError(char* message) {
|
||||||
|
fprintf(stderr, message);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//compilation functions
|
||||||
|
//TODO: move these to their own file
|
||||||
|
#include "console_colors.h"
|
||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//native functions to be called
|
||||||
|
static int nativeInitWindow(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
if (engine.window != NULL) {
|
||||||
|
fatalError("Can't re-initialize the window\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments->count != 4) {
|
||||||
|
fatalError("Incorrect number of arguments passed to initEngine\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//extract the arguments
|
||||||
|
Literal fscreen = popLiteralArray(arguments);
|
||||||
|
Literal screenHeight = popLiteralArray(arguments);
|
||||||
|
Literal screenWidth = popLiteralArray(arguments);
|
||||||
|
Literal caption = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//check argument types
|
||||||
|
if (!IS_STRING(caption) || !IS_INTEGER(screenWidth) || !IS_INTEGER(screenHeight) || !IS_BOOLEAN(fscreen)) {
|
||||||
|
fatalError("Incorrect argument type passed to initEngine\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//init the window
|
||||||
|
engine.window = SDL_CreateWindow(
|
||||||
|
AS_STRING(caption),
|
||||||
|
SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
engine.screenWidth = AS_INTEGER(screenWidth),
|
||||||
|
engine.screenHeight = AS_INTEGER(screenHeight),
|
||||||
|
IS_TRUTHY(fscreen) ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (engine.window == NULL) {
|
||||||
|
fatalError("Failed to initialize the window\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//init the renderer
|
||||||
|
engine.renderer = SDL_CreateRenderer(engine.window, -1, 0);
|
||||||
|
|
||||||
|
if (engine.renderer == NULL) {
|
||||||
|
fatalError("Failed to initialize the renderer\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||||
|
SDL_RenderSetLogicalSize(engine.renderer, engine.screenWidth, engine.screenHeight);
|
||||||
|
|
||||||
|
//only run with a window
|
||||||
|
engine.running = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeLoadRootNode(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments passed to loadRootNode\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//extract the arguments
|
||||||
|
Literal fname = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//check argument types
|
||||||
|
if (!IS_STRING(fname)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to loadRootNode\n");
|
||||||
|
freeLiteral(fname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear existing root node
|
||||||
|
if (engine.rootNode != NULL) {
|
||||||
|
callEngineNode(engine.rootNode, &engine.interpreter, "onFree");
|
||||||
|
|
||||||
|
freeEngineNode(engine.rootNode);
|
||||||
|
FREE(EngineNode, engine.rootNode);
|
||||||
|
|
||||||
|
engine.rootNode = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//load the new root node
|
||||||
|
size_t size = 0;
|
||||||
|
char* source = readFile(AS_STRING(fname), &size);
|
||||||
|
unsigned char* tb = compileString(source, &size);
|
||||||
|
free((void*)source);
|
||||||
|
|
||||||
|
engine.rootNode = ALLOCATE(EngineNode, 1);
|
||||||
|
|
||||||
|
//BUGFIX: use an inner-interpreter here, otherwise it'll mess up the original's length value by calling run within a native function
|
||||||
|
Interpreter inner;
|
||||||
|
initInterpreter(&inner);
|
||||||
|
|
||||||
|
initEngineNode(engine.rootNode, &inner, tb, size);
|
||||||
|
|
||||||
|
freeInterpreter(&inner);
|
||||||
|
|
||||||
|
//init the new node
|
||||||
|
callEngineNode(engine.rootNode, &engine.interpreter, "onInit");
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(fname);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the hook
|
||||||
|
typedef struct Natives {
|
||||||
|
char* name;
|
||||||
|
NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
|
int hookEngine(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||||
|
//build the natives list
|
||||||
|
Natives natives[] = {
|
||||||
|
{"initWindow", nativeInitWindow},
|
||||||
|
{"loadRootNode", nativeLoadRootNode},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
//store the library in an aliased dictionary
|
||||||
|
if (!IS_NULL(alias)) {
|
||||||
|
//make sure the name isn't taken
|
||||||
|
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
freeLiteral(alias);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||||
|
initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
|
||||||
|
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||||
|
func.type = LITERAL_FUNCTION_NATIVE;
|
||||||
|
|
||||||
|
setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
freeLiteral(name);
|
||||||
|
freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
|
||||||
|
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
|
||||||
|
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(dict);
|
||||||
|
freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
6
core/lib_engine.h
Normal file
6
core/lib_engine.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
int hookEngine(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||||
|
|
||||||
94
core/lib_standard.c
Normal file
94
core/lib_standard.c
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#include "lib_standard.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 0) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to clock\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the time from C (what a pain)
|
||||||
|
time_t rawtime = time(NULL);
|
||||||
|
struct tm* timeinfo = localtime( &rawtime );
|
||||||
|
char* timestr = asctime(timeinfo);
|
||||||
|
|
||||||
|
//push to the stack
|
||||||
|
int len = strlen(timestr) - 1; //-1 for the newline
|
||||||
|
Literal timeLiteral = TO_STRING_LITERAL(copyString(timestr, len), len);
|
||||||
|
|
||||||
|
//push to the stack
|
||||||
|
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the hook
|
||||||
|
typedef struct Natives {
|
||||||
|
char* name;
|
||||||
|
NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
|
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||||
|
//build the natives list
|
||||||
|
Natives natives[] = {
|
||||||
|
{"clock", nativeClock},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
//store the library in an aliased dictionary
|
||||||
|
if (!IS_NULL(alias)) {
|
||||||
|
//make sure the name isn't taken
|
||||||
|
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
freeLiteral(alias);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||||
|
initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
|
||||||
|
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||||
|
func.type = LITERAL_FUNCTION_NATIVE;
|
||||||
|
|
||||||
|
setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
freeLiteral(name);
|
||||||
|
freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
|
||||||
|
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
|
||||||
|
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(dict);
|
||||||
|
freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
6
core/lib_standard.h
Normal file
6
core/lib_standard.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||||
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
Engine engine = { .screenWidth = 800, .screenHeight = 600 };
|
initEngine();
|
||||||
|
execEngine();
|
||||||
initEngine(&engine);
|
freeEngine();
|
||||||
execEngine(&engine);
|
|
||||||
freeEngine(&engine);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user