Added basic SkSL DSL statements

(and Ternary for good measure)

Change-Id: I4afa121d54ab9ba8d0814693ce53da7cb73ef340
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353626
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/tests/SkSLDSLTest.cpp b/tests/SkSLDSLTest.cpp
index 4fdd9b6..a134390 100644
--- a/tests/SkSLDSLTest.cpp
+++ b/tests/SkSLDSLTest.cpp
@@ -53,6 +53,29 @@
     skiatest::Reporter* fReporter;
 };
 
+static bool whitespace_insensitive_compare(const char* a, const char* b) {
+    for (;;) {
+        while (isspace(*a)) {
+            ++a;
+        }
+        while (isspace(*b)) {
+            ++b;
+        }
+        if (*a != *b) {
+            return false;
+        }
+        if (*a == 0) {
+            return true;
+        }
+        ++a;
+        ++b;
+    }
+}
+
+static bool whitespace_insensitive_compare(DSLStatement& stmt, const char* description) {
+    return whitespace_insensitive_compare(stmt.release()->description().c_str(), description);
+}
+
 DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLStartup, r, ctxInfo) {
     AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
     Expression e1 = 1;
@@ -64,6 +87,15 @@
     Var a(kInt, "a");
     Expression e4 = a;
     REPORTER_ASSERT(r, e4.release()->description() == "a");
+
+    REPORTER_ASSERT(r, whitespace_insensitive_compare("", ""));
+    REPORTER_ASSERT(r, !whitespace_insensitive_compare("", "a"));
+    REPORTER_ASSERT(r, !whitespace_insensitive_compare("a", ""));
+    REPORTER_ASSERT(r, whitespace_insensitive_compare("a", "a"));
+    REPORTER_ASSERT(r, whitespace_insensitive_compare("abc", "abc"));
+    REPORTER_ASSERT(r, whitespace_insensitive_compare("abc", " abc "));
+    REPORTER_ASSERT(r, whitespace_insensitive_compare("a b  c  ", "\n\n\nabc"));
+    REPORTER_ASSERT(r, !whitespace_insensitive_compare("a b c  d", "\n\n\nabc"));
 }
 
 DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLFloat, r, ctxInfo) {
@@ -851,3 +883,105 @@
         ((a + 1)--).release();
     }
 }
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLBlock, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    Statement x = Block();
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "{ }"));
+    Var a(kInt, "a"), b(kInt, "b");
+    Statement y = Block(Declare(a, 1), Declare(b, 2), a = b);
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(y, "{ int a = 1; int b = 2; (a = b); }"));
+}
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLDeclare, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    Var a(kHalf4, "a"), b(kHalf4, "b");
+    Statement x = Declare(a);
+    REPORTER_ASSERT(r, x.release()->description() == "half4 a;");
+    Statement y = Declare(b, Half4(1));
+    REPORTER_ASSERT(r, y.release()->description() == "half4 b = half4(1.0);");
+
+    {
+        Var c(kHalf4, "c");
+        ExpectError error(r, "error: expected 'half4', but found 'int'\n");
+        Declare(c, 1).release();
+    }
+}
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLDo, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    Statement x = Do(Block(), true);
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "do {} while (true);"));
+
+    Var a(kFloat, "a"), b(kFloat, "b");
+    Statement y = Do(Block(a++, --b), a != b);
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(y, "do { a++; --b; } while ((a != b));"));
+
+    {
+        ExpectError error(r, "error: expected 'bool', but found 'int'\n");
+        Do(Block(), 7).release();
+    }
+}
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLFor, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    Statement x = For(Statement(), Expression(), Expression(), Block());
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "for (;;) {}"));
+
+    Var i(kInt, "i");
+    Statement y = For(Declare(i, 0), i < 10, ++i, i += 5);
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(y,
+                                                      "for (int i = 0; (i < 10); ++i) (i += 5);"));
+
+    {
+        ExpectError error(r, "error: expected 'bool', but found 'int'\n");
+        For(i = 0, i + 10, ++i, i += 5).release();
+    }
+}
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLIf, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    Var a(kFloat, "a"), b(kFloat, "b");
+    Statement x = If(a > b, a -= b);
+    REPORTER_ASSERT(r, x.release()->description() == "if ((a > b)) (a -= b);");
+
+    Statement y = If(a > b, a -= b, b -= a);
+    REPORTER_ASSERT(r, y.release()->description() == "if ((a > b)) (a -= b); else (b -= a);");
+
+    {
+        ExpectError error(r, "error: expected 'bool', but found 'float'\n");
+        If(a + b, a -= b).release();
+    }
+}
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLTernary, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    Var a(kInt, "a");
+    Expression x = Ternary(a > 0, 1, -1);
+    REPORTER_ASSERT(r, x.release()->description() == "((a > 0) ? 1 : -1)");
+
+    {
+        ExpectError error(r, "error: expected 'bool', but found 'int'\n");
+        Ternary(a, 1, -1).release();
+    }
+
+    {
+        ExpectError error(r, "error: ternary operator result mismatch: 'float2', 'float3'\n");
+        Ternary(a > 0, Float2(1), Float3(1)).release();
+    }
+}
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLWhile, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    Statement x = While(true, Block());
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "for (; true;) {}"));
+
+    Var a(kFloat, "a"), b(kFloat, "b");
+    Statement y = While(a != b, Block(a++, --b));
+    REPORTER_ASSERT(r, whitespace_insensitive_compare(y, "for (; (a != b);) { a++; --b; }"));
+
+    {
+        ExpectError error(r, "error: expected 'bool', but found 'int'\n");
+        While(7, Block()).release();
+    }
+}