From 4aa6f75ea78dd5d2e122ee550771ba865c12ef28 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 14 Aug 2022 19:57:15 +0100 Subject: [PATCH] Expression statements and assignemnts are working --- scripts/example.toy | 12 ++++--- source/interpreter.c | 60 ++++++++++++++++++++++++++++------ source/interpreter.h | 2 +- source/opcodes.h | 7 ++-- source/parser.c | 77 +++++++++++++++++++++++++++----------------- source/repl_main.c | 2 ++ source/token_types.h | 2 +- 7 files changed, 112 insertions(+), 50 deletions(-) diff --git a/scripts/example.toy b/scripts/example.toy index 7a3b328..3676895 100644 --- a/scripts/example.toy +++ b/scripts/example.toy @@ -52,18 +52,20 @@ print [:]; //test nested compounds print [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; -//not ready yet +//var declarations var x = 31; var y : int = 42; var arr : [int] = [1, 2, 3, 42]; var dict : [string, int] = ["hello": 1, "world":2]; -//print x; -//print x + y; -//print arr; -//print dict; +//printing expressions +print x; +print x + y; +print arr; +print dict; //test asserts at the end of the file +assert x, "This won't be seen"; assert true, "This won't be seen"; assert false, "This is a failed assert, and will end execution"; diff --git a/source/interpreter.c b/source/interpreter.c index e517e9c..2322820 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -110,13 +110,27 @@ static void consumeShort(unsigned short bytes, unsigned char* tb, int* count) { *count += 2; } +static Literal parseIdentifierToValue(Interpreter* interpreter, Literal literal) { + //this converts identifiers to values + if (IS_IDENTIFIER(literal)) { + if (!getScopeVariable(interpreter->scope, literal, &literal)) { + printf("Undeclared variable \"");; + printLiteral(literal); + printf("\"\n"); + return TO_NULL_LITERAL; + } + } + + return literal; +} + //each available statement static bool execAssert(Interpreter* interpreter) { Literal rhs = popLiteralArray(&interpreter->stack); - Literal lhs = popLiteralArray(&interpreter->stack); + Literal lhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)); if (!IS_STRING(rhs)) { - printf("[internal] The interpreter's assert keyword needs a string as the second argument, received: "); + printf("The assert keyword needs a string as the second argument, received: "); printLiteral(rhs); printf("\n"); return false; @@ -132,7 +146,7 @@ static bool execAssert(Interpreter* interpreter) { static bool execPrint(Interpreter* interpreter) { //print what is on top of the stack, then pop it - Literal lit = popLiteralArray(&interpreter->stack); + Literal lit = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)); printLiteralCustom(lit, interpreter->printOutput); @@ -152,7 +166,7 @@ static bool execPushLiteral(Interpreter* interpreter, bool lng) { index = (int)readByte(interpreter->bytecode, &interpreter->count); } - //push from cache to stack + //push from cache to stack (DO NOT account for identifiers - will do that later) pushLiteralArray(&interpreter->stack, interpreter->literalCache.literals[index]); return true; @@ -160,7 +174,7 @@ static bool execPushLiteral(Interpreter* interpreter, bool lng) { static bool execNegate(Interpreter* interpreter) { //negate the top literal on the stack - Literal lit = popLiteralArray(&interpreter->stack); + Literal lit = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)); if (IS_INTEGER(lit)) { lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit)); @@ -180,8 +194,8 @@ static bool execNegate(Interpreter* interpreter) { } static bool execArithmetic(Interpreter* interpreter, Opcode opcode) { - Literal rhs = popLiteralArray(&interpreter->stack); - Literal lhs = popLiteralArray(&interpreter->stack); + Literal rhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)); + Literal lhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)); //type coersion if (IS_FLOAT(lhs) && IS_INTEGER(rhs)) { @@ -264,7 +278,11 @@ static bool execArithmetic(Interpreter* interpreter, Opcode opcode) { } //wrong types - printf("Bad arithmetic argument\n"); + printf("Bad arithmetic argument "); + printLiteral(lhs); + printf(" and "); + printLiteral(rhs); + printf("\n"); return false; } @@ -286,19 +304,35 @@ static bool execVarDecl(Interpreter* interpreter, bool lng) { Literal type = interpreter->literalCache.literals[typeIndex]; if (!declareScopeVariable(interpreter->scope, identifier, type)) { - printf("Can't redefine the variable \"");; + printf("Can't redefine the variable \"");; printLiteral(identifier); printf("\"\n"); return false; } - if (!setScopeVariable(interpreter->scope, identifier, popLiteralArray(&interpreter->stack) )) { + if (!setScopeVariable(interpreter->scope, identifier, parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)) )) { return false; } return true; } +static bool execVarAssign(Interpreter* interpreter) { + Literal rhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)); + Literal lhs = popLiteralArray(&interpreter->stack); + + if (!IS_IDENTIFIER(lhs)) { + printf("Can't assign to a non-variable \"");; + printLiteral(lhs); + printf("\"\n"); + return false; + } + + setScopeVariable(interpreter->scope, lhs, rhs); + + return true; +} + //the heart of toy static void execInterpreter(Interpreter* interpreter) { unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); @@ -365,6 +399,12 @@ static void execInterpreter(Interpreter* interpreter) { } break; + case OP_VAR_ASSIGN: + if (!execVarAssign(interpreter)) { + return; + } + break; + default: printf("Unknown opcode found %d, terminating\n", opcode); printLiteralArray(&interpreter->stack, "\n"); diff --git a/source/interpreter.h b/source/interpreter.h index 5bbfeda..e5ec05e 100644 --- a/source/interpreter.h +++ b/source/interpreter.h @@ -14,7 +14,7 @@ typedef struct Interpreter { unsigned char* bytecode; int length; int count; - LiteralArray literalCache; //read-only - built from the bytecode + LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided //operation Scope* scope; diff --git a/source/opcodes.h b/source/opcodes.h index 41f7a92..4c00619 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -28,9 +28,10 @@ typedef enum Opcode { OP_TYPE_DECL, //declare a type to be used (as a literal) OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal) - OP_VAR_DECL, - OP_VAR_DECL_LONG, - // OP_VAR_ASSIGN, //stack: literal name, literal value + OP_VAR_DECL, //declare a variable to be used (as a literal) + OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal) + + OP_VAR_ASSIGN, //assign to a literal //meta OP_SECTION_END, diff --git a/source/parser.c b/source/parser.c index fe7dd94..153b132 100644 --- a/source/parser.c +++ b/source/parser.c @@ -300,6 +300,11 @@ static Opcode binary(Parser* parser, Node** nodeHandle, bool canBeAssigned) { return OP_MODULO; } + case TOKEN_ASSIGN: { + parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); + return OP_VAR_ASSIGN; + } + default: error(parser, parser->previous, "Unexpected token passed to binary precedence rule"); return OP_EOF; @@ -384,6 +389,17 @@ static Opcode atomic(Parser* parser, Node** nodeHandle, bool canBeAssigned) { } } +static Opcode identifier(Parser* parser, Node** nodeHandle, bool canBeAssigned) { + //make a copy of the string + Token identifierToken = parser->previous; + char* cpy = copyString(identifierToken.lexeme, identifierToken.length); + Literal identifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro + + emitNodeLiteral(nodeHandle, identifier); + + return OP_EOF; +} + ParseRule parseRules[] = { //must match the token types //types {atomic, NULL, PREC_PRIMARY},// TOKEN_NULL, @@ -419,7 +435,7 @@ ParseRule parseRules[] = { //must match the token types {NULL, NULL, PREC_NONE},// TOKEN_WHILE, //literal values - {NULL, NULL, PREC_NONE},// TOKEN_IDENTIFIER, + {identifier, NULL, PREC_PRIMARY},// TOKEN_IDENTIFIER, {atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_TRUE, {atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_FALSE, {atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_INTEGER, @@ -439,6 +455,7 @@ ParseRule parseRules[] = { //must match the token types {NULL, NULL, PREC_NONE},// TOKEN_MODULO_ASSIGN, {NULL, NULL, PREC_NONE},// TOKEN_PLUS_PLUS, {NULL, NULL, PREC_NONE},// TOKEN_MINUS_MINUS, + {NULL, binary, PREC_ASSIGNMENT},// TOKEN_ASSIGN, //logical operators {grouping, NULL, PREC_CALL},// TOKEN_PAREN_LEFT, @@ -458,7 +475,6 @@ ParseRule parseRules[] = { //must match the token types {NULL, NULL, PREC_NONE},// TOKEN_OR, //other operators - {NULL, NULL, PREC_NONE},// TOKEN_ASSIGN, {NULL, NULL, PREC_NONE},// TOKEN_COLON, {NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON, {NULL, NULL, PREC_CALL},// TOKEN_COMMA, @@ -650,26 +666,26 @@ static void expression(Parser* parser, Node** nodeHandle) { } //statements -static void blockStmt(Parser* parser, Node* node) { +static void blockStmt(Parser* parser, Node** nodeHandle) { //init - node->type = NODE_BLOCK; - node->block.nodes = NULL; - node->block.capacity = 0; - node->block.count = 0; + (*nodeHandle)->type = NODE_BLOCK; + (*nodeHandle)->block.nodes = NULL; + (*nodeHandle)->block.capacity = 0; + (*nodeHandle)->block.count = 0; //sub-scope, compile it and push it up in a node while (!match(parser, TOKEN_BRACE_RIGHT)) { - if (node->block.capacity < node->block.count + 1) { - int oldCapacity = node->block.capacity; + if ((*nodeHandle)->block.capacity < (*nodeHandle)->block.count + 1) { + int oldCapacity = (*nodeHandle)->block.capacity; - node->block.capacity = GROW_CAPACITY(oldCapacity); - node->block.nodes = GROW_ARRAY(Node, node->block.nodes, oldCapacity, node->block.capacity); + (*nodeHandle)->block.capacity = GROW_CAPACITY(oldCapacity); + (*nodeHandle)->block.nodes = GROW_ARRAY(Node, (*nodeHandle)->block.nodes, oldCapacity, (*nodeHandle)->block.capacity); } //use the next node in sequence - node->block.nodes[node->block.count].type = NODE_ERROR; //BUGFIX: so freeing won't break the damn thing + (*nodeHandle)->block.nodes[(*nodeHandle)->block.count].type = NODE_ERROR; //BUGFIX: so freeing won't break the damn thing - Node* ptr = &(node->block.nodes[node->block.count++]); + Node* ptr = &((*nodeHandle)->block.nodes[(*nodeHandle)->block.count++]); //process the grammar rule for this line declaration(parser, &ptr); @@ -681,53 +697,54 @@ static void blockStmt(Parser* parser, Node* node) { } } -static void printStmt(Parser* parser, Node* node) { +static void printStmt(Parser* parser, Node** nodeHandle) { //set the node info - node->type = NODE_UNARY; - node->unary.opcode = OP_PRINT; - expression(parser, &(node->unary.child)); + (*nodeHandle)->type = NODE_UNARY; + (*nodeHandle)->unary.opcode = OP_PRINT; + expression(parser, &((*nodeHandle)->unary.child)); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of print statement"); } -static void assertStmt(Parser* parser, Node* node) { +static void assertStmt(Parser* parser, Node** nodeHandle) { //set the node info - node->type = NODE_BINARY; - node->unary.opcode = OP_ASSERT; + (*nodeHandle)->type = NODE_BINARY; + (*nodeHandle)->unary.opcode = OP_ASSERT; - parsePrecedence(parser, &(node->binary.left), PREC_PRIMARY); + parsePrecedence(parser, &((*nodeHandle)->binary.left), PREC_PRIMARY); consume(parser, TOKEN_COMMA, "Expected ',' in assert statement"); - parsePrecedence(parser, &(node->binary.right), PREC_PRIMARY); + parsePrecedence(parser, &((*nodeHandle)->binary.right), PREC_PRIMARY); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement"); } //precedence functions -static void expressionStmt(Parser* parser, Node* node) { - error(parser, parser->previous, "Expression statements not yet implemented"); +static void expressionStmt(Parser* parser, Node** nodeHandle) { + expression(parser, nodeHandle); + consume(parser, TOKEN_SEMICOLON, "Expected ';' at the end of expression statement"); } -static void statement(Parser* parser, Node* node) { +static void statement(Parser* parser, Node** nodeHandle) { //block if (match(parser, TOKEN_BRACE_LEFT)) { - blockStmt(parser, node); + blockStmt(parser, nodeHandle); return; } //print if (match(parser, TOKEN_PRINT)) { - printStmt(parser, node); + printStmt(parser, nodeHandle); return; } //assert if (match(parser, TOKEN_ASSERT)) { - assertStmt(parser, node); + assertStmt(parser, nodeHandle); return; } //default - expressionStmt(parser, node); + expressionStmt(parser, nodeHandle); } //declarations and definitions @@ -840,7 +857,7 @@ static void declaration(Parser* parser, Node** nodeHandle) { //assume nodeHandle varDecl(parser, nodeHandle); } else { - statement(parser, *nodeHandle); + statement(parser, nodeHandle); } } diff --git a/source/repl_main.c b/source/repl_main.c index 29b9a06..ed31dd1 100644 --- a/source/repl_main.c +++ b/source/repl_main.c @@ -78,6 +78,7 @@ unsigned char* compileString(char* source, size_t* size) { while(node != NULL) { //pack up and leave if (node->type == NODE_ERROR) { + printf(ERROR "error node detected\n" RESET); freeNode(node); freeCompiler(&compiler); freeParser(&parser); @@ -163,6 +164,7 @@ void repl() { while(node != NULL) { //pack up and restart if (node->type == NODE_ERROR) { + printf(ERROR "error node detected\n" RESET); error = true; freeNode(node); break; diff --git a/source/token_types.h b/source/token_types.h index e38b601..c818478 100644 --- a/source/token_types.h +++ b/source/token_types.h @@ -55,6 +55,7 @@ typedef enum TokenType { TOKEN_MODULO_ASSIGN, TOKEN_PLUS_PLUS, TOKEN_MINUS_MINUS, + TOKEN_ASSIGN, //logical operators TOKEN_PAREN_LEFT, @@ -74,7 +75,6 @@ typedef enum TokenType { TOKEN_OR, //other operators - TOKEN_ASSIGN, TOKEN_COLON, TOKEN_SEMICOLON, TOKEN_COMMA,