Python - Syntax check of molecular formulas - python

This is going to be a long question, I hope you guys have patience.
I am writing a program that checks if the syntax for a molecular formula is correct.
I have a BNF-syntax:
<formel>::= <mol> \n
<mol> ::= <group> | <group><mol>
<group> ::= <atom> |<atom><num> | (<mol>) <num>
<atom> ::= <LETTER> | <LETTER><letter>
<LETTER>::= A | B | C | ... | Z
<letter>::= a | b | c | ... | z
<num> ::= 2 | 3 | 4 | ...
and this is my code:
from linkedQFile import LinkedQ
import string
import sys
ATOMER = ["H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar"]
class FormelError(Exception):
pass
class Gruppfel(Exception):
pass
q = LinkedQ()
formel= "(Cl)2)3"
for symbol in formel:
q.put(symbol)
def readNum():
"""Reads digits larger than 1. Raises exception if condition is not fulfilled."""
try:
if int(q.peek()) >= 2:
print(q.peek())
q.get()
return
else:
q.get()
print("Too small digit at the end of row: "+getRest())
sys.exit()
except (ValueError,TypeError):
raise FormelError("Not a number.")
def readletter():
"""Reads lowercase letters and returns them."""
if q.peek() in string.ascii_lowercase:
print(q.peek())
return q.get()
else:
raise FormelError("Expected lowercase letter.")
def readLetter():
"""Reads capital letters and returns them."""
if q.peek() in string.ascii_uppercase:
print(q.peek())
return q.get()
else:
raise FormelError("Expected capital letter.")
def readAtom():
"""Reads atoms on the form X and Xx. Raises Exception if the format for an atom is not fulfilled or if the atom does not exist."""
X = ""
try:
X += readLetter()
except FormelError:
print("Missing capital letter at end of row: "+getRest())
sys.exit()
return
try:
x = readletter()
atom = X+x
except (FormelError, TypeError):
atom = X
if atom in ATOMER:
return
else:
raise FormelError("Unknown atom.")
def readGroup():
if q.peek() in string.ascii_uppercase or q.peek() in string.ascii_lowercase:
try:
readAtom()
except:
print("Unknown atom at end of row: "+getRest())
sys.exit()
try:
while True:
readNum()
except FormelError:
pass
return
if q.peek() == "(":
print(q.peek())
q.get()
try:
readMol()
except FormelError:
pass
if q.peek() == ")":
print(q.peek())
q.get()
else:
print("Missing right parenthesis at end of row: "+ getRest())
sys.exit()
return
digitfound = False
try:
while True:
readNum()
digitfound = True
except:
if digitfound:
return
print("Missing digit at end of row: "+getRest())
sys.exit()
return
raise FormelError("Incorrect start of group")
def readMol():
try:
readGroup()
except FormelError:
print("Incorrect start of group at end of row: "+getRest())
raise FormelError
if q.peek() == None:
return
if not q.peek() == ")":
try:
readMol()
except FormelError:
pass
def readFormel():
try:
readMol()
except:
return
print("Correct formula")
def getRest():
rest = ""
while not q.isEmpty():
rest += q.get()
return rest
readFormel()
Now the code is supposed to accept some given formulas and provide an error code for some given incorrect formulas. Let's look at these given formulas:
Correct:
Si(C3(COOH)2)4(H2O)7
Incorrect:
H2O)Fe
(Cl)2)3
The program accepts the correct formula, but unfortunately also the incorrect ones. The reason for this is that the if statement in:
if not q.peek() == ")":
try:
readMol()
except FormelError:
pass
makes it so that parentheses unbalanced to the right (with one or more parenthesis too much on the right side) slip through the code, instead of being detected as incorrect starts of a "group". How can I fix this, while still having Si(C3(COOH)2)4(H2O)7 being accepted as syntactically correct?
Thank you for your patience :)

Your code for readMol has this erroneous test (you even told us) for ")". Your grammar doesn't show a need for such a test, if you are coding (as you are) a recursive descent parser.
In fact, you grammar has a odd rule for mol:
<mol> ::= <group> | <group><mol>
This doesn't work well with recursive descent parsers. You have refactor such rules to share the common prefixes in each rule. In this case, it is easy:
<mol> ::= <group> ( <mol> | empty ) ;
Then you write the code directly from the grammar rule (see link above)
[You sort of did this, except for the ")" check.]
It should look something like this (I'm not a python expert):
def readMol():
try:
readGroup()
except FormelError:
print("Incorrect start of group at end of row: "+getRest())
raise FormelError
try:
readMol()
except FormelError:
pass
It is helpful when writing recursive descent parsers, to massage the grammar into a most-compatible form first (as I did with your mol rule). Then coding the individual recognizers is a purely mechanical task that is hard to get wrong.

Related

Module calling in if condition

I am having trouble getting the proper result in this code:
class Commerce():
def Entry(_sub, test):
while True:
if _sub == "Maths" or "Busines Studies" or "Accounts" or "Economics" or "Physical Education" or "English":
try:
print("Enter marks of ", _sub, "in", test, end="=>")
x = int(input())
except:
print("Invalid input")
continue
if x in range(0, 41):
return x
break
else:
print("Marks exceded, please try again ")
else:
print("Invalid marks")
continue
class Restrict():
def limitalnum(content):
punctuations =['!','(',')','-','[',']','{','}',';',':',"'",'"',"\\",',','<','>','.','/','?','#','#','$','%','^','&','*','_','~']
count=3
while True:
ask_input=input('%s'%content)
if ask_input.isalnum()==False:
count=count-1
if count in[3,2,1]:
print("Sorry only alphabets and numeric are acceptable")
print("Remaing retry of above entry:%s"%count)
else:
pass
if count==0:
print("Remaing retry of above entry:%s"%count)
print("We are sorry you have exhausted all retry's")
break
continue
else:
break
Stream_option=Restrict.limitalnum("Which Stream you are currently pursuing")
if Stream_option=='1' or 'Maths':
MATHS_PT_MARKS=Commerce.Entry("Maths","Periodic Test-1")
elif Stream_option == '2' or 'Informatics Practices' or 'IP':
IP_PT_MARKS=Commerce.Entry("IP","Periodic Test-1")
elif Stream_option == '3' or 'Hindi':
HI_PT_MARKS = Commerce.Entry("Hindi","Periodic Test-1")
Output Coming:
Which Stream you are currently pursuing: 2
Enter marks of Maths in Periodic Test-1=>
Output Expected :
Which Stream you are currently pursuing: 2
Enter marks of IP in Periodic Test-1=>
I don't know for sure if i used if condition properly or not, Thanks
This question is not duplicate for any other questions in Stack Overflow
What i am trying to get it calling a function from a class in a conditional statement
Uniqueness:
If the Conditional statement is true
Execute the function
If the conditional statement is False
Pass to next line of command

I am getting an error when running this code saying str1 is not defined,when i have already defined it in try block

I am trying to write a small code block where in i take an input (user can enter any input in words or letters), my code should try to find if it is a positive or negative number or the entered value is a string.
try:
str1=int(input('Enter a number:'))
print('try block is completed')
except:
str1=str(str1)
if str1>0:
print('entered value is positive')
elif str1<0:
print('entered value is negative')
else:
print(str1)
The reason is the exception is caused in int(input(...)) so the str1 remains undeclared.
str1=input('Enter a number:')
try:
str1=int(str1)
print('try block is completed')
except:
str1=str(str1)
if str1>0:
print('entered value is positive')
elif str1<0:
print('entered value is negative')
else:
print(str1)
Modify your code like this to handle exception on integer and string
On your except branch you have the following call:
str1=str(str1)
Since you are on your except, and you haven't defined your str1 variable outside the try/except block, your statement will fail when it tries to cast the contentes of the str1 variable (which does not exist outside of the try branch).
A possible implementation to deal with this case would be to set str1 to None or an empty string on your except block. Something like this:
try:
str1 = int(input('Enter a number:'))
print('try block is completed')
except:
str1 = None
# First we check that our variable is not None (caught an exception)
try:
int_str1 = int(str1)
except:
int_str1 = None
if int_str1:
if int_str1 > 0:
print('entered value is positive')
elif int_str1 < 0:
print('entered value is negative')
else:
print('Could not get integer value of input')
It's because you use str1 in your except clause. If there is an exception during the processing of input or int, the variable str1 is never set.
If you add str1=None before your try statement, I am sure, it won't complain anymore, but you need to change your except clause then.
If you're just concerned about the cast to int, you could do:
str1= None
try:
str1=input('Enter a number:')
val=int(str1)
print('try block is completed')
except:
val=None
if val is None:
print('input is not a valid number')
elif val>0:
print('entered value is positive')
elif val<0:
print('entered value is negative')
else:
print(str1)
in the case fn an exception in the call int, the variable str1 never gets created, because int is evaluated (and raises the error) before str1 gets assigned.
also - in the case of an exception you'll get a TypeError for trying to compare a string with 0, so put all you int-assuming logic inside the try, like this:
str1=input('Enter a number:')
try:
str1=int(str1)
if str1>0:
print('entered value is positive')
elif str1<0:
print('entered value is negative')
except:
str1=str(str1)
print(str1)

Invalid Syntax while True statemtn

Here's my script:
def makeithappen():
word=""
while True:
try:
word=inputText()
except:
print("Something happened inputText")
else:
if len(word)>0:
break
elif word!=str:
break
For some reason however I get an invalid syntax error and I am not sure why.
def makeithappen():
word=""
while True:
try:
word=input() #is this supposed to be input()?
except:
print("Something happened inputText")
else:
if len(word)>0:
break
elif isinstance(word, basestring): #I never got the logic behind it
break
I think this is what you wanted to do. It exits if entered text is valid (length is more than 0) and input type is not of str (which is always false in case of python3).
#!/usr/bin/python
# -*- coding: utf-8 -*-
def makeithappen():
word=""
while True:
try:
word=raw_input("Go:")
except:
print("Something happened inputText")
else:
if len(word)>0:
print("Hello!!")
elif word!=str:
print("Bye!!")
break
makeithappen()

Trigger Break Keyword Using Dictionary Python

I'm trying to make do with Python's lack of a switch statement and make code more efficient by using a dictionary, but I can't quite get what I'm looking for. Here's a simplified version of the code I'm working with
def zero():
print("Yo")
def one():
print("Hey")
options = {
0: zero,
1: one,
}
while True:
response = int(input("Number: "))
try:
options.get(response)()
except TypeError:
print("Not a valid response")
and what I would like to see is some way to break the loop such as 2: break that exits the loop. Currently I'm using sys.exit(0), but was wondering if it was possible to use the break keyword
You could define a LoopBreak exception, raise that in a two function, and catch it in the loop to break:
class LoopBreak(Exception):
pass
def zero():
print("Yo")
def one():
print("Hey")
def two():
raise LoopBreak
options = {
0: zero,
1: one,
2: two
}
while True:
response = int(input("Number: "))
try:
options.get(response)()
except TypeError:
print("Not a valid response")
except LoopBreak:
break
As a point of interest, this is similar to the pattern used natively by Python to stop generators; they raise a StopIteration exception when they run out of values to yield.
EDIT: As #mfripp correctly notes below, this will mask any TypeErrors that are raised during execution of zero or one. I would change the main loop to this instead (so you don't have to rely on TypeError):
while True:
response = int(input("Number: "))
action = options.get(response)
if action is None:
print("Not a valid response")
continue
try:
action()
except LoopBreak:
break
There are a few ways you could do this.
Here's a more robust version of #BingsF's clever answer (this one won't mask exceptions raised within the selected function):
class LoopBreak(Exception):
pass
def zero():
print("Yo")
def one():
print("Hey")
def two():
raise LoopBreak
options = {
0: zero,
1: one,
2: two
}
while True:
try:
response = int(input("Number: "))
action = options[response]
except (ValueError, KeyError):
print("Not a valid response")
continue
try:
action()
except LoopBreak:
break
Or you could specify a special flag in your dictionary that will force a break:
def zero():
print("Yo")
def one():
print("Hey")
options = {
0: zero,
1: one,
2: False
}
while True:
try:
response = int(input("Number: "))
action = options[response]
except (ValueError, KeyError):
print("Not a valid response")
continue
if action is False:
break
else:
action()
Or use a special return value to force a break:
def zero():
print("Yo")
# returns None by default
def one():
print("Hey")
# returns None by default
def two():
return False
options = {
0: zero,
1: one,
2: two
}
while True:
try:
response = int(input("Number: "))
action = options[response]
except (ValueError, KeyError):
print("Not a valid response")
continue
if action() is False:
break
The following code might be more "Pythonic". It could be infinitesimally slower than the approaches above, because it has to check all the if statements instead of looking up the function in a dictionary via a hash. But it may be easier to read and maintain.
while True:
try:
response = int(input("Number: "))
except ValueError:
response = -1 # error flag
if response == 0:
print("Yo")
elif response == 1:
print("Hey")
elif response == 2:
break
else:
print("Not a valid response")
This is all you need:
while True:
response = int(input("Number: "))
if response not in options:
print("Not a valid response")
else if response == 2:
break
else:
options[response]() # invoke the corresponding function
Incidentally, storing functions in a dictionary and having to invoke them like this isn't exactly Pythonic. It's a lot nicer to simply explicitly enumerate the behaviour you need with successive ifs.

sys.exit() does not exit program when catching exceptions

This program checks molecular formulas. I want the program to exit as soon as it detects an error in a formula. For example, the formula "a", is incorrect.
When I run it through my code:
def readletter():
if q.peek() in string.ascii_lowercase:
print(q.peek())
return q.get()
else:
raise Formelfel("Förväntad liten bokstav.")
def readLetter():
if q.peek() in string.ascii_uppercase:
print(q.peek())
return q.get()
else:
raise Formelfel("Förväntad stor bokstav.")
def readAtom():
X = ""
try:
X += readLetter()
except Formelfel:
print("Missing capital letter at end of row "+getRest())
sys.exit()
return
try:
x = readletter()
atom = X+x
except (Formelfel, TypeError):
atom = X
if atom in ATOMER:
return
else:
raise Formelfel("Okänd atom.")
def readGroup():
if q.peek() in string.ascii_uppercase or q.peek() in string.ascii_lowercase:
try:
readAtom()
except:
print("Unknown atom at end of row "+getRest())
sys.exit()
I get this output:
Missing capital letter and end of row a
Unknown atom at end of row
Why is this? I called sys.exit() before print("Unknown atom at end of row "+getRest()) so why does it still execute? I want only the first row of the output to be printed.
sys.exit raises a SystemExit exception. You are catching it with your except clause.
What you should do instead is catch a more specific class of exceptions, which does not include SystemExit.
Catching Exception will work:
def readGroup():
if q.peek() in string.ascii_uppercase or q.peek() in string.ascii_lowercase:
try:
readAtom()
except Exception:
print("Unknown atom at end of row "+getRest())
sys.exit()
You can learn more about exceptions and SystemExit in the docs.
Note that you should ideally catch something more specific than Exception (which is very broad, and may catch exceptions you don't intend to catch).
Because in python exit event is processed as SystemExit exception

Categories