diff --git a/docs/TODO.txt b/docs/TODO.txt index 1f6b962..30e5346 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -34,7 +34,9 @@ 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 and _dot functions + + TODO: ternary operator TODO: Nullish types? TODO: hooks on the external libraries, triggered on import @@ -42,7 +44,8 @@ TODO: standard library TODO: external script runner library TODO: document how it all works - book? TODO: maximum recursion/function depth - +TODO: better API +TODO: packaging for release? NOPE: a = b = c = 1; -NOPE: functions return a set number of values \ No newline at end of file +NOPE: functions return a set number of values diff --git a/source/compiler.c b/source/compiler.c index 83f4124..bd2eb98 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -267,7 +267,7 @@ static int writeLiteralToCompiler(Compiler* compiler, Literal literal) { return index; } -static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAddressesPtr, void* continueAddressesPtr, int jumpOffsets) { //NOTE: jumpOfsets are included, because function arg and return indexes are embedded in the code body i.e. need to include thier sizes in the jump +static Opcode writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAddressesPtr, void* continueAddressesPtr, int jumpOffsets) { //NOTE: jumpOfsets are included, because function arg and return indexes are embedded in the code body i.e. need to include thier sizes in the jump //grow if the bytecode space is too small if (compiler->count + 32 > compiler->capacity) { int oldCapacity = compiler->capacity; @@ -295,11 +295,31 @@ static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAd compiler->bytecode[compiler->count++] = (unsigned char)node->unary.opcode; //1 byte break; - case NODE_BINARY: + //all infixes come here + case NODE_BINARY: { //pass to the child nodes, then embed the binary command (math, etc.) - writeCompilerWithJumps(compiler, node->binary.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets); - writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + Opcode override = writeCompilerWithJumps(compiler, node->binary.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + + //special case for when indexing + if (override != OP_EOF && node->binary.opcode >= OP_VAR_ASSIGN && node->binary.opcode <= OP_VAR_MODULO_ASSIGN) { + writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + compiler->bytecode[compiler->count++] = (unsigned char)override + 2; //1 byte WARNING: enum arithmetic + compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte + return OP_EOF; + } + + //return from the index-binary + Opcode ret = writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + + //loopy logic - if opcode == index or dot + if (node->binary.opcode == OP_INDEX || node->binary.opcode == OP_DOT) { + return node->binary.opcode; + } + compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte + + return ret; + } break; case NODE_GROUPING: @@ -339,6 +359,7 @@ static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAd case NODE_PAIR: fprintf(stderr, ERROR "[internal] NODE_PAIR encountered in writeCompilerWithJumps()\n" RESET); + compiler->bytecode[compiler->count++] = OP_EOF; //1 byte break; case NODE_VAR_DECL: { @@ -736,11 +757,66 @@ static void writeCompilerWithJumps(Compiler* compiler, Node* node, void* breakAd compiler->bytecode[compiler->count++] = (unsigned char)OP_EXPORT; //1 byte } break; + + case NODE_INDEX: { + //pass to the child nodes, then embed the opcode + + //first + if (!node->index.first) { + writeLiteralToCompiler(compiler, TO_NULL_LITERAL); + } + else { + writeCompilerWithJumps(compiler, node->index.first, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + } + + //second + if (!node->index.second) { + writeLiteralToCompiler(compiler, TO_NULL_LITERAL); + } + else { + writeCompilerWithJumps(compiler, node->index.second, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + } + + //third + if (!node->index.third) { + writeLiteralToCompiler(compiler, TO_NULL_LITERAL); + } + else { + writeCompilerWithJumps(compiler, node->index.third, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + } + + // compiler->bytecode[compiler->count++] = (unsigned char)OP_INDEX; //1 byte + + return OP_INDEX_ASSIGN; //override binary's instruction IF it is assign + } + break; + + case NODE_DOT: { + //pass to the child nodes, then embed the opcode + if (!node->index.first) { + writeLiteralToCompiler(compiler, TO_NULL_LITERAL); + } + else { + writeCompilerWithJumps(compiler, node->index.first, breakAddressesPtr, continueAddressesPtr, jumpOffsets); + } + + // compiler->bytecode[compiler->count++] = (unsigned char)OP_DOT; //1 byte + + return OP_DOT_ASSIGN; + } + break; } + + return OP_EOF; } void writeCompiler(Compiler* compiler, Node* node) { - writeCompilerWithJumps(compiler, node, NULL, NULL, 0); + Opcode op = writeCompilerWithJumps(compiler, node, NULL, NULL, 0); + + //compensate for indexing & dot notation being screwy + if (op != OP_EOF) { + compiler->bytecode[compiler->count++] = (unsigned char)op; //1 byte + } } void freeCompiler(Compiler* compiler) { diff --git a/source/interpreter.c b/source/interpreter.c index 75acbe4..a91f607 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -1345,6 +1345,430 @@ static bool execExport(Interpreter* interpreter) { return true; } +static bool execIndex(Interpreter* interpreter) { + //assume -> compound, first, second, third are all on the stack + + Literal third = popLiteralArray(&interpreter->stack); + Literal second = popLiteralArray(&interpreter->stack); + Literal first = popLiteralArray(&interpreter->stack); + Literal compound = popLiteralArray(&interpreter->stack); + + if (!IS_IDENTIFIER(compound)) { + interpreter->errorOutput("Unknown literal found in indexing notation\n"); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + return false; + } + + if (!parseIdentifierToValue(interpreter, &compound)) { + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + return false; + } + + if (!IS_ARRAY(compound) && !IS_DICTIONARY(compound) && !IS_STRING(compound)) { + interpreter->errorOutput("Unknown compound found in indexing notation\n"); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + return false; + } + + //get the index function + Literal func = TO_NULL_LITERAL; + char* keyStr = "_index"; + Literal key = TO_IDENTIFIER_LITERAL(copyString(keyStr, strlen(keyStr)), strlen(keyStr)); + + if (!getScopeVariable(interpreter->scope, key, &func) || !IS_FUNCTION_NATIVE(func)) { + interpreter->errorOutput("couldn't get the _index function\n"); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(func); + freeLiteral(key); + return false; + } + + //build the argument list + LiteralArray arguments; + initLiteralArray(&arguments); + + pushLiteralArray(&arguments, compound); + pushLiteralArray(&arguments, first); + pushLiteralArray(&arguments, second); + pushLiteralArray(&arguments, third); + pushLiteralArray(&arguments, TO_NULL_LITERAL); //it expects an assignment command + pushLiteralArray(&arguments, TO_NULL_LITERAL); //it expects an assignment "opcode" + + //call the function + NativeFn fn = (NativeFn)AS_FUNCTION(func).bytecode; + fn(interpreter, &arguments); + + //clean up + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(func); + freeLiteralArray(&arguments); + freeLiteral(key); + + return true; +} + +static bool execDot(Interpreter* interpreter) { + //assume -> compound, first are all on the stack + + Literal first = popLiteralArray(&interpreter->stack); + Literal compound = popLiteralArray(&interpreter->stack); + + if (!IS_IDENTIFIER(compound)) { + interpreter->errorOutput("Unknown literal found in dot notation\n"); + freeLiteral(first); + freeLiteral(compound); + return false; + } + + if (!parseIdentifierToValue(interpreter, &compound)) { + freeLiteral(first); + freeLiteral(compound); + return false; + } + + if (!IS_ARRAY(compound) && !IS_DICTIONARY(compound) && !IS_STRING(compound)) { + interpreter->errorOutput("Unknown compound found in dot notation\n"); + freeLiteral(first); + freeLiteral(compound); + return false; + } + + //get the index function + Literal func = TO_NULL_LITERAL; + char* keyStr = "_dot"; + Literal key = TO_IDENTIFIER_LITERAL(copyString(keyStr, strlen(keyStr)), strlen(keyStr)); + + if (!getScopeVariable(interpreter->scope, key, &func) || !IS_FUNCTION_NATIVE(func)) { + interpreter->errorOutput("couldn't get the _dot function\n"); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(func); + freeLiteral(key); + return false; + } + + //build the argument list + LiteralArray arguments; + initLiteralArray(&arguments); + + pushLiteralArray(&arguments, compound); + pushLiteralArray(&arguments, first); + pushLiteralArray(&arguments, TO_NULL_LITERAL); //it expects an assignment command + pushLiteralArray(&arguments, TO_NULL_LITERAL); //it expects an assignment "opcode" + + //call the function + NativeFn fn = (NativeFn)AS_FUNCTION(func).bytecode; + fn(interpreter, &arguments); + + //clean up + freeLiteral(first); + freeLiteral(compound); + freeLiteral(func); + freeLiteralArray(&arguments); + freeLiteral(key); + + return true; +} + +static bool execIndexAssign(Interpreter* interpreter) { + //assume -> compound, first, second, third, assign are all on the stack + + Literal assign = popLiteralArray(&interpreter->stack); + Literal third = popLiteralArray(&interpreter->stack); + Literal second = popLiteralArray(&interpreter->stack); + Literal first = popLiteralArray(&interpreter->stack); + Literal compound = popLiteralArray(&interpreter->stack); + + if (!IS_IDENTIFIER(compound)) { + interpreter->errorOutput("Unknown literal found in index assigning notation\n"); + freeLiteral(assign); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + return false; + } + + Literal idn = copyLiteral(compound); + + if (!parseIdentifierToValue(interpreter, &compound)) { + freeLiteral(assign); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + return false; + } + + if (!IS_ARRAY(compound) && !IS_DICTIONARY(compound) && !IS_STRING(compound)) { + interpreter->errorOutput("Unknown compound found in index assigning notation\n"); + freeLiteral(assign); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + return false; + } + + //get the index function + Literal func = TO_NULL_LITERAL; + char* keyStr = "_index"; + Literal key = TO_IDENTIFIER_LITERAL(copyString(keyStr, strlen(keyStr)), strlen(keyStr)); + + if (!getScopeVariable(interpreter->scope, key, &func) || !IS_FUNCTION_NATIVE(func)) { + interpreter->errorOutput("couldn't get the _index function\n"); + freeLiteral(assign); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteral(key); + return false; + } + + //build the opcode + unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); + char* opStr = ""; + switch(opcode) { + case OP_VAR_ASSIGN: + opStr = "="; + break; + case OP_VAR_ADDITION_ASSIGN: + opStr = "+="; + break; + case OP_VAR_SUBTRACTION_ASSIGN: + opStr = "-="; + break; + case OP_VAR_MULTIPLICATION_ASSIGN: + opStr = "*="; + break; + case OP_VAR_DIVISION_ASSIGN: + opStr = "/="; + break; + case OP_VAR_MODULO_ASSIGN: + opStr = "%="; + break; + + default: + interpreter->errorOutput("bad opcode in index assigning notation\n"); + freeLiteral(assign); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteral(key); + return false; + } + + Literal op = TO_STRING_LITERAL(copyString(opStr, strlen(opStr)), strlen(opStr)); + + //build the argument list + LiteralArray arguments; + initLiteralArray(&arguments); + + pushLiteralArray(&arguments, compound); + pushLiteralArray(&arguments, first); + pushLiteralArray(&arguments, second); + pushLiteralArray(&arguments, third); + pushLiteralArray(&arguments, assign); //it expects an assignment command + pushLiteralArray(&arguments, op); //it expects an assignment "opcode" + + //call the function + NativeFn fn = (NativeFn)AS_FUNCTION(func).bytecode; + fn(interpreter, &arguments); + + //save the result (assume top of the interpreter stack is the new compound value) + Literal result = popLiteralArray(&interpreter->stack); + if (!setScopeVariable(interpreter->scope, idn, result, true)) {//TODO: check this const-ness + interpreter->errorOutput("Incorrect type assigned to compound member: "); + printLiteralCustom(result, interpreter->errorOutput); + interpreter->errorOutput("\n"); + + //clean up + freeLiteral(op); + freeLiteral(assign); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteralArray(&arguments); + freeLiteral(key); + freeLiteral(result); + return false; + } + + //clean up + freeLiteral(op); + freeLiteral(assign); + freeLiteral(third); + freeLiteral(second); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteralArray(&arguments); + freeLiteral(key); + freeLiteral(result); + + return true; +} + +static bool execDotAssign(Interpreter* interpreter) { + //assume -> compound, first, assign are all on the stack + + Literal assign = popLiteralArray(&interpreter->stack); + Literal first = popLiteralArray(&interpreter->stack); + Literal compound = popLiteralArray(&interpreter->stack); + + if (!IS_IDENTIFIER(compound)) { + interpreter->errorOutput("Unknown literal found in dot assigning notation\n"); + freeLiteral(assign); + freeLiteral(first); + freeLiteral(compound); + return false; + } + + Literal idn = copyLiteral(compound); + + if (!parseIdentifierToValue(interpreter, &compound)) { + freeLiteral(assign); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + return false; + } + + if (!IS_ARRAY(compound) && !IS_DICTIONARY(compound) && !IS_STRING(compound)) { + interpreter->errorOutput("Unknown compound found in dot assigning notation\n"); + freeLiteral(assign); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + return false; + } + + //get the index function + Literal func = TO_NULL_LITERAL; + char* keyStr = "_dot"; + Literal key = TO_IDENTIFIER_LITERAL(copyString(keyStr, strlen(keyStr)), strlen(keyStr)); + + if (!getScopeVariable(interpreter->scope, key, &func) || !IS_FUNCTION_NATIVE(func)) { + interpreter->errorOutput("couldn't get the _dot function\n"); + freeLiteral(assign); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteral(key); + return false; + } + + //build the opcode + unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); + char* opStr = ""; + switch(opcode) { + case OP_VAR_ASSIGN: + opStr = "="; + break; + case OP_VAR_ADDITION_ASSIGN: + opStr = "+="; + break; + case OP_VAR_SUBTRACTION_ASSIGN: + opStr = "-="; + break; + case OP_VAR_MULTIPLICATION_ASSIGN: + opStr = "*="; + break; + case OP_VAR_DIVISION_ASSIGN: + opStr = "/="; + break; + case OP_VAR_MODULO_ASSIGN: + opStr = "%="; + break; + + default: + interpreter->errorOutput("bad opcode in dot assigning notation\n"); + freeLiteral(assign); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteral(key); + return false; + } + + Literal op = TO_STRING_LITERAL(copyString(opStr, strlen(opStr)), strlen(opStr)); + + //build the argument list + LiteralArray arguments; + initLiteralArray(&arguments); + + pushLiteralArray(&arguments, compound); + pushLiteralArray(&arguments, first); + pushLiteralArray(&arguments, assign); //it expects an assignment command + pushLiteralArray(&arguments, op); //it expects an assignment "opcode" + + //call the function + NativeFn fn = (NativeFn)AS_FUNCTION(func).bytecode; + fn(interpreter, &arguments); + + //save the result (assume top of the interpreter stack is the new compound value) + Literal result = popLiteralArray(&interpreter->stack); + if (!setScopeVariable(interpreter->scope, idn, result, true)) {//TODO: check this const-ness + interpreter->errorOutput("Incorrect type assigned to compound member: "); + printLiteralCustom(result, interpreter->errorOutput); + interpreter->errorOutput("\n"); + + //clean up + freeLiteral(op); + freeLiteral(assign); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteralArray(&arguments); + freeLiteral(key); + freeLiteral(result); + return false; + } + + //clean up + freeLiteral(op); + freeLiteral(assign); + freeLiteral(first); + freeLiteral(compound); + freeLiteral(idn); + freeLiteral(func); + freeLiteralArray(&arguments); + freeLiteral(key); + freeLiteral(result); + + return true; +} + //the heart of toy static void execInterpreter(Interpreter* interpreter) { //set the starting point for the interpreter @@ -1552,6 +1976,30 @@ static void execInterpreter(Interpreter* interpreter) { } break; + case OP_INDEX: + if (!execIndex(interpreter)) { + return; + } + break; + + case OP_DOT: + if (!execDot(interpreter)) { + return; + } + break; + + case OP_INDEX_ASSIGN: + if (!execIndexAssign(interpreter)) { + return; + } + break; + + case OP_DOT_ASSIGN: + if (!execDotAssign(interpreter)) { + return; + } + break; + case OP_POP_STACK: while (interpreter->stack.count > 0) { freeLiteral(popLiteralArray(&interpreter->stack)); @@ -1915,6 +2363,8 @@ void resetInterpreter(Interpreter* interpreter) { interpreter->scope = pushScope(NULL); //globally available functions + injectNativeFn(interpreter, "_index", _index); + injectNativeFn(interpreter, "_dot", _dot); injectNativeFn(interpreter, "_set", _set); injectNativeFn(interpreter, "_get", _get); injectNativeFn(interpreter, "_push", _push); diff --git a/source/interpreter.h b/source/interpreter.h index 708321c..edce019 100644 --- a/source/interpreter.h +++ b/source/interpreter.h @@ -30,12 +30,13 @@ typedef struct Interpreter { bool panic; } Interpreter; -//for native function API +//native function API typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments); bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func); -bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr); +//TODO: injectNativeHook //utilities for the host program +bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr); void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput); void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput); void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput); diff --git a/source/lib_builtin.c b/source/lib_builtin.c index a0741e4..a2f2bbb 100644 --- a/source/lib_builtin.c +++ b/source/lib_builtin.c @@ -2,6 +2,52 @@ #include "memory.h" +int _index(Interpreter* interpreter, LiteralArray* arguments) { + //_index(compound, first, second, third, assignValue, op) + Literal op = popLiteralArray(arguments); + Literal assign = popLiteralArray(arguments); + Literal third = popLiteralArray(arguments); + Literal second = popLiteralArray(arguments); + Literal first = popLiteralArray(arguments); + Literal compound = popLiteralArray(arguments); + + printLiteralCustom(compound, interpreter->printOutput); + printLiteralCustom(first, interpreter->printOutput); + printLiteralCustom(second, interpreter->printOutput); + printLiteralCustom(third, interpreter->printOutput); + printLiteralCustom(op, interpreter->printOutput); + printLiteralCustom(assign, interpreter->printOutput); + + freeLiteral(compound); + freeLiteral(first); + freeLiteral(second); + freeLiteral(third); + freeLiteral(op); + freeLiteral(assign); + + return 0; +} + +int _dot(Interpreter* interpreter, LiteralArray* arguments) { + //_dot(compound, first, assignValue, opcode) + Literal op = popLiteralArray(arguments); + Literal assign = popLiteralArray(arguments); + Literal first = popLiteralArray(arguments); + Literal compound = popLiteralArray(arguments); + + printLiteralCustom(compound, interpreter->printOutput); + printLiteralCustom(first, interpreter->printOutput); + printLiteralCustom(op, interpreter->printOutput); + printLiteralCustom(assign, interpreter->printOutput); + + freeLiteral(compound); + freeLiteral(first); + freeLiteral(op); + freeLiteral(assign); + + return 0; +} + int _set(Interpreter* interpreter, LiteralArray* arguments) { //if wrong number of arguments, fail if (arguments->count != 3) { @@ -413,4 +459,4 @@ int _clear(Interpreter* interpreter, LiteralArray* arguments) { freeLiteral(obj); return 1; -} \ No newline at end of file +} diff --git a/source/lib_builtin.h b/source/lib_builtin.h index 419afcf..a32c6ea 100644 --- a/source/lib_builtin.h +++ b/source/lib_builtin.h @@ -2,6 +2,8 @@ #include "interpreter.h" +int _index(Interpreter* interpreter, LiteralArray* arguments); +int _dot(Interpreter* interpreter, LiteralArray* arguments); int _set(Interpreter* interpreter, LiteralArray* arguments); int _get(Interpreter* interpreter, LiteralArray* arguments); int _push(Interpreter* interpreter, LiteralArray* arguments); diff --git a/source/literal.h b/source/literal.h index fec0bbc..09574fd 100644 --- a/source/literal.h +++ b/source/literal.h @@ -99,8 +99,6 @@ void freeLiteral(Literal literal); #define IS_TRUTHY(x) _isTruthy(x) #define MAX_STRING_LENGTH 4096 -// #define STRLEN(lit) ((lit).as.string.length) -// #define STRLEN_I(lit) ((lit).as.identifier.length) #define HASH_I(lit) ((lit).as.identifier.hash) #define TYPE_PUSH_SUBTYPE(lit, subtype) _typePushSubtype(lit, subtype) diff --git a/source/literal_array.c b/source/literal_array.c index 6d7f6bd..f7bbc9f 100644 --- a/source/literal_array.c +++ b/source/literal_array.c @@ -12,6 +12,16 @@ void initLiteralArray(LiteralArray* array) { array->literals = NULL; } +void freeLiteralArray(LiteralArray* array) { + //clean up memory + for(int i = 0; i < array->count; i++) { + freeLiteral(array->literals[i]); + } + + FREE_ARRAY(Literal, array->literals, array->capacity); + initLiteralArray(array); +} + int pushLiteralArray(LiteralArray* array, Literal literal) { if (array->capacity < array->count + 1) { int oldCapacity = array->capacity; @@ -39,16 +49,6 @@ Literal popLiteralArray(LiteralArray* array) { return ret; } -void freeLiteralArray(LiteralArray* array) { - //clean up memory - for(int i = 0; i < array->count; i++) { - freeLiteral(array->literals[i]); - } - - FREE_ARRAY(Literal, array->literals, array->capacity); - initLiteralArray(array); -} - //find a literal in the array that matches the "literal" argument int findLiteralIndex(LiteralArray* array, Literal literal) { for (int i = 0; i < array->count; i++) { diff --git a/source/literal_array.h b/source/literal_array.h index 94230db..16f43b9 100644 --- a/source/literal_array.h +++ b/source/literal_array.h @@ -9,8 +9,9 @@ typedef struct LiteralArray { } LiteralArray; void initLiteralArray(LiteralArray* array); +void freeLiteralArray(LiteralArray* array); int pushLiteralArray(LiteralArray* array, Literal literal); Literal popLiteralArray(LiteralArray* array); -void freeLiteralArray(LiteralArray* array); +//TODO: set & get int findLiteralIndex(LiteralArray* array, Literal literal); diff --git a/source/literal_dictionary.h b/source/literal_dictionary.h index f6cb34a..2e7e387 100644 --- a/source/literal_dictionary.h +++ b/source/literal_dictionary.h @@ -13,8 +13,8 @@ typedef struct _entry { typedef struct LiteralDictionary { _entry* entries; int capacity; - int contains; //count + tombstones int count; + int contains; //count + tombstones, for internal use } LiteralDictionary; void initLiteralDictionary(LiteralDictionary* dictionary); diff --git a/source/node.c b/source/node.c index ec3e247..a635bce 100644 --- a/source/node.c +++ b/source/node.c @@ -99,6 +99,13 @@ void freeNodeCustom(Node* node, bool freeSelf) { freeLiteral(node->import.identifier); freeLiteral(node->import.alias); break; + + case NODE_INDEX: + case NODE_DOT: + freeNode(node->index.first); + freeNode(node->index.second); + freeNode(node->index.third); + break; } if (freeSelf) { @@ -261,4 +268,26 @@ void emitNodeImport(Node** nodeHandle, NodeType mode, Literal identifier, Litera tmp->import.alias = copyLiteral(alias); *nodeHandle = tmp; -} \ No newline at end of file +} + +void emitNodeIndex(Node** nodeHandle, Node* first, Node* second, Node* third) { + Node* tmp = ALLOCATE(Node, 1); + + tmp->type = NODE_INDEX; + tmp->index.first = first; + tmp->index.second = second; + tmp->index.third = third; + + *nodeHandle = tmp; +} + +void emitNodeDot(Node** nodeHandle, Node* first) { + Node* tmp = ALLOCATE(Node, 1); + + tmp->type = NODE_DOT; + tmp->index.first = first; + tmp->index.second = NULL; + tmp->index.third = NULL; + + *nodeHandle = tmp; +} diff --git a/source/node.h b/source/node.h index 9025bd0..82b2c33 100644 --- a/source/node.h +++ b/source/node.h @@ -30,6 +30,8 @@ typedef enum NodeType { NODE_INCREMENT_POSTFIX, NODE_IMPORT, NODE_EXPORT, + NODE_INDEX, + NODE_DOT, } NodeType; typedef struct NodeLiteral { @@ -124,6 +126,13 @@ typedef struct NodeImport { Literal alias; } NodeImport; +typedef struct NodeIndex { + NodeType type; + Node* first; + Node* second; + Node* third; +} NodeIndex; + union _node { NodeType type; NodeLiteral atomic; @@ -140,6 +149,7 @@ union _node { NodePath path; NodeIncrement increment; NodeImport import; + NodeIndex index; }; void freeNode(Node* node); @@ -157,4 +167,6 @@ void emitNodeFnCollection(Node** nodeHandle); void emitNodePath(Node** nodeHandle, NodeType type, Node* preClause, Node* postClause, Node* condition, Node* thenPath, Node* elsePath); void emitNodePrefixIncrement(Node** nodeHandle, Literal identifier, int increment); void emitNodePostfixIncrement(Node** nodeHandle, Literal identifier, int increment); -void emitNodeImport(Node** nodeHandle, NodeType mode, Literal identifier, Literal alias); \ No newline at end of file +void emitNodeImport(Node** nodeHandle, NodeType mode, Literal identifier, Literal alias); +void emitNodeIndex(Node** nodeHandle, Node* first, Node* second, Node* third); +void emitNodeDot(Node** nodeHandle, Node* first); \ No newline at end of file diff --git a/source/opcodes.h b/source/opcodes.h index b2c6b9f..9e77116 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -48,6 +48,13 @@ typedef enum Opcode { OP_IMPORT, OP_EXPORT, + //for indexing + OP_INDEX, + OP_DOT, + + OP_INDEX_ASSIGN, + OP_DOT_ASSIGN, + //comparion of values OP_COMPARE_EQUAL, OP_COMPARE_NOT_EQUAL, diff --git a/source/parser.c b/source/parser.c index 47751ec..f1afc34 100644 --- a/source/parser.c +++ b/source/parser.c @@ -717,6 +717,60 @@ static Opcode fnCall(Parser* parser, Node** nodeHandle) { return OP_EOF; } +static Opcode indexAccess(Parser* parser, Node** nodeHandle) { + advance(parser); + + //val[first : second : third] + + Node* first = NULL; + Node* second = NULL; + Node* third = NULL; + + //eat the first + if (!match(parser, TOKEN_COLON)) { + parsePrecedence(parser, &first, PREC_TERNARY); + } + + if (match(parser, TOKEN_BRACKET_RIGHT)) { + emitNodeIndex(nodeHandle, first, second, third); + return OP_INDEX; + } + + consume(parser, TOKEN_COLON, "Expected ':' in index notation"); + + //eat the second + if (!match(parser, TOKEN_COLON)) { + parsePrecedence(parser, &second, PREC_TERNARY); + } + + if (match(parser, TOKEN_BRACKET_RIGHT)) { + emitNodeIndex(nodeHandle, first, second, third); + return OP_INDEX; + } + + consume(parser, TOKEN_COLON, "Expected ':' in index notation"); + + //eat the third + parsePrecedence(parser, &third, PREC_TERNARY); + emitNodeIndex(nodeHandle, first, second, third); + + consume(parser, TOKEN_BRACKET_RIGHT, "Expected ']' in index notation"); + + return OP_INDEX; +} + +static Opcode dot(Parser* parser, Node** nodeHandle) { + advance(parser); //for the dot + advance(parser); //for the identifier + + Node* first = NULL; + + identifier(parser, &first); //specific case + emitNodeDot(nodeHandle, first); + + return OP_DOT; +} + ParseRule parseRules[] = { //must match the token types //types {atomic, NULL, PREC_PRIMARY},// TOKEN_NULL, @@ -779,7 +833,7 @@ ParseRule parseRules[] = { //must match the token types //logical operators {grouping, fnCall, PREC_CALL},// TOKEN_PAREN_LEFT, {NULL, NULL, PREC_NONE},// TOKEN_PAREN_RIGHT, - {compound, NULL, PREC_CALL},// TOKEN_BRACKET_LEFT, + {compound, indexAccess, PREC_CALL},// TOKEN_BRACKET_LEFT, {NULL, NULL, PREC_NONE},// TOKEN_BRACKET_RIGHT, {NULL, NULL, PREC_NONE},// TOKEN_BRACE_LEFT, {NULL, NULL, PREC_NONE},// TOKEN_BRACE_RIGHT, @@ -797,7 +851,7 @@ ParseRule parseRules[] = { //must match the token types {NULL, NULL, PREC_NONE},// TOKEN_COLON, {NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON, {NULL, NULL, PREC_NONE},// TOKEN_COMMA, - {NULL, NULL, PREC_NONE},// TOKEN_DOT, + {NULL, dot, PREC_CALL},// TOKEN_DOT, {NULL, NULL, PREC_NONE},// TOKEN_PIPE, {NULL, NULL, PREC_NONE},// TOKEN_REST,