1 ## oils_failures_allowed: 0
2
3 #### s ~ regex and s !~ regex
4 shopt -s ysh:upgrade
5
6 var s = 'foo'
7 if (s ~ '.([[:alpha:]]+)') { # ERE syntax
8 echo matches
9 argv.py $[_group(0)] $[_group(1)]
10 }
11 if (s !~ '[[:digit:]]+') {
12 echo "does not match"
13 argv.py $[_group(0)] $[_group(1)]
14 }
15
16 if (s ~ '[[:digit:]]+') {
17 echo "matches"
18 }
19 # Should be cleared now
20 # should this be Undef rather than ''?
21 try {
22 var x = _group(0)
23 }
24 if (_status === 3) {
25 echo 'got expected status 3'
26 }
27
28 try {
29 var y = _group(1)
30 }
31 if (_status === 3) {
32 echo 'got expected status 3'
33 }
34
35 ## STDOUT:
36 matches
37 ['foo', 'oo']
38 does not match
39 ['foo', 'oo']
40 got expected status 3
41 got expected status 3
42 ## END
43
44 #### Invalid regex has libc error message
45
46 shopt -s ysh:upgrade
47
48 # Hm it's hard to test this, we can't get stderr of YSH from within YSH?
49 #redir 2>err.txt {
50 # if ('abc' ~ '+') {
51 # echo 'bad'
52 # }
53 #}
54
55 if ('abc' ~ '+') {
56 echo 'bad'
57 }
58
59 ## status: 2
60 ## STDOUT:
61 ## END
62
63 #### Eggex flags to ignore case are respected
64 shopt -s ysh:upgrade
65
66 # based on Python's spelling
67 var pat = / 'abc' ; i /
68 var pat2 = / @pat 'def' ; reg_icase / # this is allowed
69
70 if ('-abcdef-' ~ pat2) {
71 echo 'yes'
72 }
73
74 if ('-ABCDEF-' ~ pat2) {
75 echo 'yes'
76 }
77
78 if ('ABCDE' ~ pat2) {
79 echo 'BUG'
80 }
81
82 ## STDOUT:
83 yes
84 yes
85 ## END
86
87 #### Eggex flags to treat newlines as special are respected
88 shopt -s ysh:upgrade
89
90 if (u'abc123\n' ~ / digit %end /) {
91 echo 'BUG'
92 }
93 if (u'abc\n123' ~ / %start digit /) {
94 echo 'BUG'
95 }
96
97 if (u'abc123\n' ~ / digit %end ; reg_newline /) {
98 echo 'yes'
99 }
100 if (u'abc\n123' ~ / %start digit ; reg_newline /) {
101 echo 'yes'
102 }
103
104 if (u'\n' ~ / . /) {
105 echo 'yes'
106 }
107 if (u'\n' ~ / !digit /) {
108 echo 'yes'
109 }
110
111 if (u'\n' ~ / . ; reg_newline /) {
112 echo 'BUG'
113 }
114 if (u'\n' ~ / !digit ; reg_newline /) {
115 echo 'BUG'
116 }
117
118 ## STDOUT:
119 yes
120 yes
121 yes
122 yes
123 ## END
124
125 #### Positional captures with _group
126 shopt -s ysh:upgrade
127
128 var x = 'zz 2020-08-20'
129
130 if [[ $x =~ ([[:digit:]]+)-([[:digit:]]+) ]] {
131 argv.py "${BASH_REMATCH[@]}"
132 }
133
134 # THIS IS A NO-OP. The variable is SHADOWED by the special name.
135 # I think that's OK.
136 setvar BASH_REMATCH = :| reset |
137
138 if (x ~ /<capture d+> '-' <capture d+>/) {
139 argv.py "${BASH_REMATCH[@]}"
140 argv.py $[_group(0)] $[_group(1)] $[_group(2)]
141
142 # TODO: Also test _start() and _end()
143 }
144 ## STDOUT:
145 ['2020-08', '2020', '08']
146 ['2020-08', '2020', '08']
147 ['2020-08', '2020', '08']
148 ## END
149
150 #### _group() returns null when group doesn't match
151 shopt -s ysh:upgrade
152
153 var pat = / <capture 'a'> | <capture 'b'> /
154 if ('b' ~ pat) {
155 echo "$[_group(1)] $[_group(2)]"
156 }
157 ## STDOUT:
158 null b
159 ## END
160
161 #### _start() and _end()
162 shopt -s ysh:upgrade
163
164 var s = 'foo123bar'
165 if (s ~ /digit+/) {
166 echo start=$[_start(0)] end=$[_end(0)]
167 }
168 echo ---
169
170 if (s ~ / <capture [a-z]+> <capture digit+> /) {
171 echo start=$[_start(1)] end=$[_end(1)]
172 echo start=$[_start(2)] end=$[_end(2)]
173 }
174 echo ---
175
176 if (s ~ / <capture [a-z]+> | <capture digit+> /) {
177 echo start=$[_start(1)] end=$[_end(1)]
178 echo start=$[_start(2)] end=$[_end(2)]
179 }
180
181 ## STDOUT:
182 start=3 end=6
183 ---
184 start=0 end=3
185 start=3 end=6
186 ---
187 start=0 end=3
188 start=-1 end=-1
189 ## END
190
191 #### Str->search() method returns value.Match object
192
193 var s = '= Hi5- Bye6-'
194
195 var m = s.search(/ <capture [a-z]+ > <capture d+> '-' ; i /)
196 echo "g0 $[m.start(0)] $[m.end(0)] $[m.group(0)]"
197 echo "g1 $[m.start(1)] $[m.end(1)] $[m.group(1)]"
198 echo "g2 $[m.start(2)] $[m.end(2)] $[m.group(2)]"
199
200 echo ---
201
202 var pos = m.end(0) # search from end position
203 var m = s.search(/ <capture [a-z]+ > <capture d+> '-' ; i /, pos=pos)
204 echo "g0 $[m.start(0)] $[m.end(0)] $[m.group(0)]"
205 echo "g1 $[m.start(1)] $[m.end(1)] $[m.group(1)]"
206 echo "g2 $[m.start(2)] $[m.end(2)] $[m.group(2)]"
207
208 ## STDOUT:
209 g0 2 6 Hi5-
210 g1 2 4 Hi
211 g2 4 5 5
212 ---
213 g0 7 12 Bye6-
214 g1 7 10 Bye
215 g2 10 11 6
216 ## END
217
218 #### Str->search() only matches %start ^ when pos == 0
219
220 shopt -s ysh:upgrade
221
222 var anchored = / %start <capture d+> '-' /
223 var free = / <capture d+> '-' /
224
225 var s = '12-34-'
226
227 for pat in ([anchored, free]) {
228 echo "pat=$pat"
229
230 var pos = 0
231 while (true) {
232 var m = s.search(pat, pos=pos)
233 if (not m) {
234 break
235 }
236 echo $[m.group(0)]
237 setvar pos = m.end(0)
238 }
239
240 }
241
242 ## STDOUT:
243 pat=^([[:digit:]]+)-
244 12-
245 pat=([[:digit:]]+)-
246 12-
247 34-
248 ## END
249
250
251 #### search() and leftMatch() accept ERE string
252
253 var s = '= hi5- bye6-'
254
255 var m = s.search('([[:alpha:]]+)([[:digit:]]+)-')
256 echo "g0 $[m.start(0)] $[m.end(0)] $[m.group(0)]"
257 echo "g1 $[m.start(1)] $[m.end(1)] $[m.group(1)]"
258 echo "g2 $[m.start(2)] $[m.end(2)] $[m.group(2)]"
259 echo ---
260
261 var m = s[2:].leftMatch('([[:alpha:]]+)([[:digit:]]+)-')
262 echo "g0 $[m.start(0)] $[m.end(0)] $[m.group(0)]"
263 echo "g1 $[m.start(1)] $[m.end(1)] $[m.group(1)]"
264 echo "g2 $[m.start(2)] $[m.end(2)] $[m.group(2)]"
265
266 ## STDOUT:
267 g0 2 6 hi5-
268 g1 2 4 hi
269 g2 4 5 5
270 ---
271 g0 0 4 hi5-
272 g1 0 2 hi
273 g2 2 3 5
274 ## END
275
276 #### Str.leftMatch() can implement lexer pattern
277
278 shopt -s ysh:upgrade
279
280 var lexer = / <capture d+> | <capture [a-z]+> | <capture s+> /
281 #echo $lexer
282
283 proc show-tokens (s) {
284 var pos = 0
285
286 while (true) {
287 echo "pos=$pos"
288
289 var m = s.leftMatch(lexer, pos=pos)
290 if (not m) {
291 break
292 }
293 # TODO: add groups()
294 #var groups = [m.group(1), m.group(2), m.group(3)]
295 echo "$[m.group(1)]/$[m.group(2)]/$[m.group(3)]/"
296
297 echo
298
299 setvar pos = m.end(0)
300 }
301 }
302
303 show-tokens 'ab 12'
304
305 echo '==='
306
307 # There's a token here that doesn't leftMatch()
308 show-tokens 'ab+12'
309
310 ## STDOUT:
311 pos=0
312 null/ab/null/
313
314 pos=2
315 null/null/ /
316
317 pos=3
318 12/null/null/
319
320 pos=5
321 ===
322 pos=0
323 null/ab/null/
324
325 pos=2
326 ## END
327
328 #### Named captures with m.group()
329 shopt -s ysh:all
330
331 var s = 'zz 2020-08-20'
332 var pat = /<capture d+ as year> '-' <capture d+ as month>/
333
334 var m = s.search(pat)
335 argv.py $[m.group('year')] $[m.group('month')]
336 echo $[m.start('year')] $[m.end('year')]
337 echo $[m.start('month')] $[m.end('month')]
338
339 argv.py $[m.group('oops')]
340 echo 'error'
341
342 ## status: 3
343 ## STDOUT:
344 ['2020', '08']
345 3 7
346 8 10
347 ## END
348
349 #### Named captures with _group() _start() _end()
350 shopt -s ysh:all
351
352 var x = 'zz 2020-08-20'
353
354 if (x ~ /<capture d+ as year> '-' <capture d+ as month>/) {
355 argv.py $[_group('year')] $[_group('month')]
356 echo $[_start('year')] $[_end('year')]
357 echo $[_start('month')] $[_end('month')]
358 }
359
360 argv.py $[_group('oops')]
361
362 ## status: 3
363 ## STDOUT:
364 ['2020', '08']
365 3 7
366 8 10
367 ## END
368
369 #### Named Capture Decays Without Name
370 shopt -s ysh:all
371 var pat = /<capture d+ as month>/
372 echo $pat
373
374 if ('123' ~ pat) {
375 echo yes
376 }
377
378 ## STDOUT:
379 ([[:digit:]]+)
380 yes
381 ## END
382
383 #### Nested Named Capture Uses ( ordering
384
385 shopt -s ysh:upgrade
386
387 var Date = /<capture d+ as year> '-' <capture d+ as month>/
388 var Time = /<capture d+ as hour> ':' <capture d+ as minute> (':' <capture d+ as secs>)? /
389
390 var pat = / 'when: ' (<capture Date> | <capture Time as two>) /
391 #echo $pat
392
393 proc show-groups (; m) {
394 echo 0 $[m.group(0)]
395 echo 1 $[m.group(1)] # this is everything except when
396 echo 2 $[m.group(2)]
397 echo
398 echo $[m.group('two')]
399 echo $[m.group('year')] $[m.group('month')]
400 echo $[m.group('hour')] $[m.group('minute')] $[m.group('secs')]
401 }
402
403 var m = 'when: 2023-10'.leftMatch(pat)
404
405 show-groups (m)
406
407 var m = 'when: 23:30'.leftMatch(pat)
408
409 echo ---
410 show-groups (m)
411
412 var m = 'when: 23:30:59'.leftMatch(pat)
413
414 echo ---
415 show-groups (m)
416
417 ## STDOUT:
418 0 when: 2023-10
419 1 2023-10
420 2 2023-10
421
422 null
423 2023 10
424 null null null
425 ---
426 0 when: 23:30
427 1 23:30
428 2 null
429
430 23:30
431 null null
432 23 30 null
433 ---
434 0 when: 23:30:59
435 1 23:30:59
436 2 null
437
438 23:30:59
439 null null
440 23 30 59
441 ## END
442
443 #### Capture with Type Conversion Func
444 shopt -s ysh:upgrade
445
446 var s = 'hi 42-3.14'
447 var pat = / <capture d+: int> '-' <capture d+ '.' d+ : float> /
448
449 if (s ~ pat) {
450 var g1 = _group(1) # Int
451 var g2 = _group(2) # Float
452 echo $[type(g1)] $[type(g2)]
453 }
454
455 var m = s.search(pat)
456 if (m) {
457 echo $[m.group(1) => type()] $[m.group(2) => type()]
458 }
459
460 ## STDOUT:
461 Int Float
462 Int Float
463 ## END
464
465
466 #### Named Capture with Type Conversion Func
467 shopt -s ysh:upgrade
468
469 func floatNegate(x) {
470 return (-float(x))
471 }
472
473 var s = 'hi 42-3.14'
474 var pat = / <capture d+ as left: int> '-' <capture d+ '.' d+ as right: floatNegate> /
475
476 if (s ~ pat) {
477 var g1 = _group('left') # Int
478 var g2 = _group('right') # Float
479 echo $g2
480 echo $[type(g1)] $[type(g2)]
481 }
482
483 var m = s.search(pat)
484 if (m) {
485 echo $[m.group('right')]
486 echo $[m.group('left') => type()] $[m.group('right') => type()]
487 }
488
489 ## STDOUT:
490 -3.14
491 Int Float
492 -3.14
493 Int Float
494 ## END
495
496 #### Can't splice eggex with different flags
497 shopt -s ysh:upgrade
498
499 var pat = / 'abc' ; i /
500 var pat2 = / @pat 'def' ; reg_icase / # this is allowed
501
502 var pat3 = / @pat 'def' /
503 = pat3
504
505 ## status: 1
506 ## STDOUT:
507 ## END
508
509 #### Eggex with translation preference has arbitrary flags
510 shopt -s ysh:upgrade
511
512 # TODO: can provide introspection so users can translate it?
513 # This is kind of a speculative corner of the language.
514
515 var pat = / d+ ; ignorecase ; PCRE /
516
517 # This uses ERE, as a test
518 if ('ab 12' ~ pat) {
519 echo yes
520 }
521
522 ## STDOUT:
523 yes
524 ## END
525
526
527 #### Invalid sh operation on eggex
528 var pat = / d+ /
529 #pat[invalid]=1
530 pat[invalid]+=1
531 ## status: 1
532 ## stdout-json: ""
533
534 #### Long Python Example
535
536 # https://docs.python.org/3/reference/lexical_analysis.html#integer-literals
537
538 # integer ::= decinteger | bininteger | octinteger | hexinteger
539 # decinteger ::= nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")*
540 # bininteger ::= "0" ("b" | "B") (["_"] bindigit)+
541 # octinteger ::= "0" ("o" | "O") (["_"] octdigit)+
542 # hexinteger ::= "0" ("x" | "X") (["_"] hexdigit)+
543 # nonzerodigit ::= "1"..."9"
544 # digit ::= "0"..."9"
545 # bindigit ::= "0" | "1"
546 # octdigit ::= "0"..."7"
547 # hexdigit ::= digit | "a"..."f" | "A"..."F"
548
549 shopt -s ysh:all
550
551 const DecDigit = / [0-9] /
552 const BinDigit = / [0-1] /
553 const OctDigit = / [0-7] /
554 const HexDigit = / [0-9 a-f A-F] / # note: not splicing Digit into character class
555
556 const DecInt = / [1-9] ('_'? DecDigit)* | '0'+ ('_'? '0')* /
557 const BinInt = / '0' [b B] ('_'? BinDigit)+ /
558 const OctInt = / '0' [o O] ('_'? OctDigit)+ /
559 const HexInt = / '0' [x X] ('_'? HexDigit)+ /
560
561 const Integer = / %start (DecInt | BinInt | OctInt | HexInt) %end /
562
563 #echo $Integer
564
565 if ( '123' ~ Integer) { echo 'Y' }
566 if ( 'zzz' !~ Integer) { echo 'N' }
567
568 if ('123_000' ~ Integer) { echo 'Y decimal' }
569 if ('000_123' !~ Integer) { echo 'N decimal' }
570
571 if ( '0b100' ~ Integer) { echo 'Y binary' }
572 if ( '0b102' !~ Integer) { echo 'N binary' }
573
574 if ( '0o755' ~ Integer) { echo 'Y octal' }
575 if ( '0o778' !~ Integer) { echo 'N octal' }
576
577 if ( '0xFF' ~ Integer) { echo 'Y hex' }
578 if ( '0xFG' !~ Integer) { echo 'N hex' }
579
580 ## STDOUT:
581 Y
582 N
583 Y decimal
584 N decimal
585 Y binary
586 N binary
587 Y octal
588 N octal
589 Y hex
590 N hex
591 ## END
592
593 #### Regex in a loop (bug regression)
594
595 shopt --set ysh:all
596
597 var content = [ 1, 2 ]
598 var i = 0
599 while (i < len(content)) {
600 var line = content[i]
601 write $[content[i]]
602 if (str(line) ~ / s* 'imports' s* '=' s* .* /) {
603 exit
604 }
605 setvar i += 1
606 }
607
608 ## STDOUT:
609 1
610 2
611 ## END
612
613
614 #### Regex in a loop depending on var
615
616 shopt --set ysh:all
617
618 var lines = ['foo', 'bar']
619 for line in (lines) {
620 write "line $line"
621
622 # = / $line /
623
624 if ("x$line" ~ / dot @line /) {
625 #if (line ~ / $line /) {
626 write "matched $line"
627 }
628 }
629
630 ## STDOUT:
631 line foo
632 matched foo
633 line bar
634 matched bar
635 ## END
636
637
638 #### Regex with [ (bug regression)
639 shopt --set ysh:all
640
641 if ('[' ~ / '[' /) {
642 echo 'sq'
643 }
644
645 if ('[' ~ / [ '[' ] /) {
646 echo 'char class'
647 }
648
649 # User-reported string
650 if ("a" ~ / s* 'imports' s* '=' s* '[' /) {
651 echo "yes"
652 }
653
654 ## STDOUT:
655 sq
656 char class
657 ## END
658
659 #### Str.replace(Str, Str)
660 shopt --set ysh:all
661
662 var mystr = 'abca'
663 write $[mystr.replace('a', 'A')] # Two matches
664 write $[mystr.replace('b', 'B')] # One match
665 write $[mystr.replace('x', 'y')] # No matches
666
667 write $[mystr.replace('abc', '')] # Empty substitution
668 write $[mystr.replace('', 'new')] # Empty substring
669 ## STDOUT:
670 AbcA
671 aBca
672 abca
673 a
674 newanewbnewcnewanew
675 ## END
676
677 #### Str.replace(Eggex, Str)
678 shopt --set ysh:all
679
680 var mystr = 'mangled----kebab--case'
681 write $[mystr.replace(/ '-'+ /, '-')]
682
683 setvar mystr = 'smaller-to-bigger'
684 write $[mystr.replace(/ '-'+ /, '---')]
685 ## STDOUT:
686 mangled-kebab-case
687 smaller---to---bigger
688 ## END
689
690 #### Str.replace(Eggex, Expr)
691 shopt --set ysh:all
692
693 var mystr = 'name: Bob'
694 write $[mystr.replace(/ 'name: ' <capture dot+> /, ^"Hello $1")]
695 write $[mystr.replace(/ 'name: ' <capture dot+> /, ^"Hello $1 (extracted from '$0')")]
696 ## STDOUT:
697 Hello Bob
698 Hello Bob (extracted from 'name: Bob')
699 ## END
700
701 #### Str.replace(*, Expr), $0
702 shopt --set ysh:all
703
704 # Functionality
705 var mystr = 'class Foo: # this class is called Foo'
706 write $[mystr.replace("Foo", ^"$0Bar")]
707 write $[mystr.replace(/ 'Foo' /, ^"$0Bar")]
708
709 # Edge-cases
710 var dollar0 = "$0"
711 #echo dollar0=$dollar0
712 #echo "0 = $0"
713
714 var expected = "f($dollar0)($dollar0)"
715 #echo "expected = $expected"
716
717 # Eager replacement
718 assert [expected === "foo".replace("o", "($0)")]
719
720 assert ['f(o)(o)' === "foo".replace("o", ^"($0)")]
721
722 func f() { return ( "<$0>" ) }
723 assert ["<$dollar0>" === f()]
724
725 assert ['f<o><o>' === "foo".replace("o", ^[f()])]
726
727 ## STDOUT:
728 class FooBar: # this class is called FooBar
729 class FooBar: # this class is called FooBar
730 ## END
731
732 #### Str.replace(Eggex, Expr), scopes
733 shopt --set ysh:all
734
735 var mystr = '123'
736
737 var anotherVar = 'surprise!'
738 write $[mystr.replace(/ <capture d+> /, ^"Hello $1 ($anotherVar)")]
739
740 var globalName = '456'
741 write $[mystr.replace(/ <capture d+ as globalName> /, ^"Hello $globalName")]
742
743 write $[mystr.replace(/ <capture d+ as localName> /, ^"Hello $localName, $globalName")]
744 ## STDOUT:
745 Hello 123 (surprise!)
746 Hello 123
747 Hello 123, 456
748 ## END
749
750 #### Str.replace(Eggex, *, count)
751 shopt --set ysh:all
752
753 var mystr = '1abc2abc3abc'
754
755 for count in (-2..4) {
756 write $[mystr.replace('abc', "-", count=count)]
757 write $[mystr.replace('abc', ^"-", count=count)]
758 write $[mystr.replace(/ [a-z]+ /, "-", count=count)]
759 write $[mystr.replace(/ [a-z]+ /, "-", count=count)]
760 }
761 ## STDOUT:
762 1-2-3-
763 1-2-3-
764 1-2-3-
765 1-2-3-
766 1-2-3-
767 1-2-3-
768 1-2-3-
769 1-2-3-
770 1abc2abc3abc
771 1abc2abc3abc
772 1abc2abc3abc
773 1abc2abc3abc
774 1-2abc3abc
775 1-2abc3abc
776 1-2abc3abc
777 1-2abc3abc
778 1-2-3abc
779 1-2-3abc
780 1-2-3abc
781 1-2-3abc
782 1-2-3-
783 1-2-3-
784 1-2-3-
785 1-2-3-
786 ## END
787
788 #### Str.replace(Str, Str), empty new/old strings
789 var mystr = 'abca'
790 write $[mystr.replace('abc', '')] # Empty substitution
791 write $[mystr.replace('', 'new')] # Empty substring
792 write $[mystr.replace('', 'new', count=1)] # Empty substring, count != -1
793 write $[mystr.replace('', 'new', count=10)] # Empty substring, count too large
794 ## STDOUT:
795 a
796 newanewbnewcnewanew
797 newabca
798 newanewbnewcnewanew
799 ## END
800
801 #### Str.replace(Eggex, Lazy), convert_func
802 shopt --set ysh:all
803
804 var mystr = '123'
805
806 write $[mystr.replace(/ <capture d+ as n : int> /, ^"$[n + 1]")]
807
808 # values automatically get stringified
809 write $[mystr.replace(/ <capture d+ as n : int> /, ^"$1")]
810
811 func not_str(inp) {
812 return ({ "value": inp })
813 }
814
815 # should fail to stringify $1
816 try { call mystr.replace(/ <capture d+ : not_str> /, ^"$1") }
817 write status=$_status
818 ## STDOUT:
819 124
820 123
821 status=3
822 ## END
823
824 #### Str.replace(Eggex, *), eflags
825 shopt --set ysh:all
826
827 var mystr = $'1-2-3\n4-5'
828 write $[mystr.replace(/ d+ /, ^"[$0]")]
829 write $[mystr.replace(/ ^ d+ /, ^"[$0]")]
830 write $[mystr.replace(/ ^ d+ ; reg_newline /, ^"[$0]")]
831 ## STDOUT:
832 [1]-[2]-[3]
833 [4]-[5]
834 [1]-2-3
835 4-5
836 [1]-2-3
837 [4]-5
838 ## END
839
840 #### Str.replace(Eggex, *), guard against infinite loop
841 shopt --set ysh:all
842
843 var mystr = 'foo bar baz'
844 write $[mystr.replace(/ space* /, ' ')]
845 ## status: 3
846 ## STDOUT:
847 ## END
848
849 #### Str.replace(Eggex, *), str cannot contain NUL bytes
850 shopt --set ysh:all
851
852 var mystr = b'foo bar baz\y00'
853 write $[mystr.replace(/ space+ /, ' ')]
854 ## status: 3
855 ## STDOUT:
856 ## END
857
858 #### Str.replace() at top level
859 shopt --set ysh:upgrade
860
861 var s = 'mystr'
862 var pat = / 's' <capture dot> /
863 var template = ^"[$x $0 $1 $x]"
864 pp test_ (template)
865
866 var x = 'x'
867
868 var new = s.replace(pat, template)
869 echo 'replace ' $new
870
871 func myreplace(s, template) {
872 return (s.replace(pat, template))
873 }
874
875 echo myreplace $[myreplace(s, template)]
876
877 ## STDOUT:
878 <Expr>
879 replace my[x st t x]r
880 myreplace my[x st t x]r
881 ## END
882
883 #### Str.replace() lexical scope with ^""
884 shopt --set ysh:upgrade
885
886 var s = 'mystr'
887 var pat = / 's' <capture dot> /
888
889 proc p {
890 var x = 'x'
891 var template = ^"[$x $0 $1 $x]"
892 pp test_ (template)
893
894 var new = s.replace(pat, template)
895 echo 'replace ' $new
896
897 func myreplace(s, template) {
898 return (s.replace(pat, template))
899 }
900
901 echo myreplace $[myreplace(s, template)]
902 }
903
904 p
905
906 ## STDOUT:
907 <Expr>
908 replace my[x st t x]r
909 myreplace my[x st t x]r
910 ## END
911
912 #### Str.replace() lexical scope with ^[]
913 shopt --set ysh:upgrade
914
915 var s = 'mystr'
916 var pat = / 's' <capture dot> /
917
918 proc p {
919 var x = 'x'
920 var template = ^['[' ++ x ++ ' ' ++ $0 ++ ' ' ++ $1 ++ ' ' ++ x ++ ']']
921 pp test_ (template)
922
923 var new = s.replace(pat, template)
924 echo 'replace ' $new
925
926 func myreplace(s, template) {
927 return (s.replace(pat, template))
928 }
929
930 echo myreplace $[myreplace(s, template)]
931 }
932
933 p
934
935 ## STDOUT:
936 <Expr>
937 replace my[x st t x]r
938 myreplace my[x st t x]r
939 ## END