Commit 64dd0b8d95

Josh Wolfe <thejoshwolfe@gmail.com>
2015-12-13 01:03:44
fix a + b + c and similar
fix || and && closes #17
1 parent 38f12ad
doc/langref.md
@@ -92,7 +92,7 @@ AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrEx
 
 BlockExpression : IfExpression | Block
 
-BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
+BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExpression
 
 ReturnExpression : token(Return) option(Expression)
 
@@ -102,33 +102,33 @@ ElseIf : token(Else) IfExpression
 
 Else : token(Else) Block
 
-BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
+BoolAndExpression : ComparisonExpression token(BoolAnd) BoolAndExpression | 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
+BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryOrExpression | BinaryXorExpression
 
-BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryAndExpression | BinaryAndExpression
+BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryXorExpression | BinaryAndExpression
 
-BinaryAndExpression : BitShiftExpression token(BinAnd) BitShiftExpression | BitShiftExpression
+BinaryAndExpression : BitShiftExpression token(BinAnd) BinaryAndExpression | BitShiftExpression
 
-BitShiftExpression : AdditionExpression BitShiftOperator AdditionExpression | AdditionExpression
+BitShiftExpression : AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression
 
-BitShiftOperator : token(BitShiftLeft | token(BitShiftRight)
+BitShiftOperator : token(BitShiftLeft) | token(BitShiftRight)
 
-AdditionExpression : MultiplyExpression AdditionOperator MultiplyExpression | MultiplyExpression
+AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression
 
 AdditionOperator : token(Plus) | token(Minus)
 
-MultiplyExpression : CastExpression MultiplyOperator CastExpression | CastExpression
+MultiplyExpression : CastExpression MultiplyOperator MultiplyExpression | CastExpression
 
 MultiplyOperator : token(Star) | token(Slash) | token(Percent)
 
-CastExpression : PrefixOpExpression token(as) Type | PrefixOpExpression
+CastExpression : CastExpression token(as) Type | PrefixOpExpression
 
-PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression
+PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
 
 SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression)
 
src/analyze.cpp
@@ -894,20 +894,24 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                         return_type = g->builtin_types.entry_bool;
                         break;
                     case BinOpTypeBinOr:
-                        zig_panic("TODO bin or type");
-                        break;
                     case BinOpTypeBinXor:
-                        zig_panic("TODO bin xor type");
-                        break;
                     case BinOpTypeBinAnd:
-                        zig_panic("TODO bin and type");
-                        break;
+                        {
+                            // TODO: don't require i32
+                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+                            return_type = g->builtin_types.entry_i32;
+                            break;
+                        }
                     case BinOpTypeBitShiftLeft:
-                        zig_panic("TODO bit shift left type");
-                        break;
                     case BinOpTypeBitShiftRight:
-                        zig_panic("TODO bit shift right type");
-                        break;
+                        {
+                            // TODO: don't require i32
+                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+                            return_type = g->builtin_types.entry_i32;
+                            break;
+                        }
                     case BinOpTypeAdd:
                     case BinOpTypeSub:
                         // TODO think how should type checking for these work?
@@ -918,14 +922,15 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                         return_type = g->builtin_types.entry_i32;
                         break;
                     case BinOpTypeMult:
-                        zig_panic("TODO mult type");
-                        break;
                     case BinOpTypeDiv:
-                        zig_panic("TODO div type");
-                        break;
                     case BinOpTypeMod:
-                        zig_panic("TODO modulus type");
-                        break;
+                        {
+                            // TODO: don't require i32
+                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+                            return_type = g->builtin_types.entry_i32;
+                            break;
+                        }
                     case BinOpTypeInvalid:
                         zig_unreachable();
                 }
@@ -1081,11 +1086,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                     return_type = g->builtin_types.entry_bool;
                     break;
                 case PrefixOpBinNot:
-                    zig_panic("TODO type check bin not");
-                    break;
+                    {
+                        // TODO: don't require i32
+                        analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.prefix_op_expr.primary_expr);
+                        return_type = g->builtin_types.entry_i32;
+                        break;
+                    }
                 case PrefixOpNegation:
-                    zig_panic("TODO type check negation");
-                    break;
+                    {
+                        // TODO: don't require i32
+                        analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.prefix_op_expr.primary_expr);
+                        return_type = g->builtin_types.entry_i32;
+                        break;
+                    }
                 case PrefixOpInvalid:
                     zig_unreachable();
             }
src/codegen.cpp
@@ -485,22 +485,18 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
     // block for when val1 == false (don't even evaluate the second part)
     LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "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);
+    LLVMBuildCondBr(g->builder, val1, true_block, false_block);
 
     LLVMPositionBuilderAtEnd(g->builder, true_block);
     LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
     add_debug_source_node(g, node);
-    LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
     LLVMBuildBr(g->builder, false_block);
 
     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};
+    LLVMValueRef incoming_values[2] = {val1, val2};
     LLVMBasicBlockRef incoming_blocks[2] = {orig_block, true_block};
     LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
 
@@ -519,22 +515,18 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
     // block for when val1 == true (don't even evaluate the second part)
     LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "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);
+    LLVMBuildCondBr(g->builder, val1, true_block, false_block);
 
     LLVMPositionBuilderAtEnd(g->builder, false_block);
     LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
     add_debug_source_node(g, expr_node);
-    LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
     LLVMBuildBr(g->builder, true_block);
 
     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};
+    LLVMValueRef incoming_values[2] = {val1, val2};
     LLVMBasicBlockRef incoming_blocks[2] = {orig_block, false_block};
     LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
 
src/parser.cpp
@@ -944,7 +944,7 @@ static PrefixOp ast_parse_prefix_op(ParseContext *pc, int *token_index, bool man
 }
 
 /*
-PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression
+PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
 */
 static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -952,7 +952,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo
     if (prefix_op == PrefixOpInvalid)
         return ast_parse_suffix_op_expr(pc, token_index, mandatory);
 
-    AstNode *prefix_op_expr = ast_parse_suffix_op_expr(pc, token_index, true);
+    AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, true);
     AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
     node->data.prefix_op_expr.primary_expr = prefix_op_expr;
     node->data.prefix_op_expr.prefix_op = prefix_op;
@@ -962,24 +962,26 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo
 
 
 /*
-CastExpression : PrefixOpExpression token(as) Type | PrefixOpExpression
+CastExpression : CastExpression token(as) Type | PrefixOpExpression
 */
 static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bool mandatory) {
-    AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory);
-    if (!prefix_op_expr)
+    AstNode *operand_1 = ast_parse_prefix_op_expr(pc, token_index, mandatory);
+    if (!operand_1)
         return nullptr;
 
-    Token *as_kw = &pc->tokens->at(*token_index);
-    if (as_kw->id != TokenIdKeywordAs)
-        return prefix_op_expr;
-    *token_index += 1;
+    while (true) {
+        Token *as_kw = &pc->tokens->at(*token_index);
+        if (as_kw->id != TokenIdKeywordAs)
+            return operand_1;
+        *token_index += 1;
 
-    AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw);
-    node->data.cast_expr.expr = prefix_op_expr;
+        AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw);
+        node->data.cast_expr.expr = operand_1;
 
-    node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index);
+        node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index);
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 static BinOpType tok_to_mult_op(Token *token) {
@@ -1009,26 +1011,28 @@ static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mand
 }
 
 /*
-MultiplyExpression : CastExpression MultiplyOperator CastExpression | CastExpression
+MultiplyExpression : CastExpression MultiplyOperator MultiplyExpression | 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);
-    BinOpType mult_op = ast_parse_mult_op(pc, token_index, false);
-    if (mult_op == BinOpTypeInvalid)
-        return operand_1;
+    while (true) {
+        Token *token = &pc->tokens->at(*token_index);
+        BinOpType mult_op = ast_parse_mult_op(pc, token_index, false);
+        if (mult_op == BinOpTypeInvalid)
+            return operand_1;
 
-    AstNode *operand_2 = ast_parse_cast_expression(pc, token_index, true);
+        AstNode *operand_2 = ast_parse_cast_expression(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = mult_op;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = mult_op;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 static BinOpType tok_to_add_op(Token *token) {
@@ -1057,26 +1061,28 @@ static BinOpType ast_parse_add_op(ParseContext *pc, int *token_index, bool manda
 }
 
 /*
-AdditionExpression : MultiplyExpression AdditionOperator MultiplyExpression | MultiplyExpression
+AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | 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);
-    BinOpType add_op = ast_parse_add_op(pc, token_index, false);
-    if (add_op == BinOpTypeInvalid)
-        return operand_1;
+    while (true) {
+        Token *token = &pc->tokens->at(*token_index);
+        BinOpType add_op = ast_parse_add_op(pc, token_index, false);
+        if (add_op == BinOpTypeInvalid)
+            return operand_1;
 
-    AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true);
+        AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = add_op;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = add_op;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 static BinOpType tok_to_bit_shift_op(Token *token) {
@@ -1088,7 +1094,7 @@ static BinOpType tok_to_bit_shift_op(Token *token) {
 }
 
 /*
-BitShiftOperator : token(BitShiftLeft | token(BitShiftRight)
+BitShiftOperator : token(BitShiftLeft) | token(BitShiftRight)
 */
 static BinOpType ast_parse_bit_shift_op(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -1105,96 +1111,104 @@ static BinOpType ast_parse_bit_shift_op(ParseContext *pc, int *token_index, bool
 }
 
 /*
-BitShiftExpression : AdditionExpression BitShiftOperator AdditionExpression | AdditionExpression
+BitShiftExpression : AdditionExpression BitShiftOperator BitShiftExpression | 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);
-    BinOpType bit_shift_op = ast_parse_bit_shift_op(pc, token_index, false);
-    if (bit_shift_op == BinOpTypeInvalid)
-        return operand_1;
+    while (true) {
+        Token *token = &pc->tokens->at(*token_index);
+        BinOpType bit_shift_op = ast_parse_bit_shift_op(pc, token_index, false);
+        if (bit_shift_op == BinOpTypeInvalid)
+            return operand_1;
 
-    AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true);
+        AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = bit_shift_op;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = bit_shift_op;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 
 /*
-BinaryAndExpression : BitShiftExpression token(BinAnd) BitShiftExpression | BitShiftExpression
+BinaryAndExpression : BitShiftExpression token(BinAnd) BinaryAndExpression | 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;
+    while (true) {
+        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);
+        AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = BinOpTypeBinAnd;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = BinOpTypeBinAnd;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 /*
-BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryAndExpression | BinaryAndExpression
+BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryXorExpression | BinaryAndExpression
 */
 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 *token = &pc->tokens->at(*token_index);
-    if (token->id != TokenIdBinXor)
-        return operand_1;
-    *token_index += 1;
+    while (true) {
+        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 *operand_2 = ast_parse_bin_and_expr(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = BinOpTypeBinXor;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = BinOpTypeBinXor;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 /*
-BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryXorExpression | BinaryXorExpression
+BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryOrExpression | 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;
+    while (true) {
+        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 *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = BinOpTypeBinOr;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = BinOpTypeBinOr;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 static BinOpType tok_to_cmp_op(Token *token) {
@@ -1247,26 +1261,28 @@ static AstNode *ast_parse_comparison_expr(ParseContext *pc, int *token_index, bo
 }
 
 /*
-BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
+BoolAndExpression : ComparisonExpression token(BoolAnd) BoolAndExpression | 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;
+    while (true) {
+        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 *operand_2 = ast_parse_comparison_expr(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 /*
@@ -1382,26 +1398,28 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
 }
 
 /*
-BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
+BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | 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;
+    while (true) {
+        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 *operand_2 = ast_parse_bool_and_expr(pc, token_index, true);
 
-    AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
-    node->data.bin_op_expr.op1 = operand_1;
-    node->data.bin_op_expr.bin_op = BinOpTypeBoolOr;
-    node->data.bin_op_expr.op2 = operand_2;
+        AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+        node->data.bin_op_expr.op1 = operand_1;
+        node->data.bin_op_expr.bin_op = BinOpTypeBoolOr;
+        node->data.bin_op_expr.op2 = operand_2;
 
-    return node;
+        operand_1 = node;
+    }
 }
 
 /*
src/tokenizer.cpp
@@ -478,9 +478,10 @@ void tokenize(Buf *buf, Tokenization *out) {
                         t.multi_line_comment_count = 1;
                         break;
                     default:
+                        t.pos -= 1;
                         end_token(&t);
                         t.state = TokenizeStateStart;
-                        break;
+                        continue;
                 }
                 break;
             case TokenizeStateLineComment:
@@ -592,9 +593,10 @@ void tokenize(Buf *buf, Tokenization *out) {
                         t.state = TokenizeStateStart;
                         break;
                     default:
+                        t.pos -= 1;
                         end_token(&t);
                         t.state = TokenizeStateStart;
-                        break;
+                        continue;
                 }
                 break;
         }
test/run_tests.cpp
@@ -409,6 +409,27 @@ export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
     )SOURCE", "Hello, world!\n");
 
 
+    add_simple_case("a + b + c", R"SOURCE(
+use "std.zig";
+
+export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
+    if false || false || false { print_str("BAD 1\n" as string); }
+    if true && true && false   { print_str("BAD 2\n" as string); }
+    if 1 | 2 | 4 != 7          { print_str("BAD 3\n" as string); }
+    if 3 ^ 6 ^ 8 != 13         { print_str("BAD 4\n" as string); }
+    if 7 & 14 & 28 != 4        { print_str("BAD 5\n" as string); }
+    if 9  << 1 << 2 != 9  << 3 { print_str("BAD 6\n" as string); }
+    if 90 >> 1 >> 2 != 90 >> 3 { print_str("BAD 7\n" as string); }
+    if 100 - 1 + 1000 != 1099  { print_str("BAD 8\n" as string); }
+    if 5 * 4 / 2 % 3 != 1      { print_str("BAD 9\n" as string); }
+    if 5 as i32 as i32 != 5    { print_str("BAD 10\n" as string); }
+    if !!false                 { print_str("BAD 11\n" as string); }
+    if 7 != --7                { print_str("BAD 12\n" as string); }
+
+    print_str("OK\n" as string);
+    return 0;
+}
+    )SOURCE", "OK\n");
 
 }