mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
If-then-else is working with jump statements
This commit is contained in:
@@ -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
|
||||
|
||||
// //
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
21
test/jumps.toy
Normal 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";
|
||||
Reference in New Issue
Block a user