Repository: idaholab/MontePy

Issue:
Lack of data enforcement in syntax node is showing up again
**Describe the bug**

While working with a somewhat complicated model I wanted to see the exact MCNP input so I tried `cell._tree.format()` (I know rude, but I'll open another issue about this). This then lead to the error:

``` python
In [17]: cell._tree.format()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[17], line 1
----> 1 cell._tree.format()

File montepy/input_parser/syntax_node.py:185, in SyntaxNode.format(self)
    183             ret += node.format()
    184     else:
--> 185         ret += node.format()
    186 return ret

File /montepy/input_parser/syntax_node.py:239, in GeometryTree.format(self)
    237 ret = ""
    238 for node in self.nodes.values():
--> 239     ret += node.format()
    240 return ret

AttributeError: 'dict' object has no attribute 'format'
```

This is extremely similar to #204. 

I think it's finally time to get to the bottom of things and stop generating dictionaries instead of `GeometryTree`

**To Reproduce**
MWE of input:

```
100103  3926  5.00000E-02 (-102002          -36425  36426):   $ comment
                          (-102002  169239  -36426  36427)    u=424 
```


**Expected behavior**
No error.



**Additional context**
Version: `0.3.0dev1`

Discussion:

Patch:
diff --git a/montepy/__init__.py b/montepy/__init__.py
index 3d966e18..93cf0e4f 100644
--- a/montepy/__init__.py
+++ b/montepy/__init__.py
@@ -36,6 +36,7 @@ except ImportError:
     except ImportError:
         __version__ = "Undefined"
 
+
 # enable deprecated warnings for users
 if not sys.warnoptions:
     import os, warnings
diff --git a/montepy/input_parser/cell_parser.py b/montepy/input_parser/cell_parser.py
index 567c9963..8585a649 100644
--- a/montepy/input_parser/cell_parser.py
+++ b/montepy/input_parser/cell_parser.py
@@ -75,7 +75,7 @@ class CellParser(MCNP_Parser):
     def geometry_expr(self, p):
         left = p.geometry_expr
         right = p.geometry_term
-        nodes = {"left": left.nodes, "operator": p.union, "right": right.nodes}
+        nodes = {"left": left, "operator": p.union, "right": right}
         return syntax_node.GeometryTree("union", nodes, ":", left, right)
 
     @_("geometry_term")
@@ -112,7 +112,7 @@ class CellParser(MCNP_Parser):
         for node in node_iter:
             new_tree = syntax_node.GeometryTree(
                 "intersection",
-                {"left": left, "operator": None, "right": node},
+                {"left": left, "operator": syntax_node.PaddingNode(), "right": node},
                 "*",
                 left,
                 node,
@@ -142,7 +142,10 @@ class CellParser(MCNP_Parser):
 
     @_("COMPLEMENT geometry_factory")
     def geometry_factor(self, p):
-        nodes = {"operator": p.COMPLEMENT, "left": p.geometry_factory}
+        nodes = {
+            "operator": syntax_node.PaddingNode(p.COMPLEMENT),
+            "left": p.geometry_factory,
+        }
         return syntax_node.GeometryTree(
             "complement",
             nodes,
diff --git a/montepy/input_parser/syntax_node.py b/montepy/input_parser/syntax_node.py
index b6c1b6d1..496a7280 100644
--- a/montepy/input_parser/syntax_node.py
+++ b/montepy/input_parser/syntax_node.py
@@ -222,6 +222,7 @@ class GeometryTree(SyntaxNodeBase):
 
     def __init__(self, name, tokens, op, left, right=None):
         super().__init__(name)
+        assert all(list(map(lambda v: isinstance(v, SyntaxNodeBase), tokens.values())))
         self._nodes = tokens
         self._operator = Operator(op)
         self._left_side = left
@@ -241,19 +242,8 @@ class GeometryTree(SyntaxNodeBase):
 
     @property
     def comments(self):
-        for key, node in self.nodes.items():
-            if isinstance(node, SyntaxNodeBase):
-                yield from node.comments
-            elif isinstance(node, dict):
-                yield from GeometryTree._recurse_comments(node)
-
-    @staticmethod
-    def _recurse_comments(tree):
-        for node in tree.values():
-            if isinstance(node, SyntaxNodeBase):
-                yield from node.comments
-            elif isinstance(node, dict):
-                yield from GeometryTree._recurse_comments(node)
+        for node in self.nodes.values():
+            yield from node.comments
 
     @property
     def left(self):


Test patch:
diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py
index 9285e4e7..fabcf147 100644
--- a/tests/test_edge_cases.py
+++ b/tests/test_edge_cases.py
@@ -176,3 +176,4 @@ class EdgeCaseTests(TestCase):
         cell = montepy.Cell(input)
         # this step caused an error for #163
         cell.comments
+        cell._tree.format()
diff --git a/tests/test_geometry.py b/tests/test_geometry.py
index 32c0a3ff..661e474f 100644
--- a/tests/test_geometry.py
+++ b/tests/test_geometry.py
@@ -10,7 +10,7 @@ from montepy.surfaces.half_space import HalfSpace, UnitHalfSpace
 class TestHalfSpaceUnit(TestCase):
     def test_init(self):
         surface = montepy.surfaces.CylinderOnAxis()
-        node = montepy.input_parser.syntax_node.GeometryTree("hi", [], "*", " ", " ")
+        node = montepy.input_parser.syntax_node.GeometryTree("hi", {}, "*", " ", " ")
         half_space = HalfSpace(+surface, Operator.UNION, -surface, node)
         self.assertIs(half_space.operator, Operator.UNION)
         self.assertEqual(half_space.left, +surface)