Added ternary operator, resolved #46

This commit is contained in:
2023-01-14 10:24:15 +00:00
parent 4b60d65203
commit ae1dc5841e
11 changed files with 140 additions and 9 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -78,6 +78,7 @@ typedef enum TokenType {
TOKEN_OR,
//other operators
TOKEN_QUESTION,
TOKEN_COLON,
TOKEN_SEMICOLON,
TOKEN_COMMA,

View File

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

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

View File

@@ -197,6 +197,7 @@ int main() {
"long-literals.toy",
"native-functions.toy",
"panic-within-functions.toy",
"ternary-expressions.toy",
"types.toy",
NULL
};