Got hooks working

This commit is contained in:
2022-09-17 13:01:09 +01:00
parent 7eb16e51bb
commit 9c790f8cd6
12 changed files with 228 additions and 29 deletions

View File

@@ -21,7 +21,7 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
For windows, simply run `make` in the root directory. For windows, simply run `make` in the root directory.
For linux, run `make static` in the root directory (see [this issue for details](https://github.com/Ratstail91/Toy/issues/26)). For linux, run `make repl-static` in the root directory (see [this issue for details](https://github.com/Ratstail91/Toy/issues/26)).
For mac, good luck. For mac, good luck.

View File

@@ -39,6 +39,8 @@ DONE: nested compound assignment bug
TODO: hooks on the external libraries, triggered on import TODO: hooks on the external libraries, triggered on import
TODO: standard library TODO: standard library
TODO: external script runner library TODO: external script runner library
TODO: document how it all works TODO: document how it all works

View File

@@ -1,12 +1,17 @@
export OUTDIR = out export OUTDIR = out
all: $(OUTDIR) all: $(OUTDIR) repl
$(MAKE) -C source
library: clean $(OUTDIR) repl: $(OUTDIR) library
$(MAKE) -C repl
repl-static: $(OUTDIR) static
$(MAKE) -C repl
library: $(OUTDIR)
$(MAKE) -C source library $(MAKE) -C source library
static: clean $(OUTDIR) static: $(OUTDIR)
$(MAKE) -C source static $(MAKE) -C source static
test: clean $(OUTDIR) test: clean $(OUTDIR)

99
repl/lib_standard.c Normal file
View File

@@ -0,0 +1,99 @@
#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}
};
/* TODO: too exhuasted to fix this right now
//store the library in an aliased dictionary
if (!IS_NULL(alias)) {
//get the alias as a string
Literal dictName = TO_STRING_LITERAL( copyString(AS_IDENTIFIER(alias), alias.as.identifier.length), alias.as.identifier.length );
//make sure the name isn't taken
if (existsLiteralDictionary(&interpreter->scope->variables, dictName)) {
interpreter->errorOutput("Can't override an existing variable\n");
freeLiteral(dictName);
return false;
}
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
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);
setLiteralDictionary(&interpreter->scope->variables, dictName, dict);
setLiteralDictionary(&interpreter->scope->types, dictName, type);
//cleanup
freeLiteral(dict);
freeLiteral(type);
freeLiteral(dictName);
return 0;
}
*/
//default
for (int i = 0; natives[i].name; i++) {
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}

6
repl/lib_standard.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include "interpreter.h"
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);

27
repl/makefile Normal file
View File

@@ -0,0 +1,27 @@
CC=gcc
IDIR+=. ../source
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -pedantic -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=-ltoy
ODIR = obj
SRC = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
OUT=../$(OUTDIR)/toyrepl
all: $(OBJ)
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) -L$(realpath $(shell pwd)/../$(OUTDIR)) $(LIBS)
$(OBJ): | $(ODIR)
$(ODIR):
mkdir $(ODIR)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
$(RM) $(ODIR)

View File

@@ -9,6 +9,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
//extra libraries
#include "lib_standard.h"
//IO functions //IO functions
char* readFile(char* path, size_t* fileSize) { char* readFile(char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb"); FILE* file = fopen(path, "rb");
@@ -102,6 +105,10 @@ unsigned char* compileString(char* source, size_t* size) {
void runBinary(unsigned char* tb, size_t size) { void runBinary(unsigned char* tb, size_t size) {
Interpreter interpreter; Interpreter interpreter;
initInterpreter(&interpreter); initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
runInterpreter(&interpreter, tb, size); runInterpreter(&interpreter, tb, size);
freeInterpreter(&interpreter); freeInterpreter(&interpreter);
} }
@@ -123,12 +130,6 @@ void runSource(char* source) {
return; return;
} }
//DEBUG
// for (size_t i = 0; i < size; i++) {
// printf("%d, ", tb[i]);
// }
// printf("\n");
runBinary(tb, size); runBinary(tb, size);
} }
@@ -150,6 +151,9 @@ void repl() {
Interpreter interpreter; //persist the interpreter for the scopes Interpreter interpreter; //persist the interpreter for the scopes
initInterpreter(&interpreter); initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
for(;;) { for(;;) {
printf("> "); printf("> ");
fgets(input, size, stdin); fgets(input, size, stdin);

View File

@@ -1,4 +1,4 @@
#include "lib_builtin.h" #include "builtin.h"
#include "memory.h" #include "memory.h"
#include "literal.h" #include "literal.h"

View File

@@ -3,7 +3,6 @@
#include "interpreter.h" #include "interpreter.h"
int _index(Interpreter* interpreter, LiteralArray* arguments); int _index(Interpreter* interpreter, LiteralArray* arguments);
int _dot(Interpreter* interpreter, LiteralArray* arguments);
int _set(Interpreter* interpreter, LiteralArray* arguments); int _set(Interpreter* interpreter, LiteralArray* arguments);
int _get(Interpreter* interpreter, LiteralArray* arguments); int _get(Interpreter* interpreter, LiteralArray* arguments);
int _push(Interpreter* interpreter, LiteralArray* arguments); int _push(Interpreter* interpreter, LiteralArray* arguments);

View File

@@ -6,7 +6,7 @@
#include "keyword_types.h" #include "keyword_types.h"
#include "opcodes.h" #include "opcodes.h"
#include "lib_builtin.h" #include "builtin.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -56,6 +56,32 @@ bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func) {
return true; return true;
} }
bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook) {
//reject reserved words
if (findTypeByKeyword(name) != TOKEN_EOF) {
interpreter->errorOutput("Can't inject a hook on an existing keyword\n");
return false;
}
int identifierLength = strlen(name);
Literal identifier = TO_IDENTIFIER_LITERAL(copyString(name, identifierLength), identifierLength);
//make sure the name isn't taken
if (existsLiteralDictionary(interpreter->hooks, identifier)) {
interpreter->errorOutput("Can't override an existing hook\n");
return false;
}
Literal fn = TO_FUNCTION_LITERAL((void*)hook, 0);
fn.type = LITERAL_FUNCTION_NATIVE;
setLiteralDictionary(interpreter->hooks, identifier, fn);
freeLiteral(identifier);
return true;
}
void parseCompoundToPureValues(Interpreter* interpreter, Literal* literalPtr) { void parseCompoundToPureValues(Interpreter* interpreter, Literal* literalPtr) {
if (IS_IDENTIFIER(*literalPtr)) { if (IS_IDENTIFIER(*literalPtr)) {
parseIdentifierToValue(interpreter, literalPtr); parseIdentifierToValue(interpreter, literalPtr);
@@ -1180,6 +1206,7 @@ static bool execFnCall(Interpreter* interpreter, bool looseFirstArgument) {
initLiteralArray(&inner.stack); initLiteralArray(&inner.stack);
inner.exports = interpreter->exports; inner.exports = interpreter->exports;
inner.exportTypes = interpreter->exportTypes; inner.exportTypes = interpreter->exportTypes;
inner.hooks = interpreter->hooks;
setInterpreterPrint(&inner, interpreter->printOutput); setInterpreterPrint(&inner, interpreter->printOutput);
setInterpreterAssert(&inner, interpreter->assertOutput); setInterpreterAssert(&inner, interpreter->assertOutput);
setInterpreterError(&inner, interpreter->errorOutput); setInterpreterError(&inner, interpreter->errorOutput);
@@ -1413,6 +1440,31 @@ static bool execImport(Interpreter* interpreter) {
Literal alias = popLiteralArray(&interpreter->stack); Literal alias = popLiteralArray(&interpreter->stack);
Literal identifier = popLiteralArray(&interpreter->stack); Literal identifier = popLiteralArray(&interpreter->stack);
//access the hooks
if (existsLiteralDictionary(interpreter->hooks, identifier)) {
Literal func = getLiteralDictionary(interpreter->hooks, identifier);
if (!IS_FUNCTION_NATIVE(func)) {
interpreter->errorOutput("Expected native function for a hook: ");
printLiteralCustom(identifier, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
freeLiteral(func);
freeLiteral(alias);
freeLiteral(identifier);
return false;
}
HookFn fn = (HookFn)AS_FUNCTION(func).bytecode;
fn(interpreter, identifier, alias);
freeLiteral(func);
freeLiteral(alias);
freeLiteral(identifier);
return true;
}
Literal lit = getLiteralDictionary(interpreter->exports, identifier); Literal lit = getLiteralDictionary(interpreter->exports, identifier);
Literal type = getLiteralDictionary(interpreter->exportTypes, identifier); Literal type = getLiteralDictionary(interpreter->exportTypes, identifier);
@@ -2321,6 +2373,8 @@ void initInterpreter(Interpreter* interpreter) {
initLiteralDictionary(interpreter->exports); initLiteralDictionary(interpreter->exports);
interpreter->exportTypes = ALLOCATE(LiteralDictionary, 1); interpreter->exportTypes = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(interpreter->exportTypes); initLiteralDictionary(interpreter->exportTypes);
interpreter->hooks = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(interpreter->hooks);
//set up the output streams //set up the output streams
setInterpreterPrint(interpreter, printWrapper); setInterpreterPrint(interpreter, printWrapper);
@@ -2448,7 +2502,12 @@ void freeInterpreter(Interpreter* interpreter) {
freeLiteralDictionary(interpreter->exports); freeLiteralDictionary(interpreter->exports);
FREE(LiteralDictionary, interpreter->exports); FREE(LiteralDictionary, interpreter->exports);
interpreter->exports = NULL; interpreter->exports = NULL;
freeLiteralDictionary(interpreter->exportTypes); freeLiteralDictionary(interpreter->exportTypes);
FREE(LiteralDictionary, interpreter->exportTypes); FREE(LiteralDictionary, interpreter->exportTypes);
interpreter->exportTypes = NULL; interpreter->exportTypes = NULL;
freeLiteralDictionary(interpreter->hooks);
FREE(LiteralDictionary, interpreter->hooks);
interpreter->hooks = NULL;
} }

View File

@@ -21,9 +21,11 @@ typedef struct Interpreter {
Scope* scope; Scope* scope;
LiteralArray stack; LiteralArray stack;
//output
LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level
LiteralDictionary* exportTypes; LiteralDictionary* exportTypes;
LiteralDictionary* hooks;
//debug outputs
PrintFn printOutput; PrintFn printOutput;
PrintFn assertOutput; PrintFn assertOutput;
PrintFn errorOutput; PrintFn errorOutput;
@@ -32,10 +34,12 @@ typedef struct Interpreter {
bool panic; bool panic;
} Interpreter; } Interpreter;
//native function API //native API
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments); typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func); TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
//TODO: injectNativeHook
typedef int (*HookFn)(Interpreter* interpreter, Literal identifier, Literal alias);
TOY_API bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook);
//utilities for the host program //utilities for the host program
TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr); TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);

View File

@@ -1,11 +1,11 @@
CC=gcc CC=gcc
IDIR +=. IDIR+=.
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -pedantic -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -pedantic -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS += LIBS+=
ODIR = obj ODIR = obj
SRC = $(filter-out $(wildcard *main.c),$(wildcard *.c)) SRC = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o)) OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
OUTNAME=toy OUTNAME=toy
@@ -26,17 +26,11 @@ else
exit 1 exit 1
endif endif
REPLSRC = $(wildcard repl_main.c)
REPLOUT = $(OUTNAME)repl.exe
all: library $(addprefix $(ODIR)/,$(REPLSRC:.c=.o))
$(CC) -DTOY_IMPORT $(CFLAGS) -o ../$(OUTDIR)/$(REPLOUT) $(addprefix $(ODIR)/,$(REPLSRC:.c=.o)) $(LIBS) -L$(realpath $(shell pwd)/../$(OUTDIR)) -l$(OUTNAME)
library: $(OBJ) library: $(OBJ)
$(CC) -DTOY_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE) $(CC) -DTOY_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE)
static: $(OBJ) $(addprefix $(ODIR)/,$(REPLSRC:.c=.o)) static: $(OBJ)
$(CC) $(CFLAGS) -o ../$(OUTDIR)/$(REPLOUT) $(addprefix $(ODIR)/,$(REPLSRC:.c=.o)) $(OBJ) $(LIBS) ar crs ../$(OUTDIR)/lib$(OUTNAME).a $(OBJ)
$(OBJ): | $(ODIR) $(OBJ): | $(ODIR)