Given this BNF-style grammar write a Python function `evaluate(str)` that evaluates the expression <expression> ::= <term> | <expression> '+' <term> | <expression> '-' <term> <term> ::= <factor> | <term> '*' <factor> | <term> '/' <factor> <factor> ::= <number> | '(' <expression> ')' | <prefix-op> <factor> <prefix-op> ::= 'sin' | 'cos' <number> ::= <digit> | <number> <digit> <digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' For example you can evaluate this sin(3 + 2 * 4) - cos(15) = -0.240302
Given this BNF-style grammar write a Python function `evaluate(str)` that evaluates the expression <expression> ::= <term> | <expression> '+' <term> | <expression> '-' <term> <term> ::= <factor> | <term> '*' <factor> | <term> '/' <factor> <factor> ::= <number> | '(' <expression> ')' | <prefix-op> <factor> <prefix-op> ::= 'sin' | 'cos' <number> ::= <digit> | <number> <digit> <digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' For example you can evaluate this sin(3 + 2 * 4) - cos(15) = -0.240302
To evaluate expressions based on the given BNF-style grammar, we can use a recursive descent parser. This involves writing functions that correspond to each non-terminal in the grammar and recursively parsing the input string. Here’s a Python implementation of the `evaluate` function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | import math import re class Parser: def __init__(self, expression): self.tokens = re.findall(r'\d+|\+|\-|\*|/|\(|\)|sin|cos', expression) self.current_token = None self.next_token() def next_token(self): if self.tokens: self.current_token = self.tokens.pop(0) else: self.current_token = None def parse_expression(self): result = self.parse_term() while self.current_token in ('+', '-'): if self.current_token == '+': self.next_token() result += self.parse_term() elif self.current_token == '-': self.next_token() result -= self.parse_term() return result def parse_term(self): result = self.parse_factor() while self.current_token in ('*', '/'): if self.current_token == '*': self.next_token() result *= self.parse_factor() elif self.current_token == '/': self.next_token() result /= self.parse_factor() return result def parse_factor(self): if self.current_token == '(': self.next_token() result = self.parse_expression() if self.current_token != ')': raise SyntaxError("Expected ')'") self.next_token() return result elif self.current_token in ('sin', 'cos'): op = self.current_token self.next_token() if self.current_token != '(': raise SyntaxError("Expected '(' after prefix operator") self.next_token() arg = self.parse_expression() if self.current_token != ')': raise SyntaxError("Expected ')'") self.next_token() if op == 'sin': return math.sin(arg) elif op == 'cos': return math.cos(arg) else: try: result = float(self.current_token) self.next_token() return result except ValueError: raise SyntaxError(f"Unexpected token: {self.current_token}") def evaluate(expression): parser = Parser(expression) return parser.parse_expression() # Example usage: result = evaluate("sin(3 + 2 * 4) - cos(15)") print(result) # Output: -0.240302 |
I extracted the following code from that output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | import math import re class Parser: def __init__(self, expression): self.tokens = re.findall(r'\d+|\+|\-|\*|/|\(|\)|sin|cos', expression) self.current_token = None self.next_token() def next_token(self): if self.tokens: self.current_token = self.tokens.pop(0) else: self.current_token = None def parse_expression(self): result = self.parse_term() while self.current_token in ('+', '-'): if self.current_token == '+': self.next_token() result += self.parse_term() elif self.current_token == '-': self.next_token() result -= self.parse_term() return result def parse_term(self): result = self.parse_factor() while self.current_token in ('*', '/'): if self.current_token == '*': self.next_token() result *= self.parse_factor() elif self.current_token == '/': self.next_token() result /= self.parse_factor() return result def parse_factor(self): if self.current_token == '(': self.next_token() result = self.parse_expression() if self.current_token != ')': raise SyntaxError("Expected ')'") self.next_token() return result elif self.current_token in ('sin', 'cos'): op = self.current_token self.next_token() if self.current_token != '(': raise SyntaxError("Expected '(' after prefix operator") self.next_token() arg = self.parse_expression() if self.current_token != ')': raise SyntaxError("Expected ')'") self.next_token() if op == 'sin': return math.sin(arg) elif op == 'cos': return math.cos(arg) else: try: result = float(self.current_token) self.next_token() return result except ValueError: raise SyntaxError(f"Unexpected token: {self.current_token}") def evaluate(expression): parser = Parser(expression) return parser.parse_expression() # Example usage: result = evaluate("sin(3 + 2 * 4) - cos(15)") print(result) # Output: -0.240302 |
Running the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import math import re class Parser: def __init__(self, expression): self.tokens = re.findall(r'\d+|\+|\-|\*|/|\(|\)|sin|cos', expression) self.current_token = None self.next_token() def next_token(self): if self.tokens: self.current_token = self.tokens.pop(0) else: self.current_token = None def parse_expression(self): result = self.parse_term() while self.current_token in ('+', '-'): if self.current_token == '+': self.next_token() result += self.parse_term() elif self.current_token == '-': self.next_token() result -= self.parse_term() return result def parse_term(self): result = self.parse_factor() while self.current_token in ('*', '/'): if self.current_token == '*': self.next_token() result *= self.parse_factor() elif self.current_token == '/': self.next_token() result /= self.parse_factor() return result def parse_factor(self): if self.current_token == '(': self.next_token() result = self.parse_expression() if self.current_token != ')': raise SyntaxError("Expected ')'") self.next_token() return result elif self.current_token in ('sin', 'cos'): op = self.current_token self.next_token() if self.current_token != '(': raise SyntaxError("Expected '(' after prefix operator") self.next_token() arg = self.parse_expression() if self.current_token != ')': raise SyntaxError("Expected ')'") self.next_token() if op == 'sin': return math.sin(arg) elif op == 'cos': return math.cos(arg) else: try: result = float(self.current_token) self.next_token() return result except ValueError: raise SyntaxError(f"Unexpected token: {self.current_token}") def evaluate(expression): parser = Parser(expression) return parser.parse_expression() # Example usage: result = evaluate("sin(3 + 2 * 4) - cos(15)") print(result) # Output: -0.240302 answer = round(evaluate('sin(35+2*4)-cos(15)'), 5) expected = -0.07209 assert answer == expected, f'Wrong answer; got {answer} instead of {expected}' print('All tests passed') |
-0.24030229369188216
All tests passed
Testing if the previous output contains the string All tests passed
: True