diff --git a/media/test-project/os-test.spice b/media/test-project/os-test.spice index 34e41f8b9..95936d06f 100644 --- a/media/test-project/os-test.spice +++ b/media/test-project/os-test.spice @@ -10,9 +10,23 @@ f main() { printf("Hello %s!", p1.getSecond()); }*/ -import "std/net/http" as http; +/*import "std/net/http" as http; f main() { http::HttpServer server = http::HttpServer(); server.serve("/test", "Hello World!"); +}*/ + +f f1() { + printf("F1 called.\n"); + return false; +} + +f f2() { + printf("F2 called.\n"); + return false; +} + +f main() { + printf("Result: %d", f1() ?: f2()); } \ No newline at end of file diff --git a/src/Spice.g4 b/src/Spice.g4 index 193754faf..98c2666a3 100644 --- a/src/Spice.g4 +++ b/src/Spice.g4 @@ -48,7 +48,7 @@ joinCall: JOIN LPAREN assignExpr (COMMA assignExpr)* RPAREN; // Expression loop assignExpr: prefixUnaryExpr assignOp assignExpr | ternaryExpr | threadDef; -ternaryExpr: logicalOrExpr (QUESTION_MARK logicalOrExpr COLON logicalOrExpr)?; +ternaryExpr: logicalOrExpr (QUESTION_MARK logicalOrExpr? COLON logicalOrExpr)?; logicalOrExpr: logicalAndExpr (LOGICAL_OR logicalAndExpr)*; logicalAndExpr: bitwiseOrExpr (LOGICAL_AND bitwiseOrExpr)*; bitwiseOrExpr: bitwiseXorExpr (BITWISE_OR bitwiseXorExpr)*; diff --git a/src/analyzer/AnalyzerVisitor.cpp b/src/analyzer/AnalyzerVisitor.cpp index 21cda463b..7f79b63aa 100644 --- a/src/analyzer/AnalyzerVisitor.cpp +++ b/src/analyzer/AnalyzerVisitor.cpp @@ -1299,10 +1299,17 @@ std::any AnalyzerVisitor::visitAssignExpr(AssignExprNode *node) { std::any AnalyzerVisitor::visitTernaryExpr(TernaryExprNode *node) { // Check if there is a ternary operator applied if (node->children.size() > 1) { - auto condition = node->operands()[0]; + LogicalOrExprNode *condition = node->operands()[0]; auto conditionType = any_cast(visit(condition)); - auto trueType = any_cast(visit(node->operands()[1])); - auto falseType = any_cast(visit(node->operands()[2])); + SymbolType trueType; + SymbolType falseType; + if (node->isShortened) { + trueType = conditionType; + falseType = any_cast(visit(node->operands()[1])); + } else { + trueType = any_cast(visit(node->operands()[1])); + falseType = any_cast(visit(node->operands()[2])); + } // Check if the condition evaluates to boolean if (!conditionType.is(TY_BOOL)) throw err->get(condition->codeLoc, OPERATOR_WRONG_DATA_TYPE, "Condition operand in ternary must be a bool"); diff --git a/src/ast/AstNodes.h b/src/ast/AstNodes.h index 9cccbb799..dbaa32202 100644 --- a/src/ast/AstNodes.h +++ b/src/ast/AstNodes.h @@ -793,6 +793,8 @@ class TernaryExprNode : public AstNode { // Public get methods [[nodiscard]] std::vector operands() const { return getChildren(); } + + bool isShortened = false; }; // ===================================================== LogicalOrExprNode ======================================================= diff --git a/src/generator/GeneratorVisitor.cpp b/src/generator/GeneratorVisitor.cpp index 7c8d68753..209352c09 100644 --- a/src/generator/GeneratorVisitor.cpp +++ b/src/generator/GeneratorVisitor.cpp @@ -1696,8 +1696,15 @@ std::any GeneratorVisitor::visitTernaryExpr(TernaryExprNode *node) { if (node->operands().size() > 1) { llvm::Value *conditionPtr = resolveAddress(node->operands()[0]); - llvm::Value *trueValuePtr = resolveAddress(node->operands()[1]); - llvm::Value *falseValuePtr = resolveAddress(node->operands()[2]); + llvm::Value *trueValuePtr; + llvm::Value *falseValuePtr; + if (node->isShortened) { + trueValuePtr = conditionPtr; + falseValuePtr = resolveAddress(node->operands()[1]); + } else { + trueValuePtr = resolveAddress(node->operands()[1]); + falseValuePtr = resolveAddress(node->operands()[2]); + } llvm::Type *conditionType = node->operands().front()->getEvaluatedSymbolType().toLLVMType(*context, currentScope); llvm::Value *condition = builder->CreateLoad(conditionType, conditionPtr); diff --git a/src/parser/AstBuilderVisitor.cpp b/src/parser/AstBuilderVisitor.cpp index 590195488..ca29f617c 100644 --- a/src/parser/AstBuilderVisitor.cpp +++ b/src/parser/AstBuilderVisitor.cpp @@ -845,6 +845,9 @@ std::any AstBuilderVisitor::visitAssignExpr(SpiceParser::AssignExprContext *ctx) std::any AstBuilderVisitor::visitTernaryExpr(SpiceParser::TernaryExprContext *ctx) { auto ternaryExprNode = dynamic_cast(currentNode); + // Check if is shortened + ternaryExprNode->isShortened = ctx->logicalOrExpr().size() == 2; + for (const auto &subTree : ctx->children) { antlr4::ParserRuleContext *rule; if (rule = dynamic_cast(subTree); rule != nullptr) // LogicalOrExpr diff --git a/test/test-files/generator/ternary/success-shortened-ternary/cout.out b/test/test-files/generator/ternary/success-shortened-ternary/cout.out new file mode 100644 index 000000000..86a1abe1d --- /dev/null +++ b/test/test-files/generator/ternary/success-shortened-ternary/cout.out @@ -0,0 +1,3 @@ +F1 called. +F2 called. +Result: 1 \ No newline at end of file diff --git a/test/test-files/generator/ternary/success-shortened-ternary/ir-code.ll b/test/test-files/generator/ternary/success-shortened-ternary/ir-code.ll new file mode 100644 index 000000000..4ba0927fa --- /dev/null +++ b/test/test-files/generator/ternary/success-shortened-ternary/ir-code.ll @@ -0,0 +1,49 @@ +; ModuleID = 'source.spice' +source_filename = "source.spice" +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-w64-windows-gnu" + +@0 = private unnamed_addr constant [12 x i8] c"F1 called.\0A\00", align 1 +@1 = private unnamed_addr constant [12 x i8] c"F2 called.\0A\00", align 1 +@2 = private unnamed_addr constant [11 x i8] c"Result: %d\00", align 1 + +define internal i1 @_f__void__f1() { +entry.l1: + %result = alloca i1, align 1 + %0 = call i32 (ptr, ...) @printf(ptr @0) + %1 = alloca i1, align 1 + store i1 false, ptr %1, align 1 + %2 = load i1, ptr %1, align 1 + ret i1 %2 +} + +declare i32 @printf(ptr, ...) + +define internal i1 @_f__void__f2() { +entry.l6: + %result = alloca i1, align 1 + %0 = call i32 (ptr, ...) @printf(ptr @1) + %1 = alloca i1, align 1 + store i1 true, ptr %1, align 1 + %2 = load i1, ptr %1, align 1 + ret i1 %2 +} + +define i32 @main() { +entry.l11: + %result = alloca i32, align 4 + store i32 0, ptr %result, align 4 + %0 = call i1 @_f__void__f1() + %1 = alloca i1, align 1 + store i1 %0, ptr %1, align 1 + %2 = call i1 @_f__void__f2() + %3 = alloca i1, align 1 + store i1 %2, ptr %3, align 1 + %4 = load i1, ptr %1, align 1 + %5 = select i1 %4, ptr %1, ptr %3 + %6 = load i1, ptr %5, align 1 + %7 = zext i1 %6 to i32 + %8 = call i32 (ptr, ...) @printf(ptr @2, i32 %7) + %9 = load i32, ptr %result, align 4 + ret i32 %9 +} diff --git a/test/test-files/generator/ternary/success-shortened-ternary/source.spice b/test/test-files/generator/ternary/success-shortened-ternary/source.spice new file mode 100644 index 000000000..d1507a1d7 --- /dev/null +++ b/test/test-files/generator/ternary/success-shortened-ternary/source.spice @@ -0,0 +1,13 @@ +f f1() { + printf("F1 called.\n"); + return false; +} + +f f2() { + printf("F2 called.\n"); + return true; +} + +f main() { + printf("Result: %d", f1() ?: f2()); +} \ No newline at end of file