Repository: CMakePP/CMinx (language: python)

Issue:
Is it possible to generate Documentation for `.cmake` file in general ?
An example:

```cmake
#[[[ ??? Somehow mark this block to be used for the .cmake file instead for `funcA` ???
# This is the cmake documentation.
#]]

function(funcA paramA)
  message(I am a function)
endfunction()

#[[[
# This function has a documentation.
# :param paramA: Help for `paramA`
# :param paramB: This is **paramB**
#]]
function(funcB paramA paramB)
  message(Anything)
endfunction()
```


Discussion / hints:

Fix patch:
diff --git a/docs/source/documenting/index.rst b/docs/source/documenting/index.rst
index acfa1609..db3b4a11 100644
--- a/docs/source/documenting/index.rst
+++ b/docs/source/documenting/index.rst
@@ -26,6 +26,7 @@ Using CMinx to Document Your Code
    :maxdepth: 2
 
    types_of_comments
+   module
    function
    macro
    variable
diff --git a/docs/source/documenting/module.rst b/docs/source/documenting/module.rst
new file mode 100644
index 00000000..a4c6238f
--- /dev/null
+++ b/docs/source/documenting/module.rst
@@ -0,0 +1,82 @@
+.. Copyright 2021 CMakePP
+..
+.. Licensed under the Apache License, Version 2.0 (the "License");
+.. you may not use this file except in compliance with the License.
+.. You may obtain a copy of the License at
+..
+.. http://www.apache.org/licenses/LICENSE-2.0
+..
+.. Unless required by applicable law or agreed to in writing, software
+.. distributed under the License is distributed on an "AS IS" BASIS,
+.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+.. See the License for the specific language governing permissions and
+.. limitations under the License.
+..
+********************
+Documenting a Module
+********************
+
+.. note::
+   The generated module documentation will only be
+   processed by Sphinx in versions greater than or
+   equal to 5.2. In older versions, the documentation
+   body text will simply be ignored. The optional module
+   name will still be processed if supplied.
+
+CMinx is able to generate documentation for a CMake module in much
+the same way that functions and other elements are documented.
+There are a few differences however:
+
+#. The module doccomment must be the first non-comment
+   element of the file. This means it must occur before the usual
+   :code:`include_guard()` call or any other commands. Failing
+   this requirement will result in a parse error.
+#. The module doccomment must have the :code:`@module` directive
+   in the same line as the opening :code:`#[[[` tag. This requirement
+   ensures a module doccomment is not accidentally attached to
+   a command instead.
+#. The :code:`@module` directive may have a single optional argument
+   that specifies a module name to be used instead of the autogenerated
+   name. Setting the module name will also set the generated
+   documentation's title to the given name. The file name will be unchanged.
+
+A basic module doccomment looks like so:
+
+.. literalinclude:: ../../../tests/test_samples/basic_module.cmake
+   :language: cmake
+
+This doccomment is translated by CMinx to the following RST:
+
+.. literalinclude:: ../../../tests/test_samples/corr_rst/basic_module.rst
+   :language: rst
+
+As one can see, the :code:`.. module::` directive still has a module name
+even though the source does not specify one. This is because CMinx autogenerates
+a module name based on the source filename and its path relative to the input path
+that CMinx is given on the command line. The module name
+generation can be adjusted through the configuration file.
+
+A module doccomment that overrides the autogenerated module name can
+be seen here:
+
+.. literalinclude:: ../../../tests/test_samples/advanced_module.cmake
+   :language: cmake
+
+CMinx generates the following RST from this source file:
+
+.. literalinclude:: ../../../tests/test_samples/corr_rst/advanced_module.rst
+   :language: rst
+
+Here, one can see that the module name and the title were overridden with
+the name set in the :code:`@module` directive. One may also see that although
+the doccomment appears directly above a function declaration, it is not
+interpreted as a function doccomment due to the :code:`@module` directive.
+
+.. note::
+   The above is an intentionally obtuse usage of doccomments
+   to show how the :code:`@module` directive affects parsing.
+   It is *heavily* advised to separate the module doccomment
+   and any subsequent code without doccomments by at least
+   a blank line. Preferably, documented code should be
+   placed after the module doccomment to help visually separate
+   the module doccomment from any command.
\ No newline at end of file
diff --git a/docs/source/documenting/types_of_comments.rst b/docs/source/documenting/types_of_comments.rst
index 8d1f75d9..5299ba68 100644
--- a/docs/source/documenting/types_of_comments.rst
+++ b/docs/source/documenting/types_of_comments.rst
@@ -39,3 +39,23 @@ For comparison, an annotation comment looks like:
 .. literalinclude:: ../../../tests/examples/example.cmake
    :language: cmake
    :lines: 1-5
+
+Sometimes documentation comments need additional information in their
+headers to inform CMinx about the type of documentation the comment
+represents. This information is contained in the first
+line of the documentation comment (the line with :code:`#[[[`)
+and is marked by a special directive start character :code:`@`.
+For example, a module documentation comment contains the :code:`@module`
+directive:
+
+.. literalinclude:: ../../../tests/test_samples/basic_module.cmake
+   :language: cmake
+   :lines: 1-9
+
+.. note::
+   Doccomment directives marked by :code:`@` are different from RST directives.
+   They are hints to CMinx for how to parse the doccomment and how to format
+   it in the output, they *are not* passed through to the generated RST
+   and therefore Sphinx is entirely unaware of them. Consider them akin to
+   preprocessor directives.
+
diff --git a/src/cminx/aggregator.py b/src/cminx/aggregator.py
index fa894e58..1207815d 100755
--- a/src/cminx/aggregator.py
+++ b/src/cminx/aggregator.py
@@ -13,10 +13,11 @@
 # limitations under the License.
 
 import logging
+from typing import List
 
 from .documentation_types import AttributeDocumentation, FunctionDocumentation, MacroDocumentation, \
     VariableDocumentation, GenericCommandDocumentation, ClassDocumentation, TestDocumentation, SectionDocumentation, \
-    MethodDocumentation, VarType, CTestDocumentation, AbstractCommandDefinitionDocumentation
+    MethodDocumentation, VarType, CTestDocumentation, ModuleDocumentation, AbstractCommandDefinitionDocumentation
 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
@@ -77,9 +78,9 @@ def __init__(self, settings: Settings = Settings()):
 
         self.consumed = []
         """
-        A list containing all of the Command_invocationContexts that have already been processed
+        A list containing all of the contexts that have already been processed
         """
-        
+
         self.logger = logging.getLogger(__name__)
 
     def process_function(self, ctx: CMakeParser.Command_invocationContext, docstring: str):
@@ -454,19 +455,8 @@ def process_generic_command(self, command_name: str, ctx: CMakeParser.Command_in
         self.documented.append(GenericCommandDocumentation(
             command_name, docstring, args))
 
-    def enterDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
-        """
-        Main entrypoint into the documentation processor and aggregator. Called by ParseTreeWalker whenever
-        encountering a documented command. Cleans the docstring and dispatches ctx to other functions for additional
-        processing (process_{command}(), i.e. process_function())
-
-        :param ctx: Documented command context, constructed by the Antlr4 parser.
-
-        :raise NotImplementedError: If no processor can be found for the command that was documented.
-        """
-        text = ctx.Bracket_doccomment().getText()
-        lines = text.split("\n")
-
+    @staticmethod
+    def clean_doc_lines(lines: List[str]) -> str:
         # If last line starts with leading spaces or tabs, count how many and remove from all lines
         num_spaces = 0
         for i in range(0, len(lines[-1])):
@@ -491,9 +481,27 @@ def enterDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
         if cleaned_doc.startswith("\n"):
             cleaned_doc = cleaned_doc[1:]
 
+        return cleaned_doc
+
+    def enterDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
+        """
+        Main entrypoint into the documentation processor and aggregator. Called by ParseTreeWalker whenever
+        encountering a documented command. Cleans the docstring and dispatches ctx to other functions for additional
+        processing (process_{command}(), i.e. process_function())
+
+        :param ctx: Documented command context, constructed by the Antlr4 parser.
+
+        :raise NotImplementedError: If no processor can be found for the command that was documented.
+        """
+        text = ctx.bracket_doccomment().getText()
+        lines = text.split("\n")
+
+        cleaned_doc = DocumentationAggregator.clean_doc_lines(lines)
+
         try:
             command = ctx.command_invocation().Identifier().getText().lower()
             self.consumed.append(ctx.command_invocation())
+            self.consumed.append(ctx.bracket_doccomment())
             if f"process_{command}" in dir(self):
                 getattr(self, f"process_{command}")(ctx.command_invocation(), cleaned_doc)
             else:
@@ -547,7 +555,15 @@ def enterCommand_invocation(self, ctx: CMakeParser.Command_invocationContext):
                     getattr(self, f"process_{command}")(ctx, "")
                 elif command == "function" or command == "macro":
                     self.definition_command_stack.append(DefinitionCommand(None, False))
+
         except Exception as e:
             line_num = ctx.start.line
             self.logger.error(f"Caught exception while processing command beginning at line number {line_num}")
             raise e
+
+    def enterDocumented_module(self, ctx: CMakeParser.Documented_moduleContext):
+        text = ctx.Module_docstring().getText()
+        cleaned_lines = DocumentationAggregator.clean_doc_lines(text.split("\n")).split("\n")
+        module_name = cleaned_lines[0].replace("@module", "").strip()
+        doc = "\n".join(cleaned_lines[1:])
+        self.documented.append(ModuleDocumentation(module_name, doc))
diff --git a/src/cminx/documentation_types.py b/src/cminx/documentation_types.py
index e1c6e216..89967bae 100644
--- a/src/cminx/documentation_types.py
+++ b/src/cminx/documentation_types.py
@@ -29,6 +29,7 @@
 from enum import Enum
 from typing import Union, List
 
+from . import Settings
 from .rstwriter import RSTWriter, Directive, interpreted_text
 
 
@@ -382,3 +383,15 @@ def process(self, writer: RSTWriter):
         if len(self.inner_classes) > 0:
             d.text("**Inner classes**")
             d.bulleted_list(*[interpreted_text("class", clazz.name) for clazz in self.inner_classes])
+
+
+@dataclass
+class ModuleDocumentation(DocumentationType):
+    """
+    Represents documentation for an entire CMake module
+    """
+
+    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)
diff --git a/src/cminx/documenter.py b/src/cminx/documenter.py
index 4b4cdf49..d653bd88 100755
--- a/src/cminx/documenter.py
+++ b/src/cminx/documenter.py
@@ -27,11 +27,11 @@
 
 from .aggregator import DocumentationAggregator
 from cminx import Settings
-from .documentation_types import DocumentationType
+from .documentation_types import DocumentationType, ModuleDocumentation
 from .parser import ParserErrorListener
 from .parser.CMakeLexer import CMakeLexer
 from .parser.CMakeParser import CMakeParser
-from .rstwriter import RSTWriter
+from .rstwriter import RSTWriter, Directive
 
 
 class Documenter(object):
@@ -58,9 +58,11 @@ def __init__(
         if module_name is None:
             module_name = title
 
+        self.module_name = module_name
+
         self.writer = RSTWriter(title, settings=settings)
 
-        self.module = self.writer.directive("module", module_name)
+        self.module: Directive
 
         # We need a string stream of some kind, FileStream is easiest
         self.input_stream = FileStream(file)
@@ -104,5 +106,16 @@ def process_docs(self, docs: List[DocumentationType]):
         :param docs: List of documentation objects.
         """
 
+        module_docs = [x for x in docs if isinstance(x, ModuleDocumentation)]
+
+        if len(module_docs) == 0:
+            docs.insert(0, ModuleDocumentation(self.module_name, ""))
+
+        for module_doc in module_docs:
+            if module_doc.name is None or len(module_doc.name) == 0:
+                module_doc.name = self.module_name
+            else:
+                self.writer.title = module_doc.name
+
         for doc in docs:
             doc.process(self.writer)
diff --git a/src/cminx/parser/CMake.g4 b/src/cminx/parser/CMake.g4
index 70c0a220..e8d3304f 100644
--- a/src/cminx/parser/CMake.g4
+++ b/src/cminx/parser/CMake.g4
@@ -17,11 +17,48 @@ grammar CMake;
 Changes:
 	Renamed from file to cmake_file to avoid conflict with Python
 	Added documented_command to list of expected parser rules
+	Added bracket_doccomment to parser rules
+	Added Docstring to lexer rules
 */
 cmake_file
-	: (documented_command | command_invocation)* EOF
+	: documented_module? (documented_command | command_invocation)* EOF
 	;
 
+/*Begin CMinx*/
+
+documented_command
+	: bracket_doccomment command_invocation
+	;
+
+documented_module
+    : Module_docstring
+    ;
+
+bracket_doccomment
+    : Docstring
+    ;
+
+Module_docstring
+    : Doccomment_start Space? '@module' (Space Unquoted_argument)? .*? '#]]'
+    ;
+
+Docstring
+        : Doccomment_start .*? '#]]'
+	;
+
+
+Doccomment_start
+    : '#[[['
+    ;
+
+Blockcomment_end
+    : '#]]'
+    ;
+
+
+
+/*End Cminx*/
+
 command_invocation
 	: Identifier '(' (single_argument|compound_argument)* ')'
 	;
@@ -81,6 +118,8 @@ Bracket_arg_nested
 	;
 
 
+
+
 Bracket_comment
 	: '#[' Bracket_arg_nested ']'
 	-> skip
@@ -106,14 +145,3 @@ Space
 	;
 
 
-
-/*Begin CMinx*/
-
-documented_command
-	: Bracket_doccomment command_invocation
-	;
-
-Bracket_doccomment
-        : '#[[[' .*? '#]]'
-	;
-
diff --git a/src/cminx/parser/CMake.interp b/src/cminx/parser/CMake.interp
index 595e4277..af5c0780 100644
--- a/src/cminx/parser/CMake.interp
+++ b/src/cminx/parser/CMake.interp
@@ -4,6 +4,9 @@ null
 ')'
 null
 null
+'#[[['
+'#]]'
+null
 null
 null
 null
@@ -17,12 +20,15 @@ token symbolic names:
 null
 null
 null
+Module_docstring
+Docstring
+Doccomment_start
+Blockcomment_end
 Identifier
 Unquoted_argument
 Escape_sequence
 Quoted_argument
 Bracket_argument
-Bracket_doccomment
 Bracket_comment
 Line_comment
 Newline
@@ -30,11 +36,13 @@ Space
 
 rule names:
 cmake_file
-command_invocation
 documented_command
+documented_module
+bracket_doccomment
+command_invocation
 single_argument
 compound_argument
 
 
 atn:
-[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 14, 48, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 3, 2, 3, 2, 7, 2, 15, 10, 2, 12, 2, 14, 2, 18, 11, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 26, 10, 3, 12, 3, 14, 3, 29, 11, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 7, 6, 41, 10, 6, 12, 6, 14, 6, 44, 11, 6, 3, 6, 3, 6, 3, 6, 2, 2, 7, 2, 4, 6, 8, 10, 2, 3, 4, 2, 5, 6, 8, 9, 2, 48, 2, 16, 3, 2, 2, 2, 4, 21, 3, 2, 2, 2, 6, 32, 3, 2, 2, 2, 8, 35, 3, 2, 2, 2, 10, 37, 3, 2, 2, 2, 12, 15, 5, 6, 4, 2, 13, 15, 5, 4, 3, 2, 14, 12, 3, 2, 2, 2, 14, 13, 3, 2, 2, 2, 15, 18, 3, 2, 2, 2, 16, 14, 3, 2, 2, 2, 16, 17, 3, 2, 2, 2, 17, 19, 3, 2, 2, 2, 18, 16, 3, 2, 2, 2, 19, 20, 7, 2, 2, 3, 20, 3, 3, 2, 2, 2, 21, 22, 7, 5, 2, 2, 22, 27, 7, 3, 2, 2, 23, 26, 5, 8, 5, 2, 24, 26, 5, 10, 6, 2, 25, 23, 3, 2, 2, 2, 25, 24, 3, 2, 2, 2, 26, 29, 3, 2, 2, 2, 27, 25, 3, 2, 2, 2, 27, 28, 3, 2, 2, 2, 28, 30, 3, 2, 2, 2, 29, 27, 3, 2, 2, 2, 30, 31, 7, 4, 2, 2, 31, 5, 3, 2, 2, 2, 32, 33, 7, 10, 2, 2, 33, 34, 5, 4, 3, 2, 34, 7, 3, 2, 2, 2, 35, 36, 9, 2, 2, 2, 36, 9, 3, 2, 2, 2, 37, 42, 7, 3, 2, 2, 38, 41, 5, 8, 5, 2, 39, 41, 5, 10, 6, 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, 8, 14, 16, 25, 27, 40, 42]
\ No newline at end of file
+[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
diff --git a/src/cminx/parser/CMake.tokens b/src/cminx/parser/CMake.tokens
index 8db27828..d2642839 100644
--- a/src/cminx/parser/CMake.tokens
+++ b/src/cminx/parser/CMake.tokens
@@ -1,14 +1,19 @@
 T__0=1
 T__1=2
-Identifier=3
-Unquoted_argument=4
-Escape_sequence=5
-Quoted_argument=6
-Bracket_argument=7
-Bracket_doccomment=8
-Bracket_comment=9
-Line_comment=10
-Newline=11
-Space=12
+Module_docstring=3
+Docstring=4
+Doccomment_start=5
+Blockcomment_end=6
+Identifier=7
+Unquoted_argument=8
+Escape_sequence=9
+Quoted_argument=10
+Bracket_argument=11
+Bracket_comment=12
+Line_comment=13
+Newline=14
+Space=15
 '('=1
 ')'=2
+'#[[['=5
+'#]]'=6
diff --git a/src/cminx/parser/CMakeLexer.interp b/src/cminx/parser/CMakeLexer.interp
index 950eba33..3b7e277b 100644
--- a/src/cminx/parser/CMakeLexer.interp
+++ b/src/cminx/parser/CMakeLexer.interp
@@ -4,6 +4,9 @@ null
 ')'
 null
 null
+'#[[['
+'#]]'
+null
 null
 null
 null
@@ -17,12 +20,15 @@ token symbolic names:
 null
 null
 null
+Module_docstring
+Docstring
+Doccomment_start
+Blockcomment_end
 Identifier
 Unquoted_argument
 Escape_sequence
 Quoted_argument
 Bracket_argument
-Bracket_doccomment
 Bracket_comment
 Line_comment
 Newline
@@ -31,6 +37,10 @@ Space
 rule names:
 T__0
 T__1
+Module_docstring
+Docstring
+Doccomment_start
+Blockcomment_end
 Identifier
 Unquoted_argument
 Escape_sequence
@@ -41,7 +51,6 @@ Quoted_argument
 Quoted_cont
 Bracket_argument
 Bracket_arg_nested
-Bracket_doccomment
 Bracket_comment
 Line_comment
 Newline
@@ -55,4 +64,4 @@ mode names:
 DEFAULT_MODE
 
 atn:
-[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 14, 192, 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, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 7, 4, 44, 10, 4, 12, 4, 14, 4, 47, 11, 4, 3, 5, 3, 5, 6, 5, 51, 10, 5, 13, 5, 14, 5, 52, 3, 6, 3, 6, 3, 6, 5, 6, 58, 10, 6, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 69, 10, 8, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 78, 10, 10, 12, 10, 14, 10, 81, 11, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 5, 11, 88, 10, 11, 3, 11, 5, 11, 91, 10, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 7, 13, 103, 10, 13, 12, 13, 14, 13, 106, 11, 13, 3, 13, 5, 13, 109, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 7, 14, 117, 10, 14, 12, 14, 14, 14, 120, 11, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 7, 16, 138, 10, 16, 12, 16, 14, 16, 141, 11, 16, 3, 16, 3, 16, 7, 16, 145, 10, 16, 12, 16, 14, 16, 148, 11, 16, 3, 16, 3, 16, 7, 16, 152, 10, 16, 12, 16, 14, 16, 155, 11, 16, 3, 16, 3, 16, 7, 16, 159, 10, 16, 12, 16, 14, 16, 162, 11, 16, 5, 16, 164, 10, 16, 3, 16, 3, 16, 5, 16, 168, 10, 16, 3, 16, 5, 16, 171, 10, 16, 3, 16, 3, 16, 3, 17, 3, 17, 5, 17, 177, 10, 17, 3, 17, 6, 17, 180, 10, 17, 13, 17, 14, 17, 181, 3, 17, 3, 17, 3, 18, 6, 18, 187, 10, 18, 13, 18, 14, 18, 188, 3, 18, 3, 18, 4, 104, 118, 2, 19, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 2, 15, 2, 17, 2, 19, 8, 21, 2, 23, 9, 25, 2, 27, 10, 29, 11, 31, 12, 33, 13, 35, 14, 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, 214, 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, 19, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 3, 37, 3, 2, 2, 2, 5, 39, 3, 2, 2, 2, 7, 41, 3, 2, 2, 2, 9, 50, 3, 2, 2, 2, 11, 57, 3, 2, 2, 2, 13, 59, 3, 2, 2, 2, 15, 68, 3, 2, 2, 2, 17, 70, 3, 2, 2, 2, 19, 73, 3, 2, 2, 2, 21, 84, 3, 2, 2, 2, 23, 92, 3, 2, 2, 2, 25, 108, 3, 2, 2, 2, 27, 110, 3, 2, 2, 2, 29, 125, 3, 2, 2, 2, 31, 133, 3, 2, 2, 2, 33, 179, 3, 2, 2, 2, 35, 186, 3, 2, 2, 2, 37, 38, 7, 42, 2, 2, 38, 4, 3, 2, 2, 2, 39, 40, 7, 43, 2, 2, 40, 6, 3, 2, 2, 2, 41, 45, 9, 2, 2, 2, 42, 44, 9, 3, 2, 2, 43, 42, 3, 2, 2, 2, 44, 47, 3, 2, 2, 2, 45, 43, 3, 2, 2, 2, 45, 46, 3, 2, 2, 2, 46, 8, 3, 2, 2, 2, 47, 45, 3, 2, 2, 2, 48, 51, 10, 4, 2, 2, 49, 51, 5, 11, 6, 2, 50, 48, 3, 2, 2, 2, 50, 49, 3, 2, 2, 2, 51, 52, 3, 2, 2, 2, 52, 50, 3, 2, 2, 2, 52, 53, 3, 2, 2, 2, 53, 10, 3, 2, 2, 2, 54, 58, 5, 13, 7, 2, 55, 58, 5, 15, 8, 2, 56, 58, 5, 17, 9, 2, 57, 54, 3, 2, 2, 2, 57, 55, 3, 2, 2, 2, 57, 56, 3, 2, 2, 2, 58, 12, 3, 2, 2, 2, 59, 60, 7, 94, 2, 2, 60, 61, 10, 5, 2, 2, 61, 14, 3, 2, 2, 2, 62, 63, 7, 94, 2, 2, 63, 69, 7, 118, 2, 2, 64, 65, 7, 94, 2, 2, 65, 69, 7, 116, 2, 2, 66, 67, 7, 94, 2, 2, 67, 69, 7, 112, 2, 2, 68, 62, 3, 2, 2, 2, 68, 64, 3, 2, 2, 2, 68, 66, 3, 2, 2, 2, 69, 16, 3, 2, 2, 2, 70, 71, 7, 94, 2, 2, 71, 72, 7, 61, 2, 2, 72, 18, 3, 2, 2, 2, 73, 79, 7, 36, 2, 2, 74, 78, 10, 6, 2, 2, 75, 78, 5, 11, 6, 2, 76, 78, 5, 21, 11, 2, 77, 74, 3, 2, 2, 2, 77, 75, 3, 2, 2, 2, 77, 76, 3, 2, 2, 2, 78, 81, 3, 2, 2, 2, 79, 77, 3, 2, 2, 2, 79, 80, 3, 2, 2, 2, 80, 82, 3, 2, 2, 2, 81, 79, 3, 2, 2, 2, 82, 83, 7, 36, 2, 2, 83, 20, 3, 2, 2, 2, 84, 90, 7, 94, 2, 2, 85, 87, 7, 15, 2, 2, 86, 88, 7, 12, 2, 2, 87, 86, 3, 2, 2, 2, 87, 88, 3, 2, 2, 2, 88, 91, 3, 2, 2, 2, 89, 91, 7, 12, 2, 2, 90, 85, 3, 2, 2, 2, 90, 89, 3, 2, 2, 2, 91, 22, 3, 2, 2, 2, 92, 93, 7, 93, 2, 2, 93, 94, 5, 25, 13, 2, 94, 95, 7, 95, 2, 2, 95, 24, 3, 2, 2, 2, 96, 97, 7, 63, 2, 2, 97, 98, 5, 25, 13, 2, 98, 99, 7, 63, 2, 2, 99, 109, 3, 2, 2, 2, 100, 104, 7, 93, 2, 2, 101, 103, 11, 2, 2, 2, 102, 101, 3, 2, 2, 2, 103, 106, 3, 2, 2, 2, 104, 105, 3, 2, 2, 2, 104, 102, 3, 2, 2, 2, 105, 107, 3, 2, 2, 2, 106, 104, 3, 2, 2, 2, 107, 109, 7, 95, 2, 2, 108, 96, 3, 2, 2, 2, 108, 100, 3, 2, 2, 2, 109, 26, 3, 2, 2, 2, 110, 111, 7, 37, 2, 2, 111, 112, 7, 93, 2, 2, 112, 113, 7, 93, 2, 2, 113, 114, 7, 93, 2, 2, 114, 118, 3, 2, 2, 2, 115, 117, 11, 2, 2, 2, 116, 115, 3, 2, 2, 2, 117, 120, 3, 2, 2, 2, 118, 119, 3, 2, 2, 2, 118, 116, 3, 2, 2, 2, 119, 121, 3, 2, 2, 2, 120, 118, 3, 2, 2, 2, 121, 122, 7, 37, 2, 2, 122, 123, 7, 95, 2, 2, 123, 124, 7, 95, 2, 2, 124, 28, 3, 2, 2, 2, 125, 126, 7, 37, 2, 2, 126, 127, 7, 93, 2, 2, 127, 128, 3, 2, 2, 2, 128, 129, 5, 25, 13, 2, 129, 130, 7, 95, 2, 2, 130, 131, 3, 2, 2, 2, 131, 132, 8, 15, 2, 2, 132, 30, 3, 2, 2, 2, 133, 163, 7, 37, 2, 2, 134, 164, 3, 2, 2, 2, 135, 139, 7, 93, 2, 2, 136, 138, 7, 63, 2, 2, 137, 136, 3, 2, 2, 2, 138, 141, 3, 2, 2, 2, 139, 137, 3, 2, 2, 2, 139, 140, 3, 2, 2, 2, 140, 164, 3, 2, 2, 2, 141, 139, 3, 2, 2, 2, 142, 146, 7, 93, 2, 2, 143, 145, 7, 63, 2, 2, 144, 143, 3, 2, 2, 2, 145, 148, 3, 2, 2, 2, 146, 144, 3, 2, 2, 2, 146, 147, 3, 2, 2, 2, 147, 149, 3, 2, 2, 2, 148, 146, 3, 2, 2, 2, 149, 153, 10, 7, 2, 2, 150, 152, 10, 8, 2, 2, 151, 150, 3, 2, 2, 2, 152, 155, 3, 2, 2, 2, 153, 151, 3, 2, 2, 2, 153, 154, 3, 2, 2, 2, 154, 164, 3, 2, 2, 2, 155, 153, 3, 2, 2, 2, 156, 160, 10, 9, 2, 2, 157, 159, 10, 8, 2, 2, 158, 157, 3, 2, 2, 2, 159, 162, 3, 2, 2, 2, 160, 158, 3, 2, 2, 2, 160, 161, 3, 2, 2, 2, 161, 164, 3, 2, 2, 2, 162, 160, 3, 2, 2, 2, 163, 134, 3, 2, 2, 2, 163, 135, 3, 2, 2, 2, 163, 142, 3, 2, 2, 2, 163, 156, 3, 2, 2, 2, 164, 170, 3, 2, 2, 2, 165, 167, 7, 15, 2, 2, 166, 168, 7, 12, 2, 2, 167, 166, 3, 2, 2, 2, 167, 168, 3, 2, 2, 2, 168, 171, 3, 2, 2, 2, 169, 171, 9, 10, 2, 2, 170, 165, 3, 2, 2, 2, 170, 169, 3, 2, 2, 2, 171, 172, 3, 2, 2, 2, 172, 173, 8, 16, 2, 2, 173, 32, 3, 2, 2, 2, 174, 176, 7, 15, 2, 2, 175, 177, 7, 12, 2, 2, 176, 175, 3, 2, 2, 2, 176, 177, 3, 2, 2, 2, 177, 180, 3, 2, 2, 2, 178, 180, 7, 12, 2, 2, 179, 174, 3, 2, 2, 2, 179, 178, 3, 2, 2, 2, 180, 181, 3, 2, 2, 2, 181, 179, 3, 2, 2, 2, 181, 182, 3, 2, 2, 2, 182, 183, 3, 2, 2, 2, 183, 184, 8, 17, 2, 2, 184, 34, 3, 2, 2, 2, 185, 187, 9, 11, 2, 2, 186, 185, 3, 2, 2, 2, 187, 188, 3, 2, 2, 2, 188, 186, 3, 2, 2, 2, 188, 189, 3, 2, 2, 2, 189, 190, 3, 2, 2, 2, 190, 191, 8, 18, 2, 2, 191, 36, 3, 2, 2, 2, 26, 2, 45, 50, 52, 57, 68, 77, 79, 87, 90, 104, 108, 118, 139, 146, 153, 160, 163, 167, 170, 176, 179, 181, 188, 3, 8, 2, 2]
\ No newline at end of file
+[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
diff --git a/src/cminx/parser/CMakeLexer.py b/src/cminx/parser/CMakeLexer.py
index cfaa630b..c898f732 100644
--- a/src/cminx/parser/CMakeLexer.py
+++ b/src/cminx/parser/CMakeLexer.py
@@ -1,139 +1,166 @@
 # Generated from CMake.g4 by ANTLR 4.7.2
-import sys
-from io import StringIO
-
 from antlr4 import *
+from io import StringIO
 from typing.io import TextIO
+import sys
 
 
 def serializedATN():
     with StringIO() as buf:
-        buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\16")
-        buf.write("\u00c0\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("\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("\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\3\2")
-        buf.write("\3\2\3\3\3\3\3\4\3\4\7\4,\n\4\f\4\16\4/\13\4\3\5\3\5\6")
-        buf.write("\5\63\n\5\r\5\16\5\64\3\6\3\6\3\6\5\6:\n\6\3\7\3\7\3\7")
-        buf.write("\3\b\3\b\3\b\3\b\3\b\3\b\5\bE\n\b\3\t\3\t\3\t\3\n\3\n")
-        buf.write("\3\n\3\n\7\nN\n\n\f\n\16\nQ\13\n\3\n\3\n\3\13\3\13\3\13")
-        buf.write("\5\13X\n\13\3\13\5\13[\n\13\3\f\3\f\3\f\3\f\3\r\3\r\3")
-        buf.write("\r\3\r\3\r\3\r\7\rg\n\r\f\r\16\rj\13\r\3\r\5\rm\n\r\3")
-        buf.write("\16\3\16\3\16\3\16\3\16\3\16\7\16u\n\16\f\16\16\16x\13")
-        buf.write("\16\3\16\3\16\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\17")
-        buf.write("\3\17\3\17\3\20\3\20\3\20\3\20\7\20\u008a\n\20\f\20\16")
-        buf.write("\20\u008d\13\20\3\20\3\20\7\20\u0091\n\20\f\20\16\20\u0094")
-        buf.write("\13\20\3\20\3\20\7\20\u0098\n\20\f\20\16\20\u009b\13\20")
-        buf.write("\3\20\3\20\7\20\u009f\n\20\f\20\16\20\u00a2\13\20\5\20")
-        buf.write("\u00a4\n\20\3\20\3\20\5\20\u00a8\n\20\3\20\5\20\u00ab")
-        buf.write("\n\20\3\20\3\20\3\21\3\21\5\21\u00b1\n\21\3\21\6\21\u00b4")
-        buf.write("\n\21\r\21\16\21\u00b5\3\21\3\21\3\22\6\22\u00bb\n\22")
-        buf.write("\r\22\16\22\u00bc\3\22\3\22\4hv\2\23\3\3\5\4\7\5\t\6\13")
-        buf.write("\7\r\2\17\2\21\2\23\b\25\2\27\t\31\2\33\n\35\13\37\f!")
-        buf.write("\r#\16\3\2\f\5\2C\\aac|\6\2\62;C\\aac|\b\2\13\f\17\17")
-        buf.write("\"\"$%*+^^\6\2\62;==C\\c|\4\2$$^^\6\2\f\f\17\17??]]\4")
-        buf.write("\2\f\f\17\17\5\2\f\f\17\17]]\3\3\f\f\4\2\13\13\"\"\2\u00d6")
-        buf.write("\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13")
-        buf.write("\3\2\2\2\2\23\3\2\2\2\2\27\3\2\2\2\2\33\3\2\2\2\2\35\3")
-        buf.write("\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\3%\3\2\2\2\5")
-        buf.write("\'\3\2\2\2\7)\3\2\2\2\t\62\3\2\2\2\139\3\2\2\2\r;\3\2")
-        buf.write("\2\2\17D\3\2\2\2\21F\3\2\2\2\23I\3\2\2\2\25T\3\2\2\2\27")
-        buf.write("\\\3\2\2\2\31l\3\2\2\2\33n\3\2\2\2\35}\3\2\2\2\37\u0085")
-        buf.write("\3\2\2\2!\u00b3\3\2\2\2#\u00ba\3\2\2\2%&\7*\2\2&\4\3\2")
-        buf.write("\2\2\'(\7+\2\2(\6\3\2\2\2)-\t\2\2\2*,\t\3\2\2+*\3\2\2")
-        buf.write("\2,/\3\2\2\2-+\3\2\2\2-.\3\2\2\2.\b\3\2\2\2/-\3\2\2\2")
-        buf.write("\60\63\n\4\2\2\61\63\5\13\6\2\62\60\3\2\2\2\62\61\3\2")
-        buf.write("\2\2\63\64\3\2\2\2\64\62\3\2\2\2\64\65\3\2\2\2\65\n\3")
-        buf.write("\2\2\2\66:\5\r\7\2\67:\5\17\b\28:\5\21\t\29\66\3\2\2\2")
-        buf.write("9\67\3\2\2\298\3\2\2\2:\f\3\2\2\2;<\7^\2\2<=\n\5\2\2=")
-        buf.write("\16\3\2\2\2>?\7^\2\2?E\7v\2\2@A\7^\2\2AE\7t\2\2BC\7^\2")
-        buf.write("\2CE\7p\2\2D>\3\2\2\2D@\3\2\2\2DB\3\2\2\2E\20\3\2\2\2")
-        buf.write("FG\7^\2\2GH\7=\2\2H\22\3\2\2\2IO\7$\2\2JN\n\6\2\2KN\5")
-        buf.write("\13\6\2LN\5\25\13\2MJ\3\2\2\2MK\3\2\2\2ML\3\2\2\2NQ\3")
-        buf.write("\2\2\2OM\3\2\2\2OP\3\2\2\2PR\3\2\2\2QO\3\2\2\2RS\7$\2")
-        buf.write("\2S\24\3\2\2\2TZ\7^\2\2UW\7\17\2\2VX\7\f\2\2WV\3\2\2\2")
-        buf.write("WX\3\2\2\2X[\3\2\2\2Y[\7\f\2\2ZU\3\2\2\2ZY\3\2\2\2[\26")
-        buf.write("\3\2\2\2\\]\7]\2\2]^\5\31\r\2^_\7_\2\2_\30\3\2\2\2`a\7")
-        buf.write("?\2\2ab\5\31\r\2bc\7?\2\2cm\3\2\2\2dh\7]\2\2eg\13\2\2")
-        buf.write("\2fe\3\2\2\2gj\3\2\2\2hi\3\2\2\2hf\3\2\2\2ik\3\2\2\2j")
-        buf.write("h\3\2\2\2km\7_\2\2l`\3\2\2\2ld\3\2\2\2m\32\3\2\2\2no\7")
-        buf.write("%\2\2op\7]\2\2pq\7]\2\2qr\7]\2\2rv\3\2\2\2su\13\2\2\2")
-        buf.write("ts\3\2\2\2ux\3\2\2\2vw\3\2\2\2vt\3\2\2\2wy\3\2\2\2xv\3")
-        buf.write("\2\2\2yz\7%\2\2z{\7_\2\2{|\7_\2\2|\34\3\2\2\2}~\7%\2\2")
-        buf.write("~\177\7]\2\2\177\u0080\3\2\2\2\u0080\u0081\5\31\r\2\u0081")
-        buf.write("\u0082\7_\2\2\u0082\u0083\3\2\2\2\u0083\u0084\b\17\2\2")
-        buf.write("\u0084\36\3\2\2\2\u0085\u00a3\7%\2\2\u0086\u00a4\3\2\2")
-        buf.write("\2\u0087\u008b\7]\2\2\u0088\u008a\7?\2\2\u0089\u0088\3")
-        buf.write("\2\2\2\u008a\u008d\3\2\2\2\u008b\u0089\3\2\2\2\u008b\u008c")
-        buf.write("\3\2\2\2\u008c\u00a4\3\2\2\2\u008d\u008b\3\2\2\2\u008e")
-        buf.write("\u0092\7]\2\2\u008f\u0091\7?\2\2\u0090\u008f\3\2\2\2\u0091")
-        buf.write("\u0094\3\2\2\2\u0092\u0090\3\2\2\2\u0092\u0093\3\2\2\2")
-        buf.write("\u0093\u0095\3\2\2\2\u0094\u0092\3\2\2\2\u0095\u0099\n")
-        buf.write("\7\2\2\u0096\u0098\n\b\2\2\u0097\u0096\3\2\2\2\u0098\u009b")
-        buf.write("\3\2\2\2\u0099\u0097\3\2\2\2\u0099\u009a\3\2\2\2\u009a")
-        buf.write("\u00a4\3\2\2\2\u009b\u0099\3\2\2\2\u009c\u00a0\n\t\2\2")
-        buf.write("\u009d\u009f\n\b\2\2\u009e\u009d\3\2\2\2\u009f\u00a2\3")
-        buf.write("\2\2\2\u00a0\u009e\3\2\2\2\u00a0\u00a1\3\2\2\2\u00a1\u00a4")
-        buf.write("\3\2\2\2\u00a2\u00a0\3\2\2\2\u00a3\u0086\3\2\2\2\u00a3")
-        buf.write("\u0087\3\2\2\2\u00a3\u008e\3\2\2\2\u00a3\u009c\3\2\2\2")
-        buf.write("\u00a4\u00aa\3\2\2\2\u00a5\u00a7\7\17\2\2\u00a6\u00a8")
-        buf.write("\7\f\2\2\u00a7\u00a6\3\2\2\2\u00a7\u00a8\3\2\2\2\u00a8")
-        buf.write("\u00ab\3\2\2\2\u00a9\u00ab\t\n\2\2\u00aa\u00a5\3\2\2\2")
-        buf.write("\u00aa\u00a9\3\2\2\2\u00ab\u00ac\3\2\2\2\u00ac\u00ad\b")
-        buf.write("\20\2\2\u00ad \3\2\2\2\u00ae\u00b0\7\17\2\2\u00af\u00b1")
-        buf.write("\7\f\2\2\u00b0\u00af\3\2\2\2\u00b0\u00b1\3\2\2\2\u00b1")
-        buf.write("\u00b4\3\2\2\2\u00b2\u00b4\7\f\2\2\u00b3\u00ae\3\2\2\2")
-        buf.write("\u00b3\u00b2\3\2\2\2\u00b4\u00b5\3\2\2\2\u00b5\u00b3\3")
-        buf.write("\2\2\2\u00b5\u00b6\3\2\2\2\u00b6\u00b7\3\2\2\2\u00b7\u00b8")
-        buf.write("\b\21\2\2\u00b8\"\3\2\2\2\u00b9\u00bb\t\13\2\2\u00ba\u00b9")
-        buf.write("\3\2\2\2\u00bb\u00bc\3\2\2\2\u00bc\u00ba\3\2\2\2\u00bc")
-        buf.write("\u00bd\3\2\2\2\u00bd\u00be\3\2\2\2\u00be\u00bf\b\22\2")
-        buf.write("\2\u00bf$\3\2\2\2\32\2-\62\649DMOWZhlv\u008b\u0092\u0099")
-        buf.write("\u00a0\u00a3\u00a7\u00aa\u00b0\u00b3\u00b5\u00bc\3\b\2")
-        buf.write("\2")
+        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("\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")
+        buf.write("\2\f\f\17\17??]]\4\2\f\f\17\17\5\2\f\f\17\17]]\3\3\f\f")
+        buf.write("\4\2\13\13\"\"\2\u00ff\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2")
+        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("\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")
         return buf.getvalue()
 
 
 class CMakeLexer(Lexer):
+
     atn = ATNDeserializer().deserialize(serializedATN())
 
-    decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)]
+    decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ]
 
     T__0 = 1
     T__1 = 2
-    Identifier = 3
-    Unquoted_argument = 4
-    Escape_sequence = 5
-    Quoted_argument = 6
-    Bracket_argument = 7
-    Bracket_doccomment = 8
-    Bracket_comment = 9
-    Line_comment = 10
-    Newline = 11
-    Space = 12
-
-    channelNames = [u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN"]
-
-    modeNames = ["DEFAULT_MODE"]
-
-    literalNames = ["<INVALID>",
-                    "'('", "')'"]
-
-    symbolicNames = ["<INVALID>",
-                     "Identifier", "Unquoted_argument", "Escape_sequence", "Quoted_argument",
-                     "Bracket_argument", "Bracket_doccomment", "Bracket_comment",
-                     "Line_comment", "Newline", "Space"]
-
-    ruleNames = ["T__0", "T__1", "Identifier", "Unquoted_argument", "Escape_sequence",
-                 "Escape_identity", "Escape_encoded", "Escape_semicolon",
-                 "Quoted_argument", "Quoted_cont", "Bracket_argument",
-                 "Bracket_arg_nested", "Bracket_doccomment", "Bracket_comment",
-                 "Line_comment", "Newline", "Space"]
+    Module_docstring = 3
+    Docstring = 4
+    Doccomment_start = 5
+    Blockcomment_end = 6
+    Identifier = 7
+    Unquoted_argument = 8
+    Escape_sequence = 9
+    Quoted_argument = 10
+    Bracket_argument = 11
+    Bracket_comment = 12
+    Line_comment = 13
+    Newline = 14
+    Space = 15
+
+    channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ]
+
+    modeNames = [ "DEFAULT_MODE" ]
+
+    literalNames = [ "<INVALID>",
+            "'('", "')'", "'#[[['", "'#]]'" ]
+
+    symbolicNames = [ "<INVALID>",
+            "Module_docstring", "Docstring", "Doccomment_start", "Blockcomment_end", 
+            "Identifier", "Unquoted_argument", "Escape_sequence", "Quoted_argument", 
+            "Bracket_argument", "Bracket_comment", "Line_comment", "Newline", 
+            "Space" ]
+
+    ruleNames = [ "T__0", "T__1", "Module_docstring", "Docstring", "Doccomment_start", 
+                  "Blockcomment_end", "Identifier", "Unquoted_argument", 
+                  "Escape_sequence", "Escape_identity", "Escape_encoded", 
+                  "Escape_semicolon", "Quoted_argument", "Quoted_cont", 
+                  "Bracket_argument", "Bracket_arg_nested", "Bracket_comment", 
+                  "Line_comment", "Newline", "Space" ]
 
     grammarFileName = "CMake.g4"
 
-    def __init__(self, input=None, output: TextIO = sys.stdout):
+    def __init__(self, input=None, output:TextIO = sys.stdout):
         super().__init__(input, output)
         self.checkVersion("4.7.2")
         self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache())
         self._actions = None
         self._predicates = None
+
+
diff --git a/src/cminx/parser/CMakeLexer.tokens b/src/cminx/parser/CMakeLexer.tokens
index 8db27828..d2642839 100644
--- a/src/cminx/parser/CMakeLexer.tokens
+++ b/src/cminx/parser/CMakeLexer.tokens
@@ -1,14 +1,19 @@
 T__0=1
 T__1=2
-Identifier=3
-Unquoted_argument=4
-Escape_sequence=5
-Quoted_argument=6
-Bracket_argument=7
-Bracket_doccomment=8
-Bracket_comment=9
-Line_comment=10
-Newline=11
-Space=12
+Module_docstring=3
+Docstring=4
+Doccomment_start=5
+Blockcomment_end=6
+Identifier=7
+Unquoted_argument=8
+Escape_sequence=9
+Quoted_argument=10
+Bracket_argument=11
+Bracket_comment=12
+Line_comment=13
+Newline=14
+Space=15
 '('=1
 ')'=2
+'#[[['=5
+'#]]'=6
diff --git a/src/cminx/parser/CMakeListener.py b/src/cminx/parser/CMakeListener.py
index 65a89354..72874f9f 100644
--- a/src/cminx/parser/CMakeListener.py
+++ b/src/cminx/parser/CMakeListener.py
@@ -1,51 +1,73 @@
 # Generated from CMake.g4 by ANTLR 4.7.2
 from antlr4 import *
-
 if __name__ is not None and "." in __name__:
     from .CMakeParser import CMakeParser
 else:
     from CMakeParser import CMakeParser
 
-
 # This class defines a complete listener for a parse tree produced by CMakeParser.
 class CMakeListener(ParseTreeListener):
 
     # Enter a parse tree produced by CMakeParser#cmake_file.
-    def enterCmake_file(self, ctx: CMakeParser.Cmake_fileContext):
+    def enterCmake_file(self, ctx:CMakeParser.Cmake_fileContext):
         pass
 
     # Exit a parse tree produced by CMakeParser#cmake_file.
-    def exitCmake_file(self, ctx: CMakeParser.Cmake_fileContext):
+    def exitCmake_file(self, ctx:CMakeParser.Cmake_fileContext):
         pass
 
-    # Enter a parse tree produced by CMakeParser#command_invocation.
-    def enterCommand_invocation(self, ctx: CMakeParser.Command_invocationContext):
+
+    # Enter a parse tree produced by CMakeParser#documented_command.
+    def enterDocumented_command(self, ctx:CMakeParser.Documented_commandContext):
         pass
 
-    # Exit a parse tree produced by CMakeParser#command_invocation.
-    def exitCommand_invocation(self, ctx: CMakeParser.Command_invocationContext):
+    # Exit a parse tree produced by CMakeParser#documented_command.
+    def exitDocumented_command(self, ctx:CMakeParser.Documented_commandContext):
         pass
 
-    # Enter a parse tree produced by CMakeParser#documented_command.
-    def enterDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
+
+    # Enter a parse tree produced by CMakeParser#documented_module.
+    def enterDocumented_module(self, ctx:CMakeParser.Documented_moduleContext):
         pass
 
-    # Exit a parse tree produced by CMakeParser#documented_command.
-    def exitDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
+    # Exit a parse tree produced by CMakeParser#documented_module.
+    def exitDocumented_module(self, ctx:CMakeParser.Documented_moduleContext):
+        pass
+
+
+    # Enter a parse tree produced by CMakeParser#bracket_doccomment.
+    def enterBracket_doccomment(self, ctx:CMakeParser.Bracket_doccommentContext):
         pass
 
+    # Exit a parse tree produced by CMakeParser#bracket_doccomment.
+    def exitBracket_doccomment(self, ctx:CMakeParser.Bracket_doccommentContext):
+        pass
+
+
+    # Enter a parse tree produced by CMakeParser#command_invocation.
+    def enterCommand_invocation(self, ctx:CMakeParser.Command_invocationContext):
+        pass
+
+    # Exit a parse tree produced by CMakeParser#command_invocation.
+    def exitCommand_invocation(self, ctx:CMakeParser.Command_invocationContext):
+        pass
+
+
     # Enter a parse tree produced by CMakeParser#single_argument.
-    def enterSingle_argument(self, ctx: CMakeParser.Single_argumentContext):
+    def enterSingle_argument(self, ctx:CMakeParser.Single_argumentContext):
         pass
 
     # Exit a parse tree produced by CMakeParser#single_argument.
-    def exitSingle_argument(self, ctx: CMakeParser.Single_argumentContext):
+    def exitSingle_argument(self, ctx:CMakeParser.Single_argumentContext):
         pass
 
+
     # Enter a parse tree produced by CMakeParser#compound_argument.
-    def enterCompound_argument(self, ctx: CMakeParser.Compound_argumentContext):
+    def enterCompound_argument(self, ctx:CMakeParser.Compound_argumentContext):
         pass
 
     # Exit a parse tree produced by CMakeParser#compound_argument.
-    def exitCompound_argument(self, ctx: CMakeParser.Compound_argumentContext):
+    def exitCompound_argument(self, ctx:CMakeParser.Compound_argumentContext):
         pass
+
+
diff --git a/src/cminx/parser/CMakeParser.py b/src/cminx/parser/CMakeParser.py
index ac03a3e9..f3168df2 100644
--- a/src/cminx/parser/CMakeParser.py
+++ b/src/cminx/parser/CMakeParser.py
@@ -1,147 +1,176 @@
 # Generated from CMake.g4 by ANTLR 4.7.2
 # encoding: utf-8
-import sys
-from io import StringIO
-
 from antlr4 import *
+from io import StringIO
 from typing.io import TextIO
-
+import sys
 
 def serializedATN():
     with StringIO() as buf:
-        buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\16")
-        buf.write("\60\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\3\2\3\2\7")
-        buf.write("\2\17\n\2\f\2\16\2\22\13\2\3\2\3\2\3\3\3\3\3\3\3\3\7\3")
-        buf.write("\32\n\3\f\3\16\3\35\13\3\3\3\3\3\3\4\3\4\3\4\3\5\3\5\3")
-        buf.write("\6\3\6\3\6\7\6)\n\6\f\6\16\6,\13\6\3\6\3\6\3\6\2\2\7\2")
-        buf.write("\4\6\b\n\2\3\4\2\5\6\b\t\2\60\2\20\3\2\2\2\4\25\3\2\2")
-        buf.write("\2\6 \3\2\2\2\b#\3\2\2\2\n%\3\2\2\2\f\17\5\6\4\2\r\17")
-        buf.write("\5\4\3\2\16\f\3\2\2\2\16\r\3\2\2\2\17\22\3\2\2\2\20\16")
-        buf.write("\3\2\2\2\20\21\3\2\2\2\21\23\3\2\2\2\22\20\3\2\2\2\23")
-        buf.write("\24\7\2\2\3\24\3\3\2\2\2\25\26\7\5\2\2\26\33\7\3\2\2\27")
-        buf.write("\32\5\b\5\2\30\32\5\n\6\2\31\27\3\2\2\2\31\30\3\2\2\2")
-        buf.write("\32\35\3\2\2\2\33\31\3\2\2\2\33\34\3\2\2\2\34\36\3\2\2")
-        buf.write("\2\35\33\3\2\2\2\36\37\7\4\2\2\37\5\3\2\2\2 !\7\n\2\2")
-        buf.write("!\"\5\4\3\2\"\7\3\2\2\2#$\t\2\2\2$\t\3\2\2\2%*\7\3\2\2")
-        buf.write("&)\5\b\5\2\')\5\n\6\2(&\3\2\2\2(\'\3\2\2\2),\3\2\2\2*")
-        buf.write("(\3\2\2\2*+\3\2\2\2+-\3\2\2\2,*\3\2\2\2-.\7\4\2\2.\13")
-        buf.write("\3\2\2\2\b\16\20\31\33(*")
+        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")
         return buf.getvalue()
 
 
-class CMakeParser(Parser):
+class CMakeParser ( Parser ):
+
     grammarFileName = "CMake.g4"
 
     atn = ATNDeserializer().deserialize(serializedATN())
 
-    decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)]
+    decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ]
 
     sharedContextCache = PredictionContextCache()
 
-    literalNames = ["<INVALID>", "'('", "')'"]
+    literalNames = [ "<INVALID>", "'('", "')'", "<INVALID>", "<INVALID>", 
+                     "'#[[['", "'#]]'" ]
 
-    symbolicNames = ["<INVALID>", "<INVALID>", "<INVALID>", "Identifier",
-                     "Unquoted_argument", "Escape_sequence", "Quoted_argument",
-                     "Bracket_argument", "Bracket_doccomment", "Bracket_comment",
-                     "Line_comment", "Newline", "Space"]
+    symbolicNames = [ "<INVALID>", "<INVALID>", "<INVALID>", "Module_docstring", 
+                      "Docstring", "Doccomment_start", "Blockcomment_end", 
+                      "Identifier", "Unquoted_argument", "Escape_sequence", 
+                      "Quoted_argument", "Bracket_argument", "Bracket_comment", 
+                      "Line_comment", "Newline", "Space" ]
 
     RULE_cmake_file = 0
-    RULE_command_invocation = 1
-    RULE_documented_command = 2
-    RULE_single_argument = 3
-    RULE_compound_argument = 4
+    RULE_documented_command = 1
+    RULE_documented_module = 2
+    RULE_bracket_doccomment = 3
+    RULE_command_invocation = 4
+    RULE_single_argument = 5
+    RULE_compound_argument = 6
 
-    ruleNames = ["cmake_file", "command_invocation", "documented_command",
-                 "single_argument", "compound_argument"]
+    ruleNames =  [ "cmake_file", "documented_command", "documented_module", 
+                   "bracket_doccomment", "command_invocation", "single_argument", 
+                   "compound_argument" ]
 
     EOF = Token.EOF
-    T__0 = 1
-    T__1 = 2
-    Identifier = 3
-    Unquoted_argument = 4
-    Escape_sequence = 5
-    Quoted_argument = 6
-    Bracket_argument = 7
-    Bracket_doccomment = 8
-    Bracket_comment = 9
-    Line_comment = 10
-    Newline = 11
-    Space = 12
-
-    def __init__(self, input: TokenStream, output: TextIO = sys.stdout):
+    T__0=1
+    T__1=2
+    Module_docstring=3
+    Docstring=4
+    Doccomment_start=5
+    Blockcomment_end=6
+    Identifier=7
+    Unquoted_argument=8
+    Escape_sequence=9
+    Quoted_argument=10
+    Bracket_argument=11
+    Bracket_comment=12
+    Line_comment=13
+    Newline=14
+    Space=15
+
+    def __init__(self, input:TokenStream, output:TextIO = sys.stdout):
         super().__init__(input, output)
         self.checkVersion("4.7.2")
         self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache)
         self._predicates = None
 
+
+
     class Cmake_fileContext(ParserRuleContext):
 
-        def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1):
+        def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
 
         def EOF(self):
             return self.getToken(CMakeParser.EOF, 0)
 
-        def documented_command(self, i: int = None):
+        def documented_module(self):
+            return self.getTypedRuleContext(CMakeParser.Documented_moduleContext,0)
+
+
+        def documented_command(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(CMakeParser.Documented_commandContext)
             else:
-                return self.getTypedRuleContext(CMakeParser.Documented_commandContext, i)
+                return self.getTypedRuleContext(CMakeParser.Documented_commandContext,i)
 
-        def command_invocation(self, i: int = None):
+
+        def command_invocation(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(CMakeParser.Command_invocationContext)
             else:
-                return self.getTypedRuleContext(CMakeParser.Command_invocationContext, i)
+                return self.getTypedRuleContext(CMakeParser.Command_invocationContext,i)
+
 
         def getRuleIndex(self):
             return CMakeParser.RULE_cmake_file
 
-        def enterRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "enterCmake_file"):
+        def enterRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "enterCmake_file" ):
                 listener.enterCmake_file(self)
 
-        def exitRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "exitCmake_file"):
+        def exitRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "exitCmake_file" ):
                 listener.exitCmake_file(self)
 
-        def accept(self, visitor: ParseTreeVisitor):
-            if hasattr(visitor, "visitCmake_file"):
+        def accept(self, visitor:ParseTreeVisitor):
+            if hasattr( visitor, "visitCmake_file" ):
                 return visitor.visitCmake_file(self)
             else:
                 return visitor.visitChildren(self)
 
+
+
+
     def cmake_file(self):
 
         localctx = CMakeParser.Cmake_fileContext(self, self._ctx, self.state)
         self.enterRule(localctx, 0, self.RULE_cmake_file)
-        self._la = 0  # Token type
+        self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 14
+            self.state = 15
             self._errHandler.sync(self)
             _la = self._input.LA(1)
-            while _la == CMakeParser.Identifier or _la == CMakeParser.Bracket_doccomment:
-                self.state = 12
+            if _la==CMakeParser.Module_docstring:
+                self.state = 14
+                self.documented_module()
+
+
+            self.state = 21
+            self._errHandler.sync(self)
+            _la = self._input.LA(1)
+            while _la==CMakeParser.Docstring or _la==CMakeParser.Identifier:
+                self.state = 19
                 self._errHandler.sync(self)
                 token = self._input.LA(1)
-                if token in [CMakeParser.Bracket_doccomment]:
-                    self.state = 10
+                if token in [CMakeParser.Docstring]:
+                    self.state = 17
                     self.documented_command()
                     pass
                 elif token in [CMakeParser.Identifier]:
-                    self.state = 11
+                    self.state = 18
                     self.command_invocation()
                     pass
                 else:
                     raise NoViableAltException(self)
 
-                self.state = 16
+                self.state = 23
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
-            self.state = 17
+            self.state = 24
             self.match(CMakeParser.EOF)
         except RecognitionException as re:
             localctx.exception = re
@@ -151,81 +180,225 @@ def cmake_file(self):
             self.exitRule()
         return localctx
 
+    class Documented_commandContext(ParserRuleContext):
+
+        def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+            super().__init__(parent, invokingState)
+            self.parser = parser
+
+        def bracket_doccomment(self):
+            return self.getTypedRuleContext(CMakeParser.Bracket_doccommentContext,0)
+
+
+        def command_invocation(self):
+            return self.getTypedRuleContext(CMakeParser.Command_invocationContext,0)
+
+
+        def getRuleIndex(self):
+            return CMakeParser.RULE_documented_command
+
+        def enterRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "enterDocumented_command" ):
+                listener.enterDocumented_command(self)
+
+        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)
+
+
+
+
+    def documented_command(self):
+
+        localctx = CMakeParser.Documented_commandContext(self, self._ctx, self.state)
+        self.enterRule(localctx, 2, self.RULE_documented_command)
+        try:
+            self.enterOuterAlt(localctx, 1)
+            self.state = 26
+            self.bracket_doccomment()
+            self.state = 27
+            self.command_invocation()
+        except RecognitionException as re:
+            localctx.exception = re
+            self._errHandler.reportError(self, re)
+            self._errHandler.recover(self, re)
+        finally:
+            self.exitRule()
+        return localctx
+
+    class Documented_moduleContext(ParserRuleContext):
+
+        def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+            super().__init__(parent, invokingState)
+            self.parser = parser
+
+        def Module_docstring(self):
+            return self.getToken(CMakeParser.Module_docstring, 0)
+
+        def getRuleIndex(self):
+            return CMakeParser.RULE_documented_module
+
+        def enterRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "enterDocumented_module" ):
+                listener.enterDocumented_module(self)
+
+        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)
+
+
+
+
+    def documented_module(self):
+
+        localctx = CMakeParser.Documented_moduleContext(self, self._ctx, self.state)
+        self.enterRule(localctx, 4, self.RULE_documented_module)
+        try:
+            self.enterOuterAlt(localctx, 1)
+            self.state = 29
+            self.match(CMakeParser.Module_docstring)
+        except RecognitionException as re:
+            localctx.exception = re
+            self._errHandler.reportError(self, re)
+            self._errHandler.recover(self, re)
+        finally:
+            self.exitRule()
+        return localctx
+
+    class Bracket_doccommentContext(ParserRuleContext):
+
+        def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+            super().__init__(parent, invokingState)
+            self.parser = parser
+
+        def Docstring(self):
+            return self.getToken(CMakeParser.Docstring, 0)
+
+        def getRuleIndex(self):
+            return CMakeParser.RULE_bracket_doccomment
+
+        def enterRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "enterBracket_doccomment" ):
+                listener.enterBracket_doccomment(self)
+
+        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)
+
+
+
+
+    def bracket_doccomment(self):
+
+        localctx = CMakeParser.Bracket_doccommentContext(self, self._ctx, self.state)
+        self.enterRule(localctx, 6, self.RULE_bracket_doccomment)
+        try:
+            self.enterOuterAlt(localctx, 1)
+            self.state = 31
+            self.match(CMakeParser.Docstring)
+        except RecognitionException as re:
+            localctx.exception = re
+            self._errHandler.reportError(self, re)
+            self._errHandler.recover(self, re)
+        finally:
+            self.exitRule()
+        return localctx
+
     class Command_invocationContext(ParserRuleContext):
 
-        def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1):
+        def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
 
         def Identifier(self):
             return self.getToken(CMakeParser.Identifier, 0)
 
-        def single_argument(self, i: int = None):
+        def single_argument(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(CMakeParser.Single_argumentContext)
             else:
-                return self.getTypedRuleContext(CMakeParser.Single_argumentContext, i)
+                return self.getTypedRuleContext(CMakeParser.Single_argumentContext,i)
 
-        def compound_argument(self, i: int = None):
+
+        def compound_argument(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(CMakeParser.Compound_argumentContext)
             else:
-                return self.getTypedRuleContext(CMakeParser.Compound_argumentContext, i)
+                return self.getTypedRuleContext(CMakeParser.Compound_argumentContext,i)
+
 
         def getRuleIndex(self):
             return CMakeParser.RULE_command_invocation
 
-        def enterRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "enterCommand_invocation"):
+        def enterRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "enterCommand_invocation" ):
                 listener.enterCommand_invocation(self)
 
-        def exitRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "exitCommand_invocation"):
+        def exitRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "exitCommand_invocation" ):
                 listener.exitCommand_invocation(self)
 
-        def accept(self, visitor: ParseTreeVisitor):
-            if hasattr(visitor, "visitCommand_invocation"):
+        def accept(self, visitor:ParseTreeVisitor):
+            if hasattr( visitor, "visitCommand_invocation" ):
                 return visitor.visitCommand_invocation(self)
             else:
                 return visitor.visitChildren(self)
 
+
+
+
     def command_invocation(self):
 
         localctx = CMakeParser.Command_invocationContext(self, self._ctx, self.state)
-        self.enterRule(localctx, 2, self.RULE_command_invocation)
-        self._la = 0  # Token type
+        self.enterRule(localctx, 8, self.RULE_command_invocation)
+        self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 19
+            self.state = 33
             self.match(CMakeParser.Identifier)
-            self.state = 20
+            self.state = 34
             self.match(CMakeParser.T__0)
-            self.state = 25
+            self.state = 39
             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 = 23
+            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._errHandler.sync(self)
                 token = self._input.LA(1)
-                if token in [CMakeParser.Identifier, CMakeParser.Unquoted_argument, CMakeParser.Quoted_argument,
-                             CMakeParser.Bracket_argument]:
-                    self.state = 21
+                if token in [CMakeParser.Identifier, CMakeParser.Unquoted_argument, CMakeParser.Quoted_argument, CMakeParser.Bracket_argument]:
+                    self.state = 35
                     self.single_argument()
                     pass
                 elif token in [CMakeParser.T__0]:
-                    self.state = 22
+                    self.state = 36
                     self.compound_argument()
                     pass
                 else:
                     raise NoViableAltException(self)
 
-                self.state = 27
+                self.state = 41
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
-            self.state = 28
+            self.state = 42
             self.match(CMakeParser.T__1)
         except RecognitionException as re:
             localctx.exception = re
@@ -235,56 +408,9 @@ def command_invocation(self):
             self.exitRule()
         return localctx
 
-    class Documented_commandContext(ParserRuleContext):
-
-        def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1):
-            super().__init__(parent, invokingState)
-            self.parser = parser
-
-        def Bracket_doccomment(self):
-            return self.getToken(CMakeParser.Bracket_doccomment, 0)
-
-        def command_invocation(self):
-            return self.getTypedRuleContext(CMakeParser.Command_invocationContext, 0)
-
-        def getRuleIndex(self):
-            return CMakeParser.RULE_documented_command
-
-        def enterRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "enterDocumented_command"):
-                listener.enterDocumented_command(self)
-
-        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)
-
-    def documented_command(self):
-
-        localctx = CMakeParser.Documented_commandContext(self, self._ctx, self.state)
-        self.enterRule(localctx, 4, self.RULE_documented_command)
-        try:
-            self.enterOuterAlt(localctx, 1)
-            self.state = 30
-            self.match(CMakeParser.Bracket_doccomment)
-            self.state = 31
-            self.command_invocation()
-        except RecognitionException as re:
-            localctx.exception = re
-            self._errHandler.reportError(self, re)
-            self._errHandler.recover(self, re)
-        finally:
-            self.exitRule()
-        return localctx
-
     class Single_argumentContext(ParserRuleContext):
 
-        def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1):
+        def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
 
@@ -303,32 +429,33 @@ def Quoted_argument(self):
         def getRuleIndex(self):
             return CMakeParser.RULE_single_argument
 
-        def enterRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "enterSingle_argument"):
+        def enterRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "enterSingle_argument" ):
                 listener.enterSingle_argument(self)
 
-        def exitRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "exitSingle_argument"):
+        def exitRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "exitSingle_argument" ):
                 listener.exitSingle_argument(self)
 
-        def accept(self, visitor: ParseTreeVisitor):
-            if hasattr(visitor, "visitSingle_argument"):
+        def accept(self, visitor:ParseTreeVisitor):
+            if hasattr( visitor, "visitSingle_argument" ):
                 return visitor.visitSingle_argument(self)
             else:
                 return visitor.visitChildren(self)
 
+
+
+
     def single_argument(self):
 
         localctx = CMakeParser.Single_argumentContext(self, self._ctx, self.state)
-        self.enterRule(localctx, 6, self.RULE_single_argument)
-        self._la = 0  # Token type
+        self.enterRule(localctx, 10, self.RULE_single_argument)
+        self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 33
+            self.state = 44
             _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)):
+            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)
             else:
                 self._errHandler.reportMatch(self)
@@ -343,74 +470,76 @@ def single_argument(self):
 
     class Compound_argumentContext(ParserRuleContext):
 
-        def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1):
+        def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
 
-        def single_argument(self, i: int = None):
+        def single_argument(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(CMakeParser.Single_argumentContext)
             else:
-                return self.getTypedRuleContext(CMakeParser.Single_argumentContext, i)
+                return self.getTypedRuleContext(CMakeParser.Single_argumentContext,i)
+
 
-        def compound_argument(self, i: int = None):
+        def compound_argument(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(CMakeParser.Compound_argumentContext)
             else:
-                return self.getTypedRuleContext(CMakeParser.Compound_argumentContext, i)
+                return self.getTypedRuleContext(CMakeParser.Compound_argumentContext,i)
+
 
         def getRuleIndex(self):
             return CMakeParser.RULE_compound_argument
 
-        def enterRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "enterCompound_argument"):
+        def enterRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "enterCompound_argument" ):
                 listener.enterCompound_argument(self)
 
-        def exitRule(self, listener: ParseTreeListener):
-            if hasattr(listener, "exitCompound_argument"):
+        def exitRule(self, listener:ParseTreeListener):
+            if hasattr( listener, "exitCompound_argument" ):
                 listener.exitCompound_argument(self)
 
-        def accept(self, visitor: ParseTreeVisitor):
-            if hasattr(visitor, "visitCompound_argument"):
+        def accept(self, visitor:ParseTreeVisitor):
+            if hasattr( visitor, "visitCompound_argument" ):
                 return visitor.visitCompound_argument(self)
             else:
                 return visitor.visitChildren(self)
 
+
+
+
     def compound_argument(self):
 
         localctx = CMakeParser.Compound_argumentContext(self, self._ctx, self.state)
-        self.enterRule(localctx, 8, self.RULE_compound_argument)
-        self._la = 0  # Token type
+        self.enterRule(localctx, 12, self.RULE_compound_argument)
+        self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 35
+            self.state = 46
             self.match(CMakeParser.T__0)
-            self.state = 40
+            self.state = 51
             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 = 38
+            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._errHandler.sync(self)
                 token = self._input.LA(1)
-                if token in [CMakeParser.Identifier, CMakeParser.Unquoted_argument, CMakeParser.Quoted_argument,
-                             CMakeParser.Bracket_argument]:
-                    self.state = 36
+                if token in [CMakeParser.Identifier, CMakeParser.Unquoted_argument, CMakeParser.Quoted_argument, CMakeParser.Bracket_argument]:
+                    self.state = 47
                     self.single_argument()
                     pass
                 elif token in [CMakeParser.T__0]:
-                    self.state = 37
+                    self.state = 48
                     self.compound_argument()
                     pass
                 else:
                     raise NoViableAltException(self)
 
-                self.state = 42
+                self.state = 53
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
-            self.state = 43
+            self.state = 54
             self.match(CMakeParser.T__1)
         except RecognitionException as re:
             localctx.exception = re
@@ -419,3 +548,8 @@ def compound_argument(self):
         finally:
             self.exitRule()
         return localctx
+
+
+
+
+
diff --git a/src/cminx/parser/CMakeVisitor.py b/src/cminx/parser/CMakeVisitor.py
index c9f3e99f..81ae3a4f 100644
--- a/src/cminx/parser/CMakeVisitor.py
+++ b/src/cminx/parser/CMakeVisitor.py
@@ -1,35 +1,48 @@
 # Generated from CMake.g4 by ANTLR 4.7.2
 from antlr4 import *
-
 if __name__ is not None and "." in __name__:
     from .CMakeParser import CMakeParser
 else:
     from CMakeParser import CMakeParser
 
-
 # This class defines a complete generic visitor for a parse tree produced by CMakeParser.
 
 class CMakeVisitor(ParseTreeVisitor):
 
     # Visit a parse tree produced by CMakeParser#cmake_file.
-    def visitCmake_file(self, ctx: CMakeParser.Cmake_fileContext):
+    def visitCmake_file(self, ctx:CMakeParser.Cmake_fileContext):
         return self.visitChildren(ctx)
 
-    # Visit a parse tree produced by CMakeParser#command_invocation.
-    def visitCommand_invocation(self, ctx: CMakeParser.Command_invocationContext):
-        return self.visitChildren(ctx)
 
     # Visit a parse tree produced by CMakeParser#documented_command.
-    def visitDocumented_command(self, ctx: CMakeParser.Documented_commandContext):
+    def visitDocumented_command(self, ctx:CMakeParser.Documented_commandContext):
         return self.visitChildren(ctx)
 
+
+    # Visit a parse tree produced by CMakeParser#documented_module.
+    def visitDocumented_module(self, ctx:CMakeParser.Documented_moduleContext):
+        return self.visitChildren(ctx)
+
+
+    # Visit a parse tree produced by CMakeParser#bracket_doccomment.
+    def visitBracket_doccomment(self, ctx:CMakeParser.Bracket_doccommentContext):
+        return self.visitChildren(ctx)
+
+
+    # Visit a parse tree produced by CMakeParser#command_invocation.
+    def visitCommand_invocation(self, ctx:CMakeParser.Command_invocationContext):
+        return self.visitChildren(ctx)
+
+
     # Visit a parse tree produced by CMakeParser#single_argument.
-    def visitSingle_argument(self, ctx: CMakeParser.Single_argumentContext):
+    def visitSingle_argument(self, ctx:CMakeParser.Single_argumentContext):
         return self.visitChildren(ctx)
 
+
     # Visit a parse tree produced by CMakeParser#compound_argument.
-    def visitCompound_argument(self, ctx: CMakeParser.Compound_argumentContext):
+    def visitCompound_argument(self, ctx:CMakeParser.Compound_argumentContext):
         return self.visitChildren(ctx)
 
 
-del CMakeParser
+
+del CMakeParser
\ No newline at end of file


Test patch:
diff --git a/tests/test_samples/advanced_module.cmake b/tests/test_samples/advanced_module.cmake
new file mode 100644
index 00000000..d1048e02
--- /dev/null
+++ b/tests/test_samples/advanced_module.cmake
@@ -0,0 +1,11 @@
+#[[[ @module advanced_module_name
+# This is a more advanced usage of a module
+# doc comment. A call to `function()` immediately
+# follows this comment but because of the `@module` directive
+# this comment is not considered a doccomment on the `function()`
+# call. This file also sets the module name to `advanced_module_name`
+# instead of allowing it to be autogenerated.
+#]]
+function(test_func param1)
+
+endfunction()
\ No newline at end of file
diff --git a/tests/test_samples/basic_module.cmake b/tests/test_samples/basic_module.cmake
new file mode 100644
index 00000000..d4a26c6e
--- /dev/null
+++ b/tests/test_samples/basic_module.cmake
@@ -0,0 +1,9 @@
+#[[[ @module
+# This is a basic CMake module.
+# The :code:`@module` component is required and
+# must appear on the first line. An
+# optional module name may appear after the
+# module tag, if it is not present the module
+# name is autogenerated as usual. This
+# file allows the module name to be autogenerated.
+#]]
diff --git a/tests/test_samples/corr_rst/advanced_module.rst b/tests/test_samples/corr_rst/advanced_module.rst
new file mode 100644
index 00000000..913b970e
--- /dev/null
+++ b/tests/test_samples/corr_rst/advanced_module.rst
@@ -0,0 +1,20 @@
+
+####################
+advanced_module_name
+####################
+
+.. module:: advanced_module_name
+
+   This is a more advanced usage of a module
+   doc comment. A call to `function()` immediately
+   follows this comment but because of the `@module` directive
+   this comment is not considered a doccomment on the `function()`
+   call. This file also sets the module name to `advanced_module_name`
+   instead of allowing it to be autogenerated.
+   
+
+
+.. function:: test_func(param1)
+
+   
+
diff --git a/tests/test_samples/corr_rst/basic_module.rst b/tests/test_samples/corr_rst/basic_module.rst
new file mode 100644
index 00000000..e84ff2a2
--- /dev/null
+++ b/tests/test_samples/corr_rst/basic_module.rst
@@ -0,0 +1,16 @@
+
+#########################
+test_samples.basic_module
+#########################
+
+.. module:: test_samples.basic_module
+
+   This is a basic CMake module.
+   The :code:`@module` component is required and
+   must appear on the first line. An
+   optional module name may appear after the
+   module tag, if it is not present the module
+   name is autogenerated as usual. This
+   file allows the module name to be autogenerated.
+   
+
diff --git a/tests/unit_tests/test_documenter.py b/tests/unit_tests/test_documenter.py
index d2ab62e0..f8c403f4 100755
--- a/tests/unit_tests/test_documenter.py
+++ b/tests/unit_tests/test_documenter.py
@@ -19,7 +19,8 @@
 
 from cminx.documenter import Documenter
 from cminx.documentation_types import FunctionDocumentation, MacroDocumentation, VariableDocumentation, \
-    GenericCommandDocumentation, ClassDocumentation, TestDocumentation, SectionDocumentation, CTestDocumentation
+    GenericCommandDocumentation, ClassDocumentation, TestDocumentation, SectionDocumentation, CTestDocumentation, \
+    ModuleDocumentation
 from cminx.rstwriter import Directive, RSTWriter
 
 
@@ -34,13 +35,15 @@ def reset(self):
 
     def test_process(self):
         self.documenter.process()  # Convert all documentation into RST
-        self.assertEqual(len(self.documenter.aggregator.documented), len(self.documenter.writer.document) - 2,
+        self.assertEqual(len(self.documenter.aggregator.documented), len(self.documenter.writer.document) - 1,
                          "Generated RST has different length from input documentation")  # RSTWriter adds one element
         # for document heading, documenter adds another for module definition
         for i in range(0, len(self.documenter.aggregator.documented)):
             doc = self.documenter.aggregator.documented[i]
-            element = self.documenter.writer.document[i + 2]
-            if isinstance(doc, FunctionDocumentation):
+            element = self.documenter.writer.document[i + 1]
+            if isinstance(doc, ModuleDocumentation):
+                self.assertIsInstance(element, Directive, "Wrong RST element generated for module doc")
+            elif isinstance(doc, FunctionDocumentation):
                 self.assertIsInstance(element, Directive, "Wrong RST element generated for function")
                 self.assertEqual("function", element.document[0].title, "Wrong directive type for function")
             elif isinstance(doc, MacroDocumentation):
diff --git a/tests/unit_tests/test_lexer.py b/tests/unit_tests/test_lexer.py
index 86e8948d..472527ae 100644
--- a/tests/unit_tests/test_lexer.py
+++ b/tests/unit_tests/test_lexer.py
@@ -47,7 +47,7 @@ def test_block_doccomment(self):
         doc = '#[[[\nThis is a doccomment\n#]]'
         self.stream = InputStream(doc)
         self.reset()
-        self.assertListEqual([token.type for token in self.tokens], [CMakeLexer.Bracket_doccomment])
+        self.assertListEqual([token.type for token in self.tokens], [CMakeLexer.Docstring])
         self.assertEqual(self.tokens[0].text, doc)
 
     def test_identifier(self):