Python: Using strings as an object argument? - python

Essentially my problem is as follows...
In Python, I have a function that will return an output string in the following form:
'union(symbol(a), symbol(b))'
The function forms found within this string actually exist in an object class called RegExTree. Further this class contains a function to construct a tree data structure using the function "construct()" as shown below:
tree = RegExTree()
tree.construct(union(symbol(a), symbol(b))
The above two lines of code would work normally, constructing a tree based on parsing the arguments within the construct function. I want to pass in a string in a similar fashion, perhaps this line of code illustrates what I want:
tree = RegExTree()
expression = 'union(' + 'symbol(' + 'a' + ')' + ', ' + 'symbol(' + 'b' + ')' + ')'
tree.construct(expression)
Right now the way I have the code written as above it yields an error (in the Linux terminal) as follows:
$ Attribute Error: 'str' object has no attribute 'value'
Can you coerce Python to interpret the string as a valid argument/line of code. In essence, not as string, but as object constructors.
Is there a way to get Python to interpret a string as rather something that would have been parsed/compiled into objects and have it construct the objects from the string as if it were a line of code meant to describe the same end goal?
Is what I'm asking for some kind of back-door type conversion? Or is what I'm asking not possible in programming languages, specifically Python?
EDIT: Using Michael's solution posited below that involves "eval()", there is one way to hack this into form:
tree = RegExTree()
a = 'a'
b = 'b'
expression = 'union(' + 'symbol(' + a + ')' + ', ' + 'symbol(' + b + ')' + ')'
tree.construct(eval(expression))
Is there a better way of doing this? Or is it just that the nature of my output as string representing functions is just not a good idea?
[Thanks martineau for the correction for my solution edit!]

You can use the python built-in eval statement.
A word of caution though... you do not want to run eval() on a string that's coming into your program as external input provided by the user. That could create a security hole where users of your program could run arbitrary Python code of their own design.
In your example it'd look something like this:
tree = RegExTree()
expression = 'union(' + 'symbol(' + 'a' + ')' + ', ' + 'symbol(' + 'b' + ')' + ')'
tree.construct( eval(expression) ) # Notice the eval statement here

Related

Sum operation syntax error

i = 0
while i < 10:
print('print("Hello world '+ str (i*9)+'")')
i = i + 1
I was practicing loop and I wonder why I have to put + after STR(1*9) ??
print('print("Hello world '+ str (i*9)'")') and Why this code has syntax error?
(no plus sign)
I tried put code print(print("Hello world"+str(i*9))) to not use + but I got
Hello world0
None
Hello world9
None
Hello world18
None
To concatenate two string together you need the + operator to tell the python interpreter to specify its some_string plus some_other_string.
Alternatively if you dont like using the + operator you can use .format() like so;
while i < 10:
print("Hello World {0}".format(i*9))
i = i + 1
The string to be inserted goes inside the curly braces. I used 0 inside the curlies because you can add others to your string incrementing the numbers and adding another paramater to the format function.
A + concatenates strings in python. So you need it. Python does not know how to interpret two variables next to each other without , or + or else between them. '")' is also considered a variable in programming context.

exec with ';' in python

I am creating a string, to print the fields in a list, . the fields should be separated by ';', code snippet looks like this( simplified code, not actual )
list = ["abc","xyz","pqr"]
str = "print " + "list[0]" + ";" + "list[2]" # This is dynamically generated
exec (str)
My problem here is, with exec statement, it prints only "xyz" , because of the semi colon. what is the best way to solve this, so that the exec statement prints "xyz;pqr"
You are generating the following code:
print list[0];list[2]
Note that the ; is not quoted. Since a ; is used by Python to separate multiple simple statements on a line, Python executes the print list[0] first, then list[2] (which ends up doing nothing).
You'd have to generate this code instead:
print list[0] + ';' + list[2]
which you could do with:
str = "print " + "list[0]" + " + ';' + " + "list[2]"
However, you should not be using code generation at all. Use standard Python methods to join or format a string. You could use str.format():
print '{};{}'.format(list[0], list[2])
or you could use str.join():
print ';'.join([list[0], list[2]])
If you must vary what code is executed based on some other variables, try to avoid exec still. You could use from __future__ import print_function or encapsulate the print statement in a new function, then call functions dynamically. You can always use a dispatch table to map a string to a function to call, for example.
Try this:
str = "print" + "list[0]" + "';'" + "list[2]"
or
str = "print" + "list[0]" + "/;" + "list[2]"
The problem here is that Python optionally allows semicolons to delimit two separate statements (Compound statements). So when you use exec on the evaluated statement print "abc";"xyz", Python thinks they are two separate statements, hence, only printing "abc".
You could use single quotes around the semicolon to show that it is a string and concatenate them with their surrounding strings:
# Refrain from using list and str as they are built-ins
l = ["abc", "xyz", "pqr"]
s = "print " + "l[0]" + "+';'+" + "l[2]"
exec(s)

What's an equivalent in python of stringification C preprocessor macro #

https://gcc.gnu.org/onlinedocs/cpp/Stringification.html allows to stringify argument (before evaluation), which is useful for debugging. How would we do it in python?
eg:
def prettyprint(a):
#requires special function stringify
print(stringify('a') + ':' + str(a));
def test():
prettyprint(1+2)
# will print: 1+2:3
I don't think there is a way to do this without using strings. The C preprocessor really only processes text, so stringify is placing the expression in quotes and the rest is eval
def prettyprint(a):
print( a + ':' + str( eval(a) ) )
>>> prettyprint('1+2')
1+2:3
If you don't use strings, then the expression will be evaluated before the call and prettyprint has no way to figure out how the 3 it received came to be.

Function equivalent to prepending string with "r" in Python

It's great that I can write
s = r"some line\n"
but what is the functional equivalent to preprending with r? For example:
s = raw_rep( s )
There isn't one. The r is an integral part of the string literal token, and omitting it is a lossy operation.
For example, r'\n', r'\12' and r'\x0a' are three different strings. However, if you omit the r, they become identical, making it impossible to tell which of the three it was to begin with.
For this reason, this is no method that would reconstruct the original string 100% of the time.
def raw_rep(s):
quote = '"' if "'" in s else "'"
return 'r' + quote + s + quote
>>> print raw_rep(r'some line\n')
r'some line\n'

Parsing arithmetic expressions with function calls

I am working with pyparsing and found it to be excellent for developing a simple DSL that allows me to extract data fields out of MongoDB and do simple arithmetic operations on them. I am now trying to extend my tools such that I can apply functions of the form Rank[Person:Height] to the fields and potentially include simple expressions as arguments to the function calls. I am struggling hard with getting the parsing syntax to work. Here is what I have so far:
# Define parser
expr = Forward()
integer = Word(nums).setParseAction(EvalConstant)
real = Combine(Word(nums) + "." + Word(nums)).setParseAction(EvalConstant)
# Handle database field references that are coming out of Mongo,
# accounting for the fact that some fields contain whitespace
dbRef = Combine(Word(alphas) + ":" + Word(printables) + \
Optional(" " + Word(alphas) + " " + Word(alphas)))
dbRef.setParseAction(EvalDBref)
# Handle function calls
functionCall = (Keyword("Rank") | Keyword("ZS") | Keyword("Ntile")) + "[" + expr + "]"
functionCall.setParseAction(EvalFunction)
operand = functionCall | dbRef | (real | integer)
signop = oneOf('+ -')
multop = oneOf('* /')
plusop = oneOf('+ -')
# Use parse actions to attach Eval constructors to sub-expressions
expr << operatorPrecedence(operand,
[
(signop, 1, opAssoc.RIGHT, EvalSignOp),
(multop, 2, opAssoc.LEFT, EvalMultOp),
(plusop, 2, opAssoc.LEFT, EvalAddOp),
])
My issue is that when I test a simple expression like Rank[Person:Height] I am getting a parse exception:
ParseException: Expected "]" (at char 19), (line:1, col:20)
If I use a float or arithmetic expression as the argument like Rank[3 + 1.1] the parsing works ok, and if I simplify the dbRef grammar so its just Word(alphas) it also works. Cannot for the life of me figure out whats wrong with my full grammar. I have tried rearranging the order of operands as well as simplifying the functionCall grammar to no avail. Can anyone see what I am doing wrong?
Once I get this working I would want to take a last step and introduce support for variable assignment in expressions ..
EDIT: Upon further testing, if I remove the printables from dbRef grammar, things work ok:
dbRef = Combine(Word(alphas) + OneOrMore(":") + Word(alphanums) + \
Optional("_" + Word(alphas)))
HOWEVER, if I add the character "-" to dbRef (which I need for DB fields like "Class:S-N"), the parser fails again. I think the "-" is being consumed by the signop in my operatorPrecedence?
What appears to happen is that the ] character at the end of your test string (Rank[Person:Height]) gets consumed as part of the dbRef token, because the portion of this token past the initial : is declared as being made of Word(printables) (and this character set, unfortunately includes the square brackets characters)
Then the parser tries to produce a functionCall but is missing the closing ] hence the error message.
A tentative fix is to use a character set that doesn't include the square brackets, maybe something more explicit like:
dbRef = Combine(Word(alphas) + ":" + Word(alphas, alphas+"-_./") + \
Optional(" " + Word(alphas) + " " + Word(alphas)))
Edit:
Upon closer look, the above is loosely correct, but the token hierarchy is wrong (e.g. the parser attempts to produce a functionCall as one operand of an an expr etc.)
Also, my suggested fix will not work because of the ambiguity with the - sign which should be understood as a plain character when within a dbRef and as a plusOp when within an expr. This type of issue is common with parsers and there are ways to deal with this, though I'm not sure exactly how with pyparsing.
Found solution - the issue was that my grammar for dbRef was consuming some of the characters that were part of the function specification. New grammar that works correctly:
dbRef = Combine(Word(alphas) + OneOrMore(":") + Word(alphanums) + \
Optional(oneOf("_ -") + Word(alphas)))

Categories

Resources