1

I have a file with a lot of lines like this

f(a, b)
f(abc, def)
f(a, f(u, i))
...

and I was asked to write a program in Python that would translate the strings into the following format:

a+b
abc+def
a+(u+i)
...

Rule: f(a, b) -> a+b

The approach I am following right now uses eval functions:

def f(a, b):
    return "({0} + {1})".format(a,b)

eval("f(f('b','a'),'c')")

which returns

'((b + a) + c)'

However, as you can see, I need to put the letters as strings so that the eval function does not throw me a NameError when I run it.

Is there any way that will allow me to get the same behavior out of the eval function but without declaring the letters as strings?

user1790813
  • 694
  • 2
  • 7
  • 22
  • I don't think you want to use `eval` for this. Besides, it's generally unsafe to do. – 2rs2ts May 05 '14 at 15:21
  • @2rs2ts If it is a secure environment (f.e. homework on a virtual machine), it can be done. – peterh May 05 '14 at 15:22
  • 1
    Using `ast.literal_eval()` is a little safer: https://docs.python.org/2/library/ast.html#ast.literal_eval. – nofinator May 05 '14 at 15:26
  • @nofinator Wouldn't be useful for this case, though, since those aren't python literals. – 2rs2ts May 05 '14 at 15:28
  • Have you think to use `regex` ? –  May 05 '14 at 15:29
  • @projetmbc Yeah, he could use regex to pre-process the text to add the quotes so he could use `eval` more simply. That'd be nice. – 2rs2ts May 05 '14 at 15:30
  • You essentially want to parse the code, modify the nodes pertaining to certain function calls, then write out new code. See http://stackoverflow.com/q/768634/1126841 – chepner May 05 '14 at 15:32
  • Will a+b be an arithmetic operation ? Maybe `ast` can be use instead of `regex`. I can try to do something but I would like to have more informations and this will be done after 2km of swimming... –  May 05 '14 at 15:40
  • I am using a safe environment so there is no need to worry about security. I have also thought a bit about using `regex` but I am not the best user of it and I can only do very basic things. I will check on the `ast` and on the answers below. – user1790813 May 05 '14 at 16:00

3 Answers3

2

Yes, you can. The key is to use a mapping which returns the string as a key when it is missing.

>>> class Mdict(dict):
...     def __missing__(self, k):
...         return k
... 
>>> eval('foo + bar', Mdict())
'foobar'

Of course, the general caveats about eval apply -- Please don't use it unless you trust the input completely.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • What about `eval('abc + def')`? – vaultah May 05 '14 at 15:27
  • @frostnational -- unfortunately, there's absolutely nothing you can do about that since it is a `SyntaxError` and python can't parse it into an AST. All you can do there is `eval('abc + "def"')` -- Or use something other than `eval`. – mgilson May 05 '14 at 15:30
2

eval is overkill here. this is just a simple string processing exercise:

  • replace the first 'f(' and the last ')' with ''
  • replace all remaining 'f(' with '('
  • replace all ', ' with '+'

and you're done.

this assumes that the only time the characters 'f(' appear next to each other is when it's supposed to represent a call to function f.

acushner
  • 9,595
  • 1
  • 34
  • 34
1

You could use the shlex module to give yourself a nice token stack and then parse it as a sort of push down automaton.

>>> import shlex
>>> def parsef(tokens):
    ftok = tokens.get_token()       # there's no point to naming these tokens
    oparentok = tokens.get_token()  # unless you want to assert correct syntax
    lefttok = tokens.get_token()
    if 'f' == lefttok:
        tokens.push_token(lefttok)
        lefttok = "("+parsef(tokens)+")"
    commatok = tokens.get_token()
    righttok = tokens.get_token()
    if 'f' == righttok:
        tokens.push_token(righttok)
        righttok = "("+parsef(tokens)+")"
    cparentok = tokens.get_token()
    return lefttok+"+"+righttok
>>> def parseline(line):
    return parsef(shlex.shlex(line.strip()))
>>> parseline('f(a, b)')
'a+b'
>>> parseline('f(abc, def)')
'abc+def'
>>> parseline('f(a, f(u, i))')
'a+(u+i)'

Note that this assumes you are getting correct syntax.

2rs2ts
  • 10,662
  • 10
  • 51
  • 95