Implement ES2 backend for Angle translator.

With this CL, we have the option to select a code output backend: GLSL, GLSL ES, or HLSL.

Note that we always emit the highest supported float precision for fragment shader due to anglebug 168.  Although this is a temporary solution, it's not against GLSL ES spec, because it's ok for implementation to upgrade precision.

Tested with WebGL conformance test suite, GLES2 conformance test suite (only failed 2/1198), and a few webgl demos, including worlds of webgl, aquarium, etc.

anglebug=81
test=translator emitting correct GLSL ES code when ES2 backend is selected.
Review URL: http://codereview.appspot.com/4550129

git-svn-id: https://angleproject.googlecode.com/svn/trunk@687 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 64157d6..7ace016 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -24,6 +24,7 @@
  Kenneth Russell
  Ben Vanik
  Adrienne Walker
+ Zhenyao Mo
 
 Mozilla Corp.
  Vladimir Vukicevic
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index 6795a87..6fe688c 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -17,7 +17,7 @@
 
 // Version number for shader translation API.
 // It is incremented everytime the API changes.
-#define SH_VERSION 104
+#define SH_VERSION 105
 
 //
 // The names of the following enums have been derived by replacing GL prefix
@@ -36,6 +36,12 @@
 } ShShaderSpec;
 
 typedef enum {
+  SH_ESSL_OUTPUT = 0x8B45,
+  SH_GLSL_OUTPUT = 0x8B46,
+  SH_HLSL_OUTPUT = 0x8B47
+} ShShaderOutput;
+
+typedef enum {
   SH_NONE           = 0,
   SH_INT            = 0x1404,
   SH_FLOAT          = 0x1406,
@@ -128,13 +134,17 @@
 //
 // Driver calls these to create and destroy compiler objects.
 //
-// Returns the handle of constructed compiler.
+// Returns the handle of constructed compiler, null if the requested compiler is
+// not supported.
 // Parameters:
 // type: Specifies the type of shader - SH_FRAGMENT_SHADER or SH_VERTEX_SHADER.
 // spec: Specifies the language spec the compiler must conform to -
 //       SH_GLES2_SPEC or SH_WEBGL_SPEC.
+// output: Specifies the output code type - SH_ESSL_OUTPUT, SH_GLSL_OUTPUT,
+//         or SH_HLSL_OUTPUT.
 // resources: Specifies the built-in resources.
 ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec,
+                             ShShaderOutput output,
                              const ShBuiltInResources* resources);
 void ShDestruct(ShHandle handle);
 
diff --git a/samples/translator/translator.cpp b/samples/translator/translator.cpp
index fb1fb49..67da02d 100644
--- a/samples/translator/translator.cpp
+++ b/samples/translator/translator.cpp
@@ -66,6 +66,7 @@
     char* buffer = 0;
     int bufferLen = 0;
     int numAttribs = 0, numUniforms = 0;
+    ShShaderOutput output = SH_ESSL_OUTPUT;
 
     ShInitialize();
 
@@ -81,6 +82,18 @@
             case 'm': compileOptions |= SH_MAP_LONG_VARIABLE_NAMES; break;
             case 'o': compileOptions |= SH_OBJECT_CODE; break;
             case 'u': compileOptions |= SH_ATTRIBUTES_UNIFORMS; break;
+            case 'b':
+                if (argv[0][2] == '=') {
+                    switch (argv[0][3]) {
+                    case 'e': output = SH_ESSL_OUTPUT; break;
+                    case 'g': output = SH_GLSL_OUTPUT; break;
+                    case 'h': output = SH_HLSL_OUTPUT; break;
+                    default: failCode = EFailUsage;
+                    }
+                } else {
+                    failCode = EFailUsage;
+                }
+                break;
             default: failCode = EFailUsage;
             }
         } else {
@@ -88,12 +101,14 @@
             switch (FindShaderType(argv[0])) {
             case SH_VERTEX_SHADER:
                 if (vertexCompiler == 0)
-                    vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources);
+                    vertexCompiler = ShConstructCompiler(
+                        SH_VERTEX_SHADER, SH_GLES2_SPEC, output, &resources);
                 compiler = vertexCompiler;
                 break;
             case SH_FRAGMENT_SHADER:
                 if (fragmentCompiler == 0)
-                    fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources);
+                    fragmentCompiler = ShConstructCompiler(
+                        SH_FRAGMENT_SHADER, SH_GLES2_SPEC, output, &resources);
                 compiler = fragmentCompiler;
                 break;
             default: break;
@@ -159,12 +174,15 @@
 //
 void usage()
 {
-    printf("Usage: translate [-i -m -o -u] file1 file2 ...\n"
-        "Where: filename = filename ending in .frag or .vert\n"
-        "       -i = print intermediate tree\n"
-        "       -m = map long variable names\n"
-        "       -o = print translated code\n"
-        "       -u = print active attribs and uniforms\n");
+    printf("Usage: translate [-i -m -o -u -b=e -b=g -b=h] file1 file2 ...\n"
+        "Where: filename : filename ending in .frag or .vert\n"
+        "       -i       : print intermediate tree\n"
+        "       -m       : map long variable names\n"
+        "       -o       : print translated code\n"
+        "       -u       : print active attribs and uniforms\n"
+        "       -b=e     : output GLSL ES code (this is by default)\n"
+        "       -b=g     : output GLSL code\n"
+        "       -b=h     : output HLSL code\n");
 }
 
 //
diff --git a/src/build_angle.gyp b/src/build_angle.gyp
index dca692b..08727d3 100644
--- a/src/build_angle.gyp
+++ b/src/build_angle.gyp
@@ -104,8 +104,14 @@
         'compiler/CodeGenGLSL.cpp',
         'compiler/ForLoopUnroll.cpp',
         'compiler/ForLoopUnroll.h',
+        'compiler/OutputESSL.cpp',
+        'compiler/OutputESSL.h',        
+        'compiler/OutputGLSLBase.cpp',
+        'compiler/OutputGLSLBase.h',
         'compiler/OutputGLSL.cpp',
         'compiler/OutputGLSL.h',
+        'compiler/TranslatorESSL.cpp',
+        'compiler/TranslatorESSL.h',
         'compiler/TranslatorGLSL.cpp',
         'compiler/TranslatorGLSL.h',
         'compiler/VersionGLSL.cpp',
diff --git a/src/common/version.h b/src/common/version.h
index 9462f07..f47273e 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -1,7 +1,7 @@
 #define MAJOR_VERSION 0
 #define MINOR_VERSION 0
 #define BUILD_VERSION 0
-#define BUILD_REVISION 686
+#define BUILD_REVISION 687
 
 #define STRINGIFY(x) #x
 #define MACRO_STRINGIFY(x) STRINGIFY(x)
diff --git a/src/compiler/CodeGenGLSL.cpp b/src/compiler/CodeGenGLSL.cpp
index d140b37..226bf8f 100644
--- a/src/compiler/CodeGenGLSL.cpp
+++ b/src/compiler/CodeGenGLSL.cpp
@@ -5,15 +5,24 @@
 //
 
 #include "compiler/TranslatorGLSL.h"
+#include "compiler/TranslatorESSL.h"
 
 //
 // This function must be provided to create the actual
 // compile object used by higher level code.  It returns
 // a subclass of TCompiler.
 //
-TCompiler* ConstructCompiler(ShShaderType type, ShShaderSpec spec)
+TCompiler* ConstructCompiler(
+    ShShaderType type, ShShaderSpec spec, ShShaderOutput output)
 {
-    return new TranslatorGLSL(type, spec);
+    switch (output) {
+      case SH_GLSL_OUTPUT:
+        return new TranslatorGLSL(type, spec);
+      case SH_ESSL_OUTPUT:
+        return new TranslatorESSL(type, spec);
+      default:
+        return NULL;
+    }
 }
 
 //
diff --git a/src/compiler/CodeGenHLSL.cpp b/src/compiler/CodeGenHLSL.cpp
index e04e789..f46ff66 100644
--- a/src/compiler/CodeGenHLSL.cpp
+++ b/src/compiler/CodeGenHLSL.cpp
@@ -11,9 +11,15 @@
 // compile object used by higher level code.  It returns
 // a subclass of TCompiler.
 //
-TCompiler* ConstructCompiler(ShShaderType type, ShShaderSpec spec)
+TCompiler* ConstructCompiler(
+    ShShaderType type, ShShaderSpec spec, ShShaderOutput output)
 {
-    return new TranslatorHLSL(type, spec);
+  switch (output) {
+    case SH_HLSL_OUTPUT:
+      return new TranslatorHLSL(type, spec);
+    default:
+      return NULL;
+  }
 }
 
 //
diff --git a/src/compiler/Compiler.cpp b/src/compiler/Compiler.cpp
index 41eb0ec..655d62d 100644
--- a/src/compiler/Compiler.cpp
+++ b/src/compiler/Compiler.cpp
@@ -215,3 +215,8 @@
 {
     return MAX_IDENTIFIER_NAME_SIZE + 1;
 }
+
+const TExtensionBehavior& TCompiler::getExtensionBehavior() const
+{
+    return extensionBehavior;
+}
diff --git a/src/compiler/ExtensionBehavior.h b/src/compiler/ExtensionBehavior.h
index da96c24..ab5a0a5 100644
--- a/src/compiler/ExtensionBehavior.h
+++ b/src/compiler/ExtensionBehavior.h
@@ -16,6 +16,22 @@
     EBhDisable
 } TBehavior;
 
+inline const char* getBehaviorString(TBehavior b)
+{
+    switch(b) {
+      case EBhRequire:
+        return "require";
+      case EBhEnable:
+        return "enable";
+      case EBhWarn:
+        return "warn";
+      case EBhDisable:
+        return "disable";
+      default:
+        return NULL;
+    }
+}
+
 typedef TMap<TString, TBehavior> TExtensionBehavior;
 
 #endif // _EXTENSION_TABLE_INCLUDED_
diff --git a/src/compiler/OutputESSL.cpp b/src/compiler/OutputESSL.cpp
new file mode 100644
index 0000000..64ee92d
--- /dev/null
+++ b/src/compiler/OutputESSL.cpp
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2002-2011 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/OutputESSL.h"
+
+TOutputESSL::TOutputESSL(TInfoSinkBase& objSink)
+    : TOutputGLSLBase(objSink)
+{
+}
+
+bool TOutputESSL::writeVariablePrecision(TPrecision precision)
+{
+    if (precision == EbpUndefined)
+        return false;
+
+    TInfoSinkBase& out = objSink();
+    out << getPrecisionString(precision);
+    return true;
+}
diff --git a/src/compiler/OutputESSL.h b/src/compiler/OutputESSL.h
new file mode 100644
index 0000000..4fa73c8
--- /dev/null
+++ b/src/compiler/OutputESSL.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2002-2011 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.
+//
+
+#ifndef CROSSCOMPILERGLSL_OUTPUTESSL_H_
+#define CROSSCOMPILERGLSL_OUTPUTESSL_H_
+
+#include "compiler/OutputGLSLBase.h"
+
+class TOutputESSL : public TOutputGLSLBase
+{
+public:
+    TOutputESSL(TInfoSinkBase& objSink);
+
+protected:
+    virtual bool writeVariablePrecision(TPrecision precision);
+};
+
+#endif  // CROSSCOMPILERGLSL_OUTPUTESSL_H_
diff --git a/src/compiler/OutputGLSL.cpp b/src/compiler/OutputGLSL.cpp
index 3224dfd..dd31b4b 100644
--- a/src/compiler/OutputGLSL.cpp
+++ b/src/compiler/OutputGLSL.cpp
@@ -5,706 +5,13 @@
 //
 
 #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)
+    : TOutputGLSLBase(objSink)
 {
 }
 
-void TOutputGLSL::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
+bool TOutputGLSL::writeVariablePrecision(TPrecision)
 {
-    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();
-    if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node))
-        out << mLoopUnroll.GetLoopIndexValue(node);
-    else
-        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 EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
-        case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
-        case EOpFwidth: writeTriplet(visit, "fwidth(", 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.
-    TLoopType loopType = node->getType();
-    if (loopType == ELoopFor)  // for loop
-    {
-        if (!node->getUnrollFlag()) {
-            out << "for (";
-            if (node->getInit())
-                node->getInit()->traverse(this);
-            out << "; ";
-
-            if (node->getCondition())
-                node->getCondition()->traverse(this);
-            out << "; ";
-
-            if (node->getExpression())
-                node->getExpression()->traverse(this);
-            out << ")\n";
-        }
-    }
-    else if (loopType == ELoopWhile)  // while loop
-    {
-        out << "while (";
-        ASSERT(node->getCondition() != NULL);
-        node->getCondition()->traverse(this);
-        out << ")\n";
-    }
-    else  // do-while loop
-    {
-        ASSERT(loopType == ELoopDoWhile);
-        out << "do\n";
-    }
-
-    // Loop body.
-    if (node->getUnrollFlag())
-    {
-        TLoopIndexInfo indexInfo;
-        mLoopUnroll.FillLoopIndexInfo(node, indexInfo);
-        mLoopUnroll.Push(indexInfo);
-        while (mLoopUnroll.SatisfiesLoopCondition())
-        {
-            visitCodeBlock(node->getBody());
-            mLoopUnroll.Step();
-        }
-        mLoopUnroll.Pop();
-    }
-    else
-    {
-        visitCodeBlock(node->getBody());
-    }
-
-    // Loop footer.
-    if (loopType == ELoopDoWhile)  // do-while loop
-    {
-        out << "while (";
-        ASSERT(node->getCondition() != NULL);
-        node->getCondition()->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.
-    }
-}
diff --git a/src/compiler/OutputGLSL.h b/src/compiler/OutputGLSL.h
index ace110a..0fe2356 100644
--- a/src/compiler/OutputGLSL.h
+++ b/src/compiler/OutputGLSL.h
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Copyright (c) 2002-2011 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.
 //
@@ -7,46 +7,15 @@
 #ifndef CROSSCOMPILERGLSL_OUTPUTGLSL_H_
 #define CROSSCOMPILERGLSL_OUTPUTGLSL_H_
 
-#include <set>
+#include "compiler/OutputGLSLBase.h"
 
-#include "compiler/ForLoopUnroll.h"
-#include "compiler/intermediate.h"
-#include "compiler/ParseHelper.h"
-
-class TOutputGLSL : public TIntermTraverser
+class TOutputGLSL : public TOutputGLSLBase
 {
 public:
     TOutputGLSL(TInfoSinkBase& objSink);
 
 protected:
-    TInfoSinkBase& objSink() { return mObjSink; }
-    void writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr);
-    void writeVariableType(const TType& type);
-    void writeFunctionParameters(const TIntermSequence& args);
-    const ConstantUnion* writeConstantUnion(const TType& type, const ConstantUnion* pConstUnion);
-
-    virtual void visitSymbol(TIntermSymbol* node);
-    virtual void visitConstantUnion(TIntermConstantUnion* node);
-    virtual bool visitBinary(Visit visit, TIntermBinary* node);
-    virtual bool visitUnary(Visit visit, TIntermUnary* node);
-    virtual bool visitSelection(Visit visit, TIntermSelection* node);
-    virtual bool visitAggregate(Visit visit, TIntermAggregate* node);
-    virtual bool visitLoop(Visit visit, TIntermLoop* node);
-    virtual bool visitBranch(Visit visit, TIntermBranch* node);
-
-    void visitCodeBlock(TIntermNode* node);
-
-private:
-    TInfoSinkBase& mObjSink;
-    bool mDeclaringVariables;
-
-    // Structs are declared as the tree is traversed. This set contains all
-    // the structs already declared. It is maintained so that a struct is
-    // declared only once.
-    typedef std::set<TString> DeclaredStructs;
-    DeclaredStructs mDeclaredStructs;
-
-    ForLoopUnroll mLoopUnroll;
+    virtual bool writeVariablePrecision(TPrecision);
 };
 
 #endif  // CROSSCOMPILERGLSL_OUTPUTGLSL_H_
diff --git a/src/compiler/OutputGLSLBase.cpp b/src/compiler/OutputGLSLBase.cpp
new file mode 100644
index 0000000..5e3857c
--- /dev/null
+++ b/src/compiler/OutputGLSLBase.cpp
@@ -0,0 +1,713 @@
+//
+// Copyright (c) 2002-2011 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/OutputGLSLBase.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
+
+TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase& objSink)
+    : TIntermTraverser(true, true, true),
+      mObjSink(objSink),
+      mDeclaringVariables(false)
+{
+}
+
+void TOutputGLSLBase::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 TOutputGLSLBase::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);
+            if (writeVariablePrecision(fieldType->getPrecision()))
+                out << " ";
+            out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
+            if (fieldType->isArray())
+                out << arrayBrackets(*fieldType);
+            out << ";\n";
+        }
+        out << "}";
+        mDeclaredStructs.insert(type.getTypeName());
+    }
+    else
+    {
+        if (writeVariablePrecision(type.getPrecision()))
+            out << " ";
+        out << getTypeName(type);
+    }
+}
+
+void TOutputGLSLBase::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* TOutputGLSLBase::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 TOutputGLSLBase::visitSymbol(TIntermSymbol* node)
+{
+    TInfoSinkBase& out = objSink();
+    if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node))
+        out << mLoopUnroll.GetLoopIndexValue(node);
+    else
+        out << node->getSymbol();
+
+    if (mDeclaringVariables && node->getType().isArray())
+        out << arrayBrackets(node->getType());
+}
+
+void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion* node)
+{
+    writeConstantUnion(node->getType(), node->getUnionArrayPointer());
+}
+
+bool TOutputGLSLBase::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 TOutputGLSLBase::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 EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
+        case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
+        case EOpFwidth: writeTriplet(visit, "fwidth(", NULL, ")"); break;
+
+        case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
+        case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
+
+        default: UNREACHABLE(); break;
+    }
+
+    return true;
+}
+
+bool TOutputGLSLBase::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 TOutputGLSLBase::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 TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
+{
+    TInfoSinkBase& out = objSink();
+
+    incrementDepth();
+    // Loop header.
+    TLoopType loopType = node->getType();
+    if (loopType == ELoopFor)  // for loop
+    {
+        if (!node->getUnrollFlag()) {
+            out << "for (";
+            if (node->getInit())
+                node->getInit()->traverse(this);
+            out << "; ";
+
+            if (node->getCondition())
+                node->getCondition()->traverse(this);
+            out << "; ";
+
+            if (node->getExpression())
+                node->getExpression()->traverse(this);
+            out << ")\n";
+        }
+    }
+    else if (loopType == ELoopWhile)  // while loop
+    {
+        out << "while (";
+        ASSERT(node->getCondition() != NULL);
+        node->getCondition()->traverse(this);
+        out << ")\n";
+    }
+    else  // do-while loop
+    {
+        ASSERT(loopType == ELoopDoWhile);
+        out << "do\n";
+    }
+
+    // Loop body.
+    if (node->getUnrollFlag())
+    {
+        TLoopIndexInfo indexInfo;
+        mLoopUnroll.FillLoopIndexInfo(node, indexInfo);
+        mLoopUnroll.Push(indexInfo);
+        while (mLoopUnroll.SatisfiesLoopCondition())
+        {
+            visitCodeBlock(node->getBody());
+            mLoopUnroll.Step();
+        }
+        mLoopUnroll.Pop();
+    }
+    else
+    {
+        visitCodeBlock(node->getBody());
+    }
+
+    // Loop footer.
+    if (loopType == ELoopDoWhile)  // do-while loop
+    {
+        out << "while (";
+        ASSERT(node->getCondition() != NULL);
+        node->getCondition()->traverse(this);
+        out << ");\n";
+    }
+    decrementDepth();
+
+    // No need to visit children. They have been already processed in
+    // this function.
+    return false;
+}
+
+bool TOutputGLSLBase::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 TOutputGLSLBase::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.
+    }
+}
diff --git a/src/compiler/OutputGLSLBase.h b/src/compiler/OutputGLSLBase.h
new file mode 100644
index 0000000..efd0b5f
--- /dev/null
+++ b/src/compiler/OutputGLSLBase.h
@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2002-2011 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.
+//
+
+#ifndef CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_
+#define CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_
+
+#include <set>
+
+#include "compiler/ForLoopUnroll.h"
+#include "compiler/intermediate.h"
+#include "compiler/ParseHelper.h"
+
+class TOutputGLSLBase : public TIntermTraverser
+{
+public:
+    TOutputGLSLBase(TInfoSinkBase& objSink);
+
+protected:
+    TInfoSinkBase& objSink() { return mObjSink; }
+    void writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr);
+    void writeVariableType(const TType& type);
+    virtual bool writeVariablePrecision(TPrecision precision) = 0;
+    void writeFunctionParameters(const TIntermSequence& args);
+    const ConstantUnion* writeConstantUnion(const TType& type, const ConstantUnion* pConstUnion);
+
+    virtual void visitSymbol(TIntermSymbol* node);
+    virtual void visitConstantUnion(TIntermConstantUnion* node);
+    virtual bool visitBinary(Visit visit, TIntermBinary* node);
+    virtual bool visitUnary(Visit visit, TIntermUnary* node);
+    virtual bool visitSelection(Visit visit, TIntermSelection* node);
+    virtual bool visitAggregate(Visit visit, TIntermAggregate* node);
+    virtual bool visitLoop(Visit visit, TIntermLoop* node);
+    virtual bool visitBranch(Visit visit, TIntermBranch* node);
+
+    void visitCodeBlock(TIntermNode* node);
+
+private:
+    TInfoSinkBase& mObjSink;
+    bool mDeclaringVariables;
+
+    // Structs are declared as the tree is traversed. This set contains all
+    // the structs already declared. It is maintained so that a struct is
+    // declared only once.
+    typedef std::set<TString> DeclaredStructs;
+    DeclaredStructs mDeclaredStructs;
+
+    ForLoopUnroll mLoopUnroll;
+};
+
+#endif  // CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_
diff --git a/src/compiler/ParseHelper.h b/src/compiler/ParseHelper.h
index f5a6c57..0cc0728 100644
--- a/src/compiler/ParseHelper.h
+++ b/src/compiler/ParseHelper.h
@@ -30,13 +30,13 @@
 // they can be passed to the parser without needing a global.
 //
 struct TParseContext {
-    TParseContext(TSymbolTable& symt, const TExtensionBehavior& ext, TIntermediate& interm, ShShaderType type, ShShaderSpec spec, int options, const char* sourcePath, TInfoSink& is) :
+    TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, ShShaderType type, ShShaderSpec spec, int options, const char* sourcePath, TInfoSink& is) :
             intermediate(interm), symbolTable(symt), extensionBehavior(ext), infoSink(is), shaderType(type), shaderSpec(spec), compileOptions(options), sourcePath(sourcePath), treeRoot(0),
             recoveredFromError(false), numErrors(0), lexAfterType(false), loopNestingLevel(0),
             inTypeParen(false), scanner(NULL), contextPragma(true, false) {  }
     TIntermediate& intermediate; // to hold and build a parse tree
     TSymbolTable& symbolTable;   // symbol table that goes with the language currently being parsed
-    TExtensionBehavior extensionBehavior;  // mapping between supported extensions and current behavior.
+    TExtensionBehavior& extensionBehavior;  // mapping between supported extensions and current behavior.
     TInfoSink& infoSink;
     ShShaderType shaderType;              // vertex or fragment language (future: pack or unpack)
     ShShaderSpec shaderSpec;              // The language specification compiler conforms to - GLES2 or WebGL.
diff --git a/src/compiler/ShHandle.h b/src/compiler/ShHandle.h
index 788ce92..bf61d29 100644
--- a/src/compiler/ShHandle.h
+++ b/src/compiler/ShHandle.h
@@ -75,6 +75,8 @@
     void mapLongVariableNames(TIntermNode* root);
     // Translate to object code.
     virtual void translate(TIntermNode* root) = 0;
+    // Get built-in extensions with default behavior.
+    const TExtensionBehavior& getExtensionBehavior() const;
 
 private:
     ShShaderType shaderType;
@@ -104,7 +106,8 @@
 // destroy the machine dependent objects, which contain the
 // above machine independent information.
 //
-TCompiler* ConstructCompiler(ShShaderType type, ShShaderSpec spec);
+TCompiler* ConstructCompiler(
+    ShShaderType type, ShShaderSpec spec, ShShaderOutput output);
 void DeleteCompiler(TCompiler*);
 
 #endif // _SHHANDLE_INCLUDED_
diff --git a/src/compiler/ShaderLang.cpp b/src/compiler/ShaderLang.cpp
index 051e4ec..ab8799c 100644
--- a/src/compiler/ShaderLang.cpp
+++ b/src/compiler/ShaderLang.cpp
@@ -110,12 +110,13 @@
 // Driver calls these to create and destroy compiler objects.
 //
 ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec,
+                             ShShaderOutput output,
                              const ShBuiltInResources* resources)
 {
     if (!InitThread())
         return 0;
 
-    TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec));
+    TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec, output));
     TCompiler* compiler = base->getAsCompiler();
     if (compiler == 0)
         return 0;
diff --git a/src/compiler/TranslatorESSL.cpp b/src/compiler/TranslatorESSL.cpp
new file mode 100644
index 0000000..62b645f
--- /dev/null
+++ b/src/compiler/TranslatorESSL.cpp
@@ -0,0 +1,46 @@
+//
+// Copyright (c) 2002-2011 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/TranslatorESSL.h"
+
+#include "compiler/OutputESSL.h"
+
+TranslatorESSL::TranslatorESSL(ShShaderType type, ShShaderSpec spec)
+    : TCompiler(type, spec) {
+}
+
+void TranslatorESSL::translate(TIntermNode* root) {
+    TInfoSinkBase& sink = getInfoSink().obj;
+
+    // Write built-in extension behaviors.
+    writeExtensionBehavior();
+
+    // FIXME(zmo): no need to emit default precision if all variables emit
+    // their own precision.
+    // http://code.google.com/p/angleproject/issues/detail?id=168
+    if (this->getShaderType() == SH_FRAGMENT_SHADER) {
+        // Write default float precision.
+        sink << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
+             << "precision highp float;\n"
+             << "#else\n"
+             << "precision mediump float;\n"
+             << "#endif\n";
+    }
+
+    // Write translated shader.
+    TOutputESSL outputESSL(sink);
+    root->traverse(&outputESSL);
+}
+
+void TranslatorESSL::writeExtensionBehavior() {
+    TInfoSinkBase& sink = getInfoSink().obj;
+    const TExtensionBehavior& extensionBehavior = getExtensionBehavior();
+    for (TExtensionBehavior::const_iterator iter = extensionBehavior.begin();
+         iter != extensionBehavior.end(); ++iter) {
+        sink << "#extension " << iter->first << " : "
+             << getBehaviorString(iter->second) << "\n";
+    }
+}
diff --git a/src/compiler/TranslatorESSL.h b/src/compiler/TranslatorESSL.h
new file mode 100644
index 0000000..a1196bd
--- /dev/null
+++ b/src/compiler/TranslatorESSL.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2002-2011 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.
+//
+
+#ifndef COMPILER_TRANSLATORESSL_H_
+#define COMPILER_TRANSLATORESSL_H_
+
+#include "compiler/ShHandle.h"
+
+class TranslatorESSL : public TCompiler {
+public:
+    TranslatorESSL(ShShaderType type, ShShaderSpec spec);
+
+protected:
+    virtual void translate(TIntermNode* root);
+
+private:
+    void writeExtensionBehavior();
+};
+
+#endif  // COMPILER_TRANSLATORESSL_H_
diff --git a/src/libGLESv2/Shader.cpp b/src/libGLESv2/Shader.cpp
index 21c6385..f66b2c5 100644
--- a/src/libGLESv2/Shader.cpp
+++ b/src/libGLESv2/Shader.cpp
@@ -36,7 +36,7 @@
         {
             ShBuiltInResources resources;
             ShInitBuiltInResources(&resources);
-            Context *context = getContext();            
+            Context *context = getContext();
 
             resources.MaxVertexAttribs = MAX_VERTEX_ATTRIBS;
             resources.MaxVertexUniformVectors = MAX_VERTEX_UNIFORM_VECTORS;
@@ -48,8 +48,8 @@
             resources.MaxDrawBuffers = MAX_DRAW_BUFFERS;
             resources.OES_standard_derivatives = 1;
 
-            mFragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources);
-            mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources);
+            mFragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, SH_HLSL_OUTPUT, &resources);
+            mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, SH_HLSL_OUTPUT, &resources);
         }
     }