re2 1.18.2 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prettierrc CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "printWidth": 120,
2
+ "printWidth": 80,
3
3
  "singleQuote": true,
4
4
  "bracketSpacing": false,
5
5
  "arrowParens": "avoid",
package/README.md CHANGED
@@ -58,6 +58,7 @@ Supported properties:
58
58
  * [`re2.unicode`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode)
59
59
  * `RE2` engine always works in the Unicode mode. See details below.
60
60
  * [`re2.sticky`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky)
61
+ * *Since 1.19.0*: [`re2.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices)
61
62
  * [`re2.source`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source)
62
63
  * [`re2.flags`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags)
63
64
 
@@ -352,6 +353,8 @@ console.log('re2_res : ' + re2_res); // prints: re2_res : abc,a,b,c
352
353
 
353
354
  ## Release history
354
355
 
356
+ - 1.19.0 *Added `hasIndices` AKA the `d` flag. Thx, [teebu](https://github.com/teebu).*
357
+ - 1.18.3 *Fixed bug with non-matched groups. Thx, [Dan Setterquist](https://github.com/dset).*
355
358
  - 1.18.2 *Reference to the binary module by its full name.*
356
359
  - 1.18.1 *Support for Node 16, 18, 20 + Darwin arm64 precompiled binaries.*
357
360
  - 1.18.0 *Modified TS bindings, added a type test (thx, [Kenichi Kamiya](https://github.com/kachick) and [Jamie Magee](https://github.com/JamieMagee)).*
package/lib/accessors.cc CHANGED
@@ -39,9 +39,13 @@ NAN_GETTER(WrappedRE2::GetFlags)
39
39
  auto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());
40
40
 
41
41
  std::string flags;
42
+ if (re2->hasIndices)
43
+ {
44
+ flags = "d";
45
+ }
42
46
  if (re2->global)
43
47
  {
44
- flags = "g";
48
+ flags += "g";
45
49
  }
46
50
  if (re2->ignoreCase)
47
51
  {
@@ -135,6 +139,18 @@ NAN_GETTER(WrappedRE2::GetSticky)
135
139
  info.GetReturnValue().Set(re2->sticky);
136
140
  }
137
141
 
142
+ NAN_GETTER(WrappedRE2::GetHasIndices)
143
+ {
144
+ if (!WrappedRE2::HasInstance(info.This()))
145
+ {
146
+ info.GetReturnValue().SetUndefined();
147
+ return;
148
+ }
149
+
150
+ auto re2 = Nan::ObjectWrap::Unwrap<WrappedRE2>(info.This());
151
+ info.GetReturnValue().Set(re2->hasIndices);
152
+ }
153
+
138
154
  NAN_GETTER(WrappedRE2::GetLastIndex)
139
155
  {
140
156
  if (!WrappedRE2::HasInstance(info.This()))
package/lib/addon.cc CHANGED
@@ -23,9 +23,9 @@ static NAN_METHOD(GetUtf16Length)
23
23
  info.GetReturnValue().Set(-1);
24
24
  }
25
25
 
26
- static void cleanup(void* p)
26
+ static void cleanup(void *p)
27
27
  {
28
- v8::Isolate* isolate = static_cast<v8::Isolate*>(p);
28
+ v8::Isolate *isolate = static_cast<v8::Isolate *>(p);
29
29
  auto p_tpl = Nan::GetIsolateData<Nan::Persistent<v8::FunctionTemplate>>(isolate);
30
30
  delete p_tpl;
31
31
  }
@@ -71,6 +71,7 @@ v8::Local<v8::Function> WrappedRE2::Init()
71
71
  Nan::SetAccessor(instanceTemplate, Nan::New("dotAll").ToLocalChecked(), GetDotAll);
72
72
  Nan::SetAccessor(instanceTemplate, Nan::New("unicode").ToLocalChecked(), GetUnicode);
73
73
  Nan::SetAccessor(instanceTemplate, Nan::New("sticky").ToLocalChecked(), GetSticky);
74
+ Nan::SetAccessor(instanceTemplate, Nan::New("hasIndices").ToLocalChecked(), GetHasIndices);
74
75
  Nan::SetAccessor(instanceTemplate, Nan::New("lastIndex").ToLocalChecked(), GetLastIndex, SetLastIndex);
75
76
  Nan::SetAccessor(instanceTemplate, Nan::New("internalSource").ToLocalChecked(), GetInternalSource);
76
77
 
package/lib/exec.cc CHANGED
@@ -52,8 +52,9 @@ NAN_METHOD(WrappedRE2::Exec)
52
52
  {
53
53
  size_t s = getUtf8CharSize(str.data[lastIndex]);
54
54
  lastIndex += s;
55
- if (s == 4 && n >= 2) --n; // this utf8 character will take two utf16 characters
56
- // the decrement above is protected to avoid an overflow of an unsigned integer
55
+ if (s == 4 && n >= 2)
56
+ --n; // this utf8 character will take two utf16 characters
57
+ // the decrement above is protected to avoid an overflow of an unsigned integer
57
58
  }
58
59
  }
59
60
  }
@@ -74,7 +75,7 @@ NAN_METHOD(WrappedRE2::Exec)
74
75
 
75
76
  // form a result
76
77
 
77
- auto result = Nan::New<v8::Array>();
78
+ auto result = Nan::New<v8::Array>(), indices = Nan::New<v8::Array>();
78
79
  int indexOffset = re2->global || re2->sticky ? re2->lastIndex : 0;
79
80
 
80
81
  if (str.isBuffer)
@@ -82,13 +83,24 @@ NAN_METHOD(WrappedRE2::Exec)
82
83
  for (size_t i = 0, n = groups.size(); i < n; ++i)
83
84
  {
84
85
  const auto &item = groups[i];
85
- if (item.data() != NULL)
86
+ const auto data = item.data();
87
+ if (data)
86
88
  {
87
- Nan::Set(result, i, Nan::CopyBuffer(item.data(), item.size()).ToLocalChecked());
89
+ Nan::Set(result, i, Nan::CopyBuffer(data, item.size()).ToLocalChecked());
90
+ if (re2->hasIndices) {
91
+ auto pair = Nan::New<v8::Array>();
92
+ auto offset = data - str.data;
93
+ Nan::Set(pair, 0, Nan::New<v8::Integer>(static_cast<int>(offset)));
94
+ Nan::Set(pair, 1, Nan::New<v8::Integer>(static_cast<int>(offset + item.size())));
95
+ Nan::Set(indices, i, pair);
96
+ }
88
97
  }
89
98
  else
90
99
  {
91
100
  Nan::Set(result, i, Nan::Undefined());
101
+ if (re2->hasIndices) {
102
+ Nan::Set(indices, i, Nan::Undefined());
103
+ }
92
104
  }
93
105
  }
94
106
  Nan::Set(result, Nan::New("index").ToLocalChecked(), Nan::New<v8::Integer>(indexOffset + static_cast<int>(groups[0].data() - str.data)));
@@ -98,18 +110,35 @@ NAN_METHOD(WrappedRE2::Exec)
98
110
  for (size_t i = 0, n = groups.size(); i < n; ++i)
99
111
  {
100
112
  const auto &item = groups[i];
101
- if (item.data() != NULL)
113
+ const auto data = item.data();
114
+ if (data)
102
115
  {
103
- Nan::Set(result, i, Nan::New(item.data(), item.size()).ToLocalChecked());
116
+ Nan::Set(result, i, Nan::New(data, item.size()).ToLocalChecked());
117
+ if (re2->hasIndices) {
118
+ auto pair = Nan::New<v8::Array>();
119
+ auto offset = getUtf16Length(str.data + lastIndex, data);
120
+ auto length = getUtf16Length(data, data + item.size());
121
+ Nan::Set(pair, 0, Nan::New<v8::Integer>(static_cast<int>(offset)));
122
+ Nan::Set(pair, 1, Nan::New<v8::Integer>(static_cast<int>(offset + length)));
123
+ Nan::Set(indices, i, pair);
124
+ }
104
125
  }
105
126
  else
106
127
  {
107
128
  Nan::Set(result, i, Nan::Undefined());
129
+ if (re2->hasIndices) {
130
+ Nan::Set(indices, i, Nan::Undefined());
131
+ }
108
132
  }
109
133
  }
110
134
  Nan::Set(result, Nan::New("index").ToLocalChecked(), Nan::New<v8::Integer>(indexOffset + static_cast<int>(getUtf16Length(str.data + lastIndex, groups[0].data()))));
111
135
  }
112
136
 
137
+ if (re2->global || re2->sticky)
138
+ {
139
+ re2->lastIndex += str.isBuffer ? groups[0].data() - str.data + groups[0].size() - lastIndex : getUtf16Length(str.data + lastIndex, groups[0].data() + groups[0].size());
140
+ }
141
+
113
142
  Nan::Set(result, Nan::New("input").ToLocalChecked(), info[0]);
114
143
 
115
144
  const auto &groupNames = re2->regexp.CapturingGroupNames();
@@ -128,15 +157,33 @@ NAN_METHOD(WrappedRE2::Exec)
128
157
  }
129
158
 
130
159
  Nan::Set(result, Nan::New("groups").ToLocalChecked(), groups);
160
+
161
+ if (re2->hasIndices) {
162
+ auto indexGroups = Nan::New<v8::Object>();
163
+ Nan::SetPrototype(indexGroups, Nan::Null());
164
+
165
+ for (auto group : groupNames)
166
+ {
167
+ auto value = Nan::Get(indices, group.first);
168
+ if (!value.IsEmpty())
169
+ {
170
+ Nan::Set(indexGroups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked());
171
+ }
172
+ }
173
+
174
+ Nan::Set(indices, Nan::New("groups").ToLocalChecked(), indexGroups);
175
+ }
131
176
  }
132
177
  else
133
178
  {
134
179
  Nan::Set(result, Nan::New("groups").ToLocalChecked(), Nan::Undefined());
180
+ if (re2->hasIndices) {
181
+ Nan::Set(indices, Nan::New("groups").ToLocalChecked(), Nan::Undefined());
182
+ }
135
183
  }
136
184
 
137
- if (re2->global || re2->sticky)
138
- {
139
- re2->lastIndex += str.isBuffer ? groups[0].data() - str.data + groups[0].size() - lastIndex : getUtf16Length(str.data + lastIndex, groups[0].data() + groups[0].size());
185
+ if (re2->hasIndices) {
186
+ Nan::Set(result, Nan::New("indices").ToLocalChecked(), indices);
140
187
  }
141
188
 
142
189
  info.GetReturnValue().Set(result);
package/lib/match.cc CHANGED
@@ -61,8 +61,9 @@ NAN_METHOD(WrappedRE2::Match)
61
61
  {
62
62
  size_t s = getUtf8CharSize(a.data[lastIndex]);
63
63
  lastIndex += s;
64
- if (s == 4 && n >= 2) --n; // this utf8 character will take two utf16 characters
65
- // the decrement above is protected to avoid an overflow of an unsigned integer
64
+ if (s == 4 && n >= 2)
65
+ --n; // this utf8 character will take two utf16 characters
66
+ // the decrement above is protected to avoid an overflow of an unsigned integer
66
67
  }
67
68
  anchor = RE2::ANCHOR_START;
68
69
  }
@@ -81,16 +82,34 @@ NAN_METHOD(WrappedRE2::Match)
81
82
 
82
83
  // form a result
83
84
 
84
- auto result = Nan::New<v8::Array>();
85
+ auto result = Nan::New<v8::Array>(), indices = Nan::New<v8::Array>();
85
86
 
86
87
  if (a.isBuffer)
87
88
  {
88
89
  for (size_t i = 0, n = groups.size(); i < n; ++i)
89
90
  {
90
91
  const auto &item = groups[i];
91
- if (item.data() != NULL)
92
+ const auto data = item.data();
93
+ if (data)
92
94
  {
93
- Nan::Set(result, i, Nan::CopyBuffer(item.data(), item.size()).ToLocalChecked());
95
+ Nan::Set(result, i, Nan::CopyBuffer(data, item.size()).ToLocalChecked());
96
+ if (!re2->global && re2->hasIndices)
97
+ {
98
+ auto pair = Nan::New<v8::Array>();
99
+ auto offset = getUtf16Length(a.data + lastIndex, data);
100
+ auto length = getUtf16Length(data, data + item.size());
101
+ Nan::Set(pair, 0, Nan::New<v8::Integer>(static_cast<int>(offset)));
102
+ Nan::Set(pair, 1, Nan::New<v8::Integer>(static_cast<int>(offset + length)));
103
+ Nan::Set(indices, i, pair);
104
+ }
105
+ }
106
+ else
107
+ {
108
+ Nan::Set(result, i, Nan::Undefined());
109
+ if (!re2->global && re2->hasIndices)
110
+ {
111
+ Nan::Set(indices, i, Nan::Undefined());
112
+ }
94
113
  }
95
114
  }
96
115
  if (!re2->global)
@@ -104,9 +123,27 @@ NAN_METHOD(WrappedRE2::Match)
104
123
  for (size_t i = 0, n = groups.size(); i < n; ++i)
105
124
  {
106
125
  const auto &item = groups[i];
107
- if (item.data() != NULL)
126
+ const auto data = item.data();
127
+ if (data)
108
128
  {
109
- Nan::Set(result, i, Nan::New(item.data(), item.size()).ToLocalChecked());
129
+ Nan::Set(result, i, Nan::New(data, item.size()).ToLocalChecked());
130
+ if (!re2->global && re2->hasIndices)
131
+ {
132
+ auto pair = Nan::New<v8::Array>();
133
+ auto offset = getUtf16Length(a.data + lastIndex, data);
134
+ auto length = getUtf16Length(data, data + item.size());
135
+ Nan::Set(pair, 0, Nan::New<v8::Integer>(static_cast<int>(offset)));
136
+ Nan::Set(pair, 1, Nan::New<v8::Integer>(static_cast<int>(offset + length)));
137
+ Nan::Set(indices, i, pair);
138
+ }
139
+ }
140
+ else
141
+ {
142
+ Nan::Set(result, i, Nan::Undefined());
143
+ if (!re2->global && re2->hasIndices)
144
+ {
145
+ Nan::Set(indices, i, Nan::Undefined());
146
+ }
110
147
  }
111
148
  }
112
149
  if (!re2->global)
@@ -143,12 +180,38 @@ NAN_METHOD(WrappedRE2::Match)
143
180
  }
144
181
 
145
182
  Nan::Set(result, Nan::New("groups").ToLocalChecked(), groups);
183
+
184
+ if (re2->hasIndices)
185
+ {
186
+ auto indexGroups = Nan::New<v8::Object>();
187
+ Nan::SetPrototype(indexGroups, Nan::Null());
188
+
189
+ for (auto group : groupNames)
190
+ {
191
+ auto value = Nan::Get(indices, group.first);
192
+ if (!value.IsEmpty())
193
+ {
194
+ Nan::Set(indexGroups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked());
195
+ }
196
+ }
197
+
198
+ Nan::Set(indices, Nan::New("groups").ToLocalChecked(), indexGroups);
199
+ }
146
200
  }
147
201
  else
148
202
  {
149
203
  Nan::Set(result, Nan::New("groups").ToLocalChecked(), Nan::Undefined());
204
+ if (re2->hasIndices)
205
+ {
206
+ Nan::Set(indices, Nan::New("groups").ToLocalChecked(), Nan::Undefined());
207
+ }
150
208
  }
151
209
  }
152
210
 
211
+ if (re2->hasIndices)
212
+ {
213
+ Nan::Set(result, Nan::New("indices").ToLocalChecked(), indices);
214
+ }
215
+
153
216
  info.GetReturnValue().Set(result);
154
217
  }
package/lib/new.cc CHANGED
@@ -232,6 +232,7 @@ NAN_METHOD(WrappedRE2::New)
232
232
  bool dotAll = false;
233
233
  bool unicode = false;
234
234
  bool sticky = false;
235
+ bool hasIndices = false;
235
236
 
236
237
  auto context = Nan::GetCurrentContext();
237
238
 
@@ -272,6 +273,9 @@ NAN_METHOD(WrappedRE2::New)
272
273
  case 'y':
273
274
  sticky = true;
274
275
  break;
276
+ case 'd':
277
+ hasIndices = true;
278
+ break;
275
279
  }
276
280
  }
277
281
  size = 0;
@@ -306,6 +310,7 @@ NAN_METHOD(WrappedRE2::New)
306
310
  dotAll = bool(flags & v8::RegExp::kDotAll);
307
311
  unicode = bool(flags & v8::RegExp::kUnicode);
308
312
  sticky = bool(flags & v8::RegExp::kSticky);
313
+ hasIndices = bool(flags & v8::RegExp::kHasIndices);
309
314
  }
310
315
  else if (info[0]->IsObject() && !info[0]->IsString())
311
316
  {
@@ -332,6 +337,7 @@ NAN_METHOD(WrappedRE2::New)
332
337
  dotAll = re2->dotAll;
333
338
  unicode = true;
334
339
  sticky = re2->sticky;
340
+ hasIndices = re2->hasIndices;
335
341
  }
336
342
  }
337
343
  else if (info[0]->IsString())
@@ -385,7 +391,7 @@ NAN_METHOD(WrappedRE2::New)
385
391
  options.set_dot_nl(dotAll);
386
392
  options.set_log_errors(false); // inappropriate when embedding
387
393
 
388
- std::unique_ptr<WrappedRE2> re2(new WrappedRE2(re2::StringPiece(data, size), options, source, global, ignoreCase, multiline, dotAll, sticky));
394
+ std::unique_ptr<WrappedRE2> re2(new WrappedRE2(re2::StringPiece(data, size), options, source, global, ignoreCase, multiline, dotAll, sticky, hasIndices));
389
395
  if (!re2->regexp.ok())
390
396
  {
391
397
  return Nan::ThrowSyntaxError(re2->regexp.error().c_str());
package/lib/replace.cc CHANGED
@@ -228,7 +228,8 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
228
228
  {
229
229
  size_t s = getUtf8CharSize(data[lastIndex]);
230
230
  lastIndex += s;
231
- if (s == 4 && n >= 2) {
231
+ if (s == 4 && n >= 2)
232
+ {
232
233
  --n; // this utf8 character will take two utf16 characters
233
234
  }
234
235
  // the decrement above is protected to avoid an overflow of an unsigned integer
@@ -299,7 +300,7 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
299
300
 
300
301
  inline Nan::Maybe<std::string> replace(const Nan::Callback *replacer, const std::vector<re2::StringPiece> &groups, const re2::StringPiece &str, const v8::Local<v8::Value> &input, bool useBuffers, const std::map<std::string, int> &namedGroups)
301
302
  {
302
- std::vector<v8::Local<v8::Value> > argv;
303
+ std::vector<v8::Local<v8::Value>> argv;
303
304
 
304
305
  auto context = Nan::GetCurrentContext();
305
306
 
@@ -308,7 +309,15 @@ inline Nan::Maybe<std::string> replace(const Nan::Callback *replacer, const std:
308
309
  for (size_t i = 0, n = groups.size(); i < n; ++i)
309
310
  {
310
311
  const auto &item = groups[i];
311
- argv.push_back(Nan::CopyBuffer(item.data(), item.size()).ToLocalChecked());
312
+ const auto data = item.data();
313
+ if (data)
314
+ {
315
+ argv.push_back(Nan::CopyBuffer(data, item.size()).ToLocalChecked());
316
+ }
317
+ else
318
+ {
319
+ argv.push_back(Nan::Undefined());
320
+ }
312
321
  }
313
322
  argv.push_back(Nan::New(static_cast<int>(groups[0].data() - str.data())));
314
323
  }
@@ -317,7 +326,15 @@ inline Nan::Maybe<std::string> replace(const Nan::Callback *replacer, const std:
317
326
  for (size_t i = 0, n = groups.size(); i < n; ++i)
318
327
  {
319
328
  const auto &item = groups[i];
320
- argv.push_back(Nan::New(item.data(), item.size()).ToLocalChecked());
329
+ const auto data = item.data();
330
+ if (data)
331
+ {
332
+ argv.push_back(Nan::New(data, item.size()).ToLocalChecked());
333
+ }
334
+ else
335
+ {
336
+ argv.push_back(Nan::Undefined());
337
+ }
321
338
  }
322
339
  argv.push_back(Nan::New(static_cast<int>(getUtf16Length(str.data(), groups[0].data()))));
323
340
  }
@@ -381,7 +398,8 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
381
398
  {
382
399
  size_t s = getUtf8CharSize(data[lastIndex]);
383
400
  lastIndex += s;
384
- if (s == 4 && n >= 2) {
401
+ if (s == 4 && n >= 2)
402
+ {
385
403
  --n; // this utf8 character will take two utf16 characters
386
404
  }
387
405
  // the decrement above is protected to avoid an overflow of an unsigned integer
package/lib/wrapped_re2.h CHANGED
@@ -11,8 +11,8 @@ class WrappedRE2 : public Nan::ObjectWrap
11
11
  {
12
12
  private:
13
13
  WrappedRE2(const re2::StringPiece &pattern, const re2::RE2::Options &options, const std::string &src,
14
- const bool &g, const bool &i, const bool &m, const bool &s, const bool &y) : regexp(pattern, options),
15
- source(src), global(g), ignoreCase(i), multiline(m), dotAll(s), sticky(y), lastIndex(0) {}
14
+ const bool &g, const bool &i, const bool &m, const bool &s, const bool &y, const bool &d) : regexp(pattern, options),
15
+ source(src), global(g), ignoreCase(i), multiline(m), dotAll(s), sticky(y), hasIndices(d), lastIndex(0) {}
16
16
 
17
17
  static NAN_METHOD(New);
18
18
  static NAN_METHOD(ToString);
@@ -25,6 +25,7 @@ private:
25
25
  static NAN_GETTER(GetDotAll);
26
26
  static NAN_GETTER(GetUnicode);
27
27
  static NAN_GETTER(GetSticky);
28
+ static NAN_GETTER(GetHasIndices);
28
29
  static NAN_GETTER(GetLastIndex);
29
30
  static NAN_SETTER(SetLastIndex);
30
31
  static NAN_GETTER(GetInternalSource);
@@ -70,6 +71,7 @@ public:
70
71
  bool multiline;
71
72
  bool dotAll;
72
73
  bool sticky;
74
+ bool hasIndices;
73
75
  size_t lastIndex;
74
76
  };
75
77
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "re2",
3
- "version": "1.18.2",
3
+ "version": "1.19.0",
4
4
  "description": "Bindings for RE2: fast, safe alternative to backtracking regular expression engines.",
5
5
  "homepage": "https://github.com/uhop/node-re2",
6
6
  "bugs": "https://github.com/uhop/node-re2/issues",
@@ -21,9 +21,13 @@ unit.add(module, [
21
21
 
22
22
  var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
23
23
 
24
- eval(t.TEST("t.unify(result, ['Quick Brown Fox Jumps', 'Brown', 'Jumps'])"));
24
+ eval(
25
+ t.TEST("t.unify(result, ['Quick Brown Fox Jumps', 'Brown', 'Jumps'])")
26
+ );
25
27
  eval(t.TEST('result.index === 4'));
26
- eval(t.TEST("result.input === 'The Quick Brown Fox Jumps Over The Lazy Dog'"));
28
+ eval(
29
+ t.TEST("result.input === 'The Quick Brown Fox Jumps Over The Lazy Dog'")
30
+ );
27
31
  eval(t.TEST('re.lastIndex === 25'));
28
32
  },
29
33
  function test_execSucc(t) {
@@ -148,12 +152,20 @@ unit.add(module, [
148
152
 
149
153
  var result = re.exec('Каждый Охотник Желает Знать Где Сидит Фазан');
150
154
 
151
- eval(t.TEST("t.unify(result, ['Охотник Желает Знать Где', 'Желает', 'Где'])"));
155
+ eval(
156
+ t.TEST("t.unify(result, ['Охотник Желает Знать Где', 'Желает', 'Где'])")
157
+ );
152
158
  eval(t.TEST('result.index === 7'));
153
- eval(t.TEST("result.input === 'Каждый Охотник Желает Знать Где Сидит Фазан'"));
159
+ eval(
160
+ t.TEST("result.input === 'Каждый Охотник Желает Знать Где Сидит Фазан'")
161
+ );
154
162
  eval(t.TEST('re.lastIndex === 31'));
155
163
 
156
- eval(t.TEST("result.input.substr(result.index) === 'Охотник Желает Знать Где Сидит Фазан'"));
164
+ eval(
165
+ t.TEST(
166
+ "result.input.substr(result.index) === 'Охотник Желает Знать Где Сидит Фазан'"
167
+ )
168
+ );
157
169
  eval(t.TEST("result.input.substr(re.lastIndex) === ' Сидит Фазан'"));
158
170
  },
159
171
  function test_execUnicodeSubsequent(t) {
@@ -248,11 +260,21 @@ unit.add(module, [
248
260
 
249
261
  eval(t.TEST('result.index === 13'));
250
262
  eval(t.TEST('result.input instanceof Buffer'));
251
- eval(t.TEST("result.input.toString() === 'Каждый Охотник Желает Знать Где Сидит Фазан'"));
263
+ eval(
264
+ t.TEST(
265
+ "result.input.toString() === 'Каждый Охотник Желает Знать Где Сидит Фазан'"
266
+ )
267
+ );
252
268
  eval(t.TEST('re.lastIndex === 58'));
253
269
 
254
- eval(t.TEST("result.input.toString('utf8', result.index) === 'Охотник Желает Знать Где Сидит Фазан'"));
255
- eval(t.TEST("result.input.toString('utf8', re.lastIndex) === ' Сидит Фазан'"));
270
+ eval(
271
+ t.TEST(
272
+ "result.input.toString('utf8', result.index) === 'Охотник Желает Знать Где Сидит Фазан'"
273
+ )
274
+ );
275
+ eval(
276
+ t.TEST("result.input.toString('utf8', re.lastIndex) === ' Сидит Фазан'")
277
+ );
256
278
  },
257
279
 
258
280
  // Sticky tests
@@ -317,7 +339,9 @@ xy2 (at start of line)
317
339
  eval(t.TEST("result[0] === 'xy'"));
318
340
  eval(t.TEST('result.index > 3'));
319
341
  eval(t.TEST('result.index < pattern.length - 4'));
320
- eval(t.TEST('result[0] === pattern.substr(result.index, result[0].length)'));
342
+ eval(
343
+ t.TEST('result[0] === pattern.substr(result.index, result[0].length)')
344
+ );
321
345
  },
322
346
 
323
347
  // dotAll tests
@@ -332,5 +356,63 @@ xy2 (at start of line)
332
356
  eval(t.TEST("new RE2('a.c', 's').test('abc')"));
333
357
  eval(t.TEST("new RE2(/a.c/s).test('a c')"));
334
358
  eval(t.TEST("new RE2(/a.c/s).test('a\\nc')"));
359
+ },
360
+
361
+ // hasIndices tests
362
+
363
+ function test_execHasIndices(t) {
364
+ 'use strict';
365
+
366
+ eval(t.TEST("!new RE2('1').hasIndices"));
367
+ eval(t.TEST('!new RE2(/1/).hasIndices'));
368
+
369
+ var re = new RE2('(aa)(?<b>b)?(?<c>ccc)', 'd');
370
+
371
+ eval(t.TEST('re.hasIndices'));
372
+
373
+ var result = re.exec('1aabccc2');
374
+
375
+ eval(t.TEST('result.length === 4'));
376
+ eval(t.TEST("result.input === '1aabccc2'"));
377
+ eval(t.TEST('result.index === 1'));
378
+ eval(t.TEST('Object.keys(result.groups).length === 2'));
379
+ eval(t.TEST("result.groups.b === 'b'"));
380
+ eval(t.TEST("result.groups.c === 'ccc'"));
381
+ eval(t.TEST("result[0] === 'aabccc'"));
382
+ eval(t.TEST("result[1] === 'aa'"));
383
+ eval(t.TEST("result[2] === 'b'"));
384
+ eval(t.TEST("result[3] === 'ccc'"));
385
+ eval(t.TEST('result.indices.length === 4'));
386
+ eval(t.TEST('t.unify(result.indices, [[1, 7], [1, 3], [3, 4], [4, 7]])'));
387
+ eval(t.TEST('Object.keys(result.indices.groups).length === 2'));
388
+ eval(t.TEST('t.unify(result.indices.groups.b, [3, 4])'));
389
+ eval(t.TEST('t.unify(result.indices.groups.c, [4, 7])'));
390
+
391
+ result = re.exec('1aaccc2');
392
+
393
+ eval(t.TEST('result.length === 4'));
394
+ eval(t.TEST("result.input === '1aaccc2'"));
395
+ eval(t.TEST('result.index === 1'));
396
+ eval(t.TEST('Object.keys(result.groups).length === 2'));
397
+ eval(t.TEST('result.groups.b === undefined'));
398
+ eval(t.TEST("result.groups.c === 'ccc'"));
399
+ eval(t.TEST("result[0] === 'aaccc'"));
400
+ eval(t.TEST("result[1] === 'aa'"));
401
+ eval(t.TEST('result[2] === undefined'));
402
+ eval(t.TEST("result[3] === 'ccc'"));
403
+ eval(t.TEST('result.indices.length === 4'));
404
+ eval(
405
+ t.TEST('t.unify(result.indices, [[1, 6], [1, 3], undefined, [3, 6]])')
406
+ );
407
+ eval(t.TEST('Object.keys(result.indices.groups).length === 2'));
408
+ eval(t.TEST('t.unify(result.indices.groups.b, undefined)'));
409
+ eval(t.TEST('t.unify(result.indices.groups.c, [3, 6])'));
410
+
411
+ try {
412
+ re = new RE2(new RegExp('1', 'd'));
413
+ eval(t.TEST('re.hasIndices'));
414
+ } catch (e) {
415
+ // squelch
416
+ }
335
417
  }
336
418
  ]);
@@ -1,137 +1,161 @@
1
- "use strict";
2
-
3
-
4
- var unit = require("heya-unit");
5
- var RE2 = require("../re2");
1
+ 'use strict';
6
2
 
3
+ var unit = require('heya-unit');
4
+ var RE2 = require('../re2');
7
5
 
8
6
  // tests
9
7
 
10
8
  unit.add(module, [
9
+ // These tests are copied from MDN:
10
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
11
+
12
+ function test_match(t) {
13
+ 'use strict';
14
+
15
+ var str = 'For more information, see Chapter 3.4.5.1';
16
+
17
+ var re = new RE2(/(chapter \d+(\.\d)*)/i);
18
+ var result = re.match(str);
19
+
20
+ eval(t.TEST('result.input === str'));
21
+ eval(t.TEST('result.index === 26'));
22
+ eval(t.TEST('result.length === 3'));
23
+ eval(t.TEST("result[0] === 'Chapter 3.4.5.1'"));
24
+ eval(t.TEST("result[1] === 'Chapter 3.4.5.1'"));
25
+ eval(t.TEST("result[2] === '.1'"));
26
+ },
27
+ function test_matchGlobal(t) {
28
+ 'use strict';
11
29
 
12
- // These tests are copied from MDN:
13
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
14
-
15
- function test_match(t) {
16
- "use strict";
30
+ var re = new RE2(/[A-E]/gi);
31
+ var result = re.match(
32
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
33
+ );
17
34
 
18
- var str = "For more information, see Chapter 3.4.5.1";
35
+ eval(
36
+ t.TEST(
37
+ "t.unify(result, ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e'])"
38
+ )
39
+ );
40
+ },
41
+ function test_matchFail(t) {
42
+ 'use strict';
19
43
 
20
- var re = new RE2(/(chapter \d+(\.\d)*)/i);
21
- var result = re.match(str);
44
+ var re = new RE2('(a+)?(b+)?');
45
+ var result = re.match('aaabb');
22
46
 
23
- eval(t.TEST("result.input === str"));
24
- eval(t.TEST("result.index === 26"));
25
- eval(t.TEST("result.length === 3"));
26
- eval(t.TEST("result[0] === 'Chapter 3.4.5.1'"));
27
- eval(t.TEST("result[1] === 'Chapter 3.4.5.1'"));
28
- eval(t.TEST("result[2] === '.1'"));
29
- },
30
- function test_matchGlobal(t) {
31
- "use strict";
47
+ eval(t.TEST("result[1] === 'aaa'"));
48
+ eval(t.TEST("result[2] === 'bb'"));
32
49
 
33
- var re = new RE2(/[A-E]/gi);
34
- var result = re.match("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
50
+ result = re.match('aaacbb');
35
51
 
36
- eval(t.TEST("t.unify(result, ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e'])"));
37
- },
38
- function test_matchFail(t) {
39
- "use strict";
52
+ eval(t.TEST("result[1] === 'aaa'"));
53
+ eval(t.TEST('result[2] === undefined'));
54
+ },
55
+ function test_matchInvalid(t) {
56
+ 'use strict';
40
57
 
41
- var re = new RE2("(a+)?(b+)?");
42
- var result = re.match("aaabb");
58
+ var re = RE2('');
43
59
 
44
- eval(t.TEST("result[1] === 'aaa'"));
45
- eval(t.TEST("result[2] === 'bb'"));
60
+ try {
61
+ re.match({
62
+ toString() {
63
+ throw 'corner';
64
+ }
65
+ });
66
+ t.test(false); // shouldn't be here
67
+ } catch (e) {
68
+ eval(t.TEST("e === 'corner'"));
69
+ }
70
+ },
46
71
 
47
- result = re.match("aaacbb");
72
+ // Unicode tests
48
73
 
49
- eval(t.TEST("result[1] === 'aaa'"));
50
- eval(t.TEST("result[2] === undefined"));
51
- },
52
- function test_matchInvalid(t) {
53
- "use strict";
74
+ function test_matchUnicode(t) {
75
+ 'use strict';
54
76
 
55
- var re = RE2('');
77
+ var str = 'Это ГЛАВА 3.4.5.1';
56
78
 
57
- try {
58
- re.match({ toString() { throw "corner"; } });
59
- t.test(false); // shouldn't be here
60
- } catch(e) {
61
- eval(t.TEST("e === 'corner'"));
62
- }
63
- },
79
+ var re = new RE2(/(глава \d+(\.\d)*)/i);
80
+ var result = re.match(str);
64
81
 
65
- // Unicode tests
82
+ eval(t.TEST('result.input === str'));
83
+ eval(t.TEST('result.index === 4'));
84
+ eval(t.TEST('result.length === 3'));
85
+ eval(t.TEST("result[0] === 'ГЛАВА 3.4.5.1'"));
86
+ eval(t.TEST("result[1] === 'ГЛАВА 3.4.5.1'"));
87
+ eval(t.TEST("result[2] === '.1'"));
88
+ },
66
89
 
67
- function test_matchUnicode(t) {
68
- "use strict";
90
+ // Buffer tests
69
91
 
70
- var str = "Это ГЛАВА 3.4.5.1";
92
+ function test_matchBuffer(t) {
93
+ 'use strict';
71
94
 
72
- var re = new RE2(/(глава \d+(\.\d)*)/i);
73
- var result = re.match(str);
95
+ var buf = new Buffer('Это ГЛАВА 3.4.5.1');
74
96
 
75
- eval(t.TEST("result.input === str"));
76
- eval(t.TEST("result.index === 4"));
77
- eval(t.TEST("result.length === 3"));
78
- eval(t.TEST("result[0] === 'ГЛАВА 3.4.5.1'"));
79
- eval(t.TEST("result[1] === 'ГЛАВА 3.4.5.1'"));
80
- eval(t.TEST("result[2] === '.1'"));
81
- },
97
+ var re = new RE2(/(глава \d+(\.\d)*)/i);
98
+ var result = re.match(buf);
82
99
 
83
- // Buffer tests
100
+ eval(t.TEST('result.input instanceof Buffer'));
101
+ eval(t.TEST('result.length === 3'));
102
+ eval(t.TEST('result[0] instanceof Buffer'));
103
+ eval(t.TEST('result[1] instanceof Buffer'));
104
+ eval(t.TEST('result[2] instanceof Buffer'));
84
105
 
85
- function test_matchBuffer(t) {
86
- "use strict";
106
+ eval(t.TEST('result.input === buf'));
107
+ eval(t.TEST('result.index === 7'));
108
+ eval(
109
+ t.TEST("result.input.toString('utf8', result.index) === 'ГЛАВА 3.4.5.1'")
110
+ );
111
+ eval(t.TEST("result[0].toString() === 'ГЛАВА 3.4.5.1'"));
112
+ eval(t.TEST("result[1].toString() === 'ГЛАВА 3.4.5.1'"));
113
+ eval(t.TEST("result[2].toString() === '.1'"));
114
+ },
87
115
 
88
- var buf = new Buffer("Это ГЛАВА 3.4.5.1");
116
+ // Sticky tests
89
117
 
90
- var re = new RE2(/(глава \d+(\.\d)*)/i);
91
- var result = re.match(buf);
118
+ function test_matchSticky(t) {
119
+ 'use strict';
92
120
 
93
- eval(t.TEST("result.input instanceof Buffer"));
94
- eval(t.TEST("result.length === 3"));
95
- eval(t.TEST("result[0] instanceof Buffer"));
96
- eval(t.TEST("result[1] instanceof Buffer"));
97
- eval(t.TEST("result[2] instanceof Buffer"));
121
+ var re = new RE2('\\s+', 'y');
98
122
 
99
- eval(t.TEST("result.input === buf"));
100
- eval(t.TEST("result.index === 7"));
101
- eval(t.TEST("result.input.toString('utf8', result.index) === 'ГЛАВА 3.4.5.1'"));
102
- eval(t.TEST("result[0].toString() === 'ГЛАВА 3.4.5.1'"));
103
- eval(t.TEST("result[1].toString() === 'ГЛАВА 3.4.5.1'"));
104
- eval(t.TEST("result[2].toString() === '.1'"));
105
- },
123
+ eval(t.TEST("re.match('Hello world, how are you?') === null"));
106
124
 
107
- // Sticky tests
125
+ re.lastIndex = 5;
108
126
 
109
- function test_matchSticky(t) {
110
- "use strict";
127
+ var result = re.match('Hello world, how are you?');
111
128
 
112
- var re = new RE2("\\s+", "y");
129
+ eval(t.TEST("t.unify(result, [' '])"));
130
+ eval(t.TEST('result.index === 5'));
131
+ eval(t.TEST('re.lastIndex === 6'));
113
132
 
114
- eval(t.TEST("re.match('Hello world, how are you?') === null"));
133
+ var re2 = new RE2('\\s+', 'gy');
115
134
 
116
- re.lastIndex = 5;
135
+ eval(t.TEST("re2.match('Hello world, how are you?') === null"));
117
136
 
118
- var result = re.match("Hello world, how are you?");
137
+ re2.lastIndex = 5;
119
138
 
120
- eval(t.TEST("t.unify(result, [' '])"));
121
- eval(t.TEST("result.index === 5"));
122
- eval(t.TEST("re.lastIndex === 6"));
139
+ eval(t.TEST("re2.match('Hello world, how are you?') === null"));
123
140
 
124
- var re2 = new RE2("\\s+", "gy");
141
+ var re3 = new RE2(/[A-E]/giy);
142
+ var result3 = re3.match(
143
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
144
+ );
125
145
 
126
- eval(t.TEST("re2.match('Hello world, how are you?') === null"));
146
+ eval(t.TEST("t.unify(result3, ['A', 'B', 'C', 'D', 'E'])"));
147
+ },
127
148
 
128
- re2.lastIndex = 5;
149
+ // hasIndices tests
129
150
 
130
- eval(t.TEST("re2.match('Hello world, how are you?') === null"));
151
+ function test_matchHasIndices(t) {
152
+ 'use strict';
131
153
 
132
- var re3 = new RE2(/[A-E]/giy);
133
- var result3 = re3.match("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
154
+ var re = new RE2('(aa)(?<b>b)?(?<c>ccc)', 'd'),
155
+ str1 = '1aabccc2',
156
+ str2 = '1aaccc2';
134
157
 
135
- eval(t.TEST("t.unify(result3, ['A', 'B', 'C', 'D', 'E'])"));
136
- }
158
+ eval(t.TEST("t.unify(str1.match(re), re.exec(str1))"));
159
+ eval(t.TEST("t.unify(str2.match(re), re.exec(str2))"));
160
+ }
137
161
  ]);
@@ -13,7 +13,10 @@ unit.add(module, [
13
13
  'use strict';
14
14
 
15
15
  var re = new RE2(/apples/gi);
16
- var result = re.replace('Apples are round, and apples are juicy.', 'oranges');
16
+ var result = re.replace(
17
+ 'Apples are round, and apples are juicy.',
18
+ 'oranges'
19
+ );
17
20
  eval(t.TEST("result === 'oranges are round, and oranges are juicy.'"));
18
21
 
19
22
  re = new RE2(/xmas/i);
@@ -199,15 +202,24 @@ unit.add(module, [
199
202
  'use strict';
200
203
 
201
204
  var re = new RE2(/яблоки/gi);
202
- var result = re.replace(new Buffer('Яблоки красны, яблоки сочны.'), 'апельсины');
205
+ var result = re.replace(
206
+ new Buffer('Яблоки красны, яблоки сочны.'),
207
+ 'апельсины'
208
+ );
203
209
  eval(t.TEST('result instanceof Buffer'));
204
210
  eval(t.TEST("result.toString() === 'апельсины красны, апельсины сочны.'"));
205
211
 
206
- result = re.replace(new Buffer('Яблоки красны, яблоки сочны.'), new Buffer('апельсины'));
212
+ result = re.replace(
213
+ new Buffer('Яблоки красны, яблоки сочны.'),
214
+ new Buffer('апельсины')
215
+ );
207
216
  eval(t.TEST('result instanceof Buffer'));
208
217
  eval(t.TEST("result.toString() === 'апельсины красны, апельсины сочны.'"));
209
218
 
210
- result = re.replace('Яблоки красны, яблоки сочны.', new Buffer('апельсины'));
219
+ result = re.replace(
220
+ 'Яблоки красны, яблоки сочны.',
221
+ new Buffer('апельсины')
222
+ );
211
223
  eval(t.TEST("typeof result == 'string'"));
212
224
  eval(t.TEST("result === 'апельсины красны, апельсины сочны.'"));
213
225
  },
@@ -280,5 +292,47 @@ unit.add(module, [
280
292
 
281
293
  eval(t.TEST("re2.replace('ABCDEFABCDEF', '!') === '!!!!!FABCDEF'"));
282
294
  eval(t.TEST('re2.lastIndex === 0'));
295
+ },
296
+
297
+ // Non-matches
298
+
299
+ function test_replaceOneNonMatch(t) {
300
+ 'use strict';
301
+
302
+ function replacer(match, capture, offset, string) {
303
+ t.test(typeof offset == 'number');
304
+ t.test(typeof match == 'string');
305
+ t.test(typeof string == 'string');
306
+ t.test(typeof capture == 'undefined');
307
+ t.test(offset === 0);
308
+ t.test(string === 'hello ');
309
+ return '';
310
+ }
311
+
312
+ var re = new RE2(/hello (world)?/);
313
+ re.replace('hello ', replacer);
314
+ },
315
+ function test_replaceTwoNonMatches(t) {
316
+ 'use strict';
317
+
318
+ function replacer(match, capture1, capture2, offset, string, groups) {
319
+ t.test(typeof offset == 'number');
320
+ t.test(typeof match == 'string');
321
+ t.test(typeof string == 'string');
322
+ t.test(typeof capture1 == 'undefined');
323
+ t.test(typeof capture2 == 'undefined');
324
+ t.test(offset === 1);
325
+ t.test(match === 'b & y');
326
+ t.test(string === 'ab & yz');
327
+ t.test(typeof groups == 'object');
328
+ t.test(Object.keys(groups).length == 2);
329
+ t.test(groups.a === undefined);
330
+ t.test(groups.b == undefined);
331
+ return '';
332
+ }
333
+
334
+ var re = new RE2(/b(?<a>1)? & (?<b>2)?y/);
335
+ var result = re.replace('ab & yz', replacer);
336
+ eval(t.TEST("result === 'az'"));
283
337
  }
284
338
  ]);