532
|
1 |
% Chapter 1
|
|
2 |
|
|
3 |
\chapter{Introduction} % Main chapter title
|
|
4 |
|
|
5 |
\label{Introduction} % For referencing the chapter elsewhere, use \ref{Chapter1}
|
|
6 |
|
|
7 |
%----------------------------------------------------------------------------------------
|
|
8 |
|
|
9 |
% Define some commands to keep the formatting separated from the content
|
|
10 |
\newcommand{\keyword}[1]{\textbf{#1}}
|
|
11 |
\newcommand{\tabhead}[1]{\textbf{#1}}
|
|
12 |
\newcommand{\code}[1]{\texttt{#1}}
|
|
13 |
\newcommand{\file}[1]{\texttt{\bfseries#1}}
|
|
14 |
\newcommand{\option}[1]{\texttt{\itshape#1}}
|
|
15 |
|
|
16 |
%boxes
|
|
17 |
\newcommand*{\mybox}[1]{\framebox{\strut #1}}
|
|
18 |
|
|
19 |
%\newcommand{\sflataux}[1]{\textit{sflat}\_\textit{aux} \, #1}
|
|
20 |
\newcommand\sflat[1]{\llparenthesis #1 \rrparenthesis }
|
|
21 |
\newcommand{\ASEQ}[3]{\textit{ASEQ}_{#1} \, #2 \, #3}
|
543
|
22 |
\newcommand{\bderssimp}[2]{#1 \backslash_{bsimps} #2}
|
596
|
23 |
\newcommand{\rderssimp}[2]{#1 \backslash_{rsimps} #2}
|
564
|
24 |
\def\derssimp{\textit{ders}\_\textit{simp}}
|
557
|
25 |
\def\rders{\textit{rders}}
|
532
|
26 |
\newcommand{\bders}[2]{#1 \backslash #2}
|
|
27 |
\newcommand{\bsimp}[1]{\textit{bsimp}(#1)}
|
591
|
28 |
\def\bsimps{\textit{bsimp}}
|
554
|
29 |
\newcommand{\rsimp}[1]{\textit{rsimp}\; #1}
|
532
|
30 |
\newcommand{\sflataux}[1]{\llparenthesis #1 \rrparenthesis'}
|
|
31 |
\newcommand{\dn}{\stackrel{\mbox{\scriptsize def}}{=}}%
|
|
32 |
\newcommand{\denote}{\stackrel{\mbox{\scriptsize denote}}{=}}%
|
|
33 |
\newcommand{\ZERO}{\mbox{\bf 0}}
|
|
34 |
\newcommand{\ONE}{\mbox{\bf 1}}
|
|
35 |
\newcommand{\AALTS}[2]{\oplus {\scriptstyle #1}\, #2}
|
555
|
36 |
\newcommand{\rdistinct}[2]{\textit{rdistinct} \;\; #1 \;\; #2}
|
594
|
37 |
\def\rdistincts{\textit{rdistinct}}
|
556
|
38 |
\def\rDistinct{\textit{rdistinct}}
|
532
|
39 |
\newcommand\hflat[1]{\llparenthesis #1 \rrparenthesis_*}
|
|
40 |
\newcommand\hflataux[1]{\llparenthesis #1 \rrparenthesis_*'}
|
|
41 |
\newcommand\createdByStar[1]{\textit{createdByStar}(#1)}
|
|
42 |
|
|
43 |
\newcommand\myequiv{\mathrel{\stackrel{\makebox[0pt]{\mbox{\normalfont\tiny equiv}}}{=}}}
|
|
44 |
|
600
|
45 |
\def\SEQ{\textit{SEQ}}
|
|
46 |
\def\SEQs{\textit{SEQs}}
|
564
|
47 |
\def\case{\textit{case}}
|
554
|
48 |
\def\sequal{\stackrel{\mbox{\scriptsize rsimp}}{=}}
|
|
49 |
\def\rsimpalts{\textit{rsimp}_{ALTS}}
|
|
50 |
\def\good{\textit{good}}
|
|
51 |
\def\btrue{\textit{true}}
|
|
52 |
\def\bfalse{\textit{false}}
|
542
|
53 |
\def\bnullable{\textit{bnullable}}
|
543
|
54 |
\def\bnullables{\textit{bnullables}}
|
538
|
55 |
\def\Some{\textit{Some}}
|
|
56 |
\def\None{\textit{None}}
|
537
|
57 |
\def\code{\textit{code}}
|
532
|
58 |
\def\decode{\textit{decode}}
|
|
59 |
\def\internalise{\textit{internalise}}
|
|
60 |
\def\lexer{\mathit{lexer}}
|
|
61 |
\def\mkeps{\textit{mkeps}}
|
557
|
62 |
\newcommand{\rder}[2]{#2 \backslash_r #1}
|
532
|
63 |
|
585
|
64 |
\def\rerases{\textit{rerase}}
|
|
65 |
|
554
|
66 |
\def\nonnested{\textit{nonnested}}
|
532
|
67 |
\def\AZERO{\textit{AZERO}}
|
558
|
68 |
\def\sizeNregex{\textit{sizeNregex}}
|
532
|
69 |
\def\AONE{\textit{AONE}}
|
|
70 |
\def\ACHAR{\textit{ACHAR}}
|
|
71 |
|
585
|
72 |
\def\simpsulz{\textit{simp}_{Sulz}}
|
|
73 |
|
557
|
74 |
\def\scfrewrites{\stackrel{*}{\rightsquigarrow_{scf}}}
|
555
|
75 |
\def\frewrite{\rightsquigarrow_f}
|
|
76 |
\def\hrewrite{\rightsquigarrow_h}
|
|
77 |
\def\grewrite{\rightsquigarrow_g}
|
|
78 |
\def\frewrites{\stackrel{*}{\rightsquigarrow_f}}
|
|
79 |
\def\hrewrites{\stackrel{*}{\rightsquigarrow_h}}
|
|
80 |
\def\grewrites{\stackrel{*}{\rightsquigarrow_g}}
|
538
|
81 |
\def\fuse{\textit{fuse}}
|
|
82 |
\def\bder{\textit{bder}}
|
542
|
83 |
\def\der{\textit{der}}
|
532
|
84 |
\def\POSIX{\textit{POSIX}}
|
|
85 |
\def\ALTS{\textit{ALTS}}
|
|
86 |
\def\ASTAR{\textit{ASTAR}}
|
|
87 |
\def\DFA{\textit{DFA}}
|
538
|
88 |
\def\NFA{\textit{NFA}}
|
532
|
89 |
\def\bmkeps{\textit{bmkeps}}
|
543
|
90 |
\def\bmkepss{\textit{bmkepss}}
|
532
|
91 |
\def\retrieve{\textit{retrieve}}
|
|
92 |
\def\blexer{\textit{blexer}}
|
|
93 |
\def\flex{\textit{flex}}
|
573
|
94 |
\def\inj{\textit{inj}}
|
564
|
95 |
\def\Empty{\textit{Empty}}
|
567
|
96 |
\def\Left{\textit{Left}}
|
|
97 |
\def\Right{\textit{Right}}
|
573
|
98 |
\def\Stars{\textit{Stars}}
|
|
99 |
\def\Char{\textit{Char}}
|
|
100 |
\def\Seq{\textit{Seq}}
|
532
|
101 |
\def\Der{\textit{Der}}
|
|
102 |
\def\Ders{\textit{Ders}}
|
|
103 |
\def\nullable{\mathit{nullable}}
|
|
104 |
\def\Z{\mathit{Z}}
|
|
105 |
\def\S{\mathit{S}}
|
|
106 |
\def\rup{r^\uparrow}
|
|
107 |
%\def\bderssimp{\mathit{bders}\_\mathit{simp}}
|
|
108 |
\def\distinctWith{\textit{distinctWith}}
|
|
109 |
\def\lf{\textit{lf}}
|
|
110 |
\def\PD{\textit{PD}}
|
|
111 |
\def\suffix{\textit{Suffix}}
|
543
|
112 |
\def\distinctBy{\textit{distinctBy}}
|
558
|
113 |
\def\starupdate{\textit{starUpdate}}
|
|
114 |
\def\starupdates{\textit{starUpdates}}
|
|
115 |
|
532
|
116 |
|
|
117 |
\def\size{\mathit{size}}
|
|
118 |
\def\rexp{\mathbf{rexp}}
|
|
119 |
\def\simp{\mathit{simp}}
|
|
120 |
\def\simpALTs{\mathit{simp}\_\mathit{ALTs}}
|
|
121 |
\def\map{\mathit{map}}
|
|
122 |
\def\distinct{\mathit{distinct}}
|
|
123 |
\def\blexersimp{\mathit{blexer}\_\mathit{simp}}
|
590
|
124 |
\def\blexerStrong{\textit{blexerStrong}}
|
|
125 |
\def\bsimpStrong{\textit{bsimpStrong}}
|
591
|
126 |
\def\bdersStrongs{\textit{bdersStrong}}
|
590
|
127 |
\newcommand{\bdersStrong}[2]{#1 \backslash_{bsimpStrongs} #2}
|
|
128 |
|
532
|
129 |
\def\map{\textit{map}}
|
|
130 |
\def\rrexp{\textit{rrexp}}
|
554
|
131 |
\newcommand\rnullable[1]{\textit{rnullable} \; #1 }
|
532
|
132 |
\newcommand\rsize[1]{\llbracket #1 \rrbracket_r}
|
|
133 |
\newcommand\asize[1]{\llbracket #1 \rrbracket}
|
543
|
134 |
\newcommand\rerase[1]{ (#1)_{\downarrow_r}}
|
|
135 |
|
538
|
136 |
\newcommand\ChristianComment[1]{\textcolor{blue}{#1}\\}
|
532
|
137 |
|
543
|
138 |
|
|
139 |
\def\rflts{\textit{rflts}}
|
|
140 |
\def\rrewrite{\textit{rrewrite}}
|
|
141 |
\def\bsimpalts{\textit{bsimp}_{ALTS}}
|
596
|
142 |
\def\bsimpaseq{\textit{bsimp}_{ASEQ}}
|
|
143 |
\def\rsimlalts{\textit{rsimp}_{ALTs}}
|
|
144 |
\def\rsimpseq{\textit{rsimp}_{SEQ}}
|
543
|
145 |
|
532
|
146 |
\def\erase{\textit{erase}}
|
|
147 |
\def\STAR{\textit{STAR}}
|
|
148 |
\def\flts{\textit{flts}}
|
|
149 |
|
|
150 |
|
579
|
151 |
\def\zeroable{\textit{zeroable}}
|
|
152 |
\def\nub{\textit{nub}}
|
|
153 |
\def\filter{\textit{filter}}
|
601
|
154 |
%\def\not{\textit{not}}
|
579
|
155 |
|
|
156 |
|
|
157 |
|
532
|
158 |
\def\RZERO{\mathbf{0}_r }
|
|
159 |
\def\RONE{\mathbf{1}_r}
|
|
160 |
\newcommand\RCHAR[1]{\mathbf{#1}_r}
|
|
161 |
\newcommand\RSEQ[2]{#1 \cdot #2}
|
558
|
162 |
\newcommand\RALTS[1]{\sum #1}
|
532
|
163 |
\newcommand\RSTAR[1]{#1^*}
|
558
|
164 |
\newcommand\vsuf[2]{\textit{Suffix} \;#1\;#2}
|
532
|
165 |
|
538
|
166 |
|
|
167 |
|
590
|
168 |
|
|
169 |
\lstdefinestyle{myScalastyle}{
|
|
170 |
frame=tb,
|
|
171 |
language=scala,
|
|
172 |
aboveskip=3mm,
|
|
173 |
belowskip=3mm,
|
|
174 |
showstringspaces=false,
|
|
175 |
columns=flexible,
|
|
176 |
basicstyle={\small\ttfamily},
|
|
177 |
numbers=none,
|
|
178 |
numberstyle=\tiny\color{gray},
|
|
179 |
keywordstyle=\color{blue},
|
|
180 |
commentstyle=\color{dkgreen},
|
|
181 |
stringstyle=\color{mauve},
|
|
182 |
frame=single,
|
|
183 |
breaklines=true,
|
|
184 |
breakatwhitespace=true,
|
|
185 |
tabsize=3,
|
538
|
186 |
}
|
|
187 |
|
590
|
188 |
|
532
|
189 |
%----------------------------------------------------------------------------------------
|
|
190 |
%This part is about regular expressions, Brzozowski derivatives,
|
|
191 |
%and a bit-coded lexing algorithm with proven correctness and time bounds.
|
|
192 |
|
|
193 |
%TODO: look up snort rules to use here--give readers idea of what regexes look like
|
|
194 |
|
601
|
195 |
|
|
196 |
|
|
197 |
|
|
198 |
|
|
199 |
|
|
200 |
Regular expressions are widely used in computer science:
|
|
201 |
be it in text-editors \parencite{atomEditor} with syntax highlighting and auto-completion;
|
|
202 |
command-line tools like $\mathit{grep}$ that facilitate easy
|
|
203 |
text-processing; network intrusion
|
612
|
204 |
detection systems that inspect suspicious traffic; or compiler
|
|
205 |
front ends.
|
|
206 |
Given their usefulness and ubiquity, one would assume that
|
601
|
207 |
modern regular expression matching implementations
|
|
208 |
are mature and fully studied.
|
602
|
209 |
Indeed, in a popular programming language's regex engine,
|
|
210 |
supplying it with regular expressions and strings,
|
|
211 |
in most cases one can
|
|
212 |
get the matching information in a very short time.
|
|
213 |
Those matchers can be blindingly fast--some
|
|
214 |
network intrusion detection systems
|
601
|
215 |
use regex engines that are able to process
|
|
216 |
megabytes or even gigabytes of data per second \parencite{Turo_ov__2020}.
|
602
|
217 |
However, those matchers can exhibit a surprising security vulnerability
|
|
218 |
under a certain class of inputs.
|
|
219 |
%However, , this is not the case for $\mathbf{all}$ inputs.
|
601
|
220 |
%TODO: get source for SNORT/BRO's regex matching engine/speed
|
|
221 |
|
603
|
222 |
|
612
|
223 |
Consider $(a^*)^*\,b$ and ask whether
|
|
224 |
strings of the form $aa..a$ can be matched by this regular
|
603
|
225 |
expression. Obviously this is not the case---the expected $b$ in the last
|
|
226 |
position is missing. One would expect that modern regular expression
|
612
|
227 |
matching engines can find this out very quickly. Surprisingly, if one tries
|
|
228 |
this example in JavaScript, Python or Java 8, even with small strings,
|
|
229 |
say of lenght of around 30 $a$'s,
|
|
230 |
the decision takes an absurd time to finish (see graphs in figure \ref{fig:aStarStarb}).
|
603
|
231 |
This is clearly exponential behaviour, and
|
612
|
232 |
is triggered by some relatively simple regular expressions.
|
603
|
233 |
Java 9 and newer
|
612
|
234 |
versions improve this behaviour somewhat, but is still slow compared
|
|
235 |
with the approach we are going to use in this thesis.
|
603
|
236 |
|
|
237 |
|
|
238 |
|
|
239 |
This superlinear blowup in regular expression engines
|
612
|
240 |
had repeatedly caused grief in ``real life'' where it is
|
|
241 |
given the name ``catastrophic backtracking'' or ``evil'' regular expressions.
|
603
|
242 |
For example, on 20 July 2016 one evil
|
|
243 |
regular expression brought the webpage
|
|
244 |
\href{http://stackexchange.com}{Stack Exchange} to its
|
|
245 |
knees.\footnote{\url{https://stackstatus.net/post/147710624694/outage-postmortem-july-20-2016}(Last accessed in 2019)}
|
|
246 |
In this instance, a regular expression intended to just trim white
|
|
247 |
spaces from the beginning and the end of a line actually consumed
|
|
248 |
massive amounts of CPU resources---causing web servers to grind to a
|
|
249 |
halt. In this example, the time needed to process
|
|
250 |
the string was $O(n^2)$ with respect to the string length. This
|
|
251 |
quadratic overhead was enough for the homepage of Stack Exchange to
|
|
252 |
respond so slowly that the load balancer assumed a $\mathit{DoS}$
|
|
253 |
attack and therefore stopped the servers from responding to any
|
|
254 |
requests. This made the whole site become unavailable.
|
|
255 |
|
601
|
256 |
\begin{figure}[p]
|
612
|
257 |
\begin{tabular}{@{}c@{\hspace{0mm}}c@{}}
|
532
|
258 |
\begin{tikzpicture}
|
|
259 |
\begin{axis}[
|
|
260 |
xlabel={$n$},
|
|
261 |
x label style={at={(1.05,-0.05)}},
|
|
262 |
ylabel={time in secs},
|
|
263 |
enlargelimits=false,
|
|
264 |
xtick={0,5,...,30},
|
|
265 |
xmax=33,
|
|
266 |
ymax=35,
|
|
267 |
ytick={0,5,...,30},
|
|
268 |
scaled ticks=false,
|
|
269 |
axis lines=left,
|
|
270 |
width=5cm,
|
|
271 |
height=4cm,
|
|
272 |
legend entries={JavaScript},
|
|
273 |
legend pos=north west,
|
|
274 |
legend cell align=left]
|
|
275 |
\addplot[red,mark=*, mark options={fill=white}] table {re-js.data};
|
|
276 |
\end{axis}
|
|
277 |
\end{tikzpicture}
|
|
278 |
&
|
|
279 |
\begin{tikzpicture}
|
|
280 |
\begin{axis}[
|
|
281 |
xlabel={$n$},
|
|
282 |
x label style={at={(1.05,-0.05)}},
|
|
283 |
%ylabel={time in secs},
|
|
284 |
enlargelimits=false,
|
|
285 |
xtick={0,5,...,30},
|
|
286 |
xmax=33,
|
|
287 |
ymax=35,
|
|
288 |
ytick={0,5,...,30},
|
|
289 |
scaled ticks=false,
|
|
290 |
axis lines=left,
|
|
291 |
width=5cm,
|
|
292 |
height=4cm,
|
|
293 |
legend entries={Python},
|
|
294 |
legend pos=north west,
|
|
295 |
legend cell align=left]
|
|
296 |
\addplot[blue,mark=*, mark options={fill=white}] table {re-python2.data};
|
|
297 |
\end{axis}
|
612
|
298 |
\end{tikzpicture}\\
|
601
|
299 |
\begin{tikzpicture}
|
|
300 |
\begin{axis}[
|
|
301 |
xlabel={$n$},
|
|
302 |
x label style={at={(1.05,-0.05)}},
|
|
303 |
ylabel={time in secs},
|
|
304 |
enlargelimits=false,
|
|
305 |
xtick={0,5,...,30},
|
|
306 |
xmax=33,
|
|
307 |
ymax=35,
|
|
308 |
ytick={0,5,...,30},
|
|
309 |
scaled ticks=false,
|
|
310 |
axis lines=left,
|
|
311 |
width=5cm,
|
|
312 |
height=4cm,
|
612
|
313 |
legend entries={Java 8},
|
601
|
314 |
legend pos=north west,
|
|
315 |
legend cell align=left]
|
612
|
316 |
\addplot[cyan,mark=*, mark options={fill=white}] table {re-java.data};
|
601
|
317 |
\end{axis}
|
|
318 |
\end{tikzpicture}
|
|
319 |
&
|
|
320 |
\begin{tikzpicture}
|
|
321 |
\begin{axis}[
|
|
322 |
xlabel={$n$},
|
|
323 |
x label style={at={(1.05,-0.05)}},
|
|
324 |
%ylabel={time in secs},
|
|
325 |
enlargelimits=false,
|
|
326 |
xtick={0,5,...,30},
|
|
327 |
xmax=33,
|
|
328 |
ymax=35,
|
|
329 |
ytick={0,5,...,30},
|
|
330 |
scaled ticks=false,
|
|
331 |
axis lines=left,
|
|
332 |
width=5cm,
|
|
333 |
height=4cm,
|
612
|
334 |
legend entries={Dart},
|
|
335 |
legend pos=north west,
|
|
336 |
legend cell align=left]
|
|
337 |
\addplot[green,mark=*, mark options={fill=white}] table {re-dart.data};
|
|
338 |
\end{axis}
|
|
339 |
\end{tikzpicture}\\
|
|
340 |
\begin{tikzpicture}
|
|
341 |
\begin{axis}[
|
|
342 |
xlabel={$n$},
|
|
343 |
x label style={at={(1.05,-0.05)}},
|
|
344 |
ylabel={time in secs},
|
|
345 |
enlargelimits=false,
|
|
346 |
xtick={0,5,...,30},
|
|
347 |
xmax=33,
|
|
348 |
ymax=35,
|
|
349 |
ytick={0,5,...,30},
|
|
350 |
scaled ticks=false,
|
|
351 |
axis lines=left,
|
|
352 |
width=5cm,
|
|
353 |
height=4cm,
|
601
|
354 |
legend entries={Swift},
|
|
355 |
legend pos=north west,
|
|
356 |
legend cell align=left]
|
|
357 |
\addplot[purple,mark=*, mark options={fill=white}] table {re-swift.data};
|
|
358 |
\end{axis}
|
|
359 |
\end{tikzpicture}
|
612
|
360 |
&
|
|
361 |
\begin{tikzpicture}
|
|
362 |
\begin{axis}[
|
|
363 |
xlabel={$n$},
|
|
364 |
x label style={at={(1.05,-0.05)}},
|
|
365 |
%ylabel={time in secs},
|
|
366 |
enlargelimits=true,
|
|
367 |
%xtick={0,5000,...,40000},
|
|
368 |
%xmax=40000,
|
|
369 |
%ymax=35,
|
|
370 |
restrict x to domain*=0:40000,
|
|
371 |
restrict y to domain*=0:35,
|
|
372 |
%ytick={0,5,...,30},
|
|
373 |
%scaled ticks=false,
|
|
374 |
axis lines=left,
|
|
375 |
width=5cm,
|
|
376 |
height=4cm,
|
|
377 |
legend entries={Java9+},
|
|
378 |
legend pos=north west,
|
|
379 |
legend cell align=left]
|
|
380 |
\addplot[orange,mark=*, mark options={fill=white}] table {re-java9.data};
|
|
381 |
\end{axis}
|
|
382 |
\end{tikzpicture}\\
|
|
383 |
\multicolumn{2}{c}{Graphs}
|
532
|
384 |
\end{tabular}
|
601
|
385 |
\caption{Graphs showing runtime for matching $(a^*)^*\,b$ with strings
|
|
386 |
of the form $\protect\underbrace{aa..a}_{n}$ in various existing regular expression libraries.
|
612
|
387 |
The reason for their superlinear behaviour is that they do a depth-first-search
|
|
388 |
using NFAs.
|
|
389 |
If the string does not match, the regular expression matching
|
|
390 |
engine starts to explore all possibilities.
|
601
|
391 |
}\label{fig:aStarStarb}
|
|
392 |
\end{figure}\afterpage{\clearpage}
|
538
|
393 |
|
532
|
394 |
A more recent example is a global outage of all Cloudflare servers on 2 July
|
612
|
395 |
2019. A poorly written regular expression exhibited catastrophic backtracking
|
|
396 |
and exhausted CPUs that serve HTTP traffic. Although the outage
|
532
|
397 |
had several causes, at the heart was a regular expression that
|
|
398 |
was used to monitor network
|
538
|
399 |
traffic.\footnote{\url{https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/}(Last accessed in 2022)}
|
532
|
400 |
These problems with regular expressions
|
|
401 |
are not isolated events that happen
|
|
402 |
very occasionally, but actually widespread.
|
612
|
403 |
They occur so often that they have a
|
|
404 |
name: Regular-Expression-Denial-Of-Service (ReDoS)
|
532
|
405 |
attack.
|
538
|
406 |
\citeauthor{Davis18} detected more
|
612
|
407 |
than 1000 evil regular expressions
|
|
408 |
in Node.js, Python core libraries, npm and in pypi.
|
532
|
409 |
They therefore concluded that evil regular expressions
|
612
|
410 |
are real problems rather than "a parlour trick".
|
532
|
411 |
|
603
|
412 |
This work aims to address this issue
|
|
413 |
with the help of formal proofs.
|
612
|
414 |
We describe a lexing algorithm based
|
|
415 |
on Brzozowski derivatives with verified correctness (in
|
603
|
416 |
Isabelle/HOL)
|
612
|
417 |
and a finiteness property.
|
|
418 |
Such properties %guarantee the absence of
|
|
419 |
are an important step in preventing
|
|
420 |
catastrophic backtracking once and for all.
|
604
|
421 |
We will give more details in the next sections
|
|
422 |
on (i) why the slow cases in graph \ref{fig:aStarStarb}
|
612
|
423 |
can occur in traditional regular expression engines
|
604
|
424 |
and (ii) why we choose our
|
612
|
425 |
approach based on Brzozowski derivatives and formal proofs.
|
602
|
426 |
|
603
|
427 |
|
612
|
428 |
\section{Preliminaries}%Regex, and the Problems with Regex Matchers}
|
601
|
429 |
Regular expressions and regular expression matchers
|
|
430 |
have of course been studied for many, many years.
|
612
|
431 |
Theoretical results in automata theory state
|
604
|
432 |
that basic regular expression matching should be linear
|
605
|
433 |
w.r.t the input.
|
|
434 |
This assumes that the regular expression
|
|
435 |
$r$ was pre-processed and turned into a
|
612
|
436 |
deterministic finite automaton (DFA) before matching\cite{Sakarovitch2009}.
|
604
|
437 |
By basic we mean textbook definitions such as the one
|
612
|
438 |
below, involving only regular expressions for characters, alternatives,
|
604
|
439 |
sequences, and Kleene stars:
|
|
440 |
\[
|
612
|
441 |
r ::= c | r_1 + r_2 | r_1 \cdot r_2 | r^*
|
604
|
442 |
\]
|
|
443 |
Modern regular expression matchers used by programmers,
|
|
444 |
however,
|
612
|
445 |
support much richer constructs, such as bounded repetitions
|
604
|
446 |
and back-references.
|
612
|
447 |
To differentiate, we use the word \emph{regex} to refer
|
605
|
448 |
to those expressions with richer constructs while reserving the
|
|
449 |
term \emph{regular expression}
|
|
450 |
for the more traditional meaning in formal languages theory.
|
|
451 |
We follow this convention
|
|
452 |
in this thesis.
|
|
453 |
In the future, we aim to support all the popular features of regexes,
|
612
|
454 |
but for this work we mainly look at basic regular expressions
|
|
455 |
and bounded repetitions.
|
604
|
456 |
|
605
|
457 |
|
|
458 |
|
|
459 |
%Most modern regex libraries
|
|
460 |
%the so-called PCRE standard (Peral Compatible Regular Expressions)
|
|
461 |
%has the back-references
|
612
|
462 |
Regexes come with a number of constructs
|
605
|
463 |
that make it more convenient for
|
604
|
464 |
programmers to write regular expressions.
|
612
|
465 |
Depending on the types of constructs
|
605
|
466 |
the task of matching and lexing with them
|
612
|
467 |
will have different levels of complexity.
|
|
468 |
Some of those constructs are just syntactic sugars that are
|
604
|
469 |
simply short hand notations
|
605
|
470 |
that save the programmers a few keystrokes.
|
612
|
471 |
These will not cause problems for regex libraries.
|
605
|
472 |
For example the
|
612
|
473 |
non-binary alternative involving three or more choices just means:
|
604
|
474 |
\[
|
605
|
475 |
(a | b | c) \stackrel{means}{=} ((a + b)+ c)
|
604
|
476 |
\]
|
612
|
477 |
Similarly, the range operator used to express the alternative
|
|
478 |
of all characters between its operands is just a concise way:
|
604
|
479 |
\[
|
605
|
480 |
[0~-9]\stackrel{means}{=} (0 | 1 | \ldots | 9 ) \; \text{(all number digits)}
|
604
|
481 |
\]
|
612
|
482 |
for an alternative. The
|
|
483 |
wildcard character $.$ is used to refer to any single character,
|
605
|
484 |
\[
|
|
485 |
. \stackrel{means}{=} [0-9a-zA-Z+-()*\&\ldots]
|
|
486 |
\]
|
612
|
487 |
except the newline.
|
604
|
488 |
|
605
|
489 |
\subsection{Bounded Repetitions}
|
612
|
490 |
More interesting are bounded repetitions, which can
|
|
491 |
make the regular expressions much
|
605
|
492 |
more compact.
|
612
|
493 |
There are
|
|
494 |
$r^{\{n\}}$, $r^{\{\ldots m\}}$, $r^{\{n\ldots \}}$ and $r^{\{n\ldots m\}}$
|
|
495 |
(where $n$ and $m$ are constant natural numbers).
|
|
496 |
Like the star regular expressions, the set of strings or language
|
|
497 |
a bounded regular expression can match
|
|
498 |
is defined using the power operation on sets:
|
605
|
499 |
\begin{center}
|
|
500 |
\begin{tabular}{lcl}
|
|
501 |
$L \; r^{\{n\}}$ & $\dn$ & $(L \; r)^n$\\
|
|
502 |
$L \; r^{\{\ldots m\}}$ & $\dn$ & $\bigcup_{0 \leq i \leq m}. (L \; r)^i$\\
|
|
503 |
$L \; r^{\{n\ldots \}}$ & $\dn$ & $\bigcup_{n \leq i}. (L \; r)^i$\\
|
|
504 |
$L \; r^{\{n \ldots m\}}$ & $\dn$ & $\bigcup_{n \leq i \leq m}. (L \; r)^i$
|
|
505 |
\end{tabular}
|
|
506 |
\end{center}
|
612
|
507 |
The attraction of bounded repetitions is that they can be
|
|
508 |
used to avoid a blow up: for example $r^{\{n\}}$
|
|
509 |
is a shorthand for
|
605
|
510 |
\[
|
|
511 |
\underbrace{r\ldots r}_\text{n copies of r}.
|
|
512 |
\]
|
|
513 |
%Therefore, a naive algorithm that simply unfolds
|
|
514 |
%them into their desugared forms
|
|
515 |
%will suffer from at least an exponential runtime increase.
|
603
|
516 |
|
612
|
517 |
|
|
518 |
The problem with matching
|
|
519 |
is that tools based on the classic notion of
|
|
520 |
automata need to expand $r^{\{n\}}$ into $n$ connected
|
603
|
521 |
copies of the automaton for $r$. This leads to very inefficient matching
|
|
522 |
algorithms or algorithms that consume large amounts of memory.
|
605
|
523 |
Implementations using $\DFA$s will
|
|
524 |
either become excruciatingly slow
|
|
525 |
(for example Verbatim++\cite{Verbatimpp}) or get
|
|
526 |
out of memory errors (for example $\mathit{LEX}$ and
|
|
527 |
$\mathit{JFLEX}$\footnote{which are lexer generators
|
|
528 |
in C and JAVA that generate $\mathit{DFA}$-based
|
|
529 |
lexers. The user provides a set of regular expressions
|
|
530 |
and configurations to them, and then
|
|
531 |
gets an output program encoding a minimized $\mathit{DFA}$
|
|
532 |
that can be compiled and run.
|
|
533 |
When given the above countdown regular expression,
|
|
534 |
a small $n$ (a few dozen) would result in a
|
|
535 |
determinised automata
|
612
|
536 |
with millions of states.}) for large counters.
|
|
537 |
A classic example for this phenomenon is the regular expression $(a+b)^* a (a+b)^{n}$
|
605
|
538 |
where the minimal DFA requires at least $2^{n+1}$ states.
|
|
539 |
For example, when $n$ is equal to 2,
|
612
|
540 |
The corresponding $\mathit{NFA}$ looks like:
|
604
|
541 |
\begin{center}
|
|
542 |
\begin{tikzpicture}[shorten >=1pt,node distance=2cm,on grid,auto]
|
|
543 |
\node[state,initial] (q_0) {$q_0$};
|
|
544 |
\node[state, red] (q_1) [right=of q_0] {$q_1$};
|
|
545 |
\node[state, red] (q_2) [right=of q_1] {$q_2$};
|
|
546 |
\node[state, accepting, red](q_3) [right=of q_2] {$q_3$};
|
|
547 |
\path[->]
|
|
548 |
(q_0) edge node {a} (q_1)
|
|
549 |
edge [loop below] node {a,b} ()
|
|
550 |
(q_1) edge node {a,b} (q_2)
|
|
551 |
(q_2) edge node {a,b} (q_3);
|
|
552 |
\end{tikzpicture}
|
|
553 |
\end{center}
|
612
|
554 |
when turned into a DFA by the subset construction
|
|
555 |
requires at least $2^3$ states.\footnote{The
|
605
|
556 |
red states are "countdown states" which counts down
|
604
|
557 |
the number of characters needed in addition to the current
|
|
558 |
string to make a successful match.
|
|
559 |
For example, state $q_1$ indicates a match that has
|
|
560 |
gone past the $(a|b)^*$ part of $(a|b)^*a(a|b)^{\{2\}}$,
|
|
561 |
and just consumed the "delimiter" $a$ in the middle, and
|
|
562 |
need to match 2 more iterations of $(a|b)$ to complete.
|
|
563 |
State $q_2$ on the other hand, can be viewed as a state
|
|
564 |
after $q_1$ has consumed 1 character, and just waits
|
|
565 |
for 1 more character to complete.
|
|
566 |
$q_3$ is the last state, requiring 0 more character and is accepting.
|
|
567 |
Depending on the suffix of the
|
|
568 |
input string up to the current read location,
|
|
569 |
the states $q_1$ and $q_2$, $q_3$
|
|
570 |
may or may
|
|
571 |
not be active, independent from each other.
|
|
572 |
A $\mathit{DFA}$ for such an $\mathit{NFA}$ would
|
|
573 |
contain at least $2^3$ non-equivalent states that cannot be merged,
|
|
574 |
because the subset construction during determinisation will generate
|
|
575 |
all the elements in the power set $\mathit{Pow}\{q_1, q_2, q_3\}$.
|
|
576 |
Generalizing this to regular expressions with larger
|
|
577 |
bounded repetitions number, we have that
|
|
578 |
regexes shaped like $r^*ar^{\{n\}}$ when converted to $\mathit{DFA}$s
|
605
|
579 |
would require at least $2^{n+1}$ states, if $r$ itself contains
|
604
|
580 |
more than 1 string.
|
|
581 |
This is to represent all different
|
605
|
582 |
scenarios which "countdown" states are active.}
|
603
|
583 |
|
538
|
584 |
|
605
|
585 |
Bounded repetitions are very important because they
|
612
|
586 |
tend to occur a lot in practical use,
|
|
587 |
for example in the regex library RegExLib,
|
607
|
588 |
the rules library of Snort \cite{Snort1999}\footnote{
|
|
589 |
Snort is a network intrusion detection (NID) tool
|
608
|
590 |
for monitoring network traffic.
|
|
591 |
The network security community curates a list
|
|
592 |
of malicious patterns written as regexes,
|
|
593 |
which is used by Snort's detection engine
|
|
594 |
to match against network traffic for any hostile
|
|
595 |
activities such as buffer overflow attacks.},
|
607
|
596 |
as well as in XML Schema definitions (XSDs).
|
605
|
597 |
According to Bj\"{o}rklund et al \cite{xml2015},
|
|
598 |
more than half of the
|
612
|
599 |
XSDs they found on the Maven.org central repository
|
|
600 |
have bounded regular expressions in them.
|
|
601 |
Often the counters are quite large, with the largest being
|
|
602 |
approximately up to ten million.
|
605
|
603 |
An example XSD they gave
|
612
|
604 |
is:
|
|
605 |
\begin{verbatim}
|
|
606 |
<sequence minOccurs="0" maxOccurs="65535">
|
|
607 |
<element name="TimeIncr" type="mpeg7:MediaIncrDurationType"/>
|
|
608 |
<element name="MotionParams" type="float" minOccurs="2" maxOccurs="12"/>
|
|
609 |
</sequence>
|
|
610 |
\end{verbatim}
|
605
|
611 |
This can be seen as the expression
|
|
612 |
$(ab^{2\ldots 12})^{0 \ldots 65535}$, where $a$ and $b$ are themselves
|
|
613 |
regular expressions
|
|
614 |
satisfying certain constraints (such as
|
|
615 |
satisfying the floating point number format).
|
|
616 |
It is therefore quite unsatisfying that
|
|
617 |
some regular expressions matching libraries
|
|
618 |
impose adhoc limits
|
|
619 |
for bounded regular expressions:
|
|
620 |
For example, in the regular expression matching library in the Go
|
|
621 |
language the regular expression $a^{1001}$ is not permitted, because no counter
|
|
622 |
can be above 1000, and in the built-in Rust regular expression library
|
|
623 |
expressions such as $a^{\{1000\}\{100\}\{5\}}$ give an error message
|
606
|
624 |
for being too big.
|
|
625 |
As Becchi and Crawley\cite{Becchi08} have pointed out,
|
|
626 |
the reason for these restrictions
|
612
|
627 |
is that they simulate a non-deterministic finite
|
606
|
628 |
automata (NFA) with a breadth-first search.
|
|
629 |
This way the number of active states could
|
|
630 |
be equal to the counter number.
|
|
631 |
When the counters are large,
|
|
632 |
the memory requirement could become
|
612
|
633 |
infeasible, and a regex engine
|
|
634 |
like Go will reject this pattern straight away.
|
606
|
635 |
\begin{figure}[H]
|
|
636 |
\begin{center}
|
|
637 |
\begin{tikzpicture} [node distance = 2cm, on grid, auto]
|
|
638 |
|
|
639 |
\node (q0) [state, initial] {$0$};
|
|
640 |
\node (q1) [state, right = of q0] {$1$};
|
612
|
641 |
%\node (q2) [state, right = of q1] {$2$};
|
|
642 |
\node (qdots) [right = of q1] {$\ldots$};
|
606
|
643 |
\node (qn) [state, right = of qdots] {$n$};
|
|
644 |
\node (qn1) [state, right = of qn] {$n+1$};
|
|
645 |
\node (qn2) [state, right = of qn1] {$n+2$};
|
|
646 |
\node (qn3) [state, accepting, right = of qn2] {$n+3$};
|
|
647 |
|
|
648 |
\path [-stealth, thick]
|
|
649 |
(q0) edge [loop above] node {a} ()
|
|
650 |
(q0) edge node {a} (q1)
|
612
|
651 |
%(q1) edge node {.} (q2)
|
|
652 |
(q1) edge node {.} (qdots)
|
606
|
653 |
(qdots) edge node {.} (qn)
|
|
654 |
(qn) edge node {.} (qn1)
|
|
655 |
(qn1) edge node {b} (qn2)
|
|
656 |
(qn2) edge node {$c$} (qn3);
|
|
657 |
\end{tikzpicture}
|
|
658 |
%\begin{tikzpicture}[shorten >=1pt,node distance=2cm,on grid,auto]
|
|
659 |
% \node[state,initial] (q_0) {$0$};
|
|
660 |
% \node[state, ] (q_1) [right=of q_0] {$1$};
|
|
661 |
% \node[state, ] (q_2) [right=of q_1] {$2$};
|
|
662 |
% \node[state,
|
|
663 |
% \node[state, accepting, ](q_3) [right=of q_2] {$3$};
|
|
664 |
% \path[->]
|
|
665 |
% (q_0) edge node {a} (q_1)
|
|
666 |
% edge [loop below] node {a,b} ()
|
|
667 |
% (q_1) edge node {a,b} (q_2)
|
|
668 |
% (q_2) edge node {a,b} (q_3);
|
|
669 |
%\end{tikzpicture}
|
|
670 |
\end{center}
|
|
671 |
\caption{The example given by Becchi and Crawley
|
|
672 |
that NFA simulation can consume large
|
|
673 |
amounts of memory: $.^*a.^{\{n\}}bc$ matching
|
|
674 |
strings of the form $aaa\ldots aaaabc$.
|
|
675 |
When traversing in a breadth-first manner,
|
|
676 |
all states from 0 till $n+1$ will become active.}
|
|
677 |
\end{figure}
|
|
678 |
%Languages like $\mathit{Go}$ and $\mathit{Rust}$ use this
|
|
679 |
%type of $\mathit{NFA}$ simulation and guarantees a linear runtime
|
|
680 |
%in terms of input string length.
|
|
681 |
%TODO:try out these lexers
|
|
682 |
These problems can of course be solved in matching algorithms where
|
605
|
683 |
automata go beyond the classic notion and for instance include explicit
|
|
684 |
counters \cite{Turo_ov__2020}.
|
612
|
685 |
These solutions can be quite efficient,
|
606
|
686 |
with the ability to process
|
612
|
687 |
gigabytes of strings input per second
|
606
|
688 |
even with large counters \cite{Becchi08}.
|
612
|
689 |
But formal reasoning about these automata especially in Isabelle
|
|
690 |
can be challenging
|
|
691 |
and un-intuitive.
|
|
692 |
Therefore, we take correctness and runtime claims made about these solutions
|
|
693 |
with a grain of salt.
|
605
|
694 |
|
|
695 |
In the work reported in \cite{CSL2022} and here,
|
|
696 |
we add better support using derivatives
|
|
697 |
for bounded regular expressions $r^{\{n\}}$.
|
|
698 |
The results
|
|
699 |
extend straightforwardly to
|
|
700 |
repetitions with an interval such as
|
|
701 |
$r^{\{n\ldots m\}}$.
|
608
|
702 |
The merit of Brzozowski derivatives (more on this later)
|
605
|
703 |
on this problem is that
|
|
704 |
it can be naturally extended to support bounded repetitions.
|
|
705 |
Moreover these extensions are still made up of only
|
|
706 |
inductive datatypes and recursive functions,
|
|
707 |
making it handy to deal with using theorem provers.
|
|
708 |
%The point here is that Brzozowski derivatives and the algorithms by Sulzmann and Lu can be
|
|
709 |
%straightforwardly extended to deal with bounded regular expressions
|
|
710 |
%and moreover the resulting code still consists of only simple
|
|
711 |
%recursive functions and inductive datatypes.
|
|
712 |
Finally, bounded regular expressions do not destroy our finite
|
|
713 |
boundedness property, which we shall prove later on.
|
538
|
714 |
|
|
715 |
|
606
|
716 |
|
|
717 |
|
|
718 |
|
605
|
719 |
\subsection{Back-References}
|
606
|
720 |
The other way to simulate an $\mathit{NFA}$ for matching is choosing
|
|
721 |
a single transition each time, keeping all the other options in
|
|
722 |
a queue or stack, and backtracking if that choice eventually
|
|
723 |
fails. This method, often called a "depth-first-search",
|
|
724 |
is efficient in a lot of cases, but could end up
|
|
725 |
with exponential run time.
|
|
726 |
The backtracking method is employed in regex libraries
|
|
727 |
that support \emph{back-references}, for example
|
|
728 |
in Java and Python.
|
605
|
729 |
%\section{Back-references and The Terminology Regex}
|
538
|
730 |
|
605
|
731 |
%When one constructs an $\NFA$ out of a regular expression
|
|
732 |
%there is often very little to be done in the first phase, one simply
|
|
733 |
%construct the $\NFA$ states based on the structure of the input regular expression.
|
538
|
734 |
|
605
|
735 |
%In the lexing phase, one can simulate the $\mathit{NFA}$ running in two ways:
|
|
736 |
%one by keeping track of all active states after consuming
|
|
737 |
%a character, and update that set of states iteratively.
|
|
738 |
%This can be viewed as a breadth-first-search of the $\mathit{NFA}$
|
|
739 |
%for a path terminating
|
|
740 |
%at an accepting state.
|
606
|
741 |
|
|
742 |
|
|
743 |
|
|
744 |
Given a regular expression like this (the sequence
|
532
|
745 |
operator is omitted for brevity):
|
|
746 |
\begin{center}
|
606
|
747 |
$r_1r_2r_3r_4$
|
532
|
748 |
\end{center}
|
606
|
749 |
one could label sub-expressions of interest
|
532
|
750 |
by parenthesizing them and giving
|
|
751 |
them a number by the order in which their opening parentheses appear.
|
|
752 |
One possible way of parenthesizing and labelling is given below:
|
|
753 |
\begin{center}
|
|
754 |
$\underset{1}{(}r_1\underset{2}{(}r_2\underset{3}{(}r_3)\underset{4}{(}r_4)))$
|
|
755 |
\end{center}
|
606
|
756 |
The sub-expressions
|
|
757 |
$r_1r_2r_3r_4$, $r_1r_2r_3$, $r_3$ and $r_4$ are labelled
|
|
758 |
by 1 to 4, and can be ``referred back'' by their respective numbers.
|
|
759 |
%These sub-expressions are called "capturing groups".
|
|
760 |
To do so, we use the syntax $\backslash i$
|
|
761 |
to denote that we want the sub-string
|
|
762 |
of the input just matched by the i-th
|
|
763 |
sub-expression to appear again,
|
|
764 |
exactly the same as it first appeared:
|
532
|
765 |
\begin{center}
|
|
766 |
$\ldots\underset{\text{i-th lparen}}{(}{r_i})\ldots
|
|
767 |
\underset{s_i \text{ which just matched} \;r_i}{\backslash i}$
|
|
768 |
\end{center}
|
606
|
769 |
%The backslash and number $i$ are the
|
|
770 |
%so-called "back-references".
|
|
771 |
%Let $e$ be an expression made of regular expressions
|
|
772 |
%and back-references. $e$ contains the expression $e_i$
|
|
773 |
%as its $i$-th capturing group.
|
|
774 |
%The semantics of back-reference can be recursively
|
|
775 |
%written as:
|
|
776 |
%\begin{center}
|
|
777 |
% \begin{tabular}{c}
|
|
778 |
% $L ( e \cdot \backslash i) = \{s @ s_i \mid s \in L (e)\quad s_i \in L(r_i)$\\
|
|
779 |
% $s_i\; \text{match of ($e$, $s$)'s $i$-th capturing group string}\}$
|
|
780 |
% \end{tabular}
|
|
781 |
%\end{center}
|
|
782 |
A concrete example
|
618
|
783 |
for back-references is
|
532
|
784 |
\begin{center}
|
607
|
785 |
$(.^*)\backslash 1$,
|
532
|
786 |
\end{center}
|
618
|
787 |
which matches
|
606
|
788 |
strings that can be split into two identical halves,
|
618
|
789 |
for example $\mathit{foofoo}$, $\mathit{ww}$ and so on.
|
607
|
790 |
Note that this is different from
|
|
791 |
repeating the sub-expression verbatim like
|
|
792 |
\begin{center}
|
|
793 |
$(.^*)(.^*)$,
|
|
794 |
\end{center}
|
|
795 |
which does not impose any restrictions on what strings the second
|
|
796 |
sub-expression $.^*$
|
|
797 |
might match.
|
618
|
798 |
Another example of back-references is
|
607
|
799 |
\begin{center}
|
|
800 |
$(.)(.)\backslash 2\backslash 1$
|
|
801 |
\end{center}
|
618
|
802 |
which matches four-character palindromes
|
|
803 |
like $abba$, $x??x$ and so on.
|
607
|
804 |
|
|
805 |
Back-references is a regex construct
|
618
|
806 |
that programmers find quite useful.
|
607
|
807 |
According to Becchi and Crawley\cite{Becchi08},
|
618
|
808 |
6\% of Snort rules (up until 2008) use them.
|
607
|
809 |
The most common use of back-references
|
618
|
810 |
is to express well-formed html files,
|
|
811 |
where back-references are convenient for matching
|
|
812 |
opening and closing tags like
|
607
|
813 |
\begin{center}
|
|
814 |
$\langle html \rangle \ldots \langle / html \rangle$
|
|
815 |
\end{center}
|
|
816 |
A regex describing such a format
|
618
|
817 |
is
|
607
|
818 |
\begin{center}
|
|
819 |
$\langle (.^+) \rangle \ldots \langle / \backslash 1 \rangle$
|
|
820 |
\end{center}
|
618
|
821 |
Despite being useful, the expressive power of regexes
|
607
|
822 |
go beyond the regular language hierarchy
|
618
|
823 |
once back-references are included.
|
607
|
824 |
In fact, they allow the regex construct to express
|
532
|
825 |
languages that cannot be contained in context-free
|
|
826 |
languages either.
|
608
|
827 |
For example, the back-reference $(a^*)b\backslash1 b \backslash 1$
|
532
|
828 |
expresses the language $\{a^n b a^n b a^n\mid n \in \mathbb{N}\}$,
|
|
829 |
which cannot be expressed by context-free grammars\parencite{campeanu2003formal}.
|
|
830 |
Such a language is contained in the context-sensitive hierarchy
|
|
831 |
of formal languages.
|
618
|
832 |
Also solving the matching problem involving back-references
|
609
|
833 |
is known to be NP-complete \parencite{alfred2014algorithms}.
|
608
|
834 |
Regex libraries supporting back-references such as
|
|
835 |
PCRE \cite{pcre} therefore have to
|
609
|
836 |
revert to a depth-first search algorithm which backtracks.
|
|
837 |
What is unexpected is that even in the cases
|
|
838 |
not involving back-references, there is still
|
|
839 |
a (non-negligible) chance they might backtrack super-linearly,
|
618
|
840 |
as shown in the graphs in figure\ref{fig:aStarStarb}.
|
538
|
841 |
|
607
|
842 |
\subsection{Summary of the Catastrophic Backtracking Problem}
|
532
|
843 |
Summing these up, we can categorise existing
|
607
|
844 |
practical regex libraries into two kinds:
|
|
845 |
(i)The ones with linear
|
|
846 |
time guarantees like Go and Rust. The cost with them is that
|
|
847 |
they impose restrictions
|
532
|
848 |
on the user input (not allowing back-references,
|
607
|
849 |
bounded repetitions cannot exceed a counter limit etc.).
|
|
850 |
(ii) Those
|
|
851 |
that allow large bounded regular expressions and back-references
|
|
852 |
at the expense of using a backtracking algorithm.
|
|
853 |
They could grind to a halt
|
|
854 |
on some very simple cases, posing a vulnerability of
|
|
855 |
a ReDoS attack.
|
|
856 |
|
|
857 |
|
|
858 |
We would like to have regex engines that can
|
|
859 |
deal with the regular part (e.g.
|
|
860 |
bounded repetitions) of regexes more
|
|
861 |
efficiently.
|
|
862 |
Also we want to make sure that they do it correctly.
|
|
863 |
It turns out that such aim is not so easy to achieve.
|
532
|
864 |
%TODO: give examples such as RE2 GOLANG 1000 restriction, rust no repetitions
|
|
865 |
% For example, the Rust regex engine claims to be linear,
|
|
866 |
% but does not support lookarounds and back-references.
|
|
867 |
% The GoLang regex library does not support over 1000 repetitions.
|
|
868 |
% Java and Python both support back-references, but shows
|
|
869 |
%catastrophic backtracking behaviours on inputs without back-references(
|
|
870 |
%when the language is still regular).
|
|
871 |
%TODO: test performance of Rust on (((((a*a*)b*)b){20})*)c baabaabababaabaaaaaaaaababaaaababababaaaabaaabaaaaaabaabaabababaababaaaaaaaaababaaaababababaaaaaaaaaaaaac
|
|
872 |
%TODO: verify the fact Rust does not allow 1000+ reps
|
|
873 |
|
|
874 |
|
605
|
875 |
|
|
876 |
|
|
877 |
%The time cost of regex matching algorithms in general
|
|
878 |
%involve two different phases, and different things can go differently wrong on
|
|
879 |
%these phases.
|
|
880 |
%$\DFA$s usually have problems in the first (construction) phase
|
|
881 |
%, whereas $\NFA$s usually run into trouble
|
|
882 |
%on the second phase.
|
|
883 |
|
|
884 |
|
|
885 |
\section{Error-prone POSIX Implementations}
|
607
|
886 |
When there are multiple ways of matching a string
|
|
887 |
with a regular expression, a matcher needs to
|
|
888 |
disambiguate.
|
|
889 |
The standard for which particular match to pick
|
|
890 |
is called the disambiguation strategy.
|
|
891 |
The more intuitive strategy is called POSIX,
|
|
892 |
which always chooses the longest initial match.
|
|
893 |
An alternative strategy would be greedy matches,
|
|
894 |
which always ends a sub-match as early as possible.
|
|
895 |
The POSIX standard is widely adopted in many operating systems.
|
|
896 |
However, many implementations (including the C libraries
|
|
897 |
used by Linux and OS X distributions) contain bugs
|
|
898 |
or do not meet the specification they claim to adhere to.
|
|
899 |
In some cases, they either fail to generate a lexing
|
|
900 |
result when there exists a match,
|
605
|
901 |
or give results that are inconsistent with the $\POSIX$ standard.
|
607
|
902 |
A concrete example would be the regex given by \cite{fowler2003}
|
605
|
903 |
\begin{center}
|
607
|
904 |
$(aba + ab + a)^* \text{and the string} ababa$
|
605
|
905 |
\end{center}
|
|
906 |
The correct $\POSIX$ match for the above would be
|
|
907 |
with the entire string $ababa$,
|
|
908 |
split into two Kleene star iterations, $[ab] [aba]$ at positions
|
|
909 |
$[0, 2), [2, 5)$
|
|
910 |
respectively.
|
|
911 |
But trying this out in regex101\parencite{regex101}
|
|
912 |
with different language engines would yield
|
|
913 |
the same two fragmented matches: $[aba]$ at $[0, 3)$
|
|
914 |
and $a$ at $[4, 5)$.
|
607
|
915 |
Fowler \cite{fowler2003} and Kuklewicz \cite{KuklewiczHaskell}
|
|
916 |
commented that most regex libraries are not
|
605
|
917 |
correctly implementing the POSIX (maximum-munch)
|
|
918 |
rule of regular expression matching.
|
|
919 |
As Grathwohl\parencite{grathwohl2014crash} wrote,
|
|
920 |
\begin{quote}
|
607
|
921 |
``The POSIX strategy is more complicated than the
|
605
|
922 |
greedy because of the dependence on information about
|
607
|
923 |
the length of matched strings in the various subexpressions.''
|
605
|
924 |
\end{quote}
|
|
925 |
%\noindent
|
607
|
926 |
The implementation complexity of POSIX rules also come from
|
|
927 |
the specification being not very clear.
|
|
928 |
There are many informal summaries of this disambiguation
|
|
929 |
strategy, which are often quite long and delicate.
|
608
|
930 |
For example Kuklewicz \cite{KuklewiczHaskell}
|
|
931 |
described the POSIX rule as
|
607
|
932 |
\begin{quote}
|
|
933 |
``
|
|
934 |
\begin{itemize}
|
|
935 |
\item
|
|
936 |
regular expressions (REs) take the leftmost starting match, and the longest match starting there
|
|
937 |
earlier subpatterns have leftmost-longest priority over later subpatterns\\
|
|
938 |
\item
|
|
939 |
higher-level subpatterns have leftmost-longest priority over their component subpatterns\\
|
|
940 |
\item
|
|
941 |
REs have right associative concatenation which can be changed with parenthesis\\
|
|
942 |
\item
|
|
943 |
parenthesized subexpressions return the match from their last usage\\
|
|
944 |
\item
|
|
945 |
text of component subexpressions must be contained in the text of the
|
|
946 |
higher-level subexpressions\\
|
|
947 |
\item
|
|
948 |
if "p" and "q" can never match the same text then "p|q" and "q|p" are equivalent, up to trivial renumbering of captured subexpressions\\
|
|
949 |
\item
|
|
950 |
if "p" in "p*" is used to capture non-empty text then additional repetitions of "p" will not capture an empty string\\''
|
|
951 |
\end{itemize}
|
|
952 |
\end{quote}
|
|
953 |
The text above
|
|
954 |
is trying to capture something very precise,
|
|
955 |
and is crying out for formalising.
|
608
|
956 |
Ausaf et al. \cite{AusafDyckhoffUrban2016}
|
609
|
957 |
are the first to fill the gap
|
|
958 |
by not just describing such a formalised POSIX
|
|
959 |
specification in Isabelle/HOL, but also proving
|
|
960 |
that their specification coincides with the
|
|
961 |
POSIX specification given by Okui and Suzuki \cite{Okui10}
|
|
962 |
which is a completely
|
|
963 |
different characterisation.
|
608
|
964 |
They then formally proved the correctness of
|
|
965 |
a lexing algorithm by Sulzmann and Lu \cite{Sulzmann2014}
|
|
966 |
based on that specification.
|
605
|
967 |
|
608
|
968 |
In the next section we will very briefly
|
609
|
969 |
introduce Brzozowski derivatives and Sulzmann
|
|
970 |
and Lu's algorithm, which this thesis builds on.
|
|
971 |
We give a taste of what they
|
608
|
972 |
are like and why they are suitable for regular expression
|
|
973 |
matching and lexing.
|
532
|
974 |
|
609
|
975 |
\section{Our Solution--Formal Specification of POSIX Matching
|
|
976 |
and Brzozowski Derivatives}
|
|
977 |
Now we start with the central topic of the thesis: Brzozowski derivatives.
|
|
978 |
Brzozowski \cite{Brzozowski1964} first introduced the
|
|
979 |
concept of the \emph{derivative} in the 1960s.
|
|
980 |
The derivative of a regular expression $r$
|
|
981 |
with respect to a character $c$, is written as $r \backslash c$.\footnote{
|
|
982 |
Despite having the same name, regular expression
|
|
983 |
derivatives bear little similarity with the mathematical definition
|
|
984 |
of derivatives on functions.
|
|
985 |
}
|
|
986 |
It tells us what $r$ would transform into
|
|
987 |
if we chop off the first character $c$
|
|
988 |
from all strings in the language of $r$ ($L \; r$).
|
|
989 |
To give a flavour of Brzozowski derivatives, we present
|
|
990 |
two straightforward clauses from it:
|
|
991 |
\begin{center}
|
|
992 |
\begin{tabular}{lcl}
|
|
993 |
$d \backslash c$ & $\dn$ &
|
|
994 |
$\mathit{if} \;c = d\;\mathit{then}\;\ONE\;\mathit{else}\;\ZERO$\\
|
|
995 |
$(r_1 + r_2)\backslash c$ & $\dn$ & $r_1 \backslash c \,+\, r_2 \backslash c$\\
|
|
996 |
\end{tabular}
|
|
997 |
\end{center}
|
|
998 |
\noindent
|
|
999 |
The first clause says that for the regular expression
|
|
1000 |
denoting a singleton set consisting of a sinlge-character string $\{ d \}$,
|
|
1001 |
we check the derivative character $c$ against $d$,
|
|
1002 |
returning a set containing only the empty string $\{ [] \}$
|
|
1003 |
if $c$ and $d$ are equal, and the empty set $\varnothing$ otherwise.
|
|
1004 |
The second clause states that to obtain the regular expression
|
|
1005 |
representing all strings' head character $c$ being chopped off
|
|
1006 |
from $r_1 + r_2$, one simply needs to recursively take derivative
|
|
1007 |
of $r_1$ and $r_2$ and then put them together.
|
532
|
1008 |
|
609
|
1009 |
Thanks to the definition, derivatives have the nice property
|
|
1010 |
that $s \in L \; (r\backslash c)$ if and only if
|
|
1011 |
$c::s \in L \; r$.
|
|
1012 |
%This property can be used on regular expressions
|
|
1013 |
%matching and lexing--to test whether a string $s$ is in $L \; r$,
|
|
1014 |
%one simply takes derivatives of $r$ successively with
|
|
1015 |
%respect to the characters (in the correct order) in $s$,
|
|
1016 |
%and then test whether the empty string is in the last regular expression.
|
538
|
1017 |
Derivatives give a simple solution
|
609
|
1018 |
to the problem of matching and lexing a string $s$ with a regular
|
538
|
1019 |
expression $r$: if the derivative of $r$ w.r.t.\ (in
|
|
1020 |
succession) all the characters of the string matches the empty string,
|
|
1021 |
then $r$ matches $s$ (and {\em vice versa}).
|
532
|
1022 |
|
609
|
1023 |
This makes formally reasoning about these properties such
|
|
1024 |
as correctness and complexity smooth and intuitive.
|
|
1025 |
In fact, there has already been several mechanised proofs about them,
|
|
1026 |
for example the one by Owens and Slind \cite{Owens2008} in HOL4,
|
|
1027 |
another one by Krauss and Nipkow \cite{Nipkow98} in Isabelle/HOL, and
|
|
1028 |
yet another in Coq by Coquand and Siles \cite{Coquand2012}.
|
|
1029 |
|
|
1030 |
In addition, one can extend the clauses to bounded repetitions
|
|
1031 |
``for free'':
|
|
1032 |
\begin{center}
|
|
1033 |
\begin{tabular}{lcl}
|
|
1034 |
$r^{\{n\}} \backslash c$ & $\dn$ & $r \backslash c \cdot
|
|
1035 |
r^{\{n-1\}}$\\
|
|
1036 |
\end{tabular}
|
|
1037 |
\end{center}
|
|
1038 |
\noindent
|
|
1039 |
And experimental results suggest that unlike DFA-based solutions,
|
|
1040 |
this derivatives can support
|
|
1041 |
bounded regular expressions with large counters
|
|
1042 |
quite well.
|
532
|
1043 |
|
609
|
1044 |
There has also been
|
|
1045 |
extensions to other constructs.
|
|
1046 |
For example, Owens et al include the derivatives
|
|
1047 |
for \emph{NOT} regular expressions, which is
|
|
1048 |
able to concisely express C-style comments of the form
|
|
1049 |
$/* \ldots */$.
|
|
1050 |
Another extension for derivatives would be
|
|
1051 |
regular expressions with look-aheads, done by
|
|
1052 |
by Miyazaki and Minamide
|
|
1053 |
\cite{Takayuki2019}.
|
|
1054 |
%We therefore use Brzozowski derivatives on regular expressions
|
|
1055 |
%lexing
|
|
1056 |
|
|
1057 |
|
|
1058 |
|
|
1059 |
Given the above definitions and properties of
|
|
1060 |
Brzozowski derivatives, one quickly realises their potential
|
|
1061 |
in generating a formally verified algorithm for lexing--the clauses and property
|
|
1062 |
can be easily expressed in a functional programming language
|
|
1063 |
or converted to theorem prover
|
|
1064 |
code, with great extensibility.
|
|
1065 |
Perhaps this is the reason why it has sparked quite a bit of interest
|
|
1066 |
in the functional programming and theorem prover communities in the last
|
|
1067 |
fifteen or so years (
|
|
1068 |
\cite{Almeidaetal10}, \cite{Berglund14}, \cite{Berglund18},
|
|
1069 |
\cite{Chen12} and \cite{Coquand2012}
|
|
1070 |
to name a few), despite being buried in the ``sands of time'' \cite{Owens2008}
|
|
1071 |
after they were first published.
|
|
1072 |
|
|
1073 |
|
|
1074 |
However, there are two difficulties with derivative-based matchers:
|
532
|
1075 |
First, Brzozowski's original matcher only generates a yes/no answer
|
|
1076 |
for whether a regular expression matches a string or not. This is too
|
|
1077 |
little information in the context of lexing where separate tokens must
|
|
1078 |
be identified and also classified (for example as keywords
|
609
|
1079 |
or identifiers).
|
|
1080 |
Second, derivative-based matchers need to be more efficient.
|
|
1081 |
Elegant and beautiful
|
|
1082 |
as many implementations are,
|
|
1083 |
they can be excruciatingly slow.
|
|
1084 |
For example, Sulzmann and Lu
|
|
1085 |
claim a linear running time of their proposed algorithm,
|
|
1086 |
but that was falsified by our experiments. The running time
|
|
1087 |
is actually $\Omega(2^n)$ in the worst case.
|
|
1088 |
A similar claim about a theoretical runtime of $O(n^2)$
|
|
1089 |
is made for the Verbatim \cite{Verbatim}
|
|
1090 |
%TODO: give references
|
|
1091 |
lexer, which calculates POSIX matches and is based on derivatives.
|
|
1092 |
They formalized the correctness of the lexer, but not the complexity.
|
|
1093 |
In the performance evaluation section, they simply analyzed the run time
|
|
1094 |
of matching $a$ with the string
|
|
1095 |
\begin{center}
|
|
1096 |
$\underbrace{a \ldots a}_{\text{n a's}}$
|
|
1097 |
\end{center}
|
|
1098 |
and concluded that the algorithm is quadratic in terms of input length.
|
|
1099 |
When we tried out their extracted OCaml code with our example $(a+aa)^*$,
|
|
1100 |
the time it took to lex only 40 $a$'s was 5 minutes.
|
|
1101 |
|
|
1102 |
|
|
1103 |
\subsection{Sulzmann and Lu's Algorithm}
|
|
1104 |
Sulzmann and Lu~\cite{Sulzmann2014} overcame the first
|
532
|
1105 |
difficulty by cleverly extending Brzozowski's matching
|
|
1106 |
algorithm. Their extended version generates additional information on
|
|
1107 |
\emph{how} a regular expression matches a string following the POSIX
|
|
1108 |
rules for regular expression matching. They achieve this by adding a
|
|
1109 |
second ``phase'' to Brzozowski's algorithm involving an injection
|
609
|
1110 |
function simplification of internal data structures
|
|
1111 |
eliminating the exponential behaviours.
|
|
1112 |
In an earlier work, Ausaf et al provided the formal
|
532
|
1113 |
specification of what POSIX matching means and proved in Isabelle/HOL
|
|
1114 |
the correctness
|
|
1115 |
of Sulzmann and Lu's extended algorithm accordingly
|
|
1116 |
\cite{AusafDyckhoffUrban2016}.
|
|
1117 |
|
609
|
1118 |
The version of the algorithm proven correct
|
|
1119 |
suffers from the
|
|
1120 |
second difficulty though, where the internal derivatives can
|
|
1121 |
grow to arbitrarily big sizes.
|
|
1122 |
For example if we start with the
|
532
|
1123 |
regular expression $(a+aa)^*$ and take
|
|
1124 |
successive derivatives according to the character $a$, we end up with
|
|
1125 |
a sequence of ever-growing derivatives like
|
|
1126 |
|
|
1127 |
\def\ll{\stackrel{\_\backslash{} a}{\longrightarrow}}
|
|
1128 |
\begin{center}
|
|
1129 |
\begin{tabular}{rll}
|
|
1130 |
$(a + aa)^*$ & $\ll$ & $(\ONE + \ONE{}a) \cdot (a + aa)^*$\\
|
|
1131 |
& $\ll$ & $(\ZERO + \ZERO{}a + \ONE) \cdot (a + aa)^* \;+\; (\ONE + \ONE{}a) \cdot (a + aa)^*$\\
|
|
1132 |
& $\ll$ & $(\ZERO + \ZERO{}a + \ZERO) \cdot (a + aa)^* + (\ONE + \ONE{}a) \cdot (a + aa)^* \;+\; $\\
|
|
1133 |
& & $\qquad(\ZERO + \ZERO{}a + \ONE) \cdot (a + aa)^* + (\ONE + \ONE{}a) \cdot (a + aa)^*$\\
|
|
1134 |
& $\ll$ & \ldots \hspace{15mm}(regular expressions of sizes 98, 169, 283, 468, 767, \ldots)
|
|
1135 |
\end{tabular}
|
|
1136 |
\end{center}
|
|
1137 |
|
|
1138 |
\noindent where after around 35 steps we run out of memory on a
|
609
|
1139 |
typical computer (we shall define in the next chapter
|
|
1140 |
the precise details of our
|
532
|
1141 |
regular expressions and the derivative operation). Clearly, the
|
|
1142 |
notation involving $\ZERO$s and $\ONE$s already suggests
|
|
1143 |
simplification rules that can be applied to regular regular
|
|
1144 |
expressions, for example $\ZERO{}\,r \Rightarrow \ZERO$, $\ONE{}\,r
|
|
1145 |
\Rightarrow r$, $\ZERO{} + r \Rightarrow r$ and $r + r \Rightarrow
|
|
1146 |
r$. While such simple-minded simplifications have been proved in our
|
|
1147 |
earlier work to preserve the correctness of Sulzmann and Lu's
|
|
1148 |
algorithm \cite{AusafDyckhoffUrban2016}, they unfortunately do
|
|
1149 |
\emph{not} help with limiting the growth of the derivatives shown
|
|
1150 |
above: the growth is slowed, but the derivatives can still grow rather
|
|
1151 |
quickly beyond any finite bound.
|
|
1152 |
|
|
1153 |
Sulzmann and Lu overcome this ``growth problem'' in a second algorithm
|
538
|
1154 |
\cite{Sulzmann2014} where they introduce bit-coded
|
532
|
1155 |
regular expressions. In this version, POSIX values are
|
538
|
1156 |
represented as bit sequences and such sequences are incrementally generated
|
532
|
1157 |
when derivatives are calculated. The compact representation
|
538
|
1158 |
of bit sequences and regular expressions allows them to define a more
|
532
|
1159 |
``aggressive'' simplification method that keeps the size of the
|
|
1160 |
derivatives finite no matter what the length of the string is.
|
|
1161 |
They make some informal claims about the correctness and linear behaviour
|
|
1162 |
of this version, but do not provide any supporting proof arguments, not
|
538
|
1163 |
even ``pencil-and-paper'' arguments. They write about their bit-coded
|
532
|
1164 |
\emph{incremental parsing method} (that is the algorithm to be formalised
|
538
|
1165 |
in this dissertation)
|
532
|
1166 |
|
|
1167 |
|
|
1168 |
|
|
1169 |
\begin{quote}\it
|
|
1170 |
``Correctness Claim: We further claim that the incremental parsing
|
|
1171 |
method [..] in combination with the simplification steps [..]
|
|
1172 |
yields POSIX parse trees. We have tested this claim
|
|
1173 |
extensively [..] but yet
|
|
1174 |
have to work out all proof details.'' \cite[Page 14]{Sulzmann2014}
|
|
1175 |
\end{quote}
|
|
1176 |
Ausaf and Urban were able to back this correctness claim with
|
|
1177 |
a formal proof.
|
|
1178 |
|
609
|
1179 |
However a faster formally verified
|
|
1180 |
lexing program with the optimisations
|
|
1181 |
mentioned by Sulzmann and Lu's second algorithm
|
|
1182 |
is still missing.
|
|
1183 |
As they stated,
|
532
|
1184 |
\begin{quote}\it
|
609
|
1185 |
``The next step would be to implement a more aggressive simplification procedure on annotated regular expressions and then prove the corresponding algorithm generates the same values as blexer. Alas due to time constraints we are unable to do so here.''
|
532
|
1186 |
\end{quote}
|
|
1187 |
This thesis implements the aggressive simplifications envisioned
|
|
1188 |
by Ausaf and Urban,
|
609
|
1189 |
together with a formal proof of the correctness with those simplifications.
|
532
|
1190 |
|
|
1191 |
|
612
|
1192 |
One of the most recent work in the context of lexing
|
|
1193 |
%with this issue
|
|
1194 |
is the Verbatim lexer by Egolf, Lasser and Fisher\cite{Verbatim}.
|
|
1195 |
This is relevant work for us and we will compare it later with
|
|
1196 |
our derivative-based matcher we are going to present.
|
|
1197 |
There is also some newer work called
|
|
1198 |
Verbatim++\cite{Verbatimpp}, which does not use derivatives,
|
|
1199 |
but deterministic finite automaton instead.
|
|
1200 |
%An example that gives problem to automaton approaches would be
|
|
1201 |
%the regular expression $(a|b)^*a(a|b)^{\{n\}}$.
|
|
1202 |
%It requires at least $2^{n+1}$ states to represent
|
|
1203 |
%as a DFA.
|
|
1204 |
|
|
1205 |
|
532
|
1206 |
%----------------------------------------------------------------------------------------
|
|
1207 |
\section{Contribution}
|
|
1208 |
|
609
|
1209 |
In this thesis,
|
|
1210 |
we propose a solution to catastrophic
|
|
1211 |
backtracking and error-prone matchers: a formally verified
|
|
1212 |
regular expression lexing algorithm
|
|
1213 |
that is both fast
|
|
1214 |
and correct by extending Ausaf et al.'s work.
|
|
1215 |
The end result is %a regular expression lexing algorithm that comes with
|
538
|
1216 |
\begin{itemize}
|
|
1217 |
\item
|
609
|
1218 |
an improved version of Sulzmann and Lu's bit-coded algorithm using
|
|
1219 |
derivatives with simplifications,
|
|
1220 |
accompanied by
|
|
1221 |
a proven correctness theorem according to POSIX specification
|
|
1222 |
given by Ausaf et al. \cite{AusafDyckhoffUrban2016},
|
|
1223 |
\item
|
|
1224 |
a complexity-related property for that algorithm saying that the
|
|
1225 |
internal data structure will
|
|
1226 |
remain finite,
|
538
|
1227 |
\item
|
609
|
1228 |
and extension to
|
|
1229 |
the bounded repetitions construct with the correctness and finiteness property
|
|
1230 |
maintained.
|
|
1231 |
\end{itemize}
|
532
|
1232 |
|
|
1233 |
|
609
|
1234 |
With a formal finiteness bound in place,
|
|
1235 |
we can greatly reduce the attack surface of servers in terms of ReDoS attacks.
|
|
1236 |
Further improvements to the algorithm with an even stronger version of
|
|
1237 |
simplification is made.
|
|
1238 |
Thanks to our theorem-prover-friendly approach,
|
|
1239 |
we believe that
|
|
1240 |
this finiteness bound can be improved to a bound
|
|
1241 |
linear to input and
|
|
1242 |
cubic to the regular expression size using a technique by
|
|
1243 |
Antimirov\cite{Antimirov95}.
|
|
1244 |
Once formalised, this would be a guarantee for the absence of all super-linear behavious.
|
|
1245 |
We are working out the
|
|
1246 |
details.
|
|
1247 |
|
538
|
1248 |
|
609
|
1249 |
To our best knowledge, no lexing libraries using Brzozowski derivatives
|
|
1250 |
have similar complexity-related bounds,
|
|
1251 |
and claims about running time are usually speculative and backed by empirical
|
|
1252 |
evidence on a few test cases.
|
|
1253 |
If a matching or lexing algorithm
|
|
1254 |
does not come with certain basic complexity related
|
|
1255 |
guarantees (for examaple the internal data structure size
|
|
1256 |
does not grow indefinitely),
|
|
1257 |
then they cannot claim with confidence having solved the problem
|
|
1258 |
of catastrophic backtracking.
|
|
1259 |
|
538
|
1260 |
|
|
1261 |
|
532
|
1262 |
|
|
1263 |
|
|
1264 |
\section{Structure of the thesis}
|
538
|
1265 |
In chapter 2 \ref{Inj} we will introduce the concepts
|
532
|
1266 |
and notations we
|
|
1267 |
use for describing the lexing algorithm by Sulzmann and Lu,
|
538
|
1268 |
and then give the lexing algorithm.
|
|
1269 |
We will give its variant in \ref{Bitcoded1}.
|
|
1270 |
Then we illustrate in \ref{Bitcoded2}
|
532
|
1271 |
how the algorithm without bitcodes falls short for such aggressive
|
|
1272 |
simplifications and therefore introduce our version of the
|
538
|
1273 |
bit-coded algorithm and
|
532
|
1274 |
its correctness proof .
|
538
|
1275 |
In \ref{Finite} we give the second guarantee
|
532
|
1276 |
of our bitcoded algorithm, that is a finite bound on the size of any
|
|
1277 |
regex's derivatives.
|
538
|
1278 |
In \ref{Cubic} we discuss stronger simplifications to improve the finite bound
|
|
1279 |
in \ref{Finite} to a polynomial one, and demonstrate how one can extend the
|
532
|
1280 |
algorithm to include constructs such as bounded repetitions and negations.
|
|
1281 |
|
|
1282 |
|
|
1283 |
|
|
1284 |
|
|
1285 |
|
|
1286 |
%----------------------------------------------------------------------------------------
|
|
1287 |
|
|
1288 |
|
|
1289 |
%----------------------------------------------------------------------------------------
|
|
1290 |
|
|
1291 |
%----------------------------------------------------------------------------------------
|
|
1292 |
|
|
1293 |
%----------------------------------------------------------------------------------------
|
|
1294 |
|
|
1295 |
|