Skip to content

Commit

Permalink
[eggex] _match() fails when arg is out of range
Browse files Browse the repository at this point in the history
Also test that unmatched groups return null, not ''
  • Loading branch information
Andy Chu committed Dec 14, 2023
1 parent e6bbc73 commit ecc5087
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 23 deletions.
29 changes: 19 additions & 10 deletions builtin/func_eggex.py
Expand Up @@ -5,6 +5,7 @@
from __future__ import print_function

from _devbuild.gen.value_asdl import value, value_t
from core import error
from core import state
from core import vm
from frontend import typed_args
Expand All @@ -24,17 +25,25 @@ def __init__(self, mem):

def Call(self, rd):
# type: (typed_args.Reader) -> value_t
n = rd.OptionalInt(default_=0)

# TODO: Support strings
s = self.mem.GetMatch(n)
# YSH code doesn't deal well with exceptions!
#if s is None:
# raise IndexError('No such group')
if s is not None:
return value.Str(s)

return value.Null
# TODO: Support strings for named captures
i = rd.OptionalInt(default_=0)

matches = self.mem.GetRegexMatches()
num_groups = len(matches) # including group 0
if i < num_groups:
captured = matches[i]
if captured is None:
return value.Null
else:
return value.Str(captured)
else:
if num_groups == 0:
msg = 'No regex capture groups'
else:
msg = 'Expected capture group less than %d, got %d' % (
num_groups, i)
raise error.UserError(2, msg, rd.LeftParenToken())


class Start(vm._Callable):
Expand Down
3 changes: 2 additions & 1 deletion core/process.py
Expand Up @@ -1681,7 +1681,8 @@ def GetJobWithSpec(self, job_spec):

# TODO: Add support for job specs based on prefixes of process argv.
m = util.simple_regex_search(r'^%([0-9]+)$', job_spec)
if m is not None and len(m) > 1:
if m is not None:
assert len(m) == 2
job_id = int(m[1])
if job_id in self.jobs:
return self.jobs[job_id]
Expand Down
10 changes: 3 additions & 7 deletions core/state.py
Expand Up @@ -2152,13 +2152,9 @@ def SetMatches(self, matches):
# type: (List[str]) -> None
self.regex_matches[-1] = matches

def GetMatch(self, i):
# type: (int) -> Optional[str]
top = self.regex_matches[-1]
if i < len(top):
return top[i]
else:
return None
def GetRegexMatches(self):
# type: () -> List[Optional[str]]
return self.regex_matches[-1]


#
Expand Down
30 changes: 25 additions & 5 deletions spec/ysh-regex.test.sh
Expand Up @@ -131,18 +131,38 @@ if (s ~ '[[:digit:]]+') {
}
# Should be cleared now
# should this be Undef rather than ''?
var x = _match(0)
var y = _match(1)
if (x === null and y === null) {
echo 'cleared'
try {
var x = _match(0)
}
if (_status === 2) {
echo 'got expected status 2'
}

try {
var y = _match(1)
}
if (_status === 2) {
echo 'got expected status 2'
}

## STDOUT:
matches
['foo', 'oo']
does not match
['foo', 'oo']
cleared
got expected status 2
got expected status 2
## END

#### _match() returns null when group doesn't match
shopt -s ysh:upgrade

var pat = / <capture 'a'> | <capture 'b'> /
if ('b' ~ pat) {
echo "$[_match(1)] $[_match(2)]"
}
## STDOUT:
null b
## END

#### _start() and _end()
Expand Down

0 comments on commit ecc5087

Please sign in to comment.