From 1cc1a2813eec42e60c5b4fce41da09bc740c9141 Mon Sep 17 00:00:00 2001
From: John Kessenich <cepheus@frii.com>
Date: Fri, 3 Jun 2016 16:55:49 -0600
Subject: [PATCH] HLSL: 1) Implement lookahead buffers/stacks for token
 advance/recede, 2) use it for cast operation.

The grammar now accepts type casts, like "(int)x", but that
has to be disambiguated from "(a + b)", needed deeper lookahead
and backing up than what existed so far.
---
 Test/baseResults/hlsl.cast.frag.out | 86 +++++++++++++++++++++++++++++
 Test/hlsl.cast.frag                 |  4 ++
 gtests/Hlsl.FromFile.cpp            |  1 +
 hlsl/hlslGrammar.cpp                | 42 +++++++++++++-
 hlsl/hlslScanContext.h              |  2 +-
 hlsl/hlslTokenStream.cpp            | 37 ++++++++++++-
 hlsl/hlslTokenStream.h              | 26 +++++++--
 7 files changed, 190 insertions(+), 8 deletions(-)
 create mode 100755 Test/baseResults/hlsl.cast.frag.out
 create mode 100644 Test/hlsl.cast.frag

diff --git a/Test/baseResults/hlsl.cast.frag.out b/Test/baseResults/hlsl.cast.frag.out
new file mode 100755
index 000000000..1e6897887
--- /dev/null
+++ b/Test/baseResults/hlsl.cast.frag.out
@@ -0,0 +1,86 @@
+hlsl.cast.frag
+Shader version: 450
+gl_FragCoord origin is upper left
+0:? Sequence
+0:5  Function Definition: PixelShaderFunction(vf4; (temp 4-component vector of float)
+0:2    Function Parameters: 
+0:2      'input' (temp 4-component vector of float)
+0:?     Sequence
+0:3      Branch: Return with expression
+0:3        add (temp 4-component vector of float)
+0:3          add (temp 4-component vector of float)
+0:3            Construct vec4 (temp 4-component vector of float)
+0:3              'input' (temp 4-component vector of float)
+0:3            Convert int to float (temp 4-component vector of float)
+0:3              Convert float to int (temp 4-component vector of int)
+0:3                'input' (temp 4-component vector of float)
+0:3          Constant:
+0:3            1.198000
+0:3            1.198000
+0:3            1.198000
+0:3            1.198000
+0:?   Linker Objects
+
+
+Linked fragment stage:
+
+
+Shader version: 450
+gl_FragCoord origin is upper left
+0:? Sequence
+0:5  Function Definition: PixelShaderFunction(vf4; (temp 4-component vector of float)
+0:2    Function Parameters: 
+0:2      'input' (temp 4-component vector of float)
+0:?     Sequence
+0:3      Branch: Return with expression
+0:3        add (temp 4-component vector of float)
+0:3          add (temp 4-component vector of float)
+0:3            Construct vec4 (temp 4-component vector of float)
+0:3              'input' (temp 4-component vector of float)
+0:3            Convert int to float (temp 4-component vector of float)
+0:3              Convert float to int (temp 4-component vector of int)
+0:3                'input' (temp 4-component vector of float)
+0:3          Constant:
+0:3            1.198000
+0:3            1.198000
+0:3            1.198000
+0:3            1.198000
+0:?   Linker Objects
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 26
+
+                              Capability Shader
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 4  "PixelShaderFunction"
+                              ExecutionMode 4 OriginUpperLeft
+                              Source HLSL 450
+                              Name 4  "PixelShaderFunction"
+                              Name 9  "input"
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeFloat 32
+               7:             TypeVector 6(float) 4
+               8:             TypePointer Function 7(fvec4)
+              17:             TypeInt 32 1
+              18:             TypeVector 17(int) 4
+              22:    6(float) Constant 1067014160
+              23:    7(fvec4) ConstantComposite 22 22 22 22
+4(PixelShaderFunction):           2 Function None 3
+               5:             Label
+        9(input):      8(ptr) Variable Function
+              10:    7(fvec4) Load 9(input)
+              11:    6(float) CompositeExtract 10 0
+              12:    6(float) CompositeExtract 10 1
+              13:    6(float) CompositeExtract 10 2
+              14:    6(float) CompositeExtract 10 3
+              15:    7(fvec4) CompositeConstruct 11 12 13 14
+              16:    7(fvec4) Load 9(input)
+              19:   18(ivec4) ConvertFToS 16
+              20:    7(fvec4) ConvertSToF 19
+              21:    7(fvec4) FAdd 15 20
+              24:    7(fvec4) FAdd 21 23
+                              ReturnValue 24
+                              FunctionEnd
diff --git a/Test/hlsl.cast.frag b/Test/hlsl.cast.frag
new file mode 100644
index 000000000..c8dc8212d
--- /dev/null
+++ b/Test/hlsl.cast.frag
@@ -0,0 +1,4 @@
+float4 PixelShaderFunction(float4 input) : COLOR0
+{
+    return (float4)input + (int4)input + (float4)1.198;
+}
diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp
index 8f02c6a91..9c0061ed2 100644
--- a/gtests/Hlsl.FromFile.cpp
+++ b/gtests/Hlsl.FromFile.cpp
@@ -73,6 +73,7 @@ INSTANTIATE_TEST_CASE_P(
     ToSpirv, HlslCompileTest,
     ::testing::ValuesIn(std::vector<FileNameEntryPointPair>{
         {"hlsl.assoc.frag", "PixelShaderFunction"},
+        {"hlsl.cast.frag", "PixelShaderFunction"},
         {"hlsl.float1.frag", "PixelShaderFunction"},
         {"hlsl.float4.frag", "PixelShaderFunction"},
         {"hlsl.intrinsics.frag", "PixelShaderFunction"},
diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp
index a6387b748..ddb70788d 100755
--- a/hlsl/hlslGrammar.cpp
+++ b/hlsl/hlslGrammar.cpp
@@ -602,7 +602,8 @@ bool HlslGrammar::acceptBinaryExpression(TIntermTyped*& node, PrecedenceLevel pr
 }
 
 // unary_expression
-//      : + unary_expression
+//      : (type) unary_expression
+//      | + unary_expression
 //      | - unary_expression
 //      | ! unary_expression
 //      | ~ unary_expression
@@ -612,9 +613,46 @@ bool HlslGrammar::acceptBinaryExpression(TIntermTyped*& node, PrecedenceLevel pr
 //
 bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node)
 {
+    // (type) unary_expression
+    // Have to look two steps ahead, because this could be, e.g., a
+    // postfix_expression instead, since that also starts with at "(".
+    if (acceptTokenClass(EHTokLeftParen)) {
+        TType castType;
+        if (acceptType(castType)) {
+            if (! acceptTokenClass(EHTokRightParen)) {
+                expected("right parenthesis");
+                return false;
+            }
+
+            // We've matched "(type)" now, get the expression to cast
+            TSourceLoc loc = token.loc;
+            if (! acceptUnaryExpression(node))
+                return false;
+
+            // Hook it up like a constructor
+            TFunction* constructorFunction = parseContext.handleConstructorCall(loc, castType);
+            if (constructorFunction == nullptr) {
+                expected("type that can be constructed");
+                return false;
+            }
+            TIntermTyped* arguments = nullptr;
+            parseContext.handleFunctionArgument(constructorFunction, arguments, node);
+            node = parseContext.handleFunctionCall(loc, constructorFunction, arguments);
+
+            return true;
+        } else {
+            // This isn't a type cast, but it still started "(", so if it is a
+            // unary expression, it can only be a postfix_expression, so try that.
+            // Back it up first.
+            recedeToken();
+            return acceptPostfixExpression(node);
+        }
+    }
+
+    // peek for "op unary_expression"
     TOperator unaryOp = HlslOpMap::preUnary(peek());
     
-    // postfix_expression
+    // postfix_expression (if no unary operator)
     if (unaryOp == EOpNull)
         return acceptPostfixExpression(node);
 
diff --git a/hlsl/hlslScanContext.h b/hlsl/hlslScanContext.h
index 04f24383c..d761e3a88 100755
--- a/hlsl/hlslScanContext.h
+++ b/hlsl/hlslScanContext.h
@@ -57,7 +57,7 @@ struct HlslToken {
     HlslToken() : isType(false), string(nullptr), symbol(nullptr) { loc.init(); }
     TSourceLoc loc;                // location of token in the source
     EHlslTokenClass tokenClass;    // what kind of token it is
-    bool isType;                   // true if the token represents a user type
+    bool isType;                   // true if the token represents a type
     union {                        // what data the token holds
         glslang::TString *string;  // for identifiers
         int i;                     // for literals
diff --git a/hlsl/hlslTokenStream.cpp b/hlsl/hlslTokenStream.cpp
index cfc1101b9..47f779a81 100755
--- a/hlsl/hlslTokenStream.cpp
+++ b/hlsl/hlslTokenStream.cpp
@@ -37,10 +37,45 @@
 
 namespace glslang {
 
+void HlslTokenStream::pushPreToken(const HlslToken& tok)
+{
+    assert(preTokenStackSize == 0);
+    preTokenStack = tok;
+    ++preTokenStackSize;
+}
+
+HlslToken HlslTokenStream::popPreToken()
+{
+    assert(preTokenStackSize == 1);
+    --preTokenStackSize;
+
+    return preTokenStack;
+}
+
+void HlslTokenStream::pushTokenBuffer(const HlslToken& tok)
+{
+    tokenBuffer = tok;
+}
+
+HlslToken HlslTokenStream::popTokenBuffer()
+{
+    return tokenBuffer;
+}
+
 // Load 'token' with the next token in the stream of tokens.
 void HlslTokenStream::advanceToken()
 {
-    scanner.tokenize(token);
+    pushTokenBuffer(token);
+    if (preTokenStackSize > 0)
+        token = popPreToken();
+    else
+        scanner.tokenize(token);
+}
+
+void HlslTokenStream::recedeToken()
+{
+    pushPreToken(token);
+    token = popTokenBuffer();
 }
 
 // Return the current token class.
diff --git a/hlsl/hlslTokenStream.h b/hlsl/hlslTokenStream.h
index 9139df07d..83365c4cb 100755
--- a/hlsl/hlslTokenStream.h
+++ b/hlsl/hlslTokenStream.h
@@ -43,20 +43,38 @@ namespace glslang {
     class HlslTokenStream {
     public:
         explicit HlslTokenStream(HlslScanContext& scanner)
-            : scanner(scanner) { }
+            : scanner(scanner), preTokenStackSize(0) { }
         virtual ~HlslTokenStream() { }
 
     public:
         void advanceToken();
+        void recedeToken();
         bool acceptTokenClass(EHlslTokenClass);
         EHlslTokenClass peek() const;
         bool peekTokenClass(EHlslTokenClass) const;
 
     protected:
-        HlslToken token;                 // the current token we are processing
-    
+        HlslToken token;                  // the token we are currently looking at, but have not yet accepted
+
     private:
-        HlslScanContext& scanner;        // lexical scanner, to get next token
+        HlslScanContext& scanner;         // lexical scanner, to get next token
+
+        // Previously scanned tokens, returned for future advances,
+        // so logically in front of the token stream.
+        // Is logically a stack; needs last in last out semantics.
+        // Currently implemented as a stack of size 1.
+        HlslToken preTokenStack;          
+        int preTokenStackSize;
+        void pushPreToken(const HlslToken&);
+        HlslToken popPreToken();
+
+        // Previously scanned tokens, not yet return for future advances,
+        // but available for that.
+        // Is logically a fifo for normal advances, and a stack for recession.
+        // Currently implemented with an intrinsic size of 1.
+        HlslToken tokenBuffer;
+        void pushTokenBuffer(const HlslToken&);
+        HlslToken popTokenBuffer();
     };
 
 } // end namespace glslang
-- 
GitLab