Skip to content

Commit

Permalink
[ysh builtin] Sketch eggex API with methods on Str and Match
Browse files Browse the repository at this point in the history
I think this is the most general API, and you can implement everything
on top of it:

- Str->sub() aka Str->replace()
- Str->split()
- findAll()
  • Loading branch information
Andy Chu committed Dec 11, 2023
1 parent 193af2b commit c89bd0a
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 6 deletions.
26 changes: 26 additions & 0 deletions builtin/method_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,29 @@ def Call(self, rd):
self.mem.SetPlace(place, val, rd.LeftParenToken())

return value.Null


# which method group() start() end()
GROUP = 0
START = 1
END = 2

class MatchAccess(vm._Callable):

def __init__(self, method):
# type: (int) -> None
self.method = method

def Call(self, rd):
# type: (typed_args.Reader) -> value_t

# This is guaranteed
m = rd.PosMatch()

# string name or integer
val = rd.PosValue()
rd.Done()

# TODO: look at m.indices and return a string

return value.Null
31 changes: 31 additions & 0 deletions builtin/method_str.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,34 @@ def Call(self, rd):

res = string.upper()
return value.Str(res)


class Search(vm._Callable):

def __init__(self):
# type: () -> None
pass

def Call(self, rd):
# type: (typed_args.Reader) -> value_t
"""
s => search(eggex, pos=0)
"""

eggex = rd.PosEggex()
# don't confuse 'start' and 'pos'?
# Python has 2 kinds of 'pos'
pos = rd.NamedInt('pos', 0)
rd.Done()

# TODO:
#
# call libc.regex_search(str ERE, int flags, str s)
#
# which should return an array of positions
#
# - it has the regcomp cache
# - TODO: eggex evaluation has to cache the group names, and number of
# groups

return value.Null
16 changes: 10 additions & 6 deletions core/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@ def Main(

# replace substring, OR an eggex
'replace': None,

# Like Python's re.search, except we put it on the string object
# It's more consistent with Str->find(substring, pos=0)
'search': method_str.Search(),
}
methods[value_e.Dict] = {
'get': None, # doesn't raise an error
Expand Down Expand Up @@ -767,6 +771,12 @@ def Main(
}

# TODO: implement these
methods[value_e.Match] = {
'group': method_other.MatchAccess(method_other.GROUP),
'start': method_other.MatchAccess(method_other.START),
'end': method_other.MatchAccess(method_other.END),
}

methods[value_e.IO] = {
# io->eval(myblock) is the functional version of eval (myblock)
# Should we also have expr->eval() instead of evalExpr?
Expand All @@ -775,12 +785,6 @@ def Main(
# identical to command sub
'captureStdout': None,
'promptVal': method_io.PromptVal(),
# like \w - working dir
'getcwd': None,
# like \u
'getUserName': None,
# like \h
'getHostName': None,
}

methods[value_e.Place] = {
Expand Down
6 changes: 6 additions & 0 deletions core/value.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ module value
# / d+ /
| Eggex(re expr, List[str] flags, str? as_ere)

# indices has G groups and N matches. -1 values indicate no match.
# We flatten it to reduce allocations, and because group() start() end()
# methods make it user friendly
# TODO: might need a pointer to Eggex for the names
| Match(str s, List[int] indices)

# ^[42 + a[i]]
| Expr(expr e)

Expand Down
28 changes: 28 additions & 0 deletions frontend/typed_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,24 @@ def _ToPlace(self, val):
'Arg %d should be a Place' % self.pos_consumed,
self.BlamePos())

def _ToMatch(self, val):
# type: (value_t) -> value.Match
if val.tag() == value_e.Match:
return cast(value.Match, val)

raise error.TypeErr(val,
'Arg %d should be a Match' % self.pos_consumed,
self.BlamePos())

def _ToEggex(self, val):
# type: (value_t) -> value.Eggex
if val.tag() == value_e.Eggex:
return cast(value.Eggex, val)

raise error.TypeErr(val,
'Arg %d should be an Eggex' % self.pos_consumed,
self.BlamePos())

def _ToIO(self, val):
# type: (value_t) -> value.IO
if val.tag() == value_e.IO:
Expand Down Expand Up @@ -314,6 +332,16 @@ def PosPlace(self):
val = self.PosValue()
return self._ToPlace(val)

def PosEggex(self):
# type: () -> value.Eggex
val = self.PosValue()
return self._ToEggex(val)

def PosMatch(self):
# type: () -> value.Match
val = self.PosValue()
return self._ToMatch(val)

def PosIO(self):
# type: () -> value.IO
val = self.PosValue()
Expand Down

0 comments on commit c89bd0a

Please sign in to comment.