added if-expressions
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 6 Jul 2021 22:10:58 +0000 (17:10 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 6 Jul 2021 22:10:58 +0000 (17:10 -0500)
bin/onyx
core/math.onyx
include/onyxastnodes.h
src/onyxastnodes.c
src/onyxchecker.c
src/onyxclone.c
src/onyxparser.c
src/onyxsymres.c
src/onyxwasm.c
tests/if_expressions [new file with mode: 0644]
tests/if_expressions.onyx [new file with mode: 0644]

index 4248b85a435d768c33cdd750450cbac28a68f8c7..c27bdd35b9dacfe02d1b45c98de4ace21751c8c4 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 11e6610e0246f767de90e153cf873f16f3956c6d..7030b08643a94fc0abb8a4a4fc8e4c8f7c89686b 100644 (file)
@@ -232,14 +232,12 @@ log :: (a: $T, base: $R) -> T {
 // as values.
 max :: #match { wasm.max_f32, wasm.max_f64, max_poly }
 max_poly :: (a: $T, b: T) -> T {
-    if a >= b do return a;
-    else      do return b;
+    return a if a >= b else b;
 }
 
 min :: #match { wasm.min_f32, wasm.min_f64, min_poly }
 min_poly :: (a: $T, b: T) -> T {
-    if a <= b do return a;
-    else      do return b;
+    return a if a <= b else b;
 }
 
 clamp :: (v: $T, lo: T, hi: T) -> T {
@@ -255,8 +253,7 @@ sqrt_poly :: (x: $T) -> T {
 
 abs :: #match { wasm.abs_f32, wasm.abs_f64, abs_poly }
 abs_poly :: (x: $T) -> T {
-    if x >= 0 do return x;
-    else      do return -x;
+    return x if x >= 0 else -x;
 }
 
 sign :: (x: $T) -> T {
index 0b0d006768d558db31604aef3dc1af4d8b604191..2dd0327c75f8dd2ffd2939bf624e901ea675c318 100644 (file)
@@ -28,6 +28,7 @@
     NODE(ArrayLiteral)         \
     NODE(RangeLiteral)         \
     NODE(Compound)             \
+    NODE(IfExpression)         \
                                \
     NODE(DirectiveSolidify)    \
     NODE(DirectiveError)       \
@@ -165,6 +166,7 @@ typedef enum AstKind {
     Ast_Kind_File_Contents,
     Ast_Kind_Struct_Literal,
     Ast_Kind_Array_Literal,
+    Ast_Kind_If_Expression,
 
     Ast_Kind_If,
     Ast_Kind_For,
@@ -594,6 +596,13 @@ struct AstCompound {
 
     bh_arr(AstTyped *) exprs;
 };
+struct AstIfExpression {
+    AstTyped_base;
+
+    AstTyped* cond;
+    AstTyped* true_expr;
+    AstTyped* false_expr;
+};
 
 struct AstDirectiveSolidify {
     AstTyped_base;
index 0571e989339e755168f9cef070cea74c6cae523e..09168c19b32dd6473a75588e36c5ca0ab4a5fa4f 100644 (file)
@@ -67,6 +67,7 @@ static const char* ast_node_names[] = {
     "FILE CONTENTS",
     "STRUCT LITERAL",
     "ARRAY LITERAL",
+    "IF EXPRESSION",
 
     "IF",
     "FOR",
@@ -515,6 +516,20 @@ b32 type_check_or_auto_cast(AstTyped** pnode, Type* type) {
         
         return 1;
     }
+    else if (node->kind == Ast_Kind_If_Expression) {
+        AstIfExpression* if_expr = (AstIfExpression *) node;
+
+        b32 true_success  = type_check_or_auto_cast(&if_expr->true_expr,  type);
+        b32 false_success = type_check_or_auto_cast(&if_expr->false_expr, type);
+
+        if (true_success && false_success) {
+            if_expr->type = type;
+            return 1;
+
+        } else {
+            return 0;
+        }
+    }
 
     return 0;
 }
@@ -532,6 +547,15 @@ Type* resolve_expression_type(AstTyped* node) {
         node->type = resolve_expression_type(((AstArgument *) node)->value);
     }
 
+    if (node->kind == Ast_Kind_If_Expression) {
+        AstIfExpression* if_expr = (AstIfExpression *) node;
+
+        Type* ltype = resolve_expression_type(if_expr->true_expr);
+        type_check_or_auto_cast(&if_expr->false_expr, ltype);
+
+        if_expr->type = ltype;
+    }
+
     if (node_is_type((AstNode *) node)) {
         return &basic_types[Basic_Kind_Type_Index];
     }
index b7c13d2f60ac92709c046d41d90fb16fb1fa50a0..4c2fbe451471a8f244c6d3fd36e3a8ddf905802b 100644 (file)
@@ -35,6 +35,7 @@ CheckStatus check_struct_literal(AstStructLiteral* sl);
 CheckStatus check_array_literal(AstArrayLiteral* al);
 CheckStatus check_range_literal(AstRangeLiteral** range);
 CheckStatus check_compound(AstCompound* compound);
+CheckStatus check_if_expression(AstIfExpression* if_expr);
 CheckStatus check_expression(AstTyped** expr);
 CheckStatus check_address_of(AstAddressOf* aof);
 CheckStatus check_dereference(AstDereference* deref);
@@ -1276,6 +1277,28 @@ CheckStatus check_compound(AstCompound* compound) {
     return Check_Success;
 }
 
+CheckStatus check_if_expression(AstIfExpression* if_expr) {
+    CHECK(expression, &if_expr->cond);
+    CHECK(expression, &if_expr->true_expr);
+    CHECK(expression, &if_expr->false_expr);
+
+    if (!type_check_or_auto_cast(&if_expr->cond, &basic_types[Basic_Kind_Bool])) {
+        onyx_report_error(if_expr->token->pos, "If-expression expected boolean for condition, got '%s'.",
+            type_get_name(if_expr->cond->type));
+        return Check_Error;
+    }
+
+    resolve_expression_type((AstTyped *) if_expr);
+
+    if (!types_are_compatible(if_expr->true_expr->type, if_expr->false_expr->type)) {
+        onyx_report_error(if_expr->token->pos, "Mismatched types for if-expression, left side is '%s', and right side is '%s'.",
+            type_get_name(if_expr->true_expr->type), type_get_name(if_expr->false_expr->type));
+        return Check_Error;
+    }
+
+    return Check_Success;
+}
+
 CheckStatus check_address_of(AstAddressOf* aof) {
     CHECK(expression, &aof->expr);
     if (aof->expr->type == NULL) {
@@ -1625,6 +1648,10 @@ CheckStatus check_expression(AstTyped** pexpr) {
             expr->type_node = builtin_callsite_type;
             break;
 
+        case Ast_Kind_If_Expression:
+            CHECK(if_expression, (AstIfExpression *) expr);
+            break;
+
         case Ast_Kind_StrLit: break;
         case Ast_Kind_File_Contents: break;
         case Ast_Kind_Overloaded_Function: break;
index 6fbda8437a6a9f9486308c33df46ae1b425d9a8c..2d4145d4a3cc2dda55d574e07cbe68057ce3732b 100644 (file)
@@ -92,6 +92,7 @@ static inline i32 ast_kind_to_size(AstNode* node) {
         case Ast_Kind_Named_Value: return sizeof(AstNamedValue);
         case Ast_Kind_Call_Site: return sizeof(AstCallSite);
         case Ast_Kind_Static_If: return sizeof(AstIfWhile);
+        case Ast_Kind_If_Expression: return sizeof(AstIfExpression);
         case Ast_Kind_Count: return 0;
        }
 
@@ -425,6 +426,13 @@ AstNode* ast_clone(bh_allocator a, void* n) {
             ((AstNamedValue *) nn)->value = (AstTyped *) ast_clone(a, ((AstNamedValue *) node)->value);
             break;
         }
+
+        case Ast_Kind_If_Expression: {
+            ((AstIfExpression *) nn)->cond = (AstTyped *) ast_clone(a, ((AstIfExpression *) node)->cond);
+            ((AstIfExpression *) nn)->true_expr = (AstTyped *) ast_clone(a, ((AstIfExpression *) node)->true_expr);
+            ((AstIfExpression *) nn)->false_expr = (AstTyped *) ast_clone(a, ((AstIfExpression *) node)->false_expr);
+            break;
+        }
        }
 
        return nn;
index e80c64b48f175ef4f87f609c3064a4b0d786c9e2..4dfee9d5c4c07158e07f2a2e0504075c9c031e9c 100644 (file)
@@ -682,6 +682,21 @@ static AstTyped* parse_factor(OnyxParser* parser) {
                 break;
             }
 
+            case Token_Type_Keyword_If: {
+                AstIfExpression* if_expression = make_node(AstIfExpression, Ast_Kind_If_Expression);
+                if_expression->token = expect_token(parser, Token_Type_Keyword_If);
+
+                if_expression->true_expr  = retval;
+                if_expression->cond       = parse_expression(parser, 0);
+                expect_token(parser, Token_Type_Keyword_Else);
+                if_expression->false_expr = parse_expression(parser, 0);
+
+                retval = (AstTyped *) if_expression;
+
+                // nocheckin This should maybe be goto factor_parsed; ??
+                break;
+            }
+
             default: goto factor_parsed;
         }
     }
index 0c416376caced7261235078ea2d8355314c2af48..410444d57aecd21a8a5195a26ab9056a09f63ef8 100644 (file)
@@ -297,6 +297,13 @@ static SymresStatus symres_compound(AstCompound* compound) {
     return Symres_Success;
 }
 
+static SymresStatus symres_if_expression(AstIfExpression* if_expr) {
+    SYMRES(expression, &if_expr->cond);
+    SYMRES(expression, &if_expr->true_expr);
+    SYMRES(expression, &if_expr->false_expr);
+    return Symres_Success;
+}
+
 static SymresStatus symres_pipe(AstBinaryOp** pipe) {
     AstCall* call_node = (AstCall *) (*pipe)->right;
     SYMRES(expression, (AstTyped **) &call_node);
@@ -462,6 +469,10 @@ static SymresStatus symres_expression(AstTyped** expr) {
             SYMRES(package, (AstPackage *) *expr);
             break;
 
+        case Ast_Kind_If_Expression:
+            SYMRES(if_expression, (AstIfExpression *) *expr);
+            break;
+
         default: break;
     }
 
index c0777af93e6a9058d21557448814cc7eb1bd9611..7393bd326ca23739170e8973fdd640893c440178 100644 (file)
@@ -254,6 +254,7 @@ EMIT_FUNC(compound_store,                Type* type, u64 offset, b32 location_fi
 EMIT_FUNC(array_store,                   Type* type, u32 offset);
 EMIT_FUNC(array_literal,                 AstArrayLiteral* al);
 EMIT_FUNC(range_literal,                 AstRangeLiteral* range);
+EMIT_FUNC(if_expression,                 AstIfExpression* if_expr);
 EMIT_FUNC(expression,                    AstTyped* expr);
 EMIT_FUNC(cast,                          AstUnaryOp* cast);
 EMIT_FUNC(return,                        AstReturn* ret);
@@ -2238,6 +2239,45 @@ EMIT_FUNC(range_literal, AstRangeLiteral* range) {
     *pcode = code;
 }
 
+EMIT_FUNC(if_expression, AstIfExpression* if_expr) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 offset = 0;
+    u64 result_local    = local_allocate(mod->local_alloc, (AstTyped *) if_expr);
+    b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
+    bh_imap_put(&mod->local_map, (u64) if_expr, result_local);
+
+    emit_expression(mod, &code, if_expr->cond); 
+
+    emit_enter_structured_block(mod, &code, SBT_Basic_If);
+        if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+
+        emit_expression(mod, &code, if_expr->true_expr);
+
+        if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
+        else                  WIL(WI_LOCAL_SET, result_local);
+
+    WI(WI_ELSE);
+        if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+
+        emit_expression(mod, &code, if_expr->false_expr);
+
+        if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
+        else                  WIL(WI_LOCAL_SET, result_local);
+
+    emit_leave_structured_block(mod, &code);
+
+    if (!result_is_local) {
+        emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+        emit_load_instruction(mod, &code, if_expr->type, offset);
+        
+    } else {
+        WIL(WI_LOCAL_GET, result_local);
+    }
+
+    *pcode = code;
+}
+
 EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return) {
     bh_arr(WasmInstruction) code = *pcode;
 
@@ -2590,6 +2630,12 @@ EMIT_FUNC(expression, AstTyped* expr) {
             break;
         }
 
+        case Ast_Kind_If_Expression: {
+            AstIfExpression* if_expr = (AstIfExpression *) expr;
+            emit_if_expression(mod, &code, if_expr);
+            break;
+        }
+
         default:
             bh_printf("Unhandled case: %d\n", expr->kind);
             DEBUG_HERE;
diff --git a/tests/if_expressions b/tests/if_expressions
new file mode 100644 (file)
index 0000000..462d775
--- /dev/null
@@ -0,0 +1,3 @@
+5678.0000
+True
+20
diff --git a/tests/if_expressions.onyx b/tests/if_expressions.onyx
new file mode 100644 (file)
index 0000000..3388df8
--- /dev/null
@@ -0,0 +1,25 @@
+#load "core/std"
+
+use package core
+
+some_function :: () -> f32 {
+    println("In some function!");
+    return 1234;
+}
+
+new_max :: (a: $T, b: T) -> T {
+    // Much much better!
+    return a if a > b else b;
+}
+
+main :: (args: [] cstr) {
+
+    condition := 12 > 34;
+
+    x := some_function() if condition else 5678.0f;
+    println(x);
+
+    println("True" if x > 5000 else "False" if false else "REALLY FALSE");
+
+    println(new_max(10, 20));
+}
\ No newline at end of file