Implement swizzles containing 0 and 1 to represent numbers not indexes.
diff --git a/glslang/MachineIndependent/ParseContextBase.cpp b/glslang/MachineIndependent/ParseContextBase.cpp
index b464009..0852862 100644
--- a/glslang/MachineIndependent/ParseContextBase.cpp
+++ b/glslang/MachineIndependent/ParseContextBase.cpp
@@ -481,13 +481,32 @@
 // Look at a '.' field selector string and change it into numerical selectors
 // for a vector or scalar.
 //
+// These are returned as indexes in selector.
+// E.g. ".zy" will become selector = {2, 1}.
+//
 // Always return some form of swizzle, so the result is always usable.
 //
+// '0' and '1' in the field will mean to use the numeric values of 0 and 1
+// rather than the result of an index into the vector.
+// These are represented by:
+//   '0':  MaxSwizzleSelectors
+//   '1':  MaxSwizzleSelectors + 1
+// E.g., ".z01" will become selector = {2, 4, 5} (if MasSwizzleSelectors == 4).
+//
+// A leading underscore (prefix) will get ignored.
+//
 void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize,
-                                             TSwizzleSelectors<TVectorSelector>& selector)
+                                             TSwizzleSelectors<TVectorSelector>& selector, bool& numeric)
 {
+    // a swizzle does not contain numerics unless there are actually numbers
+    // in it, independent of whether there is a prefix
+    numeric = false;
+
+    // If the field uses prefix syntax, normalize it.
+    const int firstChar = compString[0] == '_';
+
     // Too long?
-    if (compString.size() > MaxSwizzleSelectors)
+    if (compString.size() - firstChar > MaxSwizzleSelectors)
         error(loc, "vector swizzle too long", compString.c_str(), "");
 
     // Use this to test that all swizzle characters are from the same swizzle-namespace-set
@@ -495,12 +514,13 @@
         exyzw,
         ergba,
         estpq,
-    } fieldSet[MaxSwizzleSelectors];
+        enumeric,
+    } fieldSet[MaxSwizzleSelectors + 1];
 
     // Decode the swizzle string.
-    int size = std::min(MaxSwizzleSelectors, (int)compString.size());
+    const int size = std::min(MaxSwizzleSelectors, (int)compString.size() - firstChar);
     for (int i = 0; i < size; ++i) {
-        switch (compString[i])  {
+        switch (compString[i + firstChar])  {
         case 'x':
             selector.push_back(0);
             fieldSet[i] = exyzw;
@@ -553,6 +573,17 @@
             fieldSet[i] = estpq;
             break;
 
+        case '0':
+            selector.push_back(MaxSwizzleSelectors);
+            fieldSet[i] = enumeric;
+            numeric = true;
+            break;
+        case '1':
+            selector.push_back(MaxSwizzleSelectors + 1);
+            fieldSet[i] = enumeric;
+            numeric = true;
+            break;
+
         default:
             error(loc, "unknown swizzle selection", compString.c_str(), "");
             break;
@@ -561,13 +592,14 @@
 
     // Additional error checking.
     for (int i = 0; i < selector.size(); ++i) {
-        if (selector[i] >= vecSize) {
+        if (selector[i] < MaxSwizzleSelectors && selector[i] >= vecSize) {
             error(loc, "vector swizzle selection out of range",  compString.c_str(), "");
             selector.resize(i);
             break;
         }
 
-        if (i > 0 && fieldSet[i] != fieldSet[i-1]) {
+        if (i > 0 && fieldSet[i] != enumeric && fieldSet[i-1] != enumeric &&
+                     fieldSet[i] != fieldSet[i-1]) {
             error(loc, "vector swizzle selectors not from the same set", compString.c_str(), "");
             selector.resize(i);
             break;
@@ -579,6 +611,24 @@
         selector.push_back(0);
 }
 
+void TParseContextBase::replicateRValue(TIntermTyped* node, int num, TVector<TIntermTyped*>& replicates)
+{
+    if (num == 0)
+        return;
+    if (num == 1) {
+        replicates.push_back(node);
+        return;
+    }
+    if (node->getAsSymbolNode()) {
+        replicates.push_back(node);
+        for (int i = 1; i < num; ++i)
+            replicates.push_back(intermediate.addSymbol(*node->getAsSymbolNode()));
+    }
+
+    // WIP: a complex expression needs to be evaluated exactly once, and then
+    // copies of the result put into the replicates.
+}
+
 #ifdef ENABLE_HLSL
 //
 // Make the passed-in variable information become a member of the
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 117c164..fa83ef2 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -886,7 +886,8 @@
     }
 
     TSwizzleSelectors<TVectorSelector> selectors;
-    parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
+    bool numeric = false;
+    parseSwizzleSelector(loc, field, base->getVectorSize(), selectors, numeric);
 
     if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitFloat())
         requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16");
@@ -895,6 +896,9 @@
     if (base->isVector() && selectors.size() != 1 && base->getType().contains8BitInt())
         requireInt8Arithmetic(loc, ".", "can't swizzle types containing (u)int8");
 
+    if (numeric)
+        return handleNumericDotSwizzle(loc, base, selectors);
+
     if (base->isScalar()) {
         if (selectors.size() == 1)
             return result;
@@ -927,6 +931,93 @@
     return result;
 }
 
+// Handle a swizzle operation where at least one selector is numeric.
+//
+// Can return
+//  - a scalar constant (e.g. for ._1), but converted to the right type
+//    and constant folded
+//  - a vector constructor
+//  - a sequence containing
+//     1. evaluation of 'base'
+//     2. a scalar constant, converted, folded
+//  - a sequence containing
+//     1. evaluation of 'base'
+//     2. a vector constructor
+//
+// Note that none of the above include swizzle operations.
+//
+// Note: A vector constructor might require copies of the rvalue being swizzled,
+//       to avoid the tree accidentally becoming a DAG when there are multiple
+//       letter swizzles present needing multiple operations to get the
+//       components.  This is quite unlike how swizzles are handled, or any
+//       other native GLSL operation.
+//
+TIntermTyped* TParseContext::handleNumericDotSwizzle(const TSourceLoc& loc, TIntermTyped* base,
+    const TSwizzleSelectors<TVectorSelector>& selectors)
+{
+    const auto isLetter = [](int selector) { return selector < MaxSwizzleSelectors; };
+    const auto isNumber = [isLetter](int selector) { return !isLetter(selector); };
+    const auto getNumber = [](int selector) { return selector - MaxSwizzleSelectors; };
+
+    // The type of the result has the 'base' component type,
+    // but the component-count of 'selectors'.
+    TType type(base->getBasicType(), EvqTemporary, selectors.size());
+
+    // If only one selector, the result is a scalar.
+    // But, its type might be changing, so add a constructor.
+    // This will always result in an already folded scalar front-end constant.
+    if (selectors.size() == 1) {
+        assert(isNumber(selectors[0]));
+        return addConstructor(loc, intermediate.addConstantUnion(getNumber(selectors[0]), loc), type);
+
+        // WIP: this is incorrect if 'base' had side effects, it still needs to
+        // be evaluated as part of a sequence operation, unless the
+        // specification for this operation says those side effects are ignored.
+    }
+
+    // Otherwise, the result is like making a vector constructor,
+    // where we know we have more than one argument.
+
+    // Collect the arguments.
+    // This is complicated by the presence of more than one letter selector,
+    // because we need to reuse the r-value for each one, so it is a rare
+    // situation of needing to replicate the r-value.
+
+    // count the letter selectors (unless the base is a constant))
+    int letterCount = 0;
+    if (!base->getType().getQualifier().isFrontEndConstant()) {
+        for (int s = 0; s < selectors.size(); ++s)
+            letterCount += isLetter(selectors[s]) ? 1 : 0;
+    }
+    // get the replicates
+    TVector<TIntermTyped*> replicates;
+    replicateRValue(base, letterCount, replicates);
+
+    // process all the selectors to make the vector
+    TIntermAggregate* args = nullptr;
+    for (int s = 0; s < (int)selectors.size(); ++s) {
+        if (isNumber(selectors[s])) {
+            args = intermediate.growAggregate(args, intermediate.addConstantUnion(getNumber(selectors[s]), loc));
+        } else {
+            // traditional swizzle selector, which needs to consume the replicates
+            // (unless base is a constant)
+            TIntermTyped* arg;
+            if (base->getType().getQualifier().isFrontEndConstant()) {
+                arg = intermediate.foldDereference(base, selectors[s], loc);
+            } else {
+                TIntermTyped* rep = replicates.back();
+                replicates.pop_back();
+                TIntermTyped* index = intermediate.addConstantUnion(selectors[s], loc);
+                arg = intermediate.addIndex(EOpIndexDirect, rep, index, loc);
+            }
+            args = intermediate.growAggregate(args, arg);
+        }
+    }
+
+    // form the constructor
+    return addConstructor(loc, args, type)->getAsAggregate();
+}
+
 void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName)
 {
     // a block that needs extension checking is either 'base', or if arrayed,
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index 9c5fdc7..eb50dad 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -217,7 +217,8 @@
         /* output */ bool& tie);
 
     virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size,
-                                      TSwizzleSelectors<TVectorSelector>&);
+                                      TSwizzleSelectors<TVectorSelector>&, bool& numeric);
+    virtual void replicateRValue(TIntermTyped* node, int n, TVector<TIntermTyped*>& replicates);
 
     // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
     TVariable* globalUniformBlock;     // the actual block, inserted into the symbol table
@@ -316,6 +317,8 @@
     TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode);
     TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
     TIntermTyped* handleDotSwizzle(const TSourceLoc&, TIntermTyped* base, const TString& field);
+    TIntermTyped* handleNumericDotSwizzle(const TSourceLoc&, TIntermTyped* base,
+        const TSwizzleSelectors<TVectorSelector>&);
     void blockMemberExtensionCheck(const TSourceLoc&, const TIntermTyped* base, int member, const TString& memberName);
     TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
     TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);
diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp
index 2dc173f..32ace82 100755
--- a/hlsl/hlslParseHelper.cpp
+++ b/hlsl/hlslParseHelper.cpp
@@ -952,7 +952,8 @@
         }
     } else if (base->isVector() || base->isScalar()) {
         TSwizzleSelectors<TVectorSelector> selectors;
-        parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
+        bool numeric = false;
+        parseSwizzleSelector(loc, field, base->getVectorSize(), selectors, numeric);
 
         if (base->isScalar()) {
             if (selectors.size() == 1)