Commit e5d1f0eea5

Andrew Kelley <superjoe30@gmail.com>
2015-11-28 08:40:54
parse and codegen for math expressions
1 parent f652934
doc/vim/syntax/zig.vim
@@ -7,7 +7,7 @@ if exists("b:current_syntax")
   finish
 endif
 
-syn keyword zigKeyword fn return mut const extern unreachable export pub
+syn keyword zigKeyword fn return mut const extern unreachable export pub as
 syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
 
 syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
src/codegen.cpp
@@ -77,6 +77,7 @@ struct CodeGen {
     ZigList<FnTableEntry *> fn_defs;
     Buf *out_name;
     OutType out_type;
+    LLVMValueRef cur_fn;
 };
 
 struct TypeNode {
@@ -301,12 +302,23 @@ static void find_declarations(CodeGen *g, AstNode *node) {
             // we handled directives in the parent function
             break;
         case NodeTypeFnDecl:
-        case NodeTypeStatementReturn:
+        case NodeTypeReturnExpr:
         case NodeTypeRoot:
         case NodeTypeBlock:
-        case NodeTypeExpression:
+        case NodeTypeBoolOrExpr:
         case NodeTypeFnCall:
         case NodeTypeRootExportDecl:
+        case NodeTypeBoolAndExpr:
+        case NodeTypeComparisonExpr:
+        case NodeTypeBinOrExpr:
+        case NodeTypeBinXorExpr:
+        case NodeTypeBinAndExpr:
+        case NodeTypeBitShiftExpr:
+        case NodeTypeAddExpr:
+        case NodeTypeMultExpr:
+        case NodeTypeCastExpr:
+        case NodeTypePrimaryExpr:
+        case NodeTypeGroupedExpr:
             zig_unreachable();
     }
 }
@@ -341,7 +353,7 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
     bool prev_statement_return = false;
     for (int i = 0; i < body_node->data.block.statements.length; i += 1) {
         AstNode *statement_node = body_node->data.block.statements.at(i);
-        if (statement_node->type == NodeTypeStatementReturn) {
+        if (statement_node->type == NodeTypeReturnExpr) {
             if (type_id == TypeIdUnreachable) {
                 add_node_error(g, statement_node,
                         buf_sprintf("return statement in function with unreachable return type"));
@@ -464,23 +476,15 @@ static void analyze_node(CodeGen *g, AstNode *node) {
                 analyze_node(g, child);
             }
             break;
-        case NodeTypeStatementReturn:
-            if (node->data.statement_return.expression) {
-                analyze_node(g, node->data.statement_return.expression);
+        case NodeTypeReturnExpr:
+            if (node->data.return_expr.expr) {
+                analyze_node(g, node->data.return_expr.expr);
             }
             break;
-        case NodeTypeExpression:
-            switch (node->data.expression.type) {
-                case AstNodeExpressionTypeNumber:
-                    break;
-                case AstNodeExpressionTypeString:
-                    break;
-                case AstNodeExpressionTypeFnCall:
-                    analyze_node(g, node->data.expression.data.fn_call);
-                    break;
-                case AstNodeExpressionTypeUnreachable:
-                    break;
-            }
+        case NodeTypeBoolOrExpr:
+            analyze_node(g, node->data.bool_or_expr.op1);
+            if (node->data.bool_or_expr.op2)
+                analyze_node(g, node->data.bool_or_expr.op2);
             break;
         case NodeTypeFnCall:
             {
@@ -511,6 +515,54 @@ static void analyze_node(CodeGen *g, AstNode *node) {
         case NodeTypeDirective:
             // we looked at directives in the parent node
             break;
+        case NodeTypeBoolAndExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeComparisonExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeBinOrExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeBinXorExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeBinAndExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeBitShiftExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeAddExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeMultExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypeCastExpr:
+            zig_panic("TODO");
+            break;
+        case NodeTypePrimaryExpr:
+            switch (node->data.primary_expr.type) {
+                case PrimaryExprTypeNumber:
+                case PrimaryExprTypeString:
+                case PrimaryExprTypeUnreachable:
+                    // nothing to do
+                    break;
+                case PrimaryExprTypeFnCall:
+                    analyze_node(g, node->data.primary_expr.data.fn_call);
+                    break;
+                case PrimaryExprTypeGroupedExpr:
+                    analyze_node(g, node->data.primary_expr.data.grouped_expr);
+                    break;
+                case PrimaryExprTypeBlock:
+                    analyze_node(g, node->data.primary_expr.data.block);
+                    break;
+            }
+            break;
+        case NodeTypeGroupedExpr:
+            zig_panic("TODO");
+            break;
     }
 }
 
@@ -649,34 +701,326 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str) {
     return global_value;
 }
 
-static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node) {
-    assert(expr_node->type == NodeTypeExpression);
-    switch (expr_node->data.expression.type) {
-        case AstNodeExpressionTypeNumber:
+static LLVMValueRef gen_primary_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypePrimaryExpr);
+
+    AstNodePrimaryExpr *prim_expr = &node->data.primary_expr;
+
+    switch (node->data.primary_expr.type) {
+        case PrimaryExprTypeNumber:
             {
-                Buf *number_str = &expr_node->data.expression.data.number;
+                Buf *number_str = &prim_expr->data.number;
                 LLVMTypeRef number_type = LLVMInt32Type();
                 LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type,
                         buf_ptr(number_str), buf_len(number_str), 10);
                 return number_val;
             }
-        case AstNodeExpressionTypeString:
+        case PrimaryExprTypeString:
             {
-                Buf *str = &expr_node->data.expression.data.string;
+                Buf *str = &prim_expr->data.string;
                 LLVMValueRef str_val = find_or_create_string(g, str);
                 LLVMValueRef indices[] = {
                     LLVMConstInt(LLVMInt32Type(), 0, false),
                     LLVMConstInt(LLVMInt32Type(), 0, false)
                 };
-                LLVMValueRef ptr_val = LLVMBuildInBoundsGEP(g->builder, str_val,
-                        indices, 2, "");
-
+                LLVMValueRef ptr_val = LLVMBuildInBoundsGEP(g->builder, str_val, indices, 2, "");
                 return ptr_val;
             }
-        case AstNodeExpressionTypeFnCall:
-            return gen_fn_call(g, expr_node->data.expression.data.fn_call);
-        case AstNodeExpressionTypeUnreachable:
+        case PrimaryExprTypeUnreachable:
+            add_debug_source_node(g, node);
             return LLVMBuildUnreachable(g->builder);
+        case PrimaryExprTypeFnCall:
+            return gen_fn_call(g, prim_expr->data.fn_call);
+        case PrimaryExprTypeGroupedExpr:
+            return gen_expr(g, prim_expr->data.grouped_expr);
+        case PrimaryExprTypeBlock:
+            break;
+    }
+
+    zig_unreachable();
+}
+
+static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeCastExpr);
+
+    LLVMValueRef expr = gen_primary_expr(g, node->data.cast_expr.primary_expr);
+
+    if (!node->data.cast_expr.type)
+        return expr;
+
+    zig_panic("TODO cast expression");
+}
+
+static LLVMValueRef gen_mult_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeMultExpr);
+
+    LLVMValueRef val1 = gen_cast_expr(g, node->data.mult_expr.op1);
+
+    if (!node->data.mult_expr.op2)
+        return val1;
+
+    LLVMValueRef val2 = gen_cast_expr(g, node->data.mult_expr.op2);
+
+    switch (node->data.mult_expr.mult_op) {
+        case MultOpMult:
+            // TODO types so we know float vs int
+            add_debug_source_node(g, node);
+            return LLVMBuildMul(g->builder, val1, val2, "");
+        case MultOpDiv:
+            // TODO types so we know float vs int and signed vs unsigned
+            add_debug_source_node(g, node);
+            return LLVMBuildSDiv(g->builder, val1, val2, "");
+        case MultOpMod:
+            // TODO types so we know float vs int and signed vs unsigned
+            add_debug_source_node(g, node);
+            return LLVMBuildSRem(g->builder, val1, val2, "");
+        case MultOpInvalid:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+static LLVMValueRef gen_add_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeAddExpr);
+
+    LLVMValueRef val1 = gen_mult_expr(g, node->data.add_expr.op1);
+
+    if (!node->data.add_expr.op2)
+        return val1;
+
+    LLVMValueRef val2 = gen_mult_expr(g, node->data.add_expr.op2);
+
+    switch (node->data.add_expr.add_op) {
+        case AddOpAdd:
+            add_debug_source_node(g, node);
+            return LLVMBuildAdd(g->builder, val1, val2, "");
+        case AddOpSub:
+            add_debug_source_node(g, node);
+            return LLVMBuildSub(g->builder, val1, val2, "");
+        case AddOpInvalid:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+static LLVMValueRef gen_bit_shift_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeBitShiftExpr);
+
+    LLVMValueRef val1 = gen_add_expr(g, node->data.bit_shift_expr.op1);
+
+    if (!node->data.bit_shift_expr.op2)
+        return val1;
+
+    LLVMValueRef val2 = gen_add_expr(g, node->data.bit_shift_expr.op2);
+
+    switch (node->data.bit_shift_expr.bit_shift_op) {
+        case BitShiftOpLeft:
+            add_debug_source_node(g, node);
+            return LLVMBuildShl(g->builder, val1, val2, "");
+        case BitShiftOpRight:
+            // TODO implement type system so that we know whether to do
+            // logical or arithmetic shifting here.
+            // signed -> arithmetic, unsigned -> logical
+            add_debug_source_node(g, node);
+            return LLVMBuildLShr(g->builder, val1, val2, "");
+        case BitShiftOpInvalid:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+static LLVMValueRef gen_bin_and_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeBinAndExpr);
+
+    LLVMValueRef val1 = gen_bit_shift_expr(g, node->data.bin_and_expr.op1);
+
+    if (!node->data.bin_and_expr.op2)
+        return val1;
+
+    LLVMValueRef val2 = gen_bit_shift_expr(g, node->data.bin_and_expr.op2);
+
+    add_debug_source_node(g, node);
+    return LLVMBuildAnd(g->builder, val1, val2, "");
+}
+
+static LLVMValueRef gen_bin_xor_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeBinXorExpr);
+
+    LLVMValueRef val1 = gen_bin_and_expr(g, node->data.bin_xor_expr.op1);
+
+    if (!node->data.bin_xor_expr.op2)
+        return val1;
+
+    LLVMValueRef val2 = gen_bin_and_expr(g, node->data.bin_xor_expr.op2);
+
+    add_debug_source_node(g, node);
+    return LLVMBuildXor(g->builder, val1, val2, "");
+}
+
+static LLVMValueRef gen_bin_or_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeBinOrExpr);
+
+    LLVMValueRef val1 = gen_bin_xor_expr(g, node->data.bin_or_expr.op1);
+
+    if (!node->data.bin_or_expr.op2)
+        return val1;
+
+    LLVMValueRef val2 = gen_bin_xor_expr(g, node->data.bin_or_expr.op2);
+
+    add_debug_source_node(g, node);
+    return LLVMBuildOr(g->builder, val1, val2, "");
+}
+
+static LLVMIntPredicate cmp_op_to_int_predicate(CmpOp cmp_op, bool is_signed) {
+    switch (cmp_op) {
+        case CmpOpInvalid:
+            zig_unreachable();
+        case CmpOpEq:
+            return LLVMIntEQ;
+        case CmpOpNotEq:
+            return LLVMIntNE;
+        case CmpOpLessThan:
+            return is_signed ? LLVMIntSLT : LLVMIntULT;
+        case CmpOpGreaterThan:
+            return is_signed ? LLVMIntSGT : LLVMIntUGT;
+        case CmpOpLessOrEq:
+            return is_signed ? LLVMIntSLE : LLVMIntULE;
+        case CmpOpGreaterOrEq:
+            return is_signed ? LLVMIntSGE : LLVMIntUGE;
+    }
+    zig_unreachable();
+}
+
+static LLVMValueRef gen_cmp_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeComparisonExpr);
+
+    LLVMValueRef val1 = gen_bin_or_expr(g, node->data.comparison_expr.op1);
+
+    if (!node->data.comparison_expr.op2)
+        return val1;
+
+    LLVMValueRef val2 = gen_bin_or_expr(g, node->data.comparison_expr.op2);
+
+    // TODO implement type system so that we know whether to do signed or unsigned comparison here
+    LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.comparison_expr.cmp_op, true);
+    add_debug_source_node(g, node);
+    return LLVMBuildICmp(g->builder, pred, val1, val2, "");
+}
+
+static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeBoolAndExpr);
+
+    LLVMValueRef val1 = gen_cmp_expr(g, node->data.bool_and_expr.op1);
+
+    if (!node->data.bool_and_expr.op2)
+        return val1;
+
+    // block for when val1 == true
+    LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn, "BoolAndTrue");
+    // block for when val1 == false (don't even evaluate the second part)
+    LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn, "BoolAndFalse");
+
+    LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(val1));
+    add_debug_source_node(g, node);
+    LLVMValueRef val1_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, zero, "");
+    LLVMBuildCondBr(g->builder, val1_i1, false_block, true_block);
+
+    LLVMPositionBuilderAtEnd(g->builder, true_block);
+    LLVMValueRef val2 = gen_cmp_expr(g, node->data.bool_and_expr.op2);
+    add_debug_source_node(g, node);
+    LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
+
+    LLVMPositionBuilderAtEnd(g->builder, false_block);
+    add_debug_source_node(g, node);
+    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
+    LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
+    LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
+    LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), true_block};
+    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+
+    return phi;
+}
+
+static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
+    assert(expr_node->type == NodeTypeBoolOrExpr);
+
+    LLVMValueRef val1 = gen_bool_and_expr(g, expr_node->data.bool_or_expr.op1);
+
+    if (!expr_node->data.bool_or_expr.op2)
+        return val1;
+
+    // block for when val1 == false
+    LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn, "BoolOrFalse");
+    // block for when val1 == true (don't even evaluate the second part)
+    LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn, "BoolOrTrue");
+
+    LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(val1));
+    add_debug_source_node(g, expr_node);
+    LLVMValueRef val1_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, zero, "");
+    LLVMBuildCondBr(g->builder, val1_i1, false_block, true_block);
+
+    LLVMPositionBuilderAtEnd(g->builder, false_block);
+    LLVMValueRef val2 = gen_bool_and_expr(g, expr_node->data.bool_or_expr.op2);
+    add_debug_source_node(g, expr_node);
+    LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
+
+    LLVMPositionBuilderAtEnd(g->builder, true_block);
+    add_debug_source_node(g, expr_node);
+    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
+    LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
+    LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
+    LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), false_block};
+    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+
+    return phi;
+}
+
+static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeReturnExpr);
+    AstNode *param_node = node->data.return_expr.expr;
+    if (param_node) {
+        LLVMValueRef value = gen_expr(g, param_node);
+
+        add_debug_source_node(g, node);
+        return LLVMBuildRet(g->builder, value);
+    } else {
+        add_debug_source_node(g, node);
+        return LLVMBuildRetVoid(g->builder);
+    }
+}
+/*
+Expression : BoolOrExpression | ReturnExpression
+*/
+static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
+    switch (node->type) {
+        case NodeTypeBoolOrExpr:
+            return gen_bool_or_expr(g, node);
+        case NodeTypeReturnExpr:
+            return gen_return_expr(g, node);
+        case NodeTypeRoot:
+        case NodeTypeRootExportDecl:
+        case NodeTypeFnProto:
+        case NodeTypeFnDef:
+        case NodeTypeFnDecl:
+        case NodeTypeParamDecl:
+        case NodeTypeType:
+        case NodeTypeBlock:
+        case NodeTypeFnCall:
+        case NodeTypeExternBlock:
+        case NodeTypeDirective:
+        case NodeTypeBoolAndExpr:
+        case NodeTypeComparisonExpr:
+        case NodeTypeBinOrExpr:
+        case NodeTypeBinXorExpr:
+        case NodeTypeBinAndExpr:
+        case NodeTypeBitShiftExpr:
+        case NodeTypeAddExpr:
+        case NodeTypeMultExpr:
+        case NodeTypeCastExpr:
+        case NodeTypePrimaryExpr:
+            return gen_primary_expr(g, node);
+        case NodeTypeGroupedExpr:
+            zig_unreachable();
     }
     zig_unreachable();
 }
@@ -692,39 +1036,7 @@ static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return)
 
     for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
         AstNode *statement_node = block_node->data.block.statements.at(i);
-        switch (statement_node->type) {
-            case NodeTypeStatementReturn:
-                {
-                    AstNode *expr_node = statement_node->data.statement_return.expression;
-                    if (expr_node) {
-                        LLVMValueRef value = gen_expr(g, expr_node);
-
-                        add_debug_source_node(g, statement_node);
-                        LLVMBuildRet(g->builder, value);
-                    } else {
-                        add_debug_source_node(g, statement_node);
-                        LLVMBuildRetVoid(g->builder);
-                    }
-                    break;
-                }
-            case NodeTypeExpression:
-                {
-                    gen_expr(g, statement_node);
-                    break;
-                }
-            case NodeTypeRoot:
-            case NodeTypeFnProto:
-            case NodeTypeFnDef:
-            case NodeTypeFnDecl:
-            case NodeTypeParamDecl:
-            case NodeTypeType:
-            case NodeTypeBlock:
-            case NodeTypeFnCall:
-            case NodeTypeExternBlock:
-            case NodeTypeDirective:
-            case NodeTypeRootExportDecl:
-                zig_unreachable();
-        }
+        gen_expr(g, statement_node);
     }
 
     if (add_implicit_return) {
@@ -810,6 +1122,7 @@ void code_gen(CodeGen *g) {
         FnTableEntry *fn_table_entry = g->fn_defs.at(i);
         AstNode *fn_def_node = fn_table_entry->fn_def_node;
         LLVMValueRef fn = fn_table_entry->fn_value;
+        g->cur_fn = fn;
 
         AstNode *proto_node = fn_table_entry->proto_node;
         assert(proto_node->type == NodeTypeFnProto);
src/parser.cpp
@@ -10,6 +10,47 @@
 #include <stdarg.h>
 #include <stdio.h>
 
+static const char *mult_op_str(MultOp mult_op) {
+    switch (mult_op) {
+        case MultOpInvalid: return "(invalid)";
+        case MultOpMult: return "*";
+        case MultOpDiv: return "/";
+        case MultOpMod: return "%";
+    }
+    zig_unreachable();
+}
+
+static const char *add_op_str(AddOp add_op) {
+    switch (add_op) {
+        case AddOpInvalid: return "(invalid)";
+        case AddOpAdd: return "+";
+        case AddOpSub: return "-";
+    }
+    zig_unreachable();
+}
+
+static const char *bit_shift_op_str(BitShiftOp bit_shift_op) {
+    switch (bit_shift_op) {
+        case BitShiftOpInvalid: return "(invalid)";
+        case BitShiftOpLeft: return "<<";
+        case BitShiftOpRight: return ">>";
+    }
+    zig_unreachable();
+}
+
+static const char *cmp_op_str(CmpOp cmp_op) {
+    switch (cmp_op) {
+        case CmpOpInvalid: return "(invalid)";
+        case CmpOpEq: return "=";
+        case CmpOpNotEq: return "!=";
+        case CmpOpLessThan: return "<";
+        case CmpOpGreaterThan: return ">";
+        case CmpOpLessOrEq: return "<=";
+        case CmpOpGreaterOrEq: return ">=";
+    }
+    zig_unreachable();
+}
+
 __attribute__ ((format (printf, 2, 3)))
 __attribute__ ((noreturn))
 static void ast_error(Token *token, const char *format, ...) {
@@ -43,16 +84,38 @@ const char *node_type_str(NodeType node_type) {
             return "Type";
         case NodeTypeBlock:
             return "Block";
-        case NodeTypeStatementReturn:
-            return "StatementReturn";
-        case NodeTypeExpression:
-            return "Expression";
+        case NodeTypeBoolOrExpr:
+            return "BoolOrExpr";
         case NodeTypeFnCall:
             return "FnCall";
         case NodeTypeExternBlock:
             return "ExternBlock";
         case NodeTypeDirective:
             return "Directive";
+        case NodeTypeReturnExpr:
+            return "ReturnExpr";
+        case NodeTypeBoolAndExpr:
+            return "BoolAndExpr";
+        case NodeTypeComparisonExpr:
+            return "ComparisonExpr";
+        case NodeTypeBinOrExpr:
+            return "BinOrExpr";
+        case NodeTypeBinXorExpr:
+            return "BinXorExpr";
+        case NodeTypeBinAndExpr:
+            return "BinAndExpr";
+        case NodeTypeBitShiftExpr:
+            return "BitShiftExpr";
+        case NodeTypeAddExpr:
+            return "AddExpr";
+        case NodeTypeMultExpr:
+            return "MultExpr";
+        case NodeTypeCastExpr:
+            return "CastExpr";
+        case NodeTypePrimaryExpr:
+            return "PrimaryExpr";
+        case NodeTypeGroupedExpr:
+            return "GroupedExpr";
     }
     zig_unreachable();
 }
@@ -133,10 +196,10 @@ void ast_print(AstNode *node, int indent) {
                     }
             }
             break;
-        case NodeTypeStatementReturn:
-            fprintf(stderr, "ReturnStatement\n");
-            if (node->data.statement_return.expression)
-                ast_print(node->data.statement_return.expression, indent + 2);
+        case NodeTypeReturnExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            if (node->data.return_expr.expr)
+                ast_print(node->data.return_expr.expr, indent + 2);
             break;
         case NodeTypeExternBlock:
             {
@@ -151,22 +214,11 @@ void ast_print(AstNode *node, int indent) {
             fprintf(stderr, "%s\n", node_type_str(node->type));
             ast_print(node->data.fn_decl.fn_proto, indent + 2);
             break;
-        case NodeTypeExpression:
-            switch (node->data.expression.type) {
-                case AstNodeExpressionTypeNumber:
-                    fprintf(stderr, "NumberLiteralExpression %s\n", buf_ptr(&node->data.expression.data.number));
-                    break;
-                case AstNodeExpressionTypeString:
-                    fprintf(stderr, "StringLiteralExpression '%s'\n", buf_ptr(&node->data.expression.data.string));
-                    break;
-                case AstNodeExpressionTypeFnCall:
-                    fprintf(stderr, "FnCallExpression\n");
-                    ast_print(node->data.expression.data.fn_call, indent + 2);
-                    break;
-                case AstNodeExpressionTypeUnreachable:
-                    fprintf(stderr, "UnreachableExpression\n");
-                    break;
-            }
+        case NodeTypeBoolOrExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.bool_or_expr.op1, indent + 2);
+            if (node->data.bool_or_expr.op2)
+                ast_print(node->data.bool_or_expr.op2, indent + 2);
             break;
         case NodeTypeFnCall:
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.fn_call.name));
@@ -178,6 +230,95 @@ void ast_print(AstNode *node, int indent) {
         case NodeTypeDirective:
             fprintf(stderr, "%s\n", node_type_str(node->type));
             break;
+        case NodeTypeBoolAndExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.bool_and_expr.op1, indent + 2);
+            if (node->data.bool_and_expr.op2)
+                ast_print(node->data.bool_and_expr.op2, indent + 2);
+            break;
+        case NodeTypeComparisonExpr:
+            fprintf(stderr, "%s %s\n", node_type_str(node->type),
+                    cmp_op_str(node->data.comparison_expr.cmp_op));
+            ast_print(node->data.comparison_expr.op1, indent + 2);
+            if (node->data.comparison_expr.op2)
+                ast_print(node->data.comparison_expr.op2, indent + 2);
+            break;
+        case NodeTypeBinOrExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.bin_or_expr.op1, indent + 2);
+            if (node->data.bin_or_expr.op2)
+                ast_print(node->data.bin_or_expr.op2, indent + 2);
+            break;
+        case NodeTypeBinXorExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.bin_xor_expr.op1, indent + 2);
+            if (node->data.bin_xor_expr.op2)
+                ast_print(node->data.bin_xor_expr.op2, indent + 2);
+            break;
+        case NodeTypeBinAndExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.bin_and_expr.op1, indent + 2);
+            if (node->data.bin_and_expr.op2)
+                ast_print(node->data.bin_and_expr.op2, indent + 2);
+            break;
+        case NodeTypeBitShiftExpr:
+            fprintf(stderr, "%s %s\n", node_type_str(node->type),
+                    bit_shift_op_str(node->data.bit_shift_expr.bit_shift_op));
+            ast_print(node->data.bit_shift_expr.op1, indent + 2);
+            if (node->data.bit_shift_expr.op2)
+                ast_print(node->data.bit_shift_expr.op2, indent + 2);
+            break;
+        case NodeTypeAddExpr:
+            fprintf(stderr, "%s %s\n", node_type_str(node->type),
+                    add_op_str(node->data.add_expr.add_op));
+            ast_print(node->data.add_expr.op1, indent + 2);
+            if (node->data.add_expr.op2)
+                ast_print(node->data.add_expr.op2, indent + 2);
+            break;
+        case NodeTypeMultExpr:
+            fprintf(stderr, "%s %s\n", node_type_str(node->type),
+                    mult_op_str(node->data.mult_expr.mult_op));
+            ast_print(node->data.mult_expr.op1, indent + 2);
+            if (node->data.mult_expr.op2)
+                ast_print(node->data.mult_expr.op2, indent + 2);
+            break;
+        case NodeTypeCastExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.cast_expr.primary_expr, indent + 2);
+            if (node->data.cast_expr.type)
+                ast_print(node->data.cast_expr.type, indent + 2);
+            break;
+        case NodeTypePrimaryExpr:
+            switch (node->data.primary_expr.type) {
+                case PrimaryExprTypeNumber:
+                    fprintf(stderr, "PrimaryExpr Number %s\n",
+                            buf_ptr(&node->data.primary_expr.data.number));
+                    break;
+                case PrimaryExprTypeString:
+                    fprintf(stderr, "PrimaryExpr String '%s'\n",
+                            buf_ptr(&node->data.primary_expr.data.string));
+                    break;
+                case PrimaryExprTypeUnreachable:
+                    fprintf(stderr, "PrimaryExpr Unreachable\n");
+                    break;
+                case PrimaryExprTypeFnCall:
+                    fprintf(stderr, "PrimaryExpr FnCall\n");
+                    ast_print(node->data.primary_expr.data.fn_call, indent + 2);
+                    break;
+                case PrimaryExprTypeGroupedExpr:
+                    fprintf(stderr, "PrimaryExpr GroupedExpr\n");
+                    ast_print(node->data.primary_expr.data.grouped_expr, indent + 2);
+                    break;
+                case PrimaryExprTypeBlock:
+                    fprintf(stderr, "PrimaryExpr Block\n");
+                    ast_print(node->data.primary_expr.data.block, indent + 2);
+                    break;
+            }
+            break;
+        case NodeTypeGroupedExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.grouped_expr.expr, indent + 2);
+            break;
     }
 }
 
@@ -267,6 +408,7 @@ void ast_invalid_token_error(ParseContext *pc, Token *token) {
 }
 
 static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
+static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
 
 
 static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
@@ -444,109 +586,515 @@ static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int
     zig_unreachable();
 }
 
+/*
+GroupedExpression : token(LParen) Expression token(RParen)
+*/
+static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *l_paren = &pc->tokens->at(*token_index);
+    if (l_paren->id != TokenIdLParen) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, l_paren);
+        } else {
+            return nullptr;
+        }
+    }
+
+    *token_index += 1;
+
+    AstNode *node = ast_create_node(NodeTypeGroupedExpr, l_paren);
+
+    node->data.grouped_expr.expr = ast_parse_expression(pc, token_index, true);
+
+    return node;
+}
+
 /*
 FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) ;
 */
-static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_token_index) {
-    Token *fn_name = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, fn_name, TokenIdSymbol);
+static AstNode *ast_parse_fn_call(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *fn_name = &pc->tokens->at(*token_index);
+    if (fn_name->id != TokenIdSymbol) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, fn_name);
+        } else {
+            return nullptr;
+        }
+    }
+
+    *token_index += 1;
 
     AstNode *node = ast_create_node(NodeTypeFnCall, fn_name);
 
 
     ast_buf_from_token(pc, fn_name, &node->data.fn_call.name);
 
-    ast_parse_fn_call_param_list(pc, token_index, &token_index, &node->data.fn_call.params);
+    ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call.params);
 
-    *new_token_index = token_index;
     return node;
 }
 
 /*
-Expression : token(Number) | token(String) | token(Unreachable) | FnCall
+PrimaryExpression : token(Number) | token(String) | token(Unreachable) | FnCall | GroupedExpression | Block
 */
-static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
+static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
-    AstNode *node = ast_create_node(NodeTypeExpression, token);
-    if (token->id == TokenIdKeywordUnreachable) {
-        node->data.expression.type = AstNodeExpressionTypeUnreachable;
-        *token_index += 1;
-    } else if (token->id == TokenIdSymbol) {
-        node->data.expression.type = AstNodeExpressionTypeFnCall;
-        node->data.expression.data.fn_call = ast_parse_fn_call(pc, *token_index, token_index);
-    } else if (token->id == TokenIdNumberLiteral) {
-        node->data.expression.type = AstNodeExpressionTypeNumber;
-        ast_buf_from_token(pc, token, &node->data.expression.data.number);
+
+    if (token->id == TokenIdNumberLiteral) {
+        AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
+        node->data.primary_expr.type = PrimaryExprTypeNumber;
+        ast_buf_from_token(pc, token, &node->data.primary_expr.data.number);
         *token_index += 1;
+        return node;
     } else if (token->id == TokenIdStringLiteral) {
-        node->data.expression.type = AstNodeExpressionTypeString;
-        parse_string_literal(pc, token, &node->data.expression.data.string);
+        AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
+        node->data.primary_expr.type = PrimaryExprTypeString;
+        parse_string_literal(pc, token, &node->data.primary_expr.data.string);
         *token_index += 1;
-    } else if (mandatory) {
-        ast_invalid_token_error(pc, token);
-    } else {
+        return node;
+    } else if (token->id == TokenIdKeywordUnreachable) {
+        AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
+        node->data.primary_expr.type = PrimaryExprTypeUnreachable;
+        *token_index += 1;
+        return node;
+    }
+
+    AstNode *block_node = ast_parse_block(pc, token_index, false);
+    if (block_node) {
+        AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
+        node->data.primary_expr.type = PrimaryExprTypeBlock;
+        node->data.primary_expr.data.block = block_node;
+        return node;
+    }
+
+    AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
+    if (grouped_expr_node) {
+        AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
+        node->data.primary_expr.type = PrimaryExprTypeGroupedExpr;
+        node->data.primary_expr.data.grouped_expr = grouped_expr_node;
+        return node;
+    }
+
+    AstNode *fn_call_node = ast_parse_fn_call(pc, token_index, false);
+    if (fn_call_node) {
+        AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
+        node->data.primary_expr.type = PrimaryExprTypeFnCall;
+        node->data.primary_expr.data.fn_call = fn_call_node;
+        return node;
+    }
+
+    if (!mandatory)
         return nullptr;
+
+    ast_invalid_token_error(pc, token);
+}
+
+/*
+CastExpression : PrimaryExpression token(As) Type | PrimaryExpression
+*/
+static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory);
+    if (!primary_expr)
+        return nullptr;
+
+    Token *as_kw = &pc->tokens->at(*token_index);
+    if (as_kw->id != TokenIdKeywordAs)
+        return primary_expr;
+    *token_index += 1;
+
+    AstNode *node = ast_create_node(NodeTypeCastExpr, as_kw);
+    node->data.cast_expr.primary_expr = primary_expr;
+
+    node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index);
+
+    return node;
+}
+
+static MultOp tok_to_mult_op(Token *token) {
+    switch (token->id) {
+        case TokenIdStar: return MultOpMult;
+        case TokenIdSlash: return MultOpDiv;
+        case TokenIdPercent: return MultOpMod;
+        default: return MultOpInvalid;
+    }
+}
+
+/*
+MultiplyOperator : token(Star) | token(Slash) | token(Percent)
+*/
+static MultOp ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *token = &pc->tokens->at(*token_index);
+    MultOp result = tok_to_mult_op(token);
+    if (result == MultOpInvalid) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, token);
+        } else {
+            return MultOpInvalid;
+        }
+    }
+    *token_index += 1;
+    return result;
+}
+
+/*
+MultiplyExpression : CastExpression MultiplyOperator CastExpression | CastExpression
+*/
+static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_cast_expression(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    MultOp mult_op = ast_parse_mult_op(pc, token_index, false);
+    if (mult_op == MultOpInvalid)
+        return operand_1;
+
+    AstNode *operand_2 = ast_parse_cast_expression(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeMultExpr, token);
+    node->data.mult_expr.op1 = operand_1;
+    node->data.mult_expr.mult_op = mult_op;
+    node->data.mult_expr.op2 = operand_2;
+
+    return node;
+}
+
+static AddOp tok_to_add_op(Token *token) {
+    switch (token->id) {
+        case TokenIdPlus: return AddOpAdd;
+        case TokenIdDash: return AddOpSub;
+        default: return AddOpInvalid;
+    }
+}
+
+/*
+AdditionOperator : token(Plus) | token(Minus)
+*/
+static AddOp ast_parse_add_op(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *token = &pc->tokens->at(*token_index);
+    AddOp result = tok_to_add_op(token);
+    if (result == AddOpInvalid) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, token);
+        } else {
+            return AddOpInvalid;
+        }
+    }
+    *token_index += 1;
+    return result;
+}
+
+/*
+AdditionExpression : MultiplyExpression AdditionOperator MultiplyExpression | MultiplyExpression
+*/
+static AstNode *ast_parse_add_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_mult_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    AddOp add_op = ast_parse_add_op(pc, token_index, false);
+    if (add_op == AddOpInvalid)
+        return operand_1;
+
+    AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeAddExpr, token);
+    node->data.add_expr.op1 = operand_1;
+    node->data.add_expr.add_op = add_op;
+    node->data.add_expr.op2 = operand_2;
+
+    return node;
+}
+
+static BitShiftOp tok_to_bit_shift_op(Token *token) {
+    switch (token->id) {
+        case TokenIdBitShiftLeft: return BitShiftOpLeft;
+        case TokenIdBitShiftRight: return BitShiftOpRight;
+        default: return BitShiftOpInvalid;
     }
+}
+
+/*
+BitShiftOperator : token(BitShiftLeft | token(BitShiftRight)
+*/
+static BitShiftOp ast_parse_bit_shift_op(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *token = &pc->tokens->at(*token_index);
+    BitShiftOp result = tok_to_bit_shift_op(token);
+    if (result == BitShiftOpInvalid) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, token);
+        } else {
+            return BitShiftOpInvalid;
+        }
+    }
+    *token_index += 1;
+    return result;
+}
+
+/*
+BitShiftExpression : AdditionExpression BitShiftOperator AdditionExpression | AdditionExpression
+*/
+static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_add_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    BitShiftOp bit_shift_op = ast_parse_bit_shift_op(pc, token_index, false);
+    if (bit_shift_op == BitShiftOpInvalid)
+        return operand_1;
+
+    AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeBitShiftExpr, token);
+    node->data.bit_shift_expr.op1 = operand_1;
+    node->data.bit_shift_expr.bit_shift_op = bit_shift_op;
+    node->data.bit_shift_expr.op2 = operand_2;
 
     return node;
 }
 
+
 /*
-Statement : ExpressionStatement  | ReturnStatement ;
+BinaryAndExpression : BitShiftExpression token(BinAnd) BitShiftExpression | BitShiftExpression
+*/
+static AstNode *ast_parse_bin_and_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_bit_shift_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    if (token->id != TokenIdBinAnd)
+        return operand_1;
+    *token_index += 1;
+
+    AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true);
 
-ExpressionStatement : Expression token(Semicolon) ;
+    AstNode *node = ast_create_node(NodeTypeBinAndExpr, token);
+    node->data.bin_and_expr.op1 = operand_1;
+    node->data.bin_and_expr.op2 = operand_2;
 
-ReturnStatement : token(Return) option(Expression) token(Semicolon) ;
+    return node;
+}
+
+/*
+BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryAndExpression | BinaryAndExpression
 */
-static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_token_index) {
-    Token *token = &pc->tokens->at(token_index);
-    if (token->id == TokenIdKeywordReturn) {
-        AstNode *node = ast_create_node(NodeTypeStatementReturn, token);
-        token_index += 1;
-        node->data.statement_return.expression = ast_parse_expression(pc, &token_index, false);
+static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_bin_and_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
 
-        Token *semicolon = &pc->tokens->at(token_index);
-        token_index += 1;
-        ast_expect_token(pc, semicolon, TokenIdSemicolon);
-        *new_token_index = token_index;
-        return node;
-    } else if (token->id == TokenIdSymbol ||
-               token->id == TokenIdStringLiteral ||
-               token->id == TokenIdKeywordUnreachable ||
-               token->id == TokenIdNumberLiteral)
-    {
-        AstNode *node = ast_parse_expression(pc, &token_index, true);
-
-        Token *semicolon = &pc->tokens->at(token_index);
-        token_index += 1;
-        ast_expect_token(pc, semicolon, TokenIdSemicolon);
-        *new_token_index = token_index;
+    Token *token = &pc->tokens->at(*token_index);
+    if (token->id != TokenIdBinXor)
+        return operand_1;
+    *token_index += 1;
+
+    AstNode *operand_2 = ast_parse_bin_and_expr(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeBinXorExpr, token);
+    node->data.bin_xor_expr.op1 = operand_1;
+    node->data.bin_xor_expr.op2 = operand_2;
+
+    return node;
+}
+
+/*
+BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryXorExpression | BinaryXorExpression
+*/
+static AstNode *ast_parse_bin_or_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_bin_xor_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    if (token->id != TokenIdBinOr)
+        return operand_1;
+    *token_index += 1;
+
+    AstNode *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeBinOrExpr, token);
+    node->data.bin_or_expr.op1 = operand_1;
+    node->data.bin_or_expr.op2 = operand_2;
+
+    return node;
+}
+
+static CmpOp tok_to_cmp_op(Token *token) {
+    switch (token->id) {
+        case TokenIdCmpEq: return CmpOpEq;
+        case TokenIdCmpNotEq: return CmpOpNotEq;
+        case TokenIdCmpLessThan: return CmpOpLessThan;
+        case TokenIdCmpGreaterThan: return CmpOpGreaterThan;
+        case TokenIdCmpLessOrEq: return CmpOpLessOrEq;
+        case TokenIdCmpGreaterOrEq: return CmpOpGreaterOrEq;
+        default: return CmpOpInvalid;
+    }
+}
+
+static CmpOp ast_parse_comparison_operator(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *token = &pc->tokens->at(*token_index);
+    CmpOp result = tok_to_cmp_op(token);
+    if (result == CmpOpInvalid) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, token);
+        } else {
+            return CmpOpInvalid;
+        }
+    }
+    *token_index += 1;
+    return result;
+}
+
+/*
+ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
+*/
+static AstNode *ast_parse_comparison_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_bin_or_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    CmpOp cmp_op = ast_parse_comparison_operator(pc, token_index, false);
+    if (cmp_op == CmpOpInvalid)
+        return operand_1;
+
+    AstNode *operand_2 = ast_parse_bin_or_expr(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeComparisonExpr, token);
+    node->data.comparison_expr.op1 = operand_1;
+    node->data.comparison_expr.cmp_op = cmp_op;
+    node->data.comparison_expr.op2 = operand_2;
+
+    return node;
+}
+
+/*
+BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
+ */
+static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_comparison_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    if (token->id != TokenIdBoolAnd)
+        return operand_1;
+    *token_index += 1;
+
+    AstNode *operand_2 = ast_parse_comparison_expr(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeBoolAndExpr, token);
+    node->data.bool_and_expr.op1 = operand_1;
+    node->data.bool_and_expr.op2 = operand_2;
+
+    return node;
+}
+
+/*
+ReturnExpression : token(Return) option(Expression)
+*/
+static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *return_tok = &pc->tokens->at(*token_index);
+    if (return_tok->id == TokenIdKeywordReturn) {
+        *token_index += 1;
+        AstNode *node = ast_create_node(NodeTypeReturnExpr, return_tok);
+        node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
         return node;
+    } else if (mandatory) {
+        ast_invalid_token_error(pc, return_tok);
     } else {
-        ast_invalid_token_error(pc, token);
+        return nullptr;
     }
 }
 
+/*
+BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
+*/
+static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *operand_1 = ast_parse_bool_and_expr(pc, token_index, mandatory);
+    if (!operand_1)
+        return nullptr;
+
+    Token *token = &pc->tokens->at(*token_index);
+    if (token->id != TokenIdBoolOr)
+        return operand_1;
+    *token_index += 1;
+
+    AstNode *operand_2 = ast_parse_bool_and_expr(pc, token_index, true);
+
+    AstNode *node = ast_create_node(NodeTypeBoolOrExpr, token);
+    node->data.bool_or_expr.op1 = operand_1;
+    node->data.bool_or_expr.op2 = operand_2;
+
+    return node;
+}
+
+/*
+Expression : BoolOrExpression | ReturnExpression
+*/
+static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *token = &pc->tokens->at(*token_index);
+
+    AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
+    if (return_expr)
+        return return_expr;
+
+    AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);
+    if (bool_or_expr)
+        return bool_or_expr;
+
+    if (!mandatory)
+        return nullptr;
+
+    ast_invalid_token_error(pc, token);
+}
+
+/*
+ExpressionStatement : Expression token(Semicolon)
+*/
+static AstNode *ast_parse_expression_statement(ParseContext *pc, int *token_index) {
+    AstNode *expr_node = ast_parse_expression(pc, token_index, true);
+
+    Token *semicolon = &pc->tokens->at(*token_index);
+    *token_index += 1;
+    ast_expect_token(pc, semicolon, TokenIdSemicolon);
+
+    return expr_node;
+}
+
+/*
+Statement : ExpressionStatement
+*/
+static AstNode *ast_parse_statement(ParseContext *pc, int *token_index) {
+    return ast_parse_expression_statement(pc, token_index);
+}
+
 /*
 Block : token(LBrace) many(Statement) token(RBrace);
 */
-static AstNode *ast_parse_block(ParseContext *pc, int token_index, int *new_token_index) {
-    Token *l_brace = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, l_brace, TokenIdLBrace);
+static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *l_brace = &pc->tokens->at(*token_index);
 
-    AstNode *node = ast_create_node(NodeTypeBlock, l_brace);
+    if (l_brace->id != TokenIdLBrace) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, l_brace);
+        } else {
+            return nullptr;
+        }
+    }
+    *token_index += 1;
 
+    AstNode *node = ast_create_node(NodeTypeBlock, l_brace);
 
     for (;;) {
-        Token *token = &pc->tokens->at(token_index);
+        Token *token = &pc->tokens->at(*token_index);
         if (token->id == TokenIdRBrace) {
-            token_index += 1;
-            *new_token_index = token_index;
+            *token_index += 1;
             return node;
         } else {
-            AstNode *statement_node = ast_parse_statement(pc, token_index, &token_index);
+            AstNode *statement_node = ast_parse_statement(pc, token_index);
             node->data.block.statements.append(statement_node);
         }
     }
@@ -620,7 +1168,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat
     AstNode *node = ast_create_node_with_node(NodeTypeFnDef, fn_proto);
 
     node->data.fn_def.fn_proto = fn_proto;
-    node->data.fn_def.body = ast_parse_block(pc, *token_index, token_index);
+    node->data.fn_def.body = ast_parse_block(pc, token_index, true);
 
     return node;
 }
src/parser.hpp
@@ -24,11 +24,22 @@ enum NodeType {
     NodeTypeParamDecl,
     NodeTypeType,
     NodeTypeBlock,
-    NodeTypeExpression,
     NodeTypeFnCall,
     NodeTypeExternBlock,
     NodeTypeDirective,
-    NodeTypeStatementReturn,
+    NodeTypeReturnExpr,
+    NodeTypeBoolOrExpr,
+    NodeTypeBoolAndExpr,
+    NodeTypeComparisonExpr,
+    NodeTypeBinOrExpr,
+    NodeTypeBinXorExpr,
+    NodeTypeBinAndExpr,
+    NodeTypeBitShiftExpr,
+    NodeTypeAddExpr,
+    NodeTypeMultExpr,
+    NodeTypeCastExpr,
+    NodeTypePrimaryExpr,
+    NodeTypeGroupedExpr,
 };
 
 struct AstNodeRoot {
@@ -80,24 +91,15 @@ struct AstNodeBlock {
     ZigList<AstNode *> statements;
 };
 
-struct AstNodeStatementReturn {
-    AstNode *expression;
+struct AstNodeReturnExpr {
+    // might be null in case of return void;
+    AstNode *expr;
 };
 
-enum AstNodeExpressionType {
-    AstNodeExpressionTypeNumber,
-    AstNodeExpressionTypeString,
-    AstNodeExpressionTypeFnCall,
-    AstNodeExpressionTypeUnreachable,
-};
-
-struct AstNodeExpression {
-    AstNodeExpressionType type;
-    union {
-        Buf number;
-        Buf string;
-        AstNode *fn_call;
-    } data;
+struct AstNodeBoolOrExpr {
+    AstNode *op1;
+    // if op2 is non-null, do boolean or, otherwise nothing
+    AstNode *op2;
 };
 
 struct AstNodeFnCall {
@@ -120,6 +122,117 @@ struct AstNodeRootExportDecl {
     Buf name;
 };
 
+struct AstNodeBoolAndExpr {
+    AstNode *op1;
+    // if op2 is non-null, do boolean and, otherwise nothing
+    AstNode *op2;
+};
+
+enum CmpOp {
+    CmpOpInvalid,
+    CmpOpEq,
+    CmpOpNotEq,
+    CmpOpLessThan,
+    CmpOpGreaterThan,
+    CmpOpLessOrEq,
+    CmpOpGreaterOrEq,
+};
+
+struct AstNodeComparisonExpr {
+    AstNode *op1;
+    CmpOp cmp_op;
+    // if op2 is non-null, do cmp_op, otherwise nothing
+    AstNode *op2;
+};
+
+struct AstNodeBinOrExpr {
+    AstNode *op1;
+    // if op2 is non-null, do binary or, otherwise nothing
+    AstNode *op2;
+};
+
+struct AstNodeBinXorExpr {
+    AstNode *op1;
+    // if op2 is non-null, do binary xor, otherwise nothing
+    AstNode *op2;
+};
+
+struct AstNodeBinAndExpr {
+    AstNode *op1;
+    // if op2 is non-null, do binary and, otherwise nothing
+    AstNode *op2;
+};
+
+enum BitShiftOp {
+    BitShiftOpInvalid,
+    BitShiftOpLeft,
+    BitShiftOpRight,
+};
+
+struct AstNodeBitShiftExpr {
+    AstNode *op1;
+    BitShiftOp bit_shift_op;
+    // if op2 is non-null, do bit_shift_op, otherwise nothing
+    AstNode *op2;
+};
+
+enum AddOp {
+    AddOpInvalid,
+    AddOpAdd,
+    AddOpSub,
+};
+
+struct AstNodeAddExpr {
+    AstNode *op1;
+    AddOp add_op;
+    // if op2 is non-null, do add_op, otherwise nothing
+    AstNode *op2;
+};
+
+enum MultOp {
+    MultOpInvalid,
+    MultOpMult,
+    MultOpDiv,
+    MultOpMod,
+};
+
+struct AstNodeMultExpr {
+    AstNode *op1;
+    MultOp mult_op;
+    // if op2 is non-null, do mult_op, otherwise nothing
+    AstNode *op2;
+};
+
+struct AstNodeCastExpr {
+    AstNode *primary_expr;
+    // if type is non-null, do cast, otherwise nothing
+    AstNode *type;
+};
+
+enum PrimaryExprType {
+    PrimaryExprTypeNumber,
+    PrimaryExprTypeString,
+    PrimaryExprTypeUnreachable,
+    PrimaryExprTypeFnCall,
+    PrimaryExprTypeGroupedExpr,
+    PrimaryExprTypeBlock,
+};
+
+struct AstNodePrimaryExpr {
+    PrimaryExprType type;
+    union {
+        Buf number;
+        Buf string;
+        AstNode *fn_call;
+        AstNode *grouped_expr;
+        AstNode *block;
+    } data;
+};
+
+struct AstNodeGroupedExpr {
+    AstNode *expr;
+};
+
 struct AstNode {
     enum NodeType type;
     AstNode *parent;
@@ -135,11 +248,22 @@ struct AstNode {
         AstNodeType type;
         AstNodeParamDecl param_decl;
         AstNodeBlock block;
-        AstNodeStatementReturn statement_return;
-        AstNodeExpression expression;
+        AstNodeReturnExpr return_expr;
+        AstNodeBoolOrExpr bool_or_expr;
         AstNodeFnCall fn_call;
         AstNodeExternBlock extern_block;
         AstNodeDirective directive;
+        AstNodeBoolAndExpr bool_and_expr;
+        AstNodeComparisonExpr comparison_expr;
+        AstNodeBinOrExpr bin_or_expr;
+        AstNodeBinXorExpr bin_xor_expr;
+        AstNodeBinAndExpr bin_and_expr;
+        AstNodeBitShiftExpr bit_shift_expr;
+        AstNodeAddExpr add_expr;
+        AstNodeMultExpr mult_expr;
+        AstNodeCastExpr cast_expr;
+        AstNodePrimaryExpr primary_expr;
+        AstNodeGroupedExpr grouped_expr;
     } data;
 };
 
src/tokenizer.cpp
@@ -98,6 +98,12 @@ enum TokenizeState {
     TokenizeStateMultiLineComment,
     TokenizeStateMultiLineCommentSlash,
     TokenizeStateMultiLineCommentStar,
+    TokenizeStatePipe,
+    TokenizeStateAmpersand,
+    TokenizeStateEq,
+    TokenizeStateBang,
+    TokenizeStateLessThan,
+    TokenizeStateGreaterThan,
 };
 
 
@@ -144,6 +150,11 @@ static void begin_token(Tokenize *t, TokenId id) {
     t->cur_tok = token;
 }
 
+static void cancel_token(Tokenize *t) {
+    t->tokens->pop();
+    t->cur_tok = nullptr;
+}
+
 static void end_token(Tokenize *t) {
     assert(t->cur_tok);
     t->cur_tok->end_pos = t->pos + 1;
@@ -167,6 +178,8 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordPub;
     } else if (mem_eql_str(token_mem, token_len, "export")) {
         t->cur_tok->id = TokenIdKeywordExport;
+    } else if (mem_eql_str(token_mem, token_len, "as")) {
+        t->cur_tok->id = TokenIdKeywordAs;
     }
 
     t->cur_tok = nullptr;
@@ -212,6 +225,10 @@ ZigList<Token> *tokenize(Buf *buf) {
                         begin_token(&t, TokenIdStar);
                         end_token(&t);
                         break;
+                    case '%':
+                        begin_token(&t, TokenIdPercent);
+                        end_token(&t);
+                        break;
                     case '{':
                         begin_token(&t, TokenIdLBrace);
                         end_token(&t);
@@ -240,24 +257,149 @@ ZigList<Token> *tokenize(Buf *buf) {
                         begin_token(&t, TokenIdNumberSign);
                         end_token(&t);
                         break;
+                    case '^':
+                        begin_token(&t, TokenIdBinXor);
+                        end_token(&t);
+                        break;
                     case '/':
+                        begin_token(&t, TokenIdSlash);
                         t.state = TokenizeStateSawSlash;
                         break;
+                    case '|':
+                        begin_token(&t, TokenIdBinOr);
+                        t.state = TokenizeStatePipe;
+                        break;
+                    case '&':
+                        begin_token(&t, TokenIdBinAnd);
+                        t.state = TokenizeStateAmpersand;
+                        break;
+                    case '=':
+                        begin_token(&t, TokenIdEq);
+                        t.state = TokenizeStateEq;
+                        break;
+                    case '!':
+                        begin_token(&t, TokenIdNot);
+                        t.state = TokenizeStateBang;
+                        break;
+                    case '<':
+                        begin_token(&t, TokenIdCmpLessThan);
+                        t.state = TokenizeStateLessThan;
+                        break;
+                    case '>':
+                        begin_token(&t, TokenIdCmpGreaterThan);
+                        t.state = TokenizeStateGreaterThan;
+                        break;
                     default:
                         tokenize_error(&t, "invalid character: '%c'", c);
                 }
                 break;
+            case TokenizeStateGreaterThan:
+                switch (c) {
+                    case '=':
+                        t.cur_tok->id = TokenIdCmpGreaterOrEq;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    case '>':
+                        t.cur_tok->id = TokenIdBitShiftRight;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        t.pos -= 1;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                }
+                break;
+            case TokenizeStateLessThan:
+                switch (c) {
+                    case '=':
+                        t.cur_tok->id = TokenIdCmpLessOrEq;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                    case '<':
+                        t.cur_tok->id = TokenIdBitShiftLeft;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        t.pos -= 1;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                }
+                break;
+            case TokenizeStateBang:
+                switch (c) {
+                    case '=':
+                        t.cur_tok->id = TokenIdCmpNotEq;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        t.pos -= 1;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                }
+                break;
+            case TokenizeStateEq:
+                switch (c) {
+                    case '=':
+                        t.cur_tok->id = TokenIdCmpEq;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        t.pos -= 1;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                }
+                break;
+            case TokenizeStateAmpersand:
+                switch (c) {
+                    case '&':
+                        t.cur_tok->id = TokenIdBoolAnd;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        t.pos -= 1;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                }
+                break;
+            case TokenizeStatePipe:
+                switch (c) {
+                    case '|':
+                        t.cur_tok->id = TokenIdBoolOr;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        t.pos -= 1;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                }
+                break;
             case TokenizeStateSawSlash:
                 switch (c) {
                     case '/':
+                        cancel_token(&t);
                         t.state = TokenizeStateLineComment;
                         break;
                     case '*':
+                        cancel_token(&t);
                         t.state = TokenizeStateMultiLineComment;
                         t.multi_line_comment_count = 1;
                         break;
                     default:
-                        tokenize_error(&t, "invalid character: '%c'", c);
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
                         break;
                 }
                 break;
@@ -371,16 +513,18 @@ ZigList<Token> *tokenize(Buf *buf) {
     switch (t.state) {
         case TokenizeStateStart:
             break;
-        case TokenizeStateSymbol:
-            end_token(&t);
-            break;
         case TokenizeStateString:
             tokenize_error(&t, "unterminated string");
             break;
+        case TokenizeStateSymbol:
         case TokenizeStateNumber:
-            end_token(&t);
-            break;
         case TokenizeStateSawDash:
+        case TokenizeStatePipe:
+        case TokenizeStateAmpersand:
+        case TokenizeStateEq:
+        case TokenizeStateBang:
+        case TokenizeStateLessThan:
+        case TokenizeStateGreaterThan:
             end_token(&t);
             break;
         case TokenizeStateSawSlash:
@@ -413,6 +557,7 @@ static const char * token_name(Token *token) {
         case TokenIdKeywordUnreachable: return "Unreachable";
         case TokenIdKeywordPub: return "Pub";
         case TokenIdKeywordExport: return "Export";
+        case TokenIdKeywordAs: return "As";
         case TokenIdLParen: return "LParen";
         case TokenIdRParen: return "RParen";
         case TokenIdComma: return "Comma";
@@ -427,6 +572,23 @@ static const char * token_name(Token *token) {
         case TokenIdArrow: return "Arrow";
         case TokenIdDash: return "Dash";
         case TokenIdNumberSign: return "NumberSign";
+        case TokenIdBinOr: return "BinOr";
+        case TokenIdBinAnd: return "BinAnd";
+        case TokenIdBinXor: return "BinXor";
+        case TokenIdBoolOr: return "BoolOr";
+        case TokenIdBoolAnd: return "BoolAnd";
+        case TokenIdEq: return "Eq";
+        case TokenIdNot: return "Not";
+        case TokenIdCmpEq: return "CmpEq";
+        case TokenIdCmpNotEq: return "CmpNotEq";
+        case TokenIdCmpLessThan: return "CmpLessThan";
+        case TokenIdCmpGreaterThan: return "CmpGreaterThan";
+        case TokenIdCmpLessOrEq: return "CmpLessOrEq";
+        case TokenIdCmpGreaterOrEq: return "CmpGreaterOrEq";
+        case TokenIdBitShiftLeft: return "BitShiftLeft";
+        case TokenIdBitShiftRight: return "BitShiftRight";
+        case TokenIdSlash: return "Slash";
+        case TokenIdPercent: return "Percent";
     }
     return "(invalid token)";
 }
src/tokenizer.hpp
@@ -21,6 +21,7 @@ enum TokenId {
     TokenIdKeywordUnreachable,
     TokenIdKeywordPub,
     TokenIdKeywordExport,
+    TokenIdKeywordAs,
     TokenIdLParen,
     TokenIdRParen,
     TokenIdComma,
@@ -35,6 +36,23 @@ enum TokenId {
     TokenIdArrow,
     TokenIdDash,
     TokenIdNumberSign,
+    TokenIdBoolOr,
+    TokenIdBoolAnd,
+    TokenIdBinOr,
+    TokenIdBinAnd,
+    TokenIdBinXor,
+    TokenIdEq,
+    TokenIdCmpEq,
+    TokenIdNot,
+    TokenIdCmpNotEq,
+    TokenIdCmpLessThan,
+    TokenIdCmpGreaterThan,
+    TokenIdCmpLessOrEq,
+    TokenIdCmpGreaterOrEq,
+    TokenIdBitShiftLeft,
+    TokenIdBitShiftRight,
+    TokenIdSlash,
+    TokenIdPercent,
 };
 
 struct Token {
README.md
@@ -30,6 +30,16 @@ readable, safe, optimal, and concise code to solve any computing problem.
    This mode should automatically provide test coverage.
  * Memory zeroed by default, unless you initialize with "uninitialized".
 
+### Building
+
+```
+mkdir build
+cd build
+cmake ..
+make
+./run_tests
+```
+
 ## Roadmap
 
  * Math expression
@@ -93,25 +103,63 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
 
 Block : token(LBrace) many(Statement) token(RBrace)
 
-Statement : ExpressionStatement | ReturnStatement
+Statement : ExpressionStatement
 
 ExpressionStatement : Expression token(Semicolon)
 
-ReturnStatement : token(Return) option(Expression) token(Semicolon)
+Expression : BoolOrExpression | ReturnExpression
+
+BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
+
+ReturnExpression : token(Return) option(Expression)
+
+BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
+
+ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
+
+ComparisonOperator : token(BoolEq) | token(BoolNotEq) | token(BoolLessThan) | token(BoolGreaterThan) | token(BoolLessEqual) | token(BoolGreaterEqual)
+
+BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryXorExpression | BinaryXorExpression
 
-Expression : token(Number) | token(String) | token(Unreachable) | FnCall
+BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryAndExpression | BinaryAndExpression
+
+BinaryAndExpression : BitShiftExpression token(BinAnd) BitShiftExpression | BitShiftExpression
+
+BitShiftExpression : AdditionExpression BitShiftOperator AdditionExpression | AdditionExpression
+
+BitShiftOperator : token(BitShiftLeft | token(BitShiftRight)
+
+AdditionExpression : MultiplyExpression AdditionOperator MultiplyExpression | MultiplyExpression
+
+AdditionOperator : token(Plus) | token(Minus)
+
+MultiplyExpression : CastExpression MultiplyOperator CastExpression | CastExpression
+
+MultiplyOperator : token(Star) | token(Slash) | token(Percent)
+
+CastExpression : PrimaryExpression token(as) Type | PrimaryExpression
+
+PrimaryExpression : token(Number) | token(String) | token(Unreachable) | FnCall | GroupedExpression | Block
+
+GroupedExpression : token(LParen) Expression token(RParen)
 
 FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen)
 
 Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
 ```
 
-### Building
+### Binary Operator Precedence
 
 ```
-mkdir build
-cd build
-cmake ..
-make
-./run_tests
+as
+* / %
++ -
+<< >>
+&
+^
+|
+== != < > <= >=
+&&
+||
+=
 ```