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
Okay, let's implement a recursive descent parser in Python to evaluate expressions based on the provided BNF grammar. We'll create a `Parser` class to manage the input string and the current parsing position. The methods within the class will correspond to the non-terminals in the grammar.
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | import math import re # For potential whitespace skipping, though manual skipping is often clearer here class ParseError(Exception): """Custom exception for parsing errors.""" pass class Parser: def __init__(self, text): self.text = text self.pos = 0 # Ensure trigonometric functions use radians as standard in math module print("Note: Trigonometric functions (sin, cos) assume input is in radians.") def _peek(self): """Return the next character without consuming it, or None if EOF.""" if self.pos < len(self.text): return self.text[self.pos] return None def _consume(self, char=None): """Consume the next character. If char is provided, check for match.""" if self.pos < len(self.text): current_char = self.text[self.pos] if char is not None and current_char != char: raise ParseError(f"Expected '{char}' but found '{current_char}' at position {self.pos}") self.pos += 1 return current_char elif char is not None: raise ParseError(f"Expected '{char}' but reached end of input") return None # Reached end of input def _skip_whitespace(self): """Consume whitespace characters.""" while self.pos < len(self.text) and self.text[self.pos].isspace(): self.pos += 1 def _parse_number(self): """Parses a sequence of digits into an integer.""" start_pos = self.pos while self.pos < len(self.text) and self.text[self.pos].isdigit(): self.pos += 1 if start_pos == self.pos: # This case should ideally be caught by factor parsing logic before calling number raise ParseError(f"Expected number but found '{self._peek()}' at position {start_pos}") num_str = self.text[start_pos:self.pos] return int(num_str) # The grammar only defines integers def _parse_factor(self): """Parses a factor: <number> | '(' <expression> ')' | <prefix-op> <factor>""" self._skip_whitespace() char = self._peek() if char is None: raise ParseError("Unexpected end of input while parsing factor") if char.isdigit(): return float(self._parse_number()) # Return float for calculations elif char == '(': self._consume('(') expr_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' but found '{self._peek()}' at position {self.pos}") self._consume(')') return expr_val elif self.text.startswith('sin', self.pos): self.pos += 3 # Consume 'sin' self._skip_whitespace() if self._peek() != '(': raise ParseError(f"Expected '(' after 'sin' but found '{self._peek()}' at position {self.pos}") self._consume('(') # Recursively call parse_expression for the argument inside sin() arg_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' after sin argument but found '{self._peek()}' at position {self.pos}") self._consume(')') return math.sin(arg_val) # Assumes radians elif self.text.startswith('cos', self.pos): self.pos += 3 # Consume 'cos' self._skip_whitespace() if self._peek() != '(': raise ParseError(f"Expected '(' after 'cos' but found '{self._peek()}' at position {self.pos}") self._consume('(') # Recursively call parse_expression for the argument inside cos() arg_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' after cos argument but found '{self._peek()}' at position {self.pos}") self._consume(')') return math.cos(arg_val) # Assumes radians else: raise ParseError(f"Unexpected character '{char}' while parsing factor at position {self.pos}") def _parse_term(self): """Parses a term: <factor> | <term> '*' <factor> | <term> '/' <factor>""" # Parse the first factor value = self._parse_factor() while True: self._skip_whitespace() op = self._peek() if op == '*': self._consume('*') right_factor = self._parse_factor() value *= right_factor elif op == '/': self._consume('/') right_factor = self._parse_factor() if right_factor == 0: raise ParseError("Division by zero") value /= right_factor # Use float division else: # No more '*' or '/' found for this term break return value def _parse_expression(self): """Parses an expression: <term> | <expression> '+' <term> | <expression> '-' <term>""" # Parse the first term value = self._parse_term() while True: self._skip_whitespace() op = self._peek() if op == '+': self._consume('+') right_term = self._parse_term() value += right_term elif op == '-': self._consume('-') right_term = self._parse_term() value -= right_term else: # No more '+' or '-' found for this expression break return value def evaluate(self): """Evaluates the entire expression text.""" result = self._parse_expression() self._skip_whitespace() # Check if we consumed the entire string if self.pos < len(self.text): raise ParseError(f"Unexpected character '{self.text[self.pos]}' after expression at position {self.pos}") return result # --- Top-level function --- def evaluate(expression_string): """ Evaluates a mathematical expression string based on the defined grammar. Args: expression_string: The string containing the mathematical expression. Returns: The numerical result of the evaluation (float). Raises: ParseError: If the string does not conform to the grammar or contains errors like division by zero or mismatched parentheses. """ try: parser = Parser(expression_string) return parser.evaluate() except ParseError as e: print(f"Parsing Error: {e}") # Or re-raise if you want the caller to handle it: raise e return None # Indicate failure except Exception as e: print(f"An unexpected error occurred: {e}") return None # Indicate failure # --- Example Usage --- expression1 = "sin(3 + 2 * 4) - cos(15)" result1 = evaluate(expression1) if result1 is not None: print(f"'{expression1}' = {result1}") # Expected output (approx): 'sin(3 + 2 * 4) - cos(15)' = -0.240302306889 expression2 = "10 + 2 * 6" result2 = evaluate(expression2) if result2 is not None: print(f"'{expression2}' = {result2}") # Expected: 22.0 expression3 = "(10 + 2) * 6" result3 = evaluate(expression3) if result3 is not None: print(f"'{expression3}' = {result3}") # Expected: 72.0 expression4 = "100 / 10 * 2" result4 = evaluate(expression4) if result4 is not None: print(f"'{expression4}' = {result4}") # Expected: 20.0 (Left-associative) expression5 = "cos(0) + sin(0)" result5 = evaluate(expression5) if result5 is not None: print(f"'{expression5}' = {result5}") # Expected: 1.0 expression6 = "5 / (4 - 2 * 2)" # Division by zero result6 = evaluate(expression6) if result6 is not None: print(f"'{expression6}' = {result6}") else: print(f"Evaluation failed for '{expression6}'") expression7 = "3 + " # Incomplete expression result7 = evaluate(expression7) if result7 is not None: print(f"'{expression7}' = {result7}") else: print(f"Evaluation failed for '{expression7}'") expression8 = "sin(1" # Mismatched parenthesis result8 = evaluate(expression8) if result8 is not None: print(f"'{expression8}' = {result8}") else: print(f"Evaluation failed for '{expression8}'") expression9 = "tan(5)" # Invalid operator result9 = evaluate(expression9) if result9 is not None: print(f"'{expression9}' = {result9}") else: print(f"Evaluation failed for '{expression9}'") expression10 = " 5 * ( sin ( 1 ) + cos( 2 ) ) / 2 " # With whitespace result10 = evaluate(expression10) if result10 is not None: print(f"'{expression10}' = {result10}") |
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | import math import re # For potential whitespace skipping, though manual skipping is often clearer here class ParseError(Exception): """Custom exception for parsing errors.""" pass class Parser: def __init__(self, text): self.text = text self.pos = 0 # Ensure trigonometric functions use radians as standard in math module print("Note: Trigonometric functions (sin, cos) assume input is in radians.") def _peek(self): """Return the next character without consuming it, or None if EOF.""" if self.pos < len(self.text): return self.text[self.pos] return None def _consume(self, char=None): """Consume the next character. If char is provided, check for match.""" if self.pos < len(self.text): current_char = self.text[self.pos] if char is not None and current_char != char: raise ParseError(f"Expected '{char}' but found '{current_char}' at position {self.pos}") self.pos += 1 return current_char elif char is not None: raise ParseError(f"Expected '{char}' but reached end of input") return None # Reached end of input def _skip_whitespace(self): """Consume whitespace characters.""" while self.pos < len(self.text) and self.text[self.pos].isspace(): self.pos += 1 def _parse_number(self): """Parses a sequence of digits into an integer.""" start_pos = self.pos while self.pos < len(self.text) and self.text[self.pos].isdigit(): self.pos += 1 if start_pos == self.pos: # This case should ideally be caught by factor parsing logic before calling number raise ParseError(f"Expected number but found '{self._peek()}' at position {start_pos}") num_str = self.text[start_pos:self.pos] return int(num_str) # The grammar only defines integers def _parse_factor(self): """Parses a factor: <number> | '(' <expression> ')' | <prefix-op> <factor>""" self._skip_whitespace() char = self._peek() if char is None: raise ParseError("Unexpected end of input while parsing factor") if char.isdigit(): return float(self._parse_number()) # Return float for calculations elif char == '(': self._consume('(') expr_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' but found '{self._peek()}' at position {self.pos}") self._consume(')') return expr_val elif self.text.startswith('sin', self.pos): self.pos += 3 # Consume 'sin' self._skip_whitespace() if self._peek() != '(': raise ParseError(f"Expected '(' after 'sin' but found '{self._peek()}' at position {self.pos}") self._consume('(') # Recursively call parse_expression for the argument inside sin() arg_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' after sin argument but found '{self._peek()}' at position {self.pos}") self._consume(')') return math.sin(arg_val) # Assumes radians elif self.text.startswith('cos', self.pos): self.pos += 3 # Consume 'cos' self._skip_whitespace() if self._peek() != '(': raise ParseError(f"Expected '(' after 'cos' but found '{self._peek()}' at position {self.pos}") self._consume('(') # Recursively call parse_expression for the argument inside cos() arg_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' after cos argument but found '{self._peek()}' at position {self.pos}") self._consume(')') return math.cos(arg_val) # Assumes radians else: raise ParseError(f"Unexpected character '{char}' while parsing factor at position {self.pos}") def _parse_term(self): """Parses a term: <factor> | <term> '*' <factor> | <term> '/' <factor>""" # Parse the first factor value = self._parse_factor() while True: self._skip_whitespace() op = self._peek() if op == '*': self._consume('*') right_factor = self._parse_factor() value *= right_factor elif op == '/': self._consume('/') right_factor = self._parse_factor() if right_factor == 0: raise ParseError("Division by zero") value /= right_factor # Use float division else: # No more '*' or '/' found for this term break return value def _parse_expression(self): """Parses an expression: <term> | <expression> '+' <term> | <expression> '-' <term>""" # Parse the first term value = self._parse_term() while True: self._skip_whitespace() op = self._peek() if op == '+': self._consume('+') right_term = self._parse_term() value += right_term elif op == '-': self._consume('-') right_term = self._parse_term() value -= right_term else: # No more '+' or '-' found for this expression break return value def evaluate(self): """Evaluates the entire expression text.""" result = self._parse_expression() self._skip_whitespace() # Check if we consumed the entire string if self.pos < len(self.text): raise ParseError(f"Unexpected character '{self.text[self.pos]}' after expression at position {self.pos}") return result # --- Top-level function --- def evaluate(expression_string): """ Evaluates a mathematical expression string based on the defined grammar. Args: expression_string: The string containing the mathematical expression. Returns: The numerical result of the evaluation (float). Raises: ParseError: If the string does not conform to the grammar or contains errors like division by zero or mismatched parentheses. """ try: parser = Parser(expression_string) return parser.evaluate() except ParseError as e: print(f"Parsing Error: {e}") # Or re-raise if you want the caller to handle it: raise e return None # Indicate failure except Exception as e: print(f"An unexpected error occurred: {e}") return None # Indicate failure # --- Example Usage --- expression1 = "sin(3 + 2 * 4) - cos(15)" result1 = evaluate(expression1) if result1 is not None: print(f"'{expression1}' = {result1}") # Expected output (approx): 'sin(3 + 2 * 4) - cos(15)' = -0.240302306889 expression2 = "10 + 2 * 6" result2 = evaluate(expression2) if result2 is not None: print(f"'{expression2}' = {result2}") # Expected: 22.0 expression3 = "(10 + 2) * 6" result3 = evaluate(expression3) if result3 is not None: print(f"'{expression3}' = {result3}") # Expected: 72.0 expression4 = "100 / 10 * 2" result4 = evaluate(expression4) if result4 is not None: print(f"'{expression4}' = {result4}") # Expected: 20.0 (Left-associative) expression5 = "cos(0) + sin(0)" result5 = evaluate(expression5) if result5 is not None: print(f"'{expression5}' = {result5}") # Expected: 1.0 expression6 = "5 / (4 - 2 * 2)" # Division by zero result6 = evaluate(expression6) if result6 is not None: print(f"'{expression6}' = {result6}") else: print(f"Evaluation failed for '{expression6}'") expression7 = "3 + " # Incomplete expression result7 = evaluate(expression7) if result7 is not None: print(f"'{expression7}' = {result7}") else: print(f"Evaluation failed for '{expression7}'") expression8 = "sin(1" # Mismatched parenthesis result8 = evaluate(expression8) if result8 is not None: print(f"'{expression8}' = {result8}") else: print(f"Evaluation failed for '{expression8}'") expression9 = "tan(5)" # Invalid operator result9 = evaluate(expression9) if result9 is not None: print(f"'{expression9}' = {result9}") else: print(f"Evaluation failed for '{expression9}'") expression10 = " 5 * ( sin ( 1 ) + cos( 2 ) ) / 2 " # With whitespace result10 = evaluate(expression10) if result10 is not None: print(f"'{expression10}' = {result10}") |
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | import math import re # For potential whitespace skipping, though manual skipping is often clearer here class ParseError(Exception): """Custom exception for parsing errors.""" pass class Parser: def __init__(self, text): self.text = text self.pos = 0 # Ensure trigonometric functions use radians as standard in math module print("Note: Trigonometric functions (sin, cos) assume input is in radians.") def _peek(self): """Return the next character without consuming it, or None if EOF.""" if self.pos < len(self.text): return self.text[self.pos] return None def _consume(self, char=None): """Consume the next character. If char is provided, check for match.""" if self.pos < len(self.text): current_char = self.text[self.pos] if char is not None and current_char != char: raise ParseError(f"Expected '{char}' but found '{current_char}' at position {self.pos}") self.pos += 1 return current_char elif char is not None: raise ParseError(f"Expected '{char}' but reached end of input") return None # Reached end of input def _skip_whitespace(self): """Consume whitespace characters.""" while self.pos < len(self.text) and self.text[self.pos].isspace(): self.pos += 1 def _parse_number(self): """Parses a sequence of digits into an integer.""" start_pos = self.pos while self.pos < len(self.text) and self.text[self.pos].isdigit(): self.pos += 1 if start_pos == self.pos: # This case should ideally be caught by factor parsing logic before calling number raise ParseError(f"Expected number but found '{self._peek()}' at position {start_pos}") num_str = self.text[start_pos:self.pos] return int(num_str) # The grammar only defines integers def _parse_factor(self): """Parses a factor: <number> | '(' <expression> ')' | <prefix-op> <factor>""" self._skip_whitespace() char = self._peek() if char is None: raise ParseError("Unexpected end of input while parsing factor") if char.isdigit(): return float(self._parse_number()) # Return float for calculations elif char == '(': self._consume('(') expr_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' but found '{self._peek()}' at position {self.pos}") self._consume(')') return expr_val elif self.text.startswith('sin', self.pos): self.pos += 3 # Consume 'sin' self._skip_whitespace() if self._peek() != '(': raise ParseError(f"Expected '(' after 'sin' but found '{self._peek()}' at position {self.pos}") self._consume('(') # Recursively call parse_expression for the argument inside sin() arg_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' after sin argument but found '{self._peek()}' at position {self.pos}") self._consume(')') return math.sin(arg_val) # Assumes radians elif self.text.startswith('cos', self.pos): self.pos += 3 # Consume 'cos' self._skip_whitespace() if self._peek() != '(': raise ParseError(f"Expected '(' after 'cos' but found '{self._peek()}' at position {self.pos}") self._consume('(') # Recursively call parse_expression for the argument inside cos() arg_val = self._parse_expression() self._skip_whitespace() if self._peek() != ')': raise ParseError(f"Expected ')' after cos argument but found '{self._peek()}' at position {self.pos}") self._consume(')') return math.cos(arg_val) # Assumes radians else: raise ParseError(f"Unexpected character '{char}' while parsing factor at position {self.pos}") def _parse_term(self): """Parses a term: <factor> | <term> '*' <factor> | <term> '/' <factor>""" # Parse the first factor value = self._parse_factor() while True: self._skip_whitespace() op = self._peek() if op == '*': self._consume('*') right_factor = self._parse_factor() value *= right_factor elif op == '/': self._consume('/') right_factor = self._parse_factor() if right_factor == 0: raise ParseError("Division by zero") value /= right_factor # Use float division else: # No more '*' or '/' found for this term break return value def _parse_expression(self): """Parses an expression: <term> | <expression> '+' <term> | <expression> '-' <term>""" # Parse the first term value = self._parse_term() while True: self._skip_whitespace() op = self._peek() if op == '+': self._consume('+') right_term = self._parse_term() value += right_term elif op == '-': self._consume('-') right_term = self._parse_term() value -= right_term else: # No more '+' or '-' found for this expression break return value def evaluate(self): """Evaluates the entire expression text.""" result = self._parse_expression() self._skip_whitespace() # Check if we consumed the entire string if self.pos < len(self.text): raise ParseError(f"Unexpected character '{self.text[self.pos]}' after expression at position {self.pos}") return result # --- Top-level function --- def evaluate(expression_string): """ Evaluates a mathematical expression string based on the defined grammar. Args: expression_string: The string containing the mathematical expression. Returns: The numerical result of the evaluation (float). Raises: ParseError: If the string does not conform to the grammar or contains errors like division by zero or mismatched parentheses. """ try: parser = Parser(expression_string) return parser.evaluate() except ParseError as e: print(f"Parsing Error: {e}") # Or re-raise if you want the caller to handle it: raise e return None # Indicate failure except Exception as e: print(f"An unexpected error occurred: {e}") return None # Indicate failure # --- Example Usage --- expression1 = "sin(3 + 2 * 4) - cos(15)" result1 = evaluate(expression1) if result1 is not None: print(f"'{expression1}' = {result1}") # Expected output (approx): 'sin(3 + 2 * 4) - cos(15)' = -0.240302306889 expression2 = "10 + 2 * 6" result2 = evaluate(expression2) if result2 is not None: print(f"'{expression2}' = {result2}") # Expected: 22.0 expression3 = "(10 + 2) * 6" result3 = evaluate(expression3) if result3 is not None: print(f"'{expression3}' = {result3}") # Expected: 72.0 expression4 = "100 / 10 * 2" result4 = evaluate(expression4) if result4 is not None: print(f"'{expression4}' = {result4}") # Expected: 20.0 (Left-associative) expression5 = "cos(0) + sin(0)" result5 = evaluate(expression5) if result5 is not None: print(f"'{expression5}' = {result5}") # Expected: 1.0 expression6 = "5 / (4 - 2 * 2)" # Division by zero result6 = evaluate(expression6) if result6 is not None: print(f"'{expression6}' = {result6}") else: print(f"Evaluation failed for '{expression6}'") expression7 = "3 + " # Incomplete expression result7 = evaluate(expression7) if result7 is not None: print(f"'{expression7}' = {result7}") else: print(f"Evaluation failed for '{expression7}'") expression8 = "sin(1" # Mismatched parenthesis result8 = evaluate(expression8) if result8 is not None: print(f"'{expression8}' = {result8}") else: print(f"Evaluation failed for '{expression8}'") expression9 = "tan(5)" # Invalid operator result9 = evaluate(expression9) if result9 is not None: print(f"'{expression9}' = {result9}") else: print(f"Evaluation failed for '{expression9}'") expression10 = " 5 * ( sin ( 1 ) + cos( 2 ) ) / 2 " # With whitespace result10 = evaluate(expression10) if result10 is not None: print(f"'{expression10}' = {result10}") 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') |
Note: Trigonometric functions (sin, cos) assume input is in radians.
'sin(3 + 2 * 4) - cos(15)' = -0.24030229369188216
Note: Trigonometric functions (sin, cos) assume input is in radians.
'10 + 2 * 6' = 22.0
Note: Trigonometric functions (sin, cos) assume input is in radians.
'(10 + 2) * 6' = 72.0
Note: Trigonometric functions (sin, cos) assume input is in radians.
'100 / 10 * 2' = 20.0
Note: Trigonometric functions (sin, cos) assume input is in radians.
'cos(0) + sin(0)' = 1.0
Note: Trigonometric functions (sin, cos) assume input is in radians.
Parsing Error: Division by zero
Evaluation failed for '5 / (4 - 2 * 2)'
Note: Trigonometric functions (sin, cos) assume input is in radians.
Parsing Error: Unexpected end of input while parsing factor
Evaluation failed for '3 + '
Note: Trigonometric functions (sin, cos) assume input is in radians.
Parsing Error: Expected ')' after sin argument but found 'None' at position 5
Evaluation failed for 'sin(1'
Note: Trigonometric functions (sin, cos) assume input is in radians.
Parsing Error: Unexpected character 't' while parsing factor at position 0
Evaluation failed for 'tan(5)'
Note: Trigonometric functions (sin, cos) assume input is in radians.
' 5 * ( sin ( 1 ) + cos( 2 ) ) / 2 ' = 1.0633103706518852
Note: Trigonometric functions (sin, cos) assume input is in radians.
All tests passed
Testing if the previous output contains the string All tests passed
: True