1

I am attempting to improve a Bash to Python translator.

Bash allows ++ and -- while Python does not. Many people have suggested that ++x can be rewritten as x += 1. But this ignores the requirement that one be able to recover the resulting value of x before or after incrementing / decrementing as part of a potentially complex bash expression eg:

x=$(( ++y + z--)).

I can find no way of converting the above to a python expression, because it appears python does not allow side effects such as assignment within an expression. A fall back would be to write a pre/post function that took a variable or string as input, and altered its external item so passed to the function. But I can't work out how to have a function alter its input parameter variables.

If I knew that the item being operated on was always an atomic value, I might be able to alter it by mapping its string name to the dictionary to recover this value and so modify it. But ++ has to work with anything bash might throw at it: eg ++y[1].

Is there any way of avoiding having to take such complex bash expressions containing assignments, and break them into separate python steps that build up the overall computation performed in bash, statement by statement. This would be pretty ugly.

I just need to implement call by reference somehow in python.

rici
  • 234,347
  • 28
  • 237
  • 341
Ian Davis
  • 21
  • 2
  • 2
    As you say, you will not be able to convert that to a Python *expression*. To convert it to Python code you will need to separate it into multiple *statements*. – BrenBarn Feb 14 '15 at 00:19
  • 3
    Since many languages allow such expressions, this was probably a deliberate design decision of the Python designers. They didn't want you writing cryptic code like that. – Barmar Feb 14 '15 at 00:29
  • There's a late entry (from Oct 15 '14) to https://stackoverflow.com/questions/1485841/behaviour-of-increment-and-decrement-operators-in-python that has a rather ugly but serviceable trick to implement pre- and postincrement in Python by picking the variable out of the caller's local symbol table by name. Whether you want to go there is up to you, but it's unlikely you'll find a pretty direct mapping. – Wintermute Feb 14 '15 at 00:56
  • @Wintermute: That answer is basically what OP proposes in the third last paragraph ("If I knew..."), and then discards because it only works for names and not "anything bash might throw at it: eg ++y[1]". – rici Feb 14 '15 at 02:31
  • Thanks Wintermute. The late entry you cited will I hope do the trick. Thanks to all the other responses too, but the idea of making the translation functional makes a lot of sense. That way you can improve the functionality post translation by simply improving the functions. – Ian Davis Feb 15 '15 at 21:13
  • Unfortunately Wintermute your suggestion doesn't work. You cannot update variables accessed via locals(). You can say things like locals()['a']+=1 and no error is reported but the variable a within a function does not actually see a change of value. Given that the variable is not changed even though the statement says it is changed, I am surprised this doesn't throw an exception. – Ian Davis Feb 16 '15 at 00:21

1 Answers1

1

If you really want the functionality, it would be easy enough to create a Box (MutableCell) type which supported mutating operators like preincrement and postincrement. But it would also be a lot of overhead, particularly since you would have to translate ((i=j)) into i = Box(j.unbox()) (or i.reset(j.unbox()) if you knew i already existed.

Roughly speaking, it would look like this, except with a lot more checking for string vs. number, and appropriate logic in such cases.

class Box(object):
  __slots__ = ["value"]
  def __init__(self, value=None):
    self.value = value
  def unbox(self):
    return self.value
  def reset(self, value=None):
    self.value = value
    return value
  def preincrement(self,inc=1):
    self.value += inc
    return self.value
  def postincrement(self,inc=1):
    tmp = self.value
    self.value += inc
    return tmp

Then you can translate, for example, x[i++] = --z + (t += 2) * u (Yuk!) into:

x[i.postincrement()] = ( Box(z.preincrement(-1)
                       + t.preincrement(2) * u.unbox())

Or you could use x[i.postincrement()].reset( ... ) if you had previously created x as a defaultdict(Box).

But it's really hard to believe that it is worth the overhead.

rici
  • 234,347
  • 28
  • 237
  • 341