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:
@@ -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
|
||||||
37
docs/spec.md
37
docs/spec.md
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
87
test/native-functions.toy
Normal 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";
|
||||||
Reference in New Issue
Block a user