@@ -26,7 +26,7 @@ Matchy.VERSION = "3.0.0";
2626Matchy . prototype = {
2727 constructor : Matchy ,
2828
29- fsa : function ( pattern , string , offset ) {
29+ fsa : function ( pattern , string , offset , return_match ) {
3030 // https://en.wikipedia.org/wiki/Finite-state_machine
3131 // https://en.wikipedia.org/wiki/String-searching_algorithm
3232 // https://euroinformatica.ro/documentation/programming/!!!Algorithms_CORMEN!!!/DDU0214.html
@@ -59,7 +59,7 @@ Matchy.prototype = {
5959 } ;
6060
6161 // matcher
62- var matcher = function ( string , offset ) {
62+ var matcher = function ( string , offset , return_match ) {
6363 var n = string . length ;
6464 if ( null == offset ) offset = 0 ;
6565 if ( 0 > offset ) offset += n ;
@@ -69,23 +69,23 @@ Matchy.prototype = {
6969 {
7070 c = string . charAt ( i ) ;
7171 q = delta ( q , c ) ;
72- if ( m === q ) return i - m + 1 ; // matched
72+ if ( m === q ) return return_match ? pattern : ( i - m + 1 ) ; // matched
7373 }
7474 }
75- return - 1 ;
75+ return return_match ? null : - 1 ;
7676 } ;
77- return null == string ? matcher : matcher ( string , offset ) ;
77+ return null == string ? matcher : matcher ( string , offset , return_match ) ;
7878 } ,
7979
80- rabinkarp : function ( pattern , string , offset ) {
80+ rabinkarp : function ( pattern , string , offset , return_match ) {
8181 // https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
8282 // http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=BA184C94C16CB23D5FA7329E257E3713?doi=10.1.1.86.9502&rep=rep1&type=pdf
8383
8484 var m = pattern . length ;
8585 var Q = 3989 ; // prime number so that 10*Q can fit in one WORD (i.e 2^16 bits)
8686
8787 // matcher
88- var matcher = function ( string , offset ) {
88+ var matcher = function ( string , offset , return_match ) {
8989 var n = string . length ;
9090 if ( null == offset ) offset = 0 ;
9191 if ( 0 > offset ) offset += n ;
@@ -121,7 +121,7 @@ Matchy.prototype = {
121121 // worst case: many hash collisions -> "naive" matching
122122 if ( pq === sq )
123123 {
124- if ( string . slice ( i , i + m ) === pattern ) return i ; // matched
124+ if ( string . slice ( i , i + m ) === pattern ) return return_match ? pattern : i ; // matched
125125 }
126126 // update text hash for next char using Horner algorithm
127127 if ( i < n )
@@ -131,12 +131,12 @@ Matchy.prototype = {
131131 }
132132 }
133133 }
134- return - 1 ;
134+ return return_match ? null : - 1 ;
135135 } ;
136- return null == string ? matcher : matcher ( string , offset ) ;
136+ return null == string ? matcher : matcher ( string , offset , return_match ) ;
137137 } ,
138138
139- knuthmorrispratt : function ( pattern , string , offset ) {
139+ knuthmorrispratt : function ( pattern , string , offset , return_match ) {
140140 // https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
141141 // http://www.eecs.ucf.edu/~shzhang/Combio09/kmp.pdf
142142
@@ -163,7 +163,7 @@ Matchy.prototype = {
163163 }
164164
165165 // matcher
166- var matcher = function ( string , offset ) {
166+ var matcher = function ( string , offset , return_match ) {
167167 var n = string . length ;
168168 if ( null == offset ) offset = 0 ;
169169 if ( 0 > offset ) offset += n ;
@@ -176,7 +176,7 @@ Matchy.prototype = {
176176 {
177177 ++ i ;
178178 ++ k ;
179- if ( k === m ) return i - k ; // matched
179+ if ( k === m ) return return_match ? pattern : ( i - k ) ; // matched
180180 }
181181 else
182182 {
@@ -189,12 +189,12 @@ Matchy.prototype = {
189189 }
190190 }
191191 }
192- return - 1 ;
192+ return return_match ? null : - 1 ;
193193 } ;
194- return null == string ? matcher : matcher ( string , offset ) ;
194+ return null == string ? matcher : matcher ( string , offset , return_match ) ;
195195 } ,
196196
197- boyermoore : function ( pattern , string , offset ) {
197+ boyermoore : function ( pattern , string , offset , return_match ) {
198198 // https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm
199199 // http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf
200200
@@ -228,7 +228,7 @@ Matchy.prototype = {
228228 }
229229
230230 // matcher
231- var matcher = function ( string , offset ) {
231+ var matcher = function ( string , offset , return_match ) {
232232 var n = string . length ;
233233 if ( null == offset ) offset = 0 ;
234234 if ( 0 > offset ) offset += n ;
@@ -244,20 +244,20 @@ Matchy.prototype = {
244244 }
245245 if ( 0 > j )
246246 {
247- return i ; // matched
247+ return return_match ? pattern : i ; // matched
248248 }
249249 else
250250 {
251251 i += shift [ j + 1 ] ;
252252 }
253253 }
254254 }
255- return - 1 ;
255+ return return_match ? null : - 1 ;
256256 } ;
257- return null == string ? matcher : matcher ( string , offset ) ;
257+ return null == string ? matcher : matcher ( string , offset , return_match ) ;
258258 } ,
259259
260- twoway : function ( pattern , string , offset ) {
260+ twoway : function ( pattern , string , offset , return_match ) {
261261 // https://en.wikipedia.org/wiki/Two-way_string-matching_algorithm
262262 // http://monge.univ-mlv.fr/~mac/Articles-PDF/CP-1991-jacm.pdf
263263
@@ -326,7 +326,7 @@ Matchy.prototype = {
326326 var small_period = ( 2 * l < m ) && ( pattern . slice ( 0 , l ) === pattern . slice ( l , l + p ) . slice ( - l ) ) ;
327327
328328 // matcher
329- var matcher = function ( string , offset ) {
329+ var matcher = function ( string , offset , return_match ) {
330330 var n = string . length ;
331331 if ( null == offset ) offset = 0 ;
332332 if ( 0 > offset ) offset += n ;
@@ -359,7 +359,7 @@ Matchy.prototype = {
359359 {
360360 -- j ;
361361 }
362- if ( j <= s ) return pos ; // matched
362+ if ( j <= s ) return return_match ? pattern : pos ; // matched
363363 pos += p ;
364364 s = m - p ;
365365 }
@@ -387,36 +387,36 @@ Matchy.prototype = {
387387 {
388388 -- j ;
389389 }
390- if ( 0 === j ) return pos ; // matched
390+ if ( 0 === j ) return return_match ? pattern : pos ; // matched
391391 pos += q ;
392392 }
393393 }
394394 }
395395 }
396- return - 1 ;
396+ return return_match ? null : - 1 ;
397397 } ;
398- return null == string ? matcher : matcher ( string , offset ) ;
398+ return null == string ? matcher : matcher ( string , offset , return_match ) ;
399399 } ,
400400
401- commentzwalter : function ( pattern , string , offset ) {
401+ commentzwalter : function ( pattern , string , offset , return_match ) {
402402 // https://en.wikipedia.org/wiki/Commentz-Walter_algorithm
403- return null == string ? non_matcher : non_matcher ( string , offset ) ; // TODO
403+ return null == string ? non_matcher : non_matcher ( string , offset , return_match ) ; // TODO
404404 } ,
405405
406- baezayatesgonnet : function ( pattern , string , offset ) {
406+ baezayatesgonnet : function ( pattern , string , offset , return_match ) {
407407 // https://en.wikipedia.org/wiki/Bitap_algorithm
408- return null == string ? non_matcher : non_matcher ( string , offset ) ; // TODO
408+ return null == string ? non_matcher : non_matcher ( string , offset , return_match ) ; // TODO
409409 } ,
410410
411- ahocorasick : function ( pattern , string , offset ) {
411+ ahocorasick : function ( pattern , string , offset , return_match ) {
412412 // https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm
413- return null == string ? non_matcher : non_matcher ( string , offset ) ; // TODO
413+ return null == string ? non_matcher : non_matcher ( string , offset , return_match ) ; // TODO
414414 }
415415} ;
416416
417- function non_matcher ( string , offset )
417+ function non_matcher ( string , offset , return_match )
418418{
419- return - 1 ;
419+ return return_match ? null : - 1 ;
420420}
421421
422422// non-deterministic finite automaton
@@ -521,7 +521,7 @@ NFA.prototype = {
521521 }
522522 if ( ',' === type )
523523 {
524- q = [ input [ 0 ] . q0 ( ) , 0 ] ;
524+ q = [ [ input [ 0 ] . q0 ( ) , 0 , 0 ] ] ;
525525 }
526526 return { q :q , e :0 } ; // keep track of errors
527527 } ,
@@ -679,34 +679,51 @@ NFA.prototype = {
679679 }
680680 if ( ',' === type )
681681 {
682- var i = q [ 1 ] , q0 , q1 , e0 = q [ 0 ] [ 'e' ] ;
683- if ( input [ i ] . accept ( q [ 0 ] ) )
684- {
685- if ( i + 1 < input . length )
682+ var n = input . length ,
683+ last_i = 0 ,
684+ qq = q ;
685+ q = [ ] ;
686+ qq . forEach ( function ( qi ) {
687+ var i = qi [ 1 ] ,
688+ e0 = qi [ 0 ] [ 'e' ] ;
689+ if ( input [ i ] . accept ( qi [ 0 ] ) )
686690 {
687- q0 = input [ i ] . d ( q [ 0 ] , c ) ;
688- q1 = input [ i + 1 ] . d ( input [ i + 1 ] . q0 ( ) , c ) ;
689- if ( ( ! input [ i ] . reject ( q0 ) ) && ( input [ i + 1 ] . reject ( q1 ) || ( q0 [ 'e' ] - e0 < q1 [ 'e' ] ) ) )
691+ if ( i + 1 < n )
690692 {
691- q = [ q0 , i ] ;
692- e += q0 [ 'e' ] - e0 ;
693+ var q0 = input [ i ] . d ( qi [ 0 ] , c ) ,
694+ q1 = input [ i + 1 ] . d ( input [ i + 1 ] . q0 ( ) , c ) ;
695+ if ( ! input [ i ] . reject ( q0 ) )
696+ {
697+ qi = [ q0 , i , qi [ 2 ] ] ;
698+ q . push ( qi ) ;
699+ }
700+ if ( ! input [ i + 1 ] . reject ( q1 ) )
701+ {
702+ ++ i ;
703+ qi = [ q1 , i , qi [ 2 ] + e0 ] ;
704+ q . push ( qi ) ;
705+ }
693706 }
694707 else
695708 {
696- q = [ q1 , i + 1 ] ;
697- e += q1 [ 'e' ] ;
709+ q . push ( qi ) ;
698710 }
699711 }
700712 else
701713 {
702- //q = q;
714+ qi = [ input [ i ] . d ( qi [ 0 ] , c ) , i , qi [ 2 ] ] ;
715+ q . push ( qi ) ;
703716 }
704- }
705- else
706- {
707- q = [ input [ i ] . d ( q [ 0 ] , c ) , i ] ;
708- e += q [ 0 ] [ 'e' ] - e0 ;
709- }
717+ if ( i > last_i )
718+ {
719+ last_i = i ;
720+ e = qi [ 0 ] [ 'e' ] + qi [ 2 ] ;
721+ }
722+ else if ( i === last_i )
723+ {
724+ e = stdMath . min ( e , qi [ 0 ] [ 'e' ] + qi [ 2 ] ) ;
725+ }
726+ } ) ;
710727 }
711728 return { q :q , e :e } ; // keep track of errors
712729 } ,
@@ -729,7 +746,7 @@ NFA.prototype = {
729746 }
730747 else
731748 {
732- return ( e <= type [ 'total_errors' ] ) && input . accept ( q ) ;
749+ return input . accept_with_errors ( q , type [ 'total_errors' ] ) ;
733750 }
734751 }
735752 if ( 'l' === type )
@@ -756,7 +773,10 @@ NFA.prototype = {
756773 }
757774 if ( ',' === type )
758775 {
759- return ( q [ 1 ] + 1 === input . length ) && input [ q [ 1 ] ] . accept ( q [ 0 ] ) ;
776+ var n = input . length ;
777+ return 0 < q . filter ( function ( qi ) {
778+ return ( qi [ 1 ] + 1 === n ) && input [ qi [ 1 ] ] . accept ( qi [ 0 ] ) ;
779+ } ) . length ;
760780 }
761781 } ,
762782
@@ -778,7 +798,7 @@ NFA.prototype = {
778798 }
779799 else
780800 {
781- return ( e > type [ 'total_errors' ] ) || input . reject ( q ) ;
801+ return input . reject_with_errors ( q , type [ 'total_errors' ] ) ;
782802 }
783803 }
784804 if ( 'l' === type )
@@ -805,7 +825,50 @@ NFA.prototype = {
805825 }
806826 if ( ',' === type )
807827 {
808- return input [ q [ 1 ] ] . reject ( q [ 0 ] ) ;
828+ return q . length === q . filter ( function ( qi ) {
829+ return input [ qi [ 1 ] ] . reject ( qi [ 0 ] ) ;
830+ } ) . length ;
831+ }
832+ } ,
833+
834+ accept_with_errors : function ( qe , max_errors ) {
835+ var self = this , type , input , q , e ;
836+ if ( ! self . accept ( qe ) ) return false ;
837+ if ( null == max_errors ) return true ;
838+ type = self . type ;
839+ input = self . input ;
840+ q = qe [ 'q' ] ;
841+ e = qe [ 'e' ] ;
842+ if ( is_obj ( type ) || ( ',' !== type ) )
843+ {
844+ return e <= max_errors ;
845+ }
846+ if ( ',' === type )
847+ {
848+ var n = input . length ;
849+ return 0 < q . filter ( function ( qi ) {
850+ return ( qi [ 1 ] + 1 === n ) && ( qi [ 0 ] [ 'e' ] + qi [ 2 ] <= max_errors ) && input [ qi [ 1 ] ] . accept ( qi [ 0 ] ) ;
851+ } ) . length ;
852+ }
853+ } ,
854+
855+ reject_with_errors : function ( qe , max_errors ) {
856+ var self = this , type , input , q , e ;
857+ if ( self . reject ( qe ) ) return true ;
858+ if ( null == max_errors ) return false ;
859+ type = self . type ;
860+ input = self . input ;
861+ q = qe [ 'q' ] ;
862+ e = qe [ 'e' ] ;
863+ if ( is_obj ( type ) || ( ',' !== type ) )
864+ {
865+ return e > max_errors ;
866+ }
867+ if ( ',' === type )
868+ {
869+ return q . length === q . filter ( function ( qi ) {
870+ return ( qi [ 0 ] [ 'e' ] + qi [ 2 ] > max_errors ) || input [ qi [ 1 ] ] . reject ( qi [ 0 ] ) ;
871+ } ) . length ;
809872 }
810873 } ,
811874
0 commit comments