Implemented typeof and added resetInterpreter()

This commit is contained in:
2022-09-05 10:56:35 +01:00
parent 2a3206d951
commit 2aecf6e8a1
11 changed files with 125 additions and 66 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"},

View File

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

View File

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

View File

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

View File

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

View File

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