mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Implemented typeof and added resetInterpreter()
This commit is contained in:
@@ -31,17 +31,18 @@ DONE: are compounds shallow or deep copies? Deep copies
|
|||||||
DONE: third output stream, for lexer/parser/compiler/interpreter errors
|
DONE: third output stream, for lexer/parser/compiler/interpreter errors
|
||||||
DONE: Assertion-based test scripts
|
DONE: Assertion-based test scripts
|
||||||
DONE: Import/export keywords
|
DONE: Import/export keywords
|
||||||
|
DONE: A way to check the type of a variable (typeOf keyword)
|
||||||
|
|
||||||
|
|
||||||
TODO: slice and dot notation around the builtin _index function
|
TODO: slice and dot notation around the builtin _index function
|
||||||
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: hooks on the external libraries, triggered on import
|
||||||
TODO: a = b = c = 1; ?
|
|
||||||
TODO: standard library
|
TODO: standard library
|
||||||
TODO: external script runner library
|
TODO: external script runner library
|
||||||
TODO: document how it all works - book?
|
TODO: document how it all works - book?
|
||||||
TODO: maximum recursion/function depth
|
TODO: maximum recursion/function depth
|
||||||
|
|
||||||
|
|
||||||
|
NOPE: a = b = c = 1;
|
||||||
NOPE: functions return a set number of values
|
NOPE: functions return a set number of values
|
||||||
@@ -49,6 +49,7 @@ The following list of keywords cannot be used as names, due to their significanc
|
|||||||
* string
|
* string
|
||||||
* true
|
* true
|
||||||
* type
|
* type
|
||||||
|
* typeof
|
||||||
* var
|
* var
|
||||||
* while
|
* while
|
||||||
|
|
||||||
@@ -163,11 +164,7 @@ var bar = "hello world";
|
|||||||
var buzz;
|
var buzz;
|
||||||
```
|
```
|
||||||
|
|
||||||
Variables can be used in place of values at any time, and can be altered and re-assigned. Multiple variables (which have been previously declared) can be assigned to the same value:
|
Variables can be used in place of values at any time, and can be altered and re-assigned.
|
||||||
|
|
||||||
```
|
|
||||||
a = b = c = 1;
|
|
||||||
```
|
|
||||||
|
|
||||||
## If-Else
|
## If-Else
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,17 @@
|
|||||||
assert func() == 69, "import/export of functions failed";
|
assert func() == 69, "import/export of functions failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: test that variables retain their types with the typeOf keyword
|
//test that variables retain their types with the typeof keyword
|
||||||
|
{
|
||||||
|
var t: type = int;
|
||||||
|
|
||||||
|
export t;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
import t;
|
||||||
|
|
||||||
|
assert typeof t == type type, "type retention failed";
|
||||||
|
}
|
||||||
|
|
||||||
print "All good";
|
print "All good";
|
||||||
|
|||||||
@@ -71,40 +71,6 @@ bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initInterpreter(Interpreter* interpreter) {
|
|
||||||
//NOTE: separate initialization for exports
|
|
||||||
interpreter->exports = ALLOCATE(LiteralDictionary, 1);
|
|
||||||
initLiteralDictionary(interpreter->exports);
|
|
||||||
interpreter->exportTypes = ALLOCATE(LiteralDictionary, 1);
|
|
||||||
initLiteralDictionary(interpreter->exportTypes);
|
|
||||||
|
|
||||||
//set up the output streams
|
|
||||||
setInterpreterPrint(interpreter, printWrapper);
|
|
||||||
setInterpreterAssert(interpreter, assertWrapper);
|
|
||||||
setInterpreterError(interpreter, errorWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeInterpreter(Interpreter* interpreter) {
|
|
||||||
//BUGFIX: handle scopes/types in the exports
|
|
||||||
for (int i = 0; i < interpreter->exports->capacity; i++) {
|
|
||||||
if (IS_FUNCTION(interpreter->exports->entries[i].key)) {
|
|
||||||
popScope(AS_FUNCTION(interpreter->exports->entries[i].key).scope);
|
|
||||||
AS_FUNCTION(interpreter->exports->entries[i].key).scope = NULL;
|
|
||||||
}
|
|
||||||
if (IS_FUNCTION(interpreter->exports->entries[i].value)) {
|
|
||||||
popScope(AS_FUNCTION(interpreter->exports->entries[i].value).scope);
|
|
||||||
AS_FUNCTION(interpreter->exports->entries[i].value).scope = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freeLiteralDictionary(interpreter->exports);
|
|
||||||
FREE(LiteralDictionary, interpreter->exports);
|
|
||||||
interpreter->exports = NULL;
|
|
||||||
freeLiteralDictionary(interpreter->exportTypes);
|
|
||||||
FREE(LiteralDictionary, interpreter->exportTypes);
|
|
||||||
interpreter->exportTypes = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//utilities for the host program
|
//utilities for the host program
|
||||||
void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput) {
|
void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput) {
|
||||||
interpreter->printOutput = printOutput;
|
interpreter->printOutput = printOutput;
|
||||||
@@ -722,6 +688,25 @@ static bool execValCast(Interpreter* interpreter) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool execTypeOf(Interpreter* interpreter) {
|
||||||
|
Literal rhs = popLiteralArray(&interpreter->stack);
|
||||||
|
Literal type = TO_NULL_LITERAL;
|
||||||
|
|
||||||
|
if (IS_IDENTIFIER(rhs)) {
|
||||||
|
type = getScopeType(interpreter->scope, rhs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
type = TO_TYPE_LITERAL(rhs.type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pushLiteralArray(&interpreter->stack, type);
|
||||||
|
|
||||||
|
freeLiteral(rhs);
|
||||||
|
freeLiteral(type);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool execCompareEqual(Interpreter* interpreter, bool invert) {
|
static bool execCompareEqual(Interpreter* interpreter, bool invert) {
|
||||||
Literal rhs = popLiteralArray(&interpreter->stack);
|
Literal rhs = popLiteralArray(&interpreter->stack);
|
||||||
Literal lhs = popLiteralArray(&interpreter->stack);
|
Literal lhs = popLiteralArray(&interpreter->stack);
|
||||||
@@ -1469,6 +1454,12 @@ static void execInterpreter(Interpreter* interpreter) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OP_TYPE_OF:
|
||||||
|
if (!execTypeOf(interpreter)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case OP_COMPARE_EQUAL:
|
case OP_COMPARE_EQUAL:
|
||||||
if (!execCompareEqual(interpreter, false)) {
|
if (!execCompareEqual(interpreter, false)) {
|
||||||
return;
|
return;
|
||||||
@@ -1823,16 +1814,26 @@ static void readInterpreterSections(Interpreter* interpreter) {
|
|||||||
consumeByte(interpreter, OP_SECTION_END, interpreter->bytecode, &interpreter->count); //terminate the function section
|
consumeByte(interpreter, OP_SECTION_END, interpreter->bytecode, &interpreter->count); //terminate the function section
|
||||||
}
|
}
|
||||||
|
|
||||||
void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) {
|
//exposed functions
|
||||||
//check for export zone
|
void initInterpreter(Interpreter* interpreter) {
|
||||||
if (!interpreter->exports || !interpreter->exportTypes) {
|
//NOTE: separate initialization for exports
|
||||||
interpreter->errorOutput("Interpreter has no export region\n");
|
interpreter->exports = ALLOCATE(LiteralDictionary, 1);
|
||||||
return;
|
initLiteralDictionary(interpreter->exports);
|
||||||
|
interpreter->exportTypes = ALLOCATE(LiteralDictionary, 1);
|
||||||
|
initLiteralDictionary(interpreter->exportTypes);
|
||||||
|
|
||||||
|
//set up the output streams
|
||||||
|
setInterpreterPrint(interpreter, printWrapper);
|
||||||
|
setInterpreterAssert(interpreter, assertWrapper);
|
||||||
|
setInterpreterError(interpreter, errorWrapper);
|
||||||
|
|
||||||
|
interpreter->scope = NULL;
|
||||||
|
resetInterpreter(interpreter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) {
|
||||||
//initialize here instead of initInterpreter()
|
//initialize here instead of initInterpreter()
|
||||||
initLiteralArray(&interpreter->literalCache);
|
initLiteralArray(&interpreter->literalCache);
|
||||||
interpreter->scope = pushScope(NULL);
|
|
||||||
interpreter->bytecode = NULL;
|
interpreter->bytecode = NULL;
|
||||||
interpreter->length = 0;
|
interpreter->length = 0;
|
||||||
interpreter->count = 0;
|
interpreter->count = 0;
|
||||||
@@ -1840,14 +1841,6 @@ void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int lengt
|
|||||||
|
|
||||||
initLiteralArray(&interpreter->stack);
|
initLiteralArray(&interpreter->stack);
|
||||||
|
|
||||||
//globally available functions
|
|
||||||
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;
|
||||||
|
|
||||||
//prep the bytecode
|
//prep the bytecode
|
||||||
@@ -1902,15 +1895,54 @@ void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int lengt
|
|||||||
freeLiteral(lit);
|
freeLiteral(lit);
|
||||||
}
|
}
|
||||||
|
|
||||||
//free the bytecode
|
//free the bytecode immediately after use
|
||||||
FREE_ARRAY(unsigned char, interpreter->bytecode, interpreter->length);
|
FREE_ARRAY(unsigned char, interpreter->bytecode, interpreter->length);
|
||||||
|
|
||||||
//free the associated data
|
//free the associated data
|
||||||
freeLiteralArray(&interpreter->literalCache);
|
freeLiteralArray(&interpreter->literalCache);
|
||||||
freeLiteralArray(&interpreter->stack);
|
freeLiteralArray(&interpreter->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetInterpreter(Interpreter* interpreter) {
|
||||||
//free the interpreter scope
|
//free the interpreter scope
|
||||||
while(interpreter->scope != NULL) {
|
while(interpreter->scope != NULL) {
|
||||||
interpreter->scope = popScope(interpreter->scope);
|
interpreter->scope = popScope(interpreter->scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//prep the scope
|
||||||
|
interpreter->scope = pushScope(NULL);
|
||||||
|
|
||||||
|
//globally available functions
|
||||||
|
injectNativeFn(interpreter, "_set", _set);
|
||||||
|
injectNativeFn(interpreter, "_get", _get);
|
||||||
|
injectNativeFn(interpreter, "_push", _push);
|
||||||
|
injectNativeFn(interpreter, "_pop", _pop);
|
||||||
|
injectNativeFn(interpreter, "_length", _length);
|
||||||
|
injectNativeFn(interpreter, "_clear", _clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeInterpreter(Interpreter* interpreter) {
|
||||||
|
//free the interpreter scope
|
||||||
|
while(interpreter->scope != NULL) {
|
||||||
|
interpreter->scope = popScope(interpreter->scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
//BUGFIX: handle scopes/types in the exports
|
||||||
|
for (int i = 0; i < interpreter->exports->capacity; i++) {
|
||||||
|
if (IS_FUNCTION(interpreter->exports->entries[i].key)) {
|
||||||
|
popScope(AS_FUNCTION(interpreter->exports->entries[i].key).scope);
|
||||||
|
AS_FUNCTION(interpreter->exports->entries[i].key).scope = NULL;
|
||||||
|
}
|
||||||
|
if (IS_FUNCTION(interpreter->exports->entries[i].value)) {
|
||||||
|
popScope(AS_FUNCTION(interpreter->exports->entries[i].value).scope);
|
||||||
|
AS_FUNCTION(interpreter->exports->entries[i].value).scope = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralDictionary(interpreter->exports);
|
||||||
|
FREE(LiteralDictionary, interpreter->exports);
|
||||||
|
interpreter->exports = NULL;
|
||||||
|
freeLiteralDictionary(interpreter->exportTypes);
|
||||||
|
FREE(LiteralDictionary, interpreter->exportTypes);
|
||||||
|
interpreter->exportTypes = NULL;
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,7 @@ void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput);
|
|||||||
void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
|
void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
|
||||||
|
|
||||||
//main access
|
//main access
|
||||||
void initInterpreter(Interpreter* interpreter);
|
void initInterpreter(Interpreter* interpreter); //start of program
|
||||||
void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length);
|
void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
|
||||||
void freeInterpreter(Interpreter* interpreter);
|
void resetInterpreter(Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
||||||
|
void freeInterpreter(Interpreter* interpreter); //end of program
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ KeywordType keywordTypes[] = {
|
|||||||
{TOKEN_PRINT, "print"},
|
{TOKEN_PRINT, "print"},
|
||||||
{TOKEN_RETURN, "return"},
|
{TOKEN_RETURN, "return"},
|
||||||
{TOKEN_TYPE, "type"},
|
{TOKEN_TYPE, "type"},
|
||||||
|
{TOKEN_TYPEOF, "typeof"},
|
||||||
{TOKEN_VAR, "var"},
|
{TOKEN_VAR, "var"},
|
||||||
{TOKEN_WHILE, "while"},
|
{TOKEN_WHILE, "while"},
|
||||||
|
|
||||||
|
|||||||
@@ -624,7 +624,10 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
|||||||
|
|
||||||
case LITERAL_FUNCTION:
|
case LITERAL_FUNCTION:
|
||||||
printToBuffer("function");
|
printToBuffer("function");
|
||||||
//TODO: how to print a function
|
break;
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION_NATIVE:
|
||||||
|
printToBuffer("native");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LITERAL_IDENTIFIER:
|
case LITERAL_IDENTIFIER:
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ typedef enum Opcode {
|
|||||||
OP_VAR_MODULO_ASSIGN,
|
OP_VAR_MODULO_ASSIGN,
|
||||||
|
|
||||||
OP_TYPE_CAST, //temporarily change a type of an atomic value
|
OP_TYPE_CAST, //temporarily change a type of an atomic value
|
||||||
|
OP_TYPE_OF, //get the type of a variable
|
||||||
|
|
||||||
OP_IMPORT,
|
OP_IMPORT,
|
||||||
OP_EXPORT,
|
OP_EXPORT,
|
||||||
|
|||||||
@@ -136,6 +136,13 @@ static Opcode forceType(Parser* parser, Node** nodeHandle) {
|
|||||||
return OP_EOF;
|
return OP_EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Opcode typeOf(Parser* parser, Node** nodeHandle) {
|
||||||
|
Node* rhs = NULL;
|
||||||
|
parsePrecedence(parser, &rhs, PREC_TERNARY);
|
||||||
|
emitNodeUnary(nodeHandle, OP_TYPE_OF, rhs);
|
||||||
|
return OP_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
static Opcode compound(Parser* parser, Node** nodeHandle) {
|
static Opcode compound(Parser* parser, Node** nodeHandle) {
|
||||||
//read either an array or a dictionary into a literal node
|
//read either an array or a dictionary into a literal node
|
||||||
|
|
||||||
@@ -730,6 +737,7 @@ ParseRule parseRules[] = { //must match the token types
|
|||||||
{NULL, NULL, PREC_NONE},// TOKEN_PRINT,
|
{NULL, NULL, PREC_NONE},// TOKEN_PRINT,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_RETURN,
|
{NULL, NULL, PREC_NONE},// TOKEN_RETURN,
|
||||||
{forceType, NULL, PREC_PRIMARY},// TOKEN_TYPE,
|
{forceType, NULL, PREC_PRIMARY},// TOKEN_TYPE,
|
||||||
|
{typeOf, NULL, PREC_CALL},// TOKEN_TYPEOF,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_VAR,
|
{NULL, NULL, PREC_NONE},// TOKEN_VAR,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_WHILE,
|
{NULL, NULL, PREC_NONE},// TOKEN_WHILE,
|
||||||
|
|
||||||
@@ -1044,14 +1052,14 @@ static void blockStmt(Parser* parser, Node** nodeHandle) {
|
|||||||
//process the grammar rule for this line
|
//process the grammar rule for this line
|
||||||
declaration(parser, &node);
|
declaration(parser, &node);
|
||||||
|
|
||||||
//BUGFIX: statements no longer require an existing node
|
|
||||||
((*nodeHandle)->block.nodes[(*nodeHandle)->block.count++]) = *node;
|
|
||||||
FREE(Node, node); //simply free the tmp node
|
|
||||||
|
|
||||||
// Ground floor: perfumery / Stationery and leather goods / Wigs and haberdashery / Kitchenware and food / Going up!
|
// Ground floor: perfumery / Stationery and leather goods / Wigs and haberdashery / Kitchenware and food / Going up!
|
||||||
if (parser->panic) {
|
if (parser->panic) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BUGFIX: statements no longer require an existing node
|
||||||
|
((*nodeHandle)->block.nodes[(*nodeHandle)->block.count++]) = *node;
|
||||||
|
FREE(Node, node); //simply free the tmp node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ typedef enum TokenType {
|
|||||||
TOKEN_PRINT,
|
TOKEN_PRINT,
|
||||||
TOKEN_RETURN,
|
TOKEN_RETURN,
|
||||||
TOKEN_TYPE,
|
TOKEN_TYPE,
|
||||||
|
TOKEN_TYPEOF,
|
||||||
TOKEN_VAR,
|
TOKEN_VAR,
|
||||||
TOKEN_WHILE,
|
TOKEN_WHILE,
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,9 @@ int main() {
|
|||||||
setInterpreterPrint(&interpreter, noPrintFn);
|
setInterpreterPrint(&interpreter, noPrintFn);
|
||||||
|
|
||||||
runInterpreter(&interpreter, exportBinary, exportSize); //automatically frees the binary data
|
runInterpreter(&interpreter, exportBinary, exportSize); //automatically frees the binary data
|
||||||
|
|
||||||
|
resetInterpreter(&interpreter);
|
||||||
|
|
||||||
runInterpreter(&interpreter, importBinary, importSize); //automatically frees the binary data
|
runInterpreter(&interpreter, importBinary, importSize); //automatically frees the binary data
|
||||||
|
|
||||||
freeInterpreter(&interpreter);
|
freeInterpreter(&interpreter);
|
||||||
|
|||||||
Reference in New Issue
Block a user