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

@@ -23,10 +23,10 @@ DONE: functions are first-class citizens
DONE: functions take a set number of parameters DONE: functions take a set number of parameters
DONE: functions last argument can be a rest parameter DONE: functions last argument can be a rest parameter
DONE: assert needs to kill the whole script, not just functions DONE: assert needs to kill the whole script, not just functions
DONE: native functions
DONE: global functions _get, _set, _push, _pop, _length, clear available
TODO: slice and dot notation around the _index function
TODO: functions return a set number of values
TODO: ternary operator TODO: ternary operator
TODO: Nullish types TODO: Nullish types
TODO: A way to check the type of a variable (typeOf keyword) TODO: A way to check the type of a variable (typeOf keyword)
@@ -37,3 +37,5 @@ TODO: standard library
TODO: external runner library TODO: external runner library
TODO: document how it all works TODO: document how it all works
TODO: third output stream, for parser/compiler/interpreter errors TODO: third output stream, for parser/compiler/interpreter errors
NOPE: functions return a set number of values

View File

@@ -448,7 +448,7 @@ print dict.foo; //syntactic sugar, only works if the key is not a built-in funct
## Indexing, Slice and Dot Notation ## Indexing, Slice and Dot Notation
Strings, arrays and dictionaries can be indexed in several ways, via the global `_get` and `_set` functions. Elements can be accessed using traditional bracket notation: Strings, arrays and dictionaries can be indexed in several ways, via the globally available functions (see below). Elements can be accessed using traditional bracket notation:
``` ```
str[0]; str[0];
@@ -490,22 +490,42 @@ print str[::-2]; //drwolH
0 cannot be used as the third argument. 0 cannot be used as the third argument.
### _get() and _set() ### Globally available functions
The slice and dot notations (the latter of which only works on dictionaries) are simply syntactic sugar for the global `_get` and `_set` functions. These functions take a number of arguments, which correlate to the slice and dot notations: The slice and dot notations (the latter of which only works on dictionaries) are simply syntactic sugar for the globally available functions.
``` ```
fn _get(self, first, second, third) { //usable with arrays and dictionaries
fn _set(self, key, value) {
//native code //native code
} }
fn _set(self, first, second, third, value) { //usable with arrays and dictionaries
fn _get(self, key) {
//native code
}
//usable with arrays
fn push(self, val) {
//native code
}
//usable with arrays
fn pop(self) {
//native code
}
//usable with arrays, dictionaries and strings
fn length(self) {
//native code
}
//usable with arrays and dictionaries
fn clear(self) {
//native code //native code
} }
``` ```
These functions can be overwritten.
## Standard Library ## Standard Library
The standard library has a number of utility functions available, and is provided by default. The standard library has a number of utility functions available, and is provided by default.
@@ -517,7 +537,6 @@ import "standard";
The following functions are available in the standard library. The following functions are available in the standard library.
* clear(self: any) - This function removes the contents of the array, dictionary or string, leaving an empty array. This alters the memory.
* clock() - This function returns the number of seconds since January 1st, 1970. * clock() - This function returns the number of seconds since January 1st, 1970.
* concat(self: any, x: any): any - This function requires an array or dictionary with a matching type as "x". This function returns a new dictionary instance which contains the contents of the current array or dictionary combined with the contents of "x". In the event of a dictionary key clash, the key-value pair in the current dictionary is included, and the key-value pair from "x" is discarded. * concat(self: any, x: any): any - This function requires an array or dictionary with a matching type as "x". This function returns a new dictionary instance which contains the contents of the current array or dictionary combined with the contents of "x". In the event of a dictionary key clash, the key-value pair in the current dictionary is included, and the key-value pair from "x" is discarded.
* containsKey(self: [any, any], k: any): bool - This function returns true if the dictionary contains a key "k", otherwise it returns false. * containsKey(self: [any, any], k: any): bool - This function returns true if the dictionary contains a key "k", otherwise it returns false.
@@ -533,8 +552,6 @@ The following functions are available in the standard library.
* lastIndexOf(self: string, str: string): int - This function returns the position of the last instance of "str" in the calling string "self". * lastIndexOf(self: string, str: string): int - This function returns the position of the last instance of "str" in the calling string "self".
* length(self: any): int - This function returns the length of the array, dictionary or string. * length(self: any): int - This function returns the length of the array, dictionary or string.
* map(self: any, cb: fn(k: any, v: any)(any)): any - This function calls "cb" once for every entry in the array or dictionary, with that key passed in as "k" and value passed in as "v". It returns a new array or dictionary with the keys copied from the current "self", and values replaced with the results of calls to "cb". * map(self: any, cb: fn(k: any, v: any)(any)): any - This function calls "cb" once for every entry in the array or dictionary, with that key passed in as "k" and value passed in as "v". It returns a new array or dictionary with the keys copied from the current "self", and values replaced with the results of calls to "cb".
* pop(self: [any]): any - This function deletes the value at the end of the array, and returns that value.
* push(self: [any], x: type) - This function inserts the value of "x" at the end of the array.
* reduce(self: any, default: any, cb: fn(acc: any, k: any, v: any): any): any - This function calls "cb" once for every element in the array or dictionary "self", with that element passed in as "k" and "v", and the value of the previous call passed in as "acc". For the first call to "cb", "default" is used for "acc". The final value of "acc" is returned by "reduce". * reduce(self: any, default: any, cb: fn(acc: any, k: any, v: any): any): any - This function calls "cb" once for every element in the array or dictionary "self", with that element passed in as "k" and "v", and the value of the previous call passed in as "acc". For the first call to "cb", "default" is used for "acc". The final value of "acc" is returned by "reduce".
* remove(self: any, k: any) - This function deletes the value at the index or key "k", shifting the remaining entries down 1 index if it's an array. This alters the memory. * remove(self: any, k: any) - This function deletes the value at the index or key "k", shifting the remaining entries down 1 index if it's an array. This alters the memory.
* replace(self: string, pat: string, rep: string): string - For each instance of "pat" that it finds in the calling string "self", it replaces it with "rep", then returns the new string. * replace(self: string, pat: string, rep: string): string - For each instance of "pat" that it finds in the calling string "self", it replaces it with "rep", then returns the new string.

View File

@@ -3,6 +3,7 @@
#include "common.h" #include "common.h"
#include "memory.h" #include "memory.h"
#include "keyword_types.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -18,6 +19,298 @@ static void stderrWrapper(const char* output) {
fprintf(stderr, "\n" RESET); //default new line 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) { void initInterpreter(Interpreter* interpreter) {
initLiteralArray(&interpreter->literalCache); initLiteralArray(&interpreter->literalCache);
interpreter->scope = pushScope(NULL); interpreter->scope = pushScope(NULL);
@@ -30,6 +323,14 @@ void initInterpreter(Interpreter* interpreter) {
setInterpreterPrint(interpreter, stdoutWrapper); setInterpreterPrint(interpreter, stdoutWrapper);
setInterpreterAssert(interpreter, stderrWrapper); 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; interpreter->panic = false;
} }
@@ -112,20 +413,6 @@ static void consumeShort(unsigned short bytes, unsigned char* tb, int* count) {
*count += 2; *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 //each available statement
static bool execAssert(Interpreter* interpreter) { static bool execAssert(Interpreter* interpreter) {
Literal rhs = popLiteralArray(&interpreter->stack); Literal rhs = popLiteralArray(&interpreter->stack);
@@ -763,7 +1050,30 @@ static bool execFnCall(Interpreter* interpreter) {
Literal identifier = popLiteralArray(&interpreter->stack); Literal identifier = popLiteralArray(&interpreter->stack);
Literal func = identifier; 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 //set up a new interpreter
Interpreter inner; Interpreter inner;

View File

@@ -29,6 +29,12 @@ typedef struct Interpreter {
bool panic; bool panic;
} Interpreter; } 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 initInterpreter(Interpreter* interpreter);
void freeInterpreter(Interpreter* interpreter); void freeInterpreter(Interpreter* interpreter);

View File

@@ -2,6 +2,8 @@
#include "common.h" #include "common.h"
#include <string.h>
KeywordType keywordTypes[] = { KeywordType keywordTypes[] = {
//type keywords //type keywords
{TOKEN_NULL, "null"}, {TOKEN_NULL, "null"},
@@ -57,4 +59,16 @@ char* findKeywordByType(TokenType type) {
} }
return NULL; 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[]; extern KeywordType keywordTypes[];
char* findKeywordByType(TokenType type); 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_TYPE_INTERMEDIATE, //used to process types in the compiler only
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions 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_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 LITERAL_ANY, //used by the type system only
} LiteralType; } LiteralType;
@@ -68,6 +68,7 @@ typedef struct {
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY) #define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY) #define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION) #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_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
#define IS_TYPE(value) ((value).type == LITERAL_TYPE) #define IS_TYPE(value) ((value).type == LITERAL_TYPE)

View File

@@ -113,9 +113,9 @@ static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int
*dictionaryHandle = newEntries; *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 //expand array if needed
if (count + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) { if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
int oldCapacity = *capacityPtr; int oldCapacity = *capacityPtr;
*capacityPtr = GROW_CAPACITY(*capacityPtr); *capacityPtr = GROW_CAPACITY(*capacityPtr);
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation 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))); value = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(value), STRLEN_I(value)));
} }
//true = count increase //true = contains increase
if (IS_NULL(entry->key)) { if (IS_NULL(entry->key)) {
setEntryValues(entry, key, value); setEntryValues(entry, key, value);
return true; 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) //HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL; dictionary->entries = NULL;
dictionary->capacity = GROW_CAPACITY(0); dictionary->capacity = GROW_CAPACITY(0);
dictionary->contains = 0;
dictionary->count = 0; dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity); adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
} }
@@ -184,7 +185,7 @@ void initLiteralDictionary(LiteralDictionary* dictionary) {
void freeLiteralDictionary(LiteralDictionary* dictionary) { void freeLiteralDictionary(LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity); freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0; dictionary->capacity = 0;
dictionary->count = 0; dictionary->contains = 0;
} }
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) { void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
@@ -193,9 +194,10 @@ void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal va
return; 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) { if (increment) {
dictionary->contains++;
dictionary->count++; dictionary->count++;
} }
} }
@@ -227,6 +229,7 @@ void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
if (entry != NULL) { if (entry != NULL) {
freeEntry(entry); freeEntry(entry);
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
dictionary->count--;
} }
} }

View File

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

View File

@@ -198,3 +198,17 @@ bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
*valueHandle = getLiteralDictionary(&scope->variables, key); *valueHandle = getLiteralDictionary(&scope->variables, key);
return true; 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 setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
bool getScopeVariable(Scope* scope, Literal key, Literal* value); bool getScopeVariable(Scope* scope, Literal key, Literal* value);
Literal getScopeType(Scope* scope, Literal key);

87
test/native-functions.toy Normal file
View File

@@ -0,0 +1,87 @@
{
//test arrays without types
var array = [];
_push(array, 1);
_push(array, 2);
_push(array, 3);
_push(array, 4);
_push(array, "foo");
assert _length(array) == 5, "_push or _length failed with array";
assert _pop(array) == "foo", "_pop failed with array";
_set(array, 2, "bar");
assert array == [1, 2, "bar", 4], "_set failed with array";
assert _get(array, 3) == 4, "_get failed with array";
//test dictionaries without types
var dict = [:];
_set(dict, "key", "value");
_set(dict, 1, 2);
assert dict == ["key":"value", 1:2], "_set failed with dictionaries";
assert _get(dict, "key") == "value", "_get failed with dictionaries";
//test _length
assert _length(array) == 4 && _length(dict) == 2, "_length failed with array or dictionaries";
//test clear
_clear(array);
_clear(dict);
assert _length(array) == 0 && _length(dict) == 0, "_clear failed with array or dictionaries";
}
{
//test arrays with types
var array: [int] = [];
_push(array, 1);
_push(array, 2);
_push(array, 3);
_push(array, 4);
_push(array, 10);
assert _length(array) == 5, "_push or _length failed with array (+ types)";
assert _pop(array) == 10, "_pop failed with array (+ types)";
_set(array, 2, 70);
assert array == [1, 2, 70, 4], "_set failed with array (+ types)";
assert _get(array, 3) == 4, "_get failed with array (+ types)";
//test dictionaries with types
var dict: [string, string] = [:];
_set(dict, "key", "value");
assert dict == ["key":"value"], "_set failed with dictionaries (+ types)";
assert _get(dict, "key") == "value", "_get failed with dictionaries (+ types)";
//test length with types
assert _length(array) == 4 && _length(dict) == 1, "_length failed with array or dictionaries (+ types)";
//test clear with types
_clear(array);
_clear(dict);
assert _length(array) == 0 && _length(dict) == 0, "_clear failed with array or dictionaries (+ types)";
}
{
var str = "hello world";
assert _length(str) == 11, "_length failed with string";
}
print "All good";