Browse Source

implement regular expressions

Kolja Strohm 7 months ago
parent
commit
7abf9d2f93

+ 20 - 20
DX12PixelShader.h

@@ -92,10 +92,10 @@ ret
 
 const BYTE DX12PixelShaderBytes[] =
 {
-     68,  88,  66,  67,  77,  82, 
-    194, 213,   8,  69,  52,  93, 
-    183, 250,  94, 100, 251, 175, 
-     11, 229,   1,   0,   0,   0, 
+     68,  88,  66,  67,  84, 183, 
+     45,  34, 237,  19,  96, 231, 
+     54, 248, 106,  42,  77, 181, 
+     83,  15,   1,   0,   0,   0, 
     184,  91,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
      36,   2,   0,   0, 188,   2, 
@@ -763,11 +763,11 @@ const BYTE DX12PixelShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0, 148,  46, 
-     49,   1,  37, 248, 242, 100, 
-      1,   0,   0,   0, 121, 110, 
-    225, 205,  49, 208, 173,  70, 
-    156, 218, 100, 199, 135, 105, 
-     65, 108,   0,   0,   0,   0, 
+     49,   1, 246,  48,   6, 101, 
+      1,   0,   0,   0, 229, 169, 
+     71, 101, 191,   5,  92,  73, 
+    162, 216, 150,  28, 145,  60, 
+    127,  33,   0,   0,   0,   0, 
       0,   0,   0,   0,   1,   0, 
       0,   0,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -938,9 +938,9 @@ const BYTE DX12PixelShaderBytes[] =
       3,   0, 242,  56,   1,   0, 
      43, 236,   3,   0,  28,  19, 
       2,   0,  65,  36,   1,   0, 
-    236, 179,   1,   0, 174,  44, 
+    236, 179,   1,   0, 213, 174, 
       3,   0, 125,  10,   2,   0, 
-    125, 181,   2,   0, 252, 225, 
+    125, 181,   2,   0, 155, 129, 
       2,   0, 193,  33,   3,   0, 
      65, 185,   2,   0, 140, 239, 
       1,   0, 246,  49,   0,   0, 
@@ -1788,7 +1788,7 @@ const BYTE DX12PixelShaderBytes[] =
     117, 114, 101,  50,  68,  32, 
     115, 104,  97, 100,  27, 226, 
      48,   1, 128,   0,   0,   0, 
-     66, 237, 151,   2, 123, 221, 
+    173, 233,  95,  11, 240, 232, 
     217,   1,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -2348,14 +2348,14 @@ const BYTE DX12PixelShaderBytes[] =
       0,   0,  23,   0,   1,   0, 
       5,  16,   0,   0,  14,   0, 
      23,  21,   0,  16,   0,   0, 
-      3,   2, 240, 101,   0,   0, 
+      3,   2,  16, 146,   0,   0, 
     242, 241,  10,   0,  24,  21, 
       8,  16,   0,   0,   1,   0, 
       1,   0,  10,   0,  24,  21, 
       9,  16,   0,   0,   1,   0, 
       0,   2,  14,   0,  23,  21, 
       0,   0,   0,   0,  10,   2, 
-    240, 101,   0,   0, 242, 241, 
+     16, 146,   0,   0, 242, 241, 
      10,   0,  24,  21,  11,  16, 
       0,   0,   1,   0,   1,   0, 
      10,   0,  24,  21,  12,  16, 
@@ -3408,11 +3408,11 @@ const BYTE DX12PixelShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
-    148,  46,  49,   1,  37, 248, 
-    242, 100,   1,   0,   0,   0, 
-    121, 110, 225, 205,  49, 208, 
-    173,  70, 156, 218, 100, 199, 
-    135, 105,  65, 108, 128,   0, 
+    148,  46,  49,   1, 246,  48, 
+      6, 101,   1,   0,   0,   0, 
+    229, 169,  71, 101, 191,   5, 
+     92,  73, 162, 216, 150,  28, 
+    145,  60, 127,  33, 128,   0, 
       0,   0,  47,  76, 105, 110, 
     107,  73, 110, 102, 111,   0, 
      47, 110,  97, 109, 101, 115, 
@@ -3512,7 +3512,7 @@ const BYTE DX12PixelShaderBytes[] =
       0,   0,   2,   0,   9,   0, 
     220,   4,   0,   0,   0,   0, 
       0,   0, 156,   1,   0,   0, 
-      1,   0, 227,  42,   0,   0, 
+      1,   0, 133, 152,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0, 109,  97, 
     105, 110,   0, 110, 111, 110, 

+ 17 - 17
DX12VertexShader.h

@@ -131,10 +131,10 @@ ret
 
 const BYTE DX12VertexShaderBytes[] =
 {
-     68,  88,  66,  67, 227, 161, 
-    106, 168, 213,  74, 153, 171, 
-    208,  72, 247, 139,  92,  47, 
-     24,  32,   1,   0,   0,   0, 
+     68,  88,  66,  67,  82, 250, 
+     47,  18,  46,  26, 254, 190, 
+     65, 219,  42, 169,  26,  62, 
+      3, 216,   1,   0,   0,   0, 
     144,  78,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
     124,   2,   0,   0,  52,   3, 
@@ -923,11 +923,11 @@ const BYTE DX12VertexShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
-    148,  46,  49,   1,  37, 248, 
-    242, 100,   1,   0,   0,   0, 
-     36, 144, 209, 113, 172, 241, 
-     34,  67, 129, 147,  52,  44, 
-    136, 154,  75, 177,   0,   0, 
+    148,  46,  49,   1, 246,  48, 
+      6, 101,   1,   0,   0,   0, 
+    102, 229, 210, 190, 194,  21, 
+    176,  65, 169, 171, 206, 158, 
+     61, 246,  36, 216,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       1,   0,   0,   0,   1,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -1103,7 +1103,7 @@ const BYTE DX12VertexShaderBytes[] =
       0,   0, 103, 159,   1,   0, 
     179, 120,   1,   0, 238,  97, 
       2,   0,  90,  28,   0,   0, 
-    219,  57,   2,   0,  53, 174, 
+    149, 154,   0,   0,  53, 174, 
       3,   0, 206,  21,   0,   0, 
     193, 205,   3,   0, 207, 193, 
       1,   0,  62,   3,   3,   0, 
@@ -1607,7 +1607,7 @@ const BYTE DX12VertexShaderBytes[] =
      97, 109, 101, 114,  97,  13, 
      10, 115, 116, 114,  27, 226, 
      48,   1, 128,   0,   0,   0, 
-     27,  92, 174,   2, 123, 221, 
+     36, 216, 117,  11, 240, 232, 
     217,   1,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -2125,7 +2125,7 @@ const BYTE DX12VertexShaderBytes[] =
      24,  21,  12,  16,   0,   0, 
       1,   0,   1,   0,  14,   0, 
      23,  21,  13,  16,   0,   0, 
-     36,   2, 176,  76,   0,   0, 
+     36,   2, 112,  33,   0,   0, 
     242, 241,  10,   0,  24,  21, 
      14,  16,   0,   0,   1,   0, 
       0,   2,  18,   0,  22,  21, 
@@ -3057,10 +3057,10 @@ const BYTE DX12VertexShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0, 148,  46,  49,   1, 
-     37, 248, 242, 100,   1,   0, 
-      0,   0,  36, 144, 209, 113, 
-    172, 241,  34,  67, 129, 147, 
-     52,  44, 136, 154,  75, 177, 
+    246,  48,   6, 101,   1,   0, 
+      0,   0, 102, 229, 210, 190, 
+    194,  21, 176,  65, 169, 171, 
+    206, 158,  61, 246,  36, 216, 
     129,   0,   0,   0,  47,  76, 
     105, 110, 107,  73, 110, 102, 
     111,   0,  47, 110,  97, 109, 
@@ -3160,7 +3160,7 @@ const BYTE DX12VertexShaderBytes[] =
       0,   0,   0,   0,   2,   0, 
       9,   0, 156,   5,   0,   0, 
       0,   0,   0,   0, 236,   2, 
-      0,   0,   1,   0, 107,  76, 
+      0,   0,   1,   0,  49,  33, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
     109,  97, 105, 110,   0, 110, 

+ 2 - 0
Framework Linux.vcxproj

@@ -152,6 +152,7 @@
     <ClCompile Include="Random.cpp" />
     <ClInclude Include="RCPointer.h" />
     <ClCompile Include="ReferenceCounter.cpp" />
+    <ClCompile Include="Regex.cpp" />
     <ClCompile Include="Schrift.cpp" />
     <ClCompile Include="Scroll.cpp" />
     <ClCompile Include="Tabelle.cpp" />
@@ -232,6 +233,7 @@
     <ClInclude Include="Reader.h" />
     <ClInclude Include="Rect2.h" />
     <ClInclude Include="ReferenceCounter.h" />
+    <ClInclude Include="Regex.h" />
     <ClInclude Include="Schrift.h" />
     <ClInclude Include="Scroll.h" />
     <ClInclude Include="Tabelle.h" />

+ 9 - 0
Framework Linux.vcxproj.filters

@@ -41,6 +41,9 @@
     <Filter Include="Framework\IO">
       <UniqueIdentifier>{8fe27319-4ce1-4796-8e09-054c878718f5}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Framework\Regex">
+      <UniqueIdentifier>{42480f75-d75d-4cf5-9189-98430e60c23e}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Model2D.h">
@@ -298,6 +301,9 @@
     <ClInclude Include="RCPointer.h">
       <Filter>Framework\Data</Filter>
     </ClInclude>
+    <ClInclude Include="Regex.h">
+      <Filter>Framework\Regex</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Bild.cpp">
@@ -486,5 +492,8 @@
     <ClCompile Include="Model3DCollection.cpp">
       <Filter>Framework\Grafik\3D</Filter>
     </ClCompile>
+    <ClCompile Include="Regex.cpp">
+      <Filter>Framework\Regex</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 1 - 0
Framework Tests/Framework Tests.vcxproj

@@ -175,6 +175,7 @@
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
     </ClCompile>
+    <ClCompile Include="Regex.cpp" />
     <ClCompile Include="Text.cpp" />
     <ClCompile Include="Trie.cpp" />
     <ClCompile Include="XML.cpp" />

+ 3 - 0
Framework Tests/Framework Tests.vcxproj.filters

@@ -39,6 +39,9 @@
     <ClCompile Include="Trie.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="Regex.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="pch.h">

+ 290 - 0
Framework Tests/Regex.cpp

@@ -0,0 +1,290 @@
+#include "pch.h"
+
+#include <Regex.h>
+
+#include "CppUnitTest.h"
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace FrameworkTests
+{
+    TEST_CLASS (RegexTests)
+    {
+        TEST_METHOD (MatchTest1)
+        {
+            auto parser = Framework::Regex::parse("ab+(c{3,6})?|b(a)");
+            auto result = parser->match("abccccab", (int)strlen("abccccab"));
+            Assert::IsTrue(result->getEintragAnzahl() == 2,
+                L"Invalid result count while matching 'abccccab' against "
+                L"'ab+(c{3,6})?|b(a)'");
+            auto* first = result->z(0);
+            auto* second = result->z(1);
+            Assert::IsTrue(first->getStart() == 0 && first->getEnd() == 6,
+                L"Invalid first result while matching 'abccccab' against "
+                L"'ab+(c{3,6})?|b(a)'");
+            Assert::IsTrue(first->getGroup(1).getStart() == 2
+                               && first->getGroup(1).getEnd() == 6,
+                L"Invalid group 1 in result 1 while matching 'abccccab' "
+                L"against "
+                L"'ab+(c{3,6})?|b(a)'");
+            Assert::IsTrue(first->getGroupCount() == 2,
+                L"Invalid group count in result 1 while matching 'abccccab' "
+                L"against "
+                L"'ab+(c{3,6})?|b(a)'");
+            Assert::IsTrue(second->getStart() == 6 && second->getEnd() == 8,
+                L"Invalid second result while matching 'abccccab' against "
+                L"'ab+(c{3,6})?|b(a)'");
+            Assert::IsTrue(second->getGroupCount() == 1,
+                L"Invalid group count in result 2 while matching 'abccccab' "
+                L"against "
+                L"'ab+(c{3,6})?|b(a)'");
+            result->release();
+            parser->release();
+        }
+
+        TEST_METHOD (MatchTest2)
+        {
+            auto parser = Framework::Regex::parse("abc");
+            auto result
+                = parser->match(" aabc abcc", (int)strlen(" aabc abcc"));
+            Assert::IsTrue(result->getEintragAnzahl() == 2,
+                L"Invalid result count while matching ' aabc abcc' against "
+                L"'abc'");
+            auto* first = result->z(0);
+            auto* second = result->z(1);
+            Assert::IsTrue(first->getStart() == 2 && first->getEnd() == 5,
+                L"Invalid first result while matching ' aabc abcc' against "
+                L"'abc'");
+            Assert::IsTrue(first->getGroupCount() == 1,
+                L"Invalid group count in result 1 while matching ' aabc abcc' "
+                L"against "
+                L"'abc'");
+            Assert::IsTrue(second->getStart() == 6 && second->getEnd() == 9,
+                L"Invalid second result while matching ' aabc abcc' against "
+                L"'abc'");
+            Assert::IsTrue(second->getGroupCount() == 1,
+                L"Invalid group count in result 2 while matching ' aabc abcc' "
+                L"against "
+                L"'abc'");
+            result->release();
+            parser->release();
+        }
+
+        TEST_METHOD (MatchTest3)
+        {
+            auto parser = Framework::Regex::parse("(?:abc)*");
+            auto result = parser->match("_aabcabcc", (int)strlen(" aabcabcc")); 
+            Assert::IsTrue(result->getEintragAnzahl() == 5,
+                L"Invalid result count while matching '_aabcabcc' against "
+                L"'(?:abc)*'");
+            auto* first = result->z(0); 
+            auto* second = result->z(1); 
+            auto* third = result->z(2); 
+            auto* forth = result->z(3);
+            auto* fifth = result->z(4);
+            Assert::IsTrue(first->getStart() == 0 && first->getEnd() == 0,
+                L"Invalid first result while matching '_aabcabcc' against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(first->getGroupCount() == 1,
+                L"Invalid group count in result 1 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(second->getStart() == 1 && second->getEnd() == 1,
+                L"Invalid second result while matching '_aabcabcc' against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(second->getGroupCount() == 1,
+                L"Invalid group count in result 2 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(third->getStart() == 2 && third->getEnd() == 8,
+                L"Invalid third result while matching '_aabcabcc' against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(third->getGroupCount() == 1,
+                L"Invalid group count in result 3 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(forth->getStart() == 8 && forth->getEnd() == 8,
+                L"Invalid result 4 while matching '_aabcabcc' against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(forth->getGroupCount() == 1,
+                L"Invalid group count in result 4 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(fifth->getStart() == 9 && fifth->getEnd() == 9,
+                L"Invalid result 5 while matching '_aabcabcc' against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(fifth->getGroupCount() == 1,
+                L"Invalid group count in result 5 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            result->release();
+            parser->release();
+        }
+
+        TEST_METHOD (MatchTest4)
+        {
+            auto parser = Framework::Regex::parse("(?:abc)*?");
+            auto result = parser->match("_aabcabcc", (int)strlen(" aabcabcc"));
+            Assert::IsTrue(result->getEintragAnzahl() == 8,
+                L"Invalid result count while matching '_aabcabcc' against "
+                L"'(?:abc)*?'"); 
+            auto* _0 = result->z(0); 
+            auto* _1 = result->z(1);
+            auto* _2 = result->z(2);
+            auto* _3 = result->z(3);
+            auto* _4 = result->z(4);
+            auto* _5 = result->z(5);
+            auto* _6 = result->z(6);
+            auto* _7 = result->z(7);
+            Assert::IsTrue(_0->getStart() == 0 && _0->getEnd() == 0,
+                L"Invalid result 1 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_0->getGroupCount() == 1,
+                L"Invalid group count in result 1 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_1->getStart() == 1 && _1->getEnd() == 1,
+                L"Invalid result 2 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_1->getGroupCount() == 1,
+                L"Invalid group count in result 2 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_2->getStart() == 2 && _2->getEnd() == 2,
+                L"Invalid result 3 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_2->getGroupCount() == 1,
+                L"Invalid group count in result 3 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_3->getStart() == 2 && _3->getEnd() == 5,
+                L"Invalid result 4 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_3->getGroupCount() == 1,
+                L"Invalid group count in result 4 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(_4->getStart() == 5 && _4->getEnd() == 5,
+                L"Invalid result 5 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_4->getGroupCount() == 1,
+                L"Invalid group count in result 5 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(_5->getStart() == 5 && _5->getEnd() == 8,
+                L"Invalid result 6 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_5->getGroupCount() == 1,
+                L"Invalid group count in result 6 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(_6->getStart() == 8 && _6->getEnd() == 8,
+                L"Invalid result 7 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_6->getGroupCount() == 1,
+                L"Invalid group count in result 7 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*'");
+            Assert::IsTrue(_7->getStart() == 9 && _7->getEnd() == 9,
+                L"Invalid result 8 while matching '_aabcabcc' against "
+                L"'(?:abc)*?'");
+            Assert::IsTrue(_7->getGroupCount() == 1,
+                L"Invalid group count in result 8 while matching '_aabcabcc' "
+                L"against "
+                L"'(?:abc)*?'");
+            result->release();
+            parser->release();
+        }
+
+        TEST_METHOD (MatchTest5)
+        {
+            auto parser = Framework::Regex::parse("(a|b)+");
+            auto result = parser->match("cabaccccbab", (int)strlen("cabaccccbab"));
+            Assert::IsTrue(result->getEintragAnzahl() == 2,
+                L"Invalid result count while matching 'cabaccccbab' against "
+                L"'a|b'");
+            auto* first = result->z(0);
+            auto* second = result->z(1);
+            Assert::IsTrue(first->getStart() == 1 && first->getEnd() == 4,
+                L"Invalid first result while matching 'cabaccccbab' against "
+                L"'a|b'");
+            Assert::IsTrue(first->getGroupCount() == 2,
+                L"Invalid group count in result 1 while matching 'cabaccccbab' "
+                L"against "
+                L"'a|b'");
+            Assert::IsTrue(first->getGroup(1).getStart() == 3
+                               && first->getGroup(1).getEnd() == 4,
+                L"Invalid group 1 in result 1 while matching 'cabaccccbab' "
+                L"against "
+                L"'a|b'");
+            Assert::IsTrue(second->getStart() == 8 && second->getEnd() == 11,
+                L"Invalid second result while matching 'cabaccccbab' against "
+                L"'a|b'");
+            Assert::IsTrue(second->getGroupCount() == 2,
+                L"Invalid group count in result 2 while matching 'cabaccccbab' "
+                L"against "
+                L"'a|b'");
+            Assert::IsTrue(second->getGroup(1).getStart() == 10
+                               && second->getGroup(1).getEnd() == 11,
+                L"Invalid group 1 in result 2 while matching 'cabaccccbab' "
+                L"against "
+                L"'a|b'");
+            result->release();
+            parser->release();
+        }
+
+        TEST_METHOD (MatchTest6)
+        {
+            auto parser = Framework::Regex::parse("(?:^|c)ab");
+            auto result
+                = parser->match("abcabcasd", (int)strlen("abcabcasd"));
+            Assert::IsTrue(result->getEintragAnzahl() == 2,
+                L"Invalid result count while matching 'abcabcasd' against "
+                L"'(?:^|c)ab'");
+            auto* first = result->z(0);
+            auto* second = result->z(1);
+            Assert::IsTrue(first->getStart() == 0 && first->getEnd() == 2,
+                L"Invalid first result while matching 'abcabcasd' against "
+                L"'(?:^|c)ab'");
+            Assert::IsTrue(first->getGroupCount() == 1,
+                L"Invalid group count in result 1 while matching 'abcabcasd' "
+                L"against "
+                L"'(?:^|c)ab'");
+            Assert::IsTrue(second->getStart() == 2 && second->getEnd() == 5,
+                L"Invalid second result while matching 'abcabcasd' against "
+                L"'(?:^|c)ab'");
+            Assert::IsTrue(second->getGroupCount() == 1,
+                L"Invalid group count in result 2 while matching 'abcabcasd' "
+                L"against "
+                L"'(?:^|c)ab'");
+            result->release();
+            parser->release();
+        }
+
+        TEST_METHOD (MatchTestComplexity)
+        {
+            Framework::Text regex = "";
+            Framework::Text str = "";
+            for (int i = 0; i < 20; ++i)
+            {
+                regex += "a?";
+                str += "a";
+            }
+            for (int i = 0; i < 20; ++i)
+            {
+                regex += "a";
+            }
+            auto parser = Framework::Regex::parse(regex);
+            auto result = parser->match(str, str.getLength());
+            Assert::IsTrue(result->getEintragAnzahl() == 1,
+                L"Invalid result count while matching 'a^20' against "
+                L"a?^20a^20");
+            auto* first = result->z(0);
+            Assert::IsTrue(first->getStart() == 0 && first->getEnd() == 20,
+                L"Invalid first result while matching 'a^20' against "
+                L"'a?^20a^20'");
+            result->release();
+            parser->release();
+        }
+    };
+} // namespace FrameworkTests

+ 2 - 0
Framework.vcxproj

@@ -203,6 +203,7 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     <ClInclude Include="Array.h" />
     <ClInclude Include="AsynchronCall.h" />
     <ClInclude Include="AuswahlBox.h" />
+    <ClInclude Include="Regex.h" />
     <ClInclude Include="Base64.h" />
     <ClInclude Include="Bild.h" />
     <ClInclude Include="Bildschirm.h" />
@@ -302,6 +303,7 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     <ClCompile Include="Animation3D.cpp" />
     <ClCompile Include="AsynchronCall.cpp" />
     <ClCompile Include="AuswahlBox.cpp" />
+    <ClCompile Include="Regex.cpp" />
     <ClCompile Include="Base64.cpp" />
     <ClCompile Include="Bild.cpp" />
     <ClCompile Include="Bildschirm.cpp" />

+ 9 - 0
Framework.vcxproj.filters

@@ -47,6 +47,9 @@
     <Filter Include="Framework\IO">
       <UniqueIdentifier>{b8b352cd-1fe4-4229-b600-127da384f3b5}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Framework\Regex">
+      <UniqueIdentifier>{b2e8cd5b-8f2f-4212-9bcb-90d07446a32e}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Model2D.h">
@@ -343,6 +346,9 @@
     <ClInclude Include="Base64.h">
       <Filter>Framework</Filter>
     </ClInclude>
+    <ClInclude Include="Regex.h">
+      <Filter>Framework\Regex</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Model3DCollection.h">
@@ -585,6 +591,9 @@
     <ClCompile Include="Model3DCollection.cpp">
       <Filter>Framework\Grafik\3D</Filter>
     </ClCompile>
+    <ClCompile Include="Regex.cpp">
+      <Filter>Framework\Regex</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <FxCompile Include="DX12VertexShader.hlsl">

+ 5 - 4
JSON.cpp

@@ -1077,13 +1077,14 @@ JSONValue* JSONMissingOneOf::getValidPart(
     {
         if (e->hasAttribute("default"))
         {
-            JSONValue *default = Parser::getValue(e->getAttributeValue("default"));
-            if (default)
+            JSONValue *defaultValue = Parser::getValue(e->getAttributeValue("default"));
+            if (defaultValue)
             {
                 JSONValue *valid = JSONValidator(
                     dynamic_cast<XML::Element*>(e->getThis()))
-                    .getValidParts(default, removedPartsValidationResults);
-                default->release();
+                          .getValidParts(
+                              defaultValue, removedPartsValidationResults);
+                defaultValue->release();
                 if (valid)
                 {
 					return valid;

+ 605 - 0
Regex.cpp

@@ -0,0 +1,605 @@
+#include "Regex.h"
+
+using namespace Framework;
+using namespace Regex;
+
+int findEndPos(Text* regex, int startPos, char endChar, char startChar)
+{
+    int length = regex->getLength();
+    int i = startPos;
+    int opened = 0;
+    while (i < length)
+    {
+        if (regex->getText()[i] == '\\')
+        {
+            i++;
+        }
+        else if (regex->getText()[i] == startChar)
+        {
+            opened++;
+        }
+        else if (regex->getText()[i] == endChar)
+        {
+            if (opened == 0)
+            {
+                return i;
+            }
+            opened--;
+        }
+        i++;
+    }
+    return -1;
+}
+
+Automata<char>* parseCharacterList(Text* regex, RegexConfig& config)
+{
+    bool negated = regex->hatAt(0, "^");
+    Automata<char>* result = new Automata<char>();
+    State<char>* start = result->addState();
+    State<char>* end = result->addState();
+    end->setFinal(true);
+    int length = regex->getLength();
+    bool escaped = false;
+    bool minusValid = false;
+    Text characterList;
+    std::function<bool(char, int)> negatedFunction
+        = [](char c, int flags) { return true; };
+    for (int i = 0; i < length; i++)
+    {
+        switch (regex->getText()[i])
+        {
+        case '\\':
+            {
+                escaped = true;
+                minusValid = false;
+                break;
+            }
+        case '-':
+            if (escaped || !minusValid)
+            {
+                characterList += '-';
+            }
+            else if (minusValid)
+            {
+                if (i == length - 1)
+                {
+                    characterList += '-';
+                }
+                else
+                {
+                    unsigned char before = (unsigned char)
+                        characterList[characterList.getLength() - 1];
+                    characterList.remove(characterList.getLength() - 1, 1);
+                    unsigned char after
+                        = (unsigned char)regex->getText()[i + 1];
+                    if (before > after)
+                    {
+                        unsigned char temp = before;
+                        before = after;
+                        after = temp;
+                    }
+                    for (unsigned char c = before; c <= after; c++)
+                    {
+                        characterList += c;
+                    }
+                    i++;
+                }
+                break;
+            }
+        default:
+            {
+                if (escaped)
+                {
+                    if (regex->getText()[i] == 'w')
+                    {
+                        if (negated)
+                        {
+                            negatedFunction
+                                = [config, negatedFunction](char c, int flags) {
+                                      return negatedFunction(c, flags)
+                                          && !config.getWordChars().hat(c);
+                                  };
+                        }
+                        else
+                        {
+                            characterList += config.getWordChars();
+                        }
+                        minusValid = false;
+                    }
+                    else if (regex->getText()[i] == 'W')
+                    {
+                        if (negated)
+                        {
+                            characterList += config.getWordChars();
+                        }
+                        else
+                        {
+                            start->addTransition(Transition<char>(
+                                [config](char c, int flags) {
+                                    return !config.getWordChars().hat(c);
+                                },
+                                end,
+                                0));
+                        }
+                        minusValid = false;
+                    }
+                    else if (regex->getText()[i] == 'd')
+                    {
+                        if (negated)
+                        {
+                            negatedFunction
+                                = [config, negatedFunction](char c, int flags) {
+                                      return negatedFunction(c, flags)
+                                          && (c < '0' || c > '9');
+                                  };
+                        }
+                        else
+                        {
+                            characterList.append("0123456789");
+                        }
+                        minusValid = false;
+                    }
+                    else if (regex->getText()[i] == 's')
+                    {
+                        if (negated)
+                        {
+                            negatedFunction = [config, negatedFunction](
+                                                  char c, int flags) {
+                                return negatedFunction(c, flags)
+                                    && !config.getWhitespaceChars().hat(c);
+                            };
+                        }
+                        else
+                        {
+                            characterList.append(config.getWhitespaceChars());
+                        }
+                        minusValid = false;
+                    }
+                    else if (regex->getText()[i] == 'D')
+                    {
+                        if (negated)
+                        {
+                            negatedFunction
+                                = [negatedFunction](char c, int flags) {
+                                      return negatedFunction(c, flags)
+                                          && (c >= '0' && c <= '9');
+                                  };
+                        }
+                        else
+                        {
+                            start->addTransition(Transition<char>(
+                                [](char c, int flags) {
+                                    return c < '0' || c > '9';
+                                },
+                                end,
+                                0));
+                        }
+                        minusValid = false;
+                    }
+                    else if (regex->getText()[i] == 'S')
+                    {
+                        if (negated)
+                        {
+                            negatedFunction
+                                = [config, negatedFunction](char c, int flags) {
+                                      return negatedFunction(c, flags)
+                                          && config.getWhitespaceChars().hat(c);
+                                  };
+                        }
+                        else
+                        {
+                            start->addTransition(Transition<char>(
+                                [config](char c, int flags) {
+                                    return !config.getWhitespaceChars().hat(c);
+                                },
+                                end,
+                                0));
+                        }
+                        minusValid = false;
+                    }
+                    else if (regex->getText()[i] == 'n')
+                    {
+                        characterList.append('\n');
+                    }
+                    else if (regex->getText()[i] == 'r')
+                    {
+                        characterList.append('\r');
+                    }
+                    else if (regex->getText()[i] == 't')
+                    {
+                        characterList.append('\t');
+                    }
+                    else
+                    {
+                        characterList.append(regex->getText()[i]);
+                        minusValid = true;
+                    }
+                }
+                else
+                {
+                    characterList.append(regex->getText()[i]);
+                    minusValid = true;
+                }
+                break;
+            }
+        }
+    }
+    if (negated)
+    {
+        negatedFunction = [characterList, negatedFunction](char c, int flags) {
+            return negatedFunction(c, flags) && !characterList.hat(c);
+        };
+        start->addTransition(Transition<char>(negatedFunction, end, 0));
+    }
+    else
+    {
+        start->addTransition(Transition<char>(
+            [characterList](char c, int flags) { return characterList.hat(c); },
+            end,
+            0));
+    }
+    regex->release();
+    return result;
+}
+
+Automata<char>* concat(Array<Automata<char>*> parsers)
+{
+    if (parsers.getEintragAnzahl() == 0)
+    {
+        Automata<char>* result = new Automata<char>();
+        State<char>* start = result->addState();
+        State<char>* end = result->addState();
+        end->setFinal(true);
+        start->addTransition(Transition<char>(0, end, 0));
+        return result;
+    }
+    Automata<char>* result = 0;
+    for (Automata<char>* parser : parsers)
+    {
+        if (result == 0)
+        {
+            result = parser;
+        }
+        else
+        {
+            result = concat(result, parser);
+        }
+    }
+    return result;
+}
+
+Automata<char>* parse(Text* regex, int& nextGroupId, RegexConfig& config)
+{
+    Array<Automata<char>*> results;
+    int length = regex->getLength();
+    for (int i = 0; i < length; i++)
+    {
+        bool escaped = false;
+        switch (regex->getText()[i])
+        {
+        case '\\':
+            {
+                escaped = true;
+                i++;
+                break;
+            }
+        case '(':
+            {
+                int end = findEndPos(regex, i + 1, ')', '(');
+                Text* subRegex = regex->getTeilText(i + 1, end);
+                Automata<char>* subAutomata = 0;
+                if (subRegex->hatAt(0, "?:"))
+                {
+                    subRegex->remove(0, 2);
+                    subAutomata = parse(subRegex, nextGroupId, config);
+                }
+                else
+                {
+                    int groupId = nextGroupId++;
+                    subAutomata
+                        = group(parse(subRegex, nextGroupId, config), groupId);
+                }
+                results.add(subAutomata);
+                i = end;
+                break;
+            }
+        case '[':
+            {
+                int end = findEndPos(regex, i + 1, ']', 0);
+                Text* subRegex = regex->getTeilText(i + 1, end);
+                Automata<char>* subAutomata
+                    = parseCharacterList(subRegex, config);
+                results.add(subAutomata);
+                i = end;
+                break;
+            }
+        case '?':
+            {
+                Automata<char>* last
+                    = results.get(results.getEintragAnzahl() - 1);
+                results.remove(results.getEintragAnzahl() - 1);
+                results.add(maybe(last));
+                break;
+            }
+        case '*':
+            {
+                bool lazy = false;
+                if (i < length - 1 && regex->getText()[i + 1] == '?')
+                {
+                    lazy = true;
+                    i++;
+                }
+                Automata<char>* last
+                    = results.get(results.getEintragAnzahl() - 1);
+                results.remove(results.getEintragAnzahl() - 1);
+                results.add(many(last, lazy));
+                break;
+            }
+        case '+':
+            {
+                bool lazy = false;
+                if (i < length - 1 && regex->getText()[i + 1] == '?')
+                {
+                    lazy = true;
+                    i++;
+                }
+                Automata<char>* last
+                    = results.get(results.getEintragAnzahl() - 1);
+                results.remove(results.getEintragAnzahl() - 1);
+                results.add(atLeastOnce(last, lazy));
+                break;
+            }
+        case '.':
+            {
+                Automata<char>* any = new Automata<char>();
+                State<char>* start = any->addState();
+                State<char>* end = any->addState();
+                end->setFinal(true);
+                start->addTransition(Transition<char>(
+                    [](char c, int flags) { return true; }, end, 0));
+                results.add(any);
+                break;
+            }
+        case '|':
+            {
+                Automata<char>* result = oneOf(concat(results),
+                    parse(regex->getTeilText(i + 1), nextGroupId, config));
+                regex->release();
+                return result;
+            }
+        case '{':
+            {
+                int end = findEndPos(regex, i + 1, '}', 0);
+                Text* subRegex = regex->getTeilText(i + 1, end);
+                int min = 0;
+                int max = 0;
+                if (subRegex->hat(","))
+                {
+                    Text* minText
+                        = subRegex->getTeilText(0, subRegex->positionVon(","));
+                    Text* maxText
+                        = subRegex->getTeilText(subRegex->positionVon(",") + 1);
+                    min = (int)*minText;
+                    max = (int)*maxText;
+                    minText->release();
+                    maxText->release();
+                }
+                else
+                {
+                    min = (int)*subRegex;
+                    max = min;
+                }
+                Automata<char>* last
+                    = results.get(results.getEintragAnzahl() - 1);
+                results.remove(results.getEintragAnzahl() - 1);
+                results.add(repeat(last, min, max));
+                i = end;
+                subRegex->release();
+                break;
+            }
+        case '^':
+            {
+                Automata<char>* qutomata = new Automata<char>();
+                State<char>* start = qutomata->addState();
+                State<char>* end = qutomata->addState();
+                end->setFinal(true);
+                start->addTransition(Transition<char>(0, end, Flags::START));
+                results.add(qutomata);
+                break;
+            }
+        case '$':
+            {
+                Automata<char>* qutomata = new Automata<char>();
+                State<char>* start = qutomata->addState();
+                State<char>* end = qutomata->addState();
+                end->setFinal(true);
+                start->addTransition(Transition<char>(0, end, Flags::END));
+                results.add(qutomata);
+                break;
+            }
+        default:
+            {
+                Automata<char>* automata = new Automata<char>();
+                State<char>* start = automata->addState();
+                State<char>* end = automata->addState();
+                end->setFinal(true);
+                char current = regex->getText()[i];
+                start->addTransition(Transition<char>(
+                    [current](char c, int flags) { return c == current; },
+                    end,
+                    0));
+                results.add(automata);
+                break;
+            }
+        }
+        if (escaped)
+        {
+            std::function<bool(char, int)> function;
+            switch (regex->getText()[i])
+            {
+            case 'n':
+                {
+                    function = [](char c, int flags) { return c == '\n'; };
+                    break;
+                }
+            case 'r':
+                {
+                    function = [](char c, int flags) { return c == '\r'; };
+                    break;
+                }
+            case 't':
+                {
+                    function = [](char c, int flags) { return c == '\t'; };
+                    break;
+                }
+            case 'w':
+                {
+                    function = [config](char c, int flags) {
+                        return config.getWordChars().hat(c);
+                    };
+                    break;
+                }
+            case 'W':
+                {
+                    function = [config](char c, int flags) {
+                        return !config.getWordChars().hat(c);
+                    };
+                    break;
+                }
+            case 'd':
+                {
+                    function = [](char c, int flags) {
+                        return c >= '0' && c <= '9';
+                    };
+                    break;
+                }
+            case 'D':
+                {
+                    function
+                        = [](char c, int flags) { return c < '0' || c > '9'; };
+                    break;
+                }
+            case 's':
+                {
+                    function = [config](char c, int flags) {
+                        return config.getWhitespaceChars().hat(c);
+                    };
+                    break;
+                }
+            case 'S':
+                {
+                    function = [config](char c, int flags) {
+                        return !config.getWhitespaceChars().hat(c);
+                    };
+                    break;
+                }
+            default:
+                {
+                    char current = regex->getText()[i];
+                    function
+                        = [current](char c, int flags) { return c == current; };
+                    break;
+                }
+            }
+            Automata<char>* automata = new Automata<char>();
+            State<char>* start = automata->addState();
+            State<char>* end = automata->addState();
+            end->setFinal(true);
+            start->addTransition(Transition<char>(function, end, 0));
+            results.add(automata);
+        }
+    }
+    regex->release();
+    return concat(results);
+}
+
+Framework::Regex::Group::Group(int start, int end, int id)
+    : start(start),
+      end(end),
+      id(id)
+{}
+
+Framework::Regex::Group::Group()
+    : start(-1),
+      end(-1),
+      id(-1)
+{}
+
+int Framework::Regex::Group::getStart()
+{
+    return start;
+}
+
+int Framework::Regex::Group::getEnd()
+{
+    return end;
+}
+
+int Framework::Regex::Group::getId()
+{
+    return id;
+}
+
+Framework::Regex::Result::Result(Array<Group> groups)
+    : ReferenceCounter(),
+      groups(groups)
+{}
+
+int Framework::Regex::Result::getGroupCount()
+{
+    return groups.getEintragAnzahl();
+}
+
+Group Framework::Regex::Result::getGroup(int index)
+{
+    return groups.get(index);
+}
+
+int Framework::Regex::Result::getStart()
+{
+    return groups.get(0).getStart();
+}
+
+int Framework::Regex::Result::getEnd()
+{
+    return groups.get(0).getEnd();
+}
+
+RegexConfig::RegexConfig()
+{
+    whitespaceChars = " \n\r\t";
+    wordChars
+        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+}
+
+void RegexConfig::setWhitespaceChars(Text whitespaceChars)
+{
+    this->whitespaceChars = whitespaceChars;
+}
+
+void RegexConfig::setWordChars(Text wordChars)
+{
+    this->wordChars = wordChars;
+}
+
+Text RegexConfig::getWhitespaceChars() const
+{
+    return whitespaceChars;
+}
+
+Text RegexConfig::getWordChars() const
+{
+    return wordChars;
+}
+
+Automata<char>* Framework::Regex::parse(Text regex)
+{
+    RegexConfig config;
+    return parse(regex, config);
+}
+
+Automata<char>* Framework::Regex::parse(Text regex, RegexConfig& config)
+{
+    int nextGroupId = 1;
+    return ::parse(new Text(regex), nextGroupId, config);
+}

+ 846 - 0
Regex.h

@@ -0,0 +1,846 @@
+#pragma once
+
+#include <functional>
+
+#include "Array.h"
+#include "Critical.h"
+#include "Text.h"
+
+namespace Framework
+{
+    namespace Regex
+    {
+        class Flags
+        {
+        public:
+            static const int START = 1;
+            static const int END = 2;
+        };
+        template<typename Data> class State;
+
+        template<typename Data> class Transition
+        {
+        private:
+            std::function<bool(Data, int)> conditionFunction;
+            State<Data>* zTarget;
+            int requiredFlags;
+
+        public:
+            Transition()
+                : conditionFunction(0),
+                  zTarget(0)
+            {}
+
+            Transition(std::function<bool(Data, int)> conditionFunction,
+                State<Data>* zTarget,
+                int requiredFlags)
+                : conditionFunction(conditionFunction),
+                  zTarget(zTarget),
+                  requiredFlags(requiredFlags)
+            {}
+
+            Transition(const Transition& other)
+                : conditionFunction(other.conditionFunction),
+                  zTarget(other.zTarget)
+            {}
+
+            bool test(Data value, int flags) const
+            {
+                return conditionFunction && conditionFunction(value, flags)
+                    && (requiredFlags | flags) == flags;
+            }
+
+            bool test(int flags) const
+            {
+                return !conditionFunction && (requiredFlags | flags) == flags;
+            }
+
+            State<Data>* zTargetState() const
+            {
+                return zTarget;
+            }
+
+            std::function<bool(Data, int)> getConditionFunction() const
+            {
+                return conditionFunction;
+            }
+
+            int getRequiredFlags() const
+            {
+                return requiredFlags;
+            }
+        };
+
+        template<typename Data> class State : public ReferenceCounter
+        {
+        private:
+            Array<Transition<Data>> transitions;
+            bool final;
+            int groupStart;
+            int groupEnd;
+            int id;
+
+        public:
+            State(int id)
+                : ReferenceCounter(),
+                  final(false),
+                  groupStart(0),
+                  groupEnd(0),
+                  id(id)
+            {}
+
+            void addTransition(Transition<Data> transition)
+            {
+                transitions.add(transition);
+            }
+
+            void setFinal(bool isFinal)
+            {
+                final = isFinal;
+            }
+
+            void setGroupStart(int groupStart)
+            {
+                this->groupStart = groupStart;
+            }
+
+            void setGroupEnd(int groupEnd)
+            {
+                this->groupEnd = groupEnd;
+            }
+
+            bool isFinal() const
+            {
+                return final;
+            }
+
+            int getGroupStart() const
+            {
+                return groupStart;
+            }
+
+            int getGroupEnd() const
+            {
+                return groupEnd;
+            }
+
+            int getId() const
+            {
+                return id;
+            }
+
+            const Array<Transition<Data>>& getTransitions() const
+            {
+                return transitions;
+            }
+        };
+
+        class Group
+        {
+        private:
+            int start;
+            int end;
+            int id;
+
+        public:
+            DLLEXPORT Group(int start, int end, int id);
+            DLLEXPORT Group();
+
+            DLLEXPORT int getStart();
+            DLLEXPORT int getEnd();
+            DLLEXPORT int getId();
+        };
+
+        class Result : public ReferenceCounter
+        {
+        private:
+            Array<Group> groups;
+
+        public:
+            DLLEXPORT Result(Array<Group> groups);
+
+            DLLEXPORT int getGroupCount();
+            DLLEXPORT Group getGroup(int index);
+            DLLEXPORT int getStart();
+            DLLEXPORT int getEnd();
+        };
+
+        template<typename Data> class ExecutionStackFrame
+        {
+        private:
+            ExecutionStackFrame* before;
+            State<Data>* state;
+            int transitionIndex;
+            int strIndex;
+
+        public:
+            ExecutionStackFrame(ExecutionStackFrame<Data>* before,
+                State<Data>* zState,
+                int strIndex)
+                : before(before),
+                  state(zState),
+                  transitionIndex(0),
+                  strIndex(strIndex)
+            {}
+
+            ~ExecutionStackFrame()
+            {
+                if (before)
+                {
+                    delete before;
+                }
+            }
+
+            ExecutionStackFrame<Data>* getBefore() const
+            {
+                return before;
+            }
+
+            State<Data>* zState() const
+            {
+                return state;
+            }
+
+            void setStrIndex(int index)
+            {
+                strIndex = index;
+            }
+
+            int getStrIndex() const
+            {
+                return strIndex;
+            }
+
+            int getTransitionIndex() const
+            {
+                return transitionIndex;
+            }
+
+            void setTransitionIndex(int transitionIndex)
+            {
+                this->transitionIndex = transitionIndex;
+            }
+
+            void discard()
+            {
+                before = 0;
+            }
+
+            Array<Group> getGroups() const
+            {
+                Array<Group> groups;
+                const ExecutionStackFrame* current = this;
+                while (true)
+                {
+                    if (current->getBefore())
+                    {
+                        current = current->getBefore();
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                groups.add(Group(current->getStrIndex(), strIndex, 0));
+                current = this;
+                Array<int> groupEnds;
+                Array<int> knownGroups;
+                while (current)
+                {
+                    if (current->zState()->getGroupEnd())
+                    {
+                        groupEnds.add(current->getStrIndex());
+                    }
+                    if (current->zState()->getGroupStart()
+                        && groupEnds.getEintragAnzahl() > 0)
+                    {
+                        if (knownGroups.getWertIndex(
+                                current->zState()->getGroupStart())
+                            < 0)
+                        {
+                            while (groups.getEintragAnzahl()
+                                   <= current->zState()->getGroupStart())
+                            {
+                                groups.add(
+                                    Group(-1, -1, groups.getEintragAnzahl()));
+                            }
+                            groups.set(Group(current->getStrIndex(),
+                                           groupEnds.get(0),
+                                           current->zState()->getGroupStart()),
+                                current->zState()->getGroupStart());
+                            knownGroups.add(current->zState()->getGroupStart());
+                        }
+                        groupEnds.remove(0);
+                    }
+                    current = current->getBefore();
+                }
+                return groups;
+            }
+
+            bool isEndlessLoop() const
+            {
+                const ExecutionStackFrame* current = this;
+                while (current)
+                {
+                    if (current->getBefore()
+                        && current->getBefore()->zState() == state
+                        && current->getBefore()->getStrIndex() == strIndex)
+                    {
+                        return true;
+                    }
+                    current = current->getBefore();
+                }
+                return false;
+            }
+        };
+
+        template<typename Data> class Automata : public ReferenceCounter
+        {
+        private:
+            RCArray<State<Data>> states;
+
+        public:
+            Automata()
+                : ReferenceCounter()
+            {}
+
+            State<Data>* addState()
+            {
+                State<Data>* state = new State<Data>(states.getEintragAnzahl());
+                states.add(state);
+                return state;
+            }
+
+            const RCArray<State<Data>>& getStates() const
+            {
+                return states;
+            }
+
+            RCArray<Result>* match(const Data* str, int length)
+            {
+                RCArray<Result>* results = new RCArray<Result>();
+                ExecutionStackFrame<Data>* currentFrame
+                    = new ExecutionStackFrame<Data>(0, states.z(0), 0);
+                int flags = Flags::START;
+                int startIndex = 0;
+                while (currentFrame)
+                {
+                    if (currentFrame->getStrIndex() >= length)
+					{
+						flags |= Flags::END;
+                    }
+                    else
+                    {
+						flags &= ~Flags::END;
+                    }
+                    if (currentFrame->getStrIndex() == 0)
+                    {
+                        flags |= Flags::START;
+                    }
+                    else
+                    {
+                        flags &= ~Flags::START;
+                    }
+                    if (currentFrame->zState()->isFinal())
+                    {
+                        results->add(new Result(currentFrame->getGroups()));
+                        int index = currentFrame->getStrIndex();
+                        if (startIndex < index)
+                        {
+                            delete currentFrame;
+                            currentFrame = new ExecutionStackFrame<Data>(
+                                0, states.z(0), index);
+                            startIndex = currentFrame->getStrIndex();
+                            if (currentFrame->getStrIndex() > length)
+                            {
+                                delete currentFrame;
+                                currentFrame = 0;
+                                continue;
+                            }
+                        }
+                    }
+                    if (currentFrame->isEndlessLoop())
+                    {
+                        delete currentFrame;
+                        results->release();
+                        return 0; // endless loop
+                    }
+                    bool found = 0;
+                    for (int i = currentFrame->getTransitionIndex();
+                         i < currentFrame->zState()
+                                 ->getTransitions()
+                                 .getEintragAnzahl();
+                         i++)
+                    {
+                        Transition<Data> t
+                            = currentFrame->zState()->getTransitions().get(i);
+                        if (t.test(flags))
+                        {
+                            currentFrame->setTransitionIndex(i + 1);
+                            currentFrame
+                                = new ExecutionStackFrame<Data>(currentFrame,
+                                    t.zTargetState(),
+                                    currentFrame->getStrIndex());
+                            found = 1;
+                            break;
+                        }
+                        else if (currentFrame->getStrIndex() < length
+                                 && t.test(
+                                     str[currentFrame->getStrIndex()], flags))
+                        {
+                            currentFrame->setTransitionIndex(i + 1);
+                            currentFrame
+                                = new ExecutionStackFrame<Data>(currentFrame,
+                                    t.zTargetState(),
+                                    currentFrame->getStrIndex() + 1);
+                            found = 1;
+                            break;
+                        }
+                    }
+                    if (!found)
+                    {
+                        ExecutionStackFrame<Data>* before
+                            = currentFrame->getBefore();
+                        if (before)
+                        {
+                            currentFrame->discard();
+                            delete currentFrame;
+                            currentFrame = before;
+                        }
+                        else
+                        {
+                            currentFrame->setStrIndex(
+                                currentFrame->getStrIndex() + 1);
+                            startIndex = currentFrame->getStrIndex();
+                            currentFrame->setTransitionIndex(0);
+                            if (currentFrame->getStrIndex() > length)
+                            {
+                                delete currentFrame;
+                                currentFrame = 0;
+                            }
+                        }
+                    }
+                }
+                return results;
+            }
+        };
+
+        template<typename Data>
+        Automata<Data>* oneOf(Automata<Data>* left, Automata<Data>* right)
+        {
+            Automata<Data>* result = new Automata<Data>();
+            State<Data>* start = result->addState();
+            int leftStateCount = left->getStates().getEintragAnzahl();
+            for (State<Data>* leftState : left->getStates())
+            {
+                if (leftState)
+                {
+                    result->addState();
+                }
+            }
+            for (State<Data>* leftState : left->getStates())
+            {
+                State<Data>* newState
+                    = result->getStates().z(leftState->getId() + 1);
+                newState->setFinal(leftState->isFinal());
+                newState->setGroupStart(leftState->getGroupStart());
+                newState->setGroupEnd(leftState->getGroupEnd());
+                for (Transition<Data> transition : leftState->getTransitions())
+                {
+                    newState->addTransition(
+                        Transition<Data>(transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId() + 1),
+                            transition.getRequiredFlags()));
+                }
+            }
+            start->addTransition(
+                Transition<Data>(0, result->getStates().z(1), 0));
+            for (State<Data>* rightState : right->getStates())
+            {
+                if (rightState)
+                {
+                    result->addState();
+                }
+            }
+            for (State<Data>* rightState : right->getStates())
+            {
+                State<Data>* newState = result->getStates().z(
+                    rightState->getId() + 1 + leftStateCount);
+                newState->setFinal(rightState->isFinal());
+                newState->setGroupStart(rightState->getGroupStart());
+                newState->setGroupEnd(rightState->getGroupEnd());
+                for (Transition<Data> transition : rightState->getTransitions())
+                {
+                    newState->addTransition(Transition<Data>(
+                        transition.getConditionFunction(),
+                        result->getStates().z(transition.zTargetState()->getId()
+                                              + 1 + leftStateCount),
+                        transition.getRequiredFlags()));
+                }
+            }
+            start->addTransition(Transition<Data>(
+                0, result->getStates().z(1 + leftStateCount), 0));
+            left->release();
+            right->release();
+            return result;
+        }
+
+        template<typename Data>
+        Automata<Data>* concat(Automata<Data>* left, Automata<Data>* right)
+        {
+            Automata<Data>* result = new Automata<Data>();
+            int leftStateCount = left->getStates().getEintragAnzahl();
+            for (State<Data>* leftState : left->getStates())
+            {
+                if (leftState)
+                {
+                    result->addState();
+                }
+            }
+            bool canSkipFirst = !right->getStates().z(0)->getGroupStart()
+                             && !right->getStates().z(0)->getGroupEnd();
+            int index = 0;
+            for (State<Data>* rightState : right->getStates())
+            {
+                if (rightState && (index != 0 || !canSkipFirst))
+                {
+                    result->addState();
+                }
+                index++;
+            }
+            for (State<Data>* leftState : left->getStates())
+            {
+                State<Data>* newState
+                    = result->getStates().z(leftState->getId());
+                newState->setGroupStart(leftState->getGroupStart());
+                newState->setGroupEnd(leftState->getGroupEnd());
+                for (Transition<Data> transition : leftState->getTransitions())
+                {
+                    newState->addTransition(
+                        Transition<Data>(transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId()),
+                            transition.getRequiredFlags()));
+                }
+                if (leftState->isFinal())
+                {
+                    if (canSkipFirst)
+                    {
+                        State<Data>* oldRightStart = right->getStates().z(0);
+                        if (oldRightStart->isFinal())
+                        {
+                            newState->setFinal(true);
+                        }
+                        for (Transition<Data> transition :
+                            oldRightStart->getTransitions())
+                        {
+                            newState->addTransition(Transition<Data>(
+                                transition.getConditionFunction(),
+                                result->getStates().z(
+                                    leftStateCount - 1
+                                    + transition.zTargetState()->getId()),
+                                transition.getRequiredFlags()));
+                        }
+                    }
+                    else
+                    {
+                        newState->addTransition(Transition<Data>(
+                            0, result->getStates().z(leftStateCount), 0));
+                    }
+                }
+            }
+            index = 0;
+            for (State<Data>* rightState : right->getStates())
+            {
+                if (index == 0 && canSkipFirst)
+                {
+                    index++;
+                    continue;
+                }
+                State<Data>* newState
+                    = result->getStates().z(rightState->getId() + leftStateCount
+                                            - (canSkipFirst ? 1 : 0));
+                newState->setFinal(rightState->isFinal());
+                newState->setGroupStart(rightState->getGroupStart());
+                newState->setGroupEnd(rightState->getGroupEnd());
+                for (Transition<Data> transition : rightState->getTransitions())
+                {
+                    newState->addTransition(
+                        Transition<Data>(transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId()
+                                + leftStateCount - (canSkipFirst ? 1 : 0)),
+                            transition.getRequiredFlags()));
+                }
+                index++;
+            }
+            left->release();
+            right->release();
+            return result;
+        }
+
+        template<typename Data>
+        Automata<Data>* many(Automata<Data>* before, bool lazy)
+        {
+            Automata<Data>* result = new Automata<Data>();
+            for (State<Data>* beforeState : before->getStates())
+            {
+                if (beforeState)
+                {
+                    result->addState();
+                }
+            }
+            State<Data>* newFinal = result->addState();
+            if (lazy)
+            {
+                result->getStates().z(0)->addTransition(
+                    Transition<Data>(0, newFinal, 0));
+            }
+            newFinal->setFinal(true);
+            for (State<Data>* beforeState : before->getStates())
+            {
+                State<Data>* newState
+                    = result->getStates().z(beforeState->getId());
+                newState->setGroupStart(beforeState->getGroupStart());
+                newState->setGroupEnd(beforeState->getGroupEnd());
+                for (Transition<Data> transition :
+                    beforeState->getTransitions())
+                {
+                    newState->addTransition(
+                        Transition<Data>(transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId()),
+                            transition.getRequiredFlags()));
+                }
+                if (beforeState->isFinal())
+                {
+                    newState->addTransition(
+                        Transition<Data>(0, result->getStates().z(0), 0));
+                }
+            }
+            if (!lazy)
+            {
+                result->getStates().z(0)->addTransition(
+                    Transition<Data>(0, newFinal, 0));
+            }
+            before->release();
+            return result;
+        }
+
+        template<typename Data>
+        Automata<Data>* atLeastOnce(Automata<Data>* before, bool lazy)
+        {
+            Automata<Data>* result = new Automata<Data>();
+            for (State<Data>* beforeState : before->getStates())
+            {
+                if (beforeState)
+                {
+                    result->addState();
+                }
+            }
+            State<Data>* newFinal = result->addState();
+            newFinal->setFinal(true);
+            for (State<Data>* beforeState : before->getStates())
+            {
+                State<Data>* newState
+                    = result->getStates().z(beforeState->getId());
+                newState->setGroupStart(beforeState->getGroupStart());
+                newState->setGroupEnd(beforeState->getGroupEnd());
+                if (lazy && beforeState->isFinal())
+                {
+                    newState->addTransition(Transition<Data>(0, newFinal, 0));
+                }
+                for (Transition<Data> transition :
+                    beforeState->getTransitions())
+                {
+                    newState->addTransition(
+                        Transition<Data>(transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId()),
+                            transition.getRequiredFlags()));
+                }
+                if (beforeState->isFinal())
+                {
+                    newState->addTransition(
+                        Transition<Data>(0, result->getStates().z(0), 0));
+                }
+                if (!lazy && beforeState->isFinal())
+                {
+                    newState->addTransition(Transition<Data>(0, newFinal, 0));
+                }
+            }
+            before->release();
+            return result;
+        }
+
+        template<typename Data> Automata<Data>* maybe(Automata<Data>* before)
+        {
+            Automata<Data>* result = new Automata<Data>();
+            for (State<Data>* beforeState : before->getStates())
+            {
+                if (beforeState)
+                {
+                    result->addState();
+                }
+            }
+            State<Data>* newFinal = result->addState();
+            newFinal->setFinal(true);
+            for (State<Data>* beforeState : before->getStates())
+            {
+                State<Data>* newState
+                    = result->getStates().z(beforeState->getId());
+                newState->setGroupStart(beforeState->getGroupStart());
+                newState->setGroupEnd(beforeState->getGroupEnd());
+                for (Transition<Data> transition :
+                    beforeState->getTransitions())
+                {
+                    newState->addTransition(
+                        Transition<Data>(transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId()),
+                            transition.getRequiredFlags()));
+                }
+                if (beforeState->isFinal())
+                {
+                    newState->addTransition(Transition<Data>(0, newFinal, 0));
+                }
+            }
+            result->getStates().z(0)->addTransition(
+                Transition<Data>(0, newFinal, 0));
+            before->release();
+            return result;
+        }
+
+        template<typename Data> Automata<Data>* repeat(
+            Automata<Data>* before, int minAmount, int maxAmount)
+        {
+            Automata<Data>* result = new Automata<Data>();
+            for (int i = 0; i < maxAmount; i++)
+            {
+                for (State<Data>* beforeState : before->getStates())
+                {
+                    if (beforeState)
+                    {
+                        result->addState();
+                    }
+                }
+                for (State<Data>* beforeState : before->getStates())
+                {
+                    State<Data>* newState = result->getStates().z(
+                        beforeState->getId()
+                        + before->getStates().getEintragAnzahl() * i);
+                    newState->setGroupStart(beforeState->getGroupStart());
+                    newState->setGroupEnd(beforeState->getGroupEnd());
+                    for (Transition<Data> transition :
+                        beforeState->getTransitions())
+                    {
+                        newState->addTransition(Transition<Data>(
+                            transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId()
+                                + before->getStates().getEintragAnzahl() * i),
+                            transition.getRequiredFlags()));
+                    }
+                }
+            }
+            State<Data>* newFinal = result->addState();
+            newFinal->setFinal(true);
+            if (minAmount == 0)
+            {
+                result->getStates().z(0)->addTransition(
+                    Transition<Data>(0, newFinal, 0));
+            }
+            for (int i = 0; i < maxAmount; i++)
+            {
+                for (State<Data>* beforeState : before->getStates())
+                {
+                    State<Data>* newState = result->getStates().z(
+                        beforeState->getId()
+                        + before->getStates().getEintragAnzahl() * i);
+                    if (beforeState->isFinal())
+                    {
+                        if (i < maxAmount - 1)
+                        {
+                            newState->addTransition(Transition<Data>(0,
+                                result->getStates().z(
+                                    before->getStates().getEintragAnzahl()
+                                    * (i + 1)),
+                                0));
+                        }
+                        if (i >= minAmount - 1)
+                        {
+                            newState->addTransition(
+                                Transition<Data>(0, newFinal, 0));
+                        }
+                    }
+                }
+            }
+            before->release();
+            return result;
+        }
+
+        template<typename Data>
+        Automata<Data>* group(Automata<Data>* before, int groupId)
+        {
+            Automata<Data>* result = new Automata<Data>();
+            State<Data>* newStart = result->addState();
+            newStart->setGroupStart(groupId);
+            State<Data>* groupStart = result->addState();
+            newStart->addTransition(Transition<Data>(0, groupStart, 0));
+            for (State<Data>* beforeState : before->getStates())
+            {
+                if (beforeState)
+                {
+                    result->addState();
+                }
+            }
+            groupStart->addTransition(
+                Transition<Data>(0, result->getStates().z(2), 0));
+            State<Data>* groupEnd = result->addState();
+            groupEnd->setGroupEnd(groupId);
+            State<Data>* newFinal = result->addState();
+            groupEnd->addTransition(Transition<Data>(0, newFinal, 0));
+            newFinal->setFinal(true);
+            for (State<Data>* beforeState : before->getStates())
+            {
+                State<Data>* newState
+                    = result->getStates().z(beforeState->getId() + 2);
+                newState->setGroupStart(beforeState->getGroupStart());
+                newState->setGroupEnd(beforeState->getGroupEnd());
+                for (Transition<Data> transition :
+                    beforeState->getTransitions())
+                {
+                    newState->addTransition(
+                        Transition<Data>(transition.getConditionFunction(),
+                            result->getStates().z(
+                                transition.zTargetState()->getId() + 2),
+                            transition.getRequiredFlags()));
+                }
+                if (beforeState->isFinal())
+                {
+                    newState->addTransition(Transition<Data>(0, groupEnd, 0));
+                }
+            }
+            before->release();
+            return result;
+        }
+
+        class RegexConfig
+        {
+        private:
+            Text whitespaceChars;
+            Text wordChars;
+
+        public:
+            DLLEXPORT RegexConfig();
+            DLLEXPORT void setWhitespaceChars(Text whitespaceChars);
+            DLLEXPORT void setWordChars(Text wordChars);
+            DLLEXPORT Text getWhitespaceChars() const;
+            DLLEXPORT Text getWordChars() const;
+        };
+
+        DLLEXPORT Automata<char>* parse(Text regex);
+        DLLEXPORT Automata<char>* parse(Text regex, RegexConfig& config);
+    } // namespace Regex
+} // namespace Framework

+ 1 - 1
Trie.h

@@ -272,7 +272,7 @@ namespace Framework
             map = new RCCharMap<RCTrie<T>, T>();
         }
 
-        TrieIterator<T, RCCharMap<RCTrie<T>, T>> getIterator()
+        TrieIterator<T, RCCharMap<RCTrie<T>, T>> getIterator() const
         {
             return TrieIterator<T, RCCharMap<RCTrie<T>, T>>(
                 TrieIteratorData<T, RCCharMap<RCTrie<T>, T>>(

+ 21 - 21
UIPixelShader.h

@@ -352,10 +352,10 @@ ret
 
 const BYTE UIPixelShader[] =
 {
-     68,  88,  66,  67,  30,  31, 
-    208, 123, 120, 206,  31, 151, 
-    181, 214,  32,  58, 179, 110, 
-    141, 183,   1,   0,   0,   0, 
+     68,  88,  66,  67,  35, 224, 
+    171, 170,  24,  28,  70, 161, 
+    126, 203, 125,  17,  65, 113, 
+    164,  61,   1,   0,   0,   0, 
      12, 134,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
     140,   6,   0,   0,  28,   7, 
@@ -1805,11 +1805,11 @@ const BYTE UIPixelShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0, 148,  46, 
-     49,   1,  38, 248, 242, 100, 
-      1,   0,   0,   0, 231,  80, 
-    145,  26,  53,  12, 175,  77, 
-    168,  96,  76, 138,  89,  20, 
-     83,   0,   0,   0,   0,   0, 
+     49,   1, 246,  48,   6, 101, 
+      1,   0,   0,   0, 186,  68, 
+    194,  98,  45, 234, 239,  76, 
+    143, 243, 253,  50,  51, 187, 
+     97, 209,   0,   0,   0,   0, 
       0,   0,   0,   0,   1,   0, 
       0,   0,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -1980,9 +1980,9 @@ const BYTE UIPixelShader[] =
       3,   0, 242,  56,   1,   0, 
      43, 236,   3,   0,  28,  19, 
       2,   0,  65,  36,   1,   0, 
-    236, 179,   1,   0, 174,  44, 
+    236, 179,   1,   0, 213, 174, 
       3,   0, 125,  10,   2,   0, 
-    125, 181,   2,   0, 252, 225, 
+    125, 181,   2,   0, 155, 129, 
       2,   0, 193,  33,   3,   0, 
      65, 185,   2,   0,   9, 241, 
       2,   0, 146, 230,   3,   0, 
@@ -3000,8 +3000,8 @@ const BYTE UIPixelShader[] =
      84, 101, 120, 116, 117, 114, 
     101,  50,  68,  32, 115, 104, 
      97, 100,  27, 226,  48,   1, 
-    128,   0,   0,   0, 222, 140, 
-    192,   2, 123, 221, 217,   1, 
+    128,   0,   0,   0,  65, 195, 
+    133,  11, 240, 232, 217,   1, 
       1,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -4243,14 +4243,14 @@ const BYTE UIPixelShader[] =
       6,  16,   0,   0,  23,   0, 
       1,   0,   5,  16,   0,   0, 
      14,   0,  23,  21,   0,  16, 
-      0,   0,   3,   2, 240, 101, 
+      0,   0,   3,   2,  16, 146, 
       0,   0, 242, 241,  10,   0, 
      24,  21,   8,  16,   0,   0, 
       1,   0,   1,   0,  10,   0, 
      24,  21,   9,  16,   0,   0, 
       1,   0,   0,   2,  14,   0, 
      23,  21,   0,   0,   0,   0, 
-     10,   2, 240, 101,   0,   0, 
+     10,   2,  16, 146,   0,   0, 
     242, 241,  10,   0,  24,  21, 
      11,  16,   0,   0,   1,   0, 
       1,   0,  10,   0,  24,  21, 
@@ -5474,11 +5474,11 @@ const BYTE UIPixelShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
-    148,  46,  49,   1,  38, 248, 
-    242, 100,   1,   0,   0,   0, 
-    231,  80, 145,  26,  53,  12, 
-    175,  77, 168,  96,  76, 138, 
-     89,  20,  83,   0, 128,   0, 
+    148,  46,  49,   1, 246,  48, 
+      6, 101,   1,   0,   0,   0, 
+    186,  68, 194,  98,  45, 234, 
+    239,  76, 143, 243, 253,  50, 
+     51, 187,  97, 209, 128,   0, 
       0,   0,  47,  76, 105, 110, 
     107,  73, 110, 102, 111,   0, 
      47, 110,  97, 109, 101, 115, 
@@ -5578,7 +5578,7 @@ const BYTE UIPixelShader[] =
       0,   0,   2,   0,   9,   0, 
     204,   8,   0,   0,   0,   0, 
       0,   0, 164,  14,   0,   0, 
-      1,   0, 140, 121,   0,   0, 
+      1,   0, 234, 205,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,  84, 101, 
     120, 116, 117, 114, 101,  80, 

+ 15 - 15
UIVertexShader.h

@@ -121,10 +121,10 @@ ret
 
 const BYTE UIVertexShader[] =
 {
-     68,  88,  66,  67,  83,  70, 
-     82,  21, 215, 163, 110, 205, 
-    115, 166,  36,  27,  32,  16, 
-     86, 243,   1,   0,   0,   0, 
+     68,  88,  66,  67, 212,  78, 
+     20, 171, 170,   7, 103, 117, 
+    208, 222,  93, 186, 198, 209, 
+    129,   1,   1,   0,   0,   0, 
     204,  77,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
      20,   2,   0,   0, 204,   2, 
@@ -881,10 +881,10 @@ const BYTE UIVertexShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0, 148,  46,  49,   1, 
-     38, 248, 242, 100,   1,   0, 
-      0,   0, 168, 240, 211, 221, 
-    164,  69, 173,  65, 150, 144, 
-     59, 238,  74,  51, 160,  62, 
+    246,  48,   6, 101,   1,   0, 
+      0,   0, 247, 139,  69, 221, 
+     29,  34, 124,  67, 159,  84, 
+     40, 234, 210, 161, 225,  96, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   1,   0,   0,   0, 
       1,   0,   0,   0,   0,   0, 
@@ -1564,8 +1564,8 @@ const BYTE UIVertexShader[] =
      13,  10,  47,  47,  32,  84, 
      89,  80,  69,  68,  69,  70, 
      27, 226,  48,   1, 128,   0, 
-      0,   0, 232, 182, 220,   2, 
-    123, 221, 217,   1,   1,   0, 
+      0,   0,  29,   3, 160,  11, 
+    240, 232, 217,   1,   1,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -2929,10 +2929,10 @@ const BYTE UIVertexShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0, 148,  46,  49,   1, 
-     38, 248, 242, 100,   1,   0, 
-      0,   0, 168, 240, 211, 221, 
-    164,  69, 173,  65, 150, 144, 
-     59, 238,  74,  51, 160,  62, 
+    246,  48,   6, 101,   1,   0, 
+      0,   0, 247, 139,  69, 221, 
+     29,  34, 124,  67, 159,  84, 
+     40, 234, 210, 161, 225,  96, 
     129,   0,   0,   0,  47,  76, 
     105, 110, 107,  73, 110, 102, 
     111,   0,  47, 110,  97, 109, 
@@ -3032,7 +3032,7 @@ const BYTE UIVertexShader[] =
       0,   0,   0,   0,   2,   0, 
       9,   0, 104,   5,   0,   0, 
       0,   0,   0,   0, 236,   2, 
-      0,   0,   1,   0, 202, 145, 
+      0,   0,   1,   0, 144, 238, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
      84, 101, 120, 116, 117, 114,