Edit

kc3-lang/angle/src/compiler/OutputGLSL.cpp

Branch :

  • Show log

    Commit

  • Author : alokp@chromium.org
    Date : 2010-08-09 22:31:36
    Hash : 434fdf21
    Message : Fix warnings about unused variables. Submitted by timeless. BUG=15 Review URL: http://codereview.appspot.com/1916046 git-svn-id: https://angleproject.googlecode.com/svn/trunk@379 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/compiler/OutputGLSL.cpp
  • //
    // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    #include "compiler/OutputGLSL.h"
    #include "compiler/debug.h"
    
    namespace
    {
    TString getTypeName(const TType& type)
    {
        TInfoSinkBase out;
        if (type.isMatrix())
        {
            out << "mat";
            out << type.getNominalSize();
        }
        else if (type.isVector())
        {
            switch (type.getBasicType())
            {
                case EbtFloat: out << "vec"; break;
                case EbtInt: out << "ivec"; break;
                case EbtBool: out << "bvec"; break;
                default: UNREACHABLE(); break;
            }
            out << type.getNominalSize();
        }
        else
        {
            if (type.getBasicType() == EbtStruct)
                out << type.getTypeName();
            else
                out << type.getBasicString();
        }
        return TString(out.c_str());
    }
    
    TString arrayBrackets(const TType& type)
    {
        ASSERT(type.isArray());
        TInfoSinkBase out;
        out << "[" << type.getArraySize() << "]";
        return TString(out.c_str());
    }
    
    bool isSingleStatement(TIntermNode* node) {
        if (const TIntermAggregate* aggregate = node->getAsAggregate())
        {
            return (aggregate->getOp() != EOpFunction) &&
                   (aggregate->getOp() != EOpSequence);
        }
        else if (const TIntermSelection* selection = node->getAsSelectionNode())
        {
            // Ternary operators are usually part of an assignment operator.
            // This handles those rare cases in which they are all by themselves.
            return selection->usesTernaryOperator();
        }
        else if (node->getAsLoopNode())
        {
            return false;
        }
        return true;
    }
    }  // namespace
    
    TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink)
        : TIntermTraverser(true, true, true),
          mObjSink(objSink),
          mDeclaringVariables(false)
    {
    }
    
    void TOutputGLSL::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
    {
        TInfoSinkBase& out = objSink();
        if (visit == PreVisit && preStr)
        {
            out << preStr;
        }
        else if (visit == InVisit && inStr)
        {
            out << inStr;
        }
        else if (visit == PostVisit && postStr)
        {
            out << postStr;
        }
    }
    
    void TOutputGLSL::writeVariableType(const TType& type)
    {
        TInfoSinkBase& out = objSink();
        TQualifier qualifier = type.getQualifier();
        // TODO(alokp): Validate qualifier for variable declarations.
        if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
            out << type.getQualifierString() << " ";
    
        // Declare the struct if we have not done so already.
        if ((type.getBasicType() == EbtStruct) &&
            (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end()))
        {
            out << "struct " << type.getTypeName() << "{\n";
            const TTypeList* structure = type.getStruct();
            ASSERT(structure != NULL);
            for (size_t i = 0; i < structure->size(); ++i)
            {
                const TType* fieldType = (*structure)[i].type;
                ASSERT(fieldType != NULL);
                out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
                if (fieldType->isArray())
                    out << arrayBrackets(*fieldType);
                out << ";\n";
            }
            out << "}";
            mDeclaredStructs.insert(type.getTypeName());
        }
        else
        {
            out << getTypeName(type);
        }
    }
    
    void TOutputGLSL::writeFunctionParameters(const TIntermSequence& args)
    {
        TInfoSinkBase& out = objSink();
        for (TIntermSequence::const_iterator iter = args.begin();
             iter != args.end(); ++iter)
        {
            const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
            ASSERT(arg != NULL);
    
            const TType& type = arg->getType();
            TQualifier qualifier = type.getQualifier();
            // TODO(alokp): Validate qualifier for function arguments.
            if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
                out << type.getQualifierString() << " ";
    
            out << getTypeName(type);
    
            const TString& name = arg->getSymbol();
            if (!name.empty())
                out << " " << name;
            if (type.isArray())
                out << arrayBrackets(type);
    
            // Put a comma if this is not the last argument.
            if (iter != args.end() - 1)
                out << ", ";
        }
    }
    
    const ConstantUnion* TOutputGLSL::writeConstantUnion(const TType& type,
                                                         const ConstantUnion* pConstUnion)
    {
        TInfoSinkBase& out = objSink();
    
        if (type.getBasicType() == EbtStruct)
        {
            out << type.getTypeName() << "(";
            const TTypeList* structure = type.getStruct();
            ASSERT(structure != NULL);
            for (size_t i = 0; i < structure->size(); ++i)
            {
                const TType* fieldType = (*structure)[i].type;
                ASSERT(fieldType != NULL);
                pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
                if (i != structure->size() - 1) out << ", ";
            }
            out << ")";
        }
        else
        {
            int size = type.getObjectSize();
            bool writeType = size > 1;
            if (writeType) out << getTypeName(type) << "(";
            for (int i = 0; i < size; ++i, ++pConstUnion)
            {
                switch (pConstUnion->getType())
                {
                    case EbtFloat: out << pConstUnion->getFConst(); break;
                    case EbtInt: out << pConstUnion->getIConst(); break;
                    case EbtBool: out << pConstUnion->getBConst(); break;
                    default: UNREACHABLE();
                }
                if (i != size - 1) out << ", ";
            }
            if (writeType) out << ")";
        }
        return pConstUnion;
    }
    
    void TOutputGLSL::visitSymbol(TIntermSymbol* node)
    {
        TInfoSinkBase& out = objSink();
        out << node->getSymbol();
    
        if (mDeclaringVariables && node->getType().isArray())
            out << arrayBrackets(node->getType());
    }
    
    void TOutputGLSL::visitConstantUnion(TIntermConstantUnion* node)
    {
        writeConstantUnion(node->getType(), node->getUnionArrayPointer());
    }
    
    bool TOutputGLSL::visitBinary(Visit visit, TIntermBinary* node)
    {
        bool visitChildren = true;
        TInfoSinkBase& out = objSink();
        switch (node->getOp())
        {
            case EOpInitialize:
                if (visit == InVisit)
                {
                    out << " = ";
                    // RHS of initialize is not being declared.
                    mDeclaringVariables = false;
                }
                break;
            case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
            case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
            case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
            case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
            // Notice the fall-through.
            case EOpMulAssign: 
            case EOpVectorTimesMatrixAssign:
            case EOpVectorTimesScalarAssign:
            case EOpMatrixTimesScalarAssign:
            case EOpMatrixTimesMatrixAssign:
                writeTriplet(visit, "(", " *= ", ")");
                break;
    
            case EOpIndexDirect:
            case EOpIndexIndirect:
                writeTriplet(visit, NULL, "[", "]");
                break;
            case EOpIndexDirectStruct:
                if (visit == InVisit)
                {
                    out << ".";
                    // TODO(alokp): ASSERT
                    out << node->getType().getFieldName();
                    visitChildren = false;
                }
                break;
            case EOpVectorSwizzle:
                if (visit == InVisit)
                {
                    out << ".";
                    TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
                    TIntermSequence& sequence = rightChild->getSequence();
                    for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
                    {
                        TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
                        ASSERT(element->getBasicType() == EbtInt);
                        ASSERT(element->getNominalSize() == 1);
                        const ConstantUnion& data = element->getUnionArrayPointer()[0];
                        ASSERT(data.getType() == EbtInt);
                        switch (data.getIConst())
                        {
                            case 0: out << "x"; break;
                            case 1: out << "y"; break;
                            case 2: out << "z"; break;
                            case 3: out << "w"; break;
                            default: UNREACHABLE(); break;
                        }
                    }
                    visitChildren = false;
                }
                break;
    
            case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
            case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
            case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
            case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
            case EOpMod: UNIMPLEMENTED(); break;
            case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
            case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
            case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
            case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
            case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
            case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
    
            // Notice the fall-through.
            case EOpVectorTimesScalar:
            case EOpVectorTimesMatrix:
            case EOpMatrixTimesVector:
            case EOpMatrixTimesScalar:
            case EOpMatrixTimesMatrix:
                writeTriplet(visit, "(", " * ", ")");
                break;
    
            case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
            case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
            case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
            default: UNREACHABLE(); break;
        }
    
        return visitChildren;
    }
    
    bool TOutputGLSL::visitUnary(Visit visit, TIntermUnary* node)
    {
        switch (node->getOp())
        {
            case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
            case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
            case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
    
            case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
            case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
            case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
            case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
    
            case EOpConvIntToBool:
            case EOpConvFloatToBool:
                switch (node->getOperand()->getType().getNominalSize())
                {
                    case 1: writeTriplet(visit, "bool(", NULL, ")");  break;
                    case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
                    case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
                    case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
                    default: UNREACHABLE();
                }
                break;
            case EOpConvBoolToFloat:
            case EOpConvIntToFloat:
                switch (node->getOperand()->getType().getNominalSize())
                {
                    case 1: writeTriplet(visit, "float(", NULL, ")");  break;
                    case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
                    case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
                    case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
                    default: UNREACHABLE();
                }
                break;
            case EOpConvFloatToInt:
            case EOpConvBoolToInt:
                switch (node->getOperand()->getType().getNominalSize())
                {
                    case 1: writeTriplet(visit, "int(", NULL, ")");  break;
                    case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
                    case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
                    case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
                    default: UNREACHABLE();
                }
                break;
    
            case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
            case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
            case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
            case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
            case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
            case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
            case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
            case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
    
            case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
            case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
            case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
            case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
            case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
            case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
    
            case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
            case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
            case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
            case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
            case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
    
            case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
            case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
    
            case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
            case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
    
            default: UNREACHABLE(); break;
        }
    
        return true;
    }
    
    bool TOutputGLSL::visitSelection(Visit visit, TIntermSelection* node)
    {
        TInfoSinkBase& out = objSink();
    
        if (node->usesTernaryOperator())
        {
            // Notice two brackets at the beginning and end. The outer ones
            // encapsulate the whole ternary expression. This preserves the
            // order of precedence when ternary expressions are used in a
            // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
            out << "((";
            node->getCondition()->traverse(this);
            out << ") ? (";
            node->getTrueBlock()->traverse(this);
            out << ") : (";
            node->getFalseBlock()->traverse(this);
            out << "))";
        }
        else
        {
            out << "if (";
            node->getCondition()->traverse(this);
            out << ")\n";
    
            incrementDepth();
            visitCodeBlock(node->getTrueBlock());
    
            if (node->getFalseBlock())
            {
                out << "else\n";
                visitCodeBlock(node->getFalseBlock());
            }
            decrementDepth();
        }
        return false;
    }
    
    bool TOutputGLSL::visitAggregate(Visit visit, TIntermAggregate* node)
    {
        bool visitChildren = true;
        TInfoSinkBase& out = objSink();
        switch (node->getOp())
        {
            case EOpSequence: {
                // Scope the sequences except when at the global scope.
                if (depth > 0) out << "{\n";
    
                incrementDepth();
                const TIntermSequence& sequence = node->getSequence();
                for (TIntermSequence::const_iterator iter = sequence.begin();
                     iter != sequence.end(); ++iter)
                {
                    TIntermNode* node = *iter;
                    ASSERT(node != NULL);
                    node->traverse(this);
    
                    if (isSingleStatement(node))
                        out << ";\n";
                }
                decrementDepth();
    
                // Scope the sequences except when at the global scope.
                if (depth > 0) out << "}\n";
                visitChildren = false;
                break;
            }
            case EOpPrototype: {
                // Function declaration.
                ASSERT(visit == PreVisit);
                TString returnType = getTypeName(node->getType());
                out << returnType << " " << node->getName();
    
                out << "(";
                writeFunctionParameters(node->getSequence());
                out << ")";
    
                visitChildren = false;
                break;
            }
            case EOpFunction: {
                // Function definition.
                ASSERT(visit == PreVisit);
                TString returnType = getTypeName(node->getType());
                TString functionName = TFunction::unmangleName(node->getName());
                out << returnType << " " << functionName;
    
                incrementDepth();
                // Function definition node contains one or two children nodes
                // representing function parameters and function body. The latter
                // is not present in case of empty function bodies.
                const TIntermSequence& sequence = node->getSequence();
                ASSERT((sequence.size() == 1) || (sequence.size() == 2));
                TIntermSequence::const_iterator seqIter = sequence.begin();
    
                // Traverse function parameters.
                TIntermAggregate* params = (*seqIter)->getAsAggregate();
                ASSERT(params != NULL);
                ASSERT(params->getOp() == EOpParameters);
                params->traverse(this);
    
                // Traverse function body.
                TIntermAggregate* body = ++seqIter != sequence.end() ?
                    (*seqIter)->getAsAggregate() : NULL;
                visitCodeBlock(body);
                decrementDepth();
     
                // Fully processed; no need to visit children.
                visitChildren = false;
                break;
            }
            case EOpFunctionCall:
                // Function call.
                if (visit == PreVisit)
                {
                    TString functionName = TFunction::unmangleName(node->getName());
                    out << functionName << "(";
                }
                else if (visit == InVisit)
                {
                    out << ", ";
                }
                else
                {
                    out << ")";
                }
                break;
            case EOpParameters: {
                // Function parameters.
                ASSERT(visit == PreVisit);
                out << "(";
                writeFunctionParameters(node->getSequence());
                out << ")";
                visitChildren = false;
                break;
            }
            case EOpDeclaration: {
                // Variable declaration.
                if (visit == PreVisit)
                {
                    const TIntermSequence& sequence = node->getSequence();
                    const TIntermTyped* variable = sequence.front()->getAsTyped();
                    writeVariableType(variable->getType());
                    out << " ";
                    mDeclaringVariables = true;
                }
                else if (visit == InVisit)
                {
                    out << ", ";
                    mDeclaringVariables = true;
                }
                else
                {
                    mDeclaringVariables = false;
                }
                break;
            }
            case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break;
            case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
            case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
            case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
            case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break;
            case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break;
            case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break;
            case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break;
            case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break;
            case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break;
            case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break;
            case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break;
            case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break;
            case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break;
            case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
            case EOpConstructStruct:
                if (visit == PreVisit)
                {
                    const TType& type = node->getType();
                    ASSERT(type.getBasicType() == EbtStruct);
                    out << type.getTypeName() << "(";
                }
                else if (visit == InVisit)
                {
                    out << ", ";
                }
                else
                {
                    out << ")";
                }
                break;
    
            case EOpLessThan: writeTriplet(visit, "lessThan(", ", ", ")"); break;
            case EOpGreaterThan: writeTriplet(visit, "greaterThan(", ", ", ")"); break;
            case EOpLessThanEqual: writeTriplet(visit, "lessThanEqual(", ", ", ")"); break;
            case EOpGreaterThanEqual: writeTriplet(visit, "greaterThanEqual(", ", ", ")"); break;
            case EOpVectorEqual: writeTriplet(visit, "equal(", ", ", ")"); break;
            case EOpVectorNotEqual: writeTriplet(visit, "notEqual(", ", ", ")"); break;
            case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break;
    
            case EOpMod: writeTriplet(visit, "mod(", ", ", ")"); break;
            case EOpPow: writeTriplet(visit, "pow(", ", ", ")"); break;
            case EOpAtan: writeTriplet(visit, "atan(", ", ", ")"); break;
            case EOpMin: writeTriplet(visit, "min(", ", ", ")"); break;
            case EOpMax: writeTriplet(visit, "max(", ", ", ")"); break;
            case EOpClamp: writeTriplet(visit, "clamp(", ", ", ")"); break;
            case EOpMix: writeTriplet(visit, "mix(", ", ", ")"); break;
            case EOpStep: writeTriplet(visit, "step(", ", ", ")"); break;
            case EOpSmoothStep: writeTriplet(visit, "smoothstep(", ", ", ")"); break;
    
            case EOpDistance: writeTriplet(visit, "distance(", ", ", ")"); break;
            case EOpDot: writeTriplet(visit, "dot(", ", ", ")"); break;
            case EOpCross: writeTriplet(visit, "cross(", ", ", ")"); break;
            case EOpFaceForward: writeTriplet(visit, "faceforward(", ", ", ")"); break;
            case EOpReflect: writeTriplet(visit, "reflect(", ", ", ")"); break;
            case EOpRefract: writeTriplet(visit, "refract(", ", ", ")"); break;
            case EOpMul: writeTriplet(visit, "matrixCompMult(", ", ", ")"); break;
    
            default: UNREACHABLE(); break;
        }
        return visitChildren;
    }
    
    bool TOutputGLSL::visitLoop(Visit visit, TIntermLoop* node)
    {
        TInfoSinkBase& out = objSink();
    
        incrementDepth();
        // Loop header.
        if (node->testFirst())  // for loop
        {
            out << "for (";
            if (node->getInit())
                node->getInit()->traverse(this);
            out << "; ";
    
            ASSERT(node->getTest() != NULL);
            node->getTest()->traverse(this);
            out << "; ";
    
            if (node->getTerminal())
                node->getTerminal()->traverse(this);
            out << ")\n";
        }
        else  // do-while loop
        {
            out << "do\n";
        }
    
        // Loop body.
        visitCodeBlock(node->getBody());
    
        // Loop footer.
        if (!node->testFirst())  // while loop
        {
            out << "while (";
            ASSERT(node->getTest() != NULL);
            node->getTest()->traverse(this);
            out << ");\n";
        }
        decrementDepth();
    
        // No need to visit children. They have been already processed in
        // this function.
        return false;
    }
    
    bool TOutputGLSL::visitBranch(Visit visit, TIntermBranch* node)
    {
        switch (node->getFlowOp())
        {
            case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
            case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
            case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
            case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
            default: UNREACHABLE(); break;
        }
    
        return true;
    }
    
    void TOutputGLSL::visitCodeBlock(TIntermNode* node) {
        TInfoSinkBase &out = objSink();
        if (node != NULL)
        {
            node->traverse(this);
            // Single statements not part of a sequence need to be terminated
            // with semi-colon.
            if (isSingleStatement(node))
                out << ";\n";
        }
        else
        {
            out << "{\n}\n";  // Empty code block.
        }
    }