Repository: CMakePP/CMinx (language: python)

Issue:
[BUG] Dangling doccomments result in a `mismatched input <EOF>` error
**Describe the bug**
Processing a CMake file that has a doccomment not attached to any other entity results in a `mismatched input <EOF>` error from Antlr.

**Expected behavior**
Since a dangling doccomment is not considered "proper" syntax, how we should handle this is up for debate. At the very least, CMinx should not vomit a stacktrace in such a case. It should probably warn the user, and either ignore the comment or stick it in a simple text block in the RST.

**Additional context**
Found after merging CMakePP/CMaize#102, during the Build-Documentation workflow.



Discussion / hints:

Fix patch:
diff --git a/src/cminx/aggregator.py b/src/cminx/aggregator.py
index c6b3ccd7..a7861d3b 100755
--- a/src/cminx/aggregator.py
+++ b/src/cminx/aggregator.py
@@ -18,7 +18,8 @@
 from .documentation_types import AttributeDocumentation, FunctionDocumentation, MacroDocumentation, \
     VariableDocumentation, GenericCommandDocumentation, ClassDocumentation, TestDocumentation, SectionDocumentation, \
     MethodDocumentation, VarType, CTestDocumentation, ModuleDocumentation, AbstractCommandDefinitionDocumentation, \
-    OptionDocumentation
+    OptionDocumentation, DanglingDoccomment
+
 from .exceptions import CMakeSyntaxException
 from .parser.CMakeListener import CMakeListener
 # Annoyingly, the Antl4 Python libraries use camelcase since it was originally Java, so we have convention
@@ -42,10 +43,11 @@
 
 
 class DefinitionCommand:
-    def __init__(self, documentation: AbstractCommandDefinitionDocumentation, should_document = True):
+    def __init__(self, documentation: AbstractCommandDefinitionDocumentation, should_document=True):
         self.documentation = documentation
         self.should_document = should_document
 
+
 class DocumentationAggregator(CMakeListener):
     """
     Processes all docstrings and their associated commands, aggregating
@@ -152,7 +154,8 @@ def process_cmake_parse_arguments(self, ctx: CMakeParser.Command_invocationConte
         """
         if len(self.definition_command_stack) > 0:
             last_element = self.definition_command_stack[-1]
-            if last_element.should_document and isinstance(last_element.documentation, AbstractCommandDefinitionDocumentation):
+            if last_element.should_document and isinstance(last_element.documentation,
+                                                           AbstractCommandDefinitionDocumentation):
                 last_element.documentation.has_kwargs = True
 
     def process_ct_add_test(self, ctx: CMakeParser.Command_invocationContext, docstring: str):
@@ -519,6 +522,7 @@ def enterDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
 
         :raise NotImplementedError: If no processor can be found for the command that was documented.
         """
+        self.consumed.append(ctx.bracket_doccomment())
         text = ctx.bracket_doccomment().getText()
         lines = text.split("\n")
 
@@ -535,7 +539,8 @@ def enterDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
         except Exception as e:
             line_num = ctx.command_invocation().start.line
             self.logger.error(
-                f"Caught exception while processing documented command beginning at line number {line_num}")
+                f"Caught exception while processing documented command beginning at line number {line_num}"
+            )
             raise e
 
     def enterCommand_invocation(self, ctx: CMakeParser.Command_invocationContext):
@@ -593,3 +598,12 @@ def enterDocumented_module(self, ctx: CMakeParser.Documented_moduleContext):
         module_name = cleaned_lines[0].replace("@module", "").strip()
         doc = "\n".join(cleaned_lines[1:])
         self.documented.append(ModuleDocumentation(module_name, doc))
+
+    def enterBracket_doccomment(self, ctx: CMakeParser.Bracket_doccommentContext):
+        if ctx not in self.consumed:
+            text = ctx.Docstring().getText()
+            cleaned_lines = DocumentationAggregator.clean_doc_lines(text.split("\n")).split("\n")
+            #self.documented.append(DanglingDoccomment("", "\n".join(cleaned_lines)))
+            self.logger.warning(
+                f"Detected dangling doccomment, ignoring. RST may change depending on CMinx version."
+            )
diff --git a/src/cminx/documentation_types.py b/src/cminx/documentation_types.py
index 545e58b5..399fc908 100644
--- a/src/cminx/documentation_types.py
+++ b/src/cminx/documentation_types.py
@@ -423,3 +423,14 @@ def process(self, writer: RSTWriter):
         module = writer.directive("module", self.name)
         if self.doc is not None and len(self.doc) != 0:
             module.text(self.doc)
+
+
+@dataclass
+class DanglingDoccomment(DocumentationType):
+    """
+    Represents a doccomment that is not attached to any other entity.
+    This documentation type's name is left unused, and the doc contains
+    the cleaned text of the dangling doccomment.
+    """
+    def process(self, writer: RSTWriter):
+        writer.text(self.doc)
diff --git a/src/cminx/parser/CMake.g4 b/src/cminx/parser/CMake.g4
index e8d3304f..dbd41d72 100644
--- a/src/cminx/parser/CMake.g4
+++ b/src/cminx/parser/CMake.g4
@@ -19,12 +19,20 @@ Changes:
 	Added documented_command to list of expected parser rules
 	Added bracket_doccomment to parser rules
 	Added Docstring to lexer rules
+	Added bracket_doccomment to top-level parse rule, enabling dangling doccomments
+*/
+
+/*Begin CMinx*/
+
+/*
+    WARNING: This grammar is ambiguous because of the bracket_doccomment alternative.
+    Ambiguity is resolved via order of alternatives, ensure order is consistent
+    with the desired precedence.
 */
 cmake_file
-	: documented_module? (documented_command | command_invocation)* EOF
+	: documented_module? (documented_command | command_invocation | bracket_doccomment)* EOF
 	;
 
-/*Begin CMinx*/
 
 documented_command
 	: bracket_doccomment command_invocation
@@ -39,11 +47,11 @@ bracket_doccomment
     ;
 
 Module_docstring
-    : Doccomment_start Space? '@module' (Space Unquoted_argument)? .*? '#]]'
+    : Doccomment_start Space? '@module' (Space Unquoted_argument)? .*? Blockcomment_end
     ;
 
 Docstring
-        : Doccomment_start .*? '#]]'
+        : Doccomment_start Space? .*? Blockcomment_end
 	;
 
 
diff --git a/src/cminx/parser/CMake.interp b/src/cminx/parser/CMake.interp
index af5c0780..114e4873 100644
--- a/src/cminx/parser/CMake.interp
+++ b/src/cminx/parser/CMake.interp
@@ -45,4 +45,4 @@ compound_argument
 
 
 atn:
-[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 17, 59, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 3, 2, 5, 2, 18, 10, 2, 3, 2, 3, 2, 7, 2, 22, 10, 2, 12, 2, 14, 2, 25, 11, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 40, 10, 6, 12, 6, 14, 6, 43, 11, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 7, 8, 52, 10, 8, 12, 8, 14, 8, 55, 11, 8, 3, 8, 3, 8, 3, 8, 2, 2, 9, 2, 4, 6, 8, 10, 12, 14, 2, 3, 4, 2, 9, 10, 12, 13, 2, 58, 2, 17, 3, 2, 2, 2, 4, 28, 3, 2, 2, 2, 6, 31, 3, 2, 2, 2, 8, 33, 3, 2, 2, 2, 10, 35, 3, 2, 2, 2, 12, 46, 3, 2, 2, 2, 14, 48, 3, 2, 2, 2, 16, 18, 5, 6, 4, 2, 17, 16, 3, 2, 2, 2, 17, 18, 3, 2, 2, 2, 18, 23, 3, 2, 2, 2, 19, 22, 5, 4, 3, 2, 20, 22, 5, 10, 6, 2, 21, 19, 3, 2, 2, 2, 21, 20, 3, 2, 2, 2, 22, 25, 3, 2, 2, 2, 23, 21, 3, 2, 2, 2, 23, 24, 3, 2, 2, 2, 24, 26, 3, 2, 2, 2, 25, 23, 3, 2, 2, 2, 26, 27, 7, 2, 2, 3, 27, 3, 3, 2, 2, 2, 28, 29, 5, 8, 5, 2, 29, 30, 5, 10, 6, 2, 30, 5, 3, 2, 2, 2, 31, 32, 7, 5, 2, 2, 32, 7, 3, 2, 2, 2, 33, 34, 7, 6, 2, 2, 34, 9, 3, 2, 2, 2, 35, 36, 7, 9, 2, 2, 36, 41, 7, 3, 2, 2, 37, 40, 5, 12, 7, 2, 38, 40, 5, 14, 8, 2, 39, 37, 3, 2, 2, 2, 39, 38, 3, 2, 2, 2, 40, 43, 3, 2, 2, 2, 41, 39, 3, 2, 2, 2, 41, 42, 3, 2, 2, 2, 42, 44, 3, 2, 2, 2, 43, 41, 3, 2, 2, 2, 44, 45, 7, 4, 2, 2, 45, 11, 3, 2, 2, 2, 46, 47, 9, 2, 2, 2, 47, 13, 3, 2, 2, 2, 48, 53, 7, 3, 2, 2, 49, 52, 5, 12, 7, 2, 50, 52, 5, 14, 8, 2, 51, 49, 3, 2, 2, 2, 51, 50, 3, 2, 2, 2, 52, 55, 3, 2, 2, 2, 53, 51, 3, 2, 2, 2, 53, 54, 3, 2, 2, 2, 54, 56, 3, 2, 2, 2, 55, 53, 3, 2, 2, 2, 56, 57, 7, 4, 2, 2, 57, 15, 3, 2, 2, 2, 9, 17, 21, 23, 39, 41, 51, 53]
\ No newline at end of file
+[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 17, 60, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 3, 2, 5, 2, 18, 10, 2, 3, 2, 3, 2, 3, 2, 7, 2, 23, 10, 2, 12, 2, 14, 2, 26, 11, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 41, 10, 6, 12, 6, 14, 6, 44, 11, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 7, 8, 53, 10, 8, 12, 8, 14, 8, 56, 11, 8, 3, 8, 3, 8, 3, 8, 2, 2, 9, 2, 4, 6, 8, 10, 12, 14, 2, 3, 4, 2, 9, 10, 12, 13, 2, 60, 2, 17, 3, 2, 2, 2, 4, 29, 3, 2, 2, 2, 6, 32, 3, 2, 2, 2, 8, 34, 3, 2, 2, 2, 10, 36, 3, 2, 2, 2, 12, 47, 3, 2, 2, 2, 14, 49, 3, 2, 2, 2, 16, 18, 5, 6, 4, 2, 17, 16, 3, 2, 2, 2, 17, 18, 3, 2, 2, 2, 18, 24, 3, 2, 2, 2, 19, 23, 5, 4, 3, 2, 20, 23, 5, 10, 6, 2, 21, 23, 5, 8, 5, 2, 22, 19, 3, 2, 2, 2, 22, 20, 3, 2, 2, 2, 22, 21, 3, 2, 2, 2, 23, 26, 3, 2, 2, 2, 24, 22, 3, 2, 2, 2, 24, 25, 3, 2, 2, 2, 25, 27, 3, 2, 2, 2, 26, 24, 3, 2, 2, 2, 27, 28, 7, 2, 2, 3, 28, 3, 3, 2, 2, 2, 29, 30, 5, 8, 5, 2, 30, 31, 5, 10, 6, 2, 31, 5, 3, 2, 2, 2, 32, 33, 7, 5, 2, 2, 33, 7, 3, 2, 2, 2, 34, 35, 7, 6, 2, 2, 35, 9, 3, 2, 2, 2, 36, 37, 7, 9, 2, 2, 37, 42, 7, 3, 2, 2, 38, 41, 5, 12, 7, 2, 39, 41, 5, 14, 8, 2, 40, 38, 3, 2, 2, 2, 40, 39, 3, 2, 2, 2, 41, 44, 3, 2, 2, 2, 42, 40, 3, 2, 2, 2, 42, 43, 3, 2, 2, 2, 43, 45, 3, 2, 2, 2, 44, 42, 3, 2, 2, 2, 45, 46, 7, 4, 2, 2, 46, 11, 3, 2, 2, 2, 47, 48, 9, 2, 2, 2, 48, 13, 3, 2, 2, 2, 49, 54, 7, 3, 2, 2, 50, 53, 5, 12, 7, 2, 51, 53, 5, 14, 8, 2, 52, 50, 3, 2, 2, 2, 52, 51, 3, 2, 2, 2, 53, 56, 3, 2, 2, 2, 54, 52, 3, 2, 2, 2, 54, 55, 3, 2, 2, 2, 55, 57, 3, 2, 2, 2, 56, 54, 3, 2, 2, 2, 57, 58, 7, 4, 2, 2, 58, 15, 3, 2, 2, 2, 9, 17, 22, 24, 40, 42, 52, 54]
\ No newline at end of file
diff --git a/src/cminx/parser/CMakeLexer.interp b/src/cminx/parser/CMakeLexer.interp
index 3b7e277b..bafa7aa0 100644
--- a/src/cminx/parser/CMakeLexer.interp
+++ b/src/cminx/parser/CMakeLexer.interp
@@ -64,4 +64,4 @@ mode names:
 DEFAULT_MODE
 
 atn:
-[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 17, 230, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 5, 4, 50, 10, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 63, 10, 4, 3, 4, 7, 4, 66, 10, 4, 12, 4, 14, 4, 69, 11, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 7, 5, 77, 10, 5, 12, 5, 14, 5, 80, 11, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 7, 8, 97, 10, 8, 12, 8, 14, 8, 100, 11, 8, 3, 9, 3, 9, 6, 9, 104, 10, 9, 13, 9, 14, 9, 105, 3, 10, 3, 10, 3, 10, 5, 10, 111, 10, 10, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 5, 12, 122, 10, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 7, 14, 131, 10, 14, 12, 14, 14, 14, 134, 11, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 5, 15, 141, 10, 15, 3, 15, 5, 15, 144, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 7, 17, 156, 10, 17, 12, 17, 14, 17, 159, 11, 17, 3, 17, 5, 17, 162, 10, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 7, 19, 176, 10, 19, 12, 19, 14, 19, 179, 11, 19, 3, 19, 3, 19, 7, 19, 183, 10, 19, 12, 19, 14, 19, 186, 11, 19, 3, 19, 3, 19, 7, 19, 190, 10, 19, 12, 19, 14, 19, 193, 11, 19, 3, 19, 3, 19, 7, 19, 197, 10, 19, 12, 19, 14, 19, 200, 11, 19, 5, 19, 202, 10, 19, 3, 19, 3, 19, 5, 19, 206, 10, 19, 3, 19, 5, 19, 209, 10, 19, 3, 19, 3, 19, 3, 20, 3, 20, 5, 20, 215, 10, 20, 3, 20, 6, 20, 218, 10, 20, 13, 20, 14, 20, 219, 3, 20, 3, 20, 3, 21, 6, 21, 225, 10, 21, 13, 21, 14, 21, 226, 3, 21, 3, 21, 5, 67, 78, 157, 2, 22, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 2, 23, 2, 25, 2, 27, 12, 29, 2, 31, 13, 33, 2, 35, 14, 37, 15, 39, 16, 41, 17, 3, 2, 12, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 8, 2, 11, 12, 15, 15, 34, 34, 36, 37, 42, 43, 94, 94, 6, 2, 50, 59, 61, 61, 67, 92, 99, 124, 4, 2, 36, 36, 94, 94, 6, 2, 12, 12, 15, 15, 63, 63, 93, 93, 4, 2, 12, 12, 15, 15, 5, 2, 12, 12, 15, 15, 93, 93, 3, 3, 12, 12, 4, 2, 11, 11, 34, 34, 2, 255, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 3, 43, 3, 2, 2, 2, 5, 45, 3, 2, 2, 2, 7, 47, 3, 2, 2, 2, 9, 74, 3, 2, 2, 2, 11, 85, 3, 2, 2, 2, 13, 90, 3, 2, 2, 2, 15, 94, 3, 2, 2, 2, 17, 103, 3, 2, 2, 2, 19, 110, 3, 2, 2, 2, 21, 112, 3, 2, 2, 2, 23, 121, 3, 2, 2, 2, 25, 123, 3, 2, 2, 2, 27, 126, 3, 2, 2, 2, 29, 137, 3, 2, 2, 2, 31, 145, 3, 2, 2, 2, 33, 161, 3, 2, 2, 2, 35, 163, 3, 2, 2, 2, 37, 171, 3, 2, 2, 2, 39, 217, 3, 2, 2, 2, 41, 224, 3, 2, 2, 2, 43, 44, 7, 42, 2, 2, 44, 4, 3, 2, 2, 2, 45, 46, 7, 43, 2, 2, 46, 6, 3, 2, 2, 2, 47, 49, 5, 11, 6, 2, 48, 50, 5, 41, 21, 2, 49, 48, 3, 2, 2, 2, 49, 50, 3, 2, 2, 2, 50, 51, 3, 2, 2, 2, 51, 52, 7, 66, 2, 2, 52, 53, 7, 111, 2, 2, 53, 54, 7, 113, 2, 2, 54, 55, 7, 102, 2, 2, 55, 56, 7, 119, 2, 2, 56, 57, 7, 110, 2, 2, 57, 58, 7, 103, 2, 2, 58, 62, 3, 2, 2, 2, 59, 60, 5, 41, 21, 2, 60, 61, 5, 17, 9, 2, 61, 63, 3, 2, 2, 2, 62, 59, 3, 2, 2, 2, 62, 63, 3, 2, 2, 2, 63, 67, 3, 2, 2, 2, 64, 66, 11, 2, 2, 2, 65, 64, 3, 2, 2, 2, 66, 69, 3, 2, 2, 2, 67, 68, 3, 2, 2, 2, 67, 65, 3, 2, 2, 2, 68, 70, 3, 2, 2, 2, 69, 67, 3, 2, 2, 2, 70, 71, 7, 37, 2, 2, 71, 72, 7, 95, 2, 2, 72, 73, 7, 95, 2, 2, 73, 8, 3, 2, 2, 2, 74, 78, 5, 11, 6, 2, 75, 77, 11, 2, 2, 2, 76, 75, 3, 2, 2, 2, 77, 80, 3, 2, 2, 2, 78, 79, 3, 2, 2, 2, 78, 76, 3, 2, 2, 2, 79, 81, 3, 2, 2, 2, 80, 78, 3, 2, 2, 2, 81, 82, 7, 37, 2, 2, 82, 83, 7, 95, 2, 2, 83, 84, 7, 95, 2, 2, 84, 10, 3, 2, 2, 2, 85, 86, 7, 37, 2, 2, 86, 87, 7, 93, 2, 2, 87, 88, 7, 93, 2, 2, 88, 89, 7, 93, 2, 2, 89, 12, 3, 2, 2, 2, 90, 91, 7, 37, 2, 2, 91, 92, 7, 95, 2, 2, 92, 93, 7, 95, 2, 2, 93, 14, 3, 2, 2, 2, 94, 98, 9, 2, 2, 2, 95, 97, 9, 3, 2, 2, 96, 95, 3, 2, 2, 2, 97, 100, 3, 2, 2, 2, 98, 96, 3, 2, 2, 2, 98, 99, 3, 2, 2, 2, 99, 16, 3, 2, 2, 2, 100, 98, 3, 2, 2, 2, 101, 104, 10, 4, 2, 2, 102, 104, 5, 19, 10, 2, 103, 101, 3, 2, 2, 2, 103, 102, 3, 2, 2, 2, 104, 105, 3, 2, 2, 2, 105, 103, 3, 2, 2, 2, 105, 106, 3, 2, 2, 2, 106, 18, 3, 2, 2, 2, 107, 111, 5, 21, 11, 2, 108, 111, 5, 23, 12, 2, 109, 111, 5, 25, 13, 2, 110, 107, 3, 2, 2, 2, 110, 108, 3, 2, 2, 2, 110, 109, 3, 2, 2, 2, 111, 20, 3, 2, 2, 2, 112, 113, 7, 94, 2, 2, 113, 114, 10, 5, 2, 2, 114, 22, 3, 2, 2, 2, 115, 116, 7, 94, 2, 2, 116, 122, 7, 118, 2, 2, 117, 118, 7, 94, 2, 2, 118, 122, 7, 116, 2, 2, 119, 120, 7, 94, 2, 2, 120, 122, 7, 112, 2, 2, 121, 115, 3, 2, 2, 2, 121, 117, 3, 2, 2, 2, 121, 119, 3, 2, 2, 2, 122, 24, 3, 2, 2, 2, 123, 124, 7, 94, 2, 2, 124, 125, 7, 61, 2, 2, 125, 26, 3, 2, 2, 2, 126, 132, 7, 36, 2, 2, 127, 131, 10, 6, 2, 2, 128, 131, 5, 19, 10, 2, 129, 131, 5, 29, 15, 2, 130, 127, 3, 2, 2, 2, 130, 128, 3, 2, 2, 2, 130, 129, 3, 2, 2, 2, 131, 134, 3, 2, 2, 2, 132, 130, 3, 2, 2, 2, 132, 133, 3, 2, 2, 2, 133, 135, 3, 2, 2, 2, 134, 132, 3, 2, 2, 2, 135, 136, 7, 36, 2, 2, 136, 28, 3, 2, 2, 2, 137, 143, 7, 94, 2, 2, 138, 140, 7, 15, 2, 2, 139, 141, 7, 12, 2, 2, 140, 139, 3, 2, 2, 2, 140, 141, 3, 2, 2, 2, 141, 144, 3, 2, 2, 2, 142, 144, 7, 12, 2, 2, 143, 138, 3, 2, 2, 2, 143, 142, 3, 2, 2, 2, 144, 30, 3, 2, 2, 2, 145, 146, 7, 93, 2, 2, 146, 147, 5, 33, 17, 2, 147, 148, 7, 95, 2, 2, 148, 32, 3, 2, 2, 2, 149, 150, 7, 63, 2, 2, 150, 151, 5, 33, 17, 2, 151, 152, 7, 63, 2, 2, 152, 162, 3, 2, 2, 2, 153, 157, 7, 93, 2, 2, 154, 156, 11, 2, 2, 2, 155, 154, 3, 2, 2, 2, 156, 159, 3, 2, 2, 2, 157, 158, 3, 2, 2, 2, 157, 155, 3, 2, 2, 2, 158, 160, 3, 2, 2, 2, 159, 157, 3, 2, 2, 2, 160, 162, 7, 95, 2, 2, 161, 149, 3, 2, 2, 2, 161, 153, 3, 2, 2, 2, 162, 34, 3, 2, 2, 2, 163, 164, 7, 37, 2, 2, 164, 165, 7, 93, 2, 2, 165, 166, 3, 2, 2, 2, 166, 167, 5, 33, 17, 2, 167, 168, 7, 95, 2, 2, 168, 169, 3, 2, 2, 2, 169, 170, 8, 18, 2, 2, 170, 36, 3, 2, 2, 2, 171, 201, 7, 37, 2, 2, 172, 202, 3, 2, 2, 2, 173, 177, 7, 93, 2, 2, 174, 176, 7, 63, 2, 2, 175, 174, 3, 2, 2, 2, 176, 179, 3, 2, 2, 2, 177, 175, 3, 2, 2, 2, 177, 178, 3, 2, 2, 2, 178, 202, 3, 2, 2, 2, 179, 177, 3, 2, 2, 2, 180, 184, 7, 93, 2, 2, 181, 183, 7, 63, 2, 2, 182, 181, 3, 2, 2, 2, 183, 186, 3, 2, 2, 2, 184, 182, 3, 2, 2, 2, 184, 185, 3, 2, 2, 2, 185, 187, 3, 2, 2, 2, 186, 184, 3, 2, 2, 2, 187, 191, 10, 7, 2, 2, 188, 190, 10, 8, 2, 2, 189, 188, 3, 2, 2, 2, 190, 193, 3, 2, 2, 2, 191, 189, 3, 2, 2, 2, 191, 192, 3, 2, 2, 2, 192, 202, 3, 2, 2, 2, 193, 191, 3, 2, 2, 2, 194, 198, 10, 9, 2, 2, 195, 197, 10, 8, 2, 2, 196, 195, 3, 2, 2, 2, 197, 200, 3, 2, 2, 2, 198, 196, 3, 2, 2, 2, 198, 199, 3, 2, 2, 2, 199, 202, 3, 2, 2, 2, 200, 198, 3, 2, 2, 2, 201, 172, 3, 2, 2, 2, 201, 173, 3, 2, 2, 2, 201, 180, 3, 2, 2, 2, 201, 194, 3, 2, 2, 2, 202, 208, 3, 2, 2, 2, 203, 205, 7, 15, 2, 2, 204, 206, 7, 12, 2, 2, 205, 204, 3, 2, 2, 2, 205, 206, 3, 2, 2, 2, 206, 209, 3, 2, 2, 2, 207, 209, 9, 10, 2, 2, 208, 203, 3, 2, 2, 2, 208, 207, 3, 2, 2, 2, 209, 210, 3, 2, 2, 2, 210, 211, 8, 19, 2, 2, 211, 38, 3, 2, 2, 2, 212, 214, 7, 15, 2, 2, 213, 215, 7, 12, 2, 2, 214, 213, 3, 2, 2, 2, 214, 215, 3, 2, 2, 2, 215, 218, 3, 2, 2, 2, 216, 218, 7, 12, 2, 2, 217, 212, 3, 2, 2, 2, 217, 216, 3, 2, 2, 2, 218, 219, 3, 2, 2, 2, 219, 217, 3, 2, 2, 2, 219, 220, 3, 2, 2, 2, 220, 221, 3, 2, 2, 2, 221, 222, 8, 20, 2, 2, 222, 40, 3, 2, 2, 2, 223, 225, 9, 11, 2, 2, 224, 223, 3, 2, 2, 2, 225, 226, 3, 2, 2, 2, 226, 224, 3, 2, 2, 2, 226, 227, 3, 2, 2, 2, 227, 228, 3, 2, 2, 2, 228, 229, 8, 21, 2, 2, 229, 42, 3, 2, 2, 2, 29, 2, 49, 62, 67, 78, 98, 103, 105, 110, 121, 130, 132, 140, 143, 157, 161, 177, 184, 191, 198, 201, 205, 208, 214, 217, 219, 226, 3, 8, 2, 2]
\ No newline at end of file
+[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 17, 229, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 5, 4, 50, 10, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 63, 10, 4, 3, 4, 7, 4, 66, 10, 4, 12, 4, 14, 4, 69, 11, 4, 3, 4, 3, 4, 3, 5, 3, 5, 5, 5, 75, 10, 5, 3, 5, 7, 5, 78, 10, 5, 12, 5, 14, 5, 81, 11, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 7, 8, 96, 10, 8, 12, 8, 14, 8, 99, 11, 8, 3, 9, 3, 9, 6, 9, 103, 10, 9, 13, 9, 14, 9, 104, 3, 10, 3, 10, 3, 10, 5, 10, 110, 10, 10, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 5, 12, 121, 10, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 7, 14, 130, 10, 14, 12, 14, 14, 14, 133, 11, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 5, 15, 140, 10, 15, 3, 15, 5, 15, 143, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 7, 17, 155, 10, 17, 12, 17, 14, 17, 158, 11, 17, 3, 17, 5, 17, 161, 10, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 7, 19, 175, 10, 19, 12, 19, 14, 19, 178, 11, 19, 3, 19, 3, 19, 7, 19, 182, 10, 19, 12, 19, 14, 19, 185, 11, 19, 3, 19, 3, 19, 7, 19, 189, 10, 19, 12, 19, 14, 19, 192, 11, 19, 3, 19, 3, 19, 7, 19, 196, 10, 19, 12, 19, 14, 19, 199, 11, 19, 5, 19, 201, 10, 19, 3, 19, 3, 19, 5, 19, 205, 10, 19, 3, 19, 5, 19, 208, 10, 19, 3, 19, 3, 19, 3, 20, 3, 20, 5, 20, 214, 10, 20, 3, 20, 6, 20, 217, 10, 20, 13, 20, 14, 20, 218, 3, 20, 3, 20, 3, 21, 6, 21, 224, 10, 21, 13, 21, 14, 21, 225, 3, 21, 3, 21, 5, 67, 79, 156, 2, 22, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 2, 23, 2, 25, 2, 27, 12, 29, 2, 31, 13, 33, 2, 35, 14, 37, 15, 39, 16, 41, 17, 3, 2, 12, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 8, 2, 11, 12, 15, 15, 34, 34, 36, 37, 42, 43, 94, 94, 6, 2, 50, 59, 61, 61, 67, 92, 99, 124, 4, 2, 36, 36, 94, 94, 6, 2, 12, 12, 15, 15, 63, 63, 93, 93, 4, 2, 12, 12, 15, 15, 5, 2, 12, 12, 15, 15, 93, 93, 3, 3, 12, 12, 4, 2, 11, 11, 34, 34, 2, 255, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 3, 43, 3, 2, 2, 2, 5, 45, 3, 2, 2, 2, 7, 47, 3, 2, 2, 2, 9, 72, 3, 2, 2, 2, 11, 84, 3, 2, 2, 2, 13, 89, 3, 2, 2, 2, 15, 93, 3, 2, 2, 2, 17, 102, 3, 2, 2, 2, 19, 109, 3, 2, 2, 2, 21, 111, 3, 2, 2, 2, 23, 120, 3, 2, 2, 2, 25, 122, 3, 2, 2, 2, 27, 125, 3, 2, 2, 2, 29, 136, 3, 2, 2, 2, 31, 144, 3, 2, 2, 2, 33, 160, 3, 2, 2, 2, 35, 162, 3, 2, 2, 2, 37, 170, 3, 2, 2, 2, 39, 216, 3, 2, 2, 2, 41, 223, 3, 2, 2, 2, 43, 44, 7, 42, 2, 2, 44, 4, 3, 2, 2, 2, 45, 46, 7, 43, 2, 2, 46, 6, 3, 2, 2, 2, 47, 49, 5, 11, 6, 2, 48, 50, 5, 41, 21, 2, 49, 48, 3, 2, 2, 2, 49, 50, 3, 2, 2, 2, 50, 51, 3, 2, 2, 2, 51, 52, 7, 66, 2, 2, 52, 53, 7, 111, 2, 2, 53, 54, 7, 113, 2, 2, 54, 55, 7, 102, 2, 2, 55, 56, 7, 119, 2, 2, 56, 57, 7, 110, 2, 2, 57, 58, 7, 103, 2, 2, 58, 62, 3, 2, 2, 2, 59, 60, 5, 41, 21, 2, 60, 61, 5, 17, 9, 2, 61, 63, 3, 2, 2, 2, 62, 59, 3, 2, 2, 2, 62, 63, 3, 2, 2, 2, 63, 67, 3, 2, 2, 2, 64, 66, 11, 2, 2, 2, 65, 64, 3, 2, 2, 2, 66, 69, 3, 2, 2, 2, 67, 68, 3, 2, 2, 2, 67, 65, 3, 2, 2, 2, 68, 70, 3, 2, 2, 2, 69, 67, 3, 2, 2, 2, 70, 71, 5, 13, 7, 2, 71, 8, 3, 2, 2, 2, 72, 74, 5, 11, 6, 2, 73, 75, 5, 41, 21, 2, 74, 73, 3, 2, 2, 2, 74, 75, 3, 2, 2, 2, 75, 79, 3, 2, 2, 2, 76, 78, 11, 2, 2, 2, 77, 76, 3, 2, 2, 2, 78, 81, 3, 2, 2, 2, 79, 80, 3, 2, 2, 2, 79, 77, 3, 2, 2, 2, 80, 82, 3, 2, 2, 2, 81, 79, 3, 2, 2, 2, 82, 83, 5, 13, 7, 2, 83, 10, 3, 2, 2, 2, 84, 85, 7, 37, 2, 2, 85, 86, 7, 93, 2, 2, 86, 87, 7, 93, 2, 2, 87, 88, 7, 93, 2, 2, 88, 12, 3, 2, 2, 2, 89, 90, 7, 37, 2, 2, 90, 91, 7, 95, 2, 2, 91, 92, 7, 95, 2, 2, 92, 14, 3, 2, 2, 2, 93, 97, 9, 2, 2, 2, 94, 96, 9, 3, 2, 2, 95, 94, 3, 2, 2, 2, 96, 99, 3, 2, 2, 2, 97, 95, 3, 2, 2, 2, 97, 98, 3, 2, 2, 2, 98, 16, 3, 2, 2, 2, 99, 97, 3, 2, 2, 2, 100, 103, 10, 4, 2, 2, 101, 103, 5, 19, 10, 2, 102, 100, 3, 2, 2, 2, 102, 101, 3, 2, 2, 2, 103, 104, 3, 2, 2, 2, 104, 102, 3, 2, 2, 2, 104, 105, 3, 2, 2, 2, 105, 18, 3, 2, 2, 2, 106, 110, 5, 21, 11, 2, 107, 110, 5, 23, 12, 2, 108, 110, 5, 25, 13, 2, 109, 106, 3, 2, 2, 2, 109, 107, 3, 2, 2, 2, 109, 108, 3, 2, 2, 2, 110, 20, 3, 2, 2, 2, 111, 112, 7, 94, 2, 2, 112, 113, 10, 5, 2, 2, 113, 22, 3, 2, 2, 2, 114, 115, 7, 94, 2, 2, 115, 121, 7, 118, 2, 2, 116, 117, 7, 94, 2, 2, 117, 121, 7, 116, 2, 2, 118, 119, 7, 94, 2, 2, 119, 121, 7, 112, 2, 2, 120, 114, 3, 2, 2, 2, 120, 116, 3, 2, 2, 2, 120, 118, 3, 2, 2, 2, 121, 24, 3, 2, 2, 2, 122, 123, 7, 94, 2, 2, 123, 124, 7, 61, 2, 2, 124, 26, 3, 2, 2, 2, 125, 131, 7, 36, 2, 2, 126, 130, 10, 6, 2, 2, 127, 130, 5, 19, 10, 2, 128, 130, 5, 29, 15, 2, 129, 126, 3, 2, 2, 2, 129, 127, 3, 2, 2, 2, 129, 128, 3, 2, 2, 2, 130, 133, 3, 2, 2, 2, 131, 129, 3, 2, 2, 2, 131, 132, 3, 2, 2, 2, 132, 134, 3, 2, 2, 2, 133, 131, 3, 2, 2, 2, 134, 135, 7, 36, 2, 2, 135, 28, 3, 2, 2, 2, 136, 142, 7, 94, 2, 2, 137, 139, 7, 15, 2, 2, 138, 140, 7, 12, 2, 2, 139, 138, 3, 2, 2, 2, 139, 140, 3, 2, 2, 2, 140, 143, 3, 2, 2, 2, 141, 143, 7, 12, 2, 2, 142, 137, 3, 2, 2, 2, 142, 141, 3, 2, 2, 2, 143, 30, 3, 2, 2, 2, 144, 145, 7, 93, 2, 2, 145, 146, 5, 33, 17, 2, 146, 147, 7, 95, 2, 2, 147, 32, 3, 2, 2, 2, 148, 149, 7, 63, 2, 2, 149, 150, 5, 33, 17, 2, 150, 151, 7, 63, 2, 2, 151, 161, 3, 2, 2, 2, 152, 156, 7, 93, 2, 2, 153, 155, 11, 2, 2, 2, 154, 153, 3, 2, 2, 2, 155, 158, 3, 2, 2, 2, 156, 157, 3, 2, 2, 2, 156, 154, 3, 2, 2, 2, 157, 159, 3, 2, 2, 2, 158, 156, 3, 2, 2, 2, 159, 161, 7, 95, 2, 2, 160, 148, 3, 2, 2, 2, 160, 152, 3, 2, 2, 2, 161, 34, 3, 2, 2, 2, 162, 163, 7, 37, 2, 2, 163, 164, 7, 93, 2, 2, 164, 165, 3, 2, 2, 2, 165, 166, 5, 33, 17, 2, 166, 167, 7, 95, 2, 2, 167, 168, 3, 2, 2, 2, 168, 169, 8, 18, 2, 2, 169, 36, 3, 2, 2, 2, 170, 200, 7, 37, 2, 2, 171, 201, 3, 2, 2, 2, 172, 176, 7, 93, 2, 2, 173, 175, 7, 63, 2, 2, 174, 173, 3, 2, 2, 2, 175, 178, 3, 2, 2, 2, 176, 174, 3, 2, 2, 2, 176, 177, 3, 2, 2, 2, 177, 201, 3, 2, 2, 2, 178, 176, 3, 2, 2, 2, 179, 183, 7, 93, 2, 2, 180, 182, 7, 63, 2, 2, 181, 180, 3, 2, 2, 2, 182, 185, 3, 2, 2, 2, 183, 181, 3, 2, 2, 2, 183, 184, 3, 2, 2, 2, 184, 186, 3, 2, 2, 2, 185, 183, 3, 2, 2, 2, 186, 190, 10, 7, 2, 2, 187, 189, 10, 8, 2, 2, 188, 187, 3, 2, 2, 2, 189, 192, 3, 2, 2, 2, 190, 188, 3, 2, 2, 2, 190, 191, 3, 2, 2, 2, 191, 201, 3, 2, 2, 2, 192, 190, 3, 2, 2, 2, 193, 197, 10, 9, 2, 2, 194, 196, 10, 8, 2, 2, 195, 194, 3, 2, 2, 2, 196, 199, 3, 2, 2, 2, 197, 195, 3, 2, 2, 2, 197, 198, 3, 2, 2, 2, 198, 201, 3, 2, 2, 2, 199, 197, 3, 2, 2, 2, 200, 171, 3, 2, 2, 2, 200, 172, 3, 2, 2, 2, 200, 179, 3, 2, 2, 2, 200, 193, 3, 2, 2, 2, 201, 207, 3, 2, 2, 2, 202, 204, 7, 15, 2, 2, 203, 205, 7, 12, 2, 2, 204, 203, 3, 2, 2, 2, 204, 205, 3, 2, 2, 2, 205, 208, 3, 2, 2, 2, 206, 208, 9, 10, 2, 2, 207, 202, 3, 2, 2, 2, 207, 206, 3, 2, 2, 2, 208, 209, 3, 2, 2, 2, 209, 210, 8, 19, 2, 2, 210, 38, 3, 2, 2, 2, 211, 213, 7, 15, 2, 2, 212, 214, 7, 12, 2, 2, 213, 212, 3, 2, 2, 2, 213, 214, 3, 2, 2, 2, 214, 217, 3, 2, 2, 2, 215, 217, 7, 12, 2, 2, 216, 211, 3, 2, 2, 2, 216, 215, 3, 2, 2, 2, 217, 218, 3, 2, 2, 2, 218, 216, 3, 2, 2, 2, 218, 219, 3, 2, 2, 2, 219, 220, 3, 2, 2, 2, 220, 221, 8, 20, 2, 2, 221, 40, 3, 2, 2, 2, 222, 224, 9, 11, 2, 2, 223, 222, 3, 2, 2, 2, 224, 225, 3, 2, 2, 2, 225, 223, 3, 2, 2, 2, 225, 226, 3, 2, 2, 2, 226, 227, 3, 2, 2, 2, 227, 228, 8, 21, 2, 2, 228, 42, 3, 2, 2, 2, 30, 2, 49, 62, 67, 74, 79, 97, 102, 104, 109, 120, 129, 131, 139, 142, 156, 160, 176, 183, 190, 197, 200, 204, 207, 213, 216, 218, 225, 3, 8, 2, 2]
\ No newline at end of file
diff --git a/src/cminx/parser/CMakeLexer.py b/src/cminx/parser/CMakeLexer.py
index c898f732..ecf31f79 100644
--- a/src/cminx/parser/CMakeLexer.py
+++ b/src/cminx/parser/CMakeLexer.py
@@ -8,29 +8,29 @@
 def serializedATN():
     with StringIO() as buf:
         buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\21")
-        buf.write("\u00e6\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7")
+        buf.write("\u00e5\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7")
         buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r")
         buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23")
         buf.write("\t\23\4\24\t\24\4\25\t\25\3\2\3\2\3\3\3\3\3\4\3\4\5\4")
         buf.write("\62\n\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\5")
-        buf.write("\4?\n\4\3\4\7\4B\n\4\f\4\16\4E\13\4\3\4\3\4\3\4\3\4\3")
-        buf.write("\5\3\5\7\5M\n\5\f\5\16\5P\13\5\3\5\3\5\3\5\3\5\3\6\3\6")
-        buf.write("\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\b\3\b\7\ba\n\b\f\b\16\b")
-        buf.write("d\13\b\3\t\3\t\6\th\n\t\r\t\16\ti\3\n\3\n\3\n\5\no\n\n")
-        buf.write("\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\5\fz\n\f\3\r\3")
-        buf.write("\r\3\r\3\16\3\16\3\16\3\16\7\16\u0083\n\16\f\16\16\16")
-        buf.write("\u0086\13\16\3\16\3\16\3\17\3\17\3\17\5\17\u008d\n\17")
-        buf.write("\3\17\5\17\u0090\n\17\3\20\3\20\3\20\3\20\3\21\3\21\3")
-        buf.write("\21\3\21\3\21\3\21\7\21\u009c\n\21\f\21\16\21\u009f\13")
-        buf.write("\21\3\21\5\21\u00a2\n\21\3\22\3\22\3\22\3\22\3\22\3\22")
-        buf.write("\3\22\3\22\3\23\3\23\3\23\3\23\7\23\u00b0\n\23\f\23\16")
-        buf.write("\23\u00b3\13\23\3\23\3\23\7\23\u00b7\n\23\f\23\16\23\u00ba")
-        buf.write("\13\23\3\23\3\23\7\23\u00be\n\23\f\23\16\23\u00c1\13\23")
-        buf.write("\3\23\3\23\7\23\u00c5\n\23\f\23\16\23\u00c8\13\23\5\23")
-        buf.write("\u00ca\n\23\3\23\3\23\5\23\u00ce\n\23\3\23\5\23\u00d1")
-        buf.write("\n\23\3\23\3\23\3\24\3\24\5\24\u00d7\n\24\3\24\6\24\u00da")
-        buf.write("\n\24\r\24\16\24\u00db\3\24\3\24\3\25\6\25\u00e1\n\25")
-        buf.write("\r\25\16\25\u00e2\3\25\3\25\5CN\u009d\2\26\3\3\5\4\7\5")
+        buf.write("\4?\n\4\3\4\7\4B\n\4\f\4\16\4E\13\4\3\4\3\4\3\5\3\5\5")
+        buf.write("\5K\n\5\3\5\7\5N\n\5\f\5\16\5Q\13\5\3\5\3\5\3\6\3\6\3")
+        buf.write("\6\3\6\3\6\3\7\3\7\3\7\3\7\3\b\3\b\7\b`\n\b\f\b\16\bc")
+        buf.write("\13\b\3\t\3\t\6\tg\n\t\r\t\16\th\3\n\3\n\3\n\5\nn\n\n")
+        buf.write("\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\5\fy\n\f\3\r\3")
+        buf.write("\r\3\r\3\16\3\16\3\16\3\16\7\16\u0082\n\16\f\16\16\16")
+        buf.write("\u0085\13\16\3\16\3\16\3\17\3\17\3\17\5\17\u008c\n\17")
+        buf.write("\3\17\5\17\u008f\n\17\3\20\3\20\3\20\3\20\3\21\3\21\3")
+        buf.write("\21\3\21\3\21\3\21\7\21\u009b\n\21\f\21\16\21\u009e\13")
+        buf.write("\21\3\21\5\21\u00a1\n\21\3\22\3\22\3\22\3\22\3\22\3\22")
+        buf.write("\3\22\3\22\3\23\3\23\3\23\3\23\7\23\u00af\n\23\f\23\16")
+        buf.write("\23\u00b2\13\23\3\23\3\23\7\23\u00b6\n\23\f\23\16\23\u00b9")
+        buf.write("\13\23\3\23\3\23\7\23\u00bd\n\23\f\23\16\23\u00c0\13\23")
+        buf.write("\3\23\3\23\7\23\u00c4\n\23\f\23\16\23\u00c7\13\23\5\23")
+        buf.write("\u00c9\n\23\3\23\3\23\5\23\u00cd\n\23\3\23\5\23\u00d0")
+        buf.write("\n\23\3\23\3\23\3\24\3\24\5\24\u00d6\n\24\3\24\6\24\u00d9")
+        buf.write("\n\24\r\24\16\24\u00da\3\24\3\24\3\25\6\25\u00e0\n\25")
+        buf.write("\r\25\16\25\u00e1\3\25\3\25\5CO\u009c\2\26\3\3\5\4\7\5")
         buf.write("\t\6\13\7\r\b\17\t\21\n\23\13\25\2\27\2\31\2\33\f\35\2")
         buf.write("\37\r!\2#\16%\17\'\20)\21\3\2\f\5\2C\\aac|\6\2\62;C\\")
         buf.write("aac|\b\2\13\f\17\17\"\"$%*+^^\6\2\62;==C\\c|\4\2$$^^\6")
@@ -39,76 +39,76 @@ def serializedATN():
         buf.write("\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2")
         buf.write("\2\21\3\2\2\2\2\23\3\2\2\2\2\33\3\2\2\2\2\37\3\2\2\2\2")
         buf.write("#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\3+\3\2\2\2")
-        buf.write("\5-\3\2\2\2\7/\3\2\2\2\tJ\3\2\2\2\13U\3\2\2\2\rZ\3\2\2")
-        buf.write("\2\17^\3\2\2\2\21g\3\2\2\2\23n\3\2\2\2\25p\3\2\2\2\27")
-        buf.write("y\3\2\2\2\31{\3\2\2\2\33~\3\2\2\2\35\u0089\3\2\2\2\37")
-        buf.write("\u0091\3\2\2\2!\u00a1\3\2\2\2#\u00a3\3\2\2\2%\u00ab\3")
-        buf.write("\2\2\2\'\u00d9\3\2\2\2)\u00e0\3\2\2\2+,\7*\2\2,\4\3\2")
+        buf.write("\5-\3\2\2\2\7/\3\2\2\2\tH\3\2\2\2\13T\3\2\2\2\rY\3\2\2")
+        buf.write("\2\17]\3\2\2\2\21f\3\2\2\2\23m\3\2\2\2\25o\3\2\2\2\27")
+        buf.write("x\3\2\2\2\31z\3\2\2\2\33}\3\2\2\2\35\u0088\3\2\2\2\37")
+        buf.write("\u0090\3\2\2\2!\u00a0\3\2\2\2#\u00a2\3\2\2\2%\u00aa\3")
+        buf.write("\2\2\2\'\u00d8\3\2\2\2)\u00df\3\2\2\2+,\7*\2\2,\4\3\2")
         buf.write("\2\2-.\7+\2\2.\6\3\2\2\2/\61\5\13\6\2\60\62\5)\25\2\61")
         buf.write("\60\3\2\2\2\61\62\3\2\2\2\62\63\3\2\2\2\63\64\7B\2\2\64")
         buf.write("\65\7o\2\2\65\66\7q\2\2\66\67\7f\2\2\678\7w\2\289\7n\2")
         buf.write("\29:\7g\2\2:>\3\2\2\2;<\5)\25\2<=\5\21\t\2=?\3\2\2\2>")
         buf.write(";\3\2\2\2>?\3\2\2\2?C\3\2\2\2@B\13\2\2\2A@\3\2\2\2BE\3")
-        buf.write("\2\2\2CD\3\2\2\2CA\3\2\2\2DF\3\2\2\2EC\3\2\2\2FG\7%\2")
-        buf.write("\2GH\7_\2\2HI\7_\2\2I\b\3\2\2\2JN\5\13\6\2KM\13\2\2\2")
-        buf.write("LK\3\2\2\2MP\3\2\2\2NO\3\2\2\2NL\3\2\2\2OQ\3\2\2\2PN\3")
-        buf.write("\2\2\2QR\7%\2\2RS\7_\2\2ST\7_\2\2T\n\3\2\2\2UV\7%\2\2")
-        buf.write("VW\7]\2\2WX\7]\2\2XY\7]\2\2Y\f\3\2\2\2Z[\7%\2\2[\\\7_")
-        buf.write("\2\2\\]\7_\2\2]\16\3\2\2\2^b\t\2\2\2_a\t\3\2\2`_\3\2\2")
-        buf.write("\2ad\3\2\2\2b`\3\2\2\2bc\3\2\2\2c\20\3\2\2\2db\3\2\2\2")
-        buf.write("eh\n\4\2\2fh\5\23\n\2ge\3\2\2\2gf\3\2\2\2hi\3\2\2\2ig")
-        buf.write("\3\2\2\2ij\3\2\2\2j\22\3\2\2\2ko\5\25\13\2lo\5\27\f\2")
-        buf.write("mo\5\31\r\2nk\3\2\2\2nl\3\2\2\2nm\3\2\2\2o\24\3\2\2\2")
-        buf.write("pq\7^\2\2qr\n\5\2\2r\26\3\2\2\2st\7^\2\2tz\7v\2\2uv\7")
-        buf.write("^\2\2vz\7t\2\2wx\7^\2\2xz\7p\2\2ys\3\2\2\2yu\3\2\2\2y")
-        buf.write("w\3\2\2\2z\30\3\2\2\2{|\7^\2\2|}\7=\2\2}\32\3\2\2\2~\u0084")
-        buf.write("\7$\2\2\177\u0083\n\6\2\2\u0080\u0083\5\23\n\2\u0081\u0083")
-        buf.write("\5\35\17\2\u0082\177\3\2\2\2\u0082\u0080\3\2\2\2\u0082")
-        buf.write("\u0081\3\2\2\2\u0083\u0086\3\2\2\2\u0084\u0082\3\2\2\2")
-        buf.write("\u0084\u0085\3\2\2\2\u0085\u0087\3\2\2\2\u0086\u0084\3")
-        buf.write("\2\2\2\u0087\u0088\7$\2\2\u0088\34\3\2\2\2\u0089\u008f")
-        buf.write("\7^\2\2\u008a\u008c\7\17\2\2\u008b\u008d\7\f\2\2\u008c")
-        buf.write("\u008b\3\2\2\2\u008c\u008d\3\2\2\2\u008d\u0090\3\2\2\2")
-        buf.write("\u008e\u0090\7\f\2\2\u008f\u008a\3\2\2\2\u008f\u008e\3")
-        buf.write("\2\2\2\u0090\36\3\2\2\2\u0091\u0092\7]\2\2\u0092\u0093")
-        buf.write("\5!\21\2\u0093\u0094\7_\2\2\u0094 \3\2\2\2\u0095\u0096")
-        buf.write("\7?\2\2\u0096\u0097\5!\21\2\u0097\u0098\7?\2\2\u0098\u00a2")
-        buf.write("\3\2\2\2\u0099\u009d\7]\2\2\u009a\u009c\13\2\2\2\u009b")
-        buf.write("\u009a\3\2\2\2\u009c\u009f\3\2\2\2\u009d\u009e\3\2\2\2")
-        buf.write("\u009d\u009b\3\2\2\2\u009e\u00a0\3\2\2\2\u009f\u009d\3")
-        buf.write("\2\2\2\u00a0\u00a2\7_\2\2\u00a1\u0095\3\2\2\2\u00a1\u0099")
-        buf.write("\3\2\2\2\u00a2\"\3\2\2\2\u00a3\u00a4\7%\2\2\u00a4\u00a5")
-        buf.write("\7]\2\2\u00a5\u00a6\3\2\2\2\u00a6\u00a7\5!\21\2\u00a7")
-        buf.write("\u00a8\7_\2\2\u00a8\u00a9\3\2\2\2\u00a9\u00aa\b\22\2\2")
-        buf.write("\u00aa$\3\2\2\2\u00ab\u00c9\7%\2\2\u00ac\u00ca\3\2\2\2")
-        buf.write("\u00ad\u00b1\7]\2\2\u00ae\u00b0\7?\2\2\u00af\u00ae\3\2")
-        buf.write("\2\2\u00b0\u00b3\3\2\2\2\u00b1\u00af\3\2\2\2\u00b1\u00b2")
-        buf.write("\3\2\2\2\u00b2\u00ca\3\2\2\2\u00b3\u00b1\3\2\2\2\u00b4")
-        buf.write("\u00b8\7]\2\2\u00b5\u00b7\7?\2\2\u00b6\u00b5\3\2\2\2\u00b7")
-        buf.write("\u00ba\3\2\2\2\u00b8\u00b6\3\2\2\2\u00b8\u00b9\3\2\2\2")
-        buf.write("\u00b9\u00bb\3\2\2\2\u00ba\u00b8\3\2\2\2\u00bb\u00bf\n")
-        buf.write("\7\2\2\u00bc\u00be\n\b\2\2\u00bd\u00bc\3\2\2\2\u00be\u00c1")
-        buf.write("\3\2\2\2\u00bf\u00bd\3\2\2\2\u00bf\u00c0\3\2\2\2\u00c0")
-        buf.write("\u00ca\3\2\2\2\u00c1\u00bf\3\2\2\2\u00c2\u00c6\n\t\2\2")
-        buf.write("\u00c3\u00c5\n\b\2\2\u00c4\u00c3\3\2\2\2\u00c5\u00c8\3")
-        buf.write("\2\2\2\u00c6\u00c4\3\2\2\2\u00c6\u00c7\3\2\2\2\u00c7\u00ca")
-        buf.write("\3\2\2\2\u00c8\u00c6\3\2\2\2\u00c9\u00ac\3\2\2\2\u00c9")
-        buf.write("\u00ad\3\2\2\2\u00c9\u00b4\3\2\2\2\u00c9\u00c2\3\2\2\2")
-        buf.write("\u00ca\u00d0\3\2\2\2\u00cb\u00cd\7\17\2\2\u00cc\u00ce")
-        buf.write("\7\f\2\2\u00cd\u00cc\3\2\2\2\u00cd\u00ce\3\2\2\2\u00ce")
-        buf.write("\u00d1\3\2\2\2\u00cf\u00d1\t\n\2\2\u00d0\u00cb\3\2\2\2")
-        buf.write("\u00d0\u00cf\3\2\2\2\u00d1\u00d2\3\2\2\2\u00d2\u00d3\b")
-        buf.write("\23\2\2\u00d3&\3\2\2\2\u00d4\u00d6\7\17\2\2\u00d5\u00d7")
-        buf.write("\7\f\2\2\u00d6\u00d5\3\2\2\2\u00d6\u00d7\3\2\2\2\u00d7")
-        buf.write("\u00da\3\2\2\2\u00d8\u00da\7\f\2\2\u00d9\u00d4\3\2\2\2")
-        buf.write("\u00d9\u00d8\3\2\2\2\u00da\u00db\3\2\2\2\u00db\u00d9\3")
-        buf.write("\2\2\2\u00db\u00dc\3\2\2\2\u00dc\u00dd\3\2\2\2\u00dd\u00de")
-        buf.write("\b\24\2\2\u00de(\3\2\2\2\u00df\u00e1\t\13\2\2\u00e0\u00df")
-        buf.write("\3\2\2\2\u00e1\u00e2\3\2\2\2\u00e2\u00e0\3\2\2\2\u00e2")
-        buf.write("\u00e3\3\2\2\2\u00e3\u00e4\3\2\2\2\u00e4\u00e5\b\25\2")
-        buf.write("\2\u00e5*\3\2\2\2\35\2\61>CNbginy\u0082\u0084\u008c\u008f")
-        buf.write("\u009d\u00a1\u00b1\u00b8\u00bf\u00c6\u00c9\u00cd\u00d0")
-        buf.write("\u00d6\u00d9\u00db\u00e2\3\b\2\2")
+        buf.write("\2\2\2CD\3\2\2\2CA\3\2\2\2DF\3\2\2\2EC\3\2\2\2FG\5\r\7")
+        buf.write("\2G\b\3\2\2\2HJ\5\13\6\2IK\5)\25\2JI\3\2\2\2JK\3\2\2\2")
+        buf.write("KO\3\2\2\2LN\13\2\2\2ML\3\2\2\2NQ\3\2\2\2OP\3\2\2\2OM")
+        buf.write("\3\2\2\2PR\3\2\2\2QO\3\2\2\2RS\5\r\7\2S\n\3\2\2\2TU\7")
+        buf.write("%\2\2UV\7]\2\2VW\7]\2\2WX\7]\2\2X\f\3\2\2\2YZ\7%\2\2Z")
+        buf.write("[\7_\2\2[\\\7_\2\2\\\16\3\2\2\2]a\t\2\2\2^`\t\3\2\2_^")
+        buf.write("\3\2\2\2`c\3\2\2\2a_\3\2\2\2ab\3\2\2\2b\20\3\2\2\2ca\3")
+        buf.write("\2\2\2dg\n\4\2\2eg\5\23\n\2fd\3\2\2\2fe\3\2\2\2gh\3\2")
+        buf.write("\2\2hf\3\2\2\2hi\3\2\2\2i\22\3\2\2\2jn\5\25\13\2kn\5\27")
+        buf.write("\f\2ln\5\31\r\2mj\3\2\2\2mk\3\2\2\2ml\3\2\2\2n\24\3\2")
+        buf.write("\2\2op\7^\2\2pq\n\5\2\2q\26\3\2\2\2rs\7^\2\2sy\7v\2\2")
+        buf.write("tu\7^\2\2uy\7t\2\2vw\7^\2\2wy\7p\2\2xr\3\2\2\2xt\3\2\2")
+        buf.write("\2xv\3\2\2\2y\30\3\2\2\2z{\7^\2\2{|\7=\2\2|\32\3\2\2\2")
+        buf.write("}\u0083\7$\2\2~\u0082\n\6\2\2\177\u0082\5\23\n\2\u0080")
+        buf.write("\u0082\5\35\17\2\u0081~\3\2\2\2\u0081\177\3\2\2\2\u0081")
+        buf.write("\u0080\3\2\2\2\u0082\u0085\3\2\2\2\u0083\u0081\3\2\2\2")
+        buf.write("\u0083\u0084\3\2\2\2\u0084\u0086\3\2\2\2\u0085\u0083\3")
+        buf.write("\2\2\2\u0086\u0087\7$\2\2\u0087\34\3\2\2\2\u0088\u008e")
+        buf.write("\7^\2\2\u0089\u008b\7\17\2\2\u008a\u008c\7\f\2\2\u008b")
+        buf.write("\u008a\3\2\2\2\u008b\u008c\3\2\2\2\u008c\u008f\3\2\2\2")
+        buf.write("\u008d\u008f\7\f\2\2\u008e\u0089\3\2\2\2\u008e\u008d\3")
+        buf.write("\2\2\2\u008f\36\3\2\2\2\u0090\u0091\7]\2\2\u0091\u0092")
+        buf.write("\5!\21\2\u0092\u0093\7_\2\2\u0093 \3\2\2\2\u0094\u0095")
+        buf.write("\7?\2\2\u0095\u0096\5!\21\2\u0096\u0097\7?\2\2\u0097\u00a1")
+        buf.write("\3\2\2\2\u0098\u009c\7]\2\2\u0099\u009b\13\2\2\2\u009a")
+        buf.write("\u0099\3\2\2\2\u009b\u009e\3\2\2\2\u009c\u009d\3\2\2\2")
+        buf.write("\u009c\u009a\3\2\2\2\u009d\u009f\3\2\2\2\u009e\u009c\3")
+        buf.write("\2\2\2\u009f\u00a1\7_\2\2\u00a0\u0094\3\2\2\2\u00a0\u0098")
+        buf.write("\3\2\2\2\u00a1\"\3\2\2\2\u00a2\u00a3\7%\2\2\u00a3\u00a4")
+        buf.write("\7]\2\2\u00a4\u00a5\3\2\2\2\u00a5\u00a6\5!\21\2\u00a6")
+        buf.write("\u00a7\7_\2\2\u00a7\u00a8\3\2\2\2\u00a8\u00a9\b\22\2\2")
+        buf.write("\u00a9$\3\2\2\2\u00aa\u00c8\7%\2\2\u00ab\u00c9\3\2\2\2")
+        buf.write("\u00ac\u00b0\7]\2\2\u00ad\u00af\7?\2\2\u00ae\u00ad\3\2")
+        buf.write("\2\2\u00af\u00b2\3\2\2\2\u00b0\u00ae\3\2\2\2\u00b0\u00b1")
+        buf.write("\3\2\2\2\u00b1\u00c9\3\2\2\2\u00b2\u00b0\3\2\2\2\u00b3")
+        buf.write("\u00b7\7]\2\2\u00b4\u00b6\7?\2\2\u00b5\u00b4\3\2\2\2\u00b6")
+        buf.write("\u00b9\3\2\2\2\u00b7\u00b5\3\2\2\2\u00b7\u00b8\3\2\2\2")
+        buf.write("\u00b8\u00ba\3\2\2\2\u00b9\u00b7\3\2\2\2\u00ba\u00be\n")
+        buf.write("\7\2\2\u00bb\u00bd\n\b\2\2\u00bc\u00bb\3\2\2\2\u00bd\u00c0")
+        buf.write("\3\2\2\2\u00be\u00bc\3\2\2\2\u00be\u00bf\3\2\2\2\u00bf")
+        buf.write("\u00c9\3\2\2\2\u00c0\u00be\3\2\2\2\u00c1\u00c5\n\t\2\2")
+        buf.write("\u00c2\u00c4\n\b\2\2\u00c3\u00c2\3\2\2\2\u00c4\u00c7\3")
+        buf.write("\2\2\2\u00c5\u00c3\3\2\2\2\u00c5\u00c6\3\2\2\2\u00c6\u00c9")
+        buf.write("\3\2\2\2\u00c7\u00c5\3\2\2\2\u00c8\u00ab\3\2\2\2\u00c8")
+        buf.write("\u00ac\3\2\2\2\u00c8\u00b3\3\2\2\2\u00c8\u00c1\3\2\2\2")
+        buf.write("\u00c9\u00cf\3\2\2\2\u00ca\u00cc\7\17\2\2\u00cb\u00cd")
+        buf.write("\7\f\2\2\u00cc\u00cb\3\2\2\2\u00cc\u00cd\3\2\2\2\u00cd")
+        buf.write("\u00d0\3\2\2\2\u00ce\u00d0\t\n\2\2\u00cf\u00ca\3\2\2\2")
+        buf.write("\u00cf\u00ce\3\2\2\2\u00d0\u00d1\3\2\2\2\u00d1\u00d2\b")
+        buf.write("\23\2\2\u00d2&\3\2\2\2\u00d3\u00d5\7\17\2\2\u00d4\u00d6")
+        buf.write("\7\f\2\2\u00d5\u00d4\3\2\2\2\u00d5\u00d6\3\2\2\2\u00d6")
+        buf.write("\u00d9\3\2\2\2\u00d7\u00d9\7\f\2\2\u00d8\u00d3\3\2\2\2")
+        buf.write("\u00d8\u00d7\3\2\2\2\u00d9\u00da\3\2\2\2\u00da\u00d8\3")
+        buf.write("\2\2\2\u00da\u00db\3\2\2\2\u00db\u00dc\3\2\2\2\u00dc\u00dd")
+        buf.write("\b\24\2\2\u00dd(\3\2\2\2\u00de\u00e0\t\13\2\2\u00df\u00de")
+        buf.write("\3\2\2\2\u00e0\u00e1\3\2\2\2\u00e1\u00df\3\2\2\2\u00e1")
+        buf.write("\u00e2\3\2\2\2\u00e2\u00e3\3\2\2\2\u00e3\u00e4\b\25\2")
+        buf.write("\2\u00e4*\3\2\2\2\36\2\61>CJOafhmx\u0081\u0083\u008b\u008e")
+        buf.write("\u009c\u00a0\u00b0\u00b7\u00be\u00c5\u00c8\u00cc\u00cf")
+        buf.write("\u00d5\u00d8\u00da\u00e1\3\b\2\2")
         return buf.getvalue()
 
 
diff --git a/src/cminx/parser/CMakeParser.py b/src/cminx/parser/CMakeParser.py
index f3168df2..03516e08 100644
--- a/src/cminx/parser/CMakeParser.py
+++ b/src/cminx/parser/CMakeParser.py
@@ -8,25 +8,26 @@
 def serializedATN():
     with StringIO() as buf:
         buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\21")
-        buf.write(";\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b")
-        buf.write("\t\b\3\2\5\2\22\n\2\3\2\3\2\7\2\26\n\2\f\2\16\2\31\13")
-        buf.write("\2\3\2\3\2\3\3\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\6\3\6")
-        buf.write("\7\6(\n\6\f\6\16\6+\13\6\3\6\3\6\3\7\3\7\3\b\3\b\3\b\7")
-        buf.write("\b\64\n\b\f\b\16\b\67\13\b\3\b\3\b\3\b\2\2\t\2\4\6\b\n")
-        buf.write("\f\16\2\3\4\2\t\n\f\r\2:\2\21\3\2\2\2\4\34\3\2\2\2\6\37")
-        buf.write("\3\2\2\2\b!\3\2\2\2\n#\3\2\2\2\f.\3\2\2\2\16\60\3\2\2")
-        buf.write("\2\20\22\5\6\4\2\21\20\3\2\2\2\21\22\3\2\2\2\22\27\3\2")
-        buf.write("\2\2\23\26\5\4\3\2\24\26\5\n\6\2\25\23\3\2\2\2\25\24\3")
-        buf.write("\2\2\2\26\31\3\2\2\2\27\25\3\2\2\2\27\30\3\2\2\2\30\32")
-        buf.write("\3\2\2\2\31\27\3\2\2\2\32\33\7\2\2\3\33\3\3\2\2\2\34\35")
-        buf.write("\5\b\5\2\35\36\5\n\6\2\36\5\3\2\2\2\37 \7\5\2\2 \7\3\2")
-        buf.write("\2\2!\"\7\6\2\2\"\t\3\2\2\2#$\7\t\2\2$)\7\3\2\2%(\5\f")
-        buf.write("\7\2&(\5\16\b\2\'%\3\2\2\2\'&\3\2\2\2(+\3\2\2\2)\'\3\2")
-        buf.write("\2\2)*\3\2\2\2*,\3\2\2\2+)\3\2\2\2,-\7\4\2\2-\13\3\2\2")
-        buf.write("\2./\t\2\2\2/\r\3\2\2\2\60\65\7\3\2\2\61\64\5\f\7\2\62")
-        buf.write("\64\5\16\b\2\63\61\3\2\2\2\63\62\3\2\2\2\64\67\3\2\2\2")
-        buf.write("\65\63\3\2\2\2\65\66\3\2\2\2\668\3\2\2\2\67\65\3\2\2\2")
-        buf.write("89\7\4\2\29\17\3\2\2\2\t\21\25\27\')\63\65")
+        buf.write("<\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b")
+        buf.write("\t\b\3\2\5\2\22\n\2\3\2\3\2\3\2\7\2\27\n\2\f\2\16\2\32")
+        buf.write("\13\2\3\2\3\2\3\3\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\6")
+        buf.write("\3\6\7\6)\n\6\f\6\16\6,\13\6\3\6\3\6\3\7\3\7\3\b\3\b\3")
+        buf.write("\b\7\b\65\n\b\f\b\16\b8\13\b\3\b\3\b\3\b\2\2\t\2\4\6\b")
+        buf.write("\n\f\16\2\3\4\2\t\n\f\r\2<\2\21\3\2\2\2\4\35\3\2\2\2\6")
+        buf.write(" \3\2\2\2\b\"\3\2\2\2\n$\3\2\2\2\f/\3\2\2\2\16\61\3\2")
+        buf.write("\2\2\20\22\5\6\4\2\21\20\3\2\2\2\21\22\3\2\2\2\22\30\3")
+        buf.write("\2\2\2\23\27\5\4\3\2\24\27\5\n\6\2\25\27\5\b\5\2\26\23")
+        buf.write("\3\2\2\2\26\24\3\2\2\2\26\25\3\2\2\2\27\32\3\2\2\2\30")
+        buf.write("\26\3\2\2\2\30\31\3\2\2\2\31\33\3\2\2\2\32\30\3\2\2\2")
+        buf.write("\33\34\7\2\2\3\34\3\3\2\2\2\35\36\5\b\5\2\36\37\5\n\6")
+        buf.write("\2\37\5\3\2\2\2 !\7\5\2\2!\7\3\2\2\2\"#\7\6\2\2#\t\3\2")
+        buf.write("\2\2$%\7\t\2\2%*\7\3\2\2&)\5\f\7\2\')\5\16\b\2(&\3\2\2")
+        buf.write("\2(\'\3\2\2\2),\3\2\2\2*(\3\2\2\2*+\3\2\2\2+-\3\2\2\2")
+        buf.write(",*\3\2\2\2-.\7\4\2\2.\13\3\2\2\2/\60\t\2\2\2\60\r\3\2")
+        buf.write("\2\2\61\66\7\3\2\2\62\65\5\f\7\2\63\65\5\16\b\2\64\62")
+        buf.write("\3\2\2\2\64\63\3\2\2\2\658\3\2\2\2\66\64\3\2\2\2\66\67")
+        buf.write("\3\2\2\2\679\3\2\2\28\66\3\2\2\29:\7\4\2\2:\17\3\2\2\2")
+        buf.write("\t\21\26\30(*\64\66")
         return buf.getvalue()
 
 
@@ -113,6 +114,13 @@ def command_invocation(self, i:int=None):
                 return self.getTypedRuleContext(CMakeParser.Command_invocationContext,i)
 
 
+        def bracket_doccomment(self, i:int=None):
+            if i is None:
+                return self.getTypedRuleContexts(CMakeParser.Bracket_doccommentContext)
+            else:
+                return self.getTypedRuleContext(CMakeParser.Bracket_doccommentContext,i)
+
+
         def getRuleIndex(self):
             return CMakeParser.RULE_cmake_file
 
@@ -124,12 +132,6 @@ def exitRule(self, listener:ParseTreeListener):
             if hasattr( listener, "exitCmake_file" ):
                 listener.exitCmake_file(self)
 
-        def accept(self, visitor:ParseTreeVisitor):
-            if hasattr( visitor, "visitCmake_file" ):
-                return visitor.visitCmake_file(self)
-            else:
-                return visitor.visitChildren(self)
-
 
 
 
@@ -148,29 +150,34 @@ def cmake_file(self):
                 self.documented_module()
 
 
-            self.state = 21
+            self.state = 22
             self._errHandler.sync(self)
             _la = self._input.LA(1)
             while _la==CMakeParser.Docstring or _la==CMakeParser.Identifier:
-                self.state = 19
+                self.state = 20
                 self._errHandler.sync(self)
-                token = self._input.LA(1)
-                if token in [CMakeParser.Docstring]:
+                la_ = self._interp.adaptivePredict(self._input,1,self._ctx)
+                if la_ == 1:
                     self.state = 17
                     self.documented_command()
                     pass
-                elif token in [CMakeParser.Identifier]:
+
+                elif la_ == 2:
                     self.state = 18
                     self.command_invocation()
                     pass
-                else:
-                    raise NoViableAltException(self)
 
-                self.state = 23
+                elif la_ == 3:
+                    self.state = 19
+                    self.bracket_doccomment()
+                    pass
+
+
+                self.state = 24
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
-            self.state = 24
+            self.state = 25
             self.match(CMakeParser.EOF)
         except RecognitionException as re:
             localctx.exception = re
@@ -205,12 +212,6 @@ def exitRule(self, listener:ParseTreeListener):
             if hasattr( listener, "exitDocumented_command" ):
                 listener.exitDocumented_command(self)
 
-        def accept(self, visitor:ParseTreeVisitor):
-            if hasattr( visitor, "visitDocumented_command" ):
-                return visitor.visitDocumented_command(self)
-            else:
-                return visitor.visitChildren(self)
-
 
 
 
@@ -220,9 +221,9 @@ def documented_command(self):
         self.enterRule(localctx, 2, self.RULE_documented_command)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 26
-            self.bracket_doccomment()
             self.state = 27
+            self.bracket_doccomment()
+            self.state = 28
             self.command_invocation()
         except RecognitionException as re:
             localctx.exception = re
@@ -252,12 +253,6 @@ def exitRule(self, listener:ParseTreeListener):
             if hasattr( listener, "exitDocumented_module" ):
                 listener.exitDocumented_module(self)
 
-        def accept(self, visitor:ParseTreeVisitor):
-            if hasattr( visitor, "visitDocumented_module" ):
-                return visitor.visitDocumented_module(self)
-            else:
-                return visitor.visitChildren(self)
-
 
 
 
@@ -267,7 +262,7 @@ def documented_module(self):
         self.enterRule(localctx, 4, self.RULE_documented_module)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 29
+            self.state = 30
             self.match(CMakeParser.Module_docstring)
         except RecognitionException as re:
             localctx.exception = re
@@ -297,12 +292,6 @@ def exitRule(self, listener:ParseTreeListener):
             if hasattr( listener, "exitBracket_doccomment" ):
                 listener.exitBracket_doccomment(self)
 
-        def accept(self, visitor:ParseTreeVisitor):
-            if hasattr( visitor, "visitBracket_doccomment" ):
-                return visitor.visitBracket_doccomment(self)
-            else:
-                return visitor.visitChildren(self)
-
 
 
 
@@ -312,7 +301,7 @@ def bracket_doccomment(self):
         self.enterRule(localctx, 6, self.RULE_bracket_doccomment)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 31
+            self.state = 32
             self.match(CMakeParser.Docstring)
         except RecognitionException as re:
             localctx.exception = re
@@ -356,12 +345,6 @@ def exitRule(self, listener:ParseTreeListener):
             if hasattr( listener, "exitCommand_invocation" ):
                 listener.exitCommand_invocation(self)
 
-        def accept(self, visitor:ParseTreeVisitor):
-            if hasattr( visitor, "visitCommand_invocation" ):
-                return visitor.visitCommand_invocation(self)
-            else:
-                return visitor.visitChildren(self)
-
 
 
 
@@ -372,33 +355,33 @@ def command_invocation(self):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 33
-            self.match(CMakeParser.Identifier)
             self.state = 34
+            self.match(CMakeParser.Identifier)
+            self.state = 35
             self.match(CMakeParser.T__0)
-            self.state = 39
+            self.state = 40
             self._errHandler.sync(self)
             _la = self._input.LA(1)
             while (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << CMakeParser.T__0) | (1 << CMakeParser.Identifier) | (1 << CMakeParser.Unquoted_argument) | (1 << CMakeParser.Quoted_argument) | (1 << CMakeParser.Bracket_argument))) != 0):
-                self.state = 37
+                self.state = 38
                 self._errHandler.sync(self)
                 token = self._input.LA(1)
                 if token in [CMakeParser.Identifier, CMakeParser.Unquoted_argument, CMakeParser.Quoted_argument, CMakeParser.Bracket_argument]:
-                    self.state = 35
+                    self.state = 36
                     self.single_argument()
                     pass
                 elif token in [CMakeParser.T__0]:
-                    self.state = 36
+                    self.state = 37
                     self.compound_argument()
                     pass
                 else:
                     raise NoViableAltException(self)
 
-                self.state = 41
+                self.state = 42
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
-            self.state = 42
+            self.state = 43
             self.match(CMakeParser.T__1)
         except RecognitionException as re:
             localctx.exception = re
@@ -437,12 +420,6 @@ def exitRule(self, listener:ParseTreeListener):
             if hasattr( listener, "exitSingle_argument" ):
                 listener.exitSingle_argument(self)
 
-        def accept(self, visitor:ParseTreeVisitor):
-            if hasattr( visitor, "visitSingle_argument" ):
-                return visitor.visitSingle_argument(self)
-            else:
-                return visitor.visitChildren(self)
-
 
 
 
@@ -453,7 +430,7 @@ def single_argument(self):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 44
+            self.state = 45
             _la = self._input.LA(1)
             if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << CMakeParser.Identifier) | (1 << CMakeParser.Unquoted_argument) | (1 << CMakeParser.Quoted_argument) | (1 << CMakeParser.Bracket_argument))) != 0)):
                 self._errHandler.recoverInline(self)
@@ -499,12 +476,6 @@ def exitRule(self, listener:ParseTreeListener):
             if hasattr( listener, "exitCompound_argument" ):
                 listener.exitCompound_argument(self)
 
-        def accept(self, visitor:ParseTreeVisitor):
-            if hasattr( visitor, "visitCompound_argument" ):
-                return visitor.visitCompound_argument(self)
-            else:
-                return visitor.visitChildren(self)
-
 
 
 
@@ -515,31 +486,31 @@ def compound_argument(self):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 46
+            self.state = 47
             self.match(CMakeParser.T__0)
-            self.state = 51
+            self.state = 52
             self._errHandler.sync(self)
             _la = self._input.LA(1)
             while (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << CMakeParser.T__0) | (1 << CMakeParser.Identifier) | (1 << CMakeParser.Unquoted_argument) | (1 << CMakeParser.Quoted_argument) | (1 << CMakeParser.Bracket_argument))) != 0):
-                self.state = 49
+                self.state = 50
                 self._errHandler.sync(self)
                 token = self._input.LA(1)
                 if token in [CMakeParser.Identifier, CMakeParser.Unquoted_argument, CMakeParser.Quoted_argument, CMakeParser.Bracket_argument]:
-                    self.state = 47
+                    self.state = 48
                     self.single_argument()
                     pass
                 elif token in [CMakeParser.T__0]:
-                    self.state = 48
+                    self.state = 49
                     self.compound_argument()
                     pass
                 else:
                     raise NoViableAltException(self)
 
-                self.state = 53
+                self.state = 54
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
-            self.state = 54
+            self.state = 55
             self.match(CMakeParser.T__1)
         except RecognitionException as re:
             localctx.exception = re
diff --git a/src/cminx/parser/__init__.py b/src/cminx/parser/__init__.py
index e157c2d0..cadb9eab 100644
--- a/src/cminx/parser/__init__.py
+++ b/src/cminx/parser/__init__.py
@@ -1,3 +1,7 @@
+import logging
+
+from antlr4 import Parser, DFA
+from antlr4.atn.ATNConfigSet import ATNConfigSet
 from antlr4.error.ErrorListener import ErrorListener
 
 
@@ -13,14 +17,19 @@ class ParserErrorListener(ErrorListener):
     def __init__(self):
         super(ParserErrorListener, self).__init__()
 
+        self.logger = logging.getLogger(__name__)
+
     def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
         """
         As per the Antlr4 Javadoc:
-            Upon syntax error, notify any interested parties. This is not how to recover from errors or compute error messages.
-            ErrorStrategy specifies how to recover from syntax errors and how to compute error messages.
-            This listener's job is simply to emit a computed message, though it has enough information to create its own message in many cases.
-            The RecognitionException (e) is non-null for all syntax errors except when we discover mismatched token errors that we can recover from in-line,
-            without returning from the surrounding rule (via the single token insertion and deletion mechanism).
+
+            Upon syntax error, notify any interested parties. This is not how to recover from errors or compute error
+            messages. ErrorStrategy specifies how to recover from syntax errors and how to compute error messages.
+            This listener's job is simply to emit a computed message, though it has enough information to create its
+            own message in many cases. The RecognitionException (e) is non-null for all syntax errors except when we
+            discover mismatched token errors that we can recover from in-line, without returning from the surrounding
+            rule (via the single token insertion and deletion mechanism).
+
         :raises RecognitionException: When the mismatched token cannot be recovered from
         :raises CMakeSyntaxError: When it is possible to recover from this error via single token insertion/deletion.
         """
@@ -32,48 +41,71 @@ def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
             s.msg = msg
             raise s
 
-    def reportAmbiguity(self, recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs):
+    def reportAmbiguity(self, recognizer: Parser, dfa: DFA, startIndex: int,
+                        stopIndex: int, exact: bool, ambigAlts: set, configs: ATNConfigSet):
         """
         As per the Antlr4 Javadoc:
-            This method is called by the parser when a full-context prediction results in an ambiguity.
-            Each full-context prediction which does not result in a syntax error will call either ErrorListener.reportContextSensitivity() or ErrorListener.reportAmbiguity().
 
-            When ambigAlts is not None, it contains the set of potentially viable alternatives identified by the prediction algorithm. When ambigAlts is None, use ATNConfigSet.getAlts() to obtain the represented alternatives from the configs argument.
+            This method is called by the parser when a full-context prediction results in an ambiguity. Each
+            full-context prediction which does not result in a syntax error will call either
+            ErrorListener.reportContextSensitivity() or ErrorListener.reportAmbiguity().
 
-            When exact is True, all of the potentially viable alternatives are truly viable, i.e. this is reporting an exact ambiguity. When exact is False, at least two of the potentially viable alternatives are viable for the current input, but the prediction algorithm terminated as soon as it determined that at least the minimum potentially viable alternative is truly viable.
+            When ambigAlts is not None, it contains the set of potentially viable alternatives identified by the
+            prediction algorithm. When ambigAlts is None, use ATNConfigSet.getAlts() to obtain the represented
+            alternatives from the configs argument.
 
-            When the PredictionMode.LL_EXACT_AMBIG_DETECTION prediction mode is used, the parser is required to identify exact ambiguities so exact will always be true.
+            When exact is True, all of the potentially viable alternatives are truly viable, i.e. this is reporting
+            an exact ambiguity. When exact is False, at least two of the potentially viable alternatives are viable
+            for the current input, but the prediction algorithm terminated as soon as it determined that at least the
+            minimum potentially viable alternative is truly viable.
+
+            When the PredictionMode.LL_EXACT_AMBIG_DETECTION prediction mode is used, the parser is required to
+            identify exact ambiguities so exact will always be true.
 
             This method is not used by lexers.
 
-        :raises RuntimeError: Unconditionally, we should not encounter ambiguities
+        This method only logs ambiguities, it does not halt parsing as our grammar
+        is ambiguous. The chosen alternative is implied to be the first one in the grammar.
         """
+        self.logger.debug(f"Found parse ambiguity at starting index {startIndex}, stop index {stopIndex}")
 
-        raise RuntimeError("Parse ambiguity")
-
-    def reportAttemptingFullContext(self, recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs):
+    def reportAttemptingFullContext(self, recognizer: Parser, dfa: DFA, startIndex: int,
+                                    stopIndex: int, conflictingAlts: set, configs: ATNConfigSet):
         """
         As per the Antlr4 Javadoc:
-            This method is called when an SLL conflict occurs and the parser is about to use the full context information to make an LL decision.
-            If one or more configurations in configs contains a semantic predicate, the predicates are evaluated before this method is called. The subset of alternatives which are still viable after predicates are evaluated is reported in conflictingAlts.
+
+            This method is called when an SLL conflict occurs and the parser is about to use the full context
+            information to make an LL decision. If one or more configurations in configs contains a semantic
+            predicate, the predicates are evaluated before this method is called. The subset of alternatives which
+            are still viable after predicates are evaluated is reported in conflictingAlts.
 
             This method is not used by lexers.
 
-        Currently this is a no-op, it is unknown whether we should allow or disallow full context LL decisions.
+        Currently, this is a no-op, it is unknown whether we should allow or disallow full context LL decisions.
         """
         pass
 
-    def reportContextSensitivity(self, recognizer, dfa, startIndex, stopIndex, prediction, configs):
+    def reportContextSensitivity(self, recognizer: Parser, dfa: DFA, startIndex: int,
+                                 stopIndex: int, prediction: int, configs: ATNConfigSet):
         """
         As per the Antlr4 Javadoc:
-            This method is called by the parser when a full-context prediction has a unique result.
-            Each full-context prediction which does not result in a syntax error will call either ErrorListener.reportContextSensitivity() or ErrorListener.reportAmbiguity().
-
-            For prediction implementations that only evaluate full-context predictions when an SLL conflict is found (including the default ParserATNSimulator implementation), this method reports cases where SLL conflicts were resolved to unique full-context predictions, i.e. the decision was context-sensitive.
-            This report does not necessarily indicate a problem, and it may appear even in completely unambiguous grammars.
-            configs may have more than one represented alternative if the full-context prediction algorithm does not evaluate predicates before beginning the full-context prediction. In all cases, the final prediction is passed as the prediction argument.
 
-            Note that the definition of "context sensitivity" in this method differs from the concept in DecisionInfo.contextSensitivities. This method reports all instances where an SLL conflict occurred but LL parsing produced a unique result, whether or not that unique result matches the minimum alternative in the SLL conflicting set.
+            This method is called by the parser when a full-context prediction has a unique result. Each full-context
+            prediction which does not result in a syntax error will call either
+            ErrorListener.reportContextSensitivity() or ErrorListener.reportAmbiguity().
+
+            For prediction implementations that only evaluate full-context predictions when an SLL conflict is found
+            (including the default ParserATNSimulator implementation), this method reports cases where SLL conflicts
+            were resolved to unique full-context predictions, i.e. the decision was context-sensitive. This report
+            does not necessarily indicate a problem, and it may appear even in completely unambiguous grammars.
+            configs may have more than one represented alternative if the full-context prediction algorithm does not
+            evaluate predicates before beginning the full-context prediction. In all cases, the final prediction is
+            passed as the prediction argument.
+
+            Note that the definition of "context sensitivity" in this method differs from the concept in
+            DecisionInfo.contextSensitivities. This method reports all instances where an SLL conflict occurred but
+            LL parsing produced a unique result, whether or not that unique result matches the minimum alternative in
+            the SLL conflicting set.
 
             This method is not used by lexers.
 


Test patch:
diff --git a/tests/test_samples/commented_out_function.cmake b/tests/test_samples/commented_out_function.cmake
new file mode 100644
index 00000000..c28dfb2f
--- /dev/null
+++ b/tests/test_samples/commented_out_function.cmake
@@ -0,0 +1,26 @@
+#[[[ @module
+# This is a module containing a commented-out function
+#]]
+# This is a line comment
+
+#[[[
+# Properly documented command
+#
+# :param param1: This is a parameter
+# :type param1: str
+# :param param2: This is another parameter
+# :type param2: desc
+#]]
+function(test1 param1 param2)
+endfunction()
+#[[[
+# Documenting a commented-out function should not blow up
+#
+# :param param3: This is a parameter
+# :type param3: str
+# :param param4: This is another parameter
+# :type param4: desc
+#]]
+#function(test param3 param4)
+#
+#endfunction()
\ No newline at end of file
diff --git a/tests/test_samples/corr_rst/commented_out_function.rst b/tests/test_samples/corr_rst/commented_out_function.rst
new file mode 100644
index 00000000..f607d38a
--- /dev/null
+++ b/tests/test_samples/corr_rst/commented_out_function.rst
@@ -0,0 +1,21 @@
+
+###################################
+test_samples.commented_out_function
+###################################
+
+.. module:: test_samples.commented_out_function
+
+   This is a module containing a commented-out function
+   
+
+
+.. function:: test1(param1 param2)
+
+   Properly documented command
+   
+   :param param1: This is a parameter
+   :type param1: str
+   :param param2: This is another parameter
+   :type param2: desc
+   
+
diff --git a/tests/unit_tests/test_error_listener.py b/tests/unit_tests/test_error_listener.py
index 3d514888..213043cc 100644
--- a/tests/unit_tests/test_error_listener.py
+++ b/tests/unit_tests/test_error_listener.py
@@ -27,8 +27,8 @@ def test_syntax_error(self):
         self.assertRaises(CMakeSyntaxError, self.listener.syntaxError, None, None, 1, 1, "Test message", None)
 
     def test_ambiguity(self):
-        # This method will only be called if the grammar is bad, call manually to ensure that if it is ever called we get an error
-        self.assertRaises(RuntimeError, self.listener.reportAmbiguity, None, None, None, None, None, None, None)
+        # Our grammar is now ambiguous, so ensure reporting an ambiguity does not raise an exception
+        self.listener.reportAmbiguity(None, None, None, None, None, None, None)
 
     def test_full_context(self):
         # No-op, we include it for documentation purposes