Python Calculator using Postfix ExpressionsA simple python3 calculatorCalculator parsing S-expressionsCommand line reverse polish calculatorSimple C++ calculator which follows BOMDAS rulesCalculator that parses S-expressionsCalculator using tokens of numbers and operatorsPostfix calculatorOverhauled tokenizer for markargsInfix to RPN converter using the Shunting-Yard AlgorithmSimple calculator to evaluate arithmetic expressionsPostfix calculator in Java

Can Jimmy hang on his rope?

What is this little owl-like bird?

Elf (adjective) vs. Elvish vs. Elven

How quality assurance engineers test calculations?

Is it possible to split a vertex?

OR-backed serious games

Integer Lists of Noah

Is there a nice way to implement a conditional type with default fail case?

How to drill holes in 3/8" steel plates?

How do you move up one folder in Finder?

How do native German speakers usually express skepticism (using even) about a premise?

Can I play a mimic PC?

What is the right approach to quit a job during probation period for a competing offer?

How effective would wooden scale armor be in a medieval setting?

What's it called when the bad guy gets eaten?

How are mathematicians paid to do research?

What minifigure is this?

What is the parallel of Day of the Dead with Stranger things?

What do three diagonal dots above a letter mean in the "Misal rico de Cisneros" (Spain, 1518)?

Is a request to book a business flight ticket for a graduate student an unreasonable one?

Misspelling my name on my mathematical publications

What is the minimum time required for final wash in film development?

What is /bin/red

The joke office



Python Calculator using Postfix Expressions


A simple python3 calculatorCalculator parsing S-expressionsCommand line reverse polish calculatorSimple C++ calculator which follows BOMDAS rulesCalculator that parses S-expressionsCalculator using tokens of numbers and operatorsPostfix calculatorOverhauled tokenizer for markargsInfix to RPN converter using the Shunting-Yard AlgorithmSimple calculator to evaluate arithmetic expressionsPostfix calculator in Java






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








3












$begingroup$


Improved upon my previous calculator program. This program has the following features:



  1. Gives information using session prompts.


  2. Supports all common operators.


  3. Follows operator precedence.


  4. Use of identifiers is supported.


  5. Result of the previous expression can accessed by using the 'r'
    identifier.



  6. Special commands:



    1. n: Stars a new session. Deletes all previous identifiers.

    2. q: Quits the program


The code describes the features in more detail.



Issues:



  1. No useful information is given to the user if any error occurred.

Other Information:



  1. Ditched the earlier use of eval() to evaluate expressions because of security reasons.


  2. Avoids the endless function call chain used earlier that could hit the recursion limit albeit after a long while by using a loop.


My Previous calculator: A simple python3 calculator



Also uploaded the code on pastebin: https://pastebin.com/WW25hWn7



import sys
import math

usage_notes = """
1. Session prompts:
1. n: New session
2. c: Current session

2. Supported operators: +, -, *, /, ^, !

3. Precedence:
1. Parenthesization
2. Factorial
3. Exponentiation
4. Multiplication and Divison
5. Addition and Subtraction

4. Use of identifiers is supported. Use commas to separate them:
n: a=10,b=5
c: a+b
-> 15

5. Result of the previous expression can accessed by using the 'r'
identifier:
n: 2+3
-> 5
c: r+10
-> 15

6. Special commands:
1. n: Stars a new session. Deletes all previous identifiers.
2. q: Quits the program
"""

identifiers =


def start():
# Creates a prompt dictionary to differentiate sessions.
# Starts the main loop of the program.
# Takes the input from the user and calls controller().

prompts =
'n': 'n: ',
'c': 'c: ',


prompt = prompts['n']
while True:
expr = input(prompt)
if expr == 'q':
break
elif expr == 'n':
prompt = prompts['n']
identifiers.clear()
else:
res = controller(expr)

if res == 'e':
print('error: invalid expressionn')
elif res == 'i':
prompt = prompts['c']
else:
print('-> ' + identifiers['r'] + 'n')
prompt = prompts['c']


def controller(expr):
# Calls create_identifiers or passes the expr to get_postfix()
# to be converted into a postfix expression list. And, calls
# postfix_eval() for evaluation. All the Exceptions
# are terminated, so the main loop keeps running.

try:
if '=' in expr:
return create_identifiers(expr)
postfix_expr = get_postfix(expr)
return postfix_eval(postfix_expr)
except Exception:
return 'e'


def create_identifiers(expr):
# Identifiers are implemented as a global dictionary. First,
# the string is split using ',' as a delimiter. The resulting
# substring are separated using '='. First substring is assigned
# as a key with second substring as the value.

expr_list = expr.replace(' ', '').split(',')

for stmt in expr_list:
var, val = stmt.split('=')
identifiers[var] = val
return 'i'


def get_postfix(expr):
# Converts infix expressions to postfix expressions to remove ambiguity.
# Example: a+b*c -> abc*+

# Remove all the spaces in the given expression.
expr = expr.replace(' ', '')
sep_str = ''

# Insert spaces only around supported operators, so splitting
# can be done easily later.
for a_char in expr:
if a_char in '+-*/^!()':
sep_str += ' %s ' % a_char
else:
sep_str += a_char

# Use the default space as the delimiter and split the string.
token_list = sep_str.split()

# Only operators are pushed on to the op_stack, digits and identifiers
# are appended to the postfix_list.
op_stack = []
postfix_list = []

prec =
prec['!'] = 5
prec['^'] = 4
prec['/'] = 3
prec['*'] = 3
prec['+'] = 2
prec['-'] = 2
prec['('] = 1

# The current operator's precedence in the loop is compared with the
# operators in the stack. If it's higher, it's pushed on the stack.

# If it less than or equal, the operators are popped until the
# precedence of the operator at the top is less than the
# current operators'.

# When parentheses are used, ')' forces all the operators above '('
# to be popped.

# Whenever an operator is popped it's appended to the postfix_list.
for token in token_list:
if isnum(token) or token.isalpha():
postfix_list.append(token)
elif token == '(':
op_stack.append(token)
elif token == ')':
top_token = op_stack.pop()
while top_token != '(':
postfix_list.append(top_token)
top_token = op_stack.pop()
else:
while op_stack != [] and
(prec[op_stack[-1]] >= prec[token]):
postfix_list.append(op_stack.pop())
op_stack.append(token)

while op_stack != []:
postfix_list.append(op_stack.pop())

return postfix_list


def postfix_eval(postfix_list):
# Similar stack based approach is used here for evaluation. If a
# identifier or digit is found, push it on the operand_stack. If
# an operator is found, use it on the last two operands or the last
# in case of '!', and append the result on the stack.

operand_stack = []

for val in postfix_list:
if isnum(val):
operand_stack.append(float(val))
elif val.isalpha():
val = identifiers[val]
operand_stack.append(float(val))
elif val in '+-*/^!':

if val != '!':
op2 = operand_stack.pop()
op1 = operand_stack.pop()
res = calc(op1, val, op2)
operand_stack.append(res)
else:
op = operand_stack.pop()
res = math.factorial(op)
operand_stack.append(res)

res = operand_stack[-1]
int_res = int(res)
if int_res == res:
res = int_res

identifiers['r'] = str(res)


def isnum(val):
# Used as a helper function to check if the argument is a number.
try:
float(val)
return True
except Exception:
return False


def calc(op1, op, op2):
# Performs the operation on the operands and returns the result.
if op == '+':
return op1 + op2
elif op == '-':
return op1 - op2
elif op == '*':
return op1 * op2
elif op == '/':
return op1 / op2
elif op == '^':
return op1 ** op2

if sys.argv[-1] == 'n':
print(usage_notes)

start()



Sample output:



n: 1+5
-> 6

c: r
-> 6

c: r*10+50*(3-1)-100
-> 60

c: r
-> 60

c: a=50,b=10
c: r+a+b
-> 120

c: n
n: 2^3+5!
-> 128

c: 1 +- 2
error: invalid expression

c: q



I am looking for ways to make the code more readable, minimize redundancy, improving the overall design, performance improvements, etc. Any help would be appreciated!










share|improve this question











$endgroup$







  • 1




    $begingroup$
    Those don't look like postfix expressions to me.
    $endgroup$
    – 200_success
    6 hours ago

















3












$begingroup$


Improved upon my previous calculator program. This program has the following features:



  1. Gives information using session prompts.


  2. Supports all common operators.


  3. Follows operator precedence.


  4. Use of identifiers is supported.


  5. Result of the previous expression can accessed by using the 'r'
    identifier.



  6. Special commands:



    1. n: Stars a new session. Deletes all previous identifiers.

    2. q: Quits the program


The code describes the features in more detail.



Issues:



  1. No useful information is given to the user if any error occurred.

Other Information:



  1. Ditched the earlier use of eval() to evaluate expressions because of security reasons.


  2. Avoids the endless function call chain used earlier that could hit the recursion limit albeit after a long while by using a loop.


My Previous calculator: A simple python3 calculator



Also uploaded the code on pastebin: https://pastebin.com/WW25hWn7



import sys
import math

usage_notes = """
1. Session prompts:
1. n: New session
2. c: Current session

2. Supported operators: +, -, *, /, ^, !

3. Precedence:
1. Parenthesization
2. Factorial
3. Exponentiation
4. Multiplication and Divison
5. Addition and Subtraction

4. Use of identifiers is supported. Use commas to separate them:
n: a=10,b=5
c: a+b
-> 15

5. Result of the previous expression can accessed by using the 'r'
identifier:
n: 2+3
-> 5
c: r+10
-> 15

6. Special commands:
1. n: Stars a new session. Deletes all previous identifiers.
2. q: Quits the program
"""

identifiers =


def start():
# Creates a prompt dictionary to differentiate sessions.
# Starts the main loop of the program.
# Takes the input from the user and calls controller().

prompts =
'n': 'n: ',
'c': 'c: ',


prompt = prompts['n']
while True:
expr = input(prompt)
if expr == 'q':
break
elif expr == 'n':
prompt = prompts['n']
identifiers.clear()
else:
res = controller(expr)

if res == 'e':
print('error: invalid expressionn')
elif res == 'i':
prompt = prompts['c']
else:
print('-> ' + identifiers['r'] + 'n')
prompt = prompts['c']


def controller(expr):
# Calls create_identifiers or passes the expr to get_postfix()
# to be converted into a postfix expression list. And, calls
# postfix_eval() for evaluation. All the Exceptions
# are terminated, so the main loop keeps running.

try:
if '=' in expr:
return create_identifiers(expr)
postfix_expr = get_postfix(expr)
return postfix_eval(postfix_expr)
except Exception:
return 'e'


def create_identifiers(expr):
# Identifiers are implemented as a global dictionary. First,
# the string is split using ',' as a delimiter. The resulting
# substring are separated using '='. First substring is assigned
# as a key with second substring as the value.

expr_list = expr.replace(' ', '').split(',')

for stmt in expr_list:
var, val = stmt.split('=')
identifiers[var] = val
return 'i'


def get_postfix(expr):
# Converts infix expressions to postfix expressions to remove ambiguity.
# Example: a+b*c -> abc*+

# Remove all the spaces in the given expression.
expr = expr.replace(' ', '')
sep_str = ''

# Insert spaces only around supported operators, so splitting
# can be done easily later.
for a_char in expr:
if a_char in '+-*/^!()':
sep_str += ' %s ' % a_char
else:
sep_str += a_char

# Use the default space as the delimiter and split the string.
token_list = sep_str.split()

# Only operators are pushed on to the op_stack, digits and identifiers
# are appended to the postfix_list.
op_stack = []
postfix_list = []

prec =
prec['!'] = 5
prec['^'] = 4
prec['/'] = 3
prec['*'] = 3
prec['+'] = 2
prec['-'] = 2
prec['('] = 1

# The current operator's precedence in the loop is compared with the
# operators in the stack. If it's higher, it's pushed on the stack.

# If it less than or equal, the operators are popped until the
# precedence of the operator at the top is less than the
# current operators'.

# When parentheses are used, ')' forces all the operators above '('
# to be popped.

# Whenever an operator is popped it's appended to the postfix_list.
for token in token_list:
if isnum(token) or token.isalpha():
postfix_list.append(token)
elif token == '(':
op_stack.append(token)
elif token == ')':
top_token = op_stack.pop()
while top_token != '(':
postfix_list.append(top_token)
top_token = op_stack.pop()
else:
while op_stack != [] and
(prec[op_stack[-1]] >= prec[token]):
postfix_list.append(op_stack.pop())
op_stack.append(token)

while op_stack != []:
postfix_list.append(op_stack.pop())

return postfix_list


def postfix_eval(postfix_list):
# Similar stack based approach is used here for evaluation. If a
# identifier or digit is found, push it on the operand_stack. If
# an operator is found, use it on the last two operands or the last
# in case of '!', and append the result on the stack.

operand_stack = []

for val in postfix_list:
if isnum(val):
operand_stack.append(float(val))
elif val.isalpha():
val = identifiers[val]
operand_stack.append(float(val))
elif val in '+-*/^!':

if val != '!':
op2 = operand_stack.pop()
op1 = operand_stack.pop()
res = calc(op1, val, op2)
operand_stack.append(res)
else:
op = operand_stack.pop()
res = math.factorial(op)
operand_stack.append(res)

res = operand_stack[-1]
int_res = int(res)
if int_res == res:
res = int_res

identifiers['r'] = str(res)


def isnum(val):
# Used as a helper function to check if the argument is a number.
try:
float(val)
return True
except Exception:
return False


def calc(op1, op, op2):
# Performs the operation on the operands and returns the result.
if op == '+':
return op1 + op2
elif op == '-':
return op1 - op2
elif op == '*':
return op1 * op2
elif op == '/':
return op1 / op2
elif op == '^':
return op1 ** op2

if sys.argv[-1] == 'n':
print(usage_notes)

start()



Sample output:



n: 1+5
-> 6

c: r
-> 6

c: r*10+50*(3-1)-100
-> 60

c: r
-> 60

c: a=50,b=10
c: r+a+b
-> 120

c: n
n: 2^3+5!
-> 128

c: 1 +- 2
error: invalid expression

c: q



I am looking for ways to make the code more readable, minimize redundancy, improving the overall design, performance improvements, etc. Any help would be appreciated!










share|improve this question











$endgroup$







  • 1




    $begingroup$
    Those don't look like postfix expressions to me.
    $endgroup$
    – 200_success
    6 hours ago













3












3








3


1



$begingroup$


Improved upon my previous calculator program. This program has the following features:



  1. Gives information using session prompts.


  2. Supports all common operators.


  3. Follows operator precedence.


  4. Use of identifiers is supported.


  5. Result of the previous expression can accessed by using the 'r'
    identifier.



  6. Special commands:



    1. n: Stars a new session. Deletes all previous identifiers.

    2. q: Quits the program


The code describes the features in more detail.



Issues:



  1. No useful information is given to the user if any error occurred.

Other Information:



  1. Ditched the earlier use of eval() to evaluate expressions because of security reasons.


  2. Avoids the endless function call chain used earlier that could hit the recursion limit albeit after a long while by using a loop.


My Previous calculator: A simple python3 calculator



Also uploaded the code on pastebin: https://pastebin.com/WW25hWn7



import sys
import math

usage_notes = """
1. Session prompts:
1. n: New session
2. c: Current session

2. Supported operators: +, -, *, /, ^, !

3. Precedence:
1. Parenthesization
2. Factorial
3. Exponentiation
4. Multiplication and Divison
5. Addition and Subtraction

4. Use of identifiers is supported. Use commas to separate them:
n: a=10,b=5
c: a+b
-> 15

5. Result of the previous expression can accessed by using the 'r'
identifier:
n: 2+3
-> 5
c: r+10
-> 15

6. Special commands:
1. n: Stars a new session. Deletes all previous identifiers.
2. q: Quits the program
"""

identifiers =


def start():
# Creates a prompt dictionary to differentiate sessions.
# Starts the main loop of the program.
# Takes the input from the user and calls controller().

prompts =
'n': 'n: ',
'c': 'c: ',


prompt = prompts['n']
while True:
expr = input(prompt)
if expr == 'q':
break
elif expr == 'n':
prompt = prompts['n']
identifiers.clear()
else:
res = controller(expr)

if res == 'e':
print('error: invalid expressionn')
elif res == 'i':
prompt = prompts['c']
else:
print('-> ' + identifiers['r'] + 'n')
prompt = prompts['c']


def controller(expr):
# Calls create_identifiers or passes the expr to get_postfix()
# to be converted into a postfix expression list. And, calls
# postfix_eval() for evaluation. All the Exceptions
# are terminated, so the main loop keeps running.

try:
if '=' in expr:
return create_identifiers(expr)
postfix_expr = get_postfix(expr)
return postfix_eval(postfix_expr)
except Exception:
return 'e'


def create_identifiers(expr):
# Identifiers are implemented as a global dictionary. First,
# the string is split using ',' as a delimiter. The resulting
# substring are separated using '='. First substring is assigned
# as a key with second substring as the value.

expr_list = expr.replace(' ', '').split(',')

for stmt in expr_list:
var, val = stmt.split('=')
identifiers[var] = val
return 'i'


def get_postfix(expr):
# Converts infix expressions to postfix expressions to remove ambiguity.
# Example: a+b*c -> abc*+

# Remove all the spaces in the given expression.
expr = expr.replace(' ', '')
sep_str = ''

# Insert spaces only around supported operators, so splitting
# can be done easily later.
for a_char in expr:
if a_char in '+-*/^!()':
sep_str += ' %s ' % a_char
else:
sep_str += a_char

# Use the default space as the delimiter and split the string.
token_list = sep_str.split()

# Only operators are pushed on to the op_stack, digits and identifiers
# are appended to the postfix_list.
op_stack = []
postfix_list = []

prec =
prec['!'] = 5
prec['^'] = 4
prec['/'] = 3
prec['*'] = 3
prec['+'] = 2
prec['-'] = 2
prec['('] = 1

# The current operator's precedence in the loop is compared with the
# operators in the stack. If it's higher, it's pushed on the stack.

# If it less than or equal, the operators are popped until the
# precedence of the operator at the top is less than the
# current operators'.

# When parentheses are used, ')' forces all the operators above '('
# to be popped.

# Whenever an operator is popped it's appended to the postfix_list.
for token in token_list:
if isnum(token) or token.isalpha():
postfix_list.append(token)
elif token == '(':
op_stack.append(token)
elif token == ')':
top_token = op_stack.pop()
while top_token != '(':
postfix_list.append(top_token)
top_token = op_stack.pop()
else:
while op_stack != [] and
(prec[op_stack[-1]] >= prec[token]):
postfix_list.append(op_stack.pop())
op_stack.append(token)

while op_stack != []:
postfix_list.append(op_stack.pop())

return postfix_list


def postfix_eval(postfix_list):
# Similar stack based approach is used here for evaluation. If a
# identifier or digit is found, push it on the operand_stack. If
# an operator is found, use it on the last two operands or the last
# in case of '!', and append the result on the stack.

operand_stack = []

for val in postfix_list:
if isnum(val):
operand_stack.append(float(val))
elif val.isalpha():
val = identifiers[val]
operand_stack.append(float(val))
elif val in '+-*/^!':

if val != '!':
op2 = operand_stack.pop()
op1 = operand_stack.pop()
res = calc(op1, val, op2)
operand_stack.append(res)
else:
op = operand_stack.pop()
res = math.factorial(op)
operand_stack.append(res)

res = operand_stack[-1]
int_res = int(res)
if int_res == res:
res = int_res

identifiers['r'] = str(res)


def isnum(val):
# Used as a helper function to check if the argument is a number.
try:
float(val)
return True
except Exception:
return False


def calc(op1, op, op2):
# Performs the operation on the operands and returns the result.
if op == '+':
return op1 + op2
elif op == '-':
return op1 - op2
elif op == '*':
return op1 * op2
elif op == '/':
return op1 / op2
elif op == '^':
return op1 ** op2

if sys.argv[-1] == 'n':
print(usage_notes)

start()



Sample output:



n: 1+5
-> 6

c: r
-> 6

c: r*10+50*(3-1)-100
-> 60

c: r
-> 60

c: a=50,b=10
c: r+a+b
-> 120

c: n
n: 2^3+5!
-> 128

c: 1 +- 2
error: invalid expression

c: q



I am looking for ways to make the code more readable, minimize redundancy, improving the overall design, performance improvements, etc. Any help would be appreciated!










share|improve this question











$endgroup$




Improved upon my previous calculator program. This program has the following features:



  1. Gives information using session prompts.


  2. Supports all common operators.


  3. Follows operator precedence.


  4. Use of identifiers is supported.


  5. Result of the previous expression can accessed by using the 'r'
    identifier.



  6. Special commands:



    1. n: Stars a new session. Deletes all previous identifiers.

    2. q: Quits the program


The code describes the features in more detail.



Issues:



  1. No useful information is given to the user if any error occurred.

Other Information:



  1. Ditched the earlier use of eval() to evaluate expressions because of security reasons.


  2. Avoids the endless function call chain used earlier that could hit the recursion limit albeit after a long while by using a loop.


My Previous calculator: A simple python3 calculator



Also uploaded the code on pastebin: https://pastebin.com/WW25hWn7



import sys
import math

usage_notes = """
1. Session prompts:
1. n: New session
2. c: Current session

2. Supported operators: +, -, *, /, ^, !

3. Precedence:
1. Parenthesization
2. Factorial
3. Exponentiation
4. Multiplication and Divison
5. Addition and Subtraction

4. Use of identifiers is supported. Use commas to separate them:
n: a=10,b=5
c: a+b
-> 15

5. Result of the previous expression can accessed by using the 'r'
identifier:
n: 2+3
-> 5
c: r+10
-> 15

6. Special commands:
1. n: Stars a new session. Deletes all previous identifiers.
2. q: Quits the program
"""

identifiers =


def start():
# Creates a prompt dictionary to differentiate sessions.
# Starts the main loop of the program.
# Takes the input from the user and calls controller().

prompts =
'n': 'n: ',
'c': 'c: ',


prompt = prompts['n']
while True:
expr = input(prompt)
if expr == 'q':
break
elif expr == 'n':
prompt = prompts['n']
identifiers.clear()
else:
res = controller(expr)

if res == 'e':
print('error: invalid expressionn')
elif res == 'i':
prompt = prompts['c']
else:
print('-> ' + identifiers['r'] + 'n')
prompt = prompts['c']


def controller(expr):
# Calls create_identifiers or passes the expr to get_postfix()
# to be converted into a postfix expression list. And, calls
# postfix_eval() for evaluation. All the Exceptions
# are terminated, so the main loop keeps running.

try:
if '=' in expr:
return create_identifiers(expr)
postfix_expr = get_postfix(expr)
return postfix_eval(postfix_expr)
except Exception:
return 'e'


def create_identifiers(expr):
# Identifiers are implemented as a global dictionary. First,
# the string is split using ',' as a delimiter. The resulting
# substring are separated using '='. First substring is assigned
# as a key with second substring as the value.

expr_list = expr.replace(' ', '').split(',')

for stmt in expr_list:
var, val = stmt.split('=')
identifiers[var] = val
return 'i'


def get_postfix(expr):
# Converts infix expressions to postfix expressions to remove ambiguity.
# Example: a+b*c -> abc*+

# Remove all the spaces in the given expression.
expr = expr.replace(' ', '')
sep_str = ''

# Insert spaces only around supported operators, so splitting
# can be done easily later.
for a_char in expr:
if a_char in '+-*/^!()':
sep_str += ' %s ' % a_char
else:
sep_str += a_char

# Use the default space as the delimiter and split the string.
token_list = sep_str.split()

# Only operators are pushed on to the op_stack, digits and identifiers
# are appended to the postfix_list.
op_stack = []
postfix_list = []

prec =
prec['!'] = 5
prec['^'] = 4
prec['/'] = 3
prec['*'] = 3
prec['+'] = 2
prec['-'] = 2
prec['('] = 1

# The current operator's precedence in the loop is compared with the
# operators in the stack. If it's higher, it's pushed on the stack.

# If it less than or equal, the operators are popped until the
# precedence of the operator at the top is less than the
# current operators'.

# When parentheses are used, ')' forces all the operators above '('
# to be popped.

# Whenever an operator is popped it's appended to the postfix_list.
for token in token_list:
if isnum(token) or token.isalpha():
postfix_list.append(token)
elif token == '(':
op_stack.append(token)
elif token == ')':
top_token = op_stack.pop()
while top_token != '(':
postfix_list.append(top_token)
top_token = op_stack.pop()
else:
while op_stack != [] and
(prec[op_stack[-1]] >= prec[token]):
postfix_list.append(op_stack.pop())
op_stack.append(token)

while op_stack != []:
postfix_list.append(op_stack.pop())

return postfix_list


def postfix_eval(postfix_list):
# Similar stack based approach is used here for evaluation. If a
# identifier or digit is found, push it on the operand_stack. If
# an operator is found, use it on the last two operands or the last
# in case of '!', and append the result on the stack.

operand_stack = []

for val in postfix_list:
if isnum(val):
operand_stack.append(float(val))
elif val.isalpha():
val = identifiers[val]
operand_stack.append(float(val))
elif val in '+-*/^!':

if val != '!':
op2 = operand_stack.pop()
op1 = operand_stack.pop()
res = calc(op1, val, op2)
operand_stack.append(res)
else:
op = operand_stack.pop()
res = math.factorial(op)
operand_stack.append(res)

res = operand_stack[-1]
int_res = int(res)
if int_res == res:
res = int_res

identifiers['r'] = str(res)


def isnum(val):
# Used as a helper function to check if the argument is a number.
try:
float(val)
return True
except Exception:
return False


def calc(op1, op, op2):
# Performs the operation on the operands and returns the result.
if op == '+':
return op1 + op2
elif op == '-':
return op1 - op2
elif op == '*':
return op1 * op2
elif op == '/':
return op1 / op2
elif op == '^':
return op1 ** op2

if sys.argv[-1] == 'n':
print(usage_notes)

start()



Sample output:



n: 1+5
-> 6

c: r
-> 6

c: r*10+50*(3-1)-100
-> 60

c: r
-> 60

c: a=50,b=10
c: r+a+b
-> 120

c: n
n: 2^3+5!
-> 128

c: 1 +- 2
error: invalid expression

c: q



I am looking for ways to make the code more readable, minimize redundancy, improving the overall design, performance improvements, etc. Any help would be appreciated!







python python-3.x calculator math-expression-eval






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 6 hours ago









200_success

134k21 gold badges171 silver badges441 bronze badges




134k21 gold badges171 silver badges441 bronze badges










asked 8 hours ago









sg7610sg7610

433 bronze badges




433 bronze badges







  • 1




    $begingroup$
    Those don't look like postfix expressions to me.
    $endgroup$
    – 200_success
    6 hours ago












  • 1




    $begingroup$
    Those don't look like postfix expressions to me.
    $endgroup$
    – 200_success
    6 hours ago







1




1




$begingroup$
Those don't look like postfix expressions to me.
$endgroup$
– 200_success
6 hours ago




$begingroup$
Those don't look like postfix expressions to me.
$endgroup$
– 200_success
6 hours ago










1 Answer
1






active

oldest

votes


















2












$begingroup$

First, the most glaring thing I see is your use of catch-all exception handlers:



def controller(expr):
. . .
# . . . All the Exceptions
# are terminated, so the main loop keeps running.
. . .
except Exception:
return 'e'

. . .

res = controller(expr)

if res == 'e':
print('error: invalid expressionn')


I have to strongly caution against this practice. You're treating every exception that may possibly come up as though it's caused by a runtime error stemming from bad user input. This is a very dangerous assumption, and will bite you eventually.



In this case, why is it such a big deal? Inside the try, you're calling three different functions, and all of those functions call several other functions. Lets say in the future you decide to modify one of the many functions that are called as a result of calling controller, and lets say you accidentally introduce a bug. In a normal workflow, you'd try to run controller as a test, it would fail horribly because you wrote something incorrect somewhere, and you can use the resulting stack trace to diagnose what caused the problem.



With how you have it here, you would run it... and it would complain about an invalid expression being entered. How much information does that give you about what happened? Throwing away the clues that help you debug a problem will only make your life more difficult once you start writing larger programs and start encountering hard-to-reproduce bugs.



The better way to approach this is to specify the exact exception that you expect to catch instead of using a catch-all Exception. If you expect a ValueError to be thrown if the expression was poorly formed, catch that. That way any other exception that may be raised will still come through. A broken program crashing is a good thing. Let it fail so you can fix it.



The same problem, but to a lesser extent can be seen in isnum (which I'd rename to at least is_num):



def is_num(val):
# Used as a helper function to check if the argument is a number.
try:
float(val)
return True
except Exception:
return False


float seems to only throw two types of exceptions; and only one of them seems relevant here. Change the catch to except ValueError. This isn't a big deal right now since only the call to float is inside the try, but if you add anything later you're opening yourself up to silent failings.



In this code, catch-all exceptions won't be the end of the world. They are a bad habit to get into though, and don't encourage a safe mindset. Be aware of what exceptions the code you're using can throw and react accordingly. Catching everything is just a band-aid.



I'd also space your code out a bit. I personally like empty lines after "bodies" of code, like the bodies of a if...else, or a try...except:



def is_num(val):
try:
float(val)
return True

except Exception:
return False

def controller(expr):
try:
if '=' in expr:
return create_identifiers(expr)

postfix_expr = get_postfix(expr)
return postfix_eval(postfix_expr)

except Exception:
return 'e'


I like giving discrete parts some breathing room. I find it helps readability.




prec = 
prec['!'] = 5
prec['^'] = 4
prec['/'] = 3
prec['*'] = 3
prec['+'] = 2
prec['-'] = 2
prec['('] = 1


This could be written as a literal, and I think it would be neater as a result:



prec = '!': 5,
'^': 4,
'/': 3,
'*': 3,
'+': 2,
'-': 2,
'(': 1



Your use of global identifiers isn't ideal. I'd prefer to pass a state around using an explicit parameter to any functions that require access to identifiers. That would make testing functions that use identifiers much easier. With how you have it now, whenever you want to test a function like postfix_eval that uses identifiers, you need to make sure to do identifiers = some_test_state before your call. If it were a parameter, its dependencies would be explicit, and it wouldn't require accessing a global mutable state.




A lot of your functions start with some comments that describe the action of the function:



def calc(op1, op, op2):
# Performs the operation on the operands and returns the result.


This is a good habit to get in. Python has a standardized convention though to handle comments intended for the end-user of the function: docstrings. Right after the function "signature", have a (triple) String literal instead of using # line comments. IDEs will grab this information and allow it to be accessed easier by callers.



def calc(op1, op, op2):
""" Performs the operation on the operands and returns the result. """





share|improve this answer











$endgroup$















    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f223772%2fpython-calculator-using-postfix-expressions%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2












    $begingroup$

    First, the most glaring thing I see is your use of catch-all exception handlers:



    def controller(expr):
    . . .
    # . . . All the Exceptions
    # are terminated, so the main loop keeps running.
    . . .
    except Exception:
    return 'e'

    . . .

    res = controller(expr)

    if res == 'e':
    print('error: invalid expressionn')


    I have to strongly caution against this practice. You're treating every exception that may possibly come up as though it's caused by a runtime error stemming from bad user input. This is a very dangerous assumption, and will bite you eventually.



    In this case, why is it such a big deal? Inside the try, you're calling three different functions, and all of those functions call several other functions. Lets say in the future you decide to modify one of the many functions that are called as a result of calling controller, and lets say you accidentally introduce a bug. In a normal workflow, you'd try to run controller as a test, it would fail horribly because you wrote something incorrect somewhere, and you can use the resulting stack trace to diagnose what caused the problem.



    With how you have it here, you would run it... and it would complain about an invalid expression being entered. How much information does that give you about what happened? Throwing away the clues that help you debug a problem will only make your life more difficult once you start writing larger programs and start encountering hard-to-reproduce bugs.



    The better way to approach this is to specify the exact exception that you expect to catch instead of using a catch-all Exception. If you expect a ValueError to be thrown if the expression was poorly formed, catch that. That way any other exception that may be raised will still come through. A broken program crashing is a good thing. Let it fail so you can fix it.



    The same problem, but to a lesser extent can be seen in isnum (which I'd rename to at least is_num):



    def is_num(val):
    # Used as a helper function to check if the argument is a number.
    try:
    float(val)
    return True
    except Exception:
    return False


    float seems to only throw two types of exceptions; and only one of them seems relevant here. Change the catch to except ValueError. This isn't a big deal right now since only the call to float is inside the try, but if you add anything later you're opening yourself up to silent failings.



    In this code, catch-all exceptions won't be the end of the world. They are a bad habit to get into though, and don't encourage a safe mindset. Be aware of what exceptions the code you're using can throw and react accordingly. Catching everything is just a band-aid.



    I'd also space your code out a bit. I personally like empty lines after "bodies" of code, like the bodies of a if...else, or a try...except:



    def is_num(val):
    try:
    float(val)
    return True

    except Exception:
    return False

    def controller(expr):
    try:
    if '=' in expr:
    return create_identifiers(expr)

    postfix_expr = get_postfix(expr)
    return postfix_eval(postfix_expr)

    except Exception:
    return 'e'


    I like giving discrete parts some breathing room. I find it helps readability.




    prec = 
    prec['!'] = 5
    prec['^'] = 4
    prec['/'] = 3
    prec['*'] = 3
    prec['+'] = 2
    prec['-'] = 2
    prec['('] = 1


    This could be written as a literal, and I think it would be neater as a result:



    prec = '!': 5,
    '^': 4,
    '/': 3,
    '*': 3,
    '+': 2,
    '-': 2,
    '(': 1



    Your use of global identifiers isn't ideal. I'd prefer to pass a state around using an explicit parameter to any functions that require access to identifiers. That would make testing functions that use identifiers much easier. With how you have it now, whenever you want to test a function like postfix_eval that uses identifiers, you need to make sure to do identifiers = some_test_state before your call. If it were a parameter, its dependencies would be explicit, and it wouldn't require accessing a global mutable state.




    A lot of your functions start with some comments that describe the action of the function:



    def calc(op1, op, op2):
    # Performs the operation on the operands and returns the result.


    This is a good habit to get in. Python has a standardized convention though to handle comments intended for the end-user of the function: docstrings. Right after the function "signature", have a (triple) String literal instead of using # line comments. IDEs will grab this information and allow it to be accessed easier by callers.



    def calc(op1, op, op2):
    """ Performs the operation on the operands and returns the result. """





    share|improve this answer











    $endgroup$

















      2












      $begingroup$

      First, the most glaring thing I see is your use of catch-all exception handlers:



      def controller(expr):
      . . .
      # . . . All the Exceptions
      # are terminated, so the main loop keeps running.
      . . .
      except Exception:
      return 'e'

      . . .

      res = controller(expr)

      if res == 'e':
      print('error: invalid expressionn')


      I have to strongly caution against this practice. You're treating every exception that may possibly come up as though it's caused by a runtime error stemming from bad user input. This is a very dangerous assumption, and will bite you eventually.



      In this case, why is it such a big deal? Inside the try, you're calling three different functions, and all of those functions call several other functions. Lets say in the future you decide to modify one of the many functions that are called as a result of calling controller, and lets say you accidentally introduce a bug. In a normal workflow, you'd try to run controller as a test, it would fail horribly because you wrote something incorrect somewhere, and you can use the resulting stack trace to diagnose what caused the problem.



      With how you have it here, you would run it... and it would complain about an invalid expression being entered. How much information does that give you about what happened? Throwing away the clues that help you debug a problem will only make your life more difficult once you start writing larger programs and start encountering hard-to-reproduce bugs.



      The better way to approach this is to specify the exact exception that you expect to catch instead of using a catch-all Exception. If you expect a ValueError to be thrown if the expression was poorly formed, catch that. That way any other exception that may be raised will still come through. A broken program crashing is a good thing. Let it fail so you can fix it.



      The same problem, but to a lesser extent can be seen in isnum (which I'd rename to at least is_num):



      def is_num(val):
      # Used as a helper function to check if the argument is a number.
      try:
      float(val)
      return True
      except Exception:
      return False


      float seems to only throw two types of exceptions; and only one of them seems relevant here. Change the catch to except ValueError. This isn't a big deal right now since only the call to float is inside the try, but if you add anything later you're opening yourself up to silent failings.



      In this code, catch-all exceptions won't be the end of the world. They are a bad habit to get into though, and don't encourage a safe mindset. Be aware of what exceptions the code you're using can throw and react accordingly. Catching everything is just a band-aid.



      I'd also space your code out a bit. I personally like empty lines after "bodies" of code, like the bodies of a if...else, or a try...except:



      def is_num(val):
      try:
      float(val)
      return True

      except Exception:
      return False

      def controller(expr):
      try:
      if '=' in expr:
      return create_identifiers(expr)

      postfix_expr = get_postfix(expr)
      return postfix_eval(postfix_expr)

      except Exception:
      return 'e'


      I like giving discrete parts some breathing room. I find it helps readability.




      prec = 
      prec['!'] = 5
      prec['^'] = 4
      prec['/'] = 3
      prec['*'] = 3
      prec['+'] = 2
      prec['-'] = 2
      prec['('] = 1


      This could be written as a literal, and I think it would be neater as a result:



      prec = '!': 5,
      '^': 4,
      '/': 3,
      '*': 3,
      '+': 2,
      '-': 2,
      '(': 1



      Your use of global identifiers isn't ideal. I'd prefer to pass a state around using an explicit parameter to any functions that require access to identifiers. That would make testing functions that use identifiers much easier. With how you have it now, whenever you want to test a function like postfix_eval that uses identifiers, you need to make sure to do identifiers = some_test_state before your call. If it were a parameter, its dependencies would be explicit, and it wouldn't require accessing a global mutable state.




      A lot of your functions start with some comments that describe the action of the function:



      def calc(op1, op, op2):
      # Performs the operation on the operands and returns the result.


      This is a good habit to get in. Python has a standardized convention though to handle comments intended for the end-user of the function: docstrings. Right after the function "signature", have a (triple) String literal instead of using # line comments. IDEs will grab this information and allow it to be accessed easier by callers.



      def calc(op1, op, op2):
      """ Performs the operation on the operands and returns the result. """





      share|improve this answer











      $endgroup$















        2












        2








        2





        $begingroup$

        First, the most glaring thing I see is your use of catch-all exception handlers:



        def controller(expr):
        . . .
        # . . . All the Exceptions
        # are terminated, so the main loop keeps running.
        . . .
        except Exception:
        return 'e'

        . . .

        res = controller(expr)

        if res == 'e':
        print('error: invalid expressionn')


        I have to strongly caution against this practice. You're treating every exception that may possibly come up as though it's caused by a runtime error stemming from bad user input. This is a very dangerous assumption, and will bite you eventually.



        In this case, why is it such a big deal? Inside the try, you're calling three different functions, and all of those functions call several other functions. Lets say in the future you decide to modify one of the many functions that are called as a result of calling controller, and lets say you accidentally introduce a bug. In a normal workflow, you'd try to run controller as a test, it would fail horribly because you wrote something incorrect somewhere, and you can use the resulting stack trace to diagnose what caused the problem.



        With how you have it here, you would run it... and it would complain about an invalid expression being entered. How much information does that give you about what happened? Throwing away the clues that help you debug a problem will only make your life more difficult once you start writing larger programs and start encountering hard-to-reproduce bugs.



        The better way to approach this is to specify the exact exception that you expect to catch instead of using a catch-all Exception. If you expect a ValueError to be thrown if the expression was poorly formed, catch that. That way any other exception that may be raised will still come through. A broken program crashing is a good thing. Let it fail so you can fix it.



        The same problem, but to a lesser extent can be seen in isnum (which I'd rename to at least is_num):



        def is_num(val):
        # Used as a helper function to check if the argument is a number.
        try:
        float(val)
        return True
        except Exception:
        return False


        float seems to only throw two types of exceptions; and only one of them seems relevant here. Change the catch to except ValueError. This isn't a big deal right now since only the call to float is inside the try, but if you add anything later you're opening yourself up to silent failings.



        In this code, catch-all exceptions won't be the end of the world. They are a bad habit to get into though, and don't encourage a safe mindset. Be aware of what exceptions the code you're using can throw and react accordingly. Catching everything is just a band-aid.



        I'd also space your code out a bit. I personally like empty lines after "bodies" of code, like the bodies of a if...else, or a try...except:



        def is_num(val):
        try:
        float(val)
        return True

        except Exception:
        return False

        def controller(expr):
        try:
        if '=' in expr:
        return create_identifiers(expr)

        postfix_expr = get_postfix(expr)
        return postfix_eval(postfix_expr)

        except Exception:
        return 'e'


        I like giving discrete parts some breathing room. I find it helps readability.




        prec = 
        prec['!'] = 5
        prec['^'] = 4
        prec['/'] = 3
        prec['*'] = 3
        prec['+'] = 2
        prec['-'] = 2
        prec['('] = 1


        This could be written as a literal, and I think it would be neater as a result:



        prec = '!': 5,
        '^': 4,
        '/': 3,
        '*': 3,
        '+': 2,
        '-': 2,
        '(': 1



        Your use of global identifiers isn't ideal. I'd prefer to pass a state around using an explicit parameter to any functions that require access to identifiers. That would make testing functions that use identifiers much easier. With how you have it now, whenever you want to test a function like postfix_eval that uses identifiers, you need to make sure to do identifiers = some_test_state before your call. If it were a parameter, its dependencies would be explicit, and it wouldn't require accessing a global mutable state.




        A lot of your functions start with some comments that describe the action of the function:



        def calc(op1, op, op2):
        # Performs the operation on the operands and returns the result.


        This is a good habit to get in. Python has a standardized convention though to handle comments intended for the end-user of the function: docstrings. Right after the function "signature", have a (triple) String literal instead of using # line comments. IDEs will grab this information and allow it to be accessed easier by callers.



        def calc(op1, op, op2):
        """ Performs the operation on the operands and returns the result. """





        share|improve this answer











        $endgroup$



        First, the most glaring thing I see is your use of catch-all exception handlers:



        def controller(expr):
        . . .
        # . . . All the Exceptions
        # are terminated, so the main loop keeps running.
        . . .
        except Exception:
        return 'e'

        . . .

        res = controller(expr)

        if res == 'e':
        print('error: invalid expressionn')


        I have to strongly caution against this practice. You're treating every exception that may possibly come up as though it's caused by a runtime error stemming from bad user input. This is a very dangerous assumption, and will bite you eventually.



        In this case, why is it such a big deal? Inside the try, you're calling three different functions, and all of those functions call several other functions. Lets say in the future you decide to modify one of the many functions that are called as a result of calling controller, and lets say you accidentally introduce a bug. In a normal workflow, you'd try to run controller as a test, it would fail horribly because you wrote something incorrect somewhere, and you can use the resulting stack trace to diagnose what caused the problem.



        With how you have it here, you would run it... and it would complain about an invalid expression being entered. How much information does that give you about what happened? Throwing away the clues that help you debug a problem will only make your life more difficult once you start writing larger programs and start encountering hard-to-reproduce bugs.



        The better way to approach this is to specify the exact exception that you expect to catch instead of using a catch-all Exception. If you expect a ValueError to be thrown if the expression was poorly formed, catch that. That way any other exception that may be raised will still come through. A broken program crashing is a good thing. Let it fail so you can fix it.



        The same problem, but to a lesser extent can be seen in isnum (which I'd rename to at least is_num):



        def is_num(val):
        # Used as a helper function to check if the argument is a number.
        try:
        float(val)
        return True
        except Exception:
        return False


        float seems to only throw two types of exceptions; and only one of them seems relevant here. Change the catch to except ValueError. This isn't a big deal right now since only the call to float is inside the try, but if you add anything later you're opening yourself up to silent failings.



        In this code, catch-all exceptions won't be the end of the world. They are a bad habit to get into though, and don't encourage a safe mindset. Be aware of what exceptions the code you're using can throw and react accordingly. Catching everything is just a band-aid.



        I'd also space your code out a bit. I personally like empty lines after "bodies" of code, like the bodies of a if...else, or a try...except:



        def is_num(val):
        try:
        float(val)
        return True

        except Exception:
        return False

        def controller(expr):
        try:
        if '=' in expr:
        return create_identifiers(expr)

        postfix_expr = get_postfix(expr)
        return postfix_eval(postfix_expr)

        except Exception:
        return 'e'


        I like giving discrete parts some breathing room. I find it helps readability.




        prec = 
        prec['!'] = 5
        prec['^'] = 4
        prec['/'] = 3
        prec['*'] = 3
        prec['+'] = 2
        prec['-'] = 2
        prec['('] = 1


        This could be written as a literal, and I think it would be neater as a result:



        prec = '!': 5,
        '^': 4,
        '/': 3,
        '*': 3,
        '+': 2,
        '-': 2,
        '(': 1



        Your use of global identifiers isn't ideal. I'd prefer to pass a state around using an explicit parameter to any functions that require access to identifiers. That would make testing functions that use identifiers much easier. With how you have it now, whenever you want to test a function like postfix_eval that uses identifiers, you need to make sure to do identifiers = some_test_state before your call. If it were a parameter, its dependencies would be explicit, and it wouldn't require accessing a global mutable state.




        A lot of your functions start with some comments that describe the action of the function:



        def calc(op1, op, op2):
        # Performs the operation on the operands and returns the result.


        This is a good habit to get in. Python has a standardized convention though to handle comments intended for the end-user of the function: docstrings. Right after the function "signature", have a (triple) String literal instead of using # line comments. IDEs will grab this information and allow it to be accessed easier by callers.



        def calc(op1, op, op2):
        """ Performs the operation on the operands and returns the result. """






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 1 hour ago

























        answered 7 hours ago









        CarcigenicateCarcigenicate

        5,7521 gold badge18 silver badges39 bronze badges




        5,7521 gold badge18 silver badges39 bronze badges



























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f223772%2fpython-calculator-using-postfix-expressions%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            ParseJSON using SSJSUsing AMPscript with SSJS ActivitiesHow to resubscribe a user in Marketing cloud using SSJS?Pulling Subscriber Status from Lists using SSJSRetrieving Emails using SSJSProblem in updating DE using SSJSUsing SSJS to send single email in Marketing CloudError adding EmailSendDefinition using SSJS

            Кампала Садржај Географија Географија Историја Становништво Привреда Партнерски градови Референце Спољашње везе Мени за навигацију0°11′ СГШ; 32°20′ ИГД / 0.18° СГШ; 32.34° ИГД / 0.18; 32.340°11′ СГШ; 32°20′ ИГД / 0.18° СГШ; 32.34° ИГД / 0.18; 32.34МедијиПодациЗванични веб-сајту

            19. јануар Садржај Догађаји Рођења Смрти Празници и дани сећања Види још Референце Мени за навигацијуу