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];
|
assert true ? true : false, "Basic true ternary failed";
|
||||||
var arr: t = [1, 2, 3.14];
|
assert false ? false : true, "Basic false ternary failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test nesting
|
||||||
{
|
{
|
||||||
var t = astype [string:int];
|
fn least(a, b, c) {
|
||||||
var dict: t = ["one": 1, "two": 2, "pi": 3.14];
|
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);
|
freeASTNode(node->binary.right);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AST_NODE_TERNARY:
|
||||||
|
freeASTNode(node->ternary.condition);
|
||||||
|
freeASTNode(node->ternary.thenPath);
|
||||||
|
freeASTNode(node->ternary.elsePath);
|
||||||
|
break;
|
||||||
|
|
||||||
case AST_NODE_GROUPING:
|
case AST_NODE_GROUPING:
|
||||||
freeASTNode(node->grouping.child);
|
freeASTNode(node->grouping.child);
|
||||||
break;
|
break;
|
||||||
@@ -169,6 +175,17 @@ void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
|
|||||||
*nodeHandle = tmp;
|
*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) {
|
void emitASTNodeGrouping(ASTNode** nodeHandle) {
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ typedef enum ASTNodeType {
|
|||||||
AST_NODE_LITERAL, //a simple value
|
AST_NODE_LITERAL, //a simple value
|
||||||
AST_NODE_UNARY, //one child + opcode
|
AST_NODE_UNARY, //one child + opcode
|
||||||
AST_NODE_BINARY, //two children, left and right + 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_GROUPING, //one child
|
||||||
AST_NODE_BLOCK, //contains a sub-node array
|
AST_NODE_BLOCK, //contains a sub-node array
|
||||||
AST_NODE_COMPOUND, //contains a sub-node array
|
AST_NODE_COMPOUND, //contains a sub-node array
|
||||||
@@ -62,6 +63,16 @@ typedef struct NodeBinary {
|
|||||||
ASTNode* right;
|
ASTNode* right;
|
||||||
} NodeBinary;
|
} 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
|
//grouping of other AST nodes
|
||||||
void emitASTNodeGrouping(ASTNode** nodeHandle);
|
void emitASTNodeGrouping(ASTNode** nodeHandle);
|
||||||
|
|
||||||
@@ -232,6 +243,7 @@ union _node {
|
|||||||
NodeLiteral atomic;
|
NodeLiteral atomic;
|
||||||
NodeUnary unary;
|
NodeUnary unary;
|
||||||
NodeBinary binary;
|
NodeBinary binary;
|
||||||
|
NodeTernary ternary;
|
||||||
NodeGrouping grouping;
|
NodeGrouping grouping;
|
||||||
NodeBlock block;
|
NodeBlock block;
|
||||||
NodeCompound compound;
|
NodeCompound compound;
|
||||||
|
|||||||
@@ -331,6 +331,47 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, ASTNode* node, void* br
|
|||||||
}
|
}
|
||||||
break;
|
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: {
|
case AST_NODE_GROUPING: {
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)OP_GROUPING_BEGIN; //1 byte
|
compiler->bytecode[compiler->count++] = (unsigned char)OP_GROUPING_BEGIN; //1 byte
|
||||||
Opcode override = writeCompilerWithJumps(compiler, node->grouping.child, breakAddressesPtr, continueAddressesPtr, jumpOffsets, node->grouping.child);
|
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, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
|
||||||
|
|
||||||
|
case '?': return makeToken(lexer, TOKEN_QUESTION);
|
||||||
case ':': return makeToken(lexer, TOKEN_COLON);
|
case ':': return makeToken(lexer, TOKEN_COLON);
|
||||||
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
|
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
|
||||||
case ',': return makeToken(lexer, TOKEN_COMMA);
|
case ',': return makeToken(lexer, TOKEN_COMMA);
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ typedef enum Opcode {
|
|||||||
//pop the stack at the end of a complex statement
|
//pop the stack at the end of a complex statement
|
||||||
OP_POP_STACK,
|
OP_POP_STACK,
|
||||||
|
|
||||||
|
//ternary shorthand
|
||||||
|
OP_TERNARY,
|
||||||
|
|
||||||
//meta
|
//meta
|
||||||
OP_FN_END, //different from SECTION_END
|
OP_FN_END, //different from SECTION_END
|
||||||
OP_SECTION_END = 255,
|
OP_SECTION_END = 255,
|
||||||
|
|||||||
@@ -778,6 +778,21 @@ static Opcode indexAccess(Parser* parser, ASTNode** nodeHandle) { //TODO: fix in
|
|||||||
return OP_INDEX;
|
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) {
|
static Opcode dot(Parser* parser, ASTNode** nodeHandle) {
|
||||||
advance(parser); //for the dot
|
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_OF,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_PRINT,
|
{NULL, NULL, PREC_NONE},// TOKEN_PRINT,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_RETURN,
|
{NULL, NULL, PREC_NONE},// TOKEN_RETURN,
|
||||||
{atomic, NULL, PREC_NONE},// TOKEN_TYPE,
|
{atomic, NULL, PREC_PRIMARY},// TOKEN_TYPE,
|
||||||
{asType, NULL, PREC_PRIMARY},// TOKEN_ASTYPE,
|
{asType, NULL, PREC_CALL},// TOKEN_ASTYPE,
|
||||||
{typeOf, NULL, PREC_CALL},// TOKEN_TYPEOF,
|
{typeOf, NULL, PREC_CALL},// TOKEN_TYPEOF,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_VAR,
|
{NULL, NULL, PREC_NONE},// TOKEN_VAR,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_WHILE,
|
{NULL, NULL, PREC_NONE},// TOKEN_WHILE,
|
||||||
@@ -871,6 +886,7 @@ ParseRule parseRules[] = { //must match the token types
|
|||||||
{NULL, binary, PREC_OR},// TOKEN_OR,
|
{NULL, binary, PREC_OR},// TOKEN_OR,
|
||||||
|
|
||||||
//other operators
|
//other operators
|
||||||
|
{NULL, question, PREC_TERNARY}, //TOKEN_QUESTION,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_COLON,
|
{NULL, NULL, PREC_NONE},// TOKEN_COLON,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON,
|
{NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_COMMA,
|
{NULL, NULL, PREC_NONE},// TOKEN_COMMA,
|
||||||
@@ -1120,6 +1136,13 @@ static void parsePrecedence(Parser* parser, ASTNode** nodeHandle, PrecedenceRule
|
|||||||
dottify(parser, &rhsNode);
|
dottify(parser, &rhsNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BUGFIX: ternary shorthand
|
||||||
|
if (opcode == OP_TERNARY) {
|
||||||
|
rhsNode->ternary.condition = *nodeHandle;
|
||||||
|
*nodeHandle = rhsNode;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
emitASTNodeBinary(nodeHandle, rhsNode, opcode);
|
emitASTNodeBinary(nodeHandle, rhsNode, opcode);
|
||||||
|
|
||||||
//optimise away the constants
|
//optimise away the constants
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ typedef enum TokenType {
|
|||||||
TOKEN_OR,
|
TOKEN_OR,
|
||||||
|
|
||||||
//other operators
|
//other operators
|
||||||
|
TOKEN_QUESTION,
|
||||||
TOKEN_COLON,
|
TOKEN_COLON,
|
||||||
TOKEN_SEMICOLON,
|
TOKEN_SEMICOLON,
|
||||||
TOKEN_COMMA,
|
TOKEN_COMMA,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#define TOY_VERSION_MAJOR 0
|
#define TOY_VERSION_MAJOR 0
|
||||||
#define TOY_VERSION_MINOR 7
|
#define TOY_VERSION_MINOR 7
|
||||||
#define TOY_VERSION_PATCH 0
|
#define TOY_VERSION_PATCH 1
|
||||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||||
|
|
||||||
//platform-specific specifications
|
//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-dictionary.toy",
|
||||||
"long-literals.toy",
|
"long-literals.toy",
|
||||||
"native-functions.toy",
|
"native-functions.toy",
|
||||||
"panic-within-functions.toy",
|
"panic-within-functions.toy",
|
||||||
|
"ternary-expressions.toy",
|
||||||
"types.toy",
|
"types.toy",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user