changed dot operator to access global functions

This commit is contained in:
2022-09-08 01:18:20 +01:00
parent 8550f3141c
commit 5861602f23
11 changed files with 81 additions and 378 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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