diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py
index 76e2907361d054f7468279e2500469b408f0d477..08422bebbb685bad1220be9412a7ecff4491f8c9 100644
--- a/src/pystencilssfg/composer/basic_composer.py
+++ b/src/pystencilssfg/composer/basic_composer.py
@@ -28,7 +28,7 @@ from ..ir.postprocessing import (
     SfgDeferredFieldMapping,
     SfgDeferredVectorMapping,
 )
-from ..ir.source_components import (
+from ..ir import (
     SfgFunction,
     SfgKernelNamespace,
     SfgKernelHandle,
diff --git a/src/pystencilssfg/composer/class_composer.py b/src/pystencilssfg/composer/class_composer.py
index fa7d6f2583fd0de6539a67af13b67d05ae1029a1..3ed5c0fe0a432d3fbd8e2e7729a6d0f06a58104f 100644
--- a/src/pystencilssfg/composer/class_composer.py
+++ b/src/pystencilssfg/composer/class_composer.py
@@ -13,8 +13,8 @@ from ..lang import (
     SfgVar,
 )
 
-from ..ir.call_tree import SfgCallTreeNode
-from ..ir.source_components import (
+from ..ir import (
+    SfgCallTreeNode,
     SfgClass,
     SfgConstructor,
     SfgMethod,
diff --git a/src/pystencilssfg/context.py b/src/pystencilssfg/context.py
index 48d0720c9927542bdca291c206f28f55f9172437..a129f982e36344410d5fdd58ae5501e112210bc0 100644
--- a/src/pystencilssfg/context.py
+++ b/src/pystencilssfg/context.py
@@ -2,14 +2,14 @@ from __future__ import annotations
 from typing import Sequence, Any, Generator
 
 from .config import CodeStyle
-from .ir.source_components import (
+from .ir import (
     SfgSourceFile,
     SfgNamespace,
     SfgNamespaceBlock,
-    SfgNamespaceElement,
     SfgCodeEntity,
     SfgGlobalNamespace,
 )
+from .ir.syntax import SfgNamespaceElement
 from .exceptions import SfgException
 
 
diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py
index 88dbc9be2e215b1fdce5833ef18eac6eab336d74..a628f00802b7863eb2f67a9a84598e4aa1be4e01 100644
--- a/src/pystencilssfg/extensions/sycl.py
+++ b/src/pystencilssfg/extensions/sycl.py
@@ -17,8 +17,8 @@ from ..composer import (
     SfgComposerMixIn,
     make_sequence,
 )
-from ..ir.source_components import SfgKernelHandle
 from ..ir import (
+    SfgKernelHandle,
     SfgCallTreeNode,
     SfgCallTreeLeaf,
     SfgKernelCallNode,
diff --git a/src/pystencilssfg/ir/__init__.py b/src/pystencilssfg/ir/__init__.py
index f1760b7c9b9ecdaa4821adf29fbc9f2129e0bd46..8f03fed0d4c2467377cdaab6cf100a13f7ded9fb 100644
--- a/src/pystencilssfg/ir/__init__.py
+++ b/src/pystencilssfg/ir/__init__.py
@@ -14,20 +14,32 @@ from .call_tree import (
     SfgSwitch,
 )
 
-from .source_components import (
+from .entities import (
+    SfgCodeEntity,
+    SfgNamespace,
+    SfgGlobalNamespace,
     SfgKernelNamespace,
     SfgKernelHandle,
-    SfgKernelParamVar,
     SfgFunction,
     SfgVisibility,
     SfgClassKeyword,
     SfgClassMember,
-    SfgVisibilityBlock,
     SfgMemberVariable,
     SfgMethod,
     SfgConstructor,
     SfgClass,
 )
+
+from .syntax import (
+    SfgEntityDecl,
+    SfgEntityDef,
+    SfgVisibilityBlock,
+    SfgNamespaceBlock,
+    SfgClassBody,
+    SfgSourceFileType,
+    SfgSourceFile,
+)
+
 from .analysis import collect_includes
 
 __all__ = [
@@ -44,17 +56,25 @@ __all__ = [
     "SfgBranch",
     "SfgSwitchCase",
     "SfgSwitch",
+    "SfgCodeEntity",
+    "SfgNamespace",
+    "SfgGlobalNamespace",
     "SfgKernelNamespace",
     "SfgKernelHandle",
-    "SfgKernelParamVar",
     "SfgFunction",
     "SfgVisibility",
     "SfgClassKeyword",
     "SfgClassMember",
-    "SfgVisibilityBlock",
     "SfgMemberVariable",
     "SfgMethod",
     "SfgConstructor",
     "SfgClass",
-    "collect_includes"
+    "SfgEntityDecl",
+    "SfgEntityDef",
+    "SfgVisibilityBlock",
+    "SfgNamespaceBlock",
+    "SfgClassBody",
+    "SfgSourceFileType",
+    "SfgSourceFile",
+    "collect_includes",
 ]
diff --git a/src/pystencilssfg/ir/analysis.py b/src/pystencilssfg/ir/analysis.py
index c550975cd0b5750f980f52145cbd2ee7e5a3f93a..c2c3e34675585568115d1a4a06f0867eb9ca563c 100644
--- a/src/pystencilssfg/ir/analysis.py
+++ b/src/pystencilssfg/ir/analysis.py
@@ -10,7 +10,7 @@ from ..lang import HeaderFile, includes
 def collect_includes(obj: Any) -> set[HeaderFile]:
     from ..context import SfgContext
     from .call_tree import SfgCallTreeNode
-    from .source_components import (
+    from .entities import (
         SfgFunction,
         SfgClass,
         SfgConstructor,
diff --git a/src/pystencilssfg/ir/call_tree.py b/src/pystencilssfg/ir/call_tree.py
index 9a29f2f0715d18878f7a84926504c51ebf78c213..2e057dd1b2864bfaf2845501cccf3ff10673640c 100644
--- a/src/pystencilssfg/ir/call_tree.py
+++ b/src/pystencilssfg/ir/call_tree.py
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Sequence, Iterable, NewType
 
 from abc import ABC, abstractmethod
 
-from .source_components import SfgKernelHandle
+from .entities import SfgKernelHandle
 from ..lang import SfgVar, HeaderFile
 
 if TYPE_CHECKING:
diff --git a/src/pystencilssfg/ir/source_components.py b/src/pystencilssfg/ir/entities.py
similarity index 68%
rename from src/pystencilssfg/ir/source_components.py
rename to src/pystencilssfg/ir/entities.py
index b4d8aa77cbf769fb465460a54ace5755168a6d3e..9d76b39fbf68afe34f62f150aa32b4557be0a840 100644
--- a/src/pystencilssfg/ir/source_components.py
+++ b/src/pystencilssfg/ir/entities.py
@@ -6,9 +6,6 @@ from typing import (
     TYPE_CHECKING,
     Sequence,
     Generator,
-    Iterable,
-    TypeVar,
-    Generic,
 )
 from dataclasses import replace
 from itertools import chain
@@ -17,7 +14,7 @@ from pystencils import CreateKernelConfig, create_kernel, Field
 from pystencils.codegen import Kernel
 from pystencils.types import PsType, PsCustomType
 
-from ..lang import SfgVar, SfgKernelParamVar, HeaderFile, void
+from ..lang import SfgVar, SfgKernelParamVar, void
 from ..exceptions import SfgException
 
 if TYPE_CHECKING:
@@ -423,9 +420,6 @@ class SfgClass(SfgCodeEntity):
         self._class_keyword = class_keyword
         self._bases_classes = tuple(bases)
 
-        self._default_block = SfgVisibilityBlock(SfgVisibility.DEFAULT)
-        self._blocks = [self._default_block]
-
         self._constructors: list[SfgConstructor] = []
         self._methods: list[SfgMethod] = []
         self._member_vars: dict[str, SfgMemberVariable] = dict()
@@ -443,30 +437,15 @@ class SfgClass(SfgCodeEntity):
     def class_keyword(self) -> SfgClassKeyword:
         return self._class_keyword
 
-    @property
-    def default(self) -> SfgVisibilityBlock:
-        return self._default_block
-
-    def append_visibility_block(self, block: SfgVisibilityBlock):
-        if block.visibility == SfgVisibility.DEFAULT:
-            raise SfgException(
-                "Can't add another block with DEFAULT visibility to a class. Use `.default` instead."
-            )
-        self._blocks.append(block)
-
-    def visibility_blocks(self) -> tuple[SfgVisibilityBlock, ...]:
-        return tuple(self._blocks)
-
     def members(
         self, visibility: SfgVisibility | None = None
     ) -> Generator[SfgClassMember, None, None]:
         if visibility is None:
-            yield from chain.from_iterable(b.members() for b in self._blocks)
-        else:
-            yield from chain.from_iterable(
-                b.members()
-                for b in filter(lambda b: b.visibility == visibility, self._blocks)
+            yield from chain(
+                self._constructors, self._methods, self._member_vars.values()
             )
+        else:
+            yield from filter(lambda m: m.visibility == visibility, self.members())
 
     def member_variables(
         self, visibility: SfgVisibility | None = None
@@ -511,186 +490,3 @@ class SfgClass(SfgCodeEntity):
             )
 
         self._member_vars[variable.name] = variable
-
-
-#   =========================================================================================================
-#
-#   SYNTACTICAL ELEMENTS
-#
-#   These classes model *code elements*, which represent the actual syntax objects that populate the output
-#   files, their namespaces and class bodies.
-#
-#   =========================================================================================================
-
-
-SourceEntity_T = TypeVar(
-    "SourceEntity_T", bound=SfgKernelHandle | SfgFunction | SfgClassMember | SfgClass, covariant=True
-)
-"""Source entities that may have declarations and definitions."""
-
-
-class SfgEntityDecl(Generic[SourceEntity_T]):
-    """Declaration of a function, class, method, or constructor"""
-
-    __match_args__ = ("entity",)
-
-    def __init__(self, entity: SourceEntity_T) -> None:
-        self._entity = entity
-
-    @property
-    def entity(self) -> SourceEntity_T:
-        return self._entity
-
-
-class SfgEntityDef(Generic[SourceEntity_T]):
-    """Definition of a function, class, method, or constructor"""
-
-    __match_args__ = ("entity",)
-
-    def __init__(self, entity: SourceEntity_T) -> None:
-        self._entity = entity
-
-    @property
-    def entity(self) -> SourceEntity_T:
-        return self._entity
-
-
-SfgClassBodyElement = (
-    str
-    | SfgEntityDecl[SfgClassMember]
-    | SfgEntityDef[SfgClassMember]
-    | SfgMemberVariable
-)
-"""Elements that may be placed in the visibility blocks of a class body."""
-
-
-class SfgVisibilityBlock:
-    """Visibility-qualified block inside a class definition body.
-
-    Visibility blocks host the code elements placed inside a class body:
-    method and constructor declarations,
-    in-class method and constructor definitions,
-    as well as variable declarations and definitions.
-
-    Args:
-        visibility: The visibility qualifier of this block
-    """
-
-    def __init__(self, visibility: SfgVisibility) -> None:
-        self._vis = visibility
-        self._elements: list[SfgClassBodyElement] = []
-        self._cls: SfgClass | None = None
-
-    @property
-    def visibility(self) -> SfgVisibility:
-        return self._vis
-
-    @property
-    def elements(self) -> list[SfgClassBodyElement]:
-        return self._elements
-
-    @elements.setter
-    def elements(self, elems: Iterable[SfgClassBodyElement]):
-        self._elements = list(elems)
-
-    def members(self) -> Generator[SfgClassMember, None, None]:
-        for elem in self._elements:
-            match elem:
-                case SfgEntityDecl(entity) | SfgEntityDef(entity):
-                    yield entity
-                case SfgMemberVariable():
-                    yield elem
-
-
-class SfgNamespaceBlock:
-    """A C++ namespace.
-
-    Each namespace has a `name` and a `parent`; its fully qualified name is given as
-    ``<parent.name>::<name>``.
-
-    Args:
-        name: Local name of this namespace
-        parent: Parent namespace enclosing this namespace
-    """
-
-    def __init__(self, namespace: SfgNamespace) -> None:
-        self._namespace = namespace
-        self._elements: list[SfgNamespaceElement] = []
-
-    @property
-    def namespace(self) -> SfgNamespace:
-        return self._namespace
-
-    @property
-    def elements(self) -> list[SfgNamespaceElement]:
-        """Sequence of source elements that make up the body of this namespace"""
-        return self._elements
-
-    @elements.setter
-    def elements(self, elems: Iterable[SfgNamespaceElement]):
-        self._elements = list(elems)
-
-
-SfgNamespaceElement = str | SfgNamespaceBlock | SfgEntityDecl | SfgEntityDef
-"""Elements that may be placed inside a namespace, including the global namespace."""
-
-
-class SfgSourceFileType(Enum):
-    HEADER = auto()
-    TRANSLATION_UNIT = auto()
-
-
-class SfgSourceFile:
-    """A C++ source file.
-
-    Args:
-        name: Name of the file (without parent directories), e.g. ``Algorithms.cpp``
-        file_type: Type of the source file (header or translation unit)
-        prelude: Optionally, text of the prelude comment printed at the top of the file
-    """
-
-    def __init__(
-        self, name: str, file_type: SfgSourceFileType, prelude: str | None = None
-    ) -> None:
-        self._name: str = name
-        self._file_type: SfgSourceFileType = file_type
-        self._prelude: str | None = prelude
-        self._includes: list[HeaderFile] = []
-        self._elements: list[SfgNamespaceElement] = []
-
-    @property
-    def name(self) -> str:
-        """Name of this source file"""
-        return self._name
-
-    @property
-    def file_type(self) -> SfgSourceFileType:
-        """File type of this source file"""
-        return self._file_type
-
-    @property
-    def prelude(self) -> str | None:
-        """Text of the prelude comment"""
-        return self._prelude
-
-    @prelude.setter
-    def prelude(self, text: str | None):
-        self._prelude = text
-
-    @property
-    def includes(self) -> list[HeaderFile]:
-        """Sequence of header files to be included at the top of this file"""
-        return self._includes
-
-    @includes.setter
-    def includes(self, incl: Iterable[HeaderFile]):
-        self._includes = list(incl)
-
-    @property
-    def elements(self) -> list[SfgNamespaceElement]:
-        """Sequence of source elements comprising the body of this file"""
-        return self._elements
-
-    @elements.setter
-    def elements(self, elems: Iterable[SfgNamespaceElement]):
-        self._elements = list(elems)
diff --git a/src/pystencilssfg/ir/postprocessing.py b/src/pystencilssfg/ir/postprocessing.py
index db26a38ba2ffd189edb91156e6f0dc95cf29c616..ca6d9f21b8d91230053b4d06a93698d357c17e5e 100644
--- a/src/pystencilssfg/ir/postprocessing.py
+++ b/src/pystencilssfg/ir/postprocessing.py
@@ -15,7 +15,7 @@ from pystencils.codegen.properties import FieldBasePtr, FieldShape, FieldStride
 from ..exceptions import SfgException
 
 from .call_tree import SfgCallTreeNode, SfgCallTreeLeaf, SfgSequence, SfgStatements
-from ..ir.source_components import SfgKernelParamVar
+from ..lang.expressions import SfgKernelParamVar
 from ..lang import (
     SfgVar,
     IFieldExtraction,
diff --git a/src/pystencilssfg/ir/syntax.py b/src/pystencilssfg/ir/syntax.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e924a032fe8c33d756a59fc62f9b5e6a2fdc732
--- /dev/null
+++ b/src/pystencilssfg/ir/syntax.py
@@ -0,0 +1,228 @@
+from __future__ import annotations
+
+from enum import Enum, auto
+from typing import (
+    Generator,
+    Iterable,
+    TypeVar,
+    Generic,
+)
+
+from ..lang import HeaderFile
+
+from .entities import (
+    SfgNamespace,
+    SfgKernelHandle,
+    SfgFunction,
+    SfgClassMember,
+    SfgMemberVariable,
+    SfgVisibility,
+    SfgClass,
+)
+
+#   =========================================================================================================
+#
+#   SYNTACTICAL ELEMENTS
+#
+#   These classes model *code elements*, which represent the actual syntax objects that populate the output
+#   files, their namespaces and class bodies.
+#
+#   =========================================================================================================
+
+
+SourceEntity_T = TypeVar(
+    "SourceEntity_T",
+    bound=SfgKernelHandle | SfgFunction | SfgClassMember | SfgClass,
+    covariant=True,
+)
+"""Source entities that may have declarations and definitions."""
+
+
+class SfgEntityDecl(Generic[SourceEntity_T]):
+    """Declaration of a function, class, method, or constructor"""
+
+    __match_args__ = ("entity",)
+
+    def __init__(self, entity: SourceEntity_T) -> None:
+        self._entity = entity
+
+    @property
+    def entity(self) -> SourceEntity_T:
+        return self._entity
+
+
+class SfgEntityDef(Generic[SourceEntity_T]):
+    """Definition of a function, class, method, or constructor"""
+
+    __match_args__ = ("entity",)
+
+    def __init__(self, entity: SourceEntity_T) -> None:
+        self._entity = entity
+
+    @property
+    def entity(self) -> SourceEntity_T:
+        return self._entity
+
+
+SfgClassBodyElement = (
+    str
+    | SfgEntityDecl[SfgClassMember]
+    | SfgEntityDef[SfgClassMember]
+    | SfgMemberVariable
+)
+"""Elements that may be placed in the visibility blocks of a class body."""
+
+
+class SfgVisibilityBlock:
+    """Visibility-qualified block inside a class definition body.
+
+    Visibility blocks host the code elements placed inside a class body:
+    method and constructor declarations,
+    in-class method and constructor definitions,
+    as well as variable declarations and definitions.
+
+    Args:
+        visibility: The visibility qualifier of this block
+    """
+
+    def __init__(self, visibility: SfgVisibility) -> None:
+        self._vis = visibility
+        self._elements: list[SfgClassBodyElement] = []
+        self._cls: SfgClass | None = None
+
+    @property
+    def visibility(self) -> SfgVisibility:
+        return self._vis
+
+    @property
+    def elements(self) -> list[SfgClassBodyElement]:
+        return self._elements
+
+    @elements.setter
+    def elements(self, elems: Iterable[SfgClassBodyElement]):
+        self._elements = list(elems)
+
+    def members(self) -> Generator[SfgClassMember, None, None]:
+        for elem in self._elements:
+            match elem:
+                case SfgEntityDecl(entity) | SfgEntityDef(entity):
+                    yield entity
+                case SfgMemberVariable():
+                    yield elem
+
+
+class SfgNamespaceBlock:
+    """A C++ namespace.
+
+    Each namespace has a `name` and a `parent`; its fully qualified name is given as
+    ``<parent.name>::<name>``.
+
+    Args:
+        name: Local name of this namespace
+        parent: Parent namespace enclosing this namespace
+    """
+
+    def __init__(self, namespace: SfgNamespace) -> None:
+        self._namespace = namespace
+        self._elements: list[SfgNamespaceElement] = []
+
+    @property
+    def namespace(self) -> SfgNamespace:
+        return self._namespace
+
+    @property
+    def elements(self) -> list[SfgNamespaceElement]:
+        """Sequence of source elements that make up the body of this namespace"""
+        return self._elements
+
+    @elements.setter
+    def elements(self, elems: Iterable[SfgNamespaceElement]):
+        self._elements = list(elems)
+
+
+class SfgClassBody:
+    """Body of a class definition."""
+
+    def __init__(self, cls: SfgClass) -> None:
+        self._cls = cls
+        self._default_block = SfgVisibilityBlock(SfgVisibility.DEFAULT)
+        self._blocks = [self._default_block]
+
+    @property
+    def default(self) -> SfgVisibilityBlock:
+        return self._default_block
+
+    def append_visibility_block(self, block: SfgVisibilityBlock):
+        if block.visibility == SfgVisibility.DEFAULT:
+            raise ValueError(
+                "Can't add another block with DEFAULT visibility to this class body."
+            )
+        self._blocks.append(block)
+
+    def visibility_blocks(self) -> tuple[SfgVisibilityBlock, ...]:
+        return tuple(self._blocks)
+
+
+SfgNamespaceElement = str | SfgNamespaceBlock | SfgEntityDecl | SfgEntityDef
+"""Elements that may be placed inside a namespace, including the global namespace."""
+
+
+class SfgSourceFileType(Enum):
+    HEADER = auto()
+    TRANSLATION_UNIT = auto()
+
+
+class SfgSourceFile:
+    """A C++ source file.
+
+    Args:
+        name: Name of the file (without parent directories), e.g. ``Algorithms.cpp``
+        file_type: Type of the source file (header or translation unit)
+        prelude: Optionally, text of the prelude comment printed at the top of the file
+    """
+
+    def __init__(
+        self, name: str, file_type: SfgSourceFileType, prelude: str | None = None
+    ) -> None:
+        self._name: str = name
+        self._file_type: SfgSourceFileType = file_type
+        self._prelude: str | None = prelude
+        self._includes: list[HeaderFile] = []
+        self._elements: list[SfgNamespaceElement] = []
+
+    @property
+    def name(self) -> str:
+        """Name of this source file"""
+        return self._name
+
+    @property
+    def file_type(self) -> SfgSourceFileType:
+        """File type of this source file"""
+        return self._file_type
+
+    @property
+    def prelude(self) -> str | None:
+        """Text of the prelude comment"""
+        return self._prelude
+
+    @prelude.setter
+    def prelude(self, text: str | None):
+        self._prelude = text
+
+    @property
+    def includes(self) -> list[HeaderFile]:
+        """Sequence of header files to be included at the top of this file"""
+        return self._includes
+
+    @includes.setter
+    def includes(self, incl: Iterable[HeaderFile]):
+        self._includes = list(incl)
+
+    @property
+    def elements(self) -> list[SfgNamespaceElement]:
+        """Sequence of source elements comprising the body of this file"""
+        return self._elements
+
+    @elements.setter
+    def elements(self, elems: Iterable[SfgNamespaceElement]):
+        self._elements = list(elems)