1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Rhino code, released
16
* The Initial Developer of the Original Code is Netscape
17
* Communications Corporation. Portions created by Netscape are
18
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
23
* Alternatively, the contents of this file may be used under the
24
* terms of the GNU Public License (the "GPL"), in which case the
25
* provisions of the GPL are applicable instead of those above.
26
* If you wish to allow use of your version of this file only
27
* under the terms of the GPL and not to allow others to use your
28
* version of this file under the NPL, indicate your decision by
29
* deleting the provisions above and replace them with the notice
30
* and other provisions required by the GPL. If you do not delete
31
* the provisions above, a recipient may use your version of this
32
* file under either the NPL or the GPL.
35
package org.mozilla.javascript.regexp;
37
import org.mozilla.javascript.*;
38
import java.util.Vector;
43
public class RegExpImpl implements RegExpProxy {
46
parens = new Vector(9);
49
public boolean isRegExp(Object obj) {
50
return obj instanceof NativeRegExp;
53
public Object newRegExp(Context cx, Scriptable scope, String source,
54
String global, boolean flat)
56
return new NativeRegExp(cx, scope, source, global, flat);
59
public Object match(Context cx, Scriptable scope,
60
Scriptable thisObj, Object[] args)
61
throws JavaScriptException
63
MatchData mdata = new MatchData();
65
mdata.mode = GlobData.GLOB_MATCH;
66
mdata.parent = ScriptableObject.getTopLevelScope(scope);
67
Object rval = matchOrReplace(cx, scope, thisObj, args,
69
return mdata.arrayobj == null ? rval : mdata.arrayobj;
72
public Object search(Context cx, Scriptable scope,
73
Scriptable thisObj, Object[] args)
74
throws JavaScriptException
76
MatchData mdata = new MatchData();
78
mdata.mode = GlobData.GLOB_SEARCH;
79
mdata.parent = ScriptableObject.getTopLevelScope(scope);
80
return matchOrReplace(cx, scope, thisObj, args, this, mdata, false);
83
public Object replace(Context cx, Scriptable scope,
84
Scriptable thisObj, Object[] args)
85
throws JavaScriptException
87
Object arg1 = args.length < 2 ? Undefined.instance : args[1];
89
Function lambda = null;
90
if (arg1 instanceof Function) {
91
lambda = (Function) arg1;
93
repstr = ScriptRuntime.toString(arg1);
96
ReplaceData rdata = new ReplaceData();
98
rdata.mode = GlobData.GLOB_REPLACE;
99
rdata.lambda = lambda;
100
rdata.repstr = repstr == null ? null : repstr.toCharArray();
101
rdata.dollar = repstr == null ? -1 : repstr.indexOf('$');
102
rdata.charArray = null;
106
Object val = matchOrReplace(cx, scope, thisObj, args,
110
if (rdata.charArray == null) {
111
if (rdata.global || val == null || !val.equals(Boolean.TRUE)) {
112
/* Didn't match even once. */
115
int leftlen = this.leftContext.length;
116
int length = leftlen + rdata.findReplen(cx, this);
117
charArray = new char[length];
118
SubString leftContext = this.leftContext;
119
System.arraycopy(leftContext.charArray, leftContext.index,
120
charArray, 0, leftlen);
121
rdata.doReplace(cx, this, charArray, leftlen);
122
rdata.charArray = charArray;
123
rdata.length = length;
126
SubString rc = this.rightContext;
127
int rightlen = rc.length;
128
int length = rdata.length + rightlen;
129
charArray = new char[length];
130
System.arraycopy(rdata.charArray, 0,
131
charArray, 0, rdata.charArray.length);
132
System.arraycopy(rc.charArray, rc.index, charArray,
133
rdata.length, rightlen);
134
return new String(charArray, 0, length);
138
* Analog of C match_or_replace.
140
private static Object matchOrReplace(Context cx, Scriptable scope,
141
Scriptable thisObj, Object[] args,
143
GlobData data, boolean forceFlat)
144
throws JavaScriptException
148
String str = ScriptRuntime.toString(thisObj);
150
Scriptable topScope = ScriptableObject.getTopLevelScope(scope);
152
if (args.length == 0)
153
re = new NativeRegExp(cx, topScope, "", "", false);
155
if (args[0] instanceof NativeRegExp) {
156
re = (NativeRegExp) args[0];
158
String src = ScriptRuntime.toString(args[0]);
160
if (data.optarg < args.length) {
162
opt = ScriptRuntime.toString(args[data.optarg]);
166
re = new NativeRegExp(cx, topScope, src, opt, forceFlat);
170
data.global = (re.getFlags() & NativeRegExp.GLOB) != 0;
171
int[] indexp = { 0 };
172
Object result = null;
173
if (data.mode == GlobData.GLOB_SEARCH) {
174
result = re.executeRegExp(cx, scope, reImpl,
175
str, indexp, NativeRegExp.TEST);
176
if (result != null && result.equals(Boolean.TRUE))
177
result = new Integer(reImpl.leftContext.length);
179
result = new Integer(-1);
180
} else if (data.global) {
182
for (int count = 0; indexp[0] <= str.length(); count++) {
183
result = re.executeRegExp(cx, scope, reImpl,
184
str, indexp, NativeRegExp.TEST);
185
if (result == null || !result.equals(Boolean.TRUE))
187
data.doGlobal(cx, scope, count, reImpl);
188
if (reImpl.lastMatch.length == 0) {
189
if (indexp[0] == str.length())
195
result = re.executeRegExp(cx, scope, reImpl, str, indexp,
196
((data.mode == GlobData.GLOB_REPLACE)
198
: NativeRegExp.MATCH));
206
public int find_split(Scriptable scope, String target, String separator,
207
Object reObj, int[] ip, int[] matchlen,
208
boolean[] matched, String[][] parensp)
211
int length = target.length();
213
Context cx = Context.getCurrentContext();
215
int version = cx.getLanguageVersion();
216
NativeRegExp re = (NativeRegExp) reObj;
218
while (true) { // imitating C label
219
/* JS1.2 deviated from Perl by never matching at end of string. */
220
int ipsave = ip[0]; // reuse ip to save object creation
222
Object ret = re.executeRegExp(cx, scope, this, target, ip,
224
if (ret != Boolean.TRUE) {
225
// Mismatch: ensure our caller advances i past end of string.
235
SubString sep = this.lastMatch;
236
matchlen[0] = sep.length;
237
if (matchlen[0] == 0) {
239
* Empty string match: never split on an empty
240
* match at the start of a find_split cycle. Same
241
* rule as for an empty global match in
246
* "Bump-along" to avoid sticking at an empty
247
* match, but don't bump past end of string --
248
* our caller must do that by adding
249
* sep->length to our return value.
252
if (version == Context.VERSION_1_2) {
261
continue again; // imitating C goto
264
// PR_ASSERT((size_t)i >= sep->length);
265
result = i - matchlen[0];
268
int size = parens.size();
269
parensp[0] = new String[size];
270
for (int num = 0; num < size; num++) {
271
SubString parsub = getParenSubString(num);
272
parensp[0][num] = parsub.toString();
278
* Analog of REGEXP_PAREN_SUBSTRING in C jsregexp.h.
279
* Assumes zero-based; i.e., for $3, i==2
281
SubString getParenSubString(int i) {
282
if (i >= parens.size())
283
return SubString.emptySubString;
284
return (SubString) parens.elementAt(i);
287
String input; /* input string to match (perl $_, GC root) */
288
boolean multiline; /* whether input contains newlines (perl $*) */
289
Vector parens; /* Vector of SubString; last set of parens
290
matched (perl $1, $2) */
291
SubString lastMatch; /* last string matched (perl $&) */
292
SubString lastParen; /* last paren matched (perl $+) */
293
SubString leftContext; /* input to left of last match (perl $`) */
294
SubString rightContext; /* input to right of last match (perl $') */
298
abstract class GlobData {
299
static final int GLOB_MATCH = 1;
300
static final int GLOB_REPLACE = 2;
301
static final int GLOB_SEARCH = 3;
303
abstract void doGlobal(Context cx, Scriptable scope, int count,
305
throws JavaScriptException;
307
byte mode; /* input: return index, match object, or void */
308
int optarg; /* input: index of optional flags argument */
309
boolean global; /* output: whether regexp was global */
310
String str; /* output: 'this' parameter object as string */
311
NativeRegExp regexp;/* output: regexp parameter object private data */
316
class MatchData extends GlobData {
319
* Analog of match_glob() in jsstr.c
321
void doGlobal(Context cx, Scriptable scope, int count, RegExpImpl reImpl)
322
throws JavaScriptException
328
if (arrayobj == null) {
329
Scriptable s = ScriptableObject.getTopLevelScope(scope);
330
arrayobj = ScriptRuntime.newObject(cx, s, "Array", null);
332
SubString matchsub = reImpl.lastMatch;
333
String matchstr = matchsub.toString();
334
arrayobj.put(count, arrayobj, matchstr);
341
class ReplaceData extends GlobData {
348
* Analog of replace_glob() in jsstr.c
350
void doGlobal(Context cx, Scriptable scope, int count, RegExpImpl reImpl)
351
throws JavaScriptException
353
ReplaceData rdata = this;
355
SubString lc = reImpl.leftContext;
357
char[] leftArray = lc.charArray;
358
int leftIndex = rdata.leftIndex;
360
int leftlen = reImpl.lastMatch.index - leftIndex;
361
rdata.leftIndex = reImpl.lastMatch.index + reImpl.lastMatch.length;
362
int replen = findReplen(cx, reImpl);
363
int growth = leftlen + replen;
365
if (rdata.charArray != null) {
366
charArray = new char[rdata.length + growth];
367
System.arraycopy(rdata.charArray, 0, charArray, 0, rdata.length);
369
charArray = new char[growth];
372
rdata.charArray = charArray;
373
rdata.length += growth;
374
int index = rdata.index;
375
rdata.index += growth;
376
System.arraycopy(leftArray, leftIndex, charArray, index, leftlen);
378
doReplace(cx, reImpl, charArray, index);
381
static SubString dollarStr = new SubString("$");
383
static SubString interpretDollar(Context cx, RegExpImpl res,
384
char[] da, int dp, int bp, int[] skip)
391
/* Allow a real backslash (literal "\\") to escape "$1" etc. */
393
throw new RuntimeException();
394
if ((cx.getLanguageVersion() != Context.VERSION_DEFAULT)
395
&& (cx.getLanguageVersion() <= Context.VERSION_1_4))
396
if (dp > bp && da[dp-1] == '\\')
399
/* Interpret all Perl match-induced dollar variables. */
401
if (NativeRegExp.isDigit(dc)) {
402
if ((cx.getLanguageVersion() != Context.VERSION_DEFAULT)
403
&& (cx.getLanguageVersion() <= Context.VERSION_1_4)) {
406
/* Check for overflow to avoid gobbling arbitrary decimal digits. */
410
while (++cp < ca.length && NativeRegExp.isDigit(dc = ca[cp])) {
411
tmp = 10 * num + NativeRegExp.unDigit(dc);
417
else { /* ECMA 3, 1-9 or 01-99 */
418
num = NativeRegExp.unDigit(dc);
420
if ((dp + 2) < da.length) {
422
if (NativeRegExp.isDigit(dc)) {
423
num = 10 * num + NativeRegExp.unDigit(dc);
427
if (num == 0) return null; /* $0 or $00 is not valid */
429
/* Adjust num from 1 $n-origin to 0 array-index-origin. */
432
return res.getParenSubString(num);
440
return res.lastMatch;
442
return res.lastParen;
444
if (cx.getLanguageVersion() == Context.VERSION_1_2) {
446
* JS1.2 imitated the Perl4 bug where left context at each step
447
* in an iterative use of a global regexp started from last match,
448
* not from the start of the target string. But Perl4 does start
449
* $` at the beginning of the target string when it is used in a
450
* substitution, so we emulate that special case here.
452
res.leftContext.index = 0;
453
res.leftContext.length = res.lastMatch.index;
455
return res.leftContext;
457
return res.rightContext;
463
* Corresponds to find_replen in jsstr.c. rdata is 'this', and
464
* the result parameter sizep is the return value (errors are
465
* propagated with exceptions).
467
int findReplen(Context cx, RegExpImpl reImpl)
468
throws JavaScriptException
470
if (lambda != null) {
471
// invoke lambda function with args lastMatch, $1, $2, ... $n,
472
// leftContext.length, whole string.
473
Vector parens = reImpl.parens;
474
int parenCount = parens.size();
475
Object[] args = new Object[parenCount + 3];
476
args[0] = reImpl.lastMatch.toString();
477
for (int i=0; i < parenCount; i++) {
478
SubString sub = (SubString) parens.elementAt(i);
479
args[i+1] = sub.toString();
481
args[parenCount+1] = new Integer(reImpl.leftContext.length);
482
args[parenCount+2] = str;
483
Scriptable parent = lambda.getParentScope();
484
Object result = lambda.call(cx, parent, parent, args);
486
this.repstr = ScriptRuntime.toString(result).toCharArray();
487
return this.repstr.length;
490
int replen = this.repstr.length;
495
for (int dp = dollar; dp < this.repstr.length ; ) {
496
char c = this.repstr[dp];
502
SubString sub = interpretDollar(cx, reImpl, this.repstr, dp,
505
replen += sub.length - skip[0];
515
* Analog of do_replace in jsstr.c
517
void doReplace(Context cx, RegExpImpl regExpImpl, char[] charArray,
522
int dp = this.dollar;
528
System.arraycopy(repstr, cp, charArray, arrayIndex,
533
SubString sub = interpretDollar(cx, regExpImpl, da,
538
System.arraycopy(sub.charArray, sub.index, charArray,
547
if (dp >= repstr.length) break;
548
while (repstr[dp] != '$') {
550
if (dp >= repstr.length) break outer;
554
if (repstr.length > cp) {
555
System.arraycopy(repstr, cp, charArray, arrayIndex,
560
Function lambda; /* replacement function object or null */
561
char[] repstr; /* replacement string */
562
int dollar; /* -1 or index of first $ in repstr */
563
char[] charArray; /* result characters, null initially */
564
int length; /* result length, 0 initially */
565
int index; /* index in result of next replacement */
566
int leftIndex; /* leftContext index, always 0 for JS1.2 */
1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Rhino code, released
16
* The Initial Developer of the Original Code is Netscape
17
* Communications Corporation. Portions created by Netscape are
18
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
23
* Alternatively, the contents of this file may be used under the
24
* terms of the GNU Public License (the "GPL"), in which case the
25
* provisions of the GPL are applicable instead of those above.
26
* If you wish to allow use of your version of this file only
27
* under the terms of the GPL and not to allow others to use your
28
* version of this file under the NPL, indicate your decision by
29
* deleting the provisions above and replace them with the notice
30
* and other provisions required by the GPL. If you do not delete
31
* the provisions above, a recipient may use your version of this
32
* file under either the NPL or the GPL.
35
package org.mozilla.javascript.regexp;
37
import org.mozilla.javascript.*;
42
public class RegExpImpl implements RegExpProxy {
44
public boolean isRegExp(Scriptable obj) {
45
return obj instanceof NativeRegExp;
48
public Object compileRegExp(Context cx, String source, String flags)
50
return NativeRegExp.compileRE(source, flags, false);
53
public Scriptable wrapRegExp(Context cx, Scriptable scope,
56
return new NativeRegExp(scope, compiled);
59
public Object action(Context cx, Scriptable scope,
60
Scriptable thisObj, Object[] args,
63
GlobData data = new GlobData();
64
data.mode = actionType;
71
rval = matchOrReplace(cx, scope, thisObj, args,
73
return data.arrayobj == null ? rval : data.arrayobj;
78
return matchOrReplace(cx, scope, thisObj, args,
83
Object arg1 = args.length < 2 ? Undefined.instance : args[1];
85
Function lambda = null;
86
if (arg1 instanceof Function) {
87
lambda = (Function) arg1;
89
repstr = ScriptRuntime.toString(arg1);
95
data.dollar = repstr == null ? -1 : repstr.indexOf('$');
98
Object val = matchOrReplace(cx, scope, thisObj, args,
100
SubString rc = this.rightContext;
102
if (data.charBuf == null) {
103
if (data.global || val == null
104
|| !val.equals(Boolean.TRUE))
106
/* Didn't match even once. */
109
SubString lc = this.leftContext;
110
replace_glob(data, cx, scope, this, lc.index, lc.length);
112
data.charBuf.append(rc.charArray, rc.index, rc.length);
113
return data.charBuf.toString();
122
* Analog of C match_or_replace.
124
private static Object matchOrReplace(Context cx, Scriptable scope,
125
Scriptable thisObj, Object[] args,
127
GlobData data, boolean forceFlat)
131
String str = ScriptRuntime.toString(thisObj);
133
Scriptable topScope = ScriptableObject.getTopLevelScope(scope);
135
if (args.length == 0) {
136
Object compiled = NativeRegExp.compileRE("", "", false);
137
re = new NativeRegExp(topScope, compiled);
138
} else if (args[0] instanceof NativeRegExp) {
139
re = (NativeRegExp) args[0];
141
String src = ScriptRuntime.toString(args[0]);
143
if (data.optarg < args.length) {
145
opt = ScriptRuntime.toString(args[data.optarg]);
149
Object compiled = NativeRegExp.compileRE(src, opt, forceFlat);
150
re = new NativeRegExp(topScope, compiled);
154
data.global = (re.getFlags() & NativeRegExp.JSREG_GLOB) != 0;
155
int[] indexp = { 0 };
156
Object result = null;
157
if (data.mode == RA_SEARCH) {
158
result = re.executeRegExp(cx, scope, reImpl,
159
str, indexp, NativeRegExp.TEST);
160
if (result != null && result.equals(Boolean.TRUE))
161
result = new Integer(reImpl.leftContext.length);
163
result = new Integer(-1);
164
} else if (data.global) {
166
for (int count = 0; indexp[0] <= str.length(); count++) {
167
result = re.executeRegExp(cx, scope, reImpl,
168
str, indexp, NativeRegExp.TEST);
169
if (result == null || !result.equals(Boolean.TRUE))
171
if (data.mode == RA_MATCH) {
172
match_glob(data, cx, scope, count, reImpl);
174
if (data.mode != RA_REPLACE) Kit.codeBug();
175
SubString lastMatch = reImpl.lastMatch;
176
int leftIndex = data.leftIndex;
177
int leftlen = lastMatch.index - leftIndex;
178
data.leftIndex = lastMatch.index + lastMatch.length;
179
replace_glob(data, cx, scope, reImpl, leftIndex, leftlen);
181
if (reImpl.lastMatch.length == 0) {
182
if (indexp[0] == str.length())
188
result = re.executeRegExp(cx, scope, reImpl, str, indexp,
189
((data.mode == RA_REPLACE)
191
: NativeRegExp.MATCH));
199
public int find_split(Context cx, Scriptable scope, String target,
200
String separator, Scriptable reObj,
201
int[] ip, int[] matchlen,
202
boolean[] matched, String[][] parensp)
205
int length = target.length();
208
int version = cx.getLanguageVersion();
209
NativeRegExp re = (NativeRegExp) reObj;
211
while (true) { // imitating C label
212
/* JS1.2 deviated from Perl by never matching at end of string. */
213
int ipsave = ip[0]; // reuse ip to save object creation
215
Object ret = re.executeRegExp(cx, scope, this, target, ip,
217
if (ret != Boolean.TRUE) {
218
// Mismatch: ensure our caller advances i past end of string.
228
SubString sep = this.lastMatch;
229
matchlen[0] = sep.length;
230
if (matchlen[0] == 0) {
232
* Empty string match: never split on an empty
233
* match at the start of a find_split cycle. Same
234
* rule as for an empty global match in
239
* "Bump-along" to avoid sticking at an empty
240
* match, but don't bump past end of string --
241
* our caller must do that by adding
242
* sep->length to our return value.
245
if (version == Context.VERSION_1_2) {
254
continue again; // imitating C goto
257
// PR_ASSERT((size_t)i >= sep->length);
258
result = i - matchlen[0];
261
int size = (parens == null) ? 0 : parens.length;
262
parensp[0] = new String[size];
263
for (int num = 0; num < size; num++) {
264
SubString parsub = getParenSubString(num);
265
parensp[0][num] = parsub.toString();
271
* Analog of REGEXP_PAREN_SUBSTRING in C jsregexp.h.
272
* Assumes zero-based; i.e., for $3, i==2
274
SubString getParenSubString(int i)
276
if (parens != null && i < parens.length) {
277
SubString parsub = parens[i];
278
if (parsub != null) {
282
return SubString.emptySubString;
286
* Analog of match_glob() in jsstr.c
288
private static void match_glob(GlobData mdata, Context cx,
289
Scriptable scope, int count,
292
if (mdata.arrayobj == null) {
293
Scriptable s = ScriptableObject.getTopLevelScope(scope);
294
mdata.arrayobj = ScriptRuntime.newObject(cx, s, "Array", null);
296
SubString matchsub = reImpl.lastMatch;
297
String matchstr = matchsub.toString();
298
mdata.arrayobj.put(count, mdata.arrayobj, matchstr);
302
* Analog of replace_glob() in jsstr.c
304
private static void replace_glob(GlobData rdata, Context cx,
305
Scriptable scope, RegExpImpl reImpl,
306
int leftIndex, int leftlen)
310
if (rdata.lambda != null) {
311
// invoke lambda function with args lastMatch, $1, $2, ... $n,
312
// leftContext.length, whole string.
313
SubString[] parens = reImpl.parens;
314
int parenCount = (parens == null) ? 0 : parens.length;
315
Object[] args = new Object[parenCount + 3];
316
args[0] = reImpl.lastMatch.toString();
317
for (int i=0; i < parenCount; i++) {
318
SubString sub = parens[i];
320
args[i+1] = sub.toString();
322
args[i+1] = Undefined.instance;
325
args[parenCount+1] = new Integer(reImpl.leftContext.length);
326
args[parenCount+2] = rdata.str;
327
Scriptable parent = ScriptableObject.getTopLevelScope(scope);
328
Object result = rdata.lambda.call(cx, parent, parent, args);
329
lambdaStr = ScriptRuntime.toString(result);
330
replen = lambdaStr.length();
333
replen = rdata.repstr.length();
334
if (rdata.dollar >= 0) {
335
int[] skip = new int[1];
336
int dp = rdata.dollar;
338
SubString sub = interpretDollar(cx, reImpl, rdata.repstr,
341
replen += sub.length - skip[0];
346
dp = rdata.repstr.indexOf('$', dp);
351
int growth = leftlen + replen + reImpl.rightContext.length;
352
StringBuffer charBuf = rdata.charBuf;
353
if (charBuf == null) {
354
charBuf = new StringBuffer(growth);
355
rdata.charBuf = charBuf;
357
charBuf.ensureCapacity(rdata.charBuf.length() + growth);
360
charBuf.append(reImpl.leftContext.charArray, leftIndex, leftlen);
361
if (rdata.lambda != null) {
362
charBuf.append(lambdaStr);
364
do_replace(rdata, cx, reImpl);
368
private static SubString interpretDollar(Context cx, RegExpImpl res,
369
String da, int dp, int[] skip)
374
if (da.charAt(dp) != '$') Kit.codeBug();
376
/* Allow a real backslash (literal "\\") to escape "$1" etc. */
377
int version = cx.getLanguageVersion();
378
if (version != Context.VERSION_DEFAULT
379
&& version <= Context.VERSION_1_4)
381
if (dp > 0 && da.charAt(dp - 1) == '\\')
384
int daL = da.length();
387
/* Interpret all Perl match-induced dollar variables. */
388
dc = da.charAt(dp + 1);
389
if (NativeRegExp.isDigit(dc)) {
391
if (version != Context.VERSION_DEFAULT
392
&& version <= Context.VERSION_1_4)
396
/* Check for overflow to avoid gobbling arbitrary decimal digits. */
399
while (++cp < daL && NativeRegExp.isDigit(dc = da.charAt(cp)))
401
tmp = 10 * num + (dc - '0');
407
else { /* ECMA 3, 1-9 or 01-99 */
408
int parenCount = (res.parens == null) ? 0 : res.parens.length;
410
if (num > parenCount)
413
if ((dp + 2) < daL) {
414
dc = da.charAt(dp + 2);
415
if (NativeRegExp.isDigit(dc)) {
416
tmp = 10 * num + (dc - '0');
417
if (tmp <= parenCount) {
423
if (num == 0) return null; /* $0 or $00 is not valid */
425
/* Adjust num from 1 $n-origin to 0 array-index-origin. */
428
return res.getParenSubString(num);
434
return new SubString("$");
436
return res.lastMatch;
438
return res.lastParen;
440
if (version == Context.VERSION_1_2) {
442
* JS1.2 imitated the Perl4 bug where left context at each step
443
* in an iterative use of a global regexp started from last match,
444
* not from the start of the target string. But Perl4 does start
445
* $` at the beginning of the target string when it is used in a
446
* substitution, so we emulate that special case here.
448
res.leftContext.index = 0;
449
res.leftContext.length = res.lastMatch.index;
451
return res.leftContext;
453
return res.rightContext;
459
* Analog of do_replace in jsstr.c
461
private static void do_replace(GlobData rdata, Context cx,
462
RegExpImpl regExpImpl)
464
StringBuffer charBuf = rdata.charBuf;
466
String da = rdata.repstr;
467
int dp = rdata.dollar;
469
int[] skip = new int[1];
472
charBuf.append(da.substring(cp, dp));
474
SubString sub = interpretDollar(cx, regExpImpl, da,
479
charBuf.append(sub.charArray, sub.index, len);
486
dp = da.indexOf('$', dp);
489
int daL = da.length();
491
charBuf.append(da.substring(cp, daL));
495
String input; /* input string to match (perl $_, GC root) */
496
boolean multiline; /* whether input contains newlines (perl $*) */
497
SubString[] parens; /* Vector of SubString; last set of parens
498
matched (perl $1, $2) */
499
SubString lastMatch; /* last string matched (perl $&) */
500
SubString lastParen; /* last paren matched (perl $+) */
501
SubString leftContext; /* input to left of last match (perl $`) */
502
SubString rightContext; /* input to right of last match (perl $') */
508
int mode; /* input: return index, match object, or void */
509
int optarg; /* input: index of optional flags argument */
510
boolean global; /* output: whether regexp was global */
511
String str; /* output: 'this' parameter object as string */
512
NativeRegExp regexp;/* output: regexp parameter object private data */
514
// match-specific data
518
// replace-specific data
520
Function lambda; /* replacement function object or null */
521
String repstr; /* replacement string */
522
int dollar = -1; /* -1 or index of first $ in repstr */
523
StringBuffer charBuf; /* result characters, null initially */
524
int leftIndex; /* leftContext index, always 0 for JS1.2 */