mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Native functions are working
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "memory.h"
|
||||
#include "keyword_types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -18,6 +19,298 @@ static void stderrWrapper(const char* output) {
|
||||
fprintf(stderr, "\n" RESET); //default new line
|
||||
}
|
||||
|
||||
bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func) {
|
||||
//reject reserved words
|
||||
if (findTypeByKeyword(name) != TOKEN_EOF) {
|
||||
printf("Can't override an existing keyword");
|
||||
return false;
|
||||
}
|
||||
|
||||
Literal identifier = TO_IDENTIFIER_LITERAL(name);
|
||||
|
||||
//make sure the name isn't taken
|
||||
if (existsLiteralDictionary(&interpreter->scope->variables, identifier)) {
|
||||
printf("Can't override an existing variable");
|
||||
return false;
|
||||
}
|
||||
|
||||
Literal fn = TO_FUNCTION_LITERAL((void*)func, 0);
|
||||
fn.type = LITERAL_FUNCTION_NATIVE;
|
||||
|
||||
Literal type = TO_TYPE_LITERAL(fn.type, true);
|
||||
|
||||
setLiteralDictionary(&interpreter->scope->variables, identifier, fn);
|
||||
setLiteralDictionary(&interpreter->scope->types, identifier, type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr) {
|
||||
//this converts identifiers to values
|
||||
if (IS_IDENTIFIER(*literalPtr)) {
|
||||
if (!getScopeVariable(interpreter->scope, *literalPtr, literalPtr)) {
|
||||
printf(ERROR "Error: Undeclared variable \"");;
|
||||
printLiteral(*literalPtr);
|
||||
printf("\"\n" RESET);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int _set(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 3) {
|
||||
(interpreter->printOutput)("Incorrect number of arguments to _set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Literal obj = arguments->literals[0];
|
||||
Literal key = arguments->literals[1];
|
||||
Literal val = arguments->literals[2];
|
||||
|
||||
parseIdentifierToValue(interpreter, &obj);
|
||||
|
||||
switch(obj.type) {
|
||||
case LITERAL_ARRAY: {
|
||||
Literal typeLiteral = getScopeType(interpreter->scope, key);
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY) {
|
||||
Literal subtypeLiteral = ((Literal*)(AS_TYPE(typeLiteral).subtypes))[0];
|
||||
|
||||
if (AS_TYPE(subtypeLiteral).typeOf != LITERAL_ANY && AS_TYPE(subtypeLiteral).typeOf != val.type) {
|
||||
(interpreter->printOutput)("bad argument type in _set");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_INTEGER(key)) {
|
||||
(interpreter->printOutput)("Expected integer index in _set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (AS_ARRAY(obj)->count <= AS_INTEGER(key) || AS_INTEGER(key) < 0) {
|
||||
(interpreter->printOutput)("Index out of bounds in _set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
parseIdentifierToValue(interpreter, &val);
|
||||
|
||||
//if it's a string or an identifier, make a local copy
|
||||
if (IS_STRING(val)) {
|
||||
val = TO_STRING_LITERAL(copyString(AS_STRING(val), STRLEN(val)));
|
||||
}
|
||||
if (IS_IDENTIFIER(val)) {
|
||||
val = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(val), STRLEN_I(val)));
|
||||
}
|
||||
|
||||
//TODO: proper copy function for literals
|
||||
|
||||
AS_ARRAY(obj)->literals[AS_INTEGER(key)] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case LITERAL_DICTIONARY: {
|
||||
Literal typeLiteral = getScopeType(interpreter->scope, key);
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_DICTIONARY) {
|
||||
Literal keySubtypeLiteral = ((Literal*)(AS_TYPE(typeLiteral).subtypes))[0];
|
||||
Literal valSubtypeLiteral = ((Literal*)(AS_TYPE(typeLiteral).subtypes))[1];
|
||||
|
||||
if (AS_TYPE(keySubtypeLiteral).typeOf != LITERAL_ANY && AS_TYPE(keySubtypeLiteral).typeOf != key.type) {
|
||||
(interpreter->printOutput)("bad argument type in _set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (AS_TYPE(valSubtypeLiteral).typeOf != LITERAL_ANY && AS_TYPE(valSubtypeLiteral).typeOf != val.type) {
|
||||
(interpreter->printOutput)("bad argument type in _set");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
parseIdentifierToValue(interpreter, &key);
|
||||
parseIdentifierToValue(interpreter, &val);
|
||||
|
||||
setLiteralDictionary(AS_DICTIONARY(obj), key, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
(interpreter->printOutput)("Incorrect compound type in _set");
|
||||
printLiteral(obj);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _get(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 2) {
|
||||
(interpreter->printOutput)("Incorrect number of arguments to _get");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Literal obj = arguments->literals[0];
|
||||
Literal key = arguments->literals[1];
|
||||
|
||||
parseIdentifierToValue(interpreter, &obj);
|
||||
|
||||
switch(obj.type) {
|
||||
case LITERAL_ARRAY: {
|
||||
if (!IS_INTEGER(key)) {
|
||||
(interpreter->printOutput)("Expected integer index in _get");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (AS_ARRAY(obj)->count <= AS_INTEGER(key) || AS_INTEGER(key) < 0) {
|
||||
(interpreter->printOutput)("Index out of bounds in _get");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pushLiteralArray(&interpreter->stack, AS_ARRAY(obj)->literals[AS_INTEGER(key)]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case LITERAL_DICTIONARY:
|
||||
pushLiteralArray(&interpreter->stack, getLiteralDictionary(AS_DICTIONARY(obj), key));
|
||||
return 1;
|
||||
|
||||
default:
|
||||
(interpreter->printOutput)("Incorrect compound type in _get");
|
||||
printLiteral(obj);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _push(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 2) {
|
||||
(interpreter->printOutput)("Incorrect number of arguments to _push");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Literal obj = arguments->literals[0];
|
||||
Literal val = arguments->literals[1];
|
||||
|
||||
parseIdentifierToValue(interpreter, &obj);
|
||||
|
||||
switch(obj.type) {
|
||||
case LITERAL_ARRAY: {
|
||||
Literal typeLiteral = getScopeType(interpreter->scope, val);
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY) {
|
||||
Literal subtypeLiteral = ((Literal*)(AS_TYPE(typeLiteral).subtypes))[0];
|
||||
|
||||
if (AS_TYPE(subtypeLiteral).typeOf != LITERAL_ANY && AS_TYPE(subtypeLiteral).typeOf != val.type) {
|
||||
(interpreter->printOutput)("bad argument type in _push");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
parseIdentifierToValue(interpreter, &val);
|
||||
|
||||
pushLiteralArray(AS_ARRAY(obj), val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
(interpreter->printOutput)("Incorrect compound type in _push");
|
||||
printLiteral(obj);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _pop(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 1) {
|
||||
(interpreter->printOutput)("Incorrect number of arguments to _pop");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Literal obj = arguments->literals[0];
|
||||
|
||||
parseIdentifierToValue(interpreter, &obj);
|
||||
|
||||
switch(obj.type) {
|
||||
case LITERAL_ARRAY: {
|
||||
pushLiteralArray(&interpreter->stack, popLiteralArray(AS_ARRAY(obj)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
(interpreter->printOutput)("Incorrect compound type in _pop");
|
||||
printLiteral(obj);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _length(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 1) {
|
||||
(interpreter->printOutput)("Incorrect number of arguments to _get");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Literal obj = arguments->literals[0];
|
||||
|
||||
parseIdentifierToValue(interpreter, &obj);
|
||||
|
||||
switch(obj.type) {
|
||||
case LITERAL_ARRAY: {
|
||||
pushLiteralArray(&interpreter->stack, TO_INTEGER_LITERAL( AS_ARRAY(obj)->count ));
|
||||
return 1;
|
||||
}
|
||||
|
||||
case LITERAL_DICTIONARY:
|
||||
pushLiteralArray(&interpreter->stack, TO_INTEGER_LITERAL( AS_DICTIONARY(obj)->count ));
|
||||
return 1;
|
||||
|
||||
case LITERAL_STRING:
|
||||
pushLiteralArray(&interpreter->stack, TO_INTEGER_LITERAL( STRLEN(obj) ));
|
||||
return 1;
|
||||
|
||||
default:
|
||||
(interpreter->printOutput)("Incorrect compound type in _length");
|
||||
printLiteral(obj);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _clear(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//if wrong number of arguments, fail
|
||||
if (arguments->count != 1) {
|
||||
(interpreter->printOutput)("Incorrect number of arguments to _get");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Literal obj = arguments->literals[0];
|
||||
|
||||
parseIdentifierToValue(interpreter, &obj);
|
||||
|
||||
switch(obj.type) {
|
||||
case LITERAL_ARRAY: {
|
||||
while(AS_ARRAY(obj)->count) {
|
||||
popLiteralArray(AS_ARRAY(obj));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
case LITERAL_DICTIONARY: {
|
||||
for (int i = 0; i < AS_DICTIONARY(obj)->capacity; i++) {
|
||||
if ( !IS_NULL(AS_DICTIONARY(obj)->entries[i].key) ) {
|
||||
removeLiteralDictionary(AS_DICTIONARY(obj), AS_DICTIONARY(obj)->entries[i].key);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
(interpreter->printOutput)("Incorrect compound type in _get");
|
||||
printLiteral(obj);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void initInterpreter(Interpreter* interpreter) {
|
||||
initLiteralArray(&interpreter->literalCache);
|
||||
interpreter->scope = pushScope(NULL);
|
||||
@@ -30,6 +323,14 @@ void initInterpreter(Interpreter* interpreter) {
|
||||
setInterpreterPrint(interpreter, stdoutWrapper);
|
||||
setInterpreterAssert(interpreter, stderrWrapper);
|
||||
|
||||
//globally available functions (tmp?)
|
||||
injectNativeFn(interpreter, "_set", _set);
|
||||
injectNativeFn(interpreter, "_get", _get);
|
||||
injectNativeFn(interpreter, "_push", _push);
|
||||
injectNativeFn(interpreter, "_pop", _pop);
|
||||
injectNativeFn(interpreter, "_length", _length);
|
||||
injectNativeFn(interpreter, "_clear", _clear);
|
||||
|
||||
interpreter->panic = false;
|
||||
}
|
||||
|
||||
@@ -112,20 +413,6 @@ static void consumeShort(unsigned short bytes, unsigned char* tb, int* count) {
|
||||
*count += 2;
|
||||
}
|
||||
|
||||
static bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr) {
|
||||
//this converts identifiers to values
|
||||
if (IS_IDENTIFIER(*literalPtr)) {
|
||||
if (!getScopeVariable(interpreter->scope, *literalPtr, literalPtr)) {
|
||||
printf(ERROR "Error: Undeclared variable \"");;
|
||||
printLiteral(*literalPtr);
|
||||
printf("\"\n" RESET);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//each available statement
|
||||
static bool execAssert(Interpreter* interpreter) {
|
||||
Literal rhs = popLiteralArray(&interpreter->stack);
|
||||
@@ -763,7 +1050,30 @@ static bool execFnCall(Interpreter* interpreter) {
|
||||
Literal identifier = popLiteralArray(&interpreter->stack);
|
||||
|
||||
Literal func = identifier;
|
||||
parseIdentifierToValue(interpreter, &func);
|
||||
|
||||
if (!parseIdentifierToValue(interpreter, &func)) {
|
||||
freeLiteralArray(&arguments);
|
||||
return false;
|
||||
}
|
||||
|
||||
//check for side-loaded native functions
|
||||
if (IS_FUNCTION_NATIVE(func)) {
|
||||
//reverse the order to the correct order
|
||||
LiteralArray correct;
|
||||
initLiteralArray(&correct);
|
||||
|
||||
while(arguments.count) {
|
||||
pushLiteralArray(&correct, popLiteralArray(&arguments));
|
||||
}
|
||||
|
||||
freeLiteralArray(&arguments);
|
||||
|
||||
//call the native function
|
||||
((NativeFn) AS_FUNCTION(func) )(interpreter, &correct);
|
||||
|
||||
freeLiteralArray(&correct);
|
||||
return true;
|
||||
}
|
||||
|
||||
//set up a new interpreter
|
||||
Interpreter inner;
|
||||
|
||||
@@ -29,6 +29,12 @@ typedef struct Interpreter {
|
||||
bool panic;
|
||||
} Interpreter;
|
||||
|
||||
//for native function API
|
||||
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
|
||||
bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
|
||||
bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);
|
||||
|
||||
//init & free
|
||||
void initInterpreter(Interpreter* interpreter);
|
||||
void freeInterpreter(Interpreter* interpreter);
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
KeywordType keywordTypes[] = {
|
||||
//type keywords
|
||||
{TOKEN_NULL, "null"},
|
||||
@@ -57,4 +59,16 @@ char* findKeywordByType(TokenType type) {
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TokenType findTypeByKeyword(const char* keyword) {
|
||||
const int length = strlen(keyword);
|
||||
|
||||
for (int i = 0; keywordTypes[i].keyword; i++) {
|
||||
if (!strncmp(keyword, keywordTypes[i].keyword, length)) {
|
||||
return keywordTypes[i].type;
|
||||
}
|
||||
}
|
||||
|
||||
return TOKEN_EOF;
|
||||
}
|
||||
@@ -10,3 +10,5 @@ typedef struct {
|
||||
extern KeywordType keywordTypes[];
|
||||
|
||||
char* findKeywordByType(TokenType type);
|
||||
|
||||
TokenType findTypeByKeyword(const char* keyword);
|
||||
@@ -20,7 +20,7 @@ typedef enum {
|
||||
LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
||||
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
||||
LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters
|
||||
// LITERAL_FUNCTION_NATIVE, //for handling native functions
|
||||
LITERAL_FUNCTION_NATIVE, //for handling native functions
|
||||
LITERAL_ANY, //used by the type system only
|
||||
} LiteralType;
|
||||
|
||||
@@ -68,6 +68,7 @@ typedef struct {
|
||||
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
|
||||
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
|
||||
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION)
|
||||
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
|
||||
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
|
||||
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
|
||||
|
||||
|
||||
@@ -113,9 +113,9 @@ static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int
|
||||
*dictionaryHandle = newEntries;
|
||||
}
|
||||
|
||||
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int count, Literal key, Literal value, int hash) {
|
||||
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int contains, Literal key, Literal value, int hash) {
|
||||
//expand array if needed
|
||||
if (count + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
|
||||
if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
|
||||
int oldCapacity = *capacityPtr;
|
||||
*capacityPtr = GROW_CAPACITY(*capacityPtr);
|
||||
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
||||
@@ -138,7 +138,7 @@ static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int count
|
||||
value = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(value), STRLEN_I(value)));
|
||||
}
|
||||
|
||||
//true = count increase
|
||||
//true = contains increase
|
||||
if (IS_NULL(entry->key)) {
|
||||
setEntryValues(entry, key, value);
|
||||
return true;
|
||||
@@ -177,6 +177,7 @@ void initLiteralDictionary(LiteralDictionary* dictionary) {
|
||||
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
|
||||
dictionary->entries = NULL;
|
||||
dictionary->capacity = GROW_CAPACITY(0);
|
||||
dictionary->contains = 0;
|
||||
dictionary->count = 0;
|
||||
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
|
||||
}
|
||||
@@ -184,7 +185,7 @@ void initLiteralDictionary(LiteralDictionary* dictionary) {
|
||||
void freeLiteralDictionary(LiteralDictionary* dictionary) {
|
||||
freeEntryArray(dictionary->entries, dictionary->capacity);
|
||||
dictionary->capacity = 0;
|
||||
dictionary->count = 0;
|
||||
dictionary->contains = 0;
|
||||
}
|
||||
|
||||
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
|
||||
@@ -193,9 +194,10 @@ void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal va
|
||||
return;
|
||||
}
|
||||
|
||||
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->count, key, value, hashLiteral(key));
|
||||
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, hashLiteral(key));
|
||||
|
||||
if (increment) {
|
||||
dictionary->contains++;
|
||||
dictionary->count++;
|
||||
}
|
||||
}
|
||||
@@ -227,6 +229,7 @@ void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||
if (entry != NULL) {
|
||||
freeEntry(entry);
|
||||
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
|
||||
dictionary->count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ typedef struct _entry {
|
||||
typedef struct LiteralDictionary {
|
||||
_entry* entries;
|
||||
int capacity;
|
||||
int contains; //count + tombstones
|
||||
int count;
|
||||
} LiteralDictionary;
|
||||
|
||||
|
||||
@@ -198,3 +198,17 @@ bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
|
||||
*valueHandle = getLiteralDictionary(&scope->variables, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
Literal getScopeType(Scope* scope, Literal key) {
|
||||
//dead end
|
||||
if (scope == NULL) {
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!existsLiteralDictionary(&scope->types, key)) {
|
||||
return getScopeType(scope->ancestor, key);
|
||||
}
|
||||
|
||||
return getLiteralDictionary(&scope->types, key);
|
||||
}
|
||||
|
||||
@@ -21,3 +21,4 @@ bool isDelcaredScopeVariable(Scope* scope, Literal key);
|
||||
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
|
||||
bool getScopeVariable(Scope* scope, Literal key, Literal* value);
|
||||
|
||||
Literal getScopeType(Scope* scope, Literal key);
|
||||
Reference in New Issue
Block a user