Native functions are working

This commit is contained in:
2022-08-27 15:39:42 +01:00
parent e523a6f60a
commit ec91bac8a5
12 changed files with 492 additions and 34 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -10,3 +10,5 @@ typedef struct {
extern KeywordType keywordTypes[];
char* findKeywordByType(TokenType type);
TokenType findTypeByKeyword(const char* keyword);

View File

@@ -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)

View File

@@ -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--;
}
}

View File

@@ -13,6 +13,7 @@ typedef struct _entry {
typedef struct LiteralDictionary {
_entry* entries;
int capacity;
int contains; //count + tombstones
int count;
} LiteralDictionary;

View File

@@ -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);
}

View File

@@ -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);