1 module tern.regex.matcher;
2 
3 import tern.state;
4 import tern.regex.builder;
5 import std.string;
6 import std.conv;
7 import std.algorithm;
8 import std.stdio;
9 
10 package class State
11 {
12 package:
13 final:
14     Element[] elements;
15     uint next;
16     ubyte flags;
17     uint index;
18 }
19 
20 package:
21 static:
22 pragma(inline, true)
23 pure bool isFulfilled(Element element, State state, string text)
24 {
25     const bool qlazy = element.modifiers.hasFlag(LAZY);
26     const bool qdef = state.next != -1 && !element.modifiers.hasFlag(GREEDY) && !qlazy;
27     foreach (k; 0..element.max)
28     {
29         if (qlazy && k >= element.min)
30             return true;
31         else if (qdef && k >= element.min && state.elements[state.next].isFulfilled(state, text))
32             return true;
33         
34         if (k != 0)
35             state.index++;
36 
37         if (state.index >= text.length)
38         {
39             state.index--;
40             return k >= element.min;
41         }
42         
43         switch (element.token)
44         {
45             case CHARACTERS:
46                 bool match;
47                 foreach (c; element.str)
48                 {
49                     if (text[state.index] == c)
50                         match = true;
51                 }
52 
53                 if (element.modifiers.hasFlag(EXCLUSIONARY) ? match : !match)
54                     return k >= element.min;
55                 break;
56 
57             case ANCHOR_START:
58                 return state.index == 0 || (state.flags.hasFlag(MULTILINE) && (text[state.index - 1] == '\r' || text[state.index - 1] == '\n' || text[state.index - 1] == '\f'));
59 
60             case ANCHOR_END:
61                 return state.index >= text.length || (state.flags.hasFlag(MULTILINE) && (text[state.index + 1] == '\r' || text[state.index + 1] == '\n' || text[state.index + 1] == '\f' || text[state.index + 1] == '\0'));
62 
63             case ANY:
64                 if (!state.flags.hasFlag(SINGLELINE) && (text[state.index] == '\r' || text[state.index] == '\n' || text[state.index] == '\f')) 
65                     return k >= element.min;
66                 break;
67 
68             case REFERENCE:
69                 
70 
71             default:
72                 return false;
73         }
74     }
75     return true;
76 }
77 
78 pragma(inline, true)
79 pure string[][] matchInternal(Element[] elements, ubyte flags, string text, uint stopAt = -1)
80 {
81     string[][] matches;
82     int k = 0;
83     int g = 1;
84     int j = 0;
85     
86     State state = new State();
87     state.elements = elements;
88     state.flags = flags;
89 
90     while (state.index < text.length)
91     {
92         if (matches.length == 0 || matches[$-1] != null)
93         {
94             matches ~= null;
95             matches[$-1] ~= null;
96         }
97         
98         for (; j < elements.length; j++)
99         {
100             Element element = elements[j];
101             debug element.str.writeln;
102             debug state.index.writeln;
103             uint ci = state.index;
104             state.next = j + 1 >= elements.length ? -1 : j + 1;
105 
106             if (stopAt == matches.length || (element.token != ANCHOR_END && state.index >= text.length))
107             {
108                 if (elements.length != 0 && j != elements.length)
109                     matches = matches[0..$-1];
110 
111                 if (matches.length != 0 && matches[$-1] == null)
112                     return matches[0..$-1];
113                 else
114                     return matches;
115             }
116 
117             if (element.isFulfilled(state, text))
118             {
119                 debug "fulfilled".writeln;
120                 if (state.index != ci)
121                     state.index--;
122                 
123                 if (element.min != 0 || element.modifiers.hasFlag(QUANTIFIED))
124                     matches[k][0] ~= text[ci..++state.index];
125             }
126             else if (element.token == RESET)
127             {
128                 debug "reset".writeln;
129                 matches[k] = null;
130             }
131             else if (element.modifiers.hasFlag(ALTERNATE))
132             {
133                 debug "skipped".writeln;
134                 continue;
135             }
136             else
137             {
138                 debug "unfulfilled".writeln;
139                 if (!elements[0].isFulfilled(state, text))
140                     state.index++;
141                 
142                 matches[k] = new string[1];
143                 debug writeln(matches[k][0] == null);
144                 j = -1;
145             }
146         }
147 
148         if (state.index >= text.length - 1)
149             break;
150 
151         if (matches[$-1][0] != null)
152             k++;
153         j = 0;
154     }
155 
156     if (elements.length != 0 && j != elements.length)
157         matches = matches[0..$-1];
158 
159     if (matches.length != 0 && matches[$-1] == null)
160         return matches[0..$-1];
161     else
162         return matches;
163 }