252252 content : "⚠️" ;
253253 font-size : 1.2rem ;
254254 }
255+
256+ .anchor-info {
257+ background : # d1ecf1 ;
258+ border : 2px solid # bee5eb ;
259+ color : # 0c5460 ;
260+ padding : 15px ;
261+ border-radius : 8px ;
262+ margin-bottom : 20px ;
263+ font-weight : 500 ;
264+ display : flex;
265+ align-items : center;
266+ gap : 10px ;
267+ }
268+
269+ .anchor-info ::before {
270+ content : "ℹ️" ;
271+ font-size : 1.2rem ;
272+ }
273+
274+ .anchor-info code {
275+ white-space : nowrap;
276+ }
255277 </ style >
256278</ head >
257279
@@ -285,6 +307,13 @@ <h1>RegExp Equivalence Checker</h1>
285307 < button id ="check-equiv-button "> Check Equivalence</ button >
286308 </ div >
287309
310+ < div class ="anchor-info " id ="anchor-info-banner " style ="display: none; ">
311+ < b > Note, without < code > ^</ code > (start) and < code > $</ code > (end) anchors RegExp match substrings.</ b >
312+ That can lead to surprising results.
313+ For example, < code > /a+/</ code > is technically equivalent to < code > /a/</ code > because < code > /a+/</ code >
314+ is the same as < code > /^.*a+.*$/</ code > which matches exactly the same strings as < code > /^.*a.*$/</ code > (i.e. < code > /a/</ code > ).
315+ </ div >
316+
288317 < svg viewBox ="0 0 300 200 " class ="venn-diagram venn-diagram-superset " style ="display: none; ">
289318 < circle cx ="100 " cy ="100 " r ="60 " stroke ="#667eea " stroke-width ="2 " fill-opacity ="0.3 " fill ="#e9ecef "> </ circle >
290319 < circle cy ="100 " stroke ="#764ba2 " stroke-width ="2 " fill-opacity ="0.3 " fill ="#764ba2 " cx ="120 " r ="30 "> </ circle >
@@ -367,6 +396,7 @@ <h4>Powered by:</h4>
367396 const resultDiv = document . getElementById ( 'result' ) ;
368397 const counterexamples1 = document . getElementById ( 'counterexamples1' ) ;
369398 const counterexamples2 = document . getElementById ( 'counterexamples2' ) ;
399+ const anchorInfoBanner = document . getElementById ( 'anchor-info-banner' ) ;
370400
371401 function clearResults ( ) {
372402 resultDiv . style . display = 'none' ;
@@ -375,6 +405,7 @@ <h4>Powered by:</h4>
375405 counterexamples1 . innerHTML = '' ;
376406 counterexamples2 . style . display = 'none' ;
377407 counterexamples2 . innerHTML = '' ;
408+ anchorInfoBanner . style . display = 'none' ;
378409 }
379410
380411 function assertMatchRegex ( regex , strings ) {
@@ -407,6 +438,19 @@ <h4>Powered by:</h4>
407438 return ;
408439 }
409440
441+ // Check for missing anchors and show info banner
442+ const hasStartAnchor1 = pattern1 . includes ( '^' ) ;
443+ const hasEndAnchor1 = pattern1 . includes ( '$' ) ;
444+ const hasStartAnchor2 = pattern2 . includes ( '^' ) ;
445+ const hasEndAnchor2 = pattern2 . includes ( '$' ) ;
446+
447+ const shouldShowAnchorInfo = ! hasStartAnchor1 || ! hasEndAnchor1 || ! hasStartAnchor2 || ! hasEndAnchor2 ;
448+
449+ // Show/hide anchor info banner
450+ if ( shouldShowAnchorInfo ) {
451+ anchorInfoBanner . style . display = 'block' ;
452+ }
453+
410454 try {
411455 // First, validate that both patterns are valid JavaScript regex syntax
412456 const regexA = new RegExp ( pattern1 ) ;
0 commit comments