Created a small utility program to generate JSON files for testing the main input.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"expression": "i + 2 + n",
|
||||
"variables": {
|
||||
"n1": {
|
||||
"name": "n1",
|
||||
"type": "Unknown",
|
||||
"starting_point": 0,
|
||||
"ending_point": 0
|
||||
},
|
||||
"i1": {
|
||||
"name": "i1",
|
||||
"type": "Unknown",
|
||||
"starting_point": 0,
|
||||
"ending_point": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
class Expression:
|
||||
expression = ""
|
||||
variables = []
|
||||
|
||||
def __init__(self, expression_string, variables):
|
||||
self.expression = expression_string
|
||||
self.variables = variables
|
||||
|
||||
class Variable:
|
||||
VARIABLE_TYPES = {
|
||||
"i" : "Interest",
|
||||
"I" : "Interest",
|
||||
"n" : "Time",
|
||||
"N" : "Time"
|
||||
}
|
||||
|
||||
def __init__(self, name, type, starting_point = 0, ending_point = 0):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.starting_point = starting_point
|
||||
self.ending_point = ending_point
|
||||
|
||||
@staticmethod
|
||||
def get_variable_type(variable_name):
|
||||
if not variable_name in Variable.VARIABLE_TYPES:
|
||||
return "Unknown"
|
||||
return Variable.VARIABLE_TYPES[variable_name]
|
||||
@@ -0,0 +1,96 @@
|
||||
import tokenizer
|
||||
import token
|
||||
import expression
|
||||
import json
|
||||
|
||||
variables = {}
|
||||
def print_vars(vars):
|
||||
i = 0
|
||||
for key in vars.keys():
|
||||
var = vars[key]
|
||||
if i != 0:
|
||||
print(", %s" %(var.name), end = "")
|
||||
else:
|
||||
print("%s " %(var.name), end = "")
|
||||
i += 1
|
||||
print()
|
||||
|
||||
def modify_variable(var, vars):
|
||||
print("%s is now selected." %(var.name))
|
||||
print("The following properties are avaliable: Variable Type, Starting Point and Ending Point.")
|
||||
print('Commands are "type", "start" and "end", respectly.')
|
||||
|
||||
while True:
|
||||
response = input("Select a property to modify for %s or q to exit property modifying: " %(var.name))
|
||||
|
||||
if response == "type":
|
||||
response = input("Enter the type of variable this is (currently %s): " %(expression.Variable.get_variable_type(var.type)))
|
||||
vars[var.name].type = response
|
||||
print("Updated to %s." %(response))
|
||||
|
||||
elif response == "start":
|
||||
response = input("Enter the variable's starting point (currently %f): " %(var.starting_point))
|
||||
|
||||
try:
|
||||
vars[var.name].starting_point = float(response)
|
||||
print("Updated to %f." %(float(response)))
|
||||
except ValueError:
|
||||
print("The starting point must be a number.")
|
||||
|
||||
elif response == "end":
|
||||
response = input("Enter the variable's ending point (currently %f): " %(var.ending_point))
|
||||
|
||||
try:
|
||||
vars[var.name].ending_point = float(response)
|
||||
print("Updated to %f." %(float(response)))
|
||||
except ValueError:
|
||||
print("The ending point must be a number.")
|
||||
|
||||
pass
|
||||
|
||||
elif response == "q":
|
||||
return
|
||||
|
||||
else:
|
||||
print('Invalid property "%s".' %(response))
|
||||
print('Commands are "type", "start" and "end".')
|
||||
|
||||
def transform(object):
|
||||
|
||||
if isinstance(object, expression.Expression) or isinstance(object, expression.Variable):
|
||||
|
||||
return object.__dict__
|
||||
|
||||
else:
|
||||
|
||||
raise TypeError("Only Books and Index will be JSON serialized!")
|
||||
|
||||
exp = input("Enter the expression the student will use: ")
|
||||
tokens = tokenizer.get_tokens_from_expression_string(exp)
|
||||
|
||||
for t in tokens:
|
||||
if t.type == token.TokenType.variable:
|
||||
variables[t.value] = expression.Variable(t.value, expression.Variable.get_variable_type(t.value))
|
||||
|
||||
selected_variable = None
|
||||
|
||||
while True:
|
||||
print("Variables in expression:")
|
||||
print_vars(variables)
|
||||
response = input("Enter a variable name to modify its properties or continue (default): ")
|
||||
|
||||
if response == "continue" or response == "":
|
||||
break
|
||||
|
||||
if response in variables:
|
||||
selected_variable = variables[response]
|
||||
|
||||
if selected_variable != None:
|
||||
modify_variable(selected_variable, variables)
|
||||
|
||||
else:
|
||||
print("Invalid selection %s." %(response))
|
||||
continue
|
||||
|
||||
with open("data.json", "w", encoding="utf-8") as f:
|
||||
json.dump(expression.Expression(exp, variables), f, default=transform, indent = 4)
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
class CToken:
|
||||
def __init__(self, value, type):
|
||||
self.value = value
|
||||
self.type = type
|
||||
|
||||
class TokenType:
|
||||
add = 0
|
||||
subtract = 1
|
||||
multiply = 2
|
||||
divide = 3
|
||||
power = 4
|
||||
variable = 5
|
||||
constant = 6
|
||||
exp_start = 7
|
||||
exp_end = 8
|
||||
unknown = 9
|
||||
|
||||
OPERATOR_VERBS = {
|
||||
0 : "Adding",
|
||||
1 : "Subtracting",
|
||||
2 : "Multiplying",
|
||||
3 : "Dividing",
|
||||
4 : "Raising"
|
||||
}
|
||||
|
||||
TOKEN_NAMES = {
|
||||
0 : "Addition",
|
||||
1 : "Subtraction",
|
||||
2 : "Multiplication",
|
||||
3 : "Division",
|
||||
4 : "Power",
|
||||
5 : "Variable",
|
||||
6 : "Constant",
|
||||
7 : "Expression Start",
|
||||
8 : "Expression End",
|
||||
9 : "Unknown"
|
||||
}
|
||||
|
||||
OPERATORS = {
|
||||
'+' : add,
|
||||
'-' : subtract,
|
||||
'*' : multiply,
|
||||
'^' : power,
|
||||
'/' : divide
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_operator_verb(token):
|
||||
if not token in TokenType.OPERATOR_VERBS:
|
||||
raise ValueError("An operator verb could not be found.", "token")
|
||||
return TokenType.OPERATOR_VERBS[token]
|
||||
|
||||
@staticmethod
|
||||
def get_operator(character):
|
||||
#
|
||||
#Accepts an individual character and returns either the math operator TokenType or unknown.
|
||||
#
|
||||
if not character in TokenType.OPERATORS:
|
||||
return TokenType.unknown
|
||||
return TokenType.OPERATORS[character]
|
||||
|
||||
@staticmethod
|
||||
def get_token_type_name(tokenType):
|
||||
return TokenType.TOKEN_NAMES[tokenType]
|
||||
@@ -0,0 +1,78 @@
|
||||
import token
|
||||
|
||||
def get_tokens_from_expression_string(expression_string):
|
||||
|
||||
#Takes user input of an actuarial formula and parses the formula to id its components.
|
||||
#:return: tokens (List of objects of CToken class).
|
||||
|
||||
tokens = [] # List of objects of CToken class
|
||||
tmp = ""
|
||||
parsing_number = False
|
||||
symbols_dic = {} # Dictionary to keep track of the no. of times each variable appears.
|
||||
|
||||
for i in range(0, len(expression_string)):
|
||||
c = expression_string[i]
|
||||
if c == '.' or c.isdigit():
|
||||
if tmp.count('.') == 1:
|
||||
raise SyntaxError("Invalid grammar, to many periods in number. Found at position " + str (i) + ".")
|
||||
if len(tokens) > 1:
|
||||
lookBehind = tokens[-1]
|
||||
if lookBehind.type == token.TokenType.exp_end:
|
||||
tokens.append(token.CToken("^", token.TokenType.power))
|
||||
if (i + 1) == len(expression_string):
|
||||
tokens.append(token.CToken(tmp + c, token.TokenType.constant))
|
||||
parsing_number = True
|
||||
tmp = tmp + c
|
||||
continue
|
||||
elif c == '(':
|
||||
if parsing_number:
|
||||
tokens.append(token.CToken(tmp, token.TokenType.constant))
|
||||
tokens.append(token.CToken("*", token.TokenType.multiply))
|
||||
tokens.append(token.CToken("(", token.TokenType.exp_start))
|
||||
parsing_number = False
|
||||
tmp = ""
|
||||
continue
|
||||
elif c == ')':
|
||||
if parsing_number:
|
||||
tokens.append(token.CToken(tmp, token.TokenType.constant))
|
||||
tokens.append(token.CToken(")", token.TokenType.exp_end))
|
||||
parsing_number = False
|
||||
tmp = ""
|
||||
continue
|
||||
elif token.TokenType.get_operator(c) != token.TokenType.unknown:
|
||||
if parsing_number:
|
||||
tokens.append(token.CToken(tmp, token.TokenType.constant))
|
||||
tokens.append(token.CToken(c, token.TokenType.get_operator(c)))
|
||||
parsing_number = False
|
||||
tmp = ""
|
||||
continue
|
||||
elif c != ' ':
|
||||
if len(tokens) > 1:
|
||||
lookBehind = tokens[-1]
|
||||
if lookBehind.type == token.TokenType.exp_end:
|
||||
tokens.append(token.CToken("^", token.TokenType.power))
|
||||
if (i + 1) < len(expression_string):
|
||||
lookAHead = expression_string[i + 1]
|
||||
if lookAHead == '(':
|
||||
tokens.append(token.CToken(c, token.TokenType.variable))
|
||||
tokens.append(token.CToken("*", token.TokenType.multiply))
|
||||
tmp = ""
|
||||
parsing_number = False
|
||||
continue
|
||||
if parsing_number:
|
||||
tokens.append(token.CToken(tmp, token.TokenType.constant))
|
||||
tokens.append(token.CToken("*", token.TokenType.multiply))
|
||||
if c in symbols_dic.keys():
|
||||
symbols_dic[c] += 1
|
||||
else:
|
||||
symbols_dic[c] = 1
|
||||
tokens.append(token.CToken(c + str(symbols_dic[c]), token.TokenType.variable))
|
||||
parsing_number = False
|
||||
tmp = ""
|
||||
#When building this list we've effectively ordered the tokens in reverse order from how we want to
|
||||
#process them. So, reverse the order of the list before returning them to the caller, we don't want
|
||||
#the caller to have to worry about such a minor detail.
|
||||
tokens.reverse()
|
||||
|
||||
return tokens
|
||||
|
||||
Reference in New Issue
Block a user