fst.docs.d08_views

FST slice indexing

To be able to execute the examples, import this.

>>> from fst import *

How to get

An fstview is a lightweight short-lived object meant to facilitate access to lists of child objects like a block statement body or Tuple / List / Set elts field. Any field which is a list of other objects (or even strings in the case of Global or Nonlocal) will make use of an fstview when accessed through the field name on the parent FST object.

>>> view = FST('[1, 2, 3]').elts
>>> type(view)
<class 'fst.view.fstview'>
>>> print(str(view)[:88])
<<List ROOT 0,0..0,9>.elts[0:3] [<Constant 0,1..0,2>, <Constant 0,4..0,5>, <Constant 0,7
>>> view.base, view.field, view.start, view.stop
(<List ROOT 0,0..0,9>, 'elts', 0, 3)

An fstview is not normally intended to be accessed by assigning it to a name and using it through that. An fstview basically exists to facilitate direct access to slices of children for immediate operations.

>>> f = FST('[1, 2]')
>>> _ = f.elts.append('3')  # _ just shuts up output
>>> print(f.src)
[1, 2, 3]
>>> del f.elts[1]
>>> print(f.src)
[1, 3]
>>> print(f.elts.insert('4', 1).base.src)
[1, 4, 3]
>>> del f.elts[:2]
>>> print(f.src)
[3]

Indexing

You can copy or cut slices from an FST using a view.

>>> f = FST('''
... i = 1
... j = 2
... k = 3
... l = 4
... '''.strip())
>>> print(f.body[1:3].copy().src)
j = 2
k = 3
>>> print(f.body[:2].cut().src)
i = 1
j = 2
>>> print(f.src)
k = 3
l = 4

You can assign slices to a view.

>>> f = FST('[a, b]')
>>> f.elts[1:1] = '{x, y}'
>>> print(f.src)
[a, x, y, b]
>>> f.elts[2:] = f.elts.copy()
>>> print(f.src)
[a, x, a, x, y, b]
>>> f.elts[:] = Tuple(elts=[])
>>> print(f.src)
[]

Non-slice indexing also works as expected, including getting and putting a single element and not a slice, even if a slice is the source.

>>> f = FST('[x, y, z]')
>>> print(f.elts[1].copy().src)
y
>>> f.elts[2] = '[a, b]'
>>> print(f.src)
[x, y, [a, b]]

If you want to assign the element as a slice, you must use slice indexing.

>>> f.elts[3:3] = '[c, d]'
>>> print(f.src)
[x, y, [a, b], c, d]

Or assign directly to the field name, replacing the entire slice, but this is not a view operation but rather a property setter of the FST class itself.

>>> f.elts = 't, u, v'
>>> print(f.src)
[t, u, v]

Other operations

With the exception if insert() (since its a standard pattern) these operations don't take indices but rather are meant to be executed on a particular indexed view which selects their range in the list.

>>> print(FST('[a, b, c]').elts.replace('[x, y]').base.src)
[[x, y]]
>>> print(FST('[a, b, c]').elts.replace('[x, y]', one=False).base.src)
[x, y]
>>> print(FST('[a, b, c]').elts.remove().base.src)
[]
>>> print(FST('[a, b, c]').elts.insert('[x, y]', 1).base.src)
[a, [x, y], b, c]
>>> print(FST('[a, b, c]').elts[1:1].insert('[x, y]').base.src)
[a, [x, y], b, c]
>>> print(FST('[a, b, c]').elts.insert('[x, y]', 1, one=False).base.src)
[a, x, y, b, c]
>>> print(FST('[a, b, c]').elts.insert('[x, y]', 'end', one=False).base.src)
[a, b, c, x, y]
>>> print(FST('[a, b, c]').elts.append('[x, y]').base.src)
[a, b, c, [x, y]]
>>> print(FST('[a, b, c]').elts.extend('[x, y]').base.src)
[a, b, c, x, y]
>>> print(FST('[a, b, c]').elts.prepend('[x, y]').base.src)
[[x, y], a, b, c]
>>> print(FST('[a, b, c]').elts.prextend('[x, y]').base.src)
[x, y, a, b, c]

They work on subviews as well.

>>> print(FST('[a, b, c]').elts[1:2].replace('[x, y]').base.src)
[a, [x, y], c]
>>> print(FST('[a, b, c]').elts[1:2].replace('[x, y]', one=False).base.src)
[a, x, y, c]
>>> print(FST('[a, b, c]').elts[1:3].remove().base.src)
[a]
>>> print(FST('[a, b, c]').elts[1:2].insert('[x, y]', 1).base.src)
[a, b, [x, y], c]
>>> print(FST('[a, b, c]').elts[1:2][1:1].insert('[x, y]').base.src)
[a, b, [x, y], c]
>>> print(FST('[a, b, c]').elts[1:2].insert('[x, y]', 1, one=False).base.src)
[a, b, x, y, c]
>>> print(FST('[a, b, c]').elts[1:2].insert('[x, y]', 'end', one=False).base.src)
[a, b, x, y, c]
>>> print(FST('[a, b, c]').elts[1:2].append('[x, y]').base.src)
[a, b, [x, y], c]
>>> print(FST('[a, b, c]').elts[1:2].extend('[x, y]').base.src)
[a, b, x, y, c]
>>> print(FST('[a, b, c]').elts[1:2].prepend('[x, y]').base.src)
[a, [x, y], b, c]
>>> print(FST('[a, b, c]').elts[1:2].prextend('[x, y]').base.src)
[a, x, y, b, c]

Why ephemeral?

As stated above, views are meant for direct use and not for keeping around. The reason for this is that any operation that changes the size of the target of a view, if it is not effectuated through the view itself, will almost certainly invalidate the view information.

>>> view = FST('[1, 2, 3]').elts

This is an operation on a node selected by the view.

>>> view[1].remove()

Notice the size of the view is 3 but there are only two elements.

>>> view
<<List ROOT 0,0..0,6>.elts[0:3] [<Constant 0,1..0,2>, <Constant 0,4..0,5>]>
>>> view = FST('[1, 2, 3]').elts

This is not an operation on this view but a subview, which will not modify this view.

>>> view[1:].cut()
<List ROOT 0,0..0,6>

And the indices don't match the contents again.

>>> view
<<List ROOT 0,0..0,3>.elts[0:3] [<Constant 0,1..0,2>]>

So it is better to let a view go away after using it rather than trying to keep it around like in this example.