started work on reducing compile time known values
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 10 Aug 2020 19:45:33 +0000 (14:45 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 10 Aug 2020 19:45:33 +0000 (14:45 -0500)
Makefile
docs/plan
include/onyxastnodes.h
include/onyxtypes.h
include/onyxwasm.h
onyx
progs/stack_based.onyx
src/onyxchecker.c
src/onyxsymres.c
src/onyxtypes.c
src/onyxutils.c

index 0e514db6108e81017afbacf2eac82318593c4738..6a575b282e20f57fc1e2e7133d374497dbadb4ff 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-RELEASE=1
+RELEASE=0
 
 OBJ_FILES=\
        build/onyxlex.o \
index 838906d84761e81874f9709ed895e59bf5267340..f025768a3aa48cd6c44c6ffeeae47bec93111daf 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -172,7 +172,7 @@ HOW:
             - Checking which things are allowed to cast to/from should be checked in the checker,
                 not in the wasm generatation
 
-        [ ] Start work on evaluating compile time known values.
+        [X] Start work on evaluating compile time known values.
             - An expression marked COMPTIME will be reduced to its value in the parse tree.
 
         [ ] All code paths return correct value
index 6d242e734f8513974bcf95c7790dde369a3586fe..41ffdc94372ccfbbea3fb997ac44f7b61b322193 100644 (file)
@@ -493,6 +493,7 @@ extern const BuiltinSymbol builtin_symbols[];
 
 
 // NOTE: Useful not inlined functions
+AstTyped* ast_reduce(bh_allocator a, AstTyped* node);
 void promote_numlit_to_larger(AstNumLit* num);
 
 // NOTE: Useful inlined functions
index 7d17297cb7103addf55c12081cc89e4df80c9485..ce7e9d0cd87a6a9035f52fb475724005f49ee383 100644 (file)
@@ -123,6 +123,7 @@ b32 type_is_pointer(Type* type);
 b32 type_is_array(Type* tyoe);
 b32 type_is_struct(Type* type);
 b32 type_is_bool(Type* type);
+b32 type_is_small_integer(Type* type);
 b32 type_is_integer(Type* type);
 b32 type_is_numeric(Type* type);
 b32 type_is_compound(Type* type);
index 3ee2c499154e43db572cc76b6001d6b89d057668..911b63a7f0aea73bee186984b115ff9b729c82b0 100644 (file)
@@ -230,7 +230,7 @@ typedef enum WasmInstructionType {
 
 typedef union {
     struct {
-        u32 i1, i2;
+        i32 i1, i2;
     };
     i64 l;
     float f;
diff --git a/onyx b/onyx
index 144ca80f6ff59a5c1a8701aa17d71c0bb1957dc2..1b601253fb755f74440e77cbe1f04c63ccb92241 100755 (executable)
Binary files a/onyx and b/onyx differ
index 5483f7c90af96e70985f3c50828fd37cb1e7c621..bae444199833f0c3b37210c010f6d1bf5af6db55 100644 (file)
@@ -44,7 +44,7 @@ vec_add :: proc (v: Vec3, u: Vec3, use out: ^Vec3) {
     z = v.z + u.z;
 }
 
-some_value := #char "A";
+some_value := 20 + 30 * 4 + 15 / 5;
 
 start :: proc #export {
     heap_init();
@@ -57,6 +57,8 @@ start :: proc #export {
     print_bin(42l);
     print_hex(42l);
 
+    print(cast(u32) (#char "a" + #char "0"));
+
     for i: #char "a", #char "f" do print_hex(cast(u64) i);
 
     a := 12345;
index 1a32875cce1a2895987cb7b195e30cd43b4a76b5..973f8cb8c70a6fc60eb78202af8121b36726ebe0 100644 (file)
@@ -13,7 +13,8 @@ CHECK(if, AstIf* ifnode);
 CHECK(while, AstWhile* whilenode);
 CHECK(for, AstFor* fornode);
 CHECK(call, AstCall* call);
-CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok);
+CHECK(binaryop, AstBinaryOp** pbinop, b32 assignment_is_ok);
+CHECK(unaryop, AstUnaryOp** punop);
 CHECK(expression, AstTyped** expr);
 CHECK(address_of, AstAddressOf* aof);
 CHECK(dereference, AstDereference* deref);
@@ -426,7 +427,7 @@ CHECK(binop_assignment, AstBinaryOp* binop, b32 assignment_is_ok) {
         binop->right = (AstTyped *) binop_node;
         binop->operation = Binary_Op_Assign;
 
-        if (check_binaryop(binop_node, 0)) return 1;
+        if (check_binaryop(&binop_node, 0)) return 1;
     }
 
     if (!types_are_compatible(binop->left->type, binop->right->type)) {
@@ -442,7 +443,9 @@ CHECK(binop_assignment, AstBinaryOp* binop, b32 assignment_is_ok) {
     return 0;
 }
 
-CHECK(binaryop_compare, AstBinaryOp* binop) {
+CHECK(binaryop_compare, AstBinaryOp** pbinop) {
+    AstBinaryOp* binop = *pbinop;
+
     if (binop->left->type == NULL) {
         onyx_message_add(Msg_Type_Unresolved_Type,
                 binop->token->pos,
@@ -469,7 +472,9 @@ CHECK(binaryop_compare, AstBinaryOp* binop) {
     return 0;
 }
 
-CHECK(binaryop_bool, AstBinaryOp* binop) {
+CHECK(binaryop_bool, AstBinaryOp** pbinop) {
+    AstBinaryOp* binop = *pbinop;
+
     if (binop->left->type == NULL) {
         onyx_message_add(Msg_Type_Unresolved_Type,
                 binop->token->pos,
@@ -495,15 +500,21 @@ CHECK(binaryop_bool, AstBinaryOp* binop) {
     return 0;
 }
 
-CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) {
+CHECK(binaryop, AstBinaryOp** pbinop, b32 assignment_is_ok) {
+    AstBinaryOp* binop = *pbinop;
+
     if (check_expression(&binop->left)) return 1;
     if (check_expression(&binop->right)) return 1;
 
+    if ((binop->left->flags & Ast_Flag_Comptime) && (binop->right->flags & Ast_Flag_Comptime)) {
+        binop->flags |= Ast_Flag_Comptime;
+    }
+
     if (binop_is_assignment(binop)) return check_binop_assignment(binop, assignment_is_ok);
-    if (binop_is_compare(binop))    return check_binaryop_compare(binop);
+    if (binop_is_compare(binop))    return check_binaryop_compare(pbinop);
     if (binop->operation == Binary_Op_Bool_And
         || binop->operation == Binary_Op_Bool_Or)
-        return check_binaryop_bool(binop);
+        return check_binaryop_bool(pbinop);
 
     if (binop->left->type == NULL) {
         onyx_message_add(Msg_Type_Unresolved_Type,
@@ -585,7 +596,7 @@ CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) {
         binop_node->type  = binop->right->type;
         binop_node->operation = Binary_Op_Multiply;
 
-        if (check_binaryop(binop_node, 0)) return 1;
+        if (check_binaryop(&binop_node, 0)) return 1;
 
         binop->right = (AstTyped *) binop_node;
         binop->type = binop->left->type;
@@ -601,6 +612,30 @@ CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) {
     }
 
     binop->type = binop->left->type;
+
+    if (binop->flags & Ast_Flag_Comptime) {
+        // NOTE: Not a binary op
+        *pbinop = (AstBinaryOp *) ast_reduce(semstate.node_allocator, (AstTyped *) binop);
+    }
+
+    return 0;
+}
+
+CHECK(unaryop, AstUnaryOp** punop) {
+    AstUnaryOp* unaryop = *punop;
+
+    if (check_expression(&unaryop->expr)) return 1;
+
+    if (unaryop->operation != Unary_Op_Cast) {
+        unaryop->type = unaryop->expr->type;
+    }
+
+    if (unaryop->expr->flags & Ast_Flag_Comptime) {
+        unaryop->flags |= Ast_Flag_Comptime;
+        // NOTE: Not a unary op
+        *punop = (AstUnaryOp *) ast_reduce(semstate.node_allocator, (AstTyped *) unaryop);
+    }
+
     return 0;
 }
 
@@ -748,15 +783,8 @@ CHECK(expression, AstTyped** pexpr) {
 
     i32 retval = 0;
     switch (expr->kind) {
-        case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp *) expr, 0); break;
-
-        case Ast_Kind_Unary_Op:
-            retval = check_expression(&((AstUnaryOp *) expr)->expr);
-
-            if (((AstUnaryOp *) expr)->operation != Unary_Op_Cast) {
-                expr->type = ((AstUnaryOp *) expr)->expr->type;
-            }
-            break;
+        case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp **) pexpr, 0); break;
+        case Ast_Kind_Unary_Op:  retval = check_unaryop((AstUnaryOp **) pexpr); break;
 
         case Ast_Kind_Call:  retval = check_call((AstCall *) expr); break;
         case Ast_Kind_Block: retval = check_block((AstBlock *) expr); break;
@@ -868,7 +896,7 @@ CHECK(statement, AstNode* stmt) {
 
         case Ast_Kind_Binary_Op:
             stmt->flags |= Ast_Flag_Expr_Ignored;
-            return check_binaryop((AstBinaryOp *stmt, 1);
+            return check_binaryop((AstBinaryOp **) &stmt, 1);
 
         default:
             stmt->flags |= Ast_Flag_Expr_Ignored;
@@ -1077,6 +1105,14 @@ CHECK(memres, AstMemRes* memres) {
 
     if (memres->initial_value != NULL) {
         fill_in_type(memres->initial_value);
+        check_expression(&memres->initial_value);
+
+        if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
+            onyx_message_add(Msg_Type_Literal,
+                    memres->initial_value->token->pos,
+                    "top level expressions must be compile time known");
+            return 1;
+        }
 
         Type* memres_type = memres->type;
         if (!type_is_compound(memres_type)) memres_type = memres_type->Pointer.elem;
@@ -1102,7 +1138,7 @@ CHECK(node, AstNode* node) {
         case Ast_Kind_If:                   return check_if((AstIf *) node);
         case Ast_Kind_While:                return check_while((AstWhile *) node);
         case Ast_Kind_Call:                 return check_call((AstCall *) node);
-        case Ast_Kind_Binary_Op:            return check_binaryop((AstBinaryOp *node, 1);
+        case Ast_Kind_Binary_Op:            return check_binaryop((AstBinaryOp **) &node, 1);
         default:                            return check_expression((AstTyped **) &node);
     }
 }
index 8a095b04400ffed6ecf0328517168749444fa80f..f5c3e4bd6f7b2ba927b4ac5adefaab87cabf8fa6 100644 (file)
@@ -228,6 +228,9 @@ static void symres_unaryop(AstUnaryOp** unaryop) {
     }
 
     symres_expression(&(*unaryop)->expr);
+
+    if ((*unaryop)->type_node == NULL)
+        (*unaryop)->type_node = ((AstUnaryOp *)(*unaryop))->expr->type_node;
 }
 
 static void symres_expression(AstTyped** expr) {
@@ -239,6 +242,8 @@ static void symres_expression(AstTyped** expr) {
         case Ast_Kind_Binary_Op:
             symres_expression(&((AstBinaryOp *)(*expr))->left);
             symres_expression(&((AstBinaryOp *)(*expr))->right);
+
+            (*expr)->type_node = ((AstBinaryOp *)(*expr))->left->type_node;
             break;
 
         case Ast_Kind_Unary_Op:     symres_unaryop((AstUnaryOp **) expr); break;
@@ -498,13 +503,6 @@ static void symres_memres(AstMemRes** memres) {
     if ((*memres)->initial_value != NULL) {
         symres_expression(&(*memres)->initial_value);
 
-        if (((*memres)->initial_value->flags & Ast_Flag_Comptime) == 0) {
-            onyx_message_add(Msg_Type_Literal,
-                    (*memres)->initial_value->token->pos,
-                    "top level expressions must be compile time known");
-            return;
-        }
-
         if ((*memres)->type_node == NULL)
             (*memres)->type_node = (*memres)->initial_value->type_node;
 
index b61e05fd4b698d711283a155fbb9350327454253..8134b4be7c4c28f7f0adb5ce694eeb496bd24498 100644 (file)
@@ -471,6 +471,12 @@ b32 type_is_bool(Type* type) {
     return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool;
 }
 
+b32 type_is_small_integer(Type* type) {
+    if (type->kind != Type_Kind_Basic) return 0;
+
+    return type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U32;
+}
+
 b32 type_is_integer(Type* type) {
     if (type->kind != Type_Kind_Basic) return 0;
 
index 4a1c20feb9deee517dfd3c2bb92894b22caad6c3..72dde5fdba65d04243b41acb2a1621c2bfb55d18 100644 (file)
@@ -2,6 +2,7 @@
 #include "onyxlex.h"
 #include "onyxastnodes.h"
 #include "onyxmsgs.h"
+#include "onyxparser.h"
 
 bh_scratch global_scratch;
 bh_allocator global_scratch_allocator;
@@ -179,9 +180,129 @@ AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) {
     return res;
 }
 
+#define REDUCE_BINOP_ALL(op) \
+    if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
+        res->value.i = left->value.i op right->value.i; \
+    } else if (type_is_integer(res->type)) { \
+        res->value.l = left->value.l op right->value.l; \
+    } else if (res->type->Basic.kind == Basic_Kind_F32) { \
+        res->value.f = left->value.f op right->value.f; \
+    } else if (res->type->Basic.kind == Basic_Kind_F64) { \
+        res->value.d = left->value.d op right->value.d; \
+    } \
+    break;
+
+#define REDUCE_BINOP_INT(op) \
+    if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
+        res->value.i = left->value.i op right->value.i; \
+    } else if (type_is_integer(res->type)) { \
+        res->value.l = left->value.l op right->value.l; \
+    } \
+    break;
+
+#define REDUCE_BINOP_BOOL(op) \
+    if (type_is_bool(res->type)) { \
+        res->value.i = left->value.i op right->value.i; \
+    } \
+    break;
+
+
+AstTyped* ast_reduce(bh_allocator a, AstTyped* node);
+
+AstNumLit* ast_reduce_binop(bh_allocator a, AstBinaryOp* node) {
+    AstNumLit* left =  (AstNumLit *) ast_reduce(a, node->left);
+    AstNumLit* right = (AstNumLit *) ast_reduce(a, node->right);
+
+    if (left->kind != Ast_Kind_NumLit || right->kind != Ast_Kind_NumLit) {
+        node->left  = (AstTyped *) left;
+        node->right = (AstTyped *) right;
+        return (AstNumLit *) node;
+    }
+
+    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    res->token = node->token;
+    res->flags |= Ast_Flag_Comptime;
+    res->type_node = node->type_node;
+    res->type = node->type;
+
+    switch (node->operation) {
+    case Binary_Op_Add:           REDUCE_BINOP_ALL(+);
+    case Binary_Op_Minus:         REDUCE_BINOP_ALL(-);
+    case Binary_Op_Multiply:      REDUCE_BINOP_ALL(*);
+    case Binary_Op_Divide:        REDUCE_BINOP_ALL(/);
+    case Binary_Op_Modulus:       REDUCE_BINOP_INT(%);
+
+    case Binary_Op_Equal:         REDUCE_BINOP_ALL(==);
+    case Binary_Op_Not_Equal:     REDUCE_BINOP_ALL(!=);
+    case Binary_Op_Less:          REDUCE_BINOP_ALL(<);
+    case Binary_Op_Less_Equal:    REDUCE_BINOP_ALL(<=);
+    case Binary_Op_Greater:       REDUCE_BINOP_ALL(>);
+    case Binary_Op_Greater_Equal: REDUCE_BINOP_ALL(>=);
+
+    case Binary_Op_And:           REDUCE_BINOP_INT(&);
+    case Binary_Op_Or:            REDUCE_BINOP_INT(|);
+    case Binary_Op_Xor:           REDUCE_BINOP_INT(^);
+    case Binary_Op_Shl:           REDUCE_BINOP_INT(<<);
+    case Binary_Op_Shr:           REDUCE_BINOP_INT(>>);
+    case Binary_Op_Sar:           REDUCE_BINOP_INT(>>);
+
+    case Binary_Op_Bool_And:      REDUCE_BINOP_BOOL(&&);
+    case Binary_Op_Bool_Or:       REDUCE_BINOP_BOOL(||);
+
+    default: break;
+    }
+
+    return res;
+}
 
+#define REDUCE_UNOP(op) \
+    if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
+        res->value.i = op ((AstNumLit *) unop->expr)->value.i; \
+    } else if (type_is_integer(unop->type)) { \
+        res->value.l = op ((AstNumLit *) unop->expr)->value.l; \
+    } else if (unop->type->Basic.kind == Basic_Kind_F32) { \
+        res->value.f = op ((AstNumLit *) unop->expr)->value.f; \
+    } else if (unop->type->Basic.kind == Basic_Kind_F64) { \
+        res->value.d = op ((AstNumLit *) unop->expr)->value.d; \
+    } \
+    break;
+
+AstTyped* ast_reduce_unaryop(bh_allocator a, AstUnaryOp* unop) {
+    unop->expr = ast_reduce(a, unop->expr);
+
+    if (unop->expr->kind != Ast_Kind_NumLit) {
+        return (AstTyped *) unop;
+    }
 
+    AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+    res->token = unop->token;
+    res->flags |= Ast_Flag_Comptime;
+    res->type_node = unop->type_node;
+    res->type = unop->type;
+
+    switch (unop->operation) {
+        case Unary_Op_Negate: REDUCE_UNOP(-);
+        case Unary_Op_Not: {
+            if (type_is_bool(res->type)) res->value.i = ! ((AstNumLit *) unop->expr)->value.i;
+            break;
+        }
 
+        default: return (AstTyped *) unop;
+    }
+
+    return (AstTyped *) res;
+}
+
+AstTyped* ast_reduce(bh_allocator a, AstTyped* node) {
+    assert(node->flags & Ast_Flag_Comptime);
+
+    switch (node->kind) {
+        case Ast_Kind_Binary_Op: return (AstTyped *) ast_reduce_binop(a, (AstBinaryOp *) node);
+        case Ast_Kind_Unary_Op:  return (AstTyped *) ast_reduce_unaryop(a, (AstUnaryOp *) node);
+        case Ast_Kind_NumLit:    return node;
+        default:                 return NULL; 
+    }
+}
 
 void promote_numlit_to_larger(AstNumLit* num) {
     assert(num->type != NULL);