mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Added ternary operator, resolved #46
This commit is contained in:
@@ -1,10 +1,21 @@
|
||||
|
||||
//test basic truth ternaries
|
||||
{
|
||||
var t = astype [int];
|
||||
var arr: t = [1, 2, 3.14];
|
||||
assert true ? true : false, "Basic true ternary failed";
|
||||
assert false ? false : true, "Basic false ternary failed";
|
||||
}
|
||||
|
||||
|
||||
//test nesting
|
||||
{
|
||||
var t = astype [string:int];
|
||||
var dict: t = ["one": 1, "two": 2, "pi": 3.14];
|
||||
fn least(a, b, c) {
|
||||
return a < b ? a : b < c ? b : c;
|
||||
}
|
||||
|
||||
assert least(1, 2, 3) == 1, "Least 1, 2, 3 failed";
|
||||
assert least(10, 5, 7) == 5, "Least 10, 5, 7 failed";
|
||||
assert least(9, 7, 5) == 5, "Least 9, 7, 5 failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
|
||||
@@ -29,6 +29,12 @@ void freeASTNodeCustom(ASTNode* node, bool freeSelf) {
|
||||
freeASTNode(node->binary.right);
|
||||
break;
|
||||
|
||||
case AST_NODE_TERNARY:
|
||||
freeASTNode(node->ternary.condition);
|
||||
freeASTNode(node->ternary.thenPath);
|
||||
freeASTNode(node->ternary.elsePath);
|
||||
break;
|
||||
|
||||
case AST_NODE_GROUPING:
|
||||
freeASTNode(node->grouping.child);
|
||||
break;
|
||||
@@ -169,6 +175,17 @@ void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeTernary(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_TERNARY;
|
||||
tmp->ternary.condition = condition;
|
||||
tmp->ternary.thenPath = thenPath;
|
||||
tmp->ternary.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeGrouping(ASTNode** nodeHandle) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ typedef enum ASTNodeType {
|
||||
AST_NODE_LITERAL, //a simple value
|
||||
AST_NODE_UNARY, //one child + opcode
|
||||
AST_NODE_BINARY, //two children, left and right + opcode
|
||||
AST_NODE_TERNARY, //three children, condition, then path & else path
|
||||
AST_NODE_GROUPING, //one child
|
||||
AST_NODE_BLOCK, //contains a sub-node array
|
||||
AST_NODE_COMPOUND, //contains a sub-node array
|
||||
@@ -62,6 +63,16 @@ typedef struct NodeBinary {
|
||||
ASTNode* right;
|
||||
} NodeBinary;
|
||||
|
||||
//ternary operator
|
||||
void emitASTNodeTernary(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
|
||||
|
||||
typedef struct NodeTernary {
|
||||
ASTNodeType type;
|
||||
ASTNode* condition;
|
||||
ASTNode* thenPath;
|
||||
ASTNode* elsePath;
|
||||
} NodeTernary;
|
||||
|
||||
//grouping of other AST nodes
|
||||
void emitASTNodeGrouping(ASTNode** nodeHandle);
|
||||
|
||||
@@ -232,6 +243,7 @@ union _node {
|
||||
NodeLiteral atomic;
|
||||
NodeUnary unary;
|
||||
NodeBinary binary;
|
||||
NodeTernary ternary;
|
||||
NodeGrouping grouping;
|
||||
NodeBlock block;
|
||||
NodeCompound compound;
|
||||
|
||||
@@ -331,6 +331,47 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, ASTNode* node, void* br
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_NODE_TERNARY: {
|
||||
// TODO
|
||||
|
||||
//process the condition
|
||||
Opcode override = writeCompilerWithJumps(compiler, node->ternary.condition, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
||||
if (override != OP_EOF) {//compensate for indexing & dot notation being screwy
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||
}
|
||||
|
||||
//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
|
||||
override = writeCompilerWithJumps(compiler, node->pathIf.thenPath, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
||||
if (override != OP_EOF) {//compensate for indexing & dot notation being screwy
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||
}
|
||||
|
||||
int jumpToEnd = 0;
|
||||
|
||||
//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
|
||||
AS_USHORT(compiler->bytecode[jumpToElse]) = compiler->count + jumpOffsets; //2 bytes
|
||||
|
||||
//write the else path
|
||||
Opcode override2 = writeCompilerWithJumps(compiler, node->pathIf.elsePath, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
||||
if (override2 != OP_EOF) {//compensate for indexing & dot notation being screwy
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||
}
|
||||
|
||||
//update the jumpToEnd to point here
|
||||
AS_USHORT(compiler->bytecode[jumpToEnd]) = compiler->count + jumpOffsets; //2 bytes
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_NODE_GROUPING: {
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)OP_GROUPING_BEGIN; //1 byte
|
||||
Opcode override = writeCompilerWithJumps(compiler, node->grouping.child, breakAddressesPtr, continueAddressesPtr, jumpOffsets, node->grouping.child);
|
||||
|
||||
@@ -292,6 +292,7 @@ Token scanLexer(Lexer* lexer) {
|
||||
|
||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
|
||||
|
||||
case '?': return makeToken(lexer, TOKEN_QUESTION);
|
||||
case ':': return makeToken(lexer, TOKEN_COLON);
|
||||
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
|
||||
case ',': return makeToken(lexer, TOKEN_COMMA);
|
||||
|
||||
@@ -76,6 +76,9 @@ typedef enum Opcode {
|
||||
//pop the stack at the end of a complex statement
|
||||
OP_POP_STACK,
|
||||
|
||||
//ternary shorthand
|
||||
OP_TERNARY,
|
||||
|
||||
//meta
|
||||
OP_FN_END, //different from SECTION_END
|
||||
OP_SECTION_END = 255,
|
||||
|
||||
@@ -778,6 +778,21 @@ static Opcode indexAccess(Parser* parser, ASTNode** nodeHandle) { //TODO: fix in
|
||||
return OP_INDEX;
|
||||
}
|
||||
|
||||
static Opcode question(Parser* parser, ASTNode** nodeHandle) {
|
||||
advance(parser); //for the question mark
|
||||
|
||||
ASTNode* thenPath = NULL;
|
||||
ASTNode* elsePath = NULL;
|
||||
|
||||
parsePrecedence(parser, &thenPath, PREC_TERNARY);
|
||||
consume(parser, TOKEN_COLON, "Expected ':' in ternary expression");
|
||||
parsePrecedence(parser, &elsePath, PREC_TERNARY);
|
||||
|
||||
emitASTNodeTernary(nodeHandle, NULL, thenPath, elsePath);
|
||||
|
||||
return OP_TERNARY;
|
||||
}
|
||||
|
||||
static Opcode dot(Parser* parser, ASTNode** nodeHandle) {
|
||||
advance(parser); //for the dot
|
||||
|
||||
@@ -824,8 +839,8 @@ ParseRule parseRules[] = { //must match the token types
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_OF,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_PRINT,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_RETURN,
|
||||
{atomic, NULL, PREC_NONE},// TOKEN_TYPE,
|
||||
{asType, NULL, PREC_PRIMARY},// TOKEN_ASTYPE,
|
||||
{atomic, NULL, PREC_PRIMARY},// TOKEN_TYPE,
|
||||
{asType, NULL, PREC_CALL},// TOKEN_ASTYPE,
|
||||
{typeOf, NULL, PREC_CALL},// TOKEN_TYPEOF,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_VAR,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_WHILE,
|
||||
@@ -871,6 +886,7 @@ ParseRule parseRules[] = { //must match the token types
|
||||
{NULL, binary, PREC_OR},// TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
{NULL, question, PREC_TERNARY}, //TOKEN_QUESTION,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_COLON,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_COMMA,
|
||||
@@ -1120,6 +1136,13 @@ static void parsePrecedence(Parser* parser, ASTNode** nodeHandle, PrecedenceRule
|
||||
dottify(parser, &rhsNode);
|
||||
}
|
||||
|
||||
//BUGFIX: ternary shorthand
|
||||
if (opcode == OP_TERNARY) {
|
||||
rhsNode->ternary.condition = *nodeHandle;
|
||||
*nodeHandle = rhsNode;
|
||||
continue;
|
||||
}
|
||||
|
||||
emitASTNodeBinary(nodeHandle, rhsNode, opcode);
|
||||
|
||||
//optimise away the constants
|
||||
|
||||
@@ -78,6 +78,7 @@ typedef enum TokenType {
|
||||
TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
TOKEN_QUESTION,
|
||||
TOKEN_COLON,
|
||||
TOKEN_SEMICOLON,
|
||||
TOKEN_COMMA,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#define TOY_VERSION_MAJOR 0
|
||||
#define TOY_VERSION_MINOR 7
|
||||
#define TOY_VERSION_PATCH 0
|
||||
#define TOY_VERSION_PATCH 1
|
||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||
|
||||
//platform-specific specifications
|
||||
|
||||
21
test/scripts/ternary-expressions.toy
Normal file
21
test/scripts/ternary-expressions.toy
Normal file
@@ -0,0 +1,21 @@
|
||||
//test basic truth ternaries
|
||||
{
|
||||
assert true ? true : false, "Basic true ternary failed";
|
||||
assert false ? false : true, "Basic false ternary failed";
|
||||
}
|
||||
|
||||
|
||||
//test nesting
|
||||
{
|
||||
fn least(a, b, c) {
|
||||
return a < b ? a : b < c ? b : c;
|
||||
}
|
||||
|
||||
assert least(1, 2, 3) == 1, "Least 1, 2, 3 failed";
|
||||
assert least(10, 5, 7) == 5, "Least 10, 5, 7 failed";
|
||||
assert least(9, 7, 5) == 5, "Least 9, 7, 5 failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -196,7 +196,8 @@ int main() {
|
||||
"long-dictionary.toy",
|
||||
"long-literals.toy",
|
||||
"native-functions.toy",
|
||||
"panic-within-functions.toy",
|
||||
"panic-within-functions.toy",
|
||||
"ternary-expressions.toy",
|
||||
"types.toy",
|
||||
NULL
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user