Leaning toothpick syndrome
Escape characters making an expression unreadable
From Wikipedia, the free encyclopedia
In computer programming, leaning toothpick syndrome (LTS) is the situation in which a quoted expression becomes visually confusing because it contains a large number of escape characters to avoid delimiter collision (i.e., to avoid ambiguous, non-deterministic interpretations in the program).
[1]
[2]
Escape characters are usually backslashes (\), but can be other character types as well.
An example of LTS would be /^\\\/\\+\/\\?\/\\$/.
Leaning Toothpick Syndrome is named as such due to the visual appearance of each backslash (or forward slash, another common escape character) resembling a leaning toothpick, combined with LTS-suffering strings containing a large number of the tilted slashes.
LTS appears in many programming languages and in many situations, including in patterns that match Uniform Resource Identifiers (URIs), programs that manipulate files and their contents, and programs that output quoted text (i.e., text that either contains quote characters, is surrounded by quote characters, or both). Many quines fall into the latter category.
The official Perl documentation[3] introduced the term to wider usage; there, the phrase is used to describe regular expressions that match Unix-style paths, in which the elements are separated by slashes /. The slash is also used as the default regular expression delimiter; so to be used literally in the expression, it must be escaped with a backslash \, leading to frequent escaped slashes represented as \/. If doubled, as in URLs, this yields \/\/ for an escaped //.
A similar phenomenon occurs for MS-DOS and Windows paths, where the backslash is used as a path separator, such as in C:\Users\Amy\Desktop\Q1-report.pdf, rather than a Unix-style /home/Amy/Desktop/Q1-report.pdf with forward slashes. As a result of using \ for paths instead of /, Windows typically requires extra escape characters when compared to Unix-style paths. Therefore, Windows paths are more likely to cause frustration to programmers.
MS Windows paths, string storage, regex
When doing any file operations that require storing (as opposed to immediately using) Windows-style paths, the operations require the path to have a doubled-up backslash \\ for each original backslash, creating C:\\Users\\Amy\\Desktop\\Q1-report.pdf.
The now-storable path string can then be re-escaped for use inside a regular expression (still inside an escaped string), requiring \\\\ to "match" a single backslash in the original text.
- Parsing-wise, the order of conversions/"escapes" is
({\\}{\\}).- The two innermost parts,
{\\}and{\\}, are used for merely storing the string inside a program. - After storage, the outermost part,
(...), now equivalent to\\as the entire string, then proceeds to be used for the regular expression where the resulting\\is to be interpreted as\(special regex matching operator to be used with ambiguous symbols) \(single symbol to be matched)rather than\(single symbol to be matched) \(single symbol to be matched).
- The two innermost parts,
- Note that {},[],() in these parsing diagrams are meant to indicate human-readable groupings, not any reserved regex groupings such as equivalence classes and
\( \)atoms for callbacks.
Pattern example
Consider the following Perl regular expression intended to match URIs that identify files under the pub directory of an FTP site:
m/ftp:\/\/[^\/]*\/pub\//
Perl, like sed before it, solves this problem by allowing many other characters to be delimiters for a regular expression. For example, the following three examples are equivalent to the expression given above:
m{ftp://[^/]*/pub/}
m#ftp://[^/]*/pub/#
m!ftp://[^/]*/pub/!
Or this common translation to convert backslashes to forward slashes:
tr/\\/\//
may be easier to understand when written like this:
tr{\\}{/}
Text With Quotes - Perl & PHP example
A Perl program to print an HTML link tag, where the URL and link text are stored in variables $url and $text respectively, might look like this. Notice the use of backslashes to escape the quoted double-quote characters:
print "<a href=\"$url\">$text</a>";
Using single quotes to delimit the string is not feasible, as Perl does not expand variables inside single-quoted strings. The code below, for example, would not work as intended:
print '<a href="$url">$text</a>'
Using the printf function is a viable solution in many languages (Perl, PHP):
printf('<a href="%s">%s</a>', $url, $text);
The qq operator in Perl allows for any delimiter:
print qq{<a href="$url">$text</a>};
print qq|<a href="$url">$text</a>|;
print qq(<a href="$url">$text</a>);
Here documents are especially well suited for multi-line strings; however, Perl here documents hadn't allowed for proper indentation before v5.26.[4] This example shows the Perl syntax:
print <<HERE_IT_ENDS;
<a href="$url">$text</a>
HERE_IT_ENDS
Other languages
C#
The C# programming language handles LTS by the use of the @ symbol at the start of string literals, before the initial quotation marks, e.g.
string filePath = @"C:\Foo\Bar.txt";
rather than otherwise requiring:
string filePath = "C:\\Foo\\Bar.txt";
C++
The C++11 standard adds raw strings:
std::string filePath = R"(C:\Foo\Bar.txt)";
If the string contains the characters )", an optional delimiter can be used, such as d in the following example:
std::regex re{ R"d(s/"\([^"]*\)"/'\1'/g)d" };
Go
Go indicates that a string is raw by using the backtick as a delimiter:
s := `C:\Foo\Bar.txt`
Raw strings may contain any character except backticks; there is no escape code for a backtick in a raw string. Raw strings may also span multiple lines, as in this example, where the strings s and t are equivalent:
s := `A string that
spans multiple
lines.`
t := "A string that\nspans multiple\nlines."
Python
Python has a similar construct using r:
filePath = r"C:\Foo\Bar.txt"
One can also use them together with triple quotes:
example = r"""First line : "C:\Foo\Bar.txt"
Second line : nothing"""
R
R has a similar construct using r or R with various bracket deliminators ((, [, {):
filePath <- r"(C:\Foo\Bar.txt)"
For raw strings that contain ( instances
string <- r"{Text with (some) parentheses}"
For additional flexibility, a number of dashes can be placed between the opening quote and the opening delimiter, as long as the same number of dashes appear between the closing delimiter and the closing quote.
Ruby
Ruby uses single quote to indicate raw string:
filePath = 'C:\Foo\Bar.txt'
It also has regex percent literals with choice of delimiter like Perl:
%r{ftp://[^/]*/pub/}
%r#ftp://[^/]*/pub/#
%r!ftp://[^/]*/pub/!
Rust
Rust uses a variant of the r prefix:[5]
"\x52"; // R
r"\x52"; // \x52
r#""foo""#; // "foo"
r##"foo #"# bar"##; // foo #"# bar
The literal starts with r followed by any number of #, followed by one ". Further " contained in the literal are considered part of the literal, unless followed by at least as many # as used after the opening r. As such, a string literal opened with r#" cannot have "# in its content.
Scala
Scala allows usage of triple quotes in order to prevent escaping confusion:
val filePath = """C:\Foo\Bar.txt"""
val pubPattern = """ftp://[^/]*/pub/"""r
The triple quotes also allow for multiline strings, as shown here:
val text = """First line,
second line."""
Sed
Sed regular expressions, particularly those using the "s" operator, are much similar to Perl (sed is a predecessor to Perl). The default delimiter is "/", but any delimiter can be used; the default is s/regexp/replacement/, but s:regexp:replacement: is also a valid form. For example, to match a "pub" directory (as in the Perl example) and replace it with "foo", the default (escaping the slashes) is
s/ftp:\/\/[^\/]*\/pub\//foo/
Using an exclamation point ("!") as delimiter instead yields
s!ftp://[^/]*/pub/!foo!