Skip to content

Commit 790dabe

Browse files
committed
support lookbehind
CJex#23 CJex#41 CJex#48 CJex#54
1 parent a5fa510 commit 790dabe

File tree

5 files changed

+102
-8
lines changed

5 files changed

+102
-8
lines changed

dist/regulex.js

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/RegExp.js

+17
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ var NFABuilders=(function _() {
284284
case AssertNegativeLookahead:
285285
f=_negativeLookahead(node);
286286
break;
287+
case AssertLookbehind:
288+
f=_lookbehind(node);
289+
break;
290+
case AssertNegativeLookbehind:
291+
f=_negativeLookbehind(node);
292+
break;
287293
}
288294
return _newAssert(node,from,f);
289295

@@ -304,10 +310,21 @@ var NFABuilders=(function _() {
304310
return ret.acceptable;
305311
};
306312
}
313+
function _lookbehind(node) {
314+
var m=NFA(tree2NFA(node.sub,['start']));
315+
return function _Lookbehind(stack,c,i,state,s) {
316+
var ret=m.input(s,i,null,stack);
317+
return ret.acceptable;
318+
};
319+
}
307320
function _negativeLookahead(node) {
308321
var f=_lookahead(node);
309322
return function _NLookahead() {return !f.apply(this,arguments)};
310323
}
324+
function _negativeLookbehind(node) {
325+
var f=_lookbehind(node);
326+
return function _NLookbehind() {return !f.apply(this,arguments)};
327+
}
311328

312329
function _isBoundary(i,s) {return !!(_isWordChar(i-1,s) ^ _isWordChar(i,s))}
313330
function _isWordChar(i,s) {return i!==-1 && i!==s.length && /\w/.test(s[i])}

src/parse.js

+16-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ var Constants={
2020
//Assertion Type Constants
2121
AssertLookahead:"AssertLookahead",
2222
AssertNegativeLookahead:"AssertNegativeLookahead",
23+
AssertLookbehind:"AssertLookbehind",
24+
AssertNegativeLookbehind:"AssertNegativeLookbehind",
2325
AssertNonWordBoundary:"AssertNonWordBoundary",
2426
AssertWordBoundary:"AssertWordBoundary",
2527
AssertEnd:"AssertEnd",
@@ -350,8 +352,14 @@ function _checkRepeat(node) {
350352
if (node.repeat) {
351353
var astype = node.assertionType;
352354
var msg = 'Nothing to repeat! Repeat after assertion doesn\'t make sense!';
353-
if (astype === 'AssertLookahead' || astype === 'AssertNegativeLookahead' ) {
354-
var assertifier = astype === 'AssertLookahead' ? '?=' : '?!';
355+
var assertifiers = {
356+
'AssertLookahead': '?=',
357+
'AssertNegativeLookahead': '?!',
358+
'AssertLookbehind': '?<=',
359+
'AssertNegativeLookbehind': '?<!',
360+
}
361+
var assertifier = assertifiers[astype];
362+
if (assertifier !== undefined) {
355363
var pattern = '('+assertifier+'b)';
356364
msg += '\n/a'+pattern+'+/、/a'+pattern+'{1,n}/ are the same as /a'+pattern+'/。\n' +
357365
'/a'+pattern+'*/、/a'+pattern+'{0,n}/、/a'+pattern+'?/ are the same as /a/。';
@@ -567,10 +575,13 @@ var actions=(function _() {
567575
group.num=undefined;
568576
stack.groupCounter.i--;
569577
}
570-
function groupToAssertion(stack,c,i) { // Convert /(?!)/,/(?=)/ to AssertNode
578+
function groupToAssertion(stack,c,i,state) { // Convert /(?!)/,/(?=)/ to AssertNode
571579
var group=stack._parentGroup;
572580
group.type=ASSERT_NODE;
573581
group.assertionType= c=='=' ? AssertLookahead : AssertNegativeLookahead ;
582+
if (state === 'groupNameStart') {
583+
group.assertionType= c==='=' ? AssertLookbehind : AssertNegativeLookbehind ;
584+
}
574585
// Caveat!!! Assertion group no need to capture
575586
group.num=undefined;
576587
stack.groupCounter.i--;
@@ -957,6 +968,8 @@ var config={
957968
['groupQualify>groupQualifiedStart',':',actions.groupNonCapture],//group non-capturing
958969
['groupQualify>groupQualifiedStart','=',actions.groupToAssertion],//group positive lookahead
959970
['groupQualify>groupQualifiedStart','!',actions.groupToAssertion],//group negative lookahead
971+
['groupNameStart>groupQualifiedStart','=',actions.groupToAssertion],//group positive lookbehind
972+
['groupNameStart>groupQualifiedStart','!',actions.groupToAssertion],//group negative lookbehind
960973
['groupQualify>groupQualifiedStart','>',actions.groupAtomicGroup],//group atomic-group
961974
['groupQualify>groupNameStart','<'],
962975
['groupNameStart>groupName','a-zA-Z', actions.groupName],//group name

src/visualize.js

+14
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,14 @@ var plotNode={
643643
fg="Purple";
644644
//txt="Negative\nLookahead!"; // break line
645645
txt="Not followed by:";
646+
} else if (nat === AssertLookbehind) {
647+
lineColor="LightSeaGreen";
648+
fg="Brown";
649+
txt="Preceded by:";
650+
} else if (nat === AssertNegativeLookbehind) {
651+
lineColor="#F69";
652+
fg="MediumVioletRed";
653+
txt="Not preceded by:";
646654
}
647655

648656
var sub=plotNode.group(node,x,y);
@@ -709,6 +717,8 @@ var hlColorMap={
709717
')':'blue',
710718
'?=':'darkgreen',
711719
'?!':'red',
720+
'?<!':'brown',
721+
'?<=':'darkcyan',
712722
'?:':'grey',
713723
'[':'navy',
714724
']':'navy',
@@ -740,6 +750,10 @@ function highlight(tree) {
740750
if (node.type===ASSERT_NODE) {
741751
if (node.assertionType===AssertLookahead) {
742752
texts.push(text('?='));
753+
} else if (node.assertionType === AssertLookbehind) {
754+
texts.push(text('?<='));
755+
} else if (node.assertionType === AssertNegativeLookbehind) {
756+
texts.push(text('?<!'));
743757
} else {
744758
texts.push(text('?!'));
745759
}

tests/testData.js

+50
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,56 @@ var re2ast =[{ raw: '\\\\{3}',
20092009
chars: 'b',
20102010
raw: 'b'
20112011
}],
2012+
}, {
2013+
raw: '(?<!foo)bar.*(?<=foo)bar',
2014+
groupCount: 0,
2015+
tree: [{
2016+
type: 'assert',
2017+
num: undefined,
2018+
sub: [{
2019+
type: 'exact',
2020+
indices: [4, 7],
2021+
chars: 'foo',
2022+
raw: 'foo'
2023+
}],
2024+
indices: [0, 8],
2025+
assertionType: 'AssertNegativeLookbehind',
2026+
endParenIndex: 7,
2027+
raw: '(?<!foo)'
2028+
}, {
2029+
type: 'exact',
2030+
indices: [8, 11],
2031+
chars: 'bar',
2032+
raw: 'bar',
2033+
}, {
2034+
type: 'dot',
2035+
indices: [11, 13],
2036+
repeat: {
2037+
min: 0,
2038+
max: Infinity,
2039+
nonGreedy: false,
2040+
possessive: false
2041+
},
2042+
raw: '.*'
2043+
}, {
2044+
type: 'assert',
2045+
num: undefined,
2046+
sub: [{
2047+
type: 'exact',
2048+
indices: [17, 20],
2049+
chars: 'foo',
2050+
raw: 'foo'
2051+
}],
2052+
indices: [13, 21],
2053+
assertionType: 'AssertLookbehind',
2054+
endParenIndex: 20,
2055+
raw: '(?<=foo)'
2056+
}, {
2057+
type: 'exact',
2058+
indices: [21, 24],
2059+
chars: 'bar',
2060+
raw: 'bar'
2061+
}]
20122062
}];
20132063

20142064

0 commit comments

Comments
 (0)