Skip to content

ure: add groups, span, start, end and sub methods - #3815

Closed
dpgeorge wants to merge 16 commits into
micropython:masterfrom
dpgeorge:extmod-ure-match-sub
Closed

dpgeorge wants to merge 16 commits into
micropython:masterfrom
dpgeorge:extmod-ure-match-sub

Conversation

@dpgeorge

Copy link
Copy Markdown
Member

Original patch provided by @dmazzella, see #3770. A few code-size reductions have been applied to that patch.

Code size increase when enable all features is roughly 3000 bytes for unix and 1200 bytes for bare-metal ports.

Comment thread extmod/modure.c Outdated
#include "re1.5/re1.5.h"

#define FLAG_DEBUG 0x1000
#define FLAG_IGNORECASE 0x0100

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmazzella it looks like IGNORECASE is not used. Do you use it in any of your scripts?

Comment thread extmod/modure.c Outdated
group = ++repl_p;

if (*group != 0 && *group == 'g') {
// search group with syntax "\g<number>"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmazzella is there a need to provide support for both "\g<number>" and "\number"? Surely only one format is needed?

Comment thread extmod/modure.c Outdated
if (n_args > 3) {
count = mp_obj_get_int(args[3]);
if (n_args > 4) {
flags = mp_obj_get_int(args[4]);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmazzella FYI I removed the keyword argument handling from this function and just made count and flags optionally positional args.

@dpgeorge

Copy link
Copy Markdown
Member Author

Thanks for the feedback @dmazzella . I updated this PR with your suggested changes.

Except, I didn't remove the \g<number> pattern. Reading the Python docs, this "g" pattern allows two things that standard \number doesn't: it allows to reference the entire group with \g<0> (\0 is not allowed); and it allows to have integers after the replacement, like \g<1>2 works but you can't do that with \12.

So "g" is more general than just a plain integer, but for most cases a plain integer is enough and you don't want tho hassle of writing out the "g" and the angle brackets. Maybe there is a way to allow both with minimal code size...

@dmazzella

dmazzella commented May 24, 2018 •

Copy link
Copy Markdown
Contributor

Except, I didn't remove the \g pattern. Reading the Python docs, this "g" pattern allows two things that standard \number doesn't: it allows to reference the entire group with \g<0> (\0 is not allowed); and it allows to have integers after the replacement, like \g<1>2 works but you can't do that with \12.

If add this check at https://github.com/dpgeorge/micropython/blob/36f45ced8608822b8c22ea6907e52bb4658a6596/extmod/modure.c#L345:

if (match_no == 0 && is_group_number == 0) {
    memmove((char *)(start_group), (char *)(end_group), strlen(start_group));
    continue;
}

fix \0 replace and enable \g<0>

@dmazzella

dmazzella commented May 25, 2018 •

Copy link
Copy Markdown
Contributor

Hi Damian,
your last commit work in most of normal use case, but as python docs:

\number
Matches the contents of the group of the same number. Groups are numbered starting from 1. For example, (.+) \1 matches 'the the' or '55 55', but not 'thethe' (note the space after the group). This special sequence can only be used to match one of the first 99 groups. If the first digit of number is 0, or number is 3 octal digits long, it will not be interpreted as a group match, but as the character with octal value number. Inside the '[' and ']' of a character class, all numeric escapes are treated as characters.

the special \0 group match must be considered.

@dpgeorge

Copy link
Copy Markdown
Member Author

the special \0 group match must be considered.

Yes. But for now I just wanted to simplify the existing code to make it more compact, without changing the functionality (at least not changing it too much).

or number is 3 octal digits long, it will not be interpreted as a group match, but as the character with octal value number.

I'm not sure we can support octal number escapes here...

@dmazzella

dmazzella commented May 26, 2018 •

Copy link
Copy Markdown
Contributor

Hi Damien,

this patch octal_number_support.zip implements all needs by:

\number
Matches the contents of the group of the same number. Groups are numbered starting from 1. For example, (.+) \1 matches 'the the' or '55 55', but not 'thethe' (note the space after the group). This special sequence can only be used to match one of the first 99 groups. If the first digit of number is 0, or number is 3 octal digits long, it will not be interpreted as a group match, but as the character with octal value number

@dmazzella

dmazzella commented May 26, 2018 •

Copy link
Copy Markdown
Contributor

this is a little test for these features:

s = re.compile(
    '(calzino) (blu|bianco|verde) e (scarpa) (blu|bianco|verde)'
).sub(
    r'\g<1> colore \2 con \g<3> colore \4? ...\0 -\01 -\05 - \g<0> - \01323 - \0898877665544332211 - \26796576576576576576576576576576',
    'calzino blu e scarpa verde'
)
print(s.encode())
print(s)

the output for upy and cpy are identicals.

@dmazzella

Copy link
Copy Markdown
Contributor

Hi @dpgeorge,
did you have time to check the last patch? Any suggestions?

@dpgeorge

dpgeorge commented Jun 9, 2018

Copy link
Copy Markdown
Member Author

did you have time to check the last patch? Any suggestions?

I had a look at it, the patch that adds support for octal numbers. There's quite a lot of code added for this feature which is probably rarely used.

And you can already achieve the same thing (inserting arbitrary bytes) by using the non-r form of a string. For example, if you want to do this:

re.sub(r'(123)', r'456\072\1', 'abc123def')

then you can instead write it like this, which doesn't require support for interpreting octals in the regex code:

re.sub(r'(123)', '456\072\\1', 'abc123def')

(note the subtle change: "r" is gone in the 2nd string and there's a double backslash before the "1")

So I'd say we shouldn't add octal support, to keep code size down.

@dpgeorge
dpgeorge force-pushed the extmod-ure-match-sub branch from 39d62ff to b5b0c20 Compare June 9, 2018 03:04
@dmazzella

Copy link
Copy Markdown
Contributor

@dpgeorge the only modification i can suggest is that the special \0 group match must be considered.

@dpgeorge

Copy link
Copy Markdown
Member Author

the special \0 group match must be considered.

In what way do you mean? At the moment \0 and \<0> are the same and will output the entire match.

@dpgeorge
dpgeorge force-pushed the extmod-ure-match-sub branch from 8c87f0d to 41db665 Compare June 13, 2018 04:12
@dmazzella

Copy link
Copy Markdown
Contributor

In what way do you mean? At the moment \0 and <0> are the same and will output the entire match.

something like that:

// Group specified with syntax "\number"
if (!is_g_format && *repl == '0') {
    vstr_add_byte(vstr_return, 0);
    repl++;
    continue;
}

for test:
print(re.sub(r'(123)', '456\072\\1\\01\\g<0>', 'abc123def').encode())

the output in cPy:
b'abc456:123\x01123def'

the output in uPy:
b'abc456:123\x001123def'

or removing:
vstr_add_byte(vstr_return, 0);

the output in uPy:
b'abc456:1231123def'

@dmazzella

dmazzella commented Jun 13, 2018 •

Copy link
Copy Markdown
Contributor

@dpgeorge

with the current implementation there is a problem of "Segmentation fault" in the construction of the number from the string.

with a group like:
print(re.sub(r'(123)', '456\072\\199999999999999999999999999999999999999999999\\01\\g<0>', 'abc123def').encode())

to avoid this I suggest adding control over the maximum of acceptable groups (99) like this:
} while ('0' <= *repl && *repl <= '9' && match_no * 10 + (*repl - '0') <= 99);

cPy:
sre_constants.error: invalid group reference 19 at position 5

in uPy we can add:

if (match_no < match->num_matches) {
...
} else {
    mp_raise_ValueError("invalid group reference");
}

@dpgeorge

Copy link
Copy Markdown
Member Author

something like that: ...

I don't think there's a need to support \0 to output null (or nothing). None of these behaviours will match CPython properly without substantial additional code. I think \0 doing the same thing as \<0> is good enough behaviour. At the very least there is a subset of behaviour of re/ure that is common to CPython and uPy. That's the important thing (so scripts can be written that run the same under both).

with the current implementation there is a problem of "Segmentation fault" in the construction of the number from the string.

Thanks for pointing this out, I've now fixed it in the latest set of commits in this PR. Simplest solution was to make match_no an unsigned int.

@dmazzella

Copy link
Copy Markdown
Contributor

I have tried this but the error differ from cPy and uPy.

cPy refer to group number 19
uPy to 4294967295

use of something like:
} while ('0' <= *repl && *repl <= '9'&& match_no < match->num_matches);
or
} while ('0' <= *repl && *repl <= '9' && match_no * 10 + (*repl - '0') <= 99);

produce the same group number.

@robert-hh

Copy link
Copy Markdown
Contributor

4294967295 is 0xffffffff

@dpgeorge

Copy link
Copy Markdown
Member Author

I have tried this but the error differ from cPy and uPy.

cPy refer to group number 19
uPy to 4294967295

IMO that's not an issue. At least uPy raises an error in this case, and doesn't error out when there's not an error.

@dpgeorge

dpgeorge commented Jul 2, 2018

Copy link
Copy Markdown
Member Author

I further optimised this set of commits for space and heap usage, updated docs, and merged in 1f86460 through 4727bd1

Thanks @dmazzella for the original patch for these features!

@dpgeorge dpgeorge closed this Jul 2, 2018
@dpgeorge
dpgeorge deleted the extmod-ure-match-sub branch July 2, 2018 05:16
tannewt added a commit to tannewt/circuitpython that referenced this pull request Dec 16, 2020
Use $(PYTHON3) instead of $(PYTHON) in make actions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants