2 years ago
#26810
Irodi
How to make recursive descent parsing recognise expressions
I am making a graphing calculator and the issue I am having is finding a way to take in inputs like sin(90) and run my function for sin that I coded into another file.
I am using a recursive descent parser to go through each input and assign it to a token but the issue I am having is that when the user enters something like sin(90), the program checks each letter individually to try assign it to a token but since it doesn't recognise any token that is just an 's' or just an 'i', it outputs an error.
def generate_tokens(self): # Generate tokens from input
while self.current_char is not None:
if self.current_char in WHITESPACE:
self.advance() # If character is whitespace, skip it
elif self.current_char == '.' or self.current_char in DIGITS:
yield self.generate_number() # If character is digit or decimal point, run next method
elif self.current_char == '+': # Specific token is generated if specific input
self.advance()
yield Token(TokenType.PLUS)
elif self.current_char == '-':
self.advance()
yield Token(TokenType.MINUS)
elif self.current_char == '*' or self.current_char == 'x':
self.advance()
yield Token(TokenType.MULTIPLY)
elif self.current_char == '/':
self.advance()
yield Token(TokenType.DIVIDE)
elif self.current_char == '(':
self.advance()
yield Token(TokenType.LPAREN)
elif self.current_char == ')':
self.advance()
yield Token(TokenType.RPAREN)
elif self.current_char == '^':
self.advance()
yield Token(TokenType.INDICES)
elif self.current_char == 's' and next(self.text) == 'i' and next(self.text) == 'n':
self.advance()
yield Token(TokenType.SIN)
else:
raise Exception(f'Illegal Character {self.current_char}')
Here is the code that generates a token for each input
I use a expression, term, factor rule and I put the sin function in the factor rule. Here is the code for the factor rule` def factor(self): # Highest level rule, checks for numbers token = self.current_token # Checks for numbers
if token.type == TokenType.LPAREN:
self.advance()
result = self.expr()
if self.current_token.type != TokenType.RPAREN:
self.raise_error()
self.advance()
return result
elif token.type == TokenType.NUMBER:
self.advance()
return NumberNode(token.value)
elif token.type == TokenType.SIN:
self.advance()
return SinNode(self.factor())
elif token.type == TokenType.PLUS:
self.advance()
return PlusNode(self.factor())
elif token.type == TokenType.MINUS: # Check if number is negative and then output it as negative
self.advance()
return MinusNode(self.factor())
self.raise_error()`
And this goes to the interpreter which has this code.
`class Interpreter: def visit(self, node): # Pass in the root node here method_name = f'visit_{type(node).name}' # This tells us the type of node the node we are on is method = getattr(self, method_name) return method(node)
def visit_NumberNode(self, node):
return Number(node.value)
def visit_AddNode(self, node):
return Number(self.visit(node.node_a).value + self.visit(node.node_b).value)
def visit_SubtractNode(self, node):
return Number(self.visit(node.node_a).value - self.visit(node.node_b).value)
def visit_MultiplyNode(self, node):
return Number(self.visit(node.node_a).value * self.visit(node.node_b).value)
def visit_DivideNode(self, node):
try:
return Number(self.visit(node.node_a).value / self.visit(node.node_b).value)
except:
raise Exception('Runtime math error')
def visit_PlusNode(self, node):
return self.visit(node.node)
def visit_MinusNode(self, node):
return Number(-self.visit(node.node).value)
def visit_IndiceNode(self, node):
return Number(self.visit(node.node_a).value ** self.visit(node.node_b).value)
def visit_SinNode(self, node):
return Number(function.sin(self.visit(node.node.value)))
` This is the code for the nodes
@dataclass
class SinNode: node: any
def __repr__(self):
return f'(sin({self.node}))'
And this is the code for the values:
@dataclass
class Number: value: float
def __repr__(self):
return f'{self.value}'
I would appreciate any help because this project is really stressing me out. I can't figure out why it won't work and I have no idea how to make it work. I just need to read an expression from the user (something like 'x^2*tan(2x)') and apply it to np.linspace and graph that but I can't figure out a way to read the expression and keep order of operations correct. I also want to minimise use of libraries since there are libraries that do this but I need to do this without them
python
recursive-descent
0 Answers
Your Answer