From 975ed41d148f217a8bc358a04fe5c8d6032d8f5a Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 20 Aug 2022 12:38:29 +0100 Subject: [PATCH] If-then-else is working with jump statements --- source/compiler.c | 42 +++++++++++++++++++++++++++++++++++- source/interpreter.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ source/interpreter.h | 1 + source/node.c | 39 +++++++++++++++++++++++++++++++++ source/node.h | 16 +++++++++++++- source/opcodes.h | 4 ++++ source/parser.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ test/jumps.toy | 21 ++++++++++++++++++ 8 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 test/jumps.toy diff --git a/source/compiler.c b/source/compiler.c index 3522e0f..301e10a 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -288,7 +288,47 @@ void writeCompiler(Compiler* compiler, Node* node) { } break; - //TODO: OP_VAR_ASSIGN + case NODE_PATH_IF: { + //process the condition + writeCompiler(compiler, node->path.condition); + + //cache the point to insert the jump distance at + compiler->bytecode[compiler->count++] = OP_IF_FALSE_JUMP; //1 byte + int jumpToElse = compiler->count; + compiler->count += sizeof(unsigned short); //2 bytes + + //write the then path + writeCompiler(compiler, node->path.thenPath); + + int jumpToEnd = 0; + + if (node->path.elsePath) { + //insert jump to end + compiler->bytecode[compiler->count++] = OP_JUMP; //1 byte + jumpToEnd = compiler->count; + compiler->count += sizeof(unsigned short); //2 bytes + } + + //update the jumpToElse to point here + compiler->bytecode[jumpToElse] = compiler->count; + + if (node->path.elsePath) { + //if there's an else path, write it and + writeCompiler(compiler, node->path.elsePath); + + //update the jumpToEnd to point here + compiler->bytecode[jumpToEnd] = compiler->count; + } + } + break; + + // case NODE_PATH_WHILE: { + // //cache the jump point + // int jumpToBeginning = compiler->count; + // compiler->count += sizeof(unsigned short); //2 bytes + + // // + // } } } diff --git a/source/interpreter.c b/source/interpreter.c index efe194e..c06d612 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -570,6 +570,42 @@ static bool execCompareLessEqual(Interpreter* interpreter, bool invert) { return true; } +static bool execJump(Interpreter* interpreter) { + int target = (int)readShort(interpreter->bytecode, &interpreter->count); + + if (target >= interpreter->length) { + printf("Jump out of range\n"); + return false; + } + + //actually jump + interpreter->count = target + interpreter->codeStart; + + return true; +} + +static bool execFalseJump(Interpreter* interpreter) { + int target = (int)readShort(interpreter->bytecode, &interpreter->count); + + if (target >= interpreter->length) { + printf("Jump out of range\n"); + return false; + } + + //actually jump + Literal lit = popLiteralArray(&interpreter->stack); + + if (!parseIdentifierToValue(interpreter, &lit)) { + return false; + } + + if (!IS_TRUTHY(lit)) { + interpreter->count = target + interpreter->codeStart; + } + + return true; +} + //the heart of toy static void execInterpreter(Interpreter* interpreter) { unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); @@ -690,6 +726,18 @@ static void execInterpreter(Interpreter* interpreter) { } break; + case OP_JUMP: + if (!execJump(interpreter)) { + return; + } + break; + + case OP_IF_FALSE_JUMP: + if (!execFalseJump(interpreter)) { + return; + } + break; + default: printf("Unknown opcode found %d, terminating\n", opcode); printLiteralArray(&interpreter->stack, "\n"); @@ -913,6 +961,9 @@ void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int lengt consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count); + //set the starting point for the interpreter + interpreter->codeStart = interpreter->count; + //code section if (command.verbose) { printf(NOTICE "executing bytecode\n" RESET); diff --git a/source/interpreter.h b/source/interpreter.h index e5ec05e..79b99ec 100644 --- a/source/interpreter.h +++ b/source/interpreter.h @@ -14,6 +14,7 @@ typedef struct Interpreter { unsigned char* bytecode; int length; int count; + int codeStart; LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided //operation diff --git a/source/node.c b/source/node.c index 3f1a0fd..d9d9e5e 100644 --- a/source/node.c +++ b/source/node.c @@ -61,6 +61,16 @@ void freeNode(Node* node) { freeLiteral(node->varDecl.typeLiteral); freeNode(node->varDecl.expression); break; + + case NODE_PATH_IF: + case NODE_PATH_WHILE: + case NODE_PATH_FOR: + freeNode(node->path.preClause); + freeNode(node->path.postClause); + freeNode(node->path.condition); + freeNode(node->path.thenPath); + freeNode(node->path.elsePath); + break; } } @@ -154,6 +164,19 @@ void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* *nodeHandle = tmp; } +void emitNodePath(Node** nodeHandle, NodeType type, Node* preClause, Node* postClause, Node* condition, Node* thenPath, Node* elsePath) { + Node* tmp = ALLOCATE(Node, 1); + + tmp->type = type; + tmp->path.preClause = preClause; + tmp->path.postClause = postClause; + tmp->path.condition = condition; + tmp->path.thenPath = thenPath; + tmp->path.elsePath = elsePath; + + *nodeHandle = tmp; +} + void printNode(Node* node) { if (node == NULL) { return; @@ -230,6 +253,22 @@ void printNode(Node* node) { printf(")"); break; + case NODE_PATH_IF: + case NODE_PATH_WHILE: + case NODE_PATH_FOR: + printf("path("); + printNode(node->path.preClause); + printf("; "); + printNode(node->path.condition); + printf("; "); + printNode(node->path.postClause); + printf("):("); + printNode(node->path.thenPath); + printf(")else("); + printNode(node->path.elsePath); + printf(")"); + break; + default: printf("[internal] unkown node type in printNode: %d\n", node->type); } diff --git a/source/node.h b/source/node.h index 686869f..7a75ac5 100644 --- a/source/node.h +++ b/source/node.h @@ -2,6 +2,7 @@ #include "literal.h" #include "opcodes.h" +#include "token_types.h" //nodes are the intermediaries between parsers and compilers typedef union _node Node; @@ -17,7 +18,9 @@ typedef enum NodeType { NODE_PAIR, //contains a left and right NODE_VAR_TYPES, //contains a type and a sub-node array for compound types NODE_VAR_DECL, //contains identifier literal, typenode, expression definition - // NODE_CONDITIONAL, //three children: conditional, then path, else path + NODE_PATH_IF, //for control flow + NODE_PATH_WHILE, //for control flow + NODE_PATH_FOR, //for control flow } NodeType; typedef struct NodeLiteral { @@ -76,6 +79,15 @@ typedef struct NodeVarDecl { Node* expression; } NodeVarDecl; +typedef struct NodePath { + NodeType type; + Node* preClause; + Node* postClause; + Node* condition; + Node* thenPath; + Node* elsePath; +} NodePath; + union _node { NodeType type; NodeLiteral atomic; @@ -87,6 +99,7 @@ union _node { NodePair pair; NodeVarTypes varTypes; NodeVarDecl varDecl; + NodePath path; }; void freeNode(Node* node); @@ -99,5 +112,6 @@ void emitNodeCompound(Node** nodeHandle, LiteralType literalType); void emitNodePair(Node** nodeHandle, Node* left, Node* right); void emitNodeVarTypes(Node** nodeHandle, Literal literal); void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression); +void emitNodePath(Node** nodeHandle, NodeType type, Node* preClause, Node* postClause, Node* condition, Node* thenPath, Node* elsePath); void printNode(Node* node); diff --git a/source/opcodes.h b/source/opcodes.h index b74f6ea..01edc6c 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -44,6 +44,10 @@ typedef enum Opcode { OP_COMPARE_GREATER_EQUAL, OP_INVERT, //for booleans + //jumps, and conditional jumps (absolute) + OP_JUMP, + OP_IF_FALSE_JUMP, + //meta OP_SECTION_END, //TODO: add more diff --git a/source/parser.c b/source/parser.c index 8a2d577..08c025f 100644 --- a/source/parser.c +++ b/source/parser.c @@ -919,6 +919,38 @@ static void assertStmt(Parser* parser, Node** nodeHandle) { consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement"); } +static void ifStmt(Parser* parser, Node** nodeHandle) { + Node* condition = NULL; + Node* thenPath = NULL; + Node* elsePath = NULL; + + //read the condition + consume(parser, TOKEN_PAREN_LEFT, "Expected '(' at end of if statement"); + parsePrecedence(parser, &condition, PREC_TERNARY); + + //read the then path + consume(parser, TOKEN_PAREN_RIGHT, "Expected ')' at end of if statement"); + thenPath = ALLOCATE(Node, 1); + declaration(parser, &thenPath); + + //read the optional else path + if (match(parser, TOKEN_ELSE)) { + elsePath = ALLOCATE(Node, 1); + declaration(parser, &elsePath); + } + + freeNode(*nodeHandle); //free the initial node + emitNodePath(nodeHandle, NODE_PATH_IF, NULL, NULL, condition, thenPath, elsePath); +} + +static void whileStmt(Parser* parser, Node** nodeHandle) { + // +} + +static void forStmt(Parser* parser, Node** nodeHandle) { + // +} + //precedence functions static void expressionStmt(Parser* parser, Node** nodeHandle) { //BUGFIX: check for empty statements @@ -959,6 +991,24 @@ static void statement(Parser* parser, Node** nodeHandle) { return; } + //if-then-else + if (match(parser, TOKEN_IF)) { + ifStmt(parser, nodeHandle); + return; + } + + //while-then + if (match(parser, TOKEN_WHILE)) { + whileStmt(parser, nodeHandle); + return; + } + + //for-pre-clause-post-then + if (match(parser, TOKEN_FOR)) { + forStmt(parser, nodeHandle); + return; + } + //default expressionStmt(parser, nodeHandle); } @@ -1058,6 +1108,7 @@ static void varDecl(Parser* parser, Node** nodeHandle) { //TODO: static type checking? //declare it + freeNode(*nodeHandle); //free the initial node emitNodeVarDecl(nodeHandle, identifier, typeLiteral, expressionNode); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of var declaration"); diff --git a/test/jumps.toy b/test/jumps.toy new file mode 100644 index 0000000..151a622 --- /dev/null +++ b/test/jumps.toy @@ -0,0 +1,21 @@ + + +//test true jump +if (true) { + assert true, "if-then failed"; +} +else { + assert false, "if-then failed"; +} + + +//test false jump +if (false) { + assert false, "if-then failed"; +} +else { + assert true, "if-then failed"; +} + + +print "All good"; \ No newline at end of file