1 /**
2  * Boost Software License - Version 1.0 - August 17th, 2003
3  *
4  * Permission is hereby granted, free of charge, to any person or organization
5  * obtaining a copy of the software and accompanying documentation covered by
6  * this license (the "Software") to use, reproduce, display, distribute,
7  * execute, and transmit the Software, and to prepare derivative works of the
8  * Software, and to permit third-parties to whom the Software is furnished to
9  * do so, all subject to the following:
10  *
11  * The copyright notices in the Software and this entire statement, including
12  * the above license grant, this restriction and the following disclaimer,
13  * must be included in all copies of the Software, in whole or in part, and
14  * all derivative works of the Software, unless such copies or derivative
15  * works are solely in the form of machine-executable object code generated by
16  * a source language processor.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  */
26 
27 module dateparser.splitter;
28 
29 debug(dateparser) import std.stdio;
30 import std.compiler;
31 import std.regex;
32 import std.traits;
33 import std.range;
34 
35 // Compatibility patch for version 2.071 and earlier
36 // To be removed when support is dropped for 2.071
37 static if (version_major == 2 && version_minor < 72)
38 {
39     /**
40      * Split the given string on `pat`, but keep the matches in the final result.
41      *
42      * Params:
43      *     r = the string to be split
44      *     pat = the regex pattern
45      * Returns:
46      *     A forward range of strings
47      */
48     package auto splitterWithMatches(Range, RegEx)(Range r, RegEx pat) if (
49         is(Unqual!(ElementEncodingType!Range) : dchar))
50     {
51         return SplitterResult!(Range, RegEx)(r, pat);
52     }
53 
54     // Issue 15831: This should be a Voldemort type, but due to linker slowdown
55     // it's a good idea to put this outside so we don't slowdown people's build
56     // times
57     package static struct SplitterResult(Range, alias RegEx = Regex)
58     {
59     private:
60         Range _input;
61         size_t _offset;
62         bool onMatch = false;
63         alias Rx = typeof(match(Range.init, RegEx.init));
64         Rx _match;
65 
66         @trusted this(Range input, RegEx separator)
67         {
68             _input = input;
69             if (_input.empty)
70             {
71                 //there is nothing to match at all, make _offset > 0
72                 _offset = 1;
73             }
74             else
75             {
76                 _match = Rx(_input, separator);
77             }
78         }
79 
80     public:
81         auto ref opSlice()
82         {
83             return this.save;
84         }
85 
86         ///Forward range primitives.
87         @property Range front()
88         {
89             import std.algorithm : min;
90 
91             assert(!empty && _offset <= _match.pre.length && _match.pre.length <= _input.length);
92 
93             if (!onMatch)
94                 return _input[_offset .. min($, _match.pre.length)];
95             else
96                 return _match.hit();
97         }
98 
99         ///ditto
100         @property bool empty()
101         {
102             return _offset >= _input.length;
103         }
104 
105         ///ditto
106         void popFront()
107         {
108             assert(!empty);
109             if (_match.empty)
110             {
111                 //No more separators, work is done here
112                 _offset = _input.length + 1;
113             }
114             else
115             {
116                 if (!onMatch)
117                 {
118                     //skip past the separator
119                     _offset = _match.pre.length;
120                     onMatch = true;
121                 }
122                 else
123                 {
124                     onMatch = false;
125                     _offset += _match.hit.length;
126                     _match.popFront();
127                 }
128             }
129         }
130 
131         ///ditto
132         @property auto save()
133         {
134             return this;
135         }
136     }
137 }
138 else
139 {
140     import std.typecons;
141 
142     package auto splitterWithMatches(Range, RegEx)(Range r, RegEx pat)
143     {
144         return splitter!(Yes.keepSeparators)(r, pat);
145     }
146 }
147 
148 unittest
149 {
150     import std.algorithm.comparison : equal;
151 
152     assert("2003.04.05"
153         .splitterWithMatches(regex(`([\.,])`, "g"))
154         .equal(["2003", ".", "04", ".", "05"]));
155 
156     assert("10:00a.m."
157         .splitterWithMatches(regex(`([\.,])`, "g"))
158         .equal(["10:00a", ".", "m", "."]));
159 }