Creating slice notation from string

Paul McGuire ptmcg at austin.rr.com
Thu Sep 3 06:46:25 CEST 2009


On Sep 2, 4:55 pm, bvdp <b... at mellowood.ca> wrote:
> I'm trying to NOT create a parser to do this .... and I'm sure that
> it's easy if I could only see the light!
>

Well, this is a nice puzzler, better than a sudoku.  Maybe a quick
parser with pyparsing will give you some guidance on how to do this
without a parser library:

from pyparsing import *

# relevant punctuation, suppress after parsing
LBR,RBR,COLON = map(Suppress,"[]:")

# expression to parse numerics and convert to int's
integer = Regex("-?\d+").setParseAction(lambda t: int(t[0]))

# first try, almost good enough, but wrongly parses "[2]" -> [2::]
sliceExpr = ( LBR + Optional(integer,default=None) +
                Optional(COLON + Optional(integer,default=None),
                        default=None) +
                Optional(COLON + Optional(integer,default=None),
                        default=None) +
                RBR )

# better, this version special-cases "[n]" -> [n:n+1]
# otherwise, just create slice from parsed int's
singleInteger = integer + ~FollowedBy(COLON)
singleInteger.setParseAction(lambda t : [t[0],t[0]+1])

sliceExpr = ( LBR +
                (singleInteger |
                Optional(integer,default=None) +
                Optional(COLON + Optional(integer,default=None),
                        default=None) +
                Optional(COLON + Optional(integer,default=None),
                        default=None)
                ) +
              RBR )

# attach parse action to convert parsed int's to a slice
sliceExpr.setParseAction(lambda t: slice(*(t.asList())))


tests = """\
[2]
[2:3]
[2:]
[2::2]
[-1:-1:-1]
[:-1]
[::-1]
[:]""".splitlines()

testlist = range(10)
for t in tests:
    parsedSlice = sliceExpr.parseString(t)[0]
    print t, parsedSlice, testlist[parsedSlice]


Prints:

[2] slice(2, 3, None) [2]
[2:3] slice(2, 3, None) [2]
[2:] slice(2, None, None) [2, 3, 4, 5, 6, 7, 8, 9]
[2::2] slice(2, None, 2) [2, 4, 6, 8]
[-1:-1:-1] slice(-1, -1, -1) []
[:-1] slice(None, -1, None) [0, 1, 2, 3, 4, 5, 6, 7, 8]
[::-1] slice(None, None, -1) [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
[:] slice(None, None, None) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


Yes, it is necessary to handle the special case of a "slice" that is
really just a single index.  If your list of parsed integers has only
a single value n, then the slice constructor creates a slice
(None,n,None).  What you really want, if you want everything to create
a slice, is to get slice(n,n+1,None).  That is what the singleInteger
special case does in the pyparsing parser.

-- Paul



More information about the Python-list mailing list