diff --git a/docs/TODO.txt b/docs/TODO.txt index 044d2d9..0c8837b 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -34,17 +34,17 @@ DONE: Import/export keywords DONE: A way to check the type of a variable (typeOf keyword) DONE: slice and dot notation around the builtin _index and _dot functions DONE: maximum recursion/function depth +DONE: better sugar for _push, _pop, _length TODO: nested compound assignment -TODO: better sugar for _push, _pop, _length TODO: ternary operator? TODO: Nullish types? TODO: hooks on the external libraries, triggered on import TODO: standard library TODO: external script runner library TODO: document how it all works - book? -TODO: better API +TODO: better API? TODO: packaging for release? NOPE: a = b = c = 1; diff --git a/docs/spec.md b/docs/spec.md index a4f156c..2d16c23 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -433,15 +433,14 @@ var dict: [string const, int const] const = [ ]; ``` -Dictionaries can be indexed using traditional bracket notation, or the dot operator when the keys are strings. Existing elements can be accessed or overwritten, or new ones inserted if they don't already exist this way by using the standard library functions. +Dictionaries can be indexed using traditional bracket notation. Existing elements can be accessed or overwritten, or new ones inserted if they don't already exist this way by using the standard library functions. ``` dict["foo"] = "bar"; print dict["foo"]; -print dict.foo; //syntactic sugar, only works if the key is not a built-in function ``` -## Indexing, Slice and Dot Notation +## Slice 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: @@ -485,11 +484,22 @@ print str[::-2]; //drwolH 0 cannot be used as the third argument. -### Globally available functions +### Globally Available Functions -The slice and dot notations (the latter of which only works on dictionaries) are simply syntactic sugar for the globally available functions. +The dot notation can be used to acces the globally available functions, like so: ``` +obj.function(); //passes in obj as the first argument +``` + +A list of globally available functions is: + +``` +//usable with arrays, dictionaries and strings - probably a good idea not to use this one, just use index notation +fn _index(self, first, second, third, assign, op) { + //native code +} + //usable with arrays and dictionaries fn _set(self, key, value) { //native code diff --git a/scripts/test/functions.toy b/scripts/test/functions.toy index 9e9f2f7..2bd921a 100644 --- a/scripts/test/functions.toy +++ b/scripts/test/functions.toy @@ -62,6 +62,16 @@ fn extra(one, two, ...rest) { extra("one", "two", "three", "four", "five", "six", "seven"); +//test underscore functions +fn _example(self, a, b, c) { + assert a == "a", "underscore failed (a)"; + assert b == "b", "underscore failed (b)"; + assert c == "c", "underscore failed (c)"; + return self; +} + +assert "hello world".example("a", "b", "c") == "hello world", "underscore call failed"; + print "All good"; diff --git a/scripts/test/index-dictionaries.toy b/scripts/test/index-dictionaries.toy index 192d48d..d1bbbc1 100644 --- a/scripts/test/index-dictionaries.toy +++ b/scripts/test/index-dictionaries.toy @@ -8,16 +8,6 @@ } -//test dot insertion -{ - var d = [:]; - - d.foo = "bar"; - - assert d == ["foo":"bar"], "dot insertion failed"; -} - - //test index arithmetic { var d = ["one":1, "two":2, "three":3]; @@ -28,28 +18,6 @@ } -//test dot arithmetic -{ - var d = ["one":1, "two":2, "three":3]; - - d.three *= 3; - - assert d == ["one":1, "two":2, "three":9], "index arithmetic failed"; -} - - -//test dot calls -{ - fn f() { - return 42; - } - - var d = ["foo":f]; - - assert d.foo() == 42, "dot calls failed"; -} - - //test indexing with variables { var d = ["one":1, "two":2, "three":3]; diff --git a/source/compiler.c b/source/compiler.c index 04460a4..7e14956 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -309,7 +309,7 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, Node* node, void* break //special case for when indexing and assigning 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)override + 1; //1 byte WARNING: enum arithmetic compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte return OP_EOF; } @@ -319,7 +319,7 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, Node* node, void* break compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte } - //return from the index-binary + //return this if... Opcode ret = writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets); //loopy logic - if opcode == index or dot @@ -504,7 +504,7 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, Node* node, void* break } //push the argument COUNT to the top of the stack - Literal argumentsCountLiteral = TO_INTEGER_LITERAL(node->fnCall.arguments->fnCollection.count); + Literal argumentsCountLiteral = TO_INTEGER_LITERAL(node->fnCall.argumentCount); //argumentCount is set elsewhere to support dot operator int argumentsCountIndex = findLiteralIndex(&compiler->literalCache, argumentsCountLiteral); if (argumentsCountIndex < 0) { argumentsCountIndex = pushLiteralArray(&compiler->literalCache, argumentsCountLiteral); @@ -859,20 +859,8 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, Node* node, void* break break; case NODE_DOT: { - //pass to the child nodes, then embed the opcode - if (!node->index.first) { - writeLiteralToCompiler(compiler, TO_NULL_LITERAL); - } - else { - Opcode override = writeCompilerWithJumps(compiler, node->index.first, breakAddressesPtr, continueAddressesPtr, jumpOffsets); - if (override != OP_EOF) {//compensate for indexing & dot notation being screwy - compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte - } - } - - // compiler->bytecode[compiler->count++] = (unsigned char)OP_DOT; //1 byte - - return OP_DOT_ASSIGN; + fprintf(stderr, ERROR "[internal] NODE_DOT encountered in writeCompilerWithJumps()\n" RESET); + compiler->bytecode[compiler->count++] = OP_EOF; //1 byte } break; } diff --git a/source/interpreter.c b/source/interpreter.c index 9c3b171..b7b11d9 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -990,7 +990,7 @@ static bool execFalseJump(Interpreter* interpreter) { static void execInterpreter(Interpreter*); static void readInterpreterSections(Interpreter* interpreter); -static bool execFnCall(Interpreter* interpreter) { +static bool execFnCall(Interpreter* interpreter, bool looseFirstArgument) { //BUGFIX: depth check - don't drown! if (interpreter->depth >= 1000) { interpreter->errorOutput("Depth check failed\n"); @@ -1004,7 +1004,14 @@ static bool execFnCall(Interpreter* interpreter) { Literal stackSize = popLiteralArray(&interpreter->stack); //unpack the stack of arguments - for (int i = 0; i < AS_INTEGER(stackSize); i++) { + for (int i = 0; i < AS_INTEGER(stackSize) - 1; i++) { + Literal lit = popLiteralArray(&interpreter->stack); + pushLiteralArray(&arguments, lit); //NOTE: also reverses the order + freeLiteral(lit); + } + + //collect one more argument + if (!looseFirstArgument && AS_INTEGER(stackSize) > 0) { Literal lit = popLiteralArray(&interpreter->stack); pushLiteralArray(&arguments, lit); //NOTE: also reverses the order freeLiteral(lit); @@ -1012,6 +1019,23 @@ static bool execFnCall(Interpreter* interpreter) { Literal identifier = popLiteralArray(&interpreter->stack); + //collect one more argument + if (looseFirstArgument) { + Literal lit = popLiteralArray(&interpreter->stack); + pushLiteralArray(&arguments, lit); //NOTE: also reverses the order + freeLiteral(lit); + } + + //let's screw with the fn name, too + if (looseFirstArgument) { + int length = strlen(AS_IDENTIFIER(identifier)) + 1; + char buffer[MAX_STRING_LENGTH]; + snprintf(buffer, MAX_STRING_LENGTH, "_%s", AS_IDENTIFIER(identifier)); //prepend an underscore + + freeLiteral(identifier); + identifier = TO_IDENTIFIER_LITERAL(copyString(buffer, length), length); + } + Literal func = identifier; if (!parseIdentifierToValue(interpreter, &func)) { @@ -1453,81 +1477,6 @@ static bool execIndex(Interpreter* interpreter) { 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); - - Literal tmp = first; - first = TO_STRING_LITERAL(copyString(AS_IDENTIFIER(tmp), strlen(AS_IDENTIFIER(tmp))) , strlen(AS_IDENTIFIER(tmp)) ); - freeLiteral(tmp); - - if (!IS_IDENTIFIER(compound)) { - interpreter->errorOutput("Unknown literal found in dot notation\n"); - printLiteralCustom(compound, interpreter->errorOutput); - interpreter->errorOutput("\n"); - freeLiteral(first); - freeLiteral(compound); - return false; - } - - Literal idn = compound; - - if (!parseIdentifierToValue(interpreter, &compound)) { - freeLiteral(first); - freeLiteral(compound); - freeLiteral(idn); - return false; - } - - if (!IS_DICTIONARY(compound)) { - interpreter->errorOutput("Unknown compound found in dot notation\n"); - 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(first); - freeLiteral(compound); - freeLiteral(idn); - 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(idn); - freeLiteral(func); - freeLiteral(key); - freeLiteralArray(&arguments); - - return true; -} - static bool execIndexAssign(Interpreter* interpreter) { //assume -> compound, first, second, third, assign are all on the stack @@ -1699,161 +1648,6 @@ static bool execIndexAssign(Interpreter* interpreter) { 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); - - Literal tmp = first; - first = TO_STRING_LITERAL(copyString(AS_IDENTIFIER(tmp), strlen(AS_IDENTIFIER(tmp))) , strlen(AS_IDENTIFIER(tmp)) ); - freeLiteral(tmp); - - if (!IS_IDENTIFIER(compound)) { - interpreter->errorOutput("Unknown literal found in dot assigning notation\n"); - printLiteralCustom(compound, interpreter->errorOutput); - interpreter->errorOutput("\n"); - freeLiteral(assign); - freeLiteral(first); - freeLiteral(compound); - return false; - } - - Literal idn = compound; - - if (!parseIdentifierToValue(interpreter, &compound)) { - freeLiteral(assign); - freeLiteral(first); - freeLiteral(compound); - freeLiteral(idn); - return false; - } - - if (!IS_DICTIONARY(compound)) { - interpreter->errorOutput("Unknown compound found in dot assigning notation\n"); - freeLiteral(assign); - freeLiteral(first); - freeLiteral(compound); - freeLiteral(idn); - return false; - } - - //check const-ness of "first" within "compound" - Literal type = getScopeType(interpreter->scope, idn); - if (AS_TYPE(type).typeOf == LITERAL_DICTIONARY && AS_TYPE(((Literal*)(AS_TYPE(type).subtypes))[1]).constant) { - interpreter->errorOutput("couldn't assign to constant within compound within dot assigning notation\n"); - freeLiteral(assign); - freeLiteral(first); - freeLiteral(compound); - freeLiteral(idn); - freeLiteral(type); - 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); - freeLiteral(type); - 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); - freeLiteral(type); - 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)) { - interpreter->errorOutput("Incorrect type assigned to compound member: "); - printLiteralCustom(result, interpreter->errorOutput); - interpreter->errorOutput("\n"); - - //clean up - freeLiteral(assign); - freeLiteral(first); - freeLiteral(compound); - freeLiteral(idn); - freeLiteral(func); - freeLiteral(key); - freeLiteral(type); - freeLiteral(result); - freeLiteralArray(&arguments); - freeLiteral(op); - return false; - } - - //clean up - freeLiteral(op); - freeLiteral(assign); - freeLiteral(first); - freeLiteral(compound); - freeLiteral(idn); - freeLiteral(func); - freeLiteralArray(&arguments); - freeLiteral(key); - freeLiteral(type); - freeLiteral(result); - - return true; -} - //the heart of toy static void execInterpreter(Interpreter* interpreter) { //set the starting point for the interpreter @@ -2038,11 +1832,18 @@ static void execInterpreter(Interpreter* interpreter) { break; case OP_FN_CALL: - if (!execFnCall(interpreter)) { + if (!execFnCall(interpreter, false)) { return; } break; + case OP_DOT: + if (!execFnCall(interpreter, true)) { //compensate for the out-of-order arguments + return; + } + break; + + case OP_FN_RETURN: if (!execFnReturn(interpreter)) { return; @@ -2067,24 +1868,12 @@ static void execInterpreter(Interpreter* interpreter) { } 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)); @@ -2450,7 +2239,6 @@ void resetInterpreter(Interpreter* interpreter) { //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/lib_builtin.c b/source/lib_builtin.c index 151f11a..7377755 100644 --- a/source/lib_builtin.c +++ b/source/lib_builtin.c @@ -913,70 +913,6 @@ int _index(Interpreter* interpreter, LiteralArray* arguments) { return 1; } -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); - - Literal value = getLiteralDictionary(AS_DICTIONARY(compound), first); - - //dictionary - if (IS_NULL(op)) { - pushLiteralArray(&interpreter->stack, value); - } - - else if (!strcmp( AS_STRING(op), "=")) { - setLiteralDictionary(AS_DICTIONARY(compound), first, assign); - pushLiteralArray(&interpreter->stack, compound); - } - - else if (!strcmp( AS_STRING(op), "+=")) { - Literal lit = addition(interpreter, value, assign); - setLiteralDictionary(AS_DICTIONARY(compound), first, lit); - freeLiteral(lit); - pushLiteralArray(&interpreter->stack, compound); - } - - else if (!strcmp( AS_STRING(op), "-=")) { - Literal lit = subtraction(interpreter, value, assign); - setLiteralDictionary(AS_DICTIONARY(compound), first, lit); - freeLiteral(lit); - pushLiteralArray(&interpreter->stack, compound); - } - - else if (!strcmp( AS_STRING(op), "*=")) { - Literal lit = multiplication(interpreter, value, assign); - setLiteralDictionary(AS_DICTIONARY(compound), first, lit); - freeLiteral(lit); - pushLiteralArray(&interpreter->stack, compound); - } - - else if (!strcmp( AS_STRING(op), "/=")) { - Literal lit = division(interpreter, value, assign); - setLiteralDictionary(AS_DICTIONARY(compound), first, lit); - freeLiteral(lit); - pushLiteralArray(&interpreter->stack, compound); - } - - else if (!strcmp( AS_STRING(op), "%=")) { - Literal lit = modulo(interpreter, value, assign); - setLiteralDictionary(AS_DICTIONARY(compound), first, lit); - freeLiteral(lit); - pushLiteralArray(&interpreter->stack, compound); - } - - //cleanup - freeLiteral(op); - freeLiteral(assign); - freeLiteral(first); - freeLiteral(compound); - freeLiteral(value); - - return 1; -} - int _set(Interpreter* interpreter, LiteralArray* arguments) { //if wrong number of arguments, fail if (arguments->count != 3) { diff --git a/source/node.c b/source/node.c index a635bce..ac3cf10 100644 --- a/source/node.c +++ b/source/node.c @@ -207,11 +207,12 @@ void emitNodeFnDecl(Node** nodeHandle, Literal identifier, Node* arguments, Node *nodeHandle = tmp; } -void emitFnCall(Node** nodeHandle, Node* arguments) { +void emitFnCall(Node** nodeHandle, Node* arguments, int argumentCount) { Node* tmp = ALLOCATE(Node, 1); tmp->type = NODE_FN_CALL; tmp->fnCall.arguments = arguments; + tmp->fnCall.argumentCount = argumentCount; *nodeHandle = tmp; } diff --git a/source/node.h b/source/node.h index 82b2c33..e82e509 100644 --- a/source/node.h +++ b/source/node.h @@ -103,6 +103,7 @@ typedef struct NodeFnCollection { typedef struct NodeFnCall { NodeType type; Node* arguments; + int argumentCount; } NodeFnCall; typedef struct NodePath { @@ -162,7 +163,7 @@ void emitNodeCompound(Node** nodeHandle, LiteralType literalType); void setNodePair(Node* node, Node* left, Node* right); void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression); void emitNodeFnDecl(Node** nodeHandle, Literal identifier, Node* arguments, Node* returns, Node* block); -void emitFnCall(Node** nodeHandle, Node* arguments); +void emitFnCall(Node** nodeHandle, Node* arguments, int argumentCount); 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); diff --git a/source/opcodes.h b/source/opcodes.h index 1e647e4..66187b4 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -50,10 +50,8 @@ typedef enum Opcode { //for indexing OP_INDEX, - OP_DOT, - OP_INDEX_ASSIGN, - OP_DOT_ASSIGN, + OP_DOT, //comparison of values OP_COMPARE_EQUAL, diff --git a/source/parser.c b/source/parser.c index 4c693b2..d9579c0 100644 --- a/source/parser.c +++ b/source/parser.c @@ -677,7 +677,7 @@ static Opcode decrementInfix(Parser* parser, Node** nodeHandle) { } static Opcode fnCall(Parser* parser, Node** nodeHandle) { - advance(parser); + advance(parser); //skip the left paren //binary() is an infix rule - so only get the RHS of the operator switch(parser->previous.type) { @@ -708,7 +708,7 @@ static Opcode fnCall(Parser* parser, Node** nodeHandle) { } //emit the call - emitFnCall(nodeHandle, arguments); + emitFnCall(nodeHandle, arguments, arguments->fnCollection.count); return OP_FN_CALL; } @@ -786,14 +786,17 @@ static Opcode indexAccess(Parser* parser, Node** nodeHandle) { static Opcode dot(Parser* parser, Node** nodeHandle) { advance(parser); //for the dot - advance(parser); //for the identifier - Node* first = NULL; + Node* node = NULL; - identifier(parser, &first); //specific case - emitNodeDot(nodeHandle, first); + parsePrecedence(parser, &node, PREC_CALL); - return OP_DOT; + //hijack the function call, and hack in an extra parameter + node->binary.right->fnCall.argumentCount++; + node->binary.opcode = OP_DOT; + + (*nodeHandle) = node; + return OP_DOT; //signal that the function name and arguments are in the wrong order } ParseRule parseRules[] = { //must match the token types