Skip to content

Commit

Permalink
Add support for regex replacement backreferences
Browse files Browse the repository at this point in the history
  • Loading branch information
radiac committed Sep 14, 2019
1 parent 8f1a0f4 commit 2767ea5
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 17 deletions.
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ Examples::
# Case insensitive global replacement
value =~ s/foo/bar/gi

# Backreferences
value =~ s/(.+?) (?<name>.+?)/$1 $name/


Dollar variables
----------------
Expand Down
10 changes: 7 additions & 3 deletions perl/translator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import io
import re
import tokenize
from enum import Enum

Expand Down Expand Up @@ -128,10 +129,10 @@ def translate(self, readline):
continue

#
# Convert $1 (as long as we're not collecting)
# Convert $1 (as long as we're not in a regex)
#

if self.collecting_match != CollectState.ACTIVE:
if self.collecting_match == CollectState.WAITING:
if tok.type == tokenize.ERRORTOKEN and tok.string == "$":
self.dollar = True
continue
Expand Down Expand Up @@ -327,7 +328,10 @@ def render(self):
]

else:
# Build replace and covert any backrefs
replace = "".join(self.replace)
replace = re.sub(r"\$(\w+)", r"\\g\g<1>", replace)

if self.is_global:
# By default the count is unlimited
count = ""
Expand All @@ -338,7 +342,7 @@ def render(self):
# Regex needs to reset the vars first in case it's a None
python = [
f"{whitespace}{variable} = __perl__reset_vars() or ",
f"re.sub(r'{match}', '{replace}', {variable}",
f"re.sub(r'{match}', r'{replace}', {variable}",
f"{count}{flags})",
]

Expand Down
Empty file.
42 changes: 28 additions & 14 deletions tests/test_translator.py → tests/test_translator/match.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,6 @@ def test_translate__match_all():
)


def test_translate__replace():
assert (
translate_string("var =~ s/foo/bar/")
== "var = __perl__reset_vars() or re.sub(r'foo', 'bar', var, count=1)"
)


def test_translate__replace_all():
assert (
translate_string("var =~ s/foo/bar/g")
== "var = __perl__reset_vars() or re.sub(r'foo', 'bar', var)"
)


def test_translate__escaped():
assert (
translate_string(r"var =~ /foo\/bar/")
Expand Down Expand Up @@ -74,3 +60,31 @@ def test_translate__match_assign():
translate_string(r"match = var =~ /foo/")
== "match = __perl__re_match(re.search(r'foo', var))"
)


def test_translate__replace():
assert (
translate_string("var =~ s/foo/bar/")
== "var = __perl__reset_vars() or re.sub(r'foo', r'bar', var, count=1)"
)


def test_translate__replace_with_backref():
assert (
translate_string("var =~ s/^foo (.+?) bar/foo $1 bar/")
== "var = __perl__reset_vars() or re.sub(r'^foo (.+?)', r'foo \\g<1> bar')"
)


def test_translate__replace_with_named_backref():
assert translate_string("var =~ s/^foo (?P<named>.+?) bar/foo $named bar/") == (
"var = __perl__reset_vars() or "
"re.sub(r'^foo (?P<named>.+?) bar', r'foo \\g<named> bar')"
)


def test_translate__replace_all():
assert (
translate_string("var =~ s/foo/bar/g")
== "var = __perl__reset_vars() or re.sub(r'foo', r'bar', var)"
)
29 changes: 29 additions & 0 deletions tests/test_translator/replace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from perl.translator import translate_string


def test_translate__replace():
assert (
translate_string("var =~ s/foo/bar/")
== "var = __perl__reset_vars() or re.sub(r'foo', r'bar', var, count=1)"
)


def test_translate__replace_with_backref():
assert (
translate_string("var =~ s/^foo (.+?) bar/foo $1 bar/")
== "var = __perl__reset_vars() or re.sub(r'^foo (.+?)', r'foo \\g<1> bar')"
)


def test_translate__replace_with_named_backref():
assert translate_string("var =~ s/^foo (?P<named>.+?) bar/foo $named bar/") == (
"var = __perl__reset_vars() or "
"re.sub(r'^foo (?P<named>.+?) bar', r'foo \\g<named> bar')"
)


def test_translate__replace_all():
assert (
translate_string("var =~ s/foo/bar/g")
== "var = __perl__reset_vars() or re.sub(r'foo', r'bar', var)"
)

0 comments on commit 2767ea5

Please sign in to comment.