private Operand generateBoolExpression(BinaryOperationNode node)
            throws IRException {

        try {
            if (node.is(BinaryOperationNode.Operation.Bool)) {
                Label rightCond = new Label();
                Label trueL = new Label();
                Label falseL = new Label();
                Label next = new Label();

                switch(node.operation()) {
                    /**
                     * a && b раскрывает в:
                     *
                     *        tmp1 = generate(a);
                     *        tmp2 = generate(b);
                     *
                     *        tmp3 = NOT tmp1;
                     *        tmp4 = NOT tmp2;
                     *
                     *        if(tmp3)
                     *          goto false;
                     *        else
                     *          goto cond;
                     * cond:  if(tmp4)
                     *          goto false;
                     *        else
                     *          goto true;
                     * true:  tmp5 = TRUE;
                     *        goto next;
                     * false: tmp5 = FALSE;
                     * next:  usage of tmp5
                     */
                    case BOOL_AND:
                    {
                        Operand left = generate(node.leftChild());   // tmp1
                        Operand right = generate(node.rightChild()); // tmp2

                        // tmp3
                        TmpVariableOperand first =
                            new TmpVariableOperand(
                                new PrimitiveType(
                                    PrimitiveType.Type.BOOL,
                                    true
                                ),
                                varTable
                            );

                        // tmp4
                        TmpVariableOperand second =
                            new TmpVariableOperand(
                                new PrimitiveType(
                                    PrimitiveType.Type.BOOL,
                                    true
                                ),
                                varTable
                            );

                        // tmp3 = !a
                        code.addStatement(
                            new UnaryOperationStatement(
                                first,
                                left,
                                UnaryOperationStatement.Operation.NOT)
                        );
                        // tmp4 = !b
                        code.addStatement(
                            new UnaryOperationStatement(
                                second,
                                right,
                                UnaryOperationStatement.Operation.NOT)
                        );

                        // !a ? goto false : goto nextCondition
                        code.addStatement(
                            new IfGoToStatement(first, falseL, rightCond)
                        );
                        // !b ? goto false : goto true
                        rightCond.setIndex(code.nextIndex());
                        code.addStatement(
                            new IfGoToStatement(second, falseL, trueL)
                        );

                        // tmp5
                        TmpVariableOperand result =
                            new TmpVariableOperand(
                                new PrimitiveType(PrimitiveType.Type.BOOL, true),
                                varTable
                            );

                        // trueL:
                        trueL.setIndex(code.nextIndex());

                        // trueL: tmp5 = true
                        code.addStatement(
                            new AssignmentStatement(
                                result,
                                new ConstantOperand(
                                    new PrimitiveType(PrimitiveType.Type.BOOL),
                                    Boolean.TRUE
                                )
                            )
                        );

                        // goto next
                        code.addStatement(new GoToStatement(next));

                        // falseL:
                        falseL.setIndex(code.nextIndex());

                        // falseL: tmp5 = false
                        code.addStatement(
                            new AssignmentStatement(
                                result,
                                new ConstantOperand(
                                    new PrimitiveType(PrimitiveType.Type.BOOL),
                                    Boolean.FALSE
                                )
                            )
                        );

                        // next:
                        next.setIndex(code.nextIndex());

                        return result;
                    }
                    case BOOL_OR:
                    {
                        Operand left = generate(node.leftChild());
                        Operand right = generate(node.rightChild());

                        code.addStatement(
                            new IfGoToStatement(left, trueL, rightCond)
                        );
                        rightCond.setIndex(code.nextIndex());
                        code.addStatement(
                            new IfGoToStatement(right, trueL, falseL)
                        );

                        TmpVariableOperand result =
                            new TmpVariableOperand(
                                new PrimitiveType(PrimitiveType.Type.BOOL, true),
                                varTable
                            );

                        trueL.setIndex(code.nextIndex());

                        code.addStatement(
                            new AssignmentStatement(
                                result,
                                new ConstantOperand(
                                    new PrimitiveType(PrimitiveType.Type.BOOL),
                                    Boolean.TRUE
                                )
                            )
                        );

                        code.addStatement(new GoToStatement(next));

                        falseL.setIndex(code.nextIndex());

                        code.addStatement(
                            new AssignmentStatement(
                                result,
                                new ConstantOperand(
                                    new PrimitiveType(PrimitiveType.Type.BOOL),
                                    Boolean.FALSE
                                )
                            )
                        );

                        next.setIndex(code.nextIndex());

                        return result;
                    }
                    default:
                        throw new UnexpectedOperationException()
                                .initPosition(node.dInfo.position)
                                .Log("ru.bmstu.iu9.compiler.ir");
                }
            } else {
                throw new UnexpectedOperationException()
                        .initPosition(node.dInfo.position)
                        .Log("ru.bmstu.iu9.compiler.ir");
            }
        } catch(IRException ex) {
            throw ex;
        } catch(Exception ex) {
            throw (NonGenerationException)new NonGenerationException(ex)
                    .Log("ru.bmstu.iu9.compiler.ir");
        }
    }