Repository: cityofzion/neo3-boa
Issue: can't compile contract

**Describe the bug**
Compiler claims class is not found, while it exists

**How to Reproduce**

[generator.zip](https://github.com/CityOfZion/neo3-boa/files/11486115/generator.zip)

Steps to reproduce the behavior:
1. unpack the zip with the contract
2. run `neo3-boa compile -db generator.py`


**Expected behavior**
contract compiles

**Screenshots**
![Screenshot from 2023-05-16 08-16-20](https://github.com/CityOfZion/neo3-boa/assets/6625537/4c6c9bfb-ad51-4b0f-a6da-20aaff651220)


**Platform:**
 - OS: Ubuntu 22.04
 - Version: development branch https://github.com/CityOfZion/neo3-boa/commit/b571759a8945d4af8c8078be36c97a378cd96955 as well as the code in this PR. **Note: the PyPi release does successfully compile this contract**
 - Python version: 3.10.9

**(Optional) Additional context**
It specifically fails here with a `NameError` exception
https://github.com/CityOfZion/neo3-boa/blob/b571759a8945d4af8c8078be36c97a378cd96955/boa3/internal/analyser/moduleanalyser.py#L379

Changed files:
boa3/internal/analyser/moduleanalyser.py

diff --git a/boa3/internal/analyser/moduleanalyser.py b/boa3/internal/analyser/moduleanalyser.py
index b533328c..d3eee8f2 100644
--- a/boa3/internal/analyser/moduleanalyser.py
+++ b/boa3/internal/analyser/moduleanalyser.py
@@ -366,23 +366,39 @@ class ModuleAnalyser(IAstAnalyser, ast.NodeVisitor):
         else:
             function.returns = None
             function.decorator_list = []
+
+            imports: List[ast.AST] = []
+            other_instructions: List[ast.AST] = []
+            for node in self._tree.body:
+                if node == function:
+                    # metadata function must be right after all the imports, so it executes correctly
+                    continue
+
+                if isinstance(node, (ast.ImportFrom, ast.Import)):
+                    imports.append(node)
+                else:
+                    other_instructions.append(node)
+
             module: ast.Module = ast.parse('')
-            module.body = [node for node in self._tree.body
-                           if isinstance(node, (ast.ImportFrom, ast.Import, ast.ClassDef))]
-            module.body.append(function)
+            module.body = imports + [function] + other_instructions
             ast.copy_location(module, function)
+            namespace = {}
 
             try:
                 # executes the function
                 code = compile(module, filename='<boa3>', mode='exec')
-                namespace = {}
                 exec(code, namespace)
-                obj: Any = namespace[function.name]()
             except ModuleNotFoundError:
                 # will fail if any imports can't be executed
                 # in this case, the error is already logged
                 return
+            except BaseException as inner_exception:
+                # reordering the module tree may raise unexpected exceptions
+                # ignore if it has generated the metadata function
+                if function.name not in namespace:
+                    raise inner_exception
 
+            obj: Any = namespace[function.name]()
             node: ast.AST = function.body[-1] if len(function.body) > 0 else function
             # return must be a NeoMetadata object
             if not isinstance(obj, NeoMetadata):