If-then-else is working with jump statements

This commit is contained in:
2022-08-20 12:38:29 +01:00
parent cbdfcbcc14
commit 975ed41d14
8 changed files with 223 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

21
test/jumps.toy Normal file
View File

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