mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
614 lines
13 KiB
614 lines
13 KiB
/*
|
|
* Exec() lastIndex behavior is a bit peculiar, especially for non-global
|
|
* regexps (= regexps whose global flag is false).
|
|
*
|
|
* For regexps with global=true, the behavior is quite straightforward:
|
|
* read lastIndex, and write it after matching regardless of success or
|
|
* failure of matching.
|
|
*
|
|
* For regexps with global=false, it is a bit odd (E5 Section 15.10.6.2):
|
|
*
|
|
* 1. lastIndex is read from the object and coerced to integer (steps 4 and 5)
|
|
* 2. the index is reset to zero if global=false (step 7)
|
|
* 3. if the input is exhausted during matching, lastIndex is *written* to
|
|
* zero and a null is returned (step 9.a)
|
|
* 4. if a match occurs, lastIndex is *not* written (step 11)
|
|
*/
|
|
|
|
var r, t;
|
|
var a, b;
|
|
|
|
/*
|
|
* Non-global regexp, normal use.
|
|
*/
|
|
|
|
/*===
|
|
0
|
|
0
|
|
0
|
|
0
|
|
0
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex);
|
|
t = r.exec('bar'); /* no match -> reset to zero (but stays the same here, since it is already 0) */
|
|
print(r.lastIndex);
|
|
t = r.exec('foo'); /* match -> don't touch */
|
|
print(r.lastIndex);
|
|
t = r.exec('foofoo'); /* match -> don't touch */
|
|
print(r.lastIndex);
|
|
t = r.exec('foofoo'); /* match -> don't touch */
|
|
print(r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* Non-global regexp, lastIndex should have no effect on matching
|
|
* and should only change if a match is not found.
|
|
*/
|
|
|
|
/*===
|
|
0
|
|
-1
|
|
foo
|
|
-1
|
|
0
|
|
9999
|
|
foo
|
|
9999
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex);
|
|
r.lastIndex = -1;
|
|
print(r.lastIndex);
|
|
t = r.exec('foo'); /* match -> don't touch */
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
|
|
r = /foo/;
|
|
print(r.lastIndex);
|
|
r.lastIndex = 9999;
|
|
print(r.lastIndex);
|
|
t = r.exec('foo'); /* match -> don't touch */
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
|
|
/*
|
|
* Non-global regexp, lastIndex is not written to if a match is found,
|
|
* even if lastIndex needs coercion.
|
|
*
|
|
* Note: Smjs and Rhino seems to coerce lastIndex to number when
|
|
* it is written, so they fail this test. lastIndex is just a
|
|
* normal writable property, see E5 Section 15.10.7.5. It is
|
|
* coerced to integer *when used*.
|
|
*/
|
|
|
|
/*===
|
|
0 number
|
|
1 string
|
|
1 string
|
|
1 string
|
|
1 string
|
|
0 number
|
|
1 object
|
|
1 object
|
|
1 object
|
|
1 object
|
|
true
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = "1";
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.exec('foo');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.exec('foofoo');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.exec('foofoo');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
a = new String("1");
|
|
r.lastIndex = a;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.exec('foo');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.exec('foofoo');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.exec('foofoo');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
print(r.lastIndex === a); /* instance should still be the same */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* Non-global regexp, more lastIndex behavior when match is found
|
|
* and when it is not.
|
|
*/
|
|
|
|
/*===
|
|
0 number
|
|
0 number
|
|
0 number
|
|
1 string
|
|
1 string
|
|
0 number
|
|
-1 number
|
|
-1 number
|
|
0 number
|
|
1000000000000 number
|
|
1000000000000 number
|
|
0 number
|
|
1 string
|
|
0 number
|
|
===*/
|
|
|
|
/* match: zero is not written to */
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foobar');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/* match: string '1' is not written to, and is ignored */
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = '1';
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foobar');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/* match: number -1 is not written to, and is ignored */
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = -1;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foobar');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/* match: a large number is not written to, and is ignored */
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = 1000000000000; /* above 2^32 */
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foobar');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/* no match: previous value is overwritten with zero (step 9.a.i),
|
|
* regardless of the global flag!
|
|
*
|
|
* Rhino and smjs fail this test.
|
|
*/
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = '1';
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('bar');
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* Global regexp, normal use.
|
|
*/
|
|
|
|
/*===
|
|
0
|
|
foo
|
|
3
|
|
null
|
|
0
|
|
0
|
|
foo
|
|
3
|
|
Foo
|
|
6
|
|
FOO
|
|
9
|
|
null
|
|
0
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/g;
|
|
print(r.lastIndex);
|
|
t = r.exec('foo');
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
t = r.exec('foo');
|
|
print(t);
|
|
print(r.lastIndex);
|
|
|
|
r = /foo/gi;
|
|
print(r.lastIndex);
|
|
t = r.exec('fooFooFOO');
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
t = r.exec('fooFooFOO');
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
t = r.exec('fooFooFOO');
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
t = r.exec('fooFooFOO');
|
|
print(t);
|
|
print(r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* Global regexp, change input string while lastIndex changes,
|
|
* just a sanity check.
|
|
*/
|
|
|
|
/*===
|
|
0
|
|
foo
|
|
3
|
|
FOO
|
|
6
|
|
null
|
|
0
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/gi;
|
|
print(r.lastIndex);
|
|
|
|
t = r.exec('foo'); /* match, leave at 3 */
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
|
|
t = r.exec('barFOO');
|
|
print(t[0]);
|
|
print(r.lastIndex);
|
|
|
|
t = r.exec('foo'); /* starts at 6, out of bounds => null and 0 */
|
|
print(t);
|
|
print(r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* Coercion and update of a non-number lastIndex. Out of bounds lastIndex
|
|
* causes exec() to return null, and reset lastIndex to zero.
|
|
*/
|
|
|
|
/*===
|
|
0
|
|
-1
|
|
null
|
|
0
|
|
999
|
|
null
|
|
0
|
|
object
|
|
2
|
|
Foo
|
|
3
|
|
6
|
|
number
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/gi;
|
|
print(r.lastIndex);
|
|
|
|
r.lastIndex = -1;
|
|
print(r.lastIndex);
|
|
t = r.exec('foo');
|
|
print(t);
|
|
print(r.lastIndex);
|
|
|
|
r.lastIndex = 999;
|
|
print(r.lastIndex);
|
|
t = r.exec('foo');
|
|
print(t);
|
|
print(r.lastIndex);
|
|
|
|
a = {};
|
|
a.valueOf = function() { return 2; };
|
|
r.lastIndex = a;
|
|
print(typeof r.lastIndex);
|
|
print(r.lastIndex.valueOf());
|
|
t = r.exec('fooFoo'); /* start matching at 2, find match at 3 for 'Foo' */
|
|
print(t[0]);
|
|
print(t.index);
|
|
print(r.lastIndex);
|
|
print(typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* lastIndex is coerced with ToInteger() which -allows- values
|
|
* larger than 32 bits. For instance, 0x100000000 must NOT be
|
|
* confused with 0x00000000 as would happen if lastIndex were
|
|
* coerced with ToUint32() for instance.
|
|
*/
|
|
|
|
/*===
|
|
0 number
|
|
null
|
|
0 number
|
|
0 number
|
|
null
|
|
0 number
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = 4294967297.0; /* 0x100000001 */
|
|
t = r.exec('foofoofoo'); /* no match, lastIndex is reset to zero */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
try {
|
|
r = /foo/g;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = 4294967297.0; /* 0x100000001 */
|
|
t = r.exec('foofoofoo'); /* no match, lastIndex is reset to zero */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* lastIndex can be NAN or +/- Infinity. These have well defined
|
|
* behavior. If the global flag is not set, they are ignored (but
|
|
* overwritten if there is no match). If the global flag is set,
|
|
* NAN is coerced to +0.0 by ToInteger(), while +/- Infinity is used
|
|
* as is. It will cause match to fail at step 9.a, and lastIndex to
|
|
* be overwritten with zero.
|
|
*
|
|
* The sign of a zero is preserved by ToInteger(). It must be preserved
|
|
* for non-global regexps which -do match-.
|
|
*/
|
|
|
|
/*===
|
|
0 number
|
|
NaN number
|
|
foo
|
|
NaN number
|
|
null
|
|
0 number Infinity
|
|
0 number
|
|
NaN number
|
|
foo
|
|
3 number
|
|
null
|
|
0 number Infinity
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = NaN;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo'); /* match -> don't update */
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('bar'); /* no match -> overwrite with zero */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
try {
|
|
r = /foo/g;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = NaN;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo'); /* match -> update */
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('bar'); /* no match -> overwrite with zero */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*===
|
|
0 number
|
|
Infinity number
|
|
foo
|
|
Infinity number
|
|
null
|
|
0 number Infinity
|
|
0 number
|
|
Infinity number
|
|
null
|
|
0 number Infinity
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = Infinity;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo'); /* match -> don't update */
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('bar'); /* no match -> overwrite with zero */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
try {
|
|
r = /foo/g;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = Infinity;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo'); /* no match (since we start from after end of string -> overwrite */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*===
|
|
0 number
|
|
-Infinity number
|
|
foo
|
|
-Infinity number
|
|
null
|
|
0 number Infinity
|
|
0 number
|
|
-Infinity number
|
|
null
|
|
0 number Infinity
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = -Infinity;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo'); /* match -> don't update */
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('bar'); /* no match -> overwrite with zero */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
try {
|
|
r = /foo/g;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = -Infinity;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo'); /* no match (since we start from after end of string -> overwrite */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*===
|
|
0 number Infinity
|
|
0 number -Infinity
|
|
foo
|
|
0 number -Infinity
|
|
null
|
|
0 number Infinity
|
|
0 number Infinity
|
|
0 number -Infinity
|
|
foo
|
|
3 number
|
|
null
|
|
0 number Infinity
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
|
|
r.lastIndex = -0.0;
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
|
|
t = r.exec('foo'); /* match -> don't update */
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
|
|
t = r.exec('bar'); /* no match -> overwrite with zero (positive) */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
try {
|
|
r = /foo/g;
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
|
|
r.lastIndex = -0.0;
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
|
|
t = r.exec('foo'); /* match -> update */
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('bar'); /* no match -> overwrite with zero (positive) */
|
|
print(t);
|
|
print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex); /* check sign too */
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
/*
|
|
* The lastIndex value is floored.
|
|
*/
|
|
|
|
/*===
|
|
0 number
|
|
0.9 number
|
|
foo
|
|
0.9 number
|
|
0 number
|
|
0.9 number
|
|
foo
|
|
3 number
|
|
===*/
|
|
|
|
try {
|
|
r = /foo/;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = 0.9;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo');
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
try {
|
|
r = /foo/g;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
r.lastIndex = 0.9;
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
t = r.exec('foo');
|
|
print(t[0]);
|
|
print(r.lastIndex, typeof r.lastIndex);
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
|
|
|
|
|