mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +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;
|
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;
|
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
|
//the heart of toy
|
||||||
static void execInterpreter(Interpreter* interpreter) {
|
static void execInterpreter(Interpreter* interpreter) {
|
||||||
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
|
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
|
||||||
@@ -690,6 +726,18 @@ static void execInterpreter(Interpreter* interpreter) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OP_JUMP:
|
||||||
|
if (!execJump(interpreter)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_IF_FALSE_JUMP:
|
||||||
|
if (!execFalseJump(interpreter)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("Unknown opcode found %d, terminating\n", opcode);
|
printf("Unknown opcode found %d, terminating\n", opcode);
|
||||||
printLiteralArray(&interpreter->stack, "\n");
|
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);
|
consumeByte(OP_SECTION_END, interpreter->bytecode, &interpreter->count);
|
||||||
|
|
||||||
|
//set the starting point for the interpreter
|
||||||
|
interpreter->codeStart = interpreter->count;
|
||||||
|
|
||||||
//code section
|
//code section
|
||||||
if (command.verbose) {
|
if (command.verbose) {
|
||||||
printf(NOTICE "executing bytecode\n" RESET);
|
printf(NOTICE "executing bytecode\n" RESET);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ typedef struct Interpreter {
|
|||||||
unsigned char* bytecode;
|
unsigned char* bytecode;
|
||||||
int length;
|
int length;
|
||||||
int count;
|
int count;
|
||||||
|
int codeStart;
|
||||||
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
||||||
|
|
||||||
//operation
|
//operation
|
||||||
|
|||||||
@@ -61,6 +61,16 @@ void freeNode(Node* node) {
|
|||||||
freeLiteral(node->varDecl.typeLiteral);
|
freeLiteral(node->varDecl.typeLiteral);
|
||||||
freeNode(node->varDecl.expression);
|
freeNode(node->varDecl.expression);
|
||||||
break;
|
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;
|
*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) {
|
void printNode(Node* node) {
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
return;
|
return;
|
||||||
@@ -230,6 +253,22 @@ void printNode(Node* node) {
|
|||||||
printf(")");
|
printf(")");
|
||||||
break;
|
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:
|
default:
|
||||||
printf("[internal] unkown node type in printNode: %d\n", node->type);
|
printf("[internal] unkown node type in printNode: %d\n", node->type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "literal.h"
|
#include "literal.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
|
#include "token_types.h"
|
||||||
|
|
||||||
//nodes are the intermediaries between parsers and compilers
|
//nodes are the intermediaries between parsers and compilers
|
||||||
typedef union _node Node;
|
typedef union _node Node;
|
||||||
@@ -17,7 +18,9 @@ typedef enum NodeType {
|
|||||||
NODE_PAIR, //contains a left and right
|
NODE_PAIR, //contains a left and right
|
||||||
NODE_VAR_TYPES, //contains a type and a sub-node array for compound types
|
NODE_VAR_TYPES, //contains a type and a sub-node array for compound types
|
||||||
NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
|
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;
|
} NodeType;
|
||||||
|
|
||||||
typedef struct NodeLiteral {
|
typedef struct NodeLiteral {
|
||||||
@@ -76,6 +79,15 @@ typedef struct NodeVarDecl {
|
|||||||
Node* expression;
|
Node* expression;
|
||||||
} NodeVarDecl;
|
} NodeVarDecl;
|
||||||
|
|
||||||
|
typedef struct NodePath {
|
||||||
|
NodeType type;
|
||||||
|
Node* preClause;
|
||||||
|
Node* postClause;
|
||||||
|
Node* condition;
|
||||||
|
Node* thenPath;
|
||||||
|
Node* elsePath;
|
||||||
|
} NodePath;
|
||||||
|
|
||||||
union _node {
|
union _node {
|
||||||
NodeType type;
|
NodeType type;
|
||||||
NodeLiteral atomic;
|
NodeLiteral atomic;
|
||||||
@@ -87,6 +99,7 @@ union _node {
|
|||||||
NodePair pair;
|
NodePair pair;
|
||||||
NodeVarTypes varTypes;
|
NodeVarTypes varTypes;
|
||||||
NodeVarDecl varDecl;
|
NodeVarDecl varDecl;
|
||||||
|
NodePath path;
|
||||||
};
|
};
|
||||||
|
|
||||||
void freeNode(Node* node);
|
void freeNode(Node* node);
|
||||||
@@ -99,5 +112,6 @@ void emitNodeCompound(Node** nodeHandle, LiteralType literalType);
|
|||||||
void emitNodePair(Node** nodeHandle, Node* left, Node* right);
|
void emitNodePair(Node** nodeHandle, Node* left, Node* right);
|
||||||
void emitNodeVarTypes(Node** nodeHandle, Literal literal);
|
void emitNodeVarTypes(Node** nodeHandle, Literal literal);
|
||||||
void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression);
|
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);
|
void printNode(Node* node);
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ typedef enum Opcode {
|
|||||||
OP_COMPARE_GREATER_EQUAL,
|
OP_COMPARE_GREATER_EQUAL,
|
||||||
OP_INVERT, //for booleans
|
OP_INVERT, //for booleans
|
||||||
|
|
||||||
|
//jumps, and conditional jumps (absolute)
|
||||||
|
OP_JUMP,
|
||||||
|
OP_IF_FALSE_JUMP,
|
||||||
|
|
||||||
//meta
|
//meta
|
||||||
OP_SECTION_END,
|
OP_SECTION_END,
|
||||||
//TODO: add more
|
//TODO: add more
|
||||||
|
|||||||
@@ -919,6 +919,38 @@ static void assertStmt(Parser* parser, Node** nodeHandle) {
|
|||||||
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement");
|
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
|
//precedence functions
|
||||||
static void expressionStmt(Parser* parser, Node** nodeHandle) {
|
static void expressionStmt(Parser* parser, Node** nodeHandle) {
|
||||||
//BUGFIX: check for empty statements
|
//BUGFIX: check for empty statements
|
||||||
@@ -959,6 +991,24 @@ static void statement(Parser* parser, Node** nodeHandle) {
|
|||||||
return;
|
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
|
//default
|
||||||
expressionStmt(parser, nodeHandle);
|
expressionStmt(parser, nodeHandle);
|
||||||
}
|
}
|
||||||
@@ -1058,6 +1108,7 @@ static void varDecl(Parser* parser, Node** nodeHandle) {
|
|||||||
//TODO: static type checking?
|
//TODO: static type checking?
|
||||||
|
|
||||||
//declare it
|
//declare it
|
||||||
|
freeNode(*nodeHandle); //free the initial node
|
||||||
emitNodeVarDecl(nodeHandle, identifier, typeLiteral, expressionNode);
|
emitNodeVarDecl(nodeHandle, identifier, typeLiteral, expressionNode);
|
||||||
|
|
||||||
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of var declaration");
|
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