| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-24 10:46:17 +01:00
										 |  |  | import textwrap | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  | from test.support.bytecode_helper import CodegenTestCase | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Tests for the code-generation stage of the compiler. | 
					
						
							|  |  |  | # Examine the un-optimized code generated from the AST. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class IsolatedCodeGenTests(CodegenTestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-24 10:46:17 +01:00
										 |  |  |     def assertInstructionsMatch_recursive(self, insts, expected_insts): | 
					
						
							|  |  |  |         expected_nested = [i for i in expected_insts if isinstance(i, list)] | 
					
						
							|  |  |  |         expected_insts = [i for i in expected_insts if not isinstance(i, list)] | 
					
						
							|  |  |  |         self.assertInstructionsMatch(insts, expected_insts) | 
					
						
							|  |  |  |         self.assertEqual(len(insts.get_nested()), len(expected_nested)) | 
					
						
							|  |  |  |         for n_insts, n_expected in zip(insts.get_nested(), expected_nested): | 
					
						
							|  |  |  |             self.assertInstructionsMatch_recursive(n_insts, n_expected) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  |     def codegen_test(self, snippet, expected_insts): | 
					
						
							|  |  |  |         import ast | 
					
						
							| 
									
										
										
										
											2024-02-15 14:32:21 +00:00
										 |  |  |         a = ast.parse(snippet, "my_file.py", "exec") | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  |         insts = self.generate_code(a) | 
					
						
							| 
									
										
										
										
											2024-04-24 10:46:17 +01:00
										 |  |  |         self.assertInstructionsMatch_recursive(insts, expected_insts) | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_if_expression(self): | 
					
						
							|  |  |  |         snippet = "42 if True else 24" | 
					
						
							|  |  |  |         false_lbl = self.Label() | 
					
						
							|  |  |  |         expected = [ | 
					
						
							|  |  |  |             ('RESUME', 0, 0), | 
					
						
							|  |  |  |             ('LOAD_CONST', 0, 1), | 
					
						
							| 
									
										
										
										
											2023-06-29 13:49:54 -07:00
										 |  |  |             ('TO_BOOL', 0, 1), | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  |             ('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1), | 
					
						
							|  |  |  |             ('LOAD_CONST', 1, 1), | 
					
						
							| 
									
										
										
										
											2024-01-06 14:20:08 +00:00
										 |  |  |             ('JUMP_NO_INTERRUPT', exit_lbl := self.Label()), | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  |             false_lbl, | 
					
						
							|  |  |  |             ('LOAD_CONST', 2, 1), | 
					
						
							|  |  |  |             exit_lbl, | 
					
						
							|  |  |  |             ('POP_TOP', None), | 
					
						
							| 
									
										
										
										
											2023-05-07 18:47:28 +01:00
										 |  |  |             ('LOAD_CONST', 3), | 
					
						
							|  |  |  |             ('RETURN_VALUE', None), | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  |         ] | 
					
						
							|  |  |  |         self.codegen_test(snippet, expected) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_for_loop(self): | 
					
						
							|  |  |  |         snippet = "for x in l:\n\tprint(x)" | 
					
						
							|  |  |  |         false_lbl = self.Label() | 
					
						
							|  |  |  |         expected = [ | 
					
						
							|  |  |  |             ('RESUME', 0, 0), | 
					
						
							|  |  |  |             ('LOAD_NAME', 0, 1), | 
					
						
							|  |  |  |             ('GET_ITER', None, 1), | 
					
						
							|  |  |  |             loop_lbl := self.Label(), | 
					
						
							|  |  |  |             ('FOR_ITER', exit_lbl := self.Label(), 1), | 
					
						
							| 
									
										
										
										
											2024-06-12 14:18:43 +02:00
										 |  |  |             ('NOP', None, 1, 1), | 
					
						
							| 
									
										
										
										
											2023-02-28 11:29:32 +00:00
										 |  |  |             ('STORE_NAME', 1, 1), | 
					
						
							|  |  |  |             ('LOAD_NAME', 2, 2), | 
					
						
							| 
									
										
										
										
											2023-08-09 11:19:39 -07:00
										 |  |  |             ('PUSH_NULL', None, 2), | 
					
						
							| 
									
										
										
										
											2023-02-28 11:29:32 +00:00
										 |  |  |             ('LOAD_NAME', 1, 2), | 
					
						
							|  |  |  |             ('CALL', 1, 2), | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  |             ('POP_TOP', None), | 
					
						
							|  |  |  |             ('JUMP', loop_lbl), | 
					
						
							|  |  |  |             exit_lbl, | 
					
						
							|  |  |  |             ('END_FOR', None), | 
					
						
							| 
									
										
										
										
											2024-01-24 15:10:17 +00:00
										 |  |  |             ('POP_TOP', None), | 
					
						
							| 
									
										
										
										
											2023-05-07 18:47:28 +01:00
										 |  |  |             ('LOAD_CONST', 0), | 
					
						
							|  |  |  |             ('RETURN_VALUE', None), | 
					
						
							| 
									
										
										
										
											2022-11-14 13:56:40 +00:00
										 |  |  |         ] | 
					
						
							|  |  |  |         self.codegen_test(snippet, expected) | 
					
						
							| 
									
										
										
										
											2024-02-15 14:32:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-24 10:46:17 +01:00
										 |  |  |     def test_function(self): | 
					
						
							|  |  |  |         snippet = textwrap.dedent("""
 | 
					
						
							|  |  |  |             def f(x): | 
					
						
							|  |  |  |                 return x + 42 | 
					
						
							|  |  |  |         """)
 | 
					
						
							|  |  |  |         expected = [ | 
					
						
							|  |  |  |             # Function definition | 
					
						
							|  |  |  |             ('RESUME', 0), | 
					
						
							|  |  |  |             ('LOAD_CONST', 0), | 
					
						
							|  |  |  |             ('MAKE_FUNCTION', None), | 
					
						
							|  |  |  |             ('STORE_NAME', 0), | 
					
						
							|  |  |  |             ('LOAD_CONST', 1), | 
					
						
							|  |  |  |             ('RETURN_VALUE', None), | 
					
						
							|  |  |  |             [ | 
					
						
							|  |  |  |                 # Function body | 
					
						
							|  |  |  |                 ('RESUME', 0), | 
					
						
							|  |  |  |                 ('LOAD_FAST', 0), | 
					
						
							|  |  |  |                 ('LOAD_CONST', 1), | 
					
						
							|  |  |  |                 ('BINARY_OP', 0), | 
					
						
							|  |  |  |                 ('RETURN_VALUE', None), | 
					
						
							|  |  |  |                 ('LOAD_CONST', 0), | 
					
						
							|  |  |  |                 ('RETURN_VALUE', None), | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         self.codegen_test(snippet, expected) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_nested_functions(self): | 
					
						
							|  |  |  |         snippet = textwrap.dedent("""
 | 
					
						
							|  |  |  |             def f(): | 
					
						
							|  |  |  |                 def h(): | 
					
						
							|  |  |  |                     return 12 | 
					
						
							|  |  |  |                 def g(): | 
					
						
							|  |  |  |                     x = 1 | 
					
						
							|  |  |  |                     y = 2 | 
					
						
							|  |  |  |                     z = 3 | 
					
						
							|  |  |  |                     u = 4 | 
					
						
							|  |  |  |                     return 42 | 
					
						
							|  |  |  |         """)
 | 
					
						
							|  |  |  |         expected = [ | 
					
						
							|  |  |  |             # Function definition | 
					
						
							|  |  |  |             ('RESUME', 0), | 
					
						
							|  |  |  |             ('LOAD_CONST', 0), | 
					
						
							|  |  |  |             ('MAKE_FUNCTION', None), | 
					
						
							|  |  |  |             ('STORE_NAME', 0), | 
					
						
							|  |  |  |             ('LOAD_CONST', 1), | 
					
						
							|  |  |  |             ('RETURN_VALUE', None), | 
					
						
							|  |  |  |             [ | 
					
						
							|  |  |  |                 # Function body | 
					
						
							|  |  |  |                 ('RESUME', 0), | 
					
						
							|  |  |  |                 ('LOAD_CONST', 1), | 
					
						
							|  |  |  |                 ('MAKE_FUNCTION', None), | 
					
						
							|  |  |  |                 ('STORE_FAST', 0), | 
					
						
							|  |  |  |                 ('LOAD_CONST', 2), | 
					
						
							|  |  |  |                 ('MAKE_FUNCTION', None), | 
					
						
							|  |  |  |                 ('STORE_FAST', 1), | 
					
						
							|  |  |  |                 ('LOAD_CONST', 0), | 
					
						
							|  |  |  |                 ('RETURN_VALUE', None), | 
					
						
							|  |  |  |                 [ | 
					
						
							|  |  |  |                     ('RESUME', 0), | 
					
						
							|  |  |  |                     ('NOP', None), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 1), | 
					
						
							|  |  |  |                     ('RETURN_VALUE', None), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 0), | 
					
						
							|  |  |  |                     ('RETURN_VALUE', None), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |                 [ | 
					
						
							|  |  |  |                     ('RESUME', 0), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 1), | 
					
						
							|  |  |  |                     ('STORE_FAST', 0), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 2), | 
					
						
							|  |  |  |                     ('STORE_FAST', 1), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 3), | 
					
						
							|  |  |  |                     ('STORE_FAST', 2), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 4), | 
					
						
							|  |  |  |                     ('STORE_FAST', 3), | 
					
						
							|  |  |  |                     ('NOP', None), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 5), | 
					
						
							|  |  |  |                     ('RETURN_VALUE', None), | 
					
						
							|  |  |  |                     ('LOAD_CONST', 0), | 
					
						
							|  |  |  |                     ('RETURN_VALUE', None), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         self.codegen_test(snippet, expected) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-15 14:32:21 +00:00
										 |  |  |     def test_syntax_error__return_not_in_function(self): | 
					
						
							|  |  |  |         snippet = "return 42" | 
					
						
							| 
									
										
										
										
											2024-10-07 23:23:49 +02:00
										 |  |  |         with self.assertRaisesRegex(SyntaxError, "'return' outside function") as cm: | 
					
						
							| 
									
										
										
										
											2024-02-15 14:32:21 +00:00
										 |  |  |             self.codegen_test(snippet, None) | 
					
						
							| 
									
										
										
										
											2024-10-07 23:23:49 +02:00
										 |  |  |         self.assertIsNone(cm.exception.text) | 
					
						
							|  |  |  |         self.assertEqual(cm.exception.offset, 1) | 
					
						
							|  |  |  |         self.assertEqual(cm.exception.end_offset, 10) |