Repository: QuTech-Delft/libqasm (language: python)

Issue:
Release 0.6.8
## [ 0.6.8 ] - [ 2024-10-16 ]

### Added
- Gate modifiers: `inv`, `pow`, and `ctrl`.
- C++ linters GitHub Actions job (just running clang-format at the moment).

### Changed
- Fix bug: empty lists are written out to JSON as `"[]"` instead of `[]`.
- Fix bug: CRk and CZ parameter types were interchanged in the instruction set.
- More descriptive and consistent (snake case) file names.
- Minor fixes and tweaks to documentation.

### Removed


Discussion / hints:

Fix patch:
diff --git a/.clang-format b/.clang-format
index f461941c8..5c76ce643 100644
--- a/.clang-format
+++ b/.clang-format
@@ -17,15 +17,22 @@ AllowShortLoopsOnASingleLine: true
 AlwaysBreakTemplateDeclarations: Yes
 BinPackArguments: false
 BreakBeforeBraces: Attach
+# Use BraceWrapping with custom BreakBeforeBraces
+# BreakBeforeBraces: Custom
+# BraceWrapping:
+BreakBeforeTernaryOperators: true
 BreakConstructorInitializers: BeforeComma
 ColumnLimit: 120
 ConstructorInitializerIndentWidth: 0
 Cpp11BracedListStyle: false
 DerivePointerAlignment: false
+IndentCaseLabels: true
 IndentWidth: 4
-LambdaBodyIndentation: OuterScope
+LambdaBodyIndentation: Signature
 PackConstructorInitializers: Never
-PointerAlignment: Right
-ReflowComments: false
+PointerAlignment: Left
+ReferenceAlignment: Left
+ReflowComments: true
 SpacesBeforeTrailingComments: 2
+Standard: c++20
 ---
diff --git a/.gitattributes b/.gitattributes
index 3812f8483..6976fbd74 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -7,4 +7,4 @@
 # (see read_file and write_file implementation in utils.{hpp,cpp})
 # Since we are comparing the actual test files against "golden test files" (e.g. ast.golden.txt),
 # we also want the golden test files to use LF line endings.
-res/v3x/parsing/*/*/*.* text eol=lf
+res/v3x/tests/integration/*/*/*.* text eol=lf
diff --git a/.github/workflows/assets.yml b/.github/workflows/assets.yaml
similarity index 98%
rename from .github/workflows/assets.yml
rename to .github/workflows/assets.yaml
index 43cbd132e..54379b48a 100644
--- a/.github/workflows/assets.yml
+++ b/.github/workflows/assets.yaml
@@ -104,8 +104,8 @@ jobs:
             build/Release/emscripten/cqasm_emscripten.wasm
           token: ${{ secrets.GITHUB_TOKEN }}
 
-  publish:
-    name: Publish
+  publish-python-packages:
+    name: Publish Python packages
     needs:
       - cibw-wheels
       - cibw-wheels-linux-arm64
diff --git a/.github/workflows/doc.yaml b/.github/workflows/doc.yaml
new file mode 100644
index 000000000..5f4d0aa61
--- /dev/null
+++ b/.github/workflows/doc.yaml
@@ -0,0 +1,50 @@
+name: Doc
+
+on:
+  pull_request:
+  push:
+    branches:
+      - develop
+      - master
+  release:
+    types:
+      - created
+
+jobs:
+  publish-github-pages:
+    name: Publish GitHub pages
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+      - name: Install dependencies
+        run: |
+          sudo apt-get install -y doxygen
+      - uses: actions/setup-python@v5
+        with:
+          python-version: "3.11"
+      - name: Install Python dependencies
+        run: |
+          python -m pip install --upgrade pip -r requirements.txt
+      - name: Configure Git
+        run: |
+          git config --global user.name "${{ github.actor }}"
+          git config --global user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
+          git fetch origin gh-pages:gh-pages
+      - name: Add mkdocs custom handlers folder to env
+        run: |
+          echo "PYTHONPATH=./scripts/python" >> $GITHUB_ENV
+      - name: Dry run
+        if: github.event_name == 'pull_request'
+        run: |          
+          mkdocs build
+      - name: Upload and tag as latest with every push to develop branch
+        if: github.ref == 'refs/heads/develop'
+        run: |
+          mike deploy --push latest
+          mike set-default --push latest
+      - name: Upload and tag as git tag with every release creation on master branch
+        if: github.event_name == 'release' && github.event.action == 'created'
+        run: |
+          mike deploy --push ${{ github.ref }}
+          mike set-default --push latest
diff --git a/.github/workflows/linters.yaml b/.github/workflows/linters.yaml
new file mode 100644
index 000000000..cd3bc7cd6
--- /dev/null
+++ b/.github/workflows/linters.yaml
@@ -0,0 +1,34 @@
+name: Linters
+
+on:
+  push:
+    branches:
+      - develop
+  pull_request:
+    branches:
+      - develop
+
+jobs:
+  cpp-linters:
+    name: "C++ linters"
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+      - name: Run C++ linters
+        uses: cpp-linter/cpp-linter-action@v2
+        id: linter
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          extensions: 'cpp,hpp'
+          files-changed-only: 'true'
+          format-review: 'true'
+          style: 'file'  # use .clang-format config file
+          tidy-checks: '-*'  # disable clang-tidy checks
+          version: 18
+      - name: Fail fast
+        if: steps.linter.outputs.clang-format-checks-failed > 0
+        run: |
+          echo "::notice::Try executing 'python3 ./scripts/run_cpp_linters.py .' to fix linter issues."
+          exit 1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d96f72003..530261be3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 
 
+## [ 0.6.8 ] - [ 2024-10-16 ]
+
+### Added
+- Gate modifiers: `inv`, `pow`, and `ctrl`.
+- C++ linters GitHub Actions job (just running clang-format at the moment).
+
+### Changed
+- Fix bug: empty lists are written out to JSON as `"[]"` instead of `[]`.
+- Fix bug: CRk and CZ parameter types were interchanged in the instruction set.
+- More descriptive and consistent (snake case) file names. 
+- Minor fixes and tweaks to documentation.
+
+### Removed
+
+
 ## [ 0.6.7 ] - [ 2024-05-30 ]
 
 ### Added
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 088709d14..9c5764722 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -43,12 +43,6 @@ option(LIBQASM_BUILD_PYTHON
 )
 mark_as_advanced(LIBQASM_BUILD_PYTHON)
 
-# Whether doc should be built.
-option(LIBQASM_BUILD_DOCS
-    "whether the doc should be built and added to `make doc`"
-    OFF
-)
-
 # Where the 'libqasm' Python module should be built.
 set(LIBQASM_PYTHON_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/python/libqasm"
     CACHE STRING "Where to install the libqasm Python module"
@@ -116,9 +110,4 @@ if(LIBQASM_BUILD_EMSCRIPTEN)
     add_subdirectory(emscripten)
 endif()
 
-# Add the doc directory.
-if(LIBQASM_BUILD_DOCS)
-    add_subdirectory(doc)
-endif()
-
 endif()  # NOT TARGET cqasm
diff --git a/README.md b/README.md
index cf00a8521..89e6ac846 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,9 @@
 ![OS](https://img.shields.io/badge/os-emscripten%20%7C%20linux%20%7C%20macos%20%7C%20windows-blue?style=flat-square)
 [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
 
-libQASM is a library to parse programs written in the cQASM quantum programming language, developed by QuTech. 
-At the moment, libQASM only supports cQASM v3.0 programs.
+libQASM is a library to parse cQASM programs, developed by QuTech. 
+At the moment, libQASM only supports cQASM v3.0 programs
+(see [cQASM-spec](https://qutech-delft.github.io/cQASM-spec/latest/) for the language specification).
 
 It performs lexical, syntactic, and semantic analysis of an input program received via a file or a string.
 It produces one of the following results:
@@ -16,7 +17,7 @@ It produces one of the following results:
 - A list of parsing or analysing errors. In case the input program was malformed.
 - A JSON representation of either the AST or the list of errors.
 
-It can be used within:
+It can be used from:
 
 - C++ projects (as a [Conan package](https://conan.io/center/recipes/libqasm)).
 - Python projects (as a [Python package](https://pypi.org/project/libqasm/)).
@@ -46,7 +47,7 @@ We can parse or analyze this circuit, using libQASM through the following progra
 ### C++
 
 ```cpp
-#include "v3/cqasm-py.hpp"
+#include "libqasm/v3x/cqasm-python.hpp"
 
 int main() {
     auto parse_result = V3xAnalyzer::parse_file("example.cq");
diff --git a/RELEASE.md b/RELEASE.md
deleted file mode 100644
index 502a9f454..000000000
--- a/RELEASE.md
+++ /dev/null
@@ -1,39 +0,0 @@
-Release procedure
-=================
-
-Most of the release process is managed by GitHub Actions. If you follow the procedure below,
-it will automatically test and build all wheels for all platforms,
-and publish them to PyPI assuming the secrets are configured correctly.
-
- - Make a branch with a name starting with "release" based upon the commit that is to be released.
-   For example, `release-0.0.1`, but the suffix doesn't matter.
-
- - Change the version in `include/version.hpp` (this is the only place where the version is hardcoded)
-   and change any other files where applicable (`CHANGELOG.md`, etc.), then commit and make a PR for it.
-
- - CI will now run not only the test workflow, but also the assets workflow.
-   The assets workflow builds the wheels, but publishes them to the GitHub Actions build artifacts only.
-   You can then test these yourself if you have reason to believe that
-   something might be wrong with them that CI might not catch.
-   To find them, go to Actions -> Assets workflow -> click the run for your branch -> Artifacts.
-
- - If the test or assets workflows fail, fix it before merging the PR (of course).
-
- - Once CI is green, merge the PR into `develop` if there are any changes.
-   If no changes were needed, just delete the branch to clean up.
-
- - Create and push a new tag using the same version you put in `include/version.hpp`.
-   For example:
-
-   ```
-   git tag 0.0.1
-   git push origin 0.0.1
-   ```
-
- - Draft a new release through the GitHub interface.
-   Set the "tag version", and the title to "Release `version`: `name`".
-   And write a short change log in the body.
-
- - CI will run the assets workflow again, now with the new version string baked into the wheels.
-   When done, these wheels are automatically added to the GitHub release,
-   and if the secrets/API keys for PyPI are correct, CI will publish them there.
diff --git a/conan/profiles/debug-gcc-linux-x64-shared b/conan/profiles/debug-gcc-linux-x64-shared
new file mode 100644
index 000000000..f7b185ced
--- /dev/null
+++ b/conan/profiles/debug-gcc-linux-x64-shared
@@ -0,0 +1,4 @@
+include(debug)
+
+[options]
+*/*:shared=True
diff --git a/conan/profiles/docs-release b/conan/profiles/docs-release
deleted file mode 100644
index a6bd58a80..000000000
--- a/conan/profiles/docs-release
+++ /dev/null
@@ -1,8 +0,0 @@
-include(default)
-
-[settings]
-compiler.cppstd=20
-libqasm/*:build_type=Release
-
-[options]
-libqasm/*:build_docs=True
diff --git a/conan/profiles/docs-release-apple_clang-macos-arm64 b/conan/profiles/docs-release-apple_clang-macos-arm64
deleted file mode 100644
index eccdeca0f..000000000
--- a/conan/profiles/docs-release-apple_clang-macos-arm64
+++ /dev/null
@@ -1,5 +0,0 @@
-include(docs-release)
-
-[settings]
-compiler=apple-clang
-compiler.version=14
diff --git a/conan/profiles/docs-release-apple_clang-macos-x64 b/conan/profiles/docs-release-apple_clang-macos-x64
deleted file mode 100644
index eccdeca0f..000000000
--- a/conan/profiles/docs-release-apple_clang-macos-x64
+++ /dev/null
@@ -1,5 +0,0 @@
-include(docs-release)
-
-[settings]
-compiler=apple-clang
-compiler.version=14
diff --git a/conan/profiles/docs-release-clang-linux-arm64 b/conan/profiles/docs-release-clang-linux-arm64
deleted file mode 100644
index 07296e63d..000000000
--- a/conan/profiles/docs-release-clang-linux-arm64
+++ /dev/null
@@ -1,9 +0,0 @@
-include(docs-release)
-
-[settings]
-compiler=clang
-compiler.version=14
-
-[conf]
-tools.build:cxxflags=["-stdlib=libc++"]
-tools.build:compiler_executables={ 'c' : 'clang', 'cpp' : 'clang++' }
diff --git a/conan/profiles/docs-release-clang-linux-x64 b/conan/profiles/docs-release-clang-linux-x64
deleted file mode 100644
index 07296e63d..000000000
--- a/conan/profiles/docs-release-clang-linux-x64
+++ /dev/null
@@ -1,9 +0,0 @@
-include(docs-release)
-
-[settings]
-compiler=clang
-compiler.version=14
-
-[conf]
-tools.build:cxxflags=["-stdlib=libc++"]
-tools.build:compiler_executables={ 'c' : 'clang', 'cpp' : 'clang++' }
diff --git a/conan/profiles/docs-release-gcc-linux-arm64 b/conan/profiles/docs-release-gcc-linux-arm64
deleted file mode 100644
index 6e80b08d2..000000000
--- a/conan/profiles/docs-release-gcc-linux-arm64
+++ /dev/null
@@ -1,4 +0,0 @@
-include(docs-release)
-
-[conf]
-tools.build:cxxflags=["-Wno-error=maybe-uninitialized"]
diff --git a/conan/profiles/docs-release-gcc-linux-x64 b/conan/profiles/docs-release-gcc-linux-x64
deleted file mode 100644
index f186681f4..000000000
--- a/conan/profiles/docs-release-gcc-linux-x64
+++ /dev/null
@@ -1,1 +0,0 @@
-include(docs-release)
diff --git a/conan/profiles/docs-release-msvc-windows-x64 b/conan/profiles/docs-release-msvc-windows-x64
deleted file mode 100644
index f186681f4..000000000
--- a/conan/profiles/docs-release-msvc-windows-x64
+++ /dev/null
@@ -1,1 +0,0 @@
-include(docs-release)
diff --git a/conanfile.py b/conanfile.py
index a65b612fd..a85c24b31 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -30,7 +30,6 @@ class LibqasmConan(ConanFile):
         "shared": [True, False],
         "fPIC": [True, False],
         "asan_enabled": [True, False],
-        "build_docs": [True, False],
         "build_python": [True, False],
         "cqasm_python_dir": [None, "ANY"],
         "python_dir": [None, "ANY"],
@@ -40,13 +39,12 @@ class LibqasmConan(ConanFile):
         "shared": False,
         "fPIC": True,
         "asan_enabled": False,
-        "build_docs": False,
         "build_python": False,
         "cqasm_python_dir": None,
         "python_dir": None,
         "python_ext": None
     }
-    exports = "version.py", "include/version.hpp"
+    exports = "version.py", "include/libqasm/versioning.hpp"
     exports_sources = "CMakeLists.txt", "include/*", "python/*", "res/*", "scripts/*", "src/*", "test/*"
 
     @property
@@ -90,7 +88,7 @@ def layout(self):
         self.cpp.build.libdirs = ["."]
 
     def build_requirements(self):
-        self.tool_requires("tree-gen/1.0.8")
+        self.tool_requires("tree-gen/1.0.9")
         if self.settings.arch != "armv8":
             self.tool_requires("zulu-openjdk/21.0.1")
         if self.settings.arch == "wasm":
@@ -112,7 +110,7 @@ def validate(self):
     def requirements(self):
         self.requires("fmt/11.0.2", transitive_headers=True)
         self.requires("range-v3/0.12.0", transitive_headers=True)
-        self.requires("tree-gen/1.0.8", transitive_headers=True, transitive_libs=True)
+        self.requires("tree-gen/1.0.9", transitive_headers=True, transitive_libs=True)
         if not self.settings.arch == "wasm":
             self.requires("antlr4-cppruntime/4.13.1", transitive_headers=True)
 
@@ -122,7 +120,6 @@ def generate(self):
         tc = CMakeToolchain(self)
         tc.variables["ASAN_ENABLED"] = self.options.asan_enabled
         tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
-        tc.variables["LIBQASM_BUILD_DOCS"] = self.options.build_docs
         tc.variables["LIBQASM_BUILD_EMSCRIPTEN"] = self.settings.arch == "wasm"
         tc.variables["LIBQASM_BUILD_PYTHON"] = self.options.build_python
         tc.variables["LIBQASM_BUILD_TESTS"] = self._should_build_test
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
deleted file mode 100644
index cf0725ca1..000000000
--- a/doc/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-message(STATUS "Building docs")
-
-find_program(MKDOCS mkdocs REQUIRED)
-
-set(sources
-    src/about/contributing.md
-    src/about/license.md
-    src/about/release-notes.md
-    src/api/cpp.md
-    src/api/emscripten.md
-    src/api/python.md
-    src/dev-guide/dev-guide.md
-    src/dev-guide/cpp.md
-    src/dev-guide/emscripten.md
-    src/dev-guide/python.md
-    src/user-guide/user-guide.md
-    src/user-guide/cpp.md
-    src/user-guide/emscripten.md
-    src/user-guide/python.md
-    src/index.md
-)
-
-add_custom_target(doc ALL
-    COMMAND ${CMAKE_COMMAND}
-    -E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/../scripts/python
-    ${MKDOCS} build -f ${CMAKE_CURRENT_SOURCE_DIR}/mkdocs.yml
-    # MkDocs requires the site dir to be outside of the doc dir.
-    --site-dir ${CMAKE_CURRENT_BINARY_DIR}/html
-    --no-directory-urls
-    SOURCES ${sources}
-)
-
-include(GNUInstallDirs)
-install(
-    DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
-    DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/libqasm OPTIONAL
-)
diff --git a/doc/mkdocs b/doc/mkdocs
deleted file mode 100644
index ca0412d4f..000000000
--- a/doc/mkdocs
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/python3
-
-# A script to invoke mkdocs with the correct environment.
-# Additionally, it supports deploying via mike:
-#   ./mkdocs deploy [mike-deploy-options]
-
-import errno
-import os
-import shutil
-import sys
-from subprocess import call
-
-doc_dir = os.path.dirname(os.path.normpath(__file__))
-scripts_dir = os.path.join(os.path.dirname(doc_dir), 'scripts')
-build_dir = os.path.join(os.path.dirname(doc_dir), 'build', 'Release', 'doc')
-
-# Set PYTHONPATH for the mkdocstrings handler.
-env = os.environ.copy()
-path = env.get('PYTHONPATH')
-env['PYTHONPATH'] = \
-  (path + ':' if path else '') + os.path.join(scripts_dir, 'python')
-
-config_path = os.path.join(doc_dir, 'mkdocs.yml')
-args = sys.argv[1:]
-if len(args) > 0:
-    command = args[0]
-    if command == 'deploy':
-        git_url = 'https://github.com/' if 'CI' in os.environ else 'git@github.com:'
-        site_repo = git_url + 'QuTech-Delft/libqasm.git'
-
-        site_dir = os.path.join(build_dir, 'libqasm')
-        try:
-            shutil.rmtree(site_dir)
-        except OSError as e:
-            if e.errno == errno.ENOENT:
-                pass
-        ret = call(['git', 'clone', '--depth=1', site_repo, site_dir])
-        if ret != 0:
-            sys.exit(ret)
-
-        # Copy the config to the build dir because the site is built relative to it.
-        config_build_path = os.path.join(build_dir, 'mkdocs.yml')
-        shutil.copyfile(config_path, config_build_path)
-
-        sys.exit(call(
-          ['mike'] + args + ['--config-file', config_build_path, '--branch', 'master'], cwd=site_dir, env=env)
-        )
-    elif not command.startswith('-'):
-        args += ['-f', config_path]
-sys.exit(call(['mkdocs'] + args, env=env))
diff --git a/doc/src/libqasm.css b/doc/src/libqasm.css
deleted file mode 100644
index 4c642c0ea..000000000
--- a/doc/src/libqasm.css
+++ /dev/null
@@ -1,61 +0,0 @@
-:root {
-    --md-primary-fg-color: #0050D0;
-}
-
-.md-grid {
-    max-width: 960px;
-}
-
-@media (min-width: 400px) {
-    .md-tabs {
-        display: block;
-    }
-}
-
-.docblock {
-    border-left: .05rem solid var(--md-primary-fg-color);
-}
-
-.docblock-desc {
-    margin-left: 1em;
-}
-
-pre > code.decl {
-    white-space: pre-wrap;
-}
-
-
-code.decl > div {
-    text-indent: -2ch; /* Negative indent to counteract the indent on the first line */
-    padding-left: 2ch; /* Add padding to the left to create an indent */
-}
-
-.features-container {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 20px;
-    justify-content: center; /* Center the items horizontally */
-}
-
-.feature {
-    flex: 1 1 calc(50% - 20px); /* Two columns with space between */
-    max-width: 600px; /* Set the maximum width for the feature boxes */
-    box-sizing: border-box;
-    padding: 10px;
-    overflow: hidden; /* Hide overflow content */
-    text-overflow: ellipsis; /* Handle text overflow */
-    white-space: normal; /* Allow text wrapping */
-}
-
-.feature h2 {
-    margin-top: 0px;
-    font-weight: bold;
-}
-
-@media (max-width: 768px) {
-    .feature {
-        flex: 1 1 100%; /* Stack columns on smaller screens */
-        max-width: 100%; /* Allow full width on smaller screens */
-        white-space: normal; /* Allow text wrapping on smaller screens */
-    }
-}
diff --git a/doc/src/about/authors.md b/docs/about/authors.md
similarity index 100%
rename from doc/src/about/authors.md
rename to docs/about/authors.md
diff --git a/doc/src/about/contributing.md b/docs/about/contributing.md
similarity index 100%
rename from doc/src/about/contributing.md
rename to docs/about/contributing.md
diff --git a/doc/src/about/license.md b/docs/about/license.md
similarity index 100%
rename from doc/src/about/license.md
rename to docs/about/license.md
diff --git a/doc/src/about/release-notes.md b/docs/about/release-notes.md
similarity index 100%
rename from doc/src/about/release-notes.md
rename to docs/about/release-notes.md
diff --git a/doc/src/api/cpp.md b/docs/api/cpp.md
similarity index 72%
rename from doc/src/api/cpp.md
rename to docs/api/cpp.md
index 9b693fc96..d83d35a7b 100644
--- a/doc/src/api/cpp.md
+++ b/docs/api/cpp.md
@@ -1,4 +1,4 @@
-libQASM C++ API is defined in `include/v3x/cqasm-py.hpp`.
+libQASM C++ API is defined in `include/v3x/cqasm_python.hpp`.
 
 The only exported class is `V3xAnalyzer`.
 This is actually a C++ class that works as a binding for accessing C++ code from Python.
diff --git a/doc/src/api/emscripten.md b/docs/api/emscripten.md
similarity index 100%
rename from doc/src/api/emscripten.md
rename to docs/api/emscripten.md
diff --git a/doc/src/api/python.md b/docs/api/python.md
similarity index 100%
rename from doc/src/api/python.md
rename to docs/api/python.md
diff --git a/doc/src/cpp_highlighting.js b/docs/cpp_highlight.js
similarity index 100%
rename from doc/src/cpp_highlighting.js
rename to docs/cpp_highlight.js
diff --git a/doc/src/dev-guide/cpp.md b/docs/dev-guide/cpp.md
similarity index 86%
rename from doc/src/dev-guide/cpp.md
rename to docs/dev-guide/cpp.md
index 2493f060c..0a811efdc 100644
--- a/doc/src/dev-guide/cpp.md
+++ b/docs/dev-guide/cpp.md
@@ -6,7 +6,7 @@ The following line will also build and cache the `libqasm` Conan package.
 
 ```shell
 conan profile detect
-conan create --version 0.6.7 . -pr:a=tests-debug -b missing
+conan create --version 0.6.8 . -pr:a=tests-debug -b missing
 ```
 
 You can test the C++ binaries:
diff --git a/doc/src/dev-guide/dev-guide.md b/docs/dev-guide/dev-guide.md
similarity index 74%
rename from doc/src/dev-guide/dev-guide.md
rename to docs/dev-guide/dev-guide.md
index 2cdaa2510..6a5c277e2 100644
--- a/doc/src/dev-guide/dev-guide.md
+++ b/docs/dev-guide/dev-guide.md
@@ -2,7 +2,7 @@
 
 For development, see:
 
-- `include`: public headers.
+- `include/libqasm`: public headers.
 - `src`: source files.
 - `test`: test files.
 - `python`: SWIG interface.
@@ -47,6 +47,20 @@ For the time being, we install Java manually for this platform.
     - `mkdocstrings`
     - `pymdown-extensions`
 
+### Linters
+
+- `clang-format-18`
+- `clang-tidy-18`
+
+On a Linux machine, these linters can be installed with the following commands:
+
+```shell
+ wget https://apt.llvm.org/llvm.sh -O llvm_install.sh
+ chmod +x llvm_install.sh
+ ./llvm_install.sh
+ apt-get install -y clang-format-18 clang-tidy-18
+```
+
 ## Build
 
 This version of libQASM can only be compiled via the Conan package manager.
@@ -86,7 +100,7 @@ All the `tests`, except for `linux-x64` profiles, enable Address Sanitizer.
 
 ### Options
 
-Profiles are a shorthand for command line options. The command above could be written, similarly,  as:
+Profiles are a shorthand for command line options. The command above could be written, similarly, as:
 
 ```shell
 conan build . -s:a compiler.cppstd=20 -s:a libqasm/*:build_type=Debug -o libqasm/*:asan_enabled=True -c tools.build:skip_test=False -b missing
@@ -95,7 +109,6 @@ conan build . -s:a compiler.cppstd=20 -s:a libqasm/*:build_type=Debug -o libqasm
 This is the list of options that could be specified either in a profile or in the command line:
 
 - `libqasm/*:asan_enabled={True,False}`: enables Address Sanitizer.
-- `libqasm/*:build_docs={True,False}`: builds documentation or not.
 - `libqasm/*:build_type={Debug,Release}`: builds in debug or release mode.
 - `libqasm/*:shared={True,False}`: builds a shared object library instead of a static library, if applicable.
 
@@ -103,15 +116,34 @@ Tests are disabled by default. To enable them, use `-c tools.build:skip_test=Fal
 
 ### Documentation
 
-Build with a `docs-release-` Conan profile.
-Then serve on `http://127.0.0.1:8000/` by running the local `mkdocs` from the `doc` folder.
+Build and serve on `http://127.0.0.1:8000/`.
 
 ```shell
-conan build . -pr:a=conan/profiles/docs-release-clang-linux-x64 -b missing
-cd doc
-python3 mkdocs serve
+export PTYHONPATH=./scripts/python
+mkdocs serve
 ```
 
+!!! note
+
+    The `export` is needed to point `mkdocs` to the custom handlers used for the C++, emscripten, and Python APIs.
+
+## Linters
+
+Continuous Integration will fail if the files do not adhere to a series of formatting and code style guidelines:
+
+- Formatting checks are defined in `.clang-format`.
+- Code style checks are defined in `.clang-tidy`.
+
+It is recommended to run these linters before pushing any change:
+
+```shell
+python3 ./scripts/run_cpp_linters.py .
+```
+
+!!! note
+
+    The linters require `clang-format-18` and `clang-tidy-18` to be installed on the system.
+
 ## Docker
 
 This tests the library in a container with the bare minimum requirements for libQASM.
@@ -120,8 +152,10 @@ This tests the library in a container with the bare minimum requirements for lib
 docker build .
 ```
 
-**Note for Windows users:** The above might fail on Windows due to the `autocrlf` transformation that git does.
-If you run into this issue, then create new clone of this repository:
+!!! note
+
+    The above might fail on Windows due to the `autocrlf` transformation that git does.
+    If you run into this issue, then create new clone of this repository:
 
 ```shell
 git clone --config core.autocrlf=input git@github.com:QuTech-Delft/libqasm.git
diff --git a/doc/src/dev-guide/emscripten.md b/docs/dev-guide/emscripten.md
similarity index 100%
rename from doc/src/dev-guide/emscripten.md
rename to docs/dev-guide/emscripten.md
diff --git a/doc/src/dev-guide/python.md b/docs/dev-guide/python.md
similarity index 100%
rename from doc/src/dev-guide/python.md
rename to docs/dev-guide/python.md
diff --git a/docs/extra_api.css b/docs/extra_api.css
new file mode 100644
index 000000000..d375e03ce
--- /dev/null
+++ b/docs/extra_api.css
@@ -0,0 +1,11 @@
+.docblock {
+    border-left: .05rem solid var(--md-primary-fg-color);
+}
+
+.docblock-desc {
+    margin-left: 1em;
+}
+
+pre > code.decl {
+    white-space: pre-wrap;
+}
diff --git a/docs/extra_highlight.css b/docs/extra_highlight.css
new file mode 100644
index 000000000..733e4cb5a
--- /dev/null
+++ b/docs/extra_highlight.css
@@ -0,0 +1,103 @@
+pre code.hljs {
+    display: block;
+    overflow-x: auto;
+    padding: 1em
+}
+code.hljs {
+    padding: 3px 5px
+}
+/*!
+  Theme: Default
+  Description: Original highlight.js style
+  Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
+  Maintainer: @highlightjs/core-team
+  Website: https://highlightjs.org/
+  License: see project LICENSE
+  Touched: 2021
+  Source: https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/default.min.css
+*/
+.hljs{background:#f3f3f3;color:#444}
+.hljs-comment{color:#697070}
+.hljs-punctuation,
+.hljs-tag{color:#444a}
+.hljs-tag
+.hljs-attr,
+.hljs-tag
+.hljs-name{color:#444}
+.hljs-attribute,
+.hljs-doctag,
+.hljs-keyword,
+.hljs-meta
+.hljs-keyword,
+.hljs-name,
+.hljs-selector-tag{font-weight:700}
+.hljs-deletion,
+.hljs-number,
+.hljs-quote,
+.hljs-selector-class,
+.hljs-selector-id,
+.hljs-string,
+.hljs-template-tag,
+.hljs-type{color:#800}
+.hljs-section,
+.hljs-title{color:#800;font-weight:700}
+.hljs-link,
+.hljs-operator,
+.hljs-regexp,
+.hljs-selector-attr,
+.hljs-selector-pseudo,
+.hljs-symbol,
+.hljs-template-variable,
+.hljs-variable{color:#ab5656}
+.hljs-literal{color:#695}
+.hljs-addition,
+.hljs-built_in,
+.hljs-bullet,
+.hljs-code{color:#397300}
+.hljs-meta{color:#1f7199}
+.hljs-meta
+.hljs-string{color:#38a}
+.hljs-emphasis{font-style:italic}
+.hljs-strong{font-weight:700}
+/*!
+  Theme: one-atom-dark
+  Source: https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-dark.min.css
+*/
+[data-md-color-scheme="slate"] .hljs{color:#abb2bf;background:#282c34}
+[data-md-color-scheme="slate"] .hljs-comment,
+[data-md-color-scheme="slate"] .hljs-quote{color:#5c6370;font-style:italic}
+[data-md-color-scheme="slate"] .hljs-doctag,
+[data-md-color-scheme="slate"] .hljs-formula,
+[data-md-color-scheme="slate"] .hljs-keyword{color:#c678dd}
+[data-md-color-scheme="slate"] .hljs-deletion,
+[data-md-color-scheme="slate"] .hljs-name,
+[data-md-color-scheme="slate"] .hljs-section,
+[data-md-color-scheme="slate"] .hljs-selector-tag,
+[data-md-color-scheme="slate"] .hljs-subst{color:#e06c75}
+[data-md-color-scheme="slate"] .hljs-literal{color:#56b6c2}
+[data-md-color-scheme="slate"] .hljs-addition,
+[data-md-color-scheme="slate"] .hljs-attribute,
+[data-md-color-scheme="slate"] .hljs-meta
+[data-md-color-scheme="slate"] .hljs-string,
+[data-md-color-scheme="slate"] .hljs-string{color:#98c379}
+[data-md-color-scheme="slate"] .hljs-attr,
+[data-md-color-scheme="slate"] .hljs-number,
+[data-md-color-scheme="slate"] .hljs-selector-attr,
+[data-md-color-scheme="slate"] .hljs-selector-class,
+[data-md-color-scheme="slate"] .hljs-selector-pseudo,
+[data-md-color-scheme="slate"] .hljs-template-variable,
+[data-md-color-scheme="slate"] .hljs-type,
+[data-md-color-scheme="slate"] .hljs-variable{color:#d19a66}
+[data-md-color-scheme="slate"] .hljs-bullet,
+[data-md-color-scheme="slate"] .hljs-link,
+[data-md-color-scheme="slate"] .hljs-meta,
+[data-md-color-scheme="slate"] .hljs-selector-id,
+[data-md-color-scheme="slate"] .hljs-symbol,
+[data-md-color-scheme="slate"] .hljs-title{color:#61aeee}
+[data-md-color-scheme="slate"] .hljs-built_in,
+[data-md-color-scheme="slate"] .hljs-class
+[data-md-color-scheme="slate"] .hljs-title,
+[data-md-color-scheme="slate"] .hljs-title.class_{color:#e6c07b}
+[data-md-color-scheme="slate"] .hljs-emphasis{font-style:italic}
+[data-md-color-scheme="slate"] .hljs-strong{font-weight:700}
+[data-md-color-scheme="slate"] .hljs-link{text-decoration:underline}
diff --git a/doc/src/index.md b/docs/index.md
similarity index 85%
rename from doc/src/index.md
rename to docs/index.md
index 0b07cce79..23cb87d12 100644
--- a/doc/src/index.md
+++ b/docs/index.md
@@ -1,5 +1,8 @@
 libQASM is a library to parse cQASM programs, developed by QuTech.
 
+At the moment, libQASM only supports cQASM v3.0 programs
+(see [cQASM-spec](https://qutech-delft.github.io/cQASM-spec/latest/) for the language specification).
+
 It performs lexical, syntactic, and semantic analysis of an input program received via a file or a string.
 It produces one of the following results:
 
diff --git a/doc/src/python_highlighting.js b/docs/python_highlight.js
similarity index 100%
rename from doc/src/python_highlighting.js
rename to docs/python_highlight.js
diff --git a/doc/src/typescript_highlighting.js b/docs/typescript_highlight.js
similarity index 100%
rename from doc/src/typescript_highlighting.js
rename to docs/typescript_highlight.js
diff --git a/doc/src/user-guide/cpp.md b/docs/user-guide/cpp.md
similarity index 85%
rename from doc/src/user-guide/cpp.md
rename to docs/user-guide/cpp.md
index ea552a78a..f1ef5c399 100644
--- a/doc/src/user-guide/cpp.md
+++ b/docs/user-guide/cpp.md
@@ -2,9 +2,9 @@ libQASM can be requested as a Conan package from a `conanfile.py`.
 
 ```
 def build_requirements(self):
-    self.tool_requires("libqasm/0.6.7")
+    self.tool_requires("libqasm/0.6.8")
 def requirements(self):
-    self.requires("libqasm/0.6.7")
+    self.requires("libqasm/0.6.8")
 ```
 
 And then linked against from a `CMakeLists.txt`:
@@ -19,7 +19,7 @@ target_link_libraries(<your target> PUBLIC libqasm::libqasm)
 **Example**:
 
 ```cpp
-#include "v3x/cqasm-py.hpp"
+#include "libqasm/v3x/cqasm_python.hpp"
 
 auto program = std::string{ R"(
     version 3.0
diff --git a/doc/src/user-guide/docker.md b/docs/user-guide/docker.md
similarity index 100%
rename from doc/src/user-guide/docker.md
rename to docs/user-guide/docker.md
diff --git a/doc/src/user-guide/emscripten.md b/docs/user-guide/emscripten.md
similarity index 100%
rename from doc/src/user-guide/emscripten.md
rename to docs/user-guide/emscripten.md
diff --git a/doc/src/user-guide/python.md b/docs/user-guide/python.md
similarity index 100%
rename from doc/src/user-guide/python.md
rename to docs/user-guide/python.md
diff --git a/doc/src/user-guide/user-guide.md b/docs/user-guide/user-guide.md
similarity index 100%
rename from doc/src/user-guide/user-guide.md
rename to docs/user-guide/user-guide.md
diff --git a/emscripten/emscripten_wrapper.cpp b/emscripten/emscripten_wrapper.cpp
index d2db0001c..33e5cc553 100644
--- a/emscripten/emscripten_wrapper.cpp
+++ b/emscripten/emscripten_wrapper.cpp
@@ -1,8 +1,7 @@
 #include "emscripten_wrapper.hpp"
 
-#include "v3x/cqasm-py.hpp"
-#include "version.hpp"
-
+#include "libqasm/v3x/cqasm_python.hpp"
+#include "libqasm/versioning.hpp"
 
 /**
  * Returns libqasm version.
@@ -11,7 +10,6 @@ std::string EmscriptenWrapper::get_version() {
     return cqasm::get_version();
 }
 
-
 /**
  * Parses a data string containing a v3.x program.
  * The file_name is only used when reporting errors.
@@ -23,11 +21,10 @@ std::string EmscriptenWrapper::get_version() {
  * Every error is mapped to an LSP Diagnostic structure:
  * severity is hardcoded to 1 at the moment (value corresponding to an Error level).
  */
-std::string EmscriptenWrapper::parse_string_to_json(const std::string &data, const std::string &file_name) {
+std::string EmscriptenWrapper::parse_string_to_json(const std::string& data, const std::string& file_name) {
     return V3xAnalyzer::parse_string_to_json(data, file_name);
 }
 
-
 /**
  * Parses and analyzes a data string containing a v3.x program.
  * The file_name is only used when reporting errors.
@@ -39,6 +36,6 @@ std::string EmscriptenWrapper::parse_string_to_json(const std::string &data, con
  * Every error is mapped to an LSP Diagnostic structure:
  * severity is hardcoded to 1 at the moment (value corresponding to an Error level).
  */
-std::string EmscriptenWrapper::analyze_string_to_json(const std::string &data, const std::string &file_name) {
+std::string EmscriptenWrapper::analyze_string_to_json(const std::string& data, const std::string& file_name) {
     return V3xAnalyzer{}.analyze_string_to_json(data, file_name);
 }
diff --git a/emscripten/emscripten_wrapper.hpp b/emscripten/emscripten_wrapper.hpp
index 780952388..c373a88a2 100644
--- a/emscripten/emscripten_wrapper.hpp
+++ b/emscripten/emscripten_wrapper.hpp
@@ -1,8 +1,8 @@
 #pragma once
 
 #include <emscripten/bind.h>
-#include <string>
 
+#include <string>
 
 /**
  * Main class for parsing and analyzing cQASM files.
@@ -60,7 +60,7 @@ struct EmscriptenWrapper {
      *      `
      *      let output = parse_string_to_json(program, "bell.cq")
      */
-    std::string parse_string_to_json(const std::string &data, const std::string &file_name);
+    std::string parse_string_to_json(const std::string& data, const std::string& file_name);
 
     /**
      * Parses and analyzes a string containing a cQASM v3.0 program.
@@ -88,14 +88,13 @@ struct EmscriptenWrapper {
      *      `
      *      let output = analyze_string_to_json(program, "bell.cq")
      */
-    std::string analyze_string_to_json(const std::string &data, const std::string &file_name);
+    std::string analyze_string_to_json(const std::string& data, const std::string& file_name);
 };
 
-
 EMSCRIPTEN_BINDINGS(CqasmJS) {
     emscripten::class_<EmscriptenWrapper>("EmscriptenWrapper")
-    .constructor()
-    .function("get_version", &EmscriptenWrapper::get_version)
-    .function("parse_string_to_json", &EmscriptenWrapper::parse_string_to_json)
-    .function("analyze_string_to_json", &EmscriptenWrapper::analyze_string_to_json);
+        .constructor()
+        .function("get_version", &EmscriptenWrapper::get_version)
+        .function("parse_string_to_json", &EmscriptenWrapper::parse_string_to_json)
+        .function("analyze_string_to_json", &EmscriptenWrapper::analyze_string_to_json);
 }
diff --git a/include/cqasm-annotations.hpp b/include/libqasm/annotations.hpp
similarity index 62%
rename from include/cqasm-annotations.hpp
rename to include/libqasm/annotations.hpp
index 9422ecfcb..5980917b4 100644
--- a/include/cqasm-annotations.hpp
+++ b/include/libqasm/annotations.hpp
@@ -24,8 +24,8 @@ struct SourceLocation {
         std::uint32_t line = 0;
         std::uint32_t column = 0;
 
-        bool operator==(const Index &other) const = default;
-        auto operator<=>(const Index &other) const = default;
+        bool operator==(const Index& other) const = default;
+        auto operator<=>(const Index& other) const = default;
     };
 
     /**
@@ -37,14 +37,14 @@ struct SourceLocation {
         Index last;
 
         Range() = default;
-        Range(const Index &f, const Index &l);
-        Range(const Range &other) = default;
-        Range(Range &&other) noexcept = default;
-        Range &operator=(const Range &other) = default;
-        Range &operator=(Range &&other) noexcept = default;
-
-        bool operator==(const Range &other) const = default;
-        auto operator<=>(const Range &other) const = default;
+        Range(const Index& f, const Index& l);
+        Range(const Range& other) = default;
+        Range(Range&& other) noexcept = default;
+        Range& operator=(const Range& other) = default;
+        Range& operator=(Range&& other) noexcept = default;
+
+        bool operator==(const Range& other) const = default;
+        auto operator<=>(const Range& other) const = default;
     };
 
     /**
@@ -61,30 +61,30 @@ struct SourceLocation {
      * Constructs a source location object.
      */
     SourceLocation() = default;
-    explicit SourceLocation(const std::optional<std::string> &file_name, const Range &range);
-    SourceLocation(const SourceLocation &other) = default;
-    SourceLocation(SourceLocation &&other) noexcept = default;
-    SourceLocation &operator=(const SourceLocation &other) = default;
-    SourceLocation &operator=(SourceLocation &&other) noexcept = default;
+    explicit SourceLocation(const std::optional<std::string>& file_name, const Range& range);
+    SourceLocation(const SourceLocation& other) = default;
+    SourceLocation(SourceLocation&& other) noexcept = default;
+    SourceLocation& operator=(const SourceLocation& other) = default;
+    SourceLocation& operator=(SourceLocation&& other) noexcept = default;
 
-    bool operator==(const SourceLocation &other) const = default;
+    bool operator==(const SourceLocation& other) const = default;
     // Some versions of clang still do not implement operator<=> for std::optional
     // So they will complain if we try to define a default operator<=> on a struct that contains a std::optional
     // This implementation just does not check the optional file_name
     // Instead, it just assumes that, when comparing source locations, the file_name will be the same
     // This will always be the case if we are just parsing or analyzing a file or a string
-    auto operator<=>(const SourceLocation &other) const { return range <=> other.range; }
+    auto operator<=>(const SourceLocation& other) const { return range <=> other.range; }
 
     /**
      * Expands the location range to contain the given location in the source file.
      */
-    void expand_to_include(const Index &last);
+    void expand_to_include(const Index& last);
 };
 
 /**
  * Stream << overload for source location objects.
  */
-std::ostream &operator<<(std::ostream &os, const SourceLocation &object);
+std::ostream& operator<<(std::ostream& os, const SourceLocation& object);
 
 }  // namespace cqasm::annotations
 
diff --git a/include/cqasm-annotations-constants.hpp b/include/libqasm/annotations_constants.hpp
similarity index 58%
rename from include/cqasm-annotations-constants.hpp
rename to include/libqasm/annotations_constants.hpp
index d243695c5..af7345ad8 100644
--- a/include/cqasm-annotations-constants.hpp
+++ b/include/libqasm/annotations_constants.hpp
@@ -2,6 +2,6 @@
 
 namespace cqasm::annotations {
 
-static constexpr const char *unknown_file_name = "<unknown file name>";
+static constexpr const char* unknown_file_name = "<unknown file name>";
 
 }  // namespace cqasm::annotations
diff --git a/include/cqasm.hpp b/include/libqasm/cqasm.hpp
similarity index 85%
rename from include/cqasm.hpp
rename to include/libqasm/cqasm.hpp
index cd77c0097..c65ff6655 100644
--- a/include/cqasm.hpp
+++ b/include/libqasm/cqasm.hpp
@@ -8,4 +8,4 @@
 
 #pragma once
 
-#include "v3x/cqasm.hpp"
+#include "libqasm/v3x/cqasm.hpp"
diff --git a/include/cqasm-error.hpp b/include/libqasm/error.hpp
similarity index 78%
rename from include/cqasm-error.hpp
rename to include/libqasm/error.hpp
index ad8422d0b..24dfe9b28 100644
--- a/include/cqasm-error.hpp
+++ b/include/libqasm/error.hpp
@@ -10,8 +10,8 @@
 #include <string>
 #include <vector>
 
-#include "cqasm-annotations.hpp"
-#include "cqasm-tree.hpp"
+#include "libqasm/annotations.hpp"
+#include "libqasm/tree.hpp"
 
 /**
  * Namespace for exceptions used by libqasm.
@@ -25,7 +25,7 @@ using AnalysisError = Error;
 using ParseErrors = std::vector<ParseError>;
 using AnalysisErrors = std::vector<AnalysisError>;
 
-static constexpr const char *unknown_error_message = "<unknown error message>";
+static constexpr const char* unknown_error_message = "<unknown error message>";
 
 /**
  * Exception used for analysis errors.
@@ -52,30 +52,30 @@ class Error : public std::runtime_error {
      * Constructs a new error.
      * If node is a non-null annotatable with a location node, its location information is attached.
      */
-    explicit Error(const std::string &message, const tree::Annotatable *node = nullptr);
+    explicit Error(const std::string& message, const tree::Annotatable* node = nullptr);
 
     /**
      * Constructs a new error from a message and a source location.
      */
-    Error(const std::string &message, std::shared_ptr<annotations::SourceLocation> location);
+    Error(const std::string& message, std::shared_ptr<annotations::SourceLocation> location);
 
     /**
      * Constructs a new error from a message and all the fields of a source location.
      */
-    Error(const std::string &message, const std::optional<std::string> &file_name,
-        const annotations::SourceLocation::Range &range);
+    Error(const std::string& message, const std::optional<std::string>& file_name,
+        const annotations::SourceLocation::Range& range);
 
     /**
      * Sets the context of this error to the SourceLocation annotation of the given node,
      * if the error doesn't already have such a context.
      * If it does, this is no-op.
      */
-    void context(const tree::Annotatable &node);
+    void context(const tree::Annotatable& node);
 
     /**
      * Returns the message exception-style.
      */
-    const char *what() const noexcept override;
+    const char* what() const noexcept override;
 
     /**
      * Returns a string with a JSON representation of an Error.
@@ -89,7 +89,7 @@ class Error : public std::runtime_error {
 /**
  * Stream << overload for Error.
  */
-std::ostream &operator<<(std::ostream &os, const Error &error);
+std::ostream& operator<<(std::ostream& os, const Error& error);
 
 /**
  * Defines a new analysis error class.
@@ -97,7 +97,7 @@ std::ostream &operator<<(std::ostream &os, const Error &error);
 #define CQASM_ANALYSIS_ERROR(Name)                                                         \
     class Name : public ::cqasm::error::AnalysisError {                                    \
     public:                                                                                \
-        explicit Name(std::string &&message = "", const tree::Annotatable *node = nullptr) \
+        explicit Name(std::string&& message = "", const tree::Annotatable* node = nullptr) \
         : ::cqasm::error::AnalysisError(std::move(message), node) {}                       \
     }
 
diff --git a/include/cqasm-overload.hpp b/include/libqasm/overload.hpp
similarity index 89%
rename from include/cqasm-overload.hpp
rename to include/libqasm/overload.hpp
index 693a29902..6f5d44a04 100644
--- a/include/cqasm-overload.hpp
+++ b/include/libqasm/overload.hpp
@@ -6,8 +6,8 @@
 #include <utility>  // pair
 #include <vector>
 
-#include "cqasm-overload.hpp"
-#include "cqasm-tree.hpp"
+#include "libqasm/overload.hpp"
+#include "libqasm/tree.hpp"
 
 /**
  * Overload templates are case-sensitive.
@@ -35,21 +35,21 @@ class Overload {
     Types param_types;
 
 public:
-    Overload(const T &tag, const Types &param_types)
+    Overload(const T& tag, const Types& param_types)
     : tag{ tag }
     , param_types{ param_types } {}
-    Overload(const Overload &other)
+    Overload(const Overload& other)
     : tag{ other.tag }
     , param_types{ other.param_types } {}
-    Overload(Overload &&other) noexcept
+    Overload(Overload&& other) noexcept
     : tag{ std::move(other.tag) }
     , param_types{ other.param_types } {}
-    Overload &operator=(const Overload &other) {
+    Overload& operator=(const Overload& other) {
         tag = other.tag;
         param_types = other.param_types;
         return *this;
     }
-    Overload &operator=(Overload &&other) noexcept {
+    Overload& operator=(Overload&& other) noexcept {
         tag = std::move(other.tag);
         param_types = other.param_types;
         return *this;
@@ -58,7 +58,7 @@ class Overload {
     /**
      * Returns the tag for this overload.
      */
-    [[nodiscard]] const T &get_tag() const { return tag; }
+    [[nodiscard]] const T& get_tag() const { return tag; }
 
     /**
      * Returns the number of parameters for this overload.
@@ -68,7 +68,7 @@ class Overload {
     /**
      * Returns the parameter type object for the parameter at the specified index.
      */
-    [[nodiscard]] const Type &param_type_at(std::size_t index) const { return param_types.at(index); }
+    [[nodiscard]] const Type& param_type_at(std::size_t index) const { return param_types.at(index); }
 };
 
 //------------------//
@@ -98,7 +98,7 @@ class OverloadResolver {
      * Note that ambiguous overloads are silently resolved by using the last applicable overload,
      * so more specific overloads should always be added last.
      */
-    void add_overload(const T &tag, const Types &param_types) { overloads.emplace_back(tag, param_types); }
+    void add_overload(const T& tag, const Types& param_types) { overloads.emplace_back(tag, param_types); }
 
     /**
      * Tries to resolve which overload belongs to the given argument list, if any.
@@ -106,7 +106,7 @@ class OverloadResolver {
      * otherwise the tag corresponding to the first proper overload and
      * the appropriately promoted vector of value pointers are returned.
      */
-    [[nodiscard]] std::pair<T, Values> resolve(const Values &args) {
+    [[nodiscard]] std::pair<T, Values> resolve(const Values& args) {
         for (auto overload = overloads.rbegin(); overload != overloads.rend(); ++overload) {
             if (overload->num_params() != args.size()) {
                 continue;
@@ -157,7 +157,7 @@ class OverloadedNameResolver {
      * Note that ambiguous overloads are silently resolved by using the last applicable overload,
      * so more specific overloads should always be added last.
      */
-    virtual void add_overload(const std::string &name, const T &tag, const Types &param_types) {
+    virtual void add_overload(const std::string& name, const T& tag, const Types& param_types) {
         if (auto entry = table.find(name); entry == table.end()) {
             auto resolver = OverloadResolver<T, TypeBase, Node>();
             resolver.add_overload(tag, param_types);
@@ -174,7 +174,7 @@ class OverloadedNameResolver {
      * returns the tag of the first applicable callable/overload pair and
      * the appropriately promoted vector of value pointers.
      */
-    [[nodiscard]] virtual std::pair<T, Values> resolve(const std::string &name, const Values &args) {
+    [[nodiscard]] virtual std::pair<T, Values> resolve(const std::string& name, const Values& args) {
         if (auto entry = table.find(name); entry != table.end()) {
             return entry->second.resolve(args);
         }
diff --git a/include/cqasm-result.hpp b/include/libqasm/result.hpp
similarity index 80%
rename from include/cqasm-result.hpp
rename to include/libqasm/result.hpp
index 7fa2e548e..dfa58cc84 100644
--- a/include/cqasm-result.hpp
+++ b/include/libqasm/result.hpp
@@ -9,7 +9,7 @@
 #include <string>
 #include <vector>
 
-#include "tree-base.hpp"
+#include "libqasm/tree.hpp"
 
 namespace cqasm::result {
 
@@ -20,13 +20,13 @@ namespace cqasm::result {
  * Notice that the AST and error messages won't be available at the same time.
  */
 template <typename Result>
-std::vector<std::string> to_strings(const Result &result) {
+std::vector<std::string> to_strings(const Result& result) {
     auto ret = std::vector<std::string>(1);
     if (result.errors.empty()) {
         ret[0] = ::tree::base::serialize(result.root);
     } else {
         ret.resize(result.errors.size() + 1);
-        std::transform(result.errors.begin(), result.errors.end(), std::next(ret.begin()), [](const auto &error) {
+        std::transform(result.errors.begin(), result.errors.end(), std::next(ret.begin()), [](const auto& error) {
             return error.what();
         });
     }
@@ -34,13 +34,13 @@ std::vector<std::string> to_strings(const Result &result) {
 }
 
 template <typename Errors>
-std::string errors_to_json(const Errors &errors) {
+std::string errors_to_json(const Errors& errors) {
     return fmt::format(R"({{"errors":[{0}]}})",
-        fmt::join(errors | ranges::views::transform([](const auto &err) { return err.to_json(); }), ","));
+        fmt::join(errors | ranges::views::transform([](const auto& err) { return err.to_json(); }), ","));
 }
 
 template <typename Root>
-std::string root_to_json(const Root &root) {
+std::string root_to_json(const Root& root) {
     std::ostringstream oss{};
     root->dump_json(oss);
     return oss.str();
@@ -50,7 +50,7 @@ std::string root_to_json(const Root &root) {
  * Returns a string with a JSON representation of a ParseResult or an AnalysisResult.
  */
 template <typename Result>
-std::string to_json(const Result &result) {
+std::string to_json(const Result& result) {
     return (result.errors.empty()) ? root_to_json(result.root) : errors_to_json(result.errors);
 }
 
diff --git a/include/cqasm-string-builder.hpp b/include/libqasm/string_builder.hpp
similarity index 77%
rename from include/cqasm-string-builder.hpp
rename to include/libqasm/string_builder.hpp
index 4ef3956fa..ca88476b8 100644
--- a/include/cqasm-string-builder.hpp
+++ b/include/libqasm/string_builder.hpp
@@ -1,6 +1,6 @@
 /** \file
  * This file only contains utility stuff on top of the abstract syntax tree
- * structure generated by \ref tree-gen based on "cqasm-ast.tree". Refer to
+ * structure generated by \ref tree-gen based on "ast.tree". Refer to
  * the cqasm::ast namespace documentation for more information.
  */
 
@@ -9,7 +9,7 @@
 #include <sstream>
 #include <string>
 
-#include "cqasm-tree.hpp"
+#include "libqasm/tree.hpp"
 
 namespace cqasm::ast {
 
@@ -24,12 +24,12 @@ struct StringBuilder : public cqasm::tree::Base {
     /**
      * Pushes a string fragment into the string.
      */
-    void push_string(const std::string &str);
+    void push_string(const std::string& str);
 
     /**
      * Pushes an escape sequence into the string.
      */
-    void push_escape(const std::string &escape);
+    void push_escape(const std::string& escape);
 };
 
 }  // namespace cqasm::ast
diff --git a/include/cqasm-tree.hpp b/include/libqasm/tree.hpp
similarity index 100%
rename from include/cqasm-tree.hpp
rename to include/libqasm/tree.hpp
diff --git a/include/cqasm-utils.hpp b/include/libqasm/utils.hpp
similarity index 61%
rename from include/cqasm-utils.hpp
rename to include/libqasm/utils.hpp
index 04a0df38b..90f41fa88 100644
--- a/include/cqasm-utils.hpp
+++ b/include/libqasm/utils.hpp
@@ -14,21 +14,21 @@ namespace cqasm::utils {
 /**
  * Makes a string lowercase.
  */
-std::string to_lowercase(const std::string &name);
+std::string to_lowercase(const std::string& name);
 
 /**
  * Case-insensitive string compare.
  */
-bool equal_case_insensitive(const std::string &lhs, const std::string &rhs);
+bool equal_case_insensitive(const std::string& lhs, const std::string& rhs);
 
 /**
  * Encodes a string in URL format.
  */
-std::string url_encode(const std::string &str);
+std::string url_encode(const std::string& str);
 
 /**
  * Encodes a string in JSON format.
  */
-std::string json_encode(const std::string &str);
+std::string json_encode(const std::string& str);
 
 }  // namespace cqasm::utils
diff --git a/include/v3x/cqasm-analysis-result.hpp b/include/libqasm/v3x/analysis_result.hpp
similarity index 94%
rename from include/v3x/cqasm-analysis-result.hpp
rename to include/libqasm/v3x/analysis_result.hpp
index 87d60b33b..9d42b3d39 100644
--- a/include/v3x/cqasm-analysis-result.hpp
+++ b/include/libqasm/v3x/analysis_result.hpp
@@ -5,9 +5,9 @@
 #include <string>
 #include <vector>
 
-#include "cqasm-ast.hpp"
-#include "cqasm-error.hpp"
-#include "cqasm-semantic.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/v3x/ast.hpp"
+#include "libqasm/v3x/semantic.hpp"
 
 /**
  * Namespace for the \ref cqasm::analyzer::Analyzer "Analyzer" class and support classes.
@@ -64,7 +64,7 @@ class AnalysisResult {
      * The exception is always an AnalysisFailed, deriving from std::runtime_error.
      * The actual error messages are in this case first written to the given output stream, defaulting to stderr.
      */
-    Root unwrap(std::ostream &out = std::cerr) const;
+    Root unwrap(std::ostream& out = std::cerr) const;
 
     /**
      * Returns a vector of strings, of which the first is reserved for the CBOR serialization of the v3.x semantic AST.
diff --git a/include/v3x/cqasm-analyzer.hpp b/include/libqasm/v3x/analyzer.hpp
similarity index 70%
rename from include/v3x/cqasm-analyzer.hpp
rename to include/libqasm/v3x/analyzer.hpp
index 639edbff8..62dd1c268 100644
--- a/include/v3x/cqasm-analyzer.hpp
+++ b/include/libqasm/v3x/analyzer.hpp
@@ -13,14 +13,14 @@
 #include <optional>
 #include <string>
 
-#include "cqasm-analysis-result.hpp"
-#include "cqasm-analyzer.hpp"
-#include "cqasm-ast.hpp"
-#include "cqasm-core-function.hpp"
-#include "cqasm-parse-helper.hpp"
-#include "cqasm-resolver.hpp"
-#include "cqasm-scope.hpp"
-#include "cqasm-semantic.hpp"
+#include "analysis_result.hpp"
+#include "analyzer.hpp"
+#include "libqasm/v3x/ast.hpp"
+#include "libqasm/v3x/core_function.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
+#include "libqasm/v3x/resolver.hpp"
+#include "libqasm/v3x/scope.hpp"
+#include "libqasm/v3x/semantic.hpp"
 
 /**
  * Namespace for the \ref cqasm::analyzer::Analyzer "Analyzer" class and support classes.
@@ -44,7 +44,7 @@ namespace cqasm::v3x::analyzer {
  * and the `analyze*()` functions never change this state (hence they are const).
  */
 class Analyzer {
-    friend class AnalyzeTreeGenAstVisitor;
+    friend class SemanticAnalyzer;
 
 public:
     /**
@@ -55,20 +55,20 @@ class Analyzer {
 protected:
     std::list<Scope> scope_stack_;
 
-    [[nodiscard]] Scope &global_scope();
-    [[nodiscard]] Scope &current_scope();
+    [[nodiscard]] Scope& global_scope();
+    [[nodiscard]] Scope& current_scope();
     [[nodiscard]] tree::One<semantic::Block> current_block();
-    [[nodiscard]] tree::Any<semantic::Variable> &current_variables();
+    [[nodiscard]] tree::Any<semantic::Variable>& current_variables();
 
-    [[nodiscard]] const Scope &global_scope() const;
-    [[nodiscard]] const Scope &current_scope() const;
-    [[nodiscard]] const tree::Any<semantic::Variable> &current_variables() const;
+    [[nodiscard]] const Scope& global_scope() const;
+    [[nodiscard]] const Scope& current_scope() const;
+    [[nodiscard]] const tree::Any<semantic::Variable>& current_variables() const;
 
 public:
     /**
      * Creates a new semantic analyzer.
      */
-    explicit Analyzer(const primitives::Version &api_version = "3.0");
+    explicit Analyzer(const primitives::Version& api_version = "3.0");
 
     /**
      * Destroys a semantic analyzer.
@@ -93,26 +93,26 @@ class Analyzer {
     /**
      * Analyzes the given program AST node.
      */
-    [[nodiscard]] virtual AnalysisResult analyze(ast::Program &program);
+    [[nodiscard]] virtual AnalysisResult analyze(ast::Program& program);
 
     /**
      * Analyzes the given parse result.
      * If there are parse errors, they are moved into the AnalysisResult error list,
      * and the root node will be empty.
      */
-    [[nodiscard]] virtual AnalysisResult analyze(const parser::ParseResult &parse_result);
+    [[nodiscard]] virtual AnalysisResult analyze(const parser::ParseResult& parse_result);
 
     /**
      * Parses and analyzes the given file.
      */
-    [[nodiscard]] virtual AnalysisResult analyze_file(const std::string &file_name);
+    [[nodiscard]] virtual AnalysisResult analyze_file(const std::string& file_name);
 
     /**
      * Parses and analyzes the given string.
      * The optional file_name argument will be used only for error messages.
      */
     [[nodiscard]] virtual AnalysisResult analyze_string(
-        const std::string &data, const std::optional<std::string> &file_name);
+        const std::string& data, const std::optional<std::string>& file_name);
 
     /**
      * Pushes a new empty scope to the top of the scope stack.
@@ -127,23 +127,23 @@ class Analyzer {
     /**
      * Adds a statement to the current scope.
      */
-    virtual void add_statement_to_current_scope(const tree::One<semantic::Statement> &statement);
+    virtual void add_statement_to_current_scope(const tree::One<semantic::Statement>& statement);
 
     /**
      * Adds a variable to the current scope.
      */
-    virtual void add_variable_to_current_scope(const tree::One<semantic::Variable> &variable);
+    virtual void add_variable_to_current_scope(const tree::One<semantic::Variable>& variable);
 
     /**
      * Resolves a variable.
      * Throws NameResolutionFailure if no variable by the given name exists.
      */
-    [[nodiscard]] virtual values::Value resolve_variable(const std::string &name) const;
+    [[nodiscard]] virtual values::Value resolve_variable(const std::string& name) const;
 
     /**
      * Registers a variable.
      */
-    virtual void register_variable(const std::string &name, const values::Value &value);
+    virtual void register_variable(const std::string& name, const values::Value& value);
 
     /**
      * Resolves a function.
@@ -153,13 +153,13 @@ class Analyzer {
      * OverloadResolutionFailure if no overload of the function exists for the given arguments,
      * or otherwise returns the value returned by the function.
      */
-    [[nodiscard]] virtual values::Value resolve_function(const std::string &name, const values::Values &args) const;
+    [[nodiscard]] virtual values::Value resolve_function(const std::string& name, const values::Values& args) const;
 
     /**
      * Registers a consteval core function.
      */
     virtual void register_consteval_core_function(
-        const std::string &name, const types::Types &param_types, const resolver::ConstEvalCoreFunction &function);
+        const std::string& name, const types::Types& param_types, const resolver::ConstEvalCoreFunction& function);
 
     /**
      * Convenience method for registering a consteval core function.
@@ -167,28 +167,38 @@ class Analyzer {
      * converted to types::Types for the other overload using types::from_spec.
      */
     virtual void register_consteval_core_function(
-        const std::string &name, const std::string &param_types, const resolver::ConstEvalCoreFunction &function);
+        const std::string& name, const std::string& param_types, const resolver::ConstEvalCoreFunction& function);
 
     /**
-     * Resolves an instruction.
+     * Resolves a GateInstruction.
+     * Throws NameResolutionFailure if no instruction by the given name exists,
+     * OverloadResolutionFailure if no overload exists for the given arguments,
+     * or otherwise returns the resolved instruction node.
+     * Annotation data, line number information, and the condition still need to be set by the caller.
+     */
+    [[nodiscard]] tree::One<semantic::Instruction> resolve_instruction(
+        const std::string& name, const tree::One<semantic::Gate>& gate, const values::Values& args) const;
+
+    /**
+     * Resolves a NonGateInstruction.
      * Throws NameResolutionFailure if no instruction by the given name exists,
      * OverloadResolutionFailure if no overload exists for the given arguments,
      * or otherwise returns the resolved instruction node.
      * Annotation data, line number information, and the condition still need to be set by the caller.
      */
     [[nodiscard]] virtual tree::One<semantic::Instruction> resolve_instruction(
-        const std::string &name, const values::Values &args) const;
+        const std::string& name, const values::Values& args) const;
 
     /**
      * Registers an instruction type.
      */
-    virtual void register_instruction(const instruction::Instruction &instruction);
+    virtual void register_instruction(const instruction::Instruction& instruction);
 
     /**
      * Convenience method for registering an instruction type.
      * The arguments are passed straight to instruction::Instruction's constructor.
      */
-    virtual void register_instruction(const std::string &name, const std::optional<std::string> &param_types);
+    virtual void register_instruction(const std::string& name, const std::optional<std::string>& operand_types);
 };
 
 }  // namespace cqasm::v3x::analyzer
diff --git a/include/v3x/CustomErrorListener.hpp b/include/libqasm/v3x/antlr_custom_error_listener.hpp
similarity index 52%
rename from include/v3x/CustomErrorListener.hpp
rename to include/libqasm/v3x/antlr_custom_error_listener.hpp
index 949818291..39d172c27 100644
--- a/include/v3x/CustomErrorListener.hpp
+++ b/include/libqasm/v3x/antlr_custom_error_listener.hpp
@@ -8,18 +8,18 @@
 
 namespace cqasm::v3x::parser {
 
-class CustomErrorListener : public antlr4::BaseErrorListener {
+class AntlrCustomErrorListener : public antlr4::BaseErrorListener {
     /**
      * Name of the file being parsed.
      */
     std::optional<std::string> file_name_;
 
-    void syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol, size_t line,
-        size_t charPositionInLine, const std::string &msg, std::exception_ptr e) override;
+    void syntaxError(antlr4::Recognizer* recognizer, antlr4::Token* offendingSymbol, size_t line,
+        size_t charPositionInLine, const std::string& msg, std::exception_ptr e) override;
 
 public:
-    explicit CustomErrorListener(const std::optional<std::string> &file_name);
-    void syntaxError(size_t line, size_t charPositionInLine, const std::string &msg);
+    explicit AntlrCustomErrorListener(const std::optional<std::string>& file_name);
+    void syntaxError(size_t line, size_t charPositionInLine, const std::string& msg);
 };
 
 }  // namespace cqasm::v3x::parser
diff --git a/include/libqasm/v3x/antlr_scanner.hpp b/include/libqasm/v3x/antlr_scanner.hpp
new file mode 100644
index 000000000..d3c6c14fc
--- /dev/null
+++ b/include/libqasm/v3x/antlr_scanner.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <memory>  // unique_ptr
+#include <string>
+
+#include "libqasm/v3x/parse_result.hpp"
+#include "libqasm/v3x/syntactic_analyzer_base.hpp"
+
+namespace antlr4 {
+class ANTLRInputStream;
+}
+namespace cqasm::v3x::parser {
+class AntlrCustomErrorListener;
+}
+
+namespace cqasm::v3x::parser {
+
+struct ScannerAdaptor {
+    virtual ~ScannerAdaptor();
+
+    virtual cqasm::v3x::parser::ParseResult parse() = 0;
+};
+
+class AntlrScanner : public ScannerAdaptor {
+    std::unique_ptr<BaseSyntacticAnalyzer> build_visitor_up_;
+    std::unique_ptr<AntlrCustomErrorListener> error_listener_up_;
+
+protected:
+    cqasm::v3x::parser::ParseResult parse_(antlr4::ANTLRInputStream& is);
+
+public:
+    AntlrScanner(std::unique_ptr<BaseSyntacticAnalyzer> build_visitor_up,
+        std::unique_ptr<AntlrCustomErrorListener> error_listener_up);
+
+    ~AntlrScanner() override;
+
+    cqasm::v3x::parser::ParseResult parse() override = 0;
+};
+
+class FileAntlrScanner : public AntlrScanner {
+    std::string file_path_;
+
+public:
+    FileAntlrScanner(std::unique_ptr<BaseSyntacticAnalyzer> build_visitor_up,
+        std::unique_ptr<AntlrCustomErrorListener> error_listener_up, const std::string& file_path);
+
+    ~FileAntlrScanner() override;
+
+    cqasm::v3x::parser::ParseResult parse() override;
+};
+
+class StringAntlrScanner : public AntlrScanner {
+    std::string data_;
+
+public:
+    StringAntlrScanner(std::unique_ptr<BaseSyntacticAnalyzer> build_visitor_up,
+        std::unique_ptr<AntlrCustomErrorListener> error_listener_up, const std::string& data);
+
+    ~StringAntlrScanner() override;
+
+    cqasm::v3x::parser::ParseResult parse() override;
+};
+
+}  // namespace cqasm::v3x::parser
diff --git a/include/v3x/cqasm-ast.hpp b/include/libqasm/v3x/ast.hpp
similarity index 65%
rename from include/v3x/cqasm-ast.hpp
rename to include/libqasm/v3x/ast.hpp
index 372e44c18..d8974da76 100644
--- a/include/v3x/cqasm-ast.hpp
+++ b/include/libqasm/v3x/ast.hpp
@@ -5,5 +5,5 @@
 
 #pragma once
 
-#include "cqasm-string-builder.hpp"
-#include "v3x/cqasm-ast-gen.hpp"
+#include "libqasm/string_builder.hpp"
+#include "libqasm/v3x/ast_generated.hpp"
diff --git a/include/v3x/cqasm-core-function.hpp b/include/libqasm/v3x/core_function.hpp
similarity index 76%
rename from include/v3x/cqasm-core-function.hpp
rename to include/libqasm/v3x/core_function.hpp
index c55fa531d..e5030c38f 100644
--- a/include/v3x/cqasm-core-function.hpp
+++ b/include/libqasm/v3x/core_function.hpp
@@ -9,8 +9,8 @@
 
 #include <string>
 
-#include "cqasm-tree.hpp"
-#include "cqasm-types.hpp"
+#include "libqasm/tree.hpp"
+#include "libqasm/v3x/types.hpp"
 
 namespace cqasm::v3x::function {
 
@@ -49,17 +49,17 @@ class CoreFunction : public tree::Base {
      * param_types is a shorthand type specification string as parsed by cqasm::types::from_spec().
      * return_type is a shorthand type specification char as parsed by cqasm::types::from_spec().
      */
-    CoreFunction(std::string name, const std::string &param_types, const char return_type);
+    CoreFunction(std::string name, const std::string& param_types, const char return_type);
 
     CoreFunction() = default;
     ~CoreFunction() override = default;
-    CoreFunction(const CoreFunction &t) = default;
-    CoreFunction(CoreFunction &&t) noexcept = default;
-    CoreFunction &operator=(const CoreFunction &t) = default;
-    CoreFunction &operator=(CoreFunction &&t) noexcept = default;
+    CoreFunction(const CoreFunction& t) = default;
+    CoreFunction(CoreFunction&& t) noexcept = default;
+    CoreFunction& operator=(const CoreFunction& t) = default;
+    CoreFunction& operator=(CoreFunction&& t) noexcept = default;
 
-    bool operator==(const CoreFunction &rhs) const;
-    inline bool operator!=(const CoreFunction &rhs) const { return !(*this == rhs); }
+    bool operator==(const CoreFunction& rhs) const;
+    inline bool operator!=(const CoreFunction& rhs) const { return !(*this == rhs); }
 };
 
 /**
@@ -70,21 +70,21 @@ using CoreFunctionRef = tree::Maybe<CoreFunction>;
 /**
  * Stream << overload for functions.
  */
-std::ostream &operator<<(std::ostream &os, const CoreFunction &function);
+std::ostream& operator<<(std::ostream& os, const CoreFunction& function);
 
 /**
  * Stream << overload for function references.
  */
-std::ostream &operator<<(std::ostream &os, const CoreFunctionRef &function);
+std::ostream& operator<<(std::ostream& os, const CoreFunctionRef& function);
 
 }  // namespace cqasm::v3x::function
 
 namespace cqasm::v3x::primitives {
 
 template <>
-void serialize(const function::CoreFunctionRef &obj, ::tree::cbor::MapWriter &map);
+void serialize(const function::CoreFunctionRef& obj, ::tree::cbor::MapWriter& map);
 template <>
-function::CoreFunctionRef deserialize(const ::tree::cbor::MapReader &map);
+function::CoreFunctionRef deserialize(const ::tree::cbor::MapReader& map);
 
 }  // namespace cqasm::v3x::primitives
 
diff --git a/include/v3x/cqasm.hpp b/include/libqasm/v3x/cqasm.hpp
similarity index 72%
rename from include/v3x/cqasm.hpp
rename to include/libqasm/v3x/cqasm.hpp
index d86a16e2a..c1820aa25 100644
--- a/include/v3x/cqasm.hpp
+++ b/include/libqasm/v3x/cqasm.hpp
@@ -7,9 +7,9 @@
 
 #include <optional>
 
-#include "cqasm-tree.hpp"
-#include "v3x/cqasm-analyzer.hpp"
-#include "v3x/cqasm-semantic.hpp"
+#include "libqasm/tree.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/semantic.hpp"
 
 /**
  * Namespace for the cQASM v3x API.
@@ -22,7 +22,7 @@ namespace cqasm::v3x {
  * dumping error messages to stderr and throwing an analyzer::AnalysisFailed on failure.
  */
 tree::One<cqasm::v3x::semantic::Program> analyze_file(
-    const std::string &file_path, const std::string &api_version = "3.0");
+    const std::string& file_path, const std::string& api_version = "3.0");
 
 /**
  * Parses and analyzes the given string with the default analyzer,
@@ -30,11 +30,11 @@ tree::One<cqasm::v3x::semantic::Program> analyze_file(
  * The optional file_name is only used for error messages.
  */
 tree::One<cqasm::v3x::semantic::Program> analyze_string(
-    const std::string &data, const std::optional<std::string> &file_name, const std::string &api_version = "3.0");
+    const std::string& data, const std::optional<std::string>& file_name, const std::string& api_version = "3.0");
 
 /**
  * Constructs an Analyzer object with the defaults for cQASM 3.0 already loaded into it.
  */
-analyzer::Analyzer default_analyzer(const std::string &api_version = "3.0");
+analyzer::Analyzer default_analyzer(const std::string& api_version = "3.0");
 
 }  // namespace cqasm::v3x
diff --git a/include/v3x/cqasm-py.hpp b/include/libqasm/v3x/cqasm_python.hpp
similarity index 87%
rename from include/v3x/cqasm-py.hpp
rename to include/libqasm/v3x/cqasm_python.hpp
index ee3f16626..a9093b91f 100644
--- a/include/v3x/cqasm-py.hpp
+++ b/include/libqasm/v3x/cqasm_python.hpp
@@ -10,6 +10,7 @@
 // We don't want SWIG to generate Python wrappers for the entire world.
 // Those headers are only included in the source file that provides the implementations.
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -86,7 +87,7 @@ class V3xAnalyzer {
      * The initial mappings and functions are not configurable.
      * The defaults for these are always used.
      */
-    V3xAnalyzer(const std::string &max_version = "3.0", bool without_defaults = false);
+    V3xAnalyzer(const std::string& max_version = "3.0", bool without_defaults = false);
 
     /**
      * Default destructor.
@@ -116,53 +117,53 @@ class V3xAnalyzer {
     /*
      * The arguments are passed directly to `instruction::Instruction`'s constructor.
      */
-    void register_instruction(const std::string &name, const std::string &param_types = "");
+    void register_instruction(const std::string& name, const std::optional<std::string>& param_types);
 
     /**
      * Parses a file containing a cQASM v3.0 program.
      */
-    static std::vector<std::string> parse_file(const std::string &file_name);
+    static std::vector<std::string> parse_file(const std::string& file_name);
 
     /**
      * Parses a file containing a cQASM v3.0 program.
      */
-    static std::string parse_file_to_json(const std::string &file_name);
+    static std::string parse_file_to_json(const std::string& file_name);
 
     /**
      * Parses a string containing a cQASM v3.0 program.
      *
      * `file_name` is optional. It is only used when reporting errors.
      */
-    static std::vector<std::string> parse_string(const std::string &data, const std::string &file_name = "");
+    static std::vector<std::string> parse_string(const std::string& data, const std::string& file_name = "");
 
     /**
      * Parses a string containing a cQASM v3.0 program.
      *
      * `file_name` is optional. It is only used when reporting errors.
      */
-    static std::string parse_string_to_json(const std::string &data, const std::string &file_name = "");
+    static std::string parse_string_to_json(const std::string& data, const std::string& file_name = "");
 
     /**
      * Parses and analyzes a file containing a cQASM v3.0 program.
      */
-    std::vector<std::string> analyze_file(const std::string &file_name) const;
+    std::vector<std::string> analyze_file(const std::string& file_name) const;
 
     /**
      * Parses and analyzes a file containing a cQASM v3.0 program.
      */
-    std::string analyze_file_to_json(const std::string &file_name) const;
+    std::string analyze_file_to_json(const std::string& file_name) const;
 
     /**
      * Parses and analyzes a string containing a cQASM v3.0 program.
      *
      * `file_name` is optional. It is only used when reporting errors.
      */
-    std::vector<std::string> analyze_string(const std::string &data, const std::string &file_name = "") const;
+    std::vector<std::string> analyze_string(const std::string& data, const std::string& file_name = "") const;
 
     /**
      * Parses and analyzes a string containing a cQASM v3.0 program.
      *
      * `file_name` is optional. It is only used when reporting errors.
      */
-    std::string analyze_string_to_json(const std::string &data, const std::string &file_name = "") const;
+    std::string analyze_string_to_json(const std::string& data, const std::string& file_name = "") const;
 };
diff --git a/include/v3x/cqasm-instruction.hpp b/include/libqasm/v3x/instruction.hpp
similarity index 56%
rename from include/v3x/cqasm-instruction.hpp
rename to include/libqasm/v3x/instruction.hpp
index 54a45c0c2..cf5c2473c 100644
--- a/include/v3x/cqasm-instruction.hpp
+++ b/include/libqasm/v3x/instruction.hpp
@@ -9,28 +9,26 @@
 
 #include <optional>
 
-#include "cqasm-types.hpp"
-#include "cqasm-values.hpp"
+#include "libqasm/v3x/types.hpp"
 
-namespace cqasm::v3x {
-
-namespace instruction {
+namespace cqasm::v3x::instruction {
 
 /**
- * Representation of an available instruction (also known as gate) in the instruction set,
- * without parameters bound to it
- * (note that libqasm cannot match instructions based on which qubits are used; you'll need to do that on your own).
+ * Representation of an available instruction in the instruction set.
+ * An instruction can be: a gate instruction or a non-gate instruction.
+ * A gate instruction can be a gate or a composition of gate modifiers acting on a unitary gate.
+ * A non-gate instruction can be a measure instruction, or a reset instruction.
  *
  * A number of these can be registered into libqasm by the program or library using it through
  * \ref cqasm::analyzer::Analyzer::register_instruction(const instruction::Instruction&) "register_instruction",
  * to inform libqasm of the supported instruction set.
- * For each instruction, libqasm needs to know its name, which parameters it expects,
+ * For each instruction, libqasm needs to know its name, which operands it expects,
  * and a few extra flags in order to be able to resolve the instruction and check for errors in the cQASM file.
  * The resolved instruction type is part of the cqasm::semantic::Instruction node present in the semantic tree
  * returned through the parse result structure.
  *
  * Note that it is legal to have multiple instructions with the same name,
- * as long as they can be distinguished through their parameter types (i.e. instructions can be overloaded).
+ * as long as they can be distinguished through their operand types (i.e. instructions can be overloaded).
  */
 class Instruction : public tree::Base {
 public:
@@ -40,19 +38,19 @@ class Instruction : public tree::Base {
     std::string name;
 
     /**
-     * The vector of parameter types that this instruction expects.
+     * The vector of operand types that this instruction expects.
      */
-    types::Types param_types;
+    types::Types operand_types;
 
     /**
      * Creates a new instruction.
-     * param_types is a shorthand type specification string as parsed by cqasm::types::from_spec().
-     * If you need more control, you can also manipulate param_types directly.
+     * operand_types is a shorthand type specification string as parsed by cqasm::types::from_spec().
      */
-    Instruction(std::string name, const std::optional<std::string> &param_types);
+    Instruction() = default;
+    Instruction(std::string name, const std::optional<std::string>& operand_types);
 
-    bool operator==(const Instruction &rhs) const;
-    inline bool operator!=(const Instruction &rhs) const { return !(*this == rhs); }
+    bool operator==(const Instruction& rhs) const;
+    inline bool operator!=(const Instruction& rhs) const { return !(*this == rhs); }
 };
 
 /**
@@ -63,25 +61,23 @@ using InstructionRef = tree::Maybe<Instruction>;
 /**
  * Stream << overload for instructions.
  */
-std::ostream &operator<<(std::ostream &os, const Instruction &instruction);
+std::ostream& operator<<(std::ostream& os, const Instruction& instruction);
 
 /**
  * Stream << overload for instruction references.
  */
-std::ostream &operator<<(std::ostream &os, const InstructionRef &instruction);
+std::ostream& operator<<(std::ostream& os, const InstructionRef& instruction);
 
-}  // namespace instruction
+}  // namespace cqasm::v3x::instruction
 
-namespace primitives {
+namespace cqasm::v3x::primitives {
 
 template <>
-void serialize(const instruction::InstructionRef &obj, ::tree::cbor::MapWriter &map);
+void serialize(const instruction::InstructionRef& obj, ::tree::cbor::MapWriter& map);
 template <>
-instruction::InstructionRef deserialize(const ::tree::cbor::MapReader &map);
-
-}  // namespace primitives
+instruction::InstructionRef deserialize(const ::tree::cbor::MapReader& map);
 
-}  // namespace cqasm::v3x
+}  // namespace cqasm::v3x::primitives
 
 /**
  * std::ostream support via fmt (uses operator<<).
diff --git a/include/libqasm/v3x/instruction_set.hpp b/include/libqasm/v3x/instruction_set.hpp
new file mode 100644
index 000000000..e3592336e
--- /dev/null
+++ b/include/libqasm/v3x/instruction_set.hpp
@@ -0,0 +1,80 @@
+#pragma once
+
+#include <map>
+#include <optional>
+#include <set>
+#include <string>
+#include <utility>  // pair
+
+namespace cqasm::v3x::instruction {
+
+using InstructionNameT = std::string;
+using ParamTypeT = std::optional<char>;
+using OperandTypesT = std::optional<std::string>;
+using KeyT = InstructionNameT;
+using ValueT = std::pair<ParamTypeT, OperandTypesT>;
+using InstructionMapT = std::multimap<KeyT, ValueT>;
+using InstructionListT = std::set<InstructionNameT>;
+using GateModifierMapT = std::map<KeyT, ParamTypeT>;
+
+class InstructionSet {
+    InstructionMapT named_gate_map;
+    InstructionMapT non_gate_map;
+    GateModifierMapT gate_modifier_map;
+
+    InstructionListT single_qubit_named_gate_list;
+    InstructionListT two_qubit_named_gate_list;
+
+    InstructionSet();
+
+public:
+    // The use of '1q' and '2q' as gate prefixes avoids any possible conflict with user defined gates
+    // since an identifier cannot start with a number
+    std::string single_qubit_gate_composition_prefix = "1q";
+    std::string two_qubit_gate_composition_prefix = "2q";
+
+    std::string measure_name = "measure";
+    std::string reset_name = "reset";
+
+    std::string inv_gate_modifier_name = "inv";
+    std::string pow_gate_modifier_name = "pow";
+    std::string ctrl_gate_modifier_name = "ctrl";
+
+public:
+    [[nodiscard]] static InstructionSet& get_instance();
+
+    [[nodiscard]] const InstructionMapT& get_named_gate_map() const;
+    [[nodiscard]] const InstructionMapT& get_non_gate_map() const;
+    [[nodiscard]] const GateModifierMapT& get_gate_modifier_map() const;
+    [[nodiscard]] const InstructionListT& get_single_qubit_named_gate_list() const;
+    [[nodiscard]] const InstructionListT& get_two_qubit_named_gate_list() const;
+
+    [[nodiscard]] bool is_single_qubit_named_gate(const std::string& name) const;
+    [[nodiscard]] bool is_two_qubit_named_gate(const std::string& name) const;
+    [[nodiscard]] bool is_named_gate(const std::string& name) const;
+
+    [[nodiscard]] bool is_single_qubit_gate_composition(const std::string& name) const;
+    [[nodiscard]] bool is_two_qubit_gate_composition(const std::string& name) const;
+    [[nodiscard]] bool is_gate_composition(const std::string& name) const;
+
+    [[nodiscard]] bool is_single_qubit_gate(const std::string& name) const;
+    [[nodiscard]] bool is_two_qubit_gate(const std::string& name) const;
+    [[nodiscard]] bool is_gate(const std::string& name) const;
+
+    [[nodiscard]] bool is_measure(const std::string& name) const;
+    [[nodiscard]] bool is_reset(const std::string& name) const;
+    [[nodiscard]] bool is_non_gate(const std::string& name) const;
+
+    [[nodiscard]] bool is_inv_gate_modifier(const std::string& name) const;
+    [[nodiscard]] bool is_pow_gate_modifier(const std::string& name) const;
+    [[nodiscard]] bool is_ctrl_gate_modifier(const std::string& name) const;
+    [[nodiscard]] bool is_single_qubit_gate_modifier(const std::string& name) const;
+    [[nodiscard]] bool is_two_qubit_gate_modifier(const std::string& name) const;
+    [[nodiscard]] bool is_gate_modifier(const std::string& name) const;
+
+    [[nodiscard]] std::optional<char> get_named_gate_param_type(const std::string& name) const;
+    [[nodiscard]] std::optional<char> get_gate_modifier_param_type(const std::string& name) const;
+    [[nodiscard]] std::optional<char> get_instruction_param_type(const std::string& name) const;
+};
+
+}  // namespace cqasm::v3x::instruction
diff --git a/include/v3x/cqasm-parse-helper.hpp b/include/libqasm/v3x/parse_helper.hpp
similarity index 73%
rename from include/v3x/cqasm-parse-helper.hpp
rename to include/libqasm/v3x/parse_helper.hpp
index d0eeee072..95649cafb 100644
--- a/include/v3x/cqasm-parse-helper.hpp
+++ b/include/libqasm/v3x/parse_helper.hpp
@@ -10,9 +10,9 @@
 #include <optional>
 #include <string>
 
-#include "cqasm-annotations.hpp"
-#include "v3x/ScannerAntlr.hpp"
-#include "v3x/cqasm-parse-result.hpp"
+#include "libqasm/annotations.hpp"
+#include "libqasm/v3x/antlr_scanner.hpp"
+#include "libqasm/v3x/parse_result.hpp"
 
 namespace cqasm::v3x::parser {
 
@@ -22,13 +22,13 @@ using SourceLocation = annotations::SourceLocation;
  * Parse using the given file path.
  * Throws a ParseError if this fails.
  */
-ParseResult parse_file(const std::string &file_path, const std::optional<std::string> &file_name);
+ParseResult parse_file(const std::string& file_path, const std::optional<std::string>& file_name);
 
 /**
  * Parse the given string.
  * A file_name may be given in addition for use within error messages.
  */
-ParseResult parse_string(const std::string &data, const std::optional<std::string> &file_name);
+ParseResult parse_string(const std::string& data, const std::optional<std::string>& file_name);
 
 /**
  * Internal helper class for parsing cQASM files.
@@ -45,7 +45,7 @@ class ParseHelper {
     std::string file_name_;
 
 public:
-    explicit ParseHelper(std::unique_ptr<ScannerAdaptor> scanner_up, const std::optional<std::string> &file_name);
+    explicit ParseHelper(std::unique_ptr<ScannerAdaptor> scanner_up, const std::optional<std::string>& file_name);
 
     /**
      * Does the actual parsing.
diff --git a/include/v3x/cqasm-parse-result.hpp b/include/libqasm/v3x/parse_result.hpp
similarity index 92%
rename from include/v3x/cqasm-parse-result.hpp
rename to include/libqasm/v3x/parse_result.hpp
index cdd81acea..8fa9f89f5 100644
--- a/include/v3x/cqasm-parse-result.hpp
+++ b/include/libqasm/v3x/parse_result.hpp
@@ -7,9 +7,9 @@
 #include <string>
 #include <vector>
 
-#include "cqasm-annotations.hpp"
-#include "cqasm-ast.hpp"
-#include "cqasm-error.hpp"
+#include "libqasm/annotations.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/v3x/ast.hpp"
 
 /**
  * Namespace for the parser functions and classes.
diff --git a/include/v3x/cqasm-primitives.hpp b/include/libqasm/v3x/primitives.hpp
similarity index 65%
rename from include/v3x/cqasm-primitives.hpp
rename to include/libqasm/v3x/primitives.hpp
index eaf334c30..fae2d49da 100644
--- a/include/v3x/cqasm-primitives.hpp
+++ b/include/libqasm/v3x/primitives.hpp
@@ -13,8 +13,8 @@
 #include <string>
 #include <vector>
 
-#include "cqasm-version.hpp"
-#include "tree-cbor.hpp"
+#include "libqasm/tree.hpp"
+#include "libqasm/version.hpp"
 
 namespace cqasm::v3x::primitives {
 
@@ -34,13 +34,13 @@ T initialize() {
  * Serializes the given primitive object to CBOR.
  */
 template <typename T>
-void serialize(const T &obj, ::tree::cbor::MapWriter &map);
+void serialize(const T& obj, ::tree::cbor::MapWriter& map);
 
 /**
  * Deserializes the given primitive object from CBOR.
  */
 template <typename T>
-T deserialize(const ::tree::cbor::MapReader &map);
+T deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * String primitive used within the AST and semantic trees.
@@ -49,9 +49,9 @@ using Str = std::string;
 template <>
 Str initialize<Str>();
 template <>
-void serialize(const Str &obj, ::tree::cbor::MapWriter &map);
+void serialize(const Str& obj, ::tree::cbor::MapWriter& map);
 template <>
-Str deserialize(const ::tree::cbor::MapReader &map);
+Str deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * Axis primitive used within the semantic trees.
@@ -61,14 +61,14 @@ struct Axis {
     double y = 0.0;
     double z = 0.0;
 
-    [[nodiscard]] bool operator==(const Axis &rhs) const = default;
+    [[nodiscard]] bool operator==(const Axis& rhs) const = default;
 };
 template <>
 Axis initialize<Axis>();
 template <>
-void serialize(const Axis &obj, ::tree::cbor::MapWriter &map);
+void serialize(const Axis& obj, ::tree::cbor::MapWriter& map);
 template <>
-Axis deserialize(const ::tree::cbor::MapReader &map);
+Axis deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * Boolean primitive used within the semantic trees. Defaults to false.
@@ -77,9 +77,9 @@ using Bool = bool;
 template <>
 Bool initialize<Bool>();
 template <>
-void serialize(const Bool &obj, ::tree::cbor::MapWriter &map);
+void serialize(const Bool& obj, ::tree::cbor::MapWriter& map);
 template <>
-Bool deserialize(const ::tree::cbor::MapReader &map);
+Bool deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * Integer primitive used within the AST and semantic trees.
@@ -88,9 +88,9 @@ using Int = std::int64_t;
 template <>
 Int initialize<Int>();
 template <>
-void serialize(const Int &obj, ::tree::cbor::MapWriter &map);
+void serialize(const Int& obj, ::tree::cbor::MapWriter& map);
 template <>
-Int deserialize(const ::tree::cbor::MapReader &map);
+Int deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * Float number primitive used within the AST and semantic trees.
@@ -99,32 +99,32 @@ using Float = double;
 template <>
 Float initialize<Float>();
 template <>
-void serialize(const Float &obj, ::tree::cbor::MapWriter &map);
+void serialize(const Float& obj, ::tree::cbor::MapWriter& map);
 template <>
-Float deserialize(const ::tree::cbor::MapReader &map);
+Float deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * Complex number primitive used within the semantic trees.
  */
 using Complex = std::complex<double>;
 template <>
-void serialize(const Complex &obj, ::tree::cbor::MapWriter &map);
+void serialize(const Complex& obj, ::tree::cbor::MapWriter& map);
 template <>
-Complex deserialize(const ::tree::cbor::MapReader &map);
+Complex deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * Version number primitive used within the AST and semantic trees.
  */
 using Version = version::Version;
 template <>
-void serialize(const Version &obj, ::tree::cbor::MapWriter &map);
+void serialize(const Version& obj, ::tree::cbor::MapWriter& map);
 template <>
-Version deserialize(const ::tree::cbor::MapReader &map);
+Version deserialize(const ::tree::cbor::MapReader& map);
 
 /**
  * Stream << overload for axis nodes.
  */
-std::ostream &operator<<(std::ostream &os, const Axis &axis);
+std::ostream& operator<<(std::ostream& os, const Axis& axis);
 
 }  // namespace cqasm::v3x::primitives
 
diff --git a/include/v3x/register-consteval-core-functions.hpp b/include/libqasm/v3x/register_consteval_core_functions.hpp
similarity index 95%
rename from include/v3x/register-consteval-core-functions.hpp
rename to include/libqasm/v3x/register_consteval_core_functions.hpp
index b2236fbaa..74d7d11ef 100644
--- a/include/v3x/register-consteval-core-functions.hpp
+++ b/include/libqasm/v3x/register_consteval_core_functions.hpp
@@ -4,8 +4,8 @@
 #include <functional>
 #include <numeric>
 
-#include "v3x/cqasm-analyzer.hpp"
-#include "v3x/cqasm-primitives.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/primitives.hpp"
 
 namespace primitives = cqasm::v3x::primitives;
 
@@ -25,7 +25,7 @@ namespace primitives = cqasm::v3x::primitives;
 namespace cqasm::v3x::function {
 
 /**
- * Function with constant parameters 
+ * Function with constant parameters
  */
 template <typename ReturnValue, typename ParamValue, auto F>
 struct f_cp {
@@ -38,7 +38,7 @@ struct f_cp {
  */
 template <typename ReturnValue, typename ParamValue, auto F>
 struct uf_cp : public f_cp<ReturnValue, ParamValue, F> {
-    values::Value operator()(const values::Values &vs) const {
+    values::Value operator()(const values::Values& vs) const {
         values::check_const(vs);
         auto arg = vs[0].as<ParamValue>()->value;
         return tree::make<ReturnValue>(F(arg));
@@ -46,11 +46,11 @@ struct uf_cp : public f_cp<ReturnValue, ParamValue, F> {
 };
 
 /**
- * Binary function with constant parameters 
+ * Binary function with constant parameters
  */
 template <typename ReturnValue, typename ParamValue, auto F>
 struct bf_cp : public f_cp<ReturnValue, ParamValue, F> {
-    values::Value operator()(const values::Values &vs) const {
+    values::Value operator()(const values::Values& vs) const {
         values::check_const(vs);
         auto a = vs[0].as<ParamValue>()->value;
         auto b = vs[1].as<ParamValue>()->value;
@@ -59,11 +59,11 @@ struct bf_cp : public f_cp<ReturnValue, ParamValue, F> {
 };
 
 /**
- * Ternary function with constant parameters 
+ * Ternary function with constant parameters
  */
 template <typename ParamValue, auto F>
 struct tf_cp : public f_cp<ParamValue, ParamValue, F> {
-    values::Value operator()(const values::Values &vs) const {
+    values::Value operator()(const values::Values& vs) const {
         values::check_const(vs);
         auto condition = vs[0]->as_const_bool()->value;
         auto if_true = vs[1].as<ParamValue>()->value;
@@ -170,6 +170,6 @@ constexpr auto fn_abs_i = uf_cp<values::ConstInt, values::ConstInt, abs>{};
  * That is, functions supported by the language, and that can be evaluated at compile time.
  * For functions supported by the language that cannot be evaluated at compile time, see CoreFunction.
  */
-void register_consteval_core_functions(analyzer::Analyzer *analyzer);
+void register_consteval_core_functions(analyzer::Analyzer* analyzer);
 
 }  // namespace cqasm::v3x::function
diff --git a/include/v3x/register-instructions.hpp b/include/libqasm/v3x/register_instructions.hpp
similarity index 60%
rename from include/v3x/register-instructions.hpp
rename to include/libqasm/v3x/register_instructions.hpp
index d04ab3af7..baf69300c 100644
--- a/include/v3x/register-instructions.hpp
+++ b/include/libqasm/v3x/register_instructions.hpp
@@ -1,12 +1,12 @@
 #pragma once
 
-#include "cqasm-analyzer.hpp"
+#include "libqasm/v3x/analyzer.hpp"
 
 namespace cqasm::v3x::instruction {
 
 /**
  * Registers the cQASM 3.0 instruction set.
  */
-void register_instructions(analyzer::Analyzer *analyzer);
+void register_instructions(analyzer::Analyzer* analyzer);
 
 }  // namespace cqasm::v3x::instruction
diff --git a/include/v3x/cqasm-resolver.hpp b/include/libqasm/v3x/resolver.hpp
similarity index 62%
rename from include/v3x/cqasm-resolver.hpp
rename to include/libqasm/v3x/resolver.hpp
index 877ca0e8e..f70796a1e 100644
--- a/include/v3x/cqasm-resolver.hpp
+++ b/include/libqasm/v3x/resolver.hpp
@@ -16,17 +16,22 @@
 #include <memory>
 #include <unordered_map>
 
-#include "cqasm-core-function.hpp"
-#include "cqasm-error.hpp"
-#include "cqasm-instruction.hpp"
-#include "cqasm-overload.hpp"
-#include "cqasm-semantic.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/overload.hpp"
+#include "libqasm/v3x/core_function.hpp"
+#include "libqasm/v3x/instruction.hpp"
+#include "libqasm/v3x/semantic.hpp"
 
 /**
  * Namespace for everything to do with name and overload resolution in cQASM.
  */
 namespace cqasm::v3x::resolver {
 
+using Type = types::Type;
+using Types = types::Types;
+using Value = values::Value;
+using Values = values::Values;
+
 //-----------------//
 // Analysis errors //
 //-----------------//
@@ -51,22 +56,24 @@ CQASM_ANALYSIS_ERROR(ResolutionFailure);
 //------------------------//
 
 template <class T>
-struct OverloadedNameResolver : public cqasm::overload::OverloadedNameResolver<T, types::TypeBase, values::ValueBase> {
+using BaseOverladedNameResolver = cqasm::overload::OverloadedNameResolver<T, types::TypeBase, values::ValueBase>;
+
+template <class T>
+struct OverloadedNameResolver : public BaseOverladedNameResolver<T> {
     virtual ~OverloadedNameResolver() = default;
 
-    void add_overload(const std::string &name, const T &tag, const types::Types &param_types) override {
-        cqasm::overload::OverloadedNameResolver<T, types::TypeBase, values::ValueBase>::add_overload(
-            name, tag, param_types);
+    void add_overload(const std::string& name, const T& tag, const types::Types& param_types) override {
+        BaseOverladedNameResolver<T>::add_overload(name, tag, param_types);
     }
 
-    [[nodiscard]] std::pair<T, values::Values> resolve(const std::string &name, const values::Values &args) override {
+    [[nodiscard]] std::pair<T, Values> resolve(const std::string& name, const Values& args) override {
         try {
-            return cqasm::overload::OverloadedNameResolver<T, types::TypeBase, values::ValueBase>::resolve(name, args);
-        } catch (const cqasm::overload::NameResolutionFailure &) {
+            return BaseOverladedNameResolver<T>::resolve(name, args);
+        } catch (const cqasm::overload::NameResolutionFailure&) {
             throw NameResolutionFailure{ fmt::format("failed to resolve '{}'", name) };
-        } catch (const cqasm::overload::OverloadResolutionFailure &) {
+        } catch (const cqasm::overload::OverloadResolutionFailure&) {
             throw OverloadResolutionFailure{ fmt::format(
-                "failed to resolve overload for '{}' with argument pack {}", name, values::types_of(args)) };
+                "failed to resolve overload for '{}' with argument pack ({})", name, types_of(args)) };
         }
     }
 };
@@ -79,19 +86,19 @@ struct OverloadedNameResolver : public cqasm::overload::OverloadedNameResolver<T
  * Table of all variables within a certain scope.
  */
 class VariableTable {
-    std::unordered_map<std::string, values::Value> table;
+    std::unordered_map<std::string, Value> table;
 
 public:
     /**
      * Adds a variable.
      */
-    void add(const std::string &name, const values::Value &value);
+    void add(const std::string& name, const Value& value);
 
     /**
      * Resolves a variable.
      * Throws NameResolutionFailure if no variable by the given name exists.
      */
-    values::Value resolve(const std::string &name) const;
+    [[nodiscard]] Value resolve(const std::string& name) const;
 };
 
 //----------------------------//
@@ -102,7 +109,7 @@ class VariableTable {
  * An overload of a function supported by the language, and that can can be evaluated at compile time.
  * This has to be a function accepting only constant arguments.
  */
-using ConstEvalCoreFunction = std::function<values::Value(const values::Values &)>;
+using ConstEvalCoreFunction = std::function<Value(const Values&)>;
 
 /**
  * Table of overloads of functions supported by the language, and that can be evaluated at compile time.
@@ -115,10 +122,10 @@ class ConstEvalCoreFunctionTable {
 public:
     ConstEvalCoreFunctionTable();
     ~ConstEvalCoreFunctionTable();
-    ConstEvalCoreFunctionTable(const ConstEvalCoreFunctionTable &t);
-    ConstEvalCoreFunctionTable(ConstEvalCoreFunctionTable &&t) noexcept;
-    ConstEvalCoreFunctionTable &operator=(const ConstEvalCoreFunctionTable &t);
-    ConstEvalCoreFunctionTable &operator=(ConstEvalCoreFunctionTable &&t) noexcept;
+    ConstEvalCoreFunctionTable(const ConstEvalCoreFunctionTable& t);
+    ConstEvalCoreFunctionTable(ConstEvalCoreFunctionTable&& t) noexcept;
+    ConstEvalCoreFunctionTable& operator=(const ConstEvalCoreFunctionTable& t);
+    ConstEvalCoreFunctionTable& operator=(ConstEvalCoreFunctionTable&& t) noexcept;
 
     /**
      * Registers a function.
@@ -133,7 +140,7 @@ class ConstEvalCoreFunctionTable {
      * However, the overload resolution engine will always use the last applicable overload it finds,
      * so adding does have the effect of overriding.
      */
-    void add(const std::string &name, const types::Types &param_types, const ConstEvalCoreFunction &function);
+    void add(const std::string& name, const Types& param_types, const ConstEvalCoreFunction& function);
 
     /**
      * Resolves a function.
@@ -141,7 +148,7 @@ class ConstEvalCoreFunctionTable {
      * OverloadResolutionFailure if no overload of the function exists for the given arguments, or otherwise
      * returns the value returned by the function.
      */
-    [[nodiscard]] values::Value resolve(const std::string &name, const values::Values &args) const;
+    [[nodiscard]] Value resolve(const std::string& name, const Values& args) const;
 };
 
 //------------------//
@@ -159,23 +166,32 @@ class InstructionTable {
 public:
     InstructionTable();
     ~InstructionTable();
-    InstructionTable(const InstructionTable &t);
-    InstructionTable(InstructionTable &&t) noexcept;
-    InstructionTable &operator=(const InstructionTable &t);
-    InstructionTable &operator=(InstructionTable &&t) noexcept;
+    InstructionTable(const InstructionTable& t);
+    InstructionTable(InstructionTable&& t) noexcept;
+    InstructionTable& operator=(const InstructionTable& t);
+    InstructionTable& operator=(InstructionTable&& t) noexcept;
 
     /**
      * Registers an instruction type.
      */
-    void add(const instruction::Instruction &type);
+    void add(const instruction::Instruction& type);
+
+    /**
+     * Resolves a GateInstruction type.
+     * Throws NameResolutionFailure if no instruction by the given name exists,
+     * OverloadResolutionFailure if no overload exists for the given arguments, or otherwise
+     * returns the resolved instruction node.
+     */
+    [[nodiscard]] tree::One<semantic::Instruction> resolve(
+        const std::string& name, const tree::One<semantic::Gate>& gate, const Values& args) const;
 
     /**
-     * Resolves an instruction.
+     * Resolves a NonGateInstruction type.
      * Throws NameResolutionFailure if no instruction by the given name exists,
      * OverloadResolutionFailure if no overload exists for the given arguments, or otherwise
      * returns the resolved instruction node.
      */
-    [[nodiscard]] tree::One<semantic::Instruction> resolve(const std::string &name, const values::Values &args) const;
+    [[nodiscard]] tree::One<semantic::Instruction> resolve(const std::string& name, const Values& args) const;
 };
 
 }  // namespace cqasm::v3x::resolver
diff --git a/include/v3x/cqasm-scope.hpp b/include/libqasm/v3x/scope.hpp
similarity index 88%
rename from include/v3x/cqasm-scope.hpp
rename to include/libqasm/v3x/scope.hpp
index c36cda5b3..69b4f5a69 100644
--- a/include/v3x/cqasm-scope.hpp
+++ b/include/libqasm/v3x/scope.hpp
@@ -1,8 +1,8 @@
 #pragma once
 
-#include "cqasm-tree.hpp"
-#include "v3x/cqasm-resolver.hpp"
-#include "v3x/cqasm-semantic-gen.hpp"
+#include "libqasm/tree.hpp"
+#include "libqasm/v3x/resolver.hpp"
+#include "libqasm/v3x/semantic_generated.hpp"
 
 namespace cqasm::v3x::analyzer {
 
diff --git a/include/libqasm/v3x/semantic.hpp b/include/libqasm/v3x/semantic.hpp
new file mode 100644
index 000000000..31a92d02c
--- /dev/null
+++ b/include/libqasm/v3x/semantic.hpp
@@ -0,0 +1,12 @@
+/** \file
+ * Defines the types for the cQASM \ref cqasm::v3x::semantic "semantic tree",
+ * based on the classes from cqasm::tree.
+ *
+ * This file is currently just a link to the \ref v3x/semantic_generated.hpp "generated file",
+ * aside from one forward declaration in \ref v3x/semantic_helper.hpp.
+ */
+
+#pragma once
+
+#include "libqasm/v3x/semantic_generated.hpp"
+#include "libqasm/v3x/semantic_helper.hpp"
diff --git a/include/v3x/AnalyzeTreeGenAstVisitor.hpp b/include/libqasm/v3x/semantic_analyzer.hpp
similarity index 52%
rename from include/v3x/AnalyzeTreeGenAstVisitor.hpp
rename to include/libqasm/v3x/semantic_analyzer.hpp
index c143def9e..7f687f678 100644
--- a/include/v3x/AnalyzeTreeGenAstVisitor.hpp
+++ b/include/libqasm/v3x/semantic_analyzer.hpp
@@ -5,70 +5,70 @@
 #include <tuple>
 #include <utility>  // pair
 
-#include "v3x/cqasm-analyzer.hpp"
-#include "v3x/cqasm-ast-gen.hpp"
-#include "v3x/cqasm-semantic-gen.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/ast_generated.hpp"
+#include "libqasm/v3x/semantic_generated.hpp"
 
 namespace cqasm::v3x::analyzer {
 
 using IndexT = values::ConstInt;
 using IndexListT = tree::Many<IndexT>;
 
-using GlobalBlockReturnT = std::tuple<tree::One<semantic::Block>, const tree::Any<semantic::Variable> &>;
+using GlobalBlockReturnT = std::tuple<tree::One<semantic::Block>, const tree::Any<semantic::Variable>&>;
 
-class AnalyzeTreeGenAstVisitor : public ast::Visitor<std::any> {
+class SemanticAnalyzer : public ast::Visitor<std::any> {
 protected:
-    Analyzer &analyzer_;
+    Analyzer& analyzer_;
     AnalysisResult result_;
 
 public:
-    explicit AnalyzeTreeGenAstVisitor(Analyzer &analyzer);
-
-    std::any visit_node(ast::Node &node) override;
-    std::any visit_program(ast::Program &node) override;
-    std::any visit_version(ast::Version &node) override;
-    std::any visit_annotated(ast::Annotated &node) override;
-    std::any visit_annotation_data(ast::AnnotationData &node) override;
-    std::any visit_global_block(ast::GlobalBlock &node) override;
-    std::any visit_variable(ast::Variable &node) override;
-    std::any visit_gate(ast::Gate &node) override;
-    std::any visit_measure_instruction(ast::MeasureInstruction &node) override;
-    std::any visit_reset_instruction(ast::ResetInstruction &node) override;
-    std::any visit_expression_list(ast::ExpressionList &node) override;
-    std::any visit_expression(ast::Expression &node) override;
-    std::any visit_unary_minus_expression(ast::UnaryMinusExpression &node) override;
-    std::any visit_bitwise_not_expression(ast::BitwiseNotExpression &node) override;
-    std::any visit_logical_not_expression(ast::LogicalNotExpression &node) override;
-    std::any visit_power_expression(ast::PowerExpression &node) override;
-    std::any visit_product_expression(ast::ProductExpression &node) override;
-    std::any visit_division_expression(ast::DivisionExpression &node) override;
-    std::any visit_modulo_expression(ast::ModuloExpression &node) override;
-    std::any visit_addition_expression(ast::AdditionExpression &node) override;
-    std::any visit_subtraction_expression(ast::SubtractionExpression &node) override;
-    std::any visit_shift_left_expression(ast::ShiftLeftExpression &node) override;
-    std::any visit_shift_right_expression(ast::ShiftRightExpression &node) override;
-    std::any visit_cmp_gt_expression(ast::CmpGtExpression &node) override;
-    std::any visit_cmp_lt_expression(ast::CmpLtExpression &node) override;
-    std::any visit_cmp_ge_expression(ast::CmpGeExpression &node) override;
-    std::any visit_cmp_le_expression(ast::CmpLeExpression &node) override;
-    std::any visit_cmp_eq_expression(ast::CmpEqExpression &node) override;
-    std::any visit_cmp_ne_expression(ast::CmpNeExpression &node) override;
-    std::any visit_bitwise_and_expression(ast::BitwiseAndExpression &node) override;
-    std::any visit_bitwise_xor_expression(ast::BitwiseXorExpression &node) override;
-    std::any visit_bitwise_or_expression(ast::BitwiseOrExpression &node) override;
-    std::any visit_logical_and_expression(ast::LogicalAndExpression &node) override;
-    std::any visit_logical_xor_expression(ast::LogicalXorExpression &node) override;
-    std::any visit_logical_or_expression(ast::LogicalOrExpression &node) override;
-    std::any visit_ternary_conditional_expression(ast::TernaryConditionalExpression &node) override;
-    std::any visit_function_call(ast::FunctionCall &node) override;
-    std::any visit_index(ast::Index &node) override;
-    std::any visit_index_list(ast::IndexList &node) override;
-    std::any visit_index_item(ast::IndexItem &node) override;
-    std::any visit_index_range(ast::IndexRange &ast) override;
-    std::any visit_identifier(ast::Identifier &node) override;
-    std::any visit_boolean_literal(ast::BooleanLiteral &node) override;
-    std::any visit_integer_literal(ast::IntegerLiteral &node) override;
-    std::any visit_float_literal(ast::FloatLiteral &node) override;
+    explicit SemanticAnalyzer(Analyzer& analyzer);
+
+    std::any visit_node(ast::Node& node) override;
+    std::any visit_program(ast::Program& node) override;
+    std::any visit_version(ast::Version& node) override;
+    std::any visit_annotated(ast::Annotated& node) override;
+    std::any visit_annotation_data(ast::AnnotationData& node) override;
+    std::any visit_global_block(ast::GlobalBlock& node) override;
+    std::any visit_variable(ast::Variable& node) override;
+    std::any visit_gate_instruction(ast::GateInstruction& node) override;
+    std::any visit_gate(ast::Gate& node) override;
+    std::any visit_non_gate_instruction(ast::NonGateInstruction& node) override;
+    std::any visit_expression_list(ast::ExpressionList& node) override;
+    std::any visit_expression(ast::Expression& node) override;
+    std::any visit_unary_minus_expression(ast::UnaryMinusExpression& node) override;
+    std::any visit_bitwise_not_expression(ast::BitwiseNotExpression& node) override;
+    std::any visit_logical_not_expression(ast::LogicalNotExpression& node) override;
+    std::any visit_power_expression(ast::PowerExpression& node) override;
+    std::any visit_product_expression(ast::ProductExpression& node) override;
+    std::any visit_division_expression(ast::DivisionExpression& node) override;
+    std::any visit_modulo_expression(ast::ModuloExpression& node) override;
+    std::any visit_addition_expression(ast::AdditionExpression& node) override;
+    std::any visit_subtraction_expression(ast::SubtractionExpression& node) override;
+    std::any visit_shift_left_expression(ast::ShiftLeftExpression& node) override;
+    std::any visit_shift_right_expression(ast::ShiftRightExpression& node) override;
+    std::any visit_cmp_gt_expression(ast::CmpGtExpression& node) override;
+    std::any visit_cmp_lt_expression(ast::CmpLtExpression& node) override;
+    std::any visit_cmp_ge_expression(ast::CmpGeExpression& node) override;
+    std::any visit_cmp_le_expression(ast::CmpLeExpression& node) override;
+    std::any visit_cmp_eq_expression(ast::CmpEqExpression& node) override;
+    std::any visit_cmp_ne_expression(ast::CmpNeExpression& node) override;
+    std::any visit_bitwise_and_expression(ast::BitwiseAndExpression& node) override;
+    std::any visit_bitwise_xor_expression(ast::BitwiseXorExpression& node) override;
+    std::any visit_bitwise_or_expression(ast::BitwiseOrExpression& node) override;
+    std::any visit_logical_and_expression(ast::LogicalAndExpression& node) override;
+    std::any visit_logical_xor_expression(ast::LogicalXorExpression& node) override;
+    std::any visit_logical_or_expression(ast::LogicalOrExpression& node) override;
+    std::any visit_ternary_conditional_expression(ast::TernaryConditionalExpression& node) override;
+    std::any visit_function_call(ast::FunctionCall& node) override;
+    std::any visit_index(ast::Index& node) override;
+    std::any visit_index_list(ast::IndexList& node) override;
+    std::any visit_index_item(ast::IndexItem& node) override;
+    std::any visit_index_range(ast::IndexRange& ast) override;
+    std::any visit_identifier(ast::Identifier& node) override;
+    std::any visit_boolean_literal(ast::BooleanLiteral& node) override;
+    std::any visit_integer_literal(ast::IntegerLiteral& node) override;
+    std::any visit_float_literal(ast::FloatLiteral& node) override;
 
 private:
     /**
@@ -77,7 +77,7 @@ class AnalyzeTreeGenAstVisitor : public ast::Visitor<std::any> {
      * or an array type SemanticTArray, which size is given by the syntactic type
      */
     template <typename SemanticT, typename SemanticTArray, typename SyntacticT>
-    types::Type build_semantic_type(const SyntacticT &type, std::string_view type_name) const {
+    types::Type build_semantic_type(const SyntacticT& type, std::string_view type_name) const {
         if (type.size.empty()) {
             return tree::make<SemanticT>(1);
         } else if (type.size->value > 0) {
@@ -91,7 +91,7 @@ class AnalyzeTreeGenAstVisitor : public ast::Visitor<std::any> {
      * Build a semantic type
      */
     template <typename SyntacticT>
-    types::Type build_semantic_type(const SyntacticT &type) const {
+    types::Type build_semantic_type(const SyntacticT& type) const {
         assert(!type.empty() && !type->name.empty());
         auto type_name = type->name->name;
         if (type_name == types::qubit_type_name) {
@@ -107,11 +107,11 @@ class AnalyzeTreeGenAstVisitor : public ast::Visitor<std::any> {
      * Convenience function for visiting a global or a local block
      */
     template <typename Block>
-    void visit_block(Block &block) {
-        for (const auto &statement_ast : block.statements) {
+    void visit_block(Block& block) {
+        for (const auto& statement_ast : block.statements) {
             try {
                 statement_ast->visit(*this);
-            } catch (error::AnalysisError &err) {
+            } catch (error::AnalysisError& err) {
                 err.context(block);
                 result_.errors.push_back(std::move(err));
             }
@@ -122,18 +122,18 @@ class AnalyzeTreeGenAstVisitor : public ast::Visitor<std::any> {
      * Convenience function for visiting a function call given the function's name and arguments
      */
     values::Value visit_function_call(
-        const tree::One<ast::Identifier> &name, const tree::Maybe<ast::ExpressionList> &arguments);
+        const tree::One<ast::Identifier>& name, const tree::Maybe<ast::ExpressionList>& arguments);
 
     /**
      * Convenience function for visiting unary operators
      */
-    std::any visit_unary_operator(const std::string &name, const tree::One<ast::Expression> &expression);
+    std::any visit_unary_operator(const std::string& name, const tree::One<ast::Expression>& expression);
 
     /**
      * Convenience function for visiting binary operators
      */
     std::any visit_binary_operator(
-        const std::string &name, const tree::One<ast::Expression> &lhs, const tree::One<ast::Expression> &rhs);
+        const std::string& name, const tree::One<ast::Expression>& lhs, const tree::One<ast::Expression>& rhs);
 
     /**
      * Shorthand for parsing an expression and promoting it to the given type,
@@ -141,14 +141,14 @@ class AnalyzeTreeGenAstVisitor : public ast::Visitor<std::any> {
      * Returns empty when the cast fails
      */
     template <class Type, class... TypeArgs>
-    values::Value visit_as(ast::Expression &expression, TypeArgs... type_args) {
+    values::Value visit_as(ast::Expression& expression, TypeArgs... type_args) {
         return values::promote(std::any_cast<values::Value>(expression.visit(*this)), tree::make<Type>(type_args...));
     }
 
     /**
      * Shorthand for parsing an expression to a constant integer
      */
-    primitives::Int visit_const_int(ast::Expression &expression);
+    primitives::Int visit_const_int(ast::Expression& expression);
 };
 
 }  // namespace cqasm::v3x::analyzer
diff --git a/include/v3x/cqasm-semantic-helper.hpp b/include/libqasm/v3x/semantic_helper.hpp
similarity index 100%
rename from include/v3x/cqasm-semantic-helper.hpp
rename to include/libqasm/v3x/semantic_helper.hpp
diff --git a/include/libqasm/v3x/syntactic_analyzer.hpp b/include/libqasm/v3x/syntactic_analyzer.hpp
new file mode 100644
index 000000000..08657da75
--- /dev/null
+++ b/include/libqasm/v3x/syntactic_analyzer.hpp
@@ -0,0 +1,114 @@
+#pragma once
+
+#include <antlr4-runtime.h>
+
+#include <any>
+#include <optional>
+#include <tuple>
+
+#include "libqasm/v3x/CqasmParser.h"
+#include "libqasm/v3x/CqasmParserVisitor.h"
+#include "libqasm/v3x/syntactic_analyzer_base.hpp"
+
+namespace cqasm::v3x::parser {
+class AntlrCustomErrorListener;
+}
+
+namespace cqasm::v3x::parser {
+
+class SyntacticAnalyzer : public BaseSyntacticAnalyzer {
+    /**
+     * Name of the file being parsed.
+     */
+    std::optional<std::string> file_name_;
+
+    /**
+     * Error listener.
+     */
+    AntlrCustomErrorListener* error_listener_p_;
+
+    [[nodiscard]] std::int64_t get_int_value(size_t line, size_t char_position_in_line, const std::string& text) const;
+    [[nodiscard]] double get_float_value(size_t line, size_t char_position_in_line, const std::string& text) const;
+
+    bool get_bool_value(antlr4::tree::TerminalNode* node) const;
+    std::int64_t get_int_value(antlr4::tree::TerminalNode* node) const;
+    double get_float_value(antlr4::tree::TerminalNode* node) const;
+
+    tree::Maybe<ast::IntegerLiteral> getArraySize(CqasmParser::ArraySizeDeclarationContext* context);
+
+    template <typename Context>
+    tree::One<ast::Variable> visitVariable(Context* context) {
+        auto ret = tree::make<ast::Variable>(
+            tree::One<ast::Identifier>{ tree::make<ast::Identifier>(context->IDENTIFIER()->getText()) },
+            std::any_cast<tree::One<ast::Type>>(context->type()->accept(this)));
+        setNodeAnnotation(ret, context->IDENTIFIER()->getSymbol());
+        return ret;
+    }
+
+    template <typename RetExpressionType, typename Context>
+    std::any visitBinaryExpression(Context* context, antlr4::Token* token) {
+        auto ret = tree::make<RetExpressionType>(
+            std::any_cast<tree::One<ast::Expression>>(context->expression(0)->accept(this)),
+            std::any_cast<tree::One<ast::Expression>>(context->expression(1)->accept(this)));
+        setNodeAnnotation(ret, token);
+        return tree::One<ast::Expression>{ ret };
+    }
+
+public:
+    std::any visitProgram(CqasmParser::ProgramContext* context) override;
+    std::any visitVersionSection(CqasmParser::VersionSectionContext* context) override;
+    std::any visitGlobalBlockSection(CqasmParser::GlobalBlockSectionContext* context) override;
+    std::any visitEofSection(CqasmParser::EofSectionContext* context) override;
+    std::any visitStatementSeparator(CqasmParser::StatementSeparatorContext* context) override;
+    std::any visitVersion(CqasmParser::VersionContext* context) override;
+    std::any visitGlobalBlockStatement(CqasmParser::GlobalBlockStatementContext* context) override;
+    std::any visitVariableDefinition(CqasmParser::VariableDefinitionContext* context) override;
+    std::any visitInstruction(CqasmParser::InstructionContext* context) override;
+    std::any visitMeasureInstruction(CqasmParser::MeasureInstructionContext* context) override;
+    std::any visitResetInstruction(CqasmParser::ResetInstructionContext* context) override;
+    std::any visitGateInstruction(CqasmParser::GateInstructionContext* context) override;
+    std::any visitInvGate(CqasmParser::InvGateContext* context) override;
+    std::any visitPowGate(CqasmParser::PowGateContext* context) override;
+    std::any visitCtrlGate(CqasmParser::CtrlGateContext* context) override;
+    std::any visitNamedGate(CqasmParser::NamedGateContext* context) override;
+    std::any visitType(CqasmParser::TypeContext* context) override;
+    std::any visitQubitType(CqasmParser::QubitTypeContext* context) override;
+    std::any visitBitType(CqasmParser::BitTypeContext* context) override;
+    std::any visitArraySizeDeclaration(CqasmParser::ArraySizeDeclarationContext* context) override;
+    std::any visitExpressionList(CqasmParser::ExpressionListContext* context) override;
+    std::any visitIndexList(CqasmParser::IndexListContext* context) override;
+    std::any visitIndexItem(CqasmParser::IndexItemContext* context) override;
+    std::any visitIndexRange(CqasmParser::IndexRangeContext* context) override;
+    std::any visitParensExpression(CqasmParser::ParensExpressionContext* context) override;
+    std::any visitUnaryPlusMinusExpression(CqasmParser::UnaryPlusMinusExpressionContext* context) override;
+    std::any visitBitwiseNotExpression(CqasmParser::BitwiseNotExpressionContext* context) override;
+    std::any visitLogicalNotExpression(CqasmParser::LogicalNotExpressionContext* context) override;
+    std::any visitPowerExpression(CqasmParser::PowerExpressionContext* context) override;
+    std::any visitProductExpression(CqasmParser::ProductExpressionContext* context) override;
+    std::any visitAdditionExpression(CqasmParser::AdditionExpressionContext* context) override;
+    std::any visitShiftExpression(CqasmParser::ShiftExpressionContext* context) override;
+    std::any visitComparisonExpression(CqasmParser::ComparisonExpressionContext* context) override;
+    std::any visitEqualityExpression(CqasmParser::EqualityExpressionContext* context) override;
+    std::any visitBitwiseAndExpression(CqasmParser::BitwiseAndExpressionContext* context) override;
+    std::any visitBitwiseXorExpression(CqasmParser::BitwiseXorExpressionContext* context) override;
+    std::any visitBitwiseOrExpression(CqasmParser::BitwiseOrExpressionContext* context) override;
+    std::any visitLogicalAndExpression(CqasmParser::LogicalAndExpressionContext* context) override;
+    std::any visitLogicalXorExpression(CqasmParser::LogicalXorExpressionContext* context) override;
+    std::any visitLogicalOrExpression(CqasmParser::LogicalOrExpressionContext* context) override;
+    std::any visitTernaryConditionalExpression(CqasmParser::TernaryConditionalExpressionContext* context) override;
+    std::any visitFunctionCall(CqasmParser::FunctionCallContext* context) override;
+    std::any visitIndex(CqasmParser::IndexContext* context) override;
+    std::any visitIdentifier(CqasmParser::IdentifierContext* context) override;
+    std::any visitBooleanLiteral(CqasmParser::BooleanLiteralContext* context) override;
+    std::any visitIntegerLiteral(CqasmParser::IntegerLiteralContext* context) override;
+    std::any visitFloatLiteral(CqasmParser::FloatLiteralContext* context) override;
+
+    explicit SyntacticAnalyzer(const std::optional<std::string>& file_name);
+    void addErrorListener(AntlrCustomErrorListener* errorListener) override;
+    void syntaxError(size_t line, size_t char_position_in_line, const std::string& text) const override;
+    void setNodeAnnotation(const ast::One<ast::Node>& node, antlr4::Token* token) const override;
+    void expandNodeAnnotation(const ast::One<ast::Node>& node, antlr4::Token* token) const override;
+    void copyNodeAnnotation(const ast::One<ast::Node>& from, const ast::One<ast::Node>& to) const override;
+};
+
+}  // namespace cqasm::v3x::parser
diff --git a/include/libqasm/v3x/syntactic_analyzer_base.hpp b/include/libqasm/v3x/syntactic_analyzer_base.hpp
new file mode 100644
index 000000000..c0caf26a2
--- /dev/null
+++ b/include/libqasm/v3x/syntactic_analyzer_base.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <any>
+
+#include "libqasm/v3x/CqasmParser.h"
+#include "libqasm/v3x/CqasmParserVisitor.h"
+#include "libqasm/v3x/ast.hpp"
+
+namespace antlr4 {
+class Token;
+}
+namespace cqasm::v3x::parser {
+class AntlrCustomErrorListener;
+}
+
+namespace cqasm::v3x::parser {
+
+class BaseSyntacticAnalyzer : public CqasmParserVisitor {
+public:
+    virtual void addErrorListener(AntlrCustomErrorListener* errorListener) = 0;
+    virtual void syntaxError(size_t line, size_t char_position_in_line, const std::string& text) const = 0;
+    virtual void setNodeAnnotation(const ast::One<ast::Node>& node, antlr4::Token* token) const = 0;
+    virtual void expandNodeAnnotation(const ast::One<ast::Node>& node, antlr4::Token* token) const = 0;
+    virtual void copyNodeAnnotation(const ast::One<ast::Node>& from, const ast::One<ast::Node>& to) const = 0;
+};
+
+}  // namespace cqasm::v3x::parser
diff --git a/include/v3x/cqasm-types.hpp b/include/libqasm/v3x/types.hpp
similarity index 61%
rename from include/v3x/cqasm-types.hpp
rename to include/libqasm/v3x/types.hpp
index 58bf1ee94..70a1a1209 100644
--- a/include/v3x/cqasm-types.hpp
+++ b/include/libqasm/v3x/types.hpp
@@ -8,20 +8,20 @@
 #include <fmt/format.h>
 #include <fmt/ostream.h>
 
-#include "v3x/cqasm-types-gen.hpp"
+#include "libqasm/v3x/types_generated.hpp"
 
 /**
  * Namespace for the classes representing the types available within cQASM.
  */
 namespace cqasm::v3x::types {
 
-constexpr const char *qubit_type_name = "qubit";
-constexpr const char *bit_type_name = "bit";
-constexpr const char *bool_type_name = "bool";
-constexpr const char *integer_type_name = "int";
-constexpr const char *float_type_name = "float";
-constexpr const char *qubit_array_type_name = "qubit array";
-constexpr const char *bit_array_type_name = "bit array";
+constexpr const char* qubit_type_name = "qubit";
+constexpr const char* bit_type_name = "bit";
+constexpr const char* bool_type_name = "bool";
+constexpr const char* integer_type_name = "int";
+constexpr const char* float_type_name = "float";
+constexpr const char* qubit_array_type_name = "qubit array";
+constexpr const char* bit_array_type_name = "bit array";
 
 /**
  * A cQASM type.
@@ -38,33 +38,35 @@ using Types = tree::Any<TypeBase>;
  * In it, each character represents one type. The supported characters are as follows:
  *
  *  - Q = qubit
+ *  - B = bit (measurement register)
  *  - b = bool
  *  - i = int
  *  - f = float
  *  - V = qubit array
+ *  - W = bit array
  */
-Type from_spec(const char spec);
-Types from_spec(const std::string &spec);
+Type from_spec(char spec);
+Types from_spec(const std::string& spec);
 
 /**
  * Returns whether the `actual` type matches the constraints of the `expected` type.
  */
-bool type_check(const Type &expected, const Type &actual);
+bool type_check(const Type& expected, const Type& actual);
 
 /**
  * Returns the number of elements of the given type.
  */
-primitives::Int size_of(const Type &type);
+primitives::Int size_of(const Type& type);
 
 /**
  * Stream << overload for a single type.
  */
-std::ostream &operator<<(std::ostream &os, const Type &type);
+std::ostream& operator<<(std::ostream& os, const Type& type);
 
 /**
  * Stream << overload for zero or more types.
  */
-std::ostream &operator<<(std::ostream &os, const Types &types);
+std::ostream& operator<<(std::ostream& os, const Types& types);
 
 }  // namespace cqasm::v3x::types
 
diff --git a/include/v3x/cqasm-values.hpp b/include/libqasm/v3x/values.hpp
similarity index 76%
rename from include/v3x/cqasm-values.hpp
rename to include/libqasm/v3x/values.hpp
index 18c2b9fd9..ff39b050a 100644
--- a/include/v3x/cqasm-values.hpp
+++ b/include/libqasm/v3x/values.hpp
@@ -11,9 +11,9 @@
 
 #include <algorithm>  // all_of, for_each
 
-#include "cqasm-ast.hpp"
-#include "cqasm-types.hpp"
-#include "v3x/cqasm-values-gen.hpp"
+#include "libqasm/v3x/ast.hpp"
+#include "libqasm/v3x/types.hpp"
+#include "libqasm/v3x/values_generated.hpp"
 
 /**
  * Namespace for the classes representing the values
@@ -38,55 +38,55 @@ using Values = tree::Any<ValueBase>;
  * otherwise returns the constructed value by way of a smart pointer.
  * If the type was an exact match, this may return the given value without modification or a clone thereof.
  */
-Value promote(const Value &value, const types::Type &type);
+Value promote(const Value& value, const types::Type& type);
 
 /**
  * Checks if a from_type can be promoted to a to_type.
  */
-bool check_promote(const types::Type &from_type, const types::Type &to_type);
+bool check_promote(const types::Type& from_type, const types::Type& to_type);
 
 /**
  * Returns the element type of the given type.
  * Throws an error if the given type is not of array type.
  */
-types::Type element_type_of(const types::Type &type);
+types::Type element_type_of(const types::Type& type);
 
 /**
  * Returns the type of the given value.
  */
-types::Type type_of(const Value &value);
+types::Type type_of(const Value& value);
 
 /**
  * Returns the types of the given values.
  */
-types::Types types_of(const Values &values);
+types::Types types_of(const Values& values);
 
 /**
  * Returns the number of elements of the given value.
  */
-primitives::Int size_of(const Value &value);
+primitives::Int size_of(const Value& value);
 
 /**
  * Throws an AnalysisError if the given value is not a constant,
  * i.e. if it doesn't have a known value at this time.
  */
-void check_const(const Value &value);
+void check_const(const Value& value);
 
 /**
  * Throws an AnalysisError if any of the given values are not a constant,
  * i.e. if it doesn't have a known value at this time.
  */
-void check_const(const Values &values);
+void check_const(const Values& values);
 
 /**
  * Stream << overload for a single value.
  */
-std::ostream &operator<<(std::ostream &os, const Value &value);
+std::ostream& operator<<(std::ostream& os, const Value& value);
 
 /**
  * Stream << overload for zero or more values.
  */
-std::ostream &operator<<(std::ostream &os, const Values &values);
+std::ostream& operator<<(std::ostream& os, const Values& values);
 
 }  // namespace cqasm::v3x::values
 
diff --git a/include/cqasm-version.hpp b/include/libqasm/version.hpp
similarity index 75%
rename from include/cqasm-version.hpp
rename to include/libqasm/version.hpp
index 22eb7d438..e41511e45 100644
--- a/include/cqasm-version.hpp
+++ b/include/libqasm/version.hpp
@@ -14,7 +14,7 @@
 #include <string>
 #include <vector>
 
-#include "cqasm-error.hpp"
+#include "libqasm/error.hpp"
 
 /**
  * Namespace for detecting and dealing with cQASM language versions.
@@ -32,9 +32,10 @@ class Version : public std::vector<std::int64_t> {
      *   1 if this version is newer than the other,
      *   -1 if this version is older than the other, or
      *   0 if both versions are the same.
-     * When there is a mismatch in the number of components between the versions, missing components are interpreted as 0.
+     * When there is a mismatch in the number of components between the versions, missing components are interpreted as
+     * 0.
      */
-    [[nodiscard]] int compare(const Version &other) const;
+    [[nodiscard]] int compare(const Version& other) const;
 
 public:
     /**
@@ -45,21 +46,21 @@ class Version : public std::vector<std::int64_t> {
     /**
      * Constructs a version object from a string.
      */
-    Version(const std::string &version);
+    Version(const std::string& version);
 
     /**
      * Constructs a version object from an array of chars.
      */
-    Version(const char *version);
+    Version(const char* version);
 
-    [[nodiscard]] auto operator==(const Version &rhs) const { return compare(rhs) == 0; }
-    [[nodiscard]] auto operator<=>(const Version &rhs) const { return compare(rhs) <=> 0; }
+    [[nodiscard]] auto operator==(const Version& rhs) const { return compare(rhs) == 0; }
+    [[nodiscard]] auto operator<=>(const Version& rhs) const { return compare(rhs) <=> 0; }
 };
 
 /**
  * Stream << overload for version nodes.
  */
-std::ostream &operator<<(std::ostream &os, const Version &object);
+std::ostream& operator<<(std::ostream& os, const Version& object);
 
 }  // namespace cqasm::version
 
diff --git a/include/libqasm/versioning.hpp b/include/libqasm/versioning.hpp
new file mode 100644
index 000000000..57b84f0a5
--- /dev/null
+++ b/include/libqasm/versioning.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+namespace cqasm {
+
+static const char* version{ "0.6.8" };
+static const char* release_year{ "2024" };
+
+[[nodiscard]] [[maybe_unused]] static const char* get_version() {
+    return version;
+}
+
+[[nodiscard]] [[maybe_unused]] static const char* get_release_year() {
+    return release_year;
+}
+
+}  // namespace cqasm
diff --git a/include/v3x/BuildCustomAstVisitor.hpp b/include/v3x/BuildCustomAstVisitor.hpp
deleted file mode 100644
index af95bf003..000000000
--- a/include/v3x/BuildCustomAstVisitor.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <any>
-
-#include "cqasm-ast.hpp"
-#include "v3x/CqasmParser.h"
-#include "v3x/CqasmParserVisitor.h"
-
-namespace antlr4 {
-class Token;
-}
-namespace cqasm::v3x::parser {
-class CustomErrorListener;
-}
-
-namespace cqasm::v3x::parser {
-
-class BuildCustomAstVisitor : public CqasmParserVisitor {
-public:
-    virtual void addErrorListener(CustomErrorListener *errorListener) = 0;
-    virtual void syntaxError(size_t line, size_t char_position_in_line, const std::string &text) const = 0;
-    virtual void setNodeAnnotation(const ast::One<ast::Node> &node, antlr4::Token *token) const = 0;
-    virtual void expandNodeAnnotation(const ast::One<ast::Node> &node, antlr4::Token *token) const = 0;
-};
-
-}  // namespace cqasm::v3x::parser
diff --git a/include/v3x/BuildTreeGenAstVisitor.hpp b/include/v3x/BuildTreeGenAstVisitor.hpp
deleted file mode 100644
index 13d0d434d..000000000
--- a/include/v3x/BuildTreeGenAstVisitor.hpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#pragma once
-
-#include <antlr4-runtime.h>
-
-#include <any>
-#include <optional>
-#include <tuple>
-
-#include "v3x/BuildCustomAstVisitor.hpp"
-#include "v3x/CqasmParser.h"
-#include "v3x/CqasmParserVisitor.h"
-
-namespace cqasm::v3x::parser {
-class CustomErrorListener;
-}
-
-namespace cqasm::v3x::parser {
-
-using InstructionsSectionT = std::tuple<tree::Any<ast::Gate>, tree::Any<ast::MeasureInstruction>>;
-
-class BuildTreeGenAstVisitor : public BuildCustomAstVisitor {
-    /**
-     * Name of the file being parsed.
-     */
-    std::optional<std::string> file_name_;
-
-    /**
-     * Error listener.
-     */
-    CustomErrorListener *error_listener_p_;
-
-    [[nodiscard]] std::int64_t get_int_value(size_t line, size_t char_position_in_line, const std::string &text) const;
-    [[nodiscard]] double get_float_value(size_t line, size_t char_position_in_line, const std::string &text) const;
-
-    bool get_bool_value(antlr4::tree::TerminalNode *node) const;
-    std::int64_t get_int_value(antlr4::tree::TerminalNode *node) const;
-    double get_float_value(antlr4::tree::TerminalNode *node) const;
-
-    tree::Maybe<ast::IntegerLiteral> getArraySize(CqasmParser::ArraySizeDeclarationContext *context);
-
-    template <typename Context>
-    tree::One<ast::Variable> visitVariable(Context *context) {
-        auto ret = tree::make<ast::Variable>(
-            tree::One<ast::Identifier>{ tree::make<ast::Identifier>(context->IDENTIFIER()->getText()) },
-            std::any_cast<tree::One<ast::Type>>(context->type()->accept(this)));
-        setNodeAnnotation(ret, context->IDENTIFIER()->getSymbol());
-        return ret;
-    }
-
-    template <typename RetExpressionType, typename Context>
-    std::any visitBinaryExpression(Context *context, antlr4::Token *token) {
-        auto ret = tree::make<RetExpressionType>(
-            std::any_cast<tree::One<ast::Expression>>(context->expression(0)->accept(this)),
-            std::any_cast<tree::One<ast::Expression>>(context->expression(1)->accept(this)));
-        setNodeAnnotation(ret, token);
-        return tree::One<ast::Expression>{ ret };
-    }
-
-public:
-    std::any visitProgram(CqasmParser::ProgramContext *context) override;
-    std::any visitVersionSection(CqasmParser::VersionSectionContext *context) override;
-    std::any visitGlobalBlockSection(CqasmParser::GlobalBlockSectionContext *context) override;
-    std::any visitEofSection(CqasmParser::EofSectionContext *context) override;
-    std::any visitStatementSeparator(CqasmParser::StatementSeparatorContext *context) override;
-    std::any visitVersion(CqasmParser::VersionContext *context) override;
-    std::any visitGlobalBlockStatement(CqasmParser::GlobalBlockStatementContext *context) override;
-    std::any visitVariableDefinition(CqasmParser::VariableDefinitionContext *context) override;
-    std::any visitMeasureInstruction(CqasmParser::MeasureInstructionContext *context) override;
-    std::any visitResetInstruction(CqasmParser::ResetInstructionContext *context) override;
-    std::any visitGate(CqasmParser::GateContext *context) override;
-    std::any visitType(CqasmParser::TypeContext *context) override;
-    std::any visitQubitType(CqasmParser::QubitTypeContext *context) override;
-    std::any visitBitType(CqasmParser::BitTypeContext *context) override;
-    std::any visitArraySizeDeclaration(CqasmParser::ArraySizeDeclarationContext *context) override;
-    std::any visitExpressionList(CqasmParser::ExpressionListContext *context) override;
-    std::any visitIndexList(CqasmParser::IndexListContext *context) override;
-    std::any visitIndexItem(CqasmParser::IndexItemContext *context) override;
-    std::any visitIndexRange(CqasmParser::IndexRangeContext *context) override;
-    std::any visitParensExpression(CqasmParser::ParensExpressionContext *context) override;
-    std::any visitUnaryPlusMinusExpression(CqasmParser::UnaryPlusMinusExpressionContext *context) override;
-    std::any visitBitwiseNotExpression(CqasmParser::BitwiseNotExpressionContext *context) override;
-    std::any visitLogicalNotExpression(CqasmParser::LogicalNotExpressionContext *context) override;
-    std::any visitPowerExpression(CqasmParser::PowerExpressionContext *context) override;
-    std::any visitProductExpression(CqasmParser::ProductExpressionContext *context) override;
-    std::any visitAdditionExpression(CqasmParser::AdditionExpressionContext *context) override;
-    std::any visitShiftExpression(CqasmParser::ShiftExpressionContext *context) override;
-    std::any visitComparisonExpression(CqasmParser::ComparisonExpressionContext *context) override;
-    std::any visitEqualityExpression(CqasmParser::EqualityExpressionContext *context) override;
-    std::any visitBitwiseAndExpression(CqasmParser::BitwiseAndExpressionContext *context) override;
-    std::any visitBitwiseXorExpression(CqasmParser::BitwiseXorExpressionContext *context) override;
-    std::any visitBitwiseOrExpression(CqasmParser::BitwiseOrExpressionContext *context) override;
-    std::any visitLogicalAndExpression(CqasmParser::LogicalAndExpressionContext *context) override;
-    std::any visitLogicalXorExpression(CqasmParser::LogicalXorExpressionContext *context) override;
-    std::any visitLogicalOrExpression(CqasmParser::LogicalOrExpressionContext *context) override;
-    std::any visitTernaryConditionalExpression(CqasmParser::TernaryConditionalExpressionContext *context) override;
-    std::any visitFunctionCall(CqasmParser::FunctionCallContext *context) override;
-    std::any visitIndex(CqasmParser::IndexContext *context) override;
-    std::any visitIdentifier(CqasmParser::IdentifierContext *context) override;
-    std::any visitBooleanLiteral(CqasmParser::BooleanLiteralContext *context) override;
-    std::any visitIntegerLiteral(CqasmParser::IntegerLiteralContext *context) override;
-    std::any visitFloatLiteral(CqasmParser::FloatLiteralContext *context) override;
-
-    explicit BuildTreeGenAstVisitor(const std::optional<std::string> &file_name);
-    void addErrorListener(CustomErrorListener *errorListener) override;
-    void syntaxError(size_t line, size_t char_position_in_line, const std::string &text) const override;
-    void setNodeAnnotation(const ast::One<ast::Node> &node, antlr4::Token *token) const override;
-    void expandNodeAnnotation(const ast::One<ast::Node> &node, antlr4::Token *token) const override;
-};
-
-}  // namespace cqasm::v3x::parser
diff --git a/include/v3x/ScannerAntlr.hpp b/include/v3x/ScannerAntlr.hpp
deleted file mode 100644
index 68a69533c..000000000
--- a/include/v3x/ScannerAntlr.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-
-#include <memory>  // unique_ptr
-#include <string>
-
-#include "v3x/BuildCustomAstVisitor.hpp"
-#include "v3x/cqasm-parse-result.hpp"
-
-namespace antlr4 {
-class ANTLRInputStream;
-}
-namespace cqasm::v3x::parser {
-class CustomErrorListener;
-}
-
-namespace cqasm::v3x::parser {
-
-struct ScannerAdaptor {
-    virtual ~ScannerAdaptor();
-
-    virtual cqasm::v3x::parser::ParseResult parse() = 0;
-};
-
-class ScannerAntlr : public ScannerAdaptor {
-    std::unique_ptr<BuildCustomAstVisitor> build_visitor_up_;
-    std::unique_ptr<CustomErrorListener> error_listener_up_;
-
-protected:
-    cqasm::v3x::parser::ParseResult parse_(antlr4::ANTLRInputStream &is);
-
-public:
-    ScannerAntlr(std::unique_ptr<BuildCustomAstVisitor> build_visitor_up,
-        std::unique_ptr<CustomErrorListener> error_listener_up);
-
-    ~ScannerAntlr() override;
-
-    cqasm::v3x::parser::ParseResult parse() override = 0;
-};
-
-class ScannerAntlrFile : public ScannerAntlr {
-    std::string file_path_;
-
-public:
-    ScannerAntlrFile(std::unique_ptr<BuildCustomAstVisitor> build_visitor_up,
-        std::unique_ptr<CustomErrorListener> error_listener_up, const std::string &file_path);
-
-    ~ScannerAntlrFile() override;
-
-    cqasm::v3x::parser::ParseResult parse() override;
-};
-
-class ScannerAntlrString : public ScannerAntlr {
-    std::string data_;
-
-public:
-    ScannerAntlrString(std::unique_ptr<BuildCustomAstVisitor> build_visitor_up,
-        std::unique_ptr<CustomErrorListener> error_listener_up, const std::string &data);
-
-    ~ScannerAntlrString() override;
-
-    cqasm::v3x::parser::ParseResult parse() override;
-};
-
-}  // namespace cqasm::v3x::parser
diff --git a/include/v3x/cqasm-semantic.hpp b/include/v3x/cqasm-semantic.hpp
deleted file mode 100644
index 010be11d8..000000000
--- a/include/v3x/cqasm-semantic.hpp
+++ /dev/null
@@ -1,12 +0,0 @@
-/** \file
- * Defines the types for the cQASM \ref cqasm::v3x::semantic "semantic tree",
- * based on the classes from cqasm::tree.
- *
- * This file is currently just a link to the \ref v3x/cqasm-semantic-gen.hpp "generated file",
- * aside from one forward declaration in \ref v3x/cqasm-semantic-helper.hpp.
- */
-
-#pragma once
-
-#include "cqasm-semantic-helper.hpp"
-#include "v3x/cqasm-semantic-gen.hpp"
diff --git a/include/version.hpp b/include/version.hpp
deleted file mode 100644
index 797b5a276..000000000
--- a/include/version.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-namespace cqasm {
-
-static const char *version{ "0.6.7" };
-static const char *release_year{ "2024" };
-
-[[nodiscard]] [[maybe_unused]] static const char *get_version() {
-    return version;
-}
-
-[[nodiscard]] [[maybe_unused]] static const char *get_release_year() {
-    return release_year;
-}
-
-}  // namespace cqasm
diff --git a/doc/mkdocs.yml b/mkdocs-base.yml
similarity index 54%
rename from doc/mkdocs.yml
rename to mkdocs-base.yml
index 0b1565e59..4d3129dd2 100644
--- a/doc/mkdocs.yml
+++ b/mkdocs-base.yml
@@ -1,35 +1,7 @@
 site_name: libQASM
-
-docs_dir: ../doc/src
-
+site_dir: public
 repo_url: https://github.com/QuTech-Delft/libqasm
-
-theme:
-  name: material
-  features:
-    - navigation.tabs
-    - navigation.top
-    - toc.integrate
-
-extra_javascript:
-  - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js
-  - cpp_highlighting.js
-  - python_highlighting.js
-  - typescript_highlighting.js
-
-extra_css:
-  - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/default.min.css
-  - libqasm.css
-
-markdown_extensions:
-  - admonition
-  - pymdownx.inlinehilite
-  - pymdownx.snippets
-
-plugins:
-  - search
-  - mkdocstrings:
-      default_handler: python
+use_directory_urls: false
 
 nav:
   - Home: index.md
@@ -54,7 +26,50 @@ nav:
     - License: about/license.md
     - Authors: about/authors.md
 
-extra:
-  version:
-    provider: mike
-  generator: false
+theme:
+  name: material
+  features:
+    - navigation.tabs
+    - navigation.top
+    - toc.integrate
+  palette:
+    # Palette toggle for automatic mode
+    - media: "(prefers-color-scheme)"
+      toggle:
+        icon: material/brightness-auto
+        name: Switch to light mode
+    # Palette toggle for light mode
+    - media: "(prefers-color-scheme: light)"
+      scheme: default
+      toggle:
+        icon: material/brightness-7
+        name: Switch to dark mode
+    # Palette toggle for dark mode
+    - media: "(prefers-color-scheme: dark)"
+      scheme: slate
+      toggle:
+        icon: material/brightness-4
+        name: Switch to system preference
+
+markdown_extensions:
+  - admonition
+  - pymdownx.inlinehilite
+  - pymdownx.snippets
+
+extra_css:
+  - extra_api.css
+  - extra_highlight.css
+
+extra_javascript:
+  - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js
+  - cpp_highlight.js
+  - python_highlight.js
+  - typescript_highlight.js
+
+plugins:
+  - search
+  - mkdocstrings:
+      default_handler: python
+
+watch:
+  - mkdocs-base.yml
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 000000000..fa5584128
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,5 @@
+INHERIT: ./mkdocs-base.yml
+
+extra:
+  version:
+    provider: mike
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index ff0f237b4..82fec437c 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -150,22 +150,22 @@ install(
     DESTINATION "${LIBQASM_PYTHON_DIR}"
 )
 install(
-    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-ast-gen.py"
+    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/ast_generated.py"
     DESTINATION "${LIBQASM_CQASM_PYTHON_DIR}/v3x"
     RENAME "ast.py"
 )
 install(
-    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-semantic-gen.py"
+    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/semantic_generated.py"
     DESTINATION "${LIBQASM_CQASM_PYTHON_DIR}/v3x"
     RENAME "semantic.py"
 )
 install(
-    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-types-gen.py"
+    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/types_generated.py"
     DESTINATION "${LIBQASM_CQASM_PYTHON_DIR}/v3x"
     RENAME "types.py"
 )
 install(
-    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-values-gen.py"
+    FILES "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/values_generated.py"
     DESTINATION "${LIBQASM_CQASM_PYTHON_DIR}/v3x"
     RENAME "values.py"
 )
diff --git a/python/libqasm.i b/python/libqasm.i
index 957c66ebc..097461552 100644
--- a/python/libqasm.i
+++ b/python/libqasm.i
@@ -8,7 +8,7 @@
 %}
 
 %{
-#include "v3x/cqasm-py.hpp"
+#include "libqasm/v3x/cqasm_python.hpp"
 %}
 
 %include "exception.i"
@@ -36,4 +36,4 @@ namespace std {
     }
 }
 
-%include "v3x/cqasm-py.hpp"
+%include "libqasm/v3x/cqasm_python.hpp"
diff --git a/python/module/cqasm/v3x/__init__.py b/python/module/cqasm/v3x/__init__.py
index 298b34193..66411a5fb 100644
--- a/python/module/cqasm/v3x/__init__.py
+++ b/python/module/cqasm/v3x/__init__.py
@@ -38,7 +38,7 @@ class Analyzer(libqasm.V3xAnalyzer):
 
      **Example**:
 
-         result = libqasm.analyze_file("grover.cq");
+         result = libqasm.analyze_file("grover.cq")
 
      **Example**:
 
@@ -50,7 +50,7 @@ class Analyzer(libqasm.V3xAnalyzer):
              CNOT q[0], q[1]
              b = measure q
          '''
-         result = libqasm.parse_string_to_json(program, "bell.cq");
+         result = libqasm.parse_string_to_json(program, "bell.cq")
     """
 
     @staticmethod
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..ba3270154
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+mike==2.1.2
+mkdocs-material==9.5.29
+mkdocstrings==0.25.1
+pymdown-extensions==10.8.1
diff --git a/res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/semantic.3.0.golden.txt b/res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/semantic.3.0.golden.txt
deleted file mode 100644
index 839ddfa8c..000000000
--- a/res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/semantic.3.0.golden.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ERROR
-Error at input.cq:6:6..13: qubit and bit indices have different sizes
diff --git a/res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/ast.golden.txt b/res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/ast.golden.txt
deleted file mode 100644
index 4ae6af684..000000000
--- a/res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/ast.golden.txt
+++ /dev/null
@@ -1,122 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:10..11
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..9
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 9
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        Variable( # input.cq:4:8..9
-          name: <
-            Identifier(
-              name: b
-            )
-          >
-          typ: <
-            Type( # input.cq:4:1..7
-              name: <
-                Keyword(
-                  name: bit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 5
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        MeasureInstruction( # input.cq:6:5..12
-          name: <
-            Identifier(
-              name: measure
-            )
-          >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Index( # input.cq:6:13..14
-              expr: <
-                Identifier(
-                  name: q
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:15..16
-                          value: 1
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:18..19
-                          value: 3
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:21..22
-                          value: 4
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:24..25
-                          value: 5
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:27..28
-                          value: 7
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/ast.golden.txt b/res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/ast.golden.txt
deleted file mode 100644
index 1c3b72531..000000000
--- a/res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/ast.golden.txt
+++ /dev/null
@@ -1,125 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:10..11
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..9
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 5
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        Variable( # input.cq:4:8..9
-          name: <
-            Identifier(
-              name: b
-            )
-          >
-          typ: <
-            Type( # input.cq:4:1..7
-              name: <
-                Keyword(
-                  name: bit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 5
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        MeasureInstruction( # input.cq:6:11..18
-          name: <
-            Identifier(
-              name: measure
-            )
-          >
-          lhs: <
-            Index( # input.cq:6:1..2
-              expr: <
-                Identifier(
-                  name: b
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:3..4
-                          value: 1
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:6..7
-                          value: 3
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          rhs: <
-            Index( # input.cq:6:19..20
-              expr: <
-                Identifier(
-                  name: q
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:21..22
-                          value: 2
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:24..25
-                          value: 4
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/ast.golden.txt b/res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/ast.golden.txt
deleted file mode 100644
index 892df8bec..000000000
--- a/res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/ast.golden.txt
+++ /dev/null
@@ -1,123 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:10..11
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..9
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 5
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        Variable( # input.cq:4:8..9
-          name: <
-            Identifier(
-              name: b
-            )
-          >
-          typ: <
-            Type( # input.cq:4:1..7
-              name: <
-                Keyword(
-                  name: bit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 5
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        MeasureInstruction( # input.cq:6:10..17
-          name: <
-            Identifier(
-              name: measure
-            )
-          >
-          lhs: <
-            Index( # input.cq:6:1..2
-              expr: <
-                Identifier(
-                  name: b
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexRange(
-                      first: <
-                        IntegerLiteral( # input.cq:6:3..4
-                          value: 1
-                        )
-                      >
-                      last: <
-                        IntegerLiteral( # input.cq:6:5..6
-                          value: 2
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          rhs: <
-            Index( # input.cq:6:18..19
-              expr: <
-                Identifier(
-                  name: q
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:20..21
-                          value: 2
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:23..24
-                          value: 4
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/ast.golden.txt b/res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/ast.golden.txt
deleted file mode 100644
index 38f974b88..000000000
--- a/res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/ast.golden.txt
+++ /dev/null
@@ -1,123 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:10..11
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..9
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 5
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        Variable( # input.cq:4:8..9
-          name: <
-            Identifier(
-              name: b
-            )
-          >
-          typ: <
-            Type( # input.cq:4:1..7
-              name: <
-                Keyword(
-                  name: bit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 5
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        MeasureInstruction( # input.cq:6:11..18
-          name: <
-            Identifier(
-              name: measure
-            )
-          >
-          lhs: <
-            Index( # input.cq:6:1..2
-              expr: <
-                Identifier(
-                  name: b
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:3..4
-                          value: 1
-                        )
-                      >
-                    )
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:6..7
-                          value: 3
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          rhs: <
-            Index( # input.cq:6:19..20
-              expr: <
-                Identifier(
-                  name: q
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexRange(
-                      first: <
-                        IntegerLiteral( # input.cq:6:21..22
-                          value: 3
-                        )
-                      >
-                      last: <
-                        IntegerLiteral( # input.cq:6:23..24
-                          value: 4
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/measure_instruction/q_to_5/ast.golden.txt b/res/v3x/parsing/measure_instruction/q_to_5/ast.golden.txt
deleted file mode 100644
index 5d8293088..000000000
--- a/res/v3x/parsing/measure_instruction/q_to_5/ast.golden.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:7..8
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..6
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: -
-            )
-          >
-          annotations: []
-        )
-        MeasureInstruction( # input.cq:5:5..12
-          name: <
-            Identifier(
-              name: measure
-            )
-          >
-          lhs: <
-            IntegerLiteral( # input.cq:5:1..2
-              value: 5
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:5:13..14
-              name: q
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/measure_instruction/q_to_q/ast.golden.txt b/res/v3x/parsing/measure_instruction/q_to_q/ast.golden.txt
deleted file mode 100644
index 01806b5f8..000000000
--- a/res/v3x/parsing/measure_instruction/q_to_q/ast.golden.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:7..8
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..6
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: -
-            )
-          >
-          annotations: []
-        )
-        MeasureInstruction( # input.cq:5:5..12
-          name: <
-            Identifier(
-              name: measure
-            )
-          >
-          lhs: <
-            Identifier( # input.cq:5:1..2
-              name: q
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:5:13..14
-              name: q
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/reset_instruction/5/ast.golden.txt b/res/v3x/parsing/reset_instruction/5/ast.golden.txt
deleted file mode 100644
index 79958aaff..000000000
--- a/res/v3x/parsing/reset_instruction/5/ast.golden.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:7..8
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..6
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: -
-            )
-          >
-          annotations: []
-        )
-        ResetInstruction( # input.cq:5:1..6
-          name: <
-            Identifier(
-              name: reset
-            )
-          >
-          operand: <
-            IntegerLiteral( # input.cq:5:7..8
-              value: 5
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/reset_instruction/no_operand/ast.golden.txt b/res/v3x/parsing/reset_instruction/no_operand/ast.golden.txt
deleted file mode 100644
index 6d393e87f..000000000
--- a/res/v3x/parsing/reset_instruction/no_operand/ast.golden.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:7..8
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..6
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: -
-            )
-          >
-          annotations: []
-        )
-        ResetInstruction( # input.cq:5:1..6
-          name: <
-            Identifier(
-              name: reset
-            )
-          >
-          operand: -
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/reset_instruction/q_range_0_4/ast.golden.txt b/res/v3x/parsing/reset_instruction/q_range_0_4/ast.golden.txt
deleted file mode 100644
index f02cce737..000000000
--- a/res/v3x/parsing/reset_instruction/q_range_0_4/ast.golden.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-SUCCESS
-Program(
-  version: <
-    Version( # input.cq:1:9..10
-      items: 3
-    )
-  >
-  block: <
-    GlobalBlock(
-      statements: [
-        Variable( # input.cq:3:10..11
-          name: <
-            Identifier(
-              name: q
-            )
-          >
-          typ: <
-            Type( # input.cq:3:1..9
-              name: <
-                Keyword(
-                  name: qubit
-                )
-              >
-              size: <
-                IntegerLiteral(
-                  value: 9
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-        ResetInstruction( # input.cq:5:1..6
-          name: <
-            Identifier(
-              name: reset
-            )
-          >
-          operand: <
-            Index( # input.cq:5:7..8
-              expr: <
-                Identifier(
-                  name: q
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexRange(
-                      first: <
-                        IntegerLiteral( # input.cq:5:9..10
-                          value: 0
-                        )
-                      >
-                      last: <
-                        IntegerLiteral( # input.cq:5:11..12
-                          value: 4
-                        )
-                      >
-                    )
-                  ]
-                )
-              >
-            )
-          >
-          annotations: []
-        )
-      ]
-    )
-  >
-)
-
diff --git a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/ast.golden.json b/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/ast.golden.json
deleted file mode 100644
index d90fbabed..000000000
--- a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/ast.golden.json
+++ /dev/null
@@ -1,1 +0,0 @@
-{"Program":{"version":{"Version":{"items":"3","source_location":"input.cq:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"4"}},"source_location":"input.cq:3:1..9"}},"annotations":"[]","source_location":"input.cq:3:10..11"}},{"Gate":{"name":{"Identifier":{"name":"CNOT"}},"operands":{"ExpressionList":{"items":[{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexItem":{"index":{"IntegerLiteral":{"value":"0","source_location":"input.cq:5:8..9"}}}},{"IndexItem":{"index":{"IntegerLiteral":{"value":"1","source_location":"input.cq:5:11..12"}}}}]}},"source_location":"input.cq:5:6..7"}},{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexItem":{"index":{"IntegerLiteral":{"value":"3","source_location":"input.cq:5:17..18"}}}},{"IndexItem":{"index":{"IntegerLiteral":{"value":"2","source_location":"input.cq:5:20..21"}}}}]}},"source_location":"input.cq:5:15..16"}}]}},"annotations":"[]","source_location":"input.cq:5:1..5"}}]}}}}
\ No newline at end of file
diff --git a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.json b/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.json
deleted file mode 100644
index b3c8f15a5..000000000
--- a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.json
+++ /dev/null
@@ -1,1 +0,0 @@
-{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"Instruction":{"instruction_ref":"CNOT(qubit array, qubit array)","name":"CNOT","operands":[{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"4"}},"annotations":"[]"}},"indices":[{"ConstInt":{"value":"0"}},{"ConstInt":{"value":"1"}}]}},{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"4"}},"annotations":"[]"}},"indices":[{"ConstInt":{"value":"3"}},{"ConstInt":{"value":"2"}}]}}],"annotations":"[]"}}]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"4"}},"annotations":"[]"}}]}}
\ No newline at end of file
diff --git a/scripts/generate_antlr_parser.py b/scripts/generate_antlr_parser.py
index 153cd782f..63fb8b945 100644
--- a/scripts/generate_antlr_parser.py
+++ b/scripts/generate_antlr_parser.py
@@ -3,6 +3,7 @@
 from urllib.request import urlopen
 from urllib import error
 
+import logging
 import os
 import shutil
 import subprocess
@@ -11,13 +12,17 @@
 
 
 def print_usage():
-    print("Usage: generate_antlr_parser.py <INPUT FOLDER> <OUTPUT SOURCE FOLDER> <OUTPUT INCLUDE FOLDER>")
-    print("Where:")
-    print("    INPUT_FOLDER: folder containing the grammar files.")
-    print("    OUTPUT_SOURCE_FOLDER: folder where generated source files will be left.")
-    print("    OUTPUT_INCLUDE_FOLDER: folder where generated include files will be left.")
-    print("Example:")
-    print("    generate_antlr_parser.py ../src/v3x ../build/Debug/src/v3x ../build/Debug/include/v3x")
+    usage_str = '''
+Usage:
+    python3 generate_antlr_parser.py <INPUT_FOLDER> <OUTPUT_SOURCE_FOLDER> <OUTPUT_INCLUDE_FOLDER>
+Where:
+    INPUT_FOLDER: folder containing the grammar files.
+    OUTPUT_SOURCE_FOLDER: folder where generated source files will be left.
+    OUTPUT_INCLUDE_FOLDER: folder where generated include files will be left.
+Example:
+    python3 generate_antlr_parser.py ../src/v3x ../build/Debug/src/v3x ../build/Debug/include/v3x
+'''
+    logging.info(usage_str)
 
 
 class AntlrJarFileDownloader:
@@ -33,13 +38,13 @@ def exists_antlr_jar_file(cls):
     def download_antlr_jar_file(cls):
         url = "https://repo1.maven.org/maven2/org/antlr/antlr4/{}/{}".format(cls.file_version, cls.file_name)
         try:
-            print("Downloading ANTLR jar file from '{}'".format(url))
+            logging.info("Downloading ANTLR jar file from '{}'".format(url))
             with urlopen(url) as response:
-                print("Writing ANTLR jar file to '{}'".format(cls.file_path))
+                logging.info("Writing ANTLR jar file to '{}'".format(cls.file_path))
                 with open(cls.file_path, "wb") as f:
                     f.write(response.read())
         except error.URLError as err:
-            print("Error downloading ANTLR jar file: {}".format(err.strerror))
+            logging.error("Error downloading ANTLR jar file: {}".format(err.strerror))
             exit(3)
 
     @classmethod
@@ -50,7 +55,7 @@ def get_antlr_jar_file_path(cls):
 
 
 def generate_antlr_parser(input_folder, output_source_folder, output_include_folder, antlr_jar_file_path):
-    print("Generating ANTLR lexer and parser files")
+    logging.info("Generating ANTLR lexer and parser files")
     try:
         completed_process = subprocess.run([
             "java",
@@ -67,22 +72,23 @@ def generate_antlr_parser(input_folder, output_source_folder, output_include_fol
             "{}/CqasmParser.g4".format(input_folder)
         ])
         if completed_process.returncode != 0:
-            print("Error generating ANTLR lexer and parser files: {}", completed_process.returncode)
+            logging.error("Error generating ANTLR lexer and parser files: {}", completed_process.returncode)
             exit(5)
         # Move header files to output include folder
         output_files = os.listdir(output_source_folder)
-        print(f"Moving output include files to {output_include_folder}")
+        logging.info(f"Moving output include files to {output_include_folder}")
         for output_file in output_files:
             if output_file.endswith(".h"):
-                print(f"\t{output_file}")
+                logging.info(f"\t{output_file}")
                 shutil.move(os.path.join(output_source_folder, output_file),
                             os.path.join(output_include_folder, output_file))
     except FileNotFoundError as err:
-        print("Error running java: {}".format(err.strerror))
+        logging.error("Error running java: {}".format(err.strerror))
         exit(4)
 
 
 def main(argv):
+    logging.basicConfig(level=logging.INFO, format='%(message)s')
     if len(argv) != 4:
         print_usage()
         exit(2)
diff --git a/scripts/python/mkdocstrings_handlers/cpp/__init__.py b/scripts/python/mkdocstrings_handlers/cpp/__init__.py
index 09e53ab2e..67e68e804 100644
--- a/scripts/python/mkdocstrings_handlers/cpp/__init__.py
+++ b/scripts/python/mkdocstrings_handlers/cpp/__init__.py
@@ -115,9 +115,7 @@ def render_decl(d: Definition) -> str:
     if d.id is not None:
         text += f'<a id="{d.id}">\n'
     text += '<pre><code class="language-cpp decl">'
-
     text += '<div>'
-    end = ';'
     if d.is_static:
         text += 'static '
     if d.kind == 'function' or d.kind == 'variable':
@@ -125,13 +123,11 @@ def render_decl(d: Definition) -> str:
     else:
         text += d.kind + ' '
     text += d.name
-
     if d.params is not None:
         params = ', '.join([
             (p.type + ' ' if p.type else '') + p.name for p in d.params])
         text += '(' + escape_html(params) + ')'
-
-    text += end
+    text += ';'
     text += '</div>'
     text += '</code></pre>\n'
     if d.id is not None:
@@ -144,14 +140,14 @@ def __init__(self, **kwargs: Any) -> None:
         super().__init__(handler='cpp', **kwargs)
 
         headers = [
-            'cqasm-py.hpp'
+            'cqasm_python.hpp'
         ]
 
         # Run doxygen.
         cmd = ['doxygen', '-']
         scripts_dir = Path(__file__).parents[3]
         top_dir = os.path.dirname(scripts_dir)
-        include_dir = os.path.join(top_dir, 'include', 'v3x')
+        include_dir = os.path.join(top_dir, 'include', 'libqasm', 'v3x')
         self._ns2doxyxml = {}
         build_dir = os.path.join(top_dir, 'build', 'Release', 'doc', 'cpp')
         os.makedirs(build_dir, exist_ok=True)
@@ -168,7 +164,7 @@ def __init__(self, **kwargs: Any) -> None:
             AUTOLINK_SUPPORT  = NO
             MACRO_EXPANSION   = YES
             PREDEFINED        = _WIN32=1 \
-                                __linux__=1 \
+                                __linux__=1
             '''
             .format(' '.join([os.path.join(include_dir, h) for h in headers]), self._doxyxml_dir)
             .encode('utf-8')
@@ -179,7 +175,7 @@ def __init__(self, **kwargs: Any) -> None:
         # Merge all file-level XMLs into one to simplify search.
         self._file_doxyxml = None
         for h in headers:
-            filename = h.replace(".hpp", "_8hpp.xml")
+            filename = h.replace("_", "__").replace(".hpp", "_8hpp.xml")
             with open(os.path.join(self._doxyxml_dir, filename)) as f:
                 doxyxml = ElementTree.parse(f)
                 if self._file_doxyxml is None:
diff --git a/scripts/python/mkdocstrings_handlers/emscripten/__init__.py b/scripts/python/mkdocstrings_handlers/emscripten/__init__.py
index cfa3b9326..b903606ce 100644
--- a/scripts/python/mkdocstrings_handlers/emscripten/__init__.py
+++ b/scripts/python/mkdocstrings_handlers/emscripten/__init__.py
@@ -35,18 +35,18 @@ def __init__(
 # A map from Doxygen to HTML tags.
 tag_map = {
     'bold': 'b',
-    'emphasis': 'em',
     'computeroutput': 'code',
+    'emphasis': 'em',
     'para': 'p',
     'programlisting': 'pre',
-    'verbatim': 'pre'
+    'verbatim': 'pre',
 }
 
 # A map from Doxygen tags to text.
 tag_text_map = {
     'codeline': '',
     'highlight': '',
-    'sp': ' '
+    'sp': ' ',
 }
 
 
@@ -61,6 +61,7 @@ def doxyxml2html(nodes: List[ElementTree.Element]):
         if not tag:
             out += tag_text_map[n.tag]
         out += '<' + tag + '>' if tag else ''
+        # Emscripten examples use Typescript syntax
         out += '<code class="language-typescript">' if tag == 'pre' else ''
         if n.text:
             out += escape_html(n.text)
@@ -112,10 +113,10 @@ def render_decl(d: Definition) -> str:
     text = ''
     if d.id is not None:
         text += f'<a id="{d.id}">\n'
-    text += '<pre><code class="language-typescript decl">'
+    # Emscripten declarations use C++ syntax
+    text += '<pre><code class="language-cpp decl">'
 
     text += '<div>'
-    end = ';'
     if d.is_static:
         text += 'static '
     if d.kind == 'function' or d.kind == 'variable':
@@ -129,7 +130,7 @@ def render_decl(d: Definition) -> str:
             (p.type + ' ' if p.type else '') + p.name for p in d.params])
         text += '(' + escape_html(params) + ')'
 
-    text += end
+    text += ';'
     text += '</div>'
     text += '</code></pre>\n'
     if d.id is not None:
diff --git a/scripts/python/mkdocstrings_handlers/python/__init__.py b/scripts/python/mkdocstrings_handlers/python/__init__.py
index e8c5e28af..a5039e78f 100644
--- a/scripts/python/mkdocstrings_handlers/python/__init__.py
+++ b/scripts/python/mkdocstrings_handlers/python/__init__.py
@@ -49,7 +49,7 @@ def __init__(
 tag_text_map = {
     'codeline': '',
     'highlight': '',
-    'sp': ' '
+    'sp': ' ',
 }
 
 
@@ -135,11 +135,11 @@ def render_decl(d: Definition) -> str:
             text += '(' + escape_html(params) + ')'
         if len(d.type) > 0:
             text += ' -> ' + escape_html(d.type)
+        text += ':'
     elif d.kind == 'variable':
         text += d.name + ': ' + escape_html(d.type)
     else:
         text += d.kind + ' ' + remove_namespaces_suffix(d.name)
-
     text += '</div>'
     text += '</code></pre>\n'
     if d.id is not None:
@@ -176,7 +176,7 @@ def __init__(self, **kwargs: Any) -> None:
             AUTOLINK_SUPPORT  = NO
             MACRO_EXPANSION   = YES
             PREDEFINED        = _WIN32=1 \
-                                __linux__=1 \
+                                __linux__=1
             '''
             .format(' '.join([os.path.join(include_dir, h) for h in headers]), self._doxyxml_dir)
             .encode('utf-8')
diff --git a/scripts/run_cpp_linters.py b/scripts/run_cpp_linters.py
new file mode 100644
index 000000000..e19f883fe
--- /dev/null
+++ b/scripts/run_cpp_linters.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+
+import logging
+import os
+import subprocess
+import sys
+
+
+def print_usage():
+    usage_str = '''
+Usage:
+    python3 run_cpp_linters.py <ROOT_FOLDER>
+Where:
+    ROOT_FOLDER: folder containing the .clang-format, .clang-tidy, and C++ files.
+Example:
+    python3 run_cpp_linters.py .
+    '''
+    logging.info(usage_str)
+
+
+def get_list_of_cpp_files(root_folder: os.PathLike) -> list[str]:
+    ret: list[str] = []
+    file_types = ['cpp', 'hpp']
+    input_subfolders = ['emscripten', 'include', 'src', 'test']
+    for input_subfolder in input_subfolders:
+        input_folder = os.path.join(root_folder, input_subfolder)
+        for root, dirs, files in os.walk(input_folder):
+            for file in files:
+                for file_type in file_types:
+                    if file.endswith(file_type):
+                        ret += [str(os.path.join(root, file))]
+    return ret
+
+
+def run_clang_format(root_folder: os.PathLike):
+    logging.info("Running clang-format")
+    file_list = get_list_of_cpp_files(root_folder)
+    try:
+        format_file_path = os.path.join(root_folder, ".clang-format")
+        file_list_str = " ".join(file_list)
+        command = f"clang-format-18 -i --style=file:{format_file_path} --verbose {file_list_str}"
+        subprocess.run(command, shell=True)
+    except FileNotFoundError as err:
+        print("Error running clang-format: {}".format(err.strerror))
+        exit(3)
+
+
+def run_clang_tidy(root_folder: os.PathLike):
+    pass
+
+
+def run_cpp_linters(root_folder: os.PathLike):
+    logging.info("Running C++ linters")
+    run_clang_format(root_folder)
+    run_clang_tidy(root_folder)
+
+
+def main(argv):
+    logging.basicConfig(level=logging.INFO, format='%(message)s')
+    if len(argv) != 2:
+        print_usage()
+        exit(2)
+    run_cpp_linters(os.path.abspath(argv[1]))
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/setup.py b/setup.py
index 18968f8fd..829753b54 100755
--- a/setup.py
+++ b/setup.py
@@ -16,10 +16,10 @@
 
 def get_version(verbose=False):
     """Extract version information from source code"""
-    inc_dir = root_dir + os.sep + "include"  # C++ include directory
-    matcher = re.compile('static const char \*version\{ "(.*)" \}')
+    inc_dir = os.path.join(root_dir, "include", "libqasm")  # C++ include directory
+    matcher = re.compile('static const char\* version\{ "(.*)" \}')
     version = None
-    with open(os.path.join(inc_dir, "version.hpp"), "r") as f:
+    with open(os.path.join(inc_dir, "versioning.hpp"), "r") as f:
         for ln in f:
             m = matcher.match(ln)
             if m:
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 07bc4d3b9..2afc1b9a9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -96,11 +96,11 @@ message(STATUS "TREE_GEN_EXECUTABLE: ${TREE_GEN_EXECUTABLE}")
 
 # List of non-generated sources.
 set(CQASM_COMMON_SOURCES
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-annotations.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-error.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-string-builder.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-utils.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-version.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/annotations.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/error.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/string_builder.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/version.cpp"
 )
 
 #-------------------------------------------------------------------------------
@@ -126,7 +126,7 @@ antlr_target(
     "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/generate_antlr_parser.py"
     "${CMAKE_CURRENT_SOURCE_DIR}/v3x"  # Input dir (grammar files)
     "${CMAKE_CURRENT_BINARY_DIR}/v3x"  # Sources output dir (lexer and parser cpp files)
-    "${CMAKE_CURRENT_BINARY_DIR}/../include/v3x"  # Include output dir (lexer and parser h files)
+    "${CMAKE_CURRENT_BINARY_DIR}/../include/libqasm/v3x"  # Include output dir (lexer and parser h files)
     "${ANTLR_OUTPUTS}"  # List of generated files
 )
 list(APPEND CQASM_V3X_SOURCES ${ANTLR_OUTPUTS})
@@ -134,44 +134,44 @@ list(APPEND CQASM_V3X_SOURCES ${ANTLR_OUTPUTS})
 # Generate the abstract syntax tree classes.
 generate_tree_py(
     "${TREE_GEN_EXECUTABLE}"
-    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/cqasm-ast.tree"
-    "${CMAKE_CURRENT_BINARY_DIR}/../include/v3x/cqasm-ast-gen.hpp"
-    "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-ast-gen.cpp"
-    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-ast-gen.py"
+    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/ast.tree"
+    "${CMAKE_CURRENT_BINARY_DIR}/../include/libqasm/v3x/ast_generated.hpp"
+    "${CMAKE_CURRENT_BINARY_DIR}/v3x/ast_generated.cpp"
+    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/ast_generated.py"
 )
-list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-ast-gen.cpp")
+list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/ast_generated.cpp")
 
 # Generate the type tree classes.
 generate_tree_py(
     "${TREE_GEN_EXECUTABLE}"
-    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/cqasm-types.tree"
-    "${CMAKE_CURRENT_BINARY_DIR}/../include/v3x/cqasm-types-gen.hpp"
-    "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-types-gen.cpp"
-    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-types-gen.py"
+    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/types.tree"
+    "${CMAKE_CURRENT_BINARY_DIR}/../include/libqasm/v3x/types_generated.hpp"
+    "${CMAKE_CURRENT_BINARY_DIR}/v3x/types_generated.cpp"
+    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/types_generated.py"
 )
-list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-types-gen.cpp")
+list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/types_generated.cpp")
 
 # Generate the semantic value classes.
 # Values can be either known at compile-time or only at runtime,
 # but their type is resolved to one of the type tree nodes at compile-time.
 generate_tree_py(
     "${TREE_GEN_EXECUTABLE}"
-    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/cqasm-values.tree"
-    "${CMAKE_CURRENT_BINARY_DIR}/../include/v3x/cqasm-values-gen.hpp"
-    "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-values-gen.cpp"
-    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-values-gen.py"
+    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/values.tree"
+    "${CMAKE_CURRENT_BINARY_DIR}/../include/libqasm/v3x/values_generated.hpp"
+    "${CMAKE_CURRENT_BINARY_DIR}/v3x/values_generated.cpp"
+    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/values_generated.py"
 )
-list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-values-gen.cpp")
+list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/values_generated.cpp")
 
 # Generate the semantic tree classes.
 generate_tree_py(
     "${TREE_GEN_EXECUTABLE}"
-    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/cqasm-semantic.tree"
-    "${CMAKE_CURRENT_BINARY_DIR}/../include/v3x/cqasm-semantic-gen.hpp"
-    "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-semantic-gen.cpp"
-    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/cqasm-semantic-gen.py"
+    "${CMAKE_CURRENT_SOURCE_DIR}/v3x/semantic.tree"
+    "${CMAKE_CURRENT_BINARY_DIR}/../include/libqasm/v3x/semantic_generated.hpp"
+    "${CMAKE_CURRENT_BINARY_DIR}/v3x/semantic_generated.cpp"
+    "${LIBQASM_GENERATED_PYTHON_FILES}/v3x/semantic_generated.py"
 )
-list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/cqasm-semantic-gen.cpp")
+list(APPEND CQASM_V3X_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/v3x/semantic_generated.cpp")
 
 #-------------------------------------------------------------------------------
 # Compilation instructions
@@ -221,7 +221,7 @@ target_include_directories(cqasm-lib-obj
     PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/"
     PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include/"
     PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/../include/"
-    PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/../include/v3x"
+    PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/../include/libqasm/v3x"
     PRIVATE "${antlr4-runtime_INCLUDE_DIRS}/"
     PRIVATE "${fmt_INCLUDE_DIRS}/"
     PRIVATE "${range-v3_INCLUDE_DIRS}/"
@@ -323,7 +323,7 @@ install(
 install(
     DIRECTORY
         "${CMAKE_CURRENT_SOURCE_DIR}/../include/"
-        "${CMAKE_CURRENT_BINARY_DIR}/../include/v3x"
+        "${CMAKE_CURRENT_BINARY_DIR}/../include/"
     DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
     FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h" PATTERN "*.inc"
 )
diff --git a/src/cqasm-annotations.cpp b/src/annotations.cpp
similarity index 79%
rename from src/cqasm-annotations.cpp
rename to src/annotations.cpp
index 7e2e72f0b..73f48178d 100644
--- a/src/cqasm-annotations.cpp
+++ b/src/annotations.cpp
@@ -1,17 +1,17 @@
 /** \file
- * Implementation for \ref include/cqasm-annotations.hpp "cqasm-annotations.hpp".
+ * Implementation for \ref include/libqasm/annotations.hpp "annotations.hpp".
  */
 
-#include "cqasm-annotations.hpp"
+#include "libqasm/annotations.hpp"
 
 #include <algorithm>  // min, max
 #include <iostream>
 
-#include "cqasm-annotations-constants.hpp"
+#include "libqasm/annotations_constants.hpp"
 
 namespace cqasm::annotations {
 
-SourceLocation::Range::Range(const Index &f, const Index &l)
+SourceLocation::Range::Range(const Index& f, const Index& l)
 : first{ f }
 , last{ l } {
     last = std::max<Index>(last, first);
@@ -20,7 +20,7 @@ SourceLocation::Range::Range(const Index &f, const Index &l)
 /**
  * Constructs a source location object.
  */
-SourceLocation::SourceLocation(const std::optional<std::string> &file_name_, const Range &range_)
+SourceLocation::SourceLocation(const std::optional<std::string>& file_name_, const Range& range_)
 : file_name{ file_name_ }
 , range{ range_ } {
     if (file_name.has_value() && file_name.value().empty()) {
@@ -31,7 +31,7 @@ SourceLocation::SourceLocation(const std::optional<std::string> &file_name_, con
 /**
  * Expands the location range to contain the given location in the source file.
  */
-void SourceLocation::expand_to_include(const Index &index) {
+void SourceLocation::expand_to_include(const Index& index) {
     range.first = std::min<Index>(range.first, index);
     range.last = std::max<Index>(range.last, index);
 }
@@ -39,7 +39,7 @@ void SourceLocation::expand_to_include(const Index &index) {
 /**
  * Stream << overload for source location objects.
  */
-std::ostream &operator<<(std::ostream &os, const SourceLocation &object) {
+std::ostream& operator<<(std::ostream& os, const SourceLocation& object) {
     // Print file name.
     os << object.file_name.value_or(unknown_file_name);
 
diff --git a/src/cqasm-error.cpp b/src/error.cpp
similarity index 87%
rename from src/cqasm-error.cpp
rename to src/error.cpp
index 3a706a214..9274c032b 100644
--- a/src/cqasm-error.cpp
+++ b/src/error.cpp
@@ -1,14 +1,12 @@
 /** \file
- * Implementation for \ref include/cqasm-error.hpp "cqasm-error.hpp".
+ * Implementation for \ref include/libqasm/error.hpp "libqasm/error.hpp".
  */
 
-#include "cqasm-error.hpp"
+#include "libqasm/error.hpp"
 
 #include <fmt/format.h>
 
-#include <regex>
-
-#include "cqasm-utils.hpp"  // url_encode
+#include "libqasm/utils.hpp"  // url_encode
 
 namespace cqasm::error {
 
@@ -16,7 +14,7 @@ namespace cqasm::error {
  * Constructs a new error.
  * If node is a non-null annotatable with a location node, its location information is attached.
  */
-Error::Error(const std::string &message, const tree::Annotatable *node)
+Error::Error(const std::string& message, const tree::Annotatable* node)
 : std::runtime_error{ !message.empty() ? message : unknown_error_message }
 , message_{ !message.empty() ? message : unknown_error_message } {
     if (node) {
@@ -27,7 +25,7 @@ Error::Error(const std::string &message, const tree::Annotatable *node)
 /**
  * Constructs a new error from a message and a source location.
  */
-Error::Error(const std::string &message, std::shared_ptr<annotations::SourceLocation> location)
+Error::Error(const std::string& message, std::shared_ptr<annotations::SourceLocation> location)
 : std::runtime_error{ !message.empty() ? message : unknown_error_message }
 , message_{ !message.empty() ? message : unknown_error_message }
 , location_{ std::move(location) } {}
@@ -35,8 +33,8 @@ Error::Error(const std::string &message, std::shared_ptr<annotations::SourceLoca
 /**
  * Constructs a new error from a message and all the fields of a source location.
  */
-Error::Error(const std::string &message, const std::optional<std::string> &file_name,
-    const annotations::SourceLocation::Range &range)
+Error::Error(const std::string& message, const std::optional<std::string>& file_name,
+    const annotations::SourceLocation::Range& range)
 : std::runtime_error{ !message.empty() ? message.c_str() : unknown_error_message }
 , message_{ !message.empty() ? message : unknown_error_message }
 , location_{ std::make_shared<annotations::SourceLocation>(file_name, range) } {}
@@ -46,7 +44,7 @@ Error::Error(const std::string &message, const std::optional<std::string> &file_
  * if the error doesn't already have such a context.
  * If it does, this is no-op.
  */
-void Error::context(const tree::Annotatable &node) {
+void Error::context(const tree::Annotatable& node) {
     if (!location_) {
         if (auto loc = node.get_annotation_ptr<annotations::SourceLocation>()) {
             location_ = std::make_shared<annotations::SourceLocation>(*loc);
@@ -57,7 +55,7 @@ void Error::context(const tree::Annotatable &node) {
 /**
  * Returns the exception-style message.
  */
-const char *Error::what() const noexcept {
+const char* Error::what() const noexcept {
     what_message_ = fmt::format("Error{}: {}",
         location_ ? fmt::format(" at {}", *location_) : std::string{},
         !message_.empty() ? message_ : unknown_error_message);
@@ -67,7 +65,7 @@ const char *Error::what() const noexcept {
 /**
  * Stream << overload for Error.
  */
-std::ostream &operator<<(std::ostream &os, const Error &error) {
+std::ostream& operator<<(std::ostream& os, const Error& error) {
     return os << error.what();
 }
 
diff --git a/src/cqasm-string-builder.cpp b/src/string_builder.cpp
similarity index 70%
rename from src/cqasm-string-builder.cpp
rename to src/string_builder.cpp
index 7088f3d28..58c210317 100644
--- a/src/cqasm-string-builder.cpp
+++ b/src/string_builder.cpp
@@ -1,22 +1,22 @@
 /** \file
- * Implementation for \ref include/cqasm-string-builder.hpp "cqasm-string-builder.hpp".
+ * Implementation for \ref include/libqasm/string_builder.hpp "libqasm/string_builder.hpp".
  */
 
-#include "cqasm-string-builder.hpp"
+#include "libqasm/string_builder.hpp"
 
 namespace cqasm::ast {
 
 /**
  * Pushes a string fragment into the string.
  */
-void StringBuilder::push_string(const std::string &str) {
+void StringBuilder::push_string(const std::string& str) {
     stream << str;
 }
 
 /**
  * Pushes an escape sequence into the string.
  */
-void StringBuilder::push_escape(const std::string &escape) {
+void StringBuilder::push_escape(const std::string& escape) {
     if (escape == "\\t") {
         stream << '\t';
     } else if (escape == "\\n") {
diff --git a/src/cqasm-utils.cpp b/src/utils.cpp
similarity index 93%
rename from src/cqasm-utils.cpp
rename to src/utils.cpp
index 78714492a..98263b453 100644
--- a/src/cqasm-utils.cpp
+++ b/src/utils.cpp
@@ -1,8 +1,8 @@
 /** \file
- * Implementation for \ref include/cqasm-utils.hpp "cqasm-utils.hpp".
+ * Implementation for \ref include/libqasm/utils.hpp "libqasm/utils.hpp".
  */
 
-#include "cqasm-utils.hpp"
+#include "libqasm/utils.hpp"
 
 #include <fmt/format.h>
 
diff --git a/src/v3x/CMakeLists.txt b/src/v3x/CMakeLists.txt
index 87613e266..ea8441981 100644
--- a/src/v3x/CMakeLists.txt
+++ b/src/v3x/CMakeLists.txt
@@ -1,22 +1,23 @@
 # List of non-generated sources.
 set(CQASM_V3X_SOURCES
-    "${CMAKE_CURRENT_SOURCE_DIR}/AnalyzeTreeGenAstVisitor.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/BuildTreeGenAstVisitor.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/CustomErrorListener.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/ScannerAntlr.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-analysis-result.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-analyzer.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-core-function.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-instruction.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-parse-helper.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-parse-result.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-primitives.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-py.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-resolver.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-types.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-values.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/analysis_result.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/analyzer.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/antlr_custom_error_listener.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/antlr_scanner.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/core_function.cpp"
     "${CMAKE_CURRENT_SOURCE_DIR}/cqasm.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/register-consteval-core-functions.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/register-instructions.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm_python.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/instruction.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/instruction_set.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/parse_helper.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/parse_result.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/primitives.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/register_consteval_core_functions.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/register_instructions.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/resolver.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/semantic_analyzer.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/syntactic_analyzer.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/types.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/values.cpp"
     PARENT_SCOPE
 )
diff --git a/src/v3x/CqasmLexer.g4 b/src/v3x/CqasmLexer.g4
index 84121c537..3c5f30785 100644
--- a/src/v3x/CqasmLexer.g4
+++ b/src/v3x/CqasmLexer.g4
@@ -50,6 +50,9 @@ TERNARY_CONDITIONAL_OP: '?';
 VERSION: 'version' -> pushMode(VERSION_STATEMENT);
 MEASURE: 'measure';
 RESET: 'reset';
+INV: 'inv';
+POW: 'pow';
+CTRL: 'ctrl';
 QUBIT_TYPE: 'qubit';
 BIT_TYPE: 'bit';
 
@@ -57,9 +60,9 @@ BIT_TYPE: 'bit';
 BOOLEAN_LITERAL: 'true' | 'false';
 INTEGER_LITERAL: Digit+;
 FLOAT_LITERAL:
-    Digit+ '.' Digit+ Exponent?
-    | Digit+ '.' Exponent?  // float literals can end with a dot
-    | '.' Digit+ Exponent?;  // or just start with a dot
+    Digit+ DOT Digit+ Exponent?
+    | Digit+ DOT Exponent?  // float literals can end with a dot
+    | DOT Digit+ Exponent?;  // or just start with a dot
 fragment Digit: [0-9];
 fragment Exponent: [eE][-+]?Digit+;
 
diff --git a/src/v3x/CqasmParser.g4 b/src/v3x/CqasmParser.g4
index efb41a278..dc0281495 100644
--- a/src/v3x/CqasmParser.g4
+++ b/src/v3x/CqasmParser.g4
@@ -31,9 +31,22 @@ globalBlockStatement:
 variableDefinition: type IDENTIFIER;
 
 instruction:
+    gateInstruction
+    | nonGateInstruction
+    ;
+
+gateInstruction: gate expressionList;
+
+gate:
+    INV DOT gate  # invGate
+    | POW OPEN_PARENS expression CLOSE_PARENS DOT gate  # powGate
+    | CTRL DOT gate # ctrlGate
+    | IDENTIFIER (OPEN_PARENS expression CLOSE_PARENS)?  # namedGate
+    ;
+
+nonGateInstruction:
     expression EQUALS MEASURE expression  # measureInstruction
     | RESET expression?  # resetInstruction
-    | IDENTIFIER (OPEN_PARENS expression CLOSE_PARENS)? expressionList  # gate
     ;
 
 type: quantumType;
diff --git a/src/v3x/ScannerAntlr.cpp b/src/v3x/ScannerAntlr.cpp
deleted file mode 100644
index c8a5677c0..000000000
--- a/src/v3x/ScannerAntlr.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "v3x/ScannerAntlr.hpp"
-
-#include <antlr4-runtime.h>
-#include <fmt/format.h>
-
-#include <filesystem>
-
-#include "v3x/BuildTreeGenAstVisitor.hpp"
-#include "v3x/CqasmLexer.h"
-#include "v3x/CqasmParser.h"
-#include "v3x/CustomErrorListener.hpp"
-#include "v3x/cqasm-ast.hpp"
-#include "v3x/cqasm-parse-result.hpp"
-
-namespace fs = std::filesystem;
-
-namespace cqasm::v3x::parser {
-
-ScannerAdaptor::~ScannerAdaptor() {}
-
-ScannerAntlr::ScannerAntlr(
-    std::unique_ptr<BuildCustomAstVisitor> build_visitor_up, std::unique_ptr<CustomErrorListener> error_listener_up)
-: build_visitor_up_{ std::move(build_visitor_up) }
-, error_listener_up_{ std::move(error_listener_up) } {}
-
-ScannerAntlr::~ScannerAntlr() {}
-
-cqasm::v3x::parser::ParseResult ScannerAntlr::parse_(antlr4::ANTLRInputStream &is) {
-    CqasmLexer lexer{ &is };
-    lexer.removeErrorListeners();
-    lexer.addErrorListener(error_listener_up_.get());
-    antlr4::CommonTokenStream tokens{ &lexer };
-
-    CqasmParser parser{ &tokens };
-    parser.removeErrorListeners();
-    parser.addErrorListener(error_listener_up_.get());
-    auto ast = parser.program();
-
-    build_visitor_up_->addErrorListener(error_listener_up_.get());
-    auto custom_ast = build_visitor_up_->visitProgram(ast);
-    return cqasm::v3x::parser::ParseResult{
-        std::any_cast<cqasm::v3x::ast::One<cqasm::v3x::ast::Program>>(custom_ast),  // root
-        {}  // error
-    };
-}
-
-ScannerAntlrFile::ScannerAntlrFile(std::unique_ptr<BuildCustomAstVisitor> build_visitor_up,
-    std::unique_ptr<CustomErrorListener> error_listener_up, const std::string &file_path)
-: ScannerAntlr{ std::move(build_visitor_up), std::move(error_listener_up) }
-, file_path_{ file_path } {
-    if (!fs::exists(file_path_) || !fs::is_regular_file(file_path_)) {
-        throw cqasm::error::ParseError{ fmt::format("ScannerAntlrFile couldn't access file '{}'.", file_path_) };
-    }
-}
-
-ScannerAntlrFile::~ScannerAntlrFile() {}
-
-cqasm::v3x::parser::ParseResult ScannerAntlrFile::parse() {
-    antlr4::ANTLRFileStream ifs{};
-    ifs.loadFromFile(file_path_);
-    return parse_(ifs);
-}
-
-ScannerAntlrString::ScannerAntlrString(std::unique_ptr<BuildCustomAstVisitor> build_visitor_up,
-    std::unique_ptr<CustomErrorListener> error_listener_up, const std::string &data)
-: ScannerAntlr{ std::move(build_visitor_up), std::move(error_listener_up) }
-, data_{ data } {}
-
-ScannerAntlrString::~ScannerAntlrString() {}
-
-cqasm::v3x::parser::ParseResult ScannerAntlrString::parse() {
-    antlr4::ANTLRInputStream is{ data_ };
-    return parse_(is);
-}
-
-}  // namespace cqasm::v3x::parser
diff --git a/src/v3x/cqasm-analysis-result.cpp b/src/v3x/analysis_result.cpp
similarity index 89%
rename from src/v3x/cqasm-analysis-result.cpp
rename to src/v3x/analysis_result.cpp
index 747374360..b137dd0fa 100644
--- a/src/v3x/cqasm-analysis-result.cpp
+++ b/src/v3x/analysis_result.cpp
@@ -1,8 +1,8 @@
-#include "v3x/cqasm-analysis-result.hpp"
+#include "libqasm/v3x/analysis_result.hpp"
 
 #include <fmt/format.h>
 
-#include "cqasm-result.hpp"
+#include "libqasm/result.hpp"
 
 namespace cqasm::v3x::analyzer {
 
@@ -11,7 +11,7 @@ namespace cqasm::v3x::analyzer {
  * The exception is always an AnalysisFailed, deriving from std::runtime_error.
  * The actual error messages are in this case first written to the given output stream, defaulting to stderr.
  */
-Root AnalysisResult::unwrap(std::ostream &out) const {
+Root AnalysisResult::unwrap(std::ostream& out) const {
     if (errors.empty()) {
         return root;
     }
diff --git a/src/v3x/cqasm-analyzer.cpp b/src/v3x/analyzer.cpp
similarity index 66%
rename from src/v3x/cqasm-analyzer.cpp
rename to src/v3x/analyzer.cpp
index 61e03837b..a91c00b60 100644
--- a/src/v3x/cqasm-analyzer.cpp
+++ b/src/v3x/analyzer.cpp
@@ -1,8 +1,8 @@
 /** \file
- * Implementation for \ref include/v3x/cqasm-analyzer.hpp "v3x/cqasm-analyzer.hpp".
+ * Implementation for \ref include/libqasm/v3x/analyzer.hpp "libqasm/v3x/analyzer.hpp".
  */
 
-#include "v3x/cqasm-analyzer.hpp"
+#include "libqasm/v3x/analyzer.hpp"
 
 #include <fmt/format.h>
 
@@ -10,12 +10,12 @@
 #include <numbers>
 #include <stdexcept>  // runtime_error
 
-#include "cqasm-error.hpp"
-#include "v3x/AnalyzeTreeGenAstVisitor.hpp"
-#include "v3x/cqasm-core-function.hpp"
-#include "v3x/cqasm-parse-helper.hpp"
-#include "v3x/register-consteval-core-functions.hpp"
-#include "v3x/register-instructions.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/v3x/core_function.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
+#include "libqasm/v3x/register_consteval_core_functions.hpp"
+#include "libqasm/v3x/register_instructions.hpp"
+#include "libqasm/v3x/semantic_analyzer.hpp"
 
 namespace cqasm::v3x::analyzer {
 
@@ -23,7 +23,7 @@ namespace cqasm::v3x::analyzer {
  * Creates a new semantic analyzer.
  * Creates a global scope.
  */
-Analyzer::Analyzer(const primitives::Version &api_version)
+Analyzer::Analyzer(const primitives::Version& api_version)
 : api_version{ api_version }
 , scope_stack_{ Scope{} } {
     if (api_version != "3.0") {
@@ -32,26 +32,26 @@ Analyzer::Analyzer(const primitives::Version &api_version)
     global_scope().block = tree::make<semantic::Block>();
 }
 
-[[nodiscard]] Scope &Analyzer::global_scope() {
+[[nodiscard]] Scope& Analyzer::global_scope() {
     return scope_stack_.back();
 }
-[[nodiscard]] Scope &Analyzer::current_scope() {
+[[nodiscard]] Scope& Analyzer::current_scope() {
     return scope_stack_.front();
 }
 [[nodiscard]] tree::One<semantic::Block> Analyzer::current_block() {
     return current_scope().block;
 }
-[[nodiscard]] tree::Any<semantic::Variable> &Analyzer::current_variables() {
+[[nodiscard]] tree::Any<semantic::Variable>& Analyzer::current_variables() {
     return current_scope().variables;
 }
 
-[[nodiscard]] const Scope &Analyzer::global_scope() const {
+[[nodiscard]] const Scope& Analyzer::global_scope() const {
     return scope_stack_.back();
 }
-[[nodiscard]] const Scope &Analyzer::current_scope() const {
+[[nodiscard]] const Scope& Analyzer::current_scope() const {
     return scope_stack_.front();
 }
-[[nodiscard]] const tree::Any<semantic::Variable> &Analyzer::current_variables() const {
+[[nodiscard]] const tree::Any<semantic::Variable>& Analyzer::current_variables() const {
     return current_scope().variables;
 }
 
@@ -84,13 +84,13 @@ void Analyzer::register_default_instructions() {
 /**
  * Analyzes the given AST.
  */
-AnalysisResult Analyzer::analyze(ast::Program &ast) {
-    auto analyze_visitor_up = std::make_unique<AnalyzeTreeGenAstVisitor>(*this);
+AnalysisResult Analyzer::analyze(ast::Program& ast) {
+    auto analyze_visitor_up = std::make_unique<SemanticAnalyzer>(*this);
     auto result = std::any_cast<AnalysisResult>(analyze_visitor_up->visit_program(ast));
     if (result.errors.empty()) {
         try {
             result.root.check_well_formed();
-        } catch (const std::runtime_error &err) {
+        } catch (const std::runtime_error& err) {
             fmt::print("Error: {}\nDumping semantic AST...\n---\n", err.what());
             result.root->dump_raw_pointers();
             fmt::print("---\n");
@@ -105,7 +105,7 @@ AnalysisResult Analyzer::analyze(ast::Program &ast) {
  * If there are parse errors, they are moved into the AnalysisResult error list,
  * and the root node will be empty.
  */
-AnalysisResult Analyzer::analyze(const parser::ParseResult &parse_result) {
+AnalysisResult Analyzer::analyze(const parser::ParseResult& parse_result) {
     if (!parse_result.errors.empty()) {
         return AnalysisResult{ {}, parse_result.errors };
     }
@@ -115,7 +115,7 @@ AnalysisResult Analyzer::analyze(const parser::ParseResult &parse_result) {
 /**
  * Parses and analyzes the given file.
  */
-AnalysisResult Analyzer::analyze_file(const std::string &file_name) {
+AnalysisResult Analyzer::analyze_file(const std::string& file_name) {
     return analyze(parser::parse_file(file_name, file_name));
 }
 
@@ -123,7 +123,7 @@ AnalysisResult Analyzer::analyze_file(const std::string &file_name) {
  * Parses and analyzes the given string.
  * The optional file_name argument will be used only for error messages.
  */
-AnalysisResult Analyzer::analyze_string(const std::string &data, const std::optional<std::string> &file_name) {
+AnalysisResult Analyzer::analyze_string(const std::string& data, const std::optional<std::string>& file_name) {
     return analyze(parser::parse_string(data, file_name));
 }
 
@@ -145,7 +145,7 @@ void Analyzer::pop_scope() {
 /**
  * Adds a statement to the current scope.
  */
-void Analyzer::add_statement_to_current_scope(const tree::One<semantic::Statement> &statement) {
+void Analyzer::add_statement_to_current_scope(const tree::One<semantic::Statement>& statement) {
     if (current_block().empty()) {
         throw error::AnalysisError{ "trying to add a statement but current block is empty" };
     }
@@ -167,7 +167,7 @@ void Analyzer::add_statement_to_current_scope(const tree::One<semantic::Statemen
 /**
  * Adds a variable to the current scope.
  */
-void Analyzer::add_variable_to_current_scope(const tree::One<semantic::Variable> &variable) {
+void Analyzer::add_variable_to_current_scope(const tree::One<semantic::Variable>& variable) {
     current_variables().add(variable);
 }
 
@@ -175,11 +175,11 @@ void Analyzer::add_variable_to_current_scope(const tree::One<semantic::Variable>
  * Resolves a variable.
  * Throws NameResolutionFailure if no variable by the given name exists.
  */
-values::Value Analyzer::resolve_variable(const std::string &name) const {
-    for (const auto &scope : scope_stack_) {
+values::Value Analyzer::resolve_variable(const std::string& name) const {
+    for (const auto& scope : scope_stack_) {
         try {
             return scope.variable_table.resolve(name);
-        } catch (const error::AnalysisError &) {
+        } catch (const error::AnalysisError&) {
             continue;
         }
     }
@@ -189,7 +189,7 @@ values::Value Analyzer::resolve_variable(const std::string &name) const {
 /**
  * Registers a variable.
  */
-void Analyzer::register_variable(const std::string &name, const values::Value &value) {
+void Analyzer::register_variable(const std::string& name, const values::Value& value) {
     current_scope().variable_table.add(name, value);
 }
 
@@ -201,7 +201,7 @@ void Analyzer::register_variable(const std::string &name, const values::Value &v
  * OverloadResolutionFailure if no overload of the function exists for the given arguments,
  * or otherwise returns the value returned by the function.
  */
-values::Value Analyzer::resolve_function(const std::string &name, const values::Values &args) const {
+values::Value Analyzer::resolve_function(const std::string& name, const values::Values& args) const {
     return global_scope().consteval_core_function_table.resolve(name, args);
 }
 
@@ -209,7 +209,7 @@ values::Value Analyzer::resolve_function(const std::string &name, const values::
  * Registers a consteval core function.
  */
 void Analyzer::register_consteval_core_function(
-    const std::string &name, const types::Types &param_types, const resolver::ConstEvalCoreFunction &function) {
+    const std::string& name, const types::Types& param_types, const resolver::ConstEvalCoreFunction& function) {
     global_scope().consteval_core_function_table.add(name, param_types, function);
 }
 
@@ -219,34 +219,54 @@ void Analyzer::register_consteval_core_function(
  * converted to types::Types for the other overload using types::from_spec.
  */
 void Analyzer::register_consteval_core_function(
-    const std::string &name, const std::string &param_types, const resolver::ConstEvalCoreFunction &function) {
+    const std::string& name, const std::string& param_types, const resolver::ConstEvalCoreFunction& function) {
     global_scope().consteval_core_function_table.add(name, types::from_spec(param_types), function);
 }
 
 /**
- * Resolves an instruction.
+ * Resolves a GateInstruction.
  * Throws NameResolutionFailure if no instruction by the given name exists,
  * OverloadResolutionFailure if no overload exists for the given arguments,
  * or otherwise returns the resolved instruction node.
  * Annotation data, line number information, and the condition still need to be set by the caller.
  */
 [[nodiscard]] tree::One<semantic::Instruction> Analyzer::resolve_instruction(
-    const std::string &name, const values::Values &args) const {
-    for (const auto &scope : scope_stack_) {
+    const std::string& name, const tree::One<semantic::Gate>& gate, const values::Values& args) const {
+    for (const auto& scope : scope_stack_) {
+        try {
+            return scope.instruction_table.resolve(name, gate, args);
+        } catch (const error::AnalysisError&) {
+            continue;
+        }
+    }
+    throw resolver::ResolutionFailure{ fmt::format(
+        "failed to resolve instruction '{}' with argument pack ({})", name, values::types_of(args)) };
+}
+
+/**
+ * Resolves a NonGateInstruction.
+ * Throws NameResolutionFailure if no instruction by the given name exists,
+ * OverloadResolutionFailure if no overload exists for the given arguments,
+ * or otherwise returns the resolved instruction node.
+ * Annotation data, line number information, and the condition still need to be set by the caller.
+ */
+[[nodiscard]] tree::One<semantic::Instruction> Analyzer::resolve_instruction(
+    const std::string& name, const values::Values& args) const {
+    for (const auto& scope : scope_stack_) {
         try {
             return scope.instruction_table.resolve(name, args);
-        } catch (const error::AnalysisError &) {
+        } catch (const error::AnalysisError&) {
             continue;
         }
     }
     throw resolver::ResolutionFailure{ fmt::format(
-        "failed to resolve instruction '{}' with argument pack {}", name, values::types_of(args)) };
+        "failed to resolve instruction '{}' with argument pack ({})", name, values::types_of(args)) };
 }
 
 /**
  * Registers an instruction type.
  */
-void Analyzer::register_instruction(const instruction::Instruction &instruction) {
+void Analyzer::register_instruction(const instruction::Instruction& instruction) {
     current_scope().instruction_table.add(instruction);
 }
 
@@ -254,8 +274,8 @@ void Analyzer::register_instruction(const instruction::Instruction &instruction)
  * Convenience method for registering an instruction type.
  * The arguments are passed straight to instruction::Instruction's constructor.
  */
-void Analyzer::register_instruction(const std::string &name, const std::optional<std::string> &param_types) {
-    register_instruction(instruction::Instruction{ name, param_types });
+void Analyzer::register_instruction(const std::string& name, const std::optional<std::string>& operand_types) {
+    register_instruction(instruction::Instruction{ name, operand_types });
 }
 
 }  // namespace cqasm::v3x::analyzer
diff --git a/src/v3x/CustomErrorListener.cpp b/src/v3x/antlr_custom_error_listener.cpp
similarity index 70%
rename from src/v3x/CustomErrorListener.cpp
rename to src/v3x/antlr_custom_error_listener.cpp
index 345eded0a..de54cdcac 100644
--- a/src/v3x/CustomErrorListener.cpp
+++ b/src/v3x/antlr_custom_error_listener.cpp
@@ -1,22 +1,22 @@
-#include "v3x/CustomErrorListener.hpp"
+#include "libqasm/v3x/antlr_custom_error_listener.hpp"
 
 #include <cstdint>  // uint32_t
 #include <stdexcept>
 
-#include "cqasm-annotations.hpp"
-#include "cqasm-error.hpp"
+#include "libqasm/annotations.hpp"
+#include "libqasm/error.hpp"
 
 namespace cqasm::v3x::parser {
 
-CustomErrorListener::CustomErrorListener(const std::optional<std::string> &file_name)
+AntlrCustomErrorListener::AntlrCustomErrorListener(const std::optional<std::string>& file_name)
 : file_name_{ file_name } {
     if (file_name_.has_value() && file_name_.value().empty()) {
         file_name_ = std::nullopt;
     }
 }
 
-void CustomErrorListener::syntaxError(antlr4::Recognizer * /* recognizer */, antlr4::Token *offendingSymbol,
-    size_t line, size_t charPositionInLine, const std::string &msg, std::exception_ptr /* e */) {
+void AntlrCustomErrorListener::syntaxError(antlr4::Recognizer* /* recognizer */, antlr4::Token* offendingSymbol,
+    size_t line, size_t charPositionInLine, const std::string& msg, std::exception_ptr /* e */) {
     // ANTLR provides a zero-based character position in line
     // We change it here to a one-based index, which is the more human-readable,
     // and the common option in text editors
@@ -35,7 +35,7 @@ void CustomErrorListener::syntaxError(antlr4::Recognizer * /* recognizer */, ant
             { static_cast<std::uint32_t>(line), static_cast<std::uint32_t>(end_column) } } };
 }
 
-void CustomErrorListener::syntaxError(size_t line, size_t charPositionInLine, const std::string &msg) {
+void AntlrCustomErrorListener::syntaxError(size_t line, size_t charPositionInLine, const std::string& msg) {
     syntaxError(nullptr, nullptr, line, charPositionInLine, msg, nullptr);
 }
 
diff --git a/src/v3x/antlr_scanner.cpp b/src/v3x/antlr_scanner.cpp
new file mode 100644
index 000000000..db8ca1179
--- /dev/null
+++ b/src/v3x/antlr_scanner.cpp
@@ -0,0 +1,76 @@
+#include "libqasm/v3x/antlr_scanner.hpp"
+
+#include <antlr4-runtime.h>
+#include <fmt/format.h>
+
+#include <filesystem>
+
+#include "libqasm/v3x/CqasmLexer.h"
+#include "libqasm/v3x/CqasmParser.h"
+#include "libqasm/v3x/antlr_custom_error_listener.hpp"
+#include "libqasm/v3x/ast.hpp"
+#include "libqasm/v3x/parse_result.hpp"
+#include "libqasm/v3x/syntactic_analyzer.hpp"
+
+namespace fs = std::filesystem;
+
+namespace cqasm::v3x::parser {
+
+ScannerAdaptor::~ScannerAdaptor() = default;
+
+AntlrScanner::AntlrScanner(std::unique_ptr<BaseSyntacticAnalyzer> build_visitor_up,
+    std::unique_ptr<AntlrCustomErrorListener> error_listener_up)
+: build_visitor_up_{ std::move(build_visitor_up) }
+, error_listener_up_{ std::move(error_listener_up) } {}
+
+AntlrScanner::~AntlrScanner() = default;
+
+cqasm::v3x::parser::ParseResult AntlrScanner::parse_(antlr4::ANTLRInputStream& is) {
+    CqasmLexer lexer{ &is };
+    lexer.removeErrorListeners();
+    lexer.addErrorListener(error_listener_up_.get());
+    antlr4::CommonTokenStream tokens{ &lexer };
+
+    CqasmParser parser{ &tokens };
+    parser.removeErrorListeners();
+    parser.addErrorListener(error_listener_up_.get());
+    auto ast = parser.program();
+
+    build_visitor_up_->addErrorListener(error_listener_up_.get());
+    auto custom_ast = build_visitor_up_->visitProgram(ast);
+    return cqasm::v3x::parser::ParseResult{
+        std::any_cast<cqasm::v3x::ast::One<cqasm::v3x::ast::Program>>(custom_ast),  // root
+        {}  // error
+    };
+}
+
+FileAntlrScanner::FileAntlrScanner(std::unique_ptr<BaseSyntacticAnalyzer> build_visitor_up,
+    std::unique_ptr<AntlrCustomErrorListener> error_listener_up, const std::string& file_path)
+: AntlrScanner{ std::move(build_visitor_up), std::move(error_listener_up) }
+, file_path_{ file_path } {
+    if (!fs::exists(file_path_) || !fs::is_regular_file(file_path_)) {
+        throw cqasm::error::ParseError{ fmt::format("FileAntlrScanner couldn't access file '{}'.", file_path_) };
+    }
+}
+
+FileAntlrScanner::~FileAntlrScanner() = default;
+
+cqasm::v3x::parser::ParseResult FileAntlrScanner::parse() {
+    antlr4::ANTLRFileStream ifs{};
+    ifs.loadFromFile(file_path_);
+    return parse_(ifs);
+}
+
+StringAntlrScanner::StringAntlrScanner(std::unique_ptr<BaseSyntacticAnalyzer> build_visitor_up,
+    std::unique_ptr<AntlrCustomErrorListener> error_listener_up, const std::string& data)
+: AntlrScanner{ std::move(build_visitor_up), std::move(error_listener_up) }
+, data_{ data } {}
+
+StringAntlrScanner::~StringAntlrScanner() = default;
+
+cqasm::v3x::parser::ParseResult StringAntlrScanner::parse() {
+    antlr4::ANTLRInputStream is{ data_ };
+    return parse_(is);
+}
+
+}  // namespace cqasm::v3x::parser
diff --git a/src/v3x/cqasm-ast.tree b/src/v3x/ast.tree
similarity index 83%
rename from src/v3x/cqasm-ast.tree
rename to src/v3x/ast.tree
index 1dfeafc87..94811e641 100644
--- a/src/v3x/cqasm-ast.tree
+++ b/src/v3x/ast.tree
@@ -2,14 +2,14 @@
 source
 
 # Header file for the abstract syntax tree node classes
-header "v3x/cqasm-ast-gen.hpp"
+header "libqasm/v3x/ast_generated.hpp"
 
 // Include tree base classes.
-include "cqasm-tree.hpp"
+include "libqasm/tree.hpp"
 tree_namespace cqasm::tree
 
 // Include primitive types.
-include "v3x/cqasm-primitives.hpp"
+include "libqasm/v3x/primitives.hpp"
 import cqasm.v3x.primitives
 
 // Initialization function to use to construct default values for the tree base classes and primitives
@@ -17,7 +17,7 @@ initialize_function cqasm::v3x::primitives::initialize
 serdes_functions cqasm::v3x::primitives::serialize cqasm::v3x::primitives::deserialize
 
 // Include SourceLocation annotation object for the debug dump generator
-src_include "cqasm-annotations.hpp"
+src_include "libqasm/annotations.hpp"
 location cqasm::annotations::SourceLocation
 
 
@@ -163,6 +163,13 @@ annotated {
     # Zero or more annotations attached to this object.
     annotations: Any<annotation_data>;
 
+    # A gate can be a named gate or a composition of gate modifiers acting on a gate.
+    gate {
+        name: One<identifier>;
+        gate: Maybe<gate>;
+        parameter: Maybe<expression>;
+    }
+
     statement {
         block_statement {
             # One variable declaration of some type.
@@ -172,24 +179,14 @@ annotated {
             }
 
             instruction {
-                gate {
-                    name: One<identifier>;
+                gate_instruction {
+                    gate: One<gate>;
                     operands: One<expression_list>;
                 }
 
-                # For some instructions, like the measure instruction,
-                # we want to differentiate between left hand side and right and side operands.
-                # For a measure instruction, the right hand side operand should be a qubit,
-                # and the left hand side operand a bit.
-                measure_instruction {
-                    name: One<identifier>;
-                    lhs: One<expression>;
-                    rhs: One<expression>;
-                }
-
-                reset_instruction {
-                    name: One<identifier>;
-                    operand: Maybe<expression>;
+                non_gate_instruction {
+                    name: One<keyword>;
+                    operands: One<expression_list>;
                 }
             }
         }
diff --git a/src/v3x/cqasm-core-function.cpp b/src/v3x/core_function.cpp
similarity index 60%
rename from src/v3x/cqasm-core-function.cpp
rename to src/v3x/core_function.cpp
index 8da4c167d..c723fad0f 100644
--- a/src/v3x/cqasm-core-function.cpp
+++ b/src/v3x/core_function.cpp
@@ -1,10 +1,12 @@
 /** \file
- * Implementation for \ref include/v3x/cqasm-core-function.hpp "v3x/cqasm-core-function.hpp".
+ * Implementation for \ref include/libqasm/v3x/core_function.hpp "libqasm/v3x/core_function.hpp".
  */
 
-#include "v3x/cqasm-core-function.hpp"
+#include "libqasm/v3x/core_function.hpp"
 
-#include "v3x/cqasm-semantic.hpp"
+#include <fmt/format.h>
+
+#include "libqasm/v3x/semantic.hpp"
 
 namespace cqasm::v3x::function {
 
@@ -13,7 +15,7 @@ namespace cqasm::v3x::function {
  * param_types is a shorthand type specification string as parsed by cqasm::types::from_spec().
  * return_type is a shorthand type specification char as parsed by cqasm::types::from_spec().
  */
-CoreFunction::CoreFunction(std::string name, const std::string &param_types, const char return_type)
+CoreFunction::CoreFunction(std::string name, const std::string& param_types, const char return_type)
 : name{ std::move(name) }
 , param_types{ types::from_spec(param_types) }
 , return_type{ types::from_spec(return_type) } {}
@@ -21,21 +23,21 @@ CoreFunction::CoreFunction(std::string name, const std::string &param_types, con
 /**
  * Equality operator.
  */
-bool CoreFunction::operator==(const CoreFunction &rhs) const {
+bool CoreFunction::operator==(const CoreFunction& rhs) const {
     return name == rhs.name && param_types.equals(rhs.param_types) && return_type.equals(rhs.return_type);
 }
 
 /**
  * Stream << overload for functions.
  */
-std::ostream &operator<<(std::ostream &os, const CoreFunction &function) {
-    return os << function.name << function.param_types;
+std::ostream& operator<<(std::ostream& os, const CoreFunction& function) {
+    return os << fmt::format("{}({}) -> {}", function.name, function.param_types, function.return_type);
 }
 
 /**
  * Stream << overload for function references.
  */
-std::ostream &operator<<(std::ostream &os, const CoreFunctionRef &function) {
+std::ostream& operator<<(std::ostream& os, const CoreFunctionRef& function) {
     return function.empty() ? os << "unresolved" : os << *function;
 }
 
@@ -44,32 +46,32 @@ std::ostream &operator<<(std::ostream &os, const CoreFunctionRef &function) {
 namespace cqasm::v3x::primitives {
 
 template <>
-void serialize(const function::CoreFunctionRef &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const function::CoreFunctionRef& obj, ::tree::cbor::MapWriter& map) {
     if (obj.empty()) {
         return;
     }
     map.append_string("n", obj->name);
-    auto aw = map.append_array("t");
-    for (const auto &t : obj->param_types) {
+    auto aw = map.append_array("pt");
+    for (const auto& t : obj->param_types) {
         aw.append_binary(::tree::base::serialize(::tree::base::Maybe<types::TypeBase>{ t.get_ptr() }));
     }
     aw.close();
-    map.append_binary("r", ::tree::base::serialize(::tree::base::Maybe<types::TypeBase>{ obj->return_type.get_ptr() }));
+    map.append_binary(
+        "rt", ::tree::base::serialize(::tree::base::Maybe<types::TypeBase>{ obj->return_type.get_ptr() }));
 }
 
 template <>
-function::CoreFunctionRef deserialize(const ::tree::cbor::MapReader &map) {
+function::CoreFunctionRef deserialize(const ::tree::cbor::MapReader& map) {
     if (!map.count("n")) {
         return {};
     }
-
     auto ret = tree::make<function::CoreFunction>();
     ret->name = map.at("n").as_string();
-    auto ar = map.at("t").as_array();
-    for (const auto &element : ar) {
+    auto ar = map.at("pt").as_array();
+    for (const auto& element : ar) {
         ret->param_types.add(::tree::base::deserialize<types::Node>(element.as_binary()));
     }
-    ret->return_type = ::tree::base::deserialize<types::Node>(map.at("r").as_binary());
+    ret->return_type = ::tree::base::deserialize<types::Node>(map.at("rt").as_binary());
     return ret;
 }
 
diff --git a/src/v3x/cqasm-instruction.cpp b/src/v3x/cqasm-instruction.cpp
deleted file mode 100644
index 76aa4fa50..000000000
--- a/src/v3x/cqasm-instruction.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/** \file
- * Implementation for \ref include/v3x/cqasm-instruction.hpp "v3x/cqasm-instruction.hpp".
- */
-
-#include "v3x/cqasm-instruction.hpp"
-
-#include "v3x/cqasm-semantic.hpp"
-
-namespace cqasm::v3x {
-
-namespace instruction {
-
-/**
- * Creates a new instruction.
- * param_types is a shorthand type specification string as parsed by cqasm::types::from_spec().
- * If you need more control, you can also manipulate param_types directly.
- */
-Instruction::Instruction(std::string name, const std::optional<std::string> &param_types)
-: name{ std::move(name) }
-, param_types{ types::from_spec(param_types.value_or("")) } {}
-
-/**
- * Equality operator.
- */
-bool Instruction::operator==(const Instruction &rhs) const {
-    return name == rhs.name && param_types == rhs.param_types;
-}
-
-/**
- * Stream << overload for instructions.
- */
-std::ostream &operator<<(std::ostream &os, const Instruction &instruction) {
-    return os << instruction.name << instruction.param_types;
-}
-
-/**
- * Stream << overload for instruction references.
- */
-std::ostream &operator<<(std::ostream &os, const InstructionRef &instruction) {
-    return instruction.empty() ? os << "unresolved" : os << *instruction;
-}
-
-}  // namespace instruction
-
-namespace primitives {
-
-template <>
-void serialize(const instruction::InstructionRef &obj, ::tree::cbor::MapWriter &map) {
-    if (obj.empty()) {
-        return;
-    }
-    map.append_string("n", obj->name);
-    auto aw = map.append_array("t");
-    for (const auto &t : obj->param_types) {
-        aw.append_binary(::tree::base::serialize(::tree::base::Maybe<types::TypeBase>{ t.get_ptr() }));
-    }
-    aw.close();
-}
-
-template <>
-instruction::InstructionRef deserialize(const ::tree::cbor::MapReader &map) {
-    if (!map.count("n")) {
-        return {};
-    }
-    auto instruction = tree::make<instruction::Instruction>(map.at("n").as_string(), "");
-    auto ar = map.at("t").as_array();
-    for (const auto &element : ar) {
-        instruction->param_types.add(::tree::base::deserialize<types::Node>(element.as_binary()));
-    }
-    return instruction;
-}
-
-}  // namespace primitives
-
-}  // namespace cqasm::v3x
diff --git a/src/v3x/cqasm.cpp b/src/v3x/cqasm.cpp
index 43cd98b55..1acbd5f4f 100644
--- a/src/v3x/cqasm.cpp
+++ b/src/v3x/cqasm.cpp
@@ -3,14 +3,14 @@
  * Contains the non-generated source files and private header files for libqasm.
  *
  * \file
- * Implementation for \ref include/v3x/cqasm.hpp "v3x/cqasm.hpp".
+ * Implementation for \ref include/libqasm/v3x/cqasm.hpp "libqasm/v3x/cqasm.hpp".
  */
 
-#include "v3x/cqasm.hpp"
+#include "libqasm/v3x/cqasm.hpp"
 
 #include <stdexcept>  // runtime_error
 
-#include "v3x/cqasm-parse-helper.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
 
 namespace cqasm::v3x {
 
@@ -18,9 +18,9 @@ namespace cqasm::v3x {
  * Parses and analyzes the given file path with the default analyzer,
  * dumping error messages to stderr and throwing an analyzer::AnalysisFailed on failure.
  */
-tree::One<cqasm::v3x::semantic::Program> analyze_file(const std::string &file_path, const std::string &api_version) {
-    const auto &parse_result = cqasm::v3x::parser::parse_file(file_path, std::nullopt);
-    const auto &analysis_result = cqasm::v3x::default_analyzer(api_version).analyze(parse_result);
+tree::One<cqasm::v3x::semantic::Program> analyze_file(const std::string& file_path, const std::string& api_version) {
+    const auto& parse_result = cqasm::v3x::parser::parse_file(file_path, std::nullopt);
+    const auto& analysis_result = cqasm::v3x::default_analyzer(api_version).analyze(parse_result);
     return analysis_result.unwrap();
 }
 
@@ -30,16 +30,16 @@ tree::One<cqasm::v3x::semantic::Program> analyze_file(const std::string &file_pa
  * The optional file_name is only used for error messages.
  */
 tree::One<cqasm::v3x::semantic::Program> analyze_string(
-    const std::string &data, const std::optional<std::string> &file_name, const std::string &api_version) {
-    const auto &parse_result = cqasm::v3x::parser::parse_string(data, file_name);
-    const auto &analysis_result = cqasm::v3x::default_analyzer(api_version).analyze(parse_result);
+    const std::string& data, const std::optional<std::string>& file_name, const std::string& api_version) {
+    const auto& parse_result = cqasm::v3x::parser::parse_string(data, file_name);
+    const auto& analysis_result = cqasm::v3x::default_analyzer(api_version).analyze(parse_result);
     return analysis_result.unwrap();
 }
 
 /**
  * Constructs an Analyzer object with the defaults for cQASM 3.0 already loaded into it.
  */
-analyzer::Analyzer default_analyzer(const std::string &api_version) {
+analyzer::Analyzer default_analyzer(const std::string& api_version) {
     analyzer::Analyzer analyzer{ api_version };
 
     analyzer.register_default_constants();
diff --git a/src/v3x/cqasm-py.cpp b/src/v3x/cqasm_python.cpp
similarity index 79%
rename from src/v3x/cqasm-py.cpp
rename to src/v3x/cqasm_python.cpp
index 0f2cca7bc..84591997d 100644
--- a/src/v3x/cqasm-py.cpp
+++ b/src/v3x/cqasm_python.cpp
@@ -2,15 +2,15 @@
  * Implementation for the internal Python-wrapped functions and classes.
  */
 
-#include "v3x/cqasm-py.hpp"
+#include "libqasm/v3x/cqasm_python.hpp"
 
 #include <memory>
 #include <optional>
 
-#include "cqasm-version.hpp"
-#include "v3x/cqasm-analyzer.hpp"
-#include "v3x/cqasm-parse-helper.hpp"
-#include "v3x/cqasm.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/cqasm.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
+#include "libqasm/version.hpp"
 
 namespace v3x = cqasm::v3x;
 
@@ -23,7 +23,7 @@ namespace v3x = cqasm::v3x;
  * the initial mappings and functions are not configurable at all.
  * The defaults for these are always used.
  */
-V3xAnalyzer::V3xAnalyzer(const std::string &max_version, bool without_defaults) {
+V3xAnalyzer::V3xAnalyzer(const std::string& max_version, bool without_defaults) {
     if (without_defaults) {
         analyzer = std::make_unique<v3x::analyzer::Analyzer>(max_version);
         analyzer->register_default_constants();
@@ -44,7 +44,7 @@ V3xAnalyzer::~V3xAnalyzer() = default;
  * Registers an instruction type.
  * The arguments are passed straight to instruction::Instruction's constructor.
  */
-void V3xAnalyzer::register_instruction(const std::string &name, const std::string &param_types) {
+void V3xAnalyzer::register_instruction(const std::string& name, const std::optional<std::string>& param_types) {
     analyzer->register_instruction(name, param_types);
 }
 
@@ -56,8 +56,8 @@ void V3xAnalyzer::register_instruction(const std::string &name, const std::strin
  * Any additional strings represent error messages.
  * Notice that the AST and error messages won't be available at the same time.
  */
-std::vector<std::string> V3xAnalyzer::parse_file(const std::string &file_name) {
-    const auto &parse_result = v3x::parser::parse_file(file_name, std::nullopt);
+std::vector<std::string> V3xAnalyzer::parse_file(const std::string& file_name) {
+    const auto& parse_result = v3x::parser::parse_file(file_name, std::nullopt);
     return parse_result.to_strings();
 }
 
@@ -71,8 +71,8 @@ std::vector<std::string> V3xAnalyzer::parse_file(const std::string &file_name) {
  * Every error is mapped to an LSP Diagnostic structure:
  * severity is hardcoded to 1 at the moment (value corresponding to an Error level).
  */
-std::string V3xAnalyzer::parse_file_to_json(const std::string &file_name) {
-    const auto &parse_result = v3x::parser::parse_file(file_name, std::nullopt);
+std::string V3xAnalyzer::parse_file_to_json(const std::string& file_name) {
+    const auto& parse_result = v3x::parser::parse_file(file_name, std::nullopt);
     return parse_result.to_json();
 }
 
@@ -80,9 +80,9 @@ std::string V3xAnalyzer::parse_file_to_json(const std::string &file_name) {
  * Same as parse_file(), but instead receives the file contents directly.
  * The file_name, if non-empty, is only used when reporting errors.
  */
-std::vector<std::string> V3xAnalyzer::parse_string(const std::string &data, const std::string &file_name) {
+std::vector<std::string> V3xAnalyzer::parse_string(const std::string& data, const std::string& file_name) {
     auto file_name_op = !file_name.empty() ? std::optional<std::string>{ file_name } : std::nullopt;
-    const auto &parse_result = v3x::parser::parse_string(data, file_name_op);
+    const auto& parse_result = v3x::parser::parse_string(data, file_name_op);
     return parse_result.to_strings();
 }
 
@@ -97,9 +97,9 @@ std::vector<std::string> V3xAnalyzer::parse_string(const std::string &data, cons
  * Every error is mapped to an LSP Diagnostic structure:
  * severity is hardcoded to 1 at the moment (value corresponding to an Error level).
  */
-std::string V3xAnalyzer::parse_string_to_json(const std::string &data, const std::string &file_name) {
+std::string V3xAnalyzer::parse_string_to_json(const std::string& data, const std::string& file_name) {
     auto file_name_op = !file_name.empty() ? std::optional<std::string>{ file_name } : std::nullopt;
-    const auto &parse_result = v3x::parser::parse_string(data, file_name_op);
+    const auto& parse_result = v3x::parser::parse_string(data, file_name_op);
     return parse_result.to_json();
 }
 
@@ -112,9 +112,9 @@ std::string V3xAnalyzer::parse_string_to_json(const std::string &data, const std
  * Any additional strings represent error messages.
  * Notice that the AST and error messages won't be available at the same time.
  */
-std::vector<std::string> V3xAnalyzer::analyze_file(const std::string &file_name) const {
-    const auto &parse_result = v3x::parser::parse_file(file_name, std::nullopt);
-    const auto &analysis_result = analyzer->analyze(parse_result);
+std::vector<std::string> V3xAnalyzer::analyze_file(const std::string& file_name) const {
+    const auto& parse_result = v3x::parser::parse_file(file_name, std::nullopt);
+    const auto& analysis_result = analyzer->analyze(parse_result);
     return analysis_result.to_strings();
 }
 
@@ -128,9 +128,9 @@ std::vector<std::string> V3xAnalyzer::analyze_file(const std::string &file_name)
  * Every error is mapped to an LSP Diagnostic structure:
  * severity is hardcoded to 1 at the moment (value corresponding to an Error level).
  */
-[[nodiscard]] std::string V3xAnalyzer::analyze_file_to_json(const std::string &file_name) const {
-    const auto &parse_result = v3x::parser::parse_file(file_name, std::nullopt);
-    const auto &analysis_result = analyzer->analyze(parse_result);
+[[nodiscard]] std::string V3xAnalyzer::analyze_file_to_json(const std::string& file_name) const {
+    const auto& parse_result = v3x::parser::parse_file(file_name, std::nullopt);
+    const auto& analysis_result = analyzer->analyze(parse_result);
     return analysis_result.to_json();
 }
 
@@ -138,10 +138,10 @@ std::vector<std::string> V3xAnalyzer::analyze_file(const std::string &file_name)
  * Same as analyze_file(), but instead receives the file contents directly.
  * The file_name, if specified, is only used when reporting errors.
  */
-std::vector<std::string> V3xAnalyzer::analyze_string(const std::string &data, const std::string &file_name) const {
+std::vector<std::string> V3xAnalyzer::analyze_string(const std::string& data, const std::string& file_name) const {
     auto file_name_op = !file_name.empty() ? std::optional<std::string>{ file_name } : std::nullopt;
-    const auto &parse_result = v3x::parser::parse_string(data, file_name_op);
-    const auto &analysis_result = analyzer->analyze(parse_result);
+    const auto& parse_result = v3x::parser::parse_string(data, file_name_op);
+    const auto& analysis_result = analyzer->analyze(parse_result);
     return analysis_result.to_strings();
 }
 
@@ -157,9 +157,9 @@ std::vector<std::string> V3xAnalyzer::analyze_string(const std::string &data, co
  * severity is hardcoded to 1 at the moment (value corresponding to an Error level).
  */
 [[nodiscard]] std::string V3xAnalyzer::analyze_string_to_json(
-    const std::string &data, const std::string &file_name) const {
+    const std::string& data, const std::string& file_name) const {
     auto file_name_op = !file_name.empty() ? std::optional<std::string>{ file_name } : std::nullopt;
-    const auto &parse_result = v3x::parser::parse_string(data, file_name_op);
-    const auto &analysis_result = analyzer->analyze(parse_result);
+    const auto& parse_result = v3x::parser::parse_string(data, file_name_op);
+    const auto& analysis_result = analyzer->analyze(parse_result);
     return analysis_result.to_json();
 }
diff --git a/src/v3x/instruction.cpp b/src/v3x/instruction.cpp
new file mode 100644
index 000000000..39a7c00c3
--- /dev/null
+++ b/src/v3x/instruction.cpp
@@ -0,0 +1,74 @@
+/** \file
+ * Implementation for \ref include/libqasm/v3x/instruction.hpp "libqasm/v3x/instruction.hpp".
+ */
+
+#include "libqasm/v3x/instruction.hpp"
+
+#include <fmt/format.h>
+
+#include "libqasm/v3x/semantic.hpp"
+
+namespace cqasm::v3x::instruction {
+
+/**
+ * Creates a new instruction.
+ * operand_types is a shorthand type specification string as parsed by cqasm::types::from_spec().
+ * If you need more control, you can also manipulate param_types directly.
+ */
+Instruction::Instruction(std::string name, const std::optional<std::string>& operand_types)
+: name{ std::move(name) }
+, operand_types{ types::from_spec(operand_types.value_or("")) } {}
+
+/**
+ * Equality operator.
+ */
+bool Instruction::operator==(const Instruction& rhs) const {
+    return name == rhs.name && operand_types == rhs.operand_types;
+}
+
+/**
+ * Stream << overload for instructions.
+ */
+std::ostream& operator<<(std::ostream& os, const Instruction& instruction) {
+    return os << fmt::format("{}({})", instruction.name, instruction.operand_types);
+}
+
+/**
+ * Stream << overload for instruction references.
+ */
+std::ostream& operator<<(std::ostream& os, const InstructionRef& instruction) {
+    return instruction.empty() ? os << "unresolved" : os << *instruction;
+}
+
+}  // namespace cqasm::v3x::instruction
+
+namespace cqasm::v3x::primitives {
+
+template <>
+void serialize(const instruction::InstructionRef& obj, ::tree::cbor::MapWriter& map) {
+    if (obj.empty()) {
+        return;
+    }
+    map.append_string("name", obj->name);
+    auto operand_types_array = map.append_array("operand_types");
+    for (const auto& t : obj->operand_types) {
+        operand_types_array.append_binary(::tree::base::serialize(::tree::base::Maybe<types::TypeBase>{ t.get_ptr() }));
+    }
+    operand_types_array.close();
+}
+
+template <>
+instruction::InstructionRef deserialize(const ::tree::cbor::MapReader& map) {
+    if (!map.count("name")) {
+        return {};
+    }
+    auto ret = tree::make<instruction::Instruction>();
+    ret->name = map.at("name").as_string();
+    auto operand_types_array = map.at("operand_types").as_array();
+    for (const auto& element : operand_types_array) {
+        ret->operand_types.add(::tree::base::deserialize<types::Node>(element.as_binary()));
+    }
+    return ret;
+}
+
+}  // namespace cqasm::v3x::primitives
diff --git a/src/v3x/instruction_set.cpp b/src/v3x/instruction_set.cpp
new file mode 100644
index 000000000..20d20df1c
--- /dev/null
+++ b/src/v3x/instruction_set.cpp
@@ -0,0 +1,204 @@
+#include "libqasm/v3x/instruction_set.hpp"
+
+#include <fmt/format.h>
+
+#include "libqasm/error.hpp"
+
+namespace cqasm::v3x::instruction {
+
+InstructionSet::InstructionSet()
+: named_gate_map{
+    { "CNOT", { std::nullopt, "QQ" } },
+    { "CNOT", { std::nullopt, "QV" } },
+    { "CNOT", { std::nullopt, "VQ" } },
+    { "CNOT", { std::nullopt, "VV" } },
+    { "CR", { 'f', "QQ" } },
+    { "CR", { 'f', "QV" } },
+    { "CR", { 'f', "VQ" } },
+    { "CR", { 'f', "VV" } },
+    { "CRk", { 'i', "QQ" } },
+    { "CRk", { 'i', "QV" } },
+    { "CRk", { 'i', "VQ" } },
+    { "CRk", { 'i', "VV" } },
+    { "CZ", { std::nullopt, "QQ" } },
+    { "CZ", { std::nullopt, "QV" } },
+    { "CZ", { std::nullopt, "VQ" } },
+    { "CZ", { std::nullopt, "VV" } },
+    { "H", { std::nullopt, "Q" } },
+    { "H", { std::nullopt, "V" } },
+    { "I", { std::nullopt, "Q" } },
+    { "I", { std::nullopt, "V" } },
+    { "mX90", { std::nullopt, "Q" } },
+    { "mX90", { std::nullopt, "V" } },
+    { "mY90", { std::nullopt, "Q" } },
+    { "mY90", { std::nullopt, "V" } },
+    { "Rx", { 'f', "Q" } },
+    { "Rx", { 'f', "V" } },
+    { "Ry", { 'f', "Q" } },
+    { "Ry", { 'f', "V" } },
+    { "Rz", { 'f', "Q" } },
+    { "Rz", { 'f', "V" } },
+    { "S", { std::nullopt, "Q" } },
+    { "S", { std::nullopt, "V" } },
+    { "Sdag", { std::nullopt, "Q" } },
+    { "Sdag", { std::nullopt, "V" } },
+    { "T", { std::nullopt, "Q" } },
+    { "T", { std::nullopt, "V" } },
+    { "Tdag", { std::nullopt, "Q" } },
+    { "Tdag", { std::nullopt, "V" } },
+    { "X", { std::nullopt, "Q" } },
+    { "X", { std::nullopt, "V" } },
+    { "X90", { std::nullopt, "Q" } },
+    { "X90", { std::nullopt, "V" } },
+    { "Y", { std::nullopt, "Q" } },
+    { "Y", { std::nullopt, "V" } },
+    { "Y90", { std::nullopt, "Q" } },
+    { "Y90", { std::nullopt, "V" } },
+    { "Z", { std::nullopt, "Q" } },
+    { "Z", { std::nullopt, "V" } }
+}
+, non_gate_map{
+    { "measure", { std::nullopt, "BQ" } },
+    { "measure", { std::nullopt, "WV" } },
+    { "measure", { std::nullopt, "BV" } },
+    { "measure", { std::nullopt, "WQ" } },
+    { "reset", { std::nullopt, std::nullopt } },
+    { "reset", { std::nullopt, "Q" } },
+    { "reset", { std::nullopt, "V" } } }
+, gate_modifier_map{
+    { "inv", std::nullopt },
+    { "pow", 'f' },
+    { "ctrl", std::nullopt },
+}
+, single_qubit_named_gate_list{
+    "H", "I", "mX90", "mY90", "Rx", "Ry", "Rz", "S", "Sdag", "T", "Tdag", "X", "X90", "Y", "Y90", "Z"
+}
+, two_qubit_named_gate_list{
+    "CNOT", "CR", "CRk", "CZ"
+}
+{}
+
+[[nodiscard]] /* static */ InstructionSet& InstructionSet::get_instance() {
+    static InstructionSet instance;
+    return instance;
+}
+
+[[nodiscard]] const InstructionMapT& InstructionSet::get_named_gate_map() const {
+    return named_gate_map;
+}
+
+[[nodiscard]] const InstructionMapT& InstructionSet::get_non_gate_map() const {
+    return non_gate_map;
+}
+
+[[nodiscard]] const GateModifierMapT& InstructionSet::get_gate_modifier_map() const {
+    return gate_modifier_map;
+}
+
+[[nodiscard]] const InstructionListT& InstructionSet::get_single_qubit_named_gate_list() const {
+    return single_qubit_named_gate_list;
+}
+
+[[nodiscard]] const InstructionListT& InstructionSet::get_two_qubit_named_gate_list() const {
+    return two_qubit_named_gate_list;
+}
+
+[[nodiscard]] bool InstructionSet::is_single_qubit_named_gate(const std::string& name) const {
+    return single_qubit_named_gate_list.contains(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_two_qubit_named_gate(const std::string& name) const {
+    return two_qubit_named_gate_list.contains(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_named_gate(const std::string& name) const {
+    return is_single_qubit_named_gate(name) || is_two_qubit_named_gate(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_single_qubit_gate_composition(const std::string& name) const {
+    return name.starts_with(single_qubit_gate_composition_prefix);
+}
+
+[[nodiscard]] bool InstructionSet::is_two_qubit_gate_composition(const std::string& name) const {
+    return name.starts_with(two_qubit_gate_composition_prefix);
+}
+
+[[nodiscard]] bool InstructionSet::is_gate_composition(const std::string& name) const {
+    return is_single_qubit_gate_composition(name) || is_two_qubit_gate_composition(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_single_qubit_gate(const std::string& name) const {
+    return is_single_qubit_named_gate(name) || is_single_qubit_gate_composition(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_two_qubit_gate(const std::string& name) const {
+    return is_two_qubit_named_gate(name) || is_two_qubit_gate_composition(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_gate(const std::string& name) const {
+    return is_named_gate(name) || is_gate_composition(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_measure(const std::string& name) const {
+    return name == measure_name;
+}
+
+[[nodiscard]] bool InstructionSet::is_reset(const std::string& name) const {
+    return name == reset_name;
+}
+
+[[nodiscard]] bool InstructionSet::is_non_gate(const std::string& name) const {
+    return is_measure(name) || is_reset(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_inv_gate_modifier(const std::string& name) const {
+    return name == inv_gate_modifier_name;
+}
+
+[[nodiscard]] bool InstructionSet::is_pow_gate_modifier(const std::string& name) const {
+    return name == pow_gate_modifier_name;
+}
+
+[[nodiscard]] bool InstructionSet::is_ctrl_gate_modifier(const std::string& name) const {
+    return name == ctrl_gate_modifier_name;
+}
+
+[[nodiscard]] bool InstructionSet::is_single_qubit_gate_modifier(const std::string& name) const {
+    return is_inv_gate_modifier(name) || is_pow_gate_modifier(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_two_qubit_gate_modifier(const std::string& name) const {
+    return is_ctrl_gate_modifier(name);
+}
+
+[[nodiscard]] bool InstructionSet::is_gate_modifier(const std::string& name) const {
+    return is_inv_gate_modifier(name) || is_pow_gate_modifier(name) || is_ctrl_gate_modifier(name);
+}
+
+[[nodiscard]] std::optional<char> InstructionSet::get_named_gate_param_type(const std::string& name) const {
+    if (const auto& it = named_gate_map.find(name); it != named_gate_map.end()) {
+        const auto& pair_param_types_operand_types = it->second;
+        return pair_param_types_operand_types.first;
+    }
+    throw error::AnalysisError{ fmt::format("couldn't find gate '{}'", name) };
+}
+
+[[nodiscard]] std::optional<char> InstructionSet::get_gate_modifier_param_type(const std::string& name) const {
+    if (const auto& it = gate_modifier_map.find(name); it != gate_modifier_map.end()) {
+        return it->second;
+    }
+    throw error::AnalysisError{ fmt::format("couldn't find gate modifier '{}'", name) };
+}
+
+[[nodiscard]] std::optional<char> InstructionSet::get_instruction_param_type(const std::string& name) const {
+    if (is_named_gate(name)) {
+        return get_named_gate_param_type(name);
+    } else if (is_non_gate(name)) {
+        return std::nullopt;
+    } else if (is_gate_modifier(name)) {
+        return get_gate_modifier_param_type(name);
+    }
+    throw error::AnalysisError{ fmt::format("couldn't find instruction '{}'", name) };
+}
+
+}  // namespace cqasm::v3x::instruction
diff --git a/src/v3x/cqasm-parse-helper.cpp b/src/v3x/parse_helper.cpp
similarity index 54%
rename from src/v3x/cqasm-parse-helper.cpp
rename to src/v3x/parse_helper.cpp
index 753c52998..016d6c35c 100644
--- a/src/v3x/cqasm-parse-helper.cpp
+++ b/src/v3x/parse_helper.cpp
@@ -1,14 +1,14 @@
 /** \file
- * Implementation for \ref include/v3x/cqasm-parse-helper.hpp "v3x/cqasm-parse-helper.hpp".
+ * Implementation for \ref include/libqasm/v3x/parse_helper.hpp "libqasm/v3x/parse_helper.hpp".
  */
 
-#include "v3x/cqasm-parse-helper.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
 
-#include "cqasm-annotations-constants.hpp"
-#include "v3x/BuildTreeGenAstVisitor.hpp"
-#include "v3x/CustomErrorListener.hpp"
-#include "v3x/ScannerAntlr.hpp"
-#include "v3x/cqasm-parse-result.hpp"
+#include "libqasm/annotations_constants.hpp"
+#include "libqasm/v3x/antlr_custom_error_listener.hpp"
+#include "libqasm/v3x/antlr_scanner.hpp"
+#include "libqasm/v3x/parse_result.hpp"
+#include "libqasm/v3x/syntactic_analyzer.hpp"
 
 namespace cqasm::v3x::parser {
 
@@ -17,11 +17,11 @@ namespace cqasm::v3x::parser {
  * Throws a ParseError if the file does not exist.
  * A file_name may be given in addition for use within error messages.
  */
-ParseResult parse_file(const std::string &file_path, const std::optional<std::string> &file_name) {
-    auto builder_visitor_up = std::make_unique<BuildTreeGenAstVisitor>(file_name);
-    auto error_listener_up = std::make_unique<CustomErrorListener>(file_name);
+ParseResult parse_file(const std::string& file_path, const std::optional<std::string>& file_name) {
+    auto builder_visitor_up = std::make_unique<SyntacticAnalyzer>(file_name);
+    auto error_listener_up = std::make_unique<AntlrCustomErrorListener>(file_name);
     auto scanner_up =
-        std::make_unique<ScannerAntlrFile>(std::move(builder_visitor_up), std::move(error_listener_up), file_path);
+        std::make_unique<FileAntlrScanner>(std::move(builder_visitor_up), std::move(error_listener_up), file_path);
     return ParseHelper(std::move(scanner_up), file_name).parse();
 }
 
@@ -29,15 +29,15 @@ ParseResult parse_file(const std::string &file_path, const std::optional<std::st
  * Parse the given string.
  * A file_name may be given in addition for use within error messages.
  */
-ParseResult parse_string(const std::string &data, const std::optional<std::string> &file_name) {
-    auto builder_visitor_up = std::make_unique<BuildTreeGenAstVisitor>(file_name);
-    auto error_listener_up = std::make_unique<CustomErrorListener>(file_name);
+ParseResult parse_string(const std::string& data, const std::optional<std::string>& file_name) {
+    auto builder_visitor_up = std::make_unique<SyntacticAnalyzer>(file_name);
+    auto error_listener_up = std::make_unique<AntlrCustomErrorListener>(file_name);
     auto scanner_up =
-        std::make_unique<ScannerAntlrString>(std::move(builder_visitor_up), std::move(error_listener_up), data);
+        std::make_unique<StringAntlrScanner>(std::move(builder_visitor_up), std::move(error_listener_up), data);
     return ParseHelper(std::move(scanner_up), file_name).parse();
 }
 
-ParseHelper::ParseHelper(std::unique_ptr<ScannerAdaptor> scanner_up, const std::optional<std::string> &file_name)
+ParseHelper::ParseHelper(std::unique_ptr<ScannerAdaptor> scanner_up, const std::optional<std::string>& file_name)
 : scanner_up_{ std::move(scanner_up) }
 , file_name_{ file_name.value_or(annotations::unknown_file_name) } {
     if (file_name_.empty()) {
@@ -52,9 +52,9 @@ ParseResult ParseHelper::parse() {
     ParseResult result;
     try {
         result = scanner_up_->parse();
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         result.errors.push_back(std::move(err));
-    } catch (const std::runtime_error &err) {
+    } catch (const std::runtime_error& err) {
         result.errors.emplace_back(err.what());
     }
 
diff --git a/src/v3x/cqasm-parse-result.cpp b/src/v3x/parse_result.cpp
similarity index 89%
rename from src/v3x/cqasm-parse-result.cpp
rename to src/v3x/parse_result.cpp
index aa6994b6e..5725612bc 100644
--- a/src/v3x/cqasm-parse-result.cpp
+++ b/src/v3x/parse_result.cpp
@@ -1,6 +1,6 @@
-#include "v3x/cqasm-parse-result.hpp"
+#include "libqasm/v3x/parse_result.hpp"
 
-#include "cqasm-result.hpp"
+#include "libqasm/result.hpp"
 
 namespace cqasm::v3x::parser {
 
diff --git a/src/v3x/cqasm-primitives.cpp b/src/v3x/primitives.cpp
similarity index 62%
rename from src/v3x/cqasm-primitives.cpp
rename to src/v3x/primitives.cpp
index 2580f025a..fb3712a89 100644
--- a/src/v3x/cqasm-primitives.cpp
+++ b/src/v3x/primitives.cpp
@@ -1,8 +1,8 @@
 /** \file
- * Implementation for \ref include/v3x/cqasm-primitives.hpp "v3x/cqasm-primitives.hpp".
+ * Implementation for \ref include/libqasm/v3x/primitives.hpp "libqasm/v3x/primitives.hpp".
  */
 
-#include "v3x/cqasm-primitives.hpp"
+#include "libqasm/v3x/primitives.hpp"
 
 #include <fmt/format.h>
 
@@ -19,12 +19,12 @@ Str initialize<Str>() {
 }
 
 template <>
-void serialize(const Str &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const Str& obj, ::tree::cbor::MapWriter& map) {
     map.append_binary("x", obj);
 }
 
 template <>
-Str deserialize(const ::tree::cbor::MapReader &map) {
+Str deserialize(const ::tree::cbor::MapReader& map) {
     return map.at("x").as_binary();
 }
 
@@ -37,14 +37,14 @@ Axis initialize<Axis>() {
 }
 
 template <>
-void serialize(const Axis &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const Axis& obj, ::tree::cbor::MapWriter& map) {
     map.append_float("x", obj.x);
     map.append_float("y", obj.y);
     map.append_float("z", obj.z);
 }
 
 template <>
-Axis deserialize(const ::tree::cbor::MapReader &map) {
+Axis deserialize(const ::tree::cbor::MapReader& map) {
     return { map.at("x").as_float(), map.at("y").as_float(), map.at("z").as_float() };
 }
 
@@ -57,12 +57,12 @@ Bool initialize<Bool>() {
 }
 
 template <>
-void serialize(const Bool &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const Bool& obj, ::tree::cbor::MapWriter& map) {
     map.append_bool("x", obj);
 }
 
 template <>
-Bool deserialize(const ::tree::cbor::MapReader &map) {
+Bool deserialize(const ::tree::cbor::MapReader& map) {
     return map.at("x").as_bool();
 }
 
@@ -75,12 +75,12 @@ Int initialize<Int>() {
 }
 
 template <>
-void serialize(const Int &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const Int& obj, ::tree::cbor::MapWriter& map) {
     map.append_int("x", obj);
 }
 
 template <>
-Int deserialize(const ::tree::cbor::MapReader &map) {
+Int deserialize(const ::tree::cbor::MapReader& map) {
     return map.at("x").as_int();
 }
 
@@ -93,12 +93,12 @@ Float initialize<Float>() {
 }
 
 template <>
-void serialize(const Float &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const Float& obj, ::tree::cbor::MapWriter& map) {
     map.append_float("x", obj);
 }
 
 template <>
-Float deserialize(const ::tree::cbor::MapReader &map) {
+Float deserialize(const ::tree::cbor::MapReader& map) {
     return map.at("x").as_float();
 }
 
@@ -106,13 +106,13 @@ Float deserialize(const ::tree::cbor::MapReader &map) {
  * Complex
  */
 template <>
-void serialize(const Complex &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const Complex& obj, ::tree::cbor::MapWriter& map) {
     map.append_float("r", obj.real());
     map.append_float("i", obj.imag());
 }
 
 template <>
-Complex deserialize(const ::tree::cbor::MapReader &map) {
+Complex deserialize(const ::tree::cbor::MapReader& map) {
     return { map.at("r").as_float(), map.at("i").as_float() };
 }
 
@@ -120,7 +120,7 @@ Complex deserialize(const ::tree::cbor::MapReader &map) {
  * Version
  */
 template <>
-void serialize(const Version &obj, ::tree::cbor::MapWriter &map) {
+void serialize(const Version& obj, ::tree::cbor::MapWriter& map) {
     auto aw = map.append_array("x");
     for (auto x : obj) {
         aw.append_int(x);
@@ -129,18 +129,18 @@ void serialize(const Version &obj, ::tree::cbor::MapWriter &map) {
 }
 
 template <>
-Version deserialize(const ::tree::cbor::MapReader &map) {
+Version deserialize(const ::tree::cbor::MapReader& map) {
     auto ar = map.at("x").as_array();
     auto v = Version("");
     v.resize(ar.size());
-    std::transform(ar.begin(), ar.end(), v.begin(), [](const auto &e) { return e.as_int(); });
+    std::transform(ar.begin(), ar.end(), v.begin(), [](const auto& e) { return e.as_int(); });
     return v;
 }
 
 /**
  * Stream << overload for axis nodes.
  */
-std::ostream &operator<<(std::ostream &os, const Axis &axis) {
+std::ostream& operator<<(std::ostream& os, const Axis& axis) {
     return os << fmt::format("[{}, {}, {}]", axis.x, axis.y, axis.z);
 }
 
diff --git a/src/v3x/register-instructions.cpp b/src/v3x/register-instructions.cpp
deleted file mode 100644
index df57628ca..000000000
--- a/src/v3x/register-instructions.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "v3x/register-instructions.hpp"
-
-#include <cassert>
-
-namespace cqasm::v3x::instruction {
-
-/**
- * Registers the cQASM 3.0 instruction set.
- */
-void register_instructions(analyzer::Analyzer *analyzer) {
-    assert(analyzer);
-
-    analyzer->register_instruction("CNOT", "QQ");
-    analyzer->register_instruction("CNOT", "QV");
-    analyzer->register_instruction("CNOT", "VQ");
-    analyzer->register_instruction("CNOT", "VV");
-    analyzer->register_instruction("CR", "QQf");
-    analyzer->register_instruction("CR", "QVf");
-    analyzer->register_instruction("CR", "VQf");
-    analyzer->register_instruction("CR", "VVf");
-    analyzer->register_instruction("CRk", "QQi");
-    analyzer->register_instruction("CRk", "QVi");
-    analyzer->register_instruction("CRk", "VQi");
-    analyzer->register_instruction("CRk", "VVi");
-    analyzer->register_instruction("CZ", "QQ");
-    analyzer->register_instruction("CZ", "QV");
-    analyzer->register_instruction("CZ", "VQ");
-    analyzer->register_instruction("CZ", "VV");
-    analyzer->register_instruction("H", "Q");
-    analyzer->register_instruction("H", "V");
-    analyzer->register_instruction("I", "Q");
-    analyzer->register_instruction("I", "V");
-    analyzer->register_instruction("measure", "BQ");
-    analyzer->register_instruction("measure", "WV");
-    analyzer->register_instruction("measure", "BV");
-    analyzer->register_instruction("measure", "WQ");
-    analyzer->register_instruction("mX90", "Q");
-    analyzer->register_instruction("mX90", "V");
-    analyzer->register_instruction("mY90", "Q");
-    analyzer->register_instruction("mY90", "V");
-    analyzer->register_instruction("reset", std::nullopt);
-    analyzer->register_instruction("reset", "Q");
-    analyzer->register_instruction("reset", "V");
-    analyzer->register_instruction("Rx", "Qf");
-    analyzer->register_instruction("Rx", "Vf");
-    analyzer->register_instruction("Ry", "Qf");
-    analyzer->register_instruction("Ry", "Vf");
-    analyzer->register_instruction("Rz", "Qf");
-    analyzer->register_instruction("Rz", "Vf");
-    analyzer->register_instruction("S", "Q");
-    analyzer->register_instruction("S", "V");
-    analyzer->register_instruction("Sdag", "Q");
-    analyzer->register_instruction("Sdag", "V");
-    analyzer->register_instruction("T", "Q");
-    analyzer->register_instruction("T", "V");
-    analyzer->register_instruction("Tdag", "Q");
-    analyzer->register_instruction("Tdag", "V");
-    analyzer->register_instruction("X", "Q");
-    analyzer->register_instruction("X", "V");
-    analyzer->register_instruction("X90", "Q");
-    analyzer->register_instruction("X90", "V");
-    analyzer->register_instruction("Y", "Q");
-    analyzer->register_instruction("Y", "V");
-    analyzer->register_instruction("Y90", "Q");
-    analyzer->register_instruction("Y90", "V");
-    analyzer->register_instruction("Z", "Q");
-    analyzer->register_instruction("Z", "V");
-}
-
-}  // namespace cqasm::v3x::instruction
diff --git a/src/v3x/register-consteval-core-functions.cpp b/src/v3x/register_consteval_core_functions.cpp
similarity index 97%
rename from src/v3x/register-consteval-core-functions.cpp
rename to src/v3x/register_consteval_core_functions.cpp
index 1d23bfd11..cd034bb2e 100644
--- a/src/v3x/register-consteval-core-functions.cpp
+++ b/src/v3x/register_consteval_core_functions.cpp
@@ -1,4 +1,4 @@
-#include "v3x/register-consteval-core-functions.hpp"
+#include "libqasm/v3x/register_consteval_core_functions.hpp"
 
 #include <cassert>
 
@@ -9,7 +9,7 @@ namespace cqasm::v3x::function {
  * That is, functions supported by the language, and that can be evaluated at compile time.
  * For functions supported by the language that cannot be evaluated at compile time, see CoreFunction.
  */
-void register_consteval_core_functions(analyzer::Analyzer *analyzer) {
+void register_consteval_core_functions(analyzer::Analyzer* analyzer) {
     assert(analyzer);
 
     analyzer->register_consteval_core_function("operator-", "f", op_neg_f);
diff --git a/src/v3x/register_instructions.cpp b/src/v3x/register_instructions.cpp
new file mode 100644
index 000000000..e0ca200c6
--- /dev/null
+++ b/src/v3x/register_instructions.cpp
@@ -0,0 +1,62 @@
+#include "libqasm/v3x/register_instructions.hpp"
+
+#include <fmt/format.h>
+
+#include <cassert>
+
+#include "libqasm/v3x/instruction_set.hpp"
+
+namespace cqasm::v3x::instruction {
+
+/**
+ * Registers the cQASM 3.0 instruction set.
+ */
+void register_instructions(analyzer::Analyzer* analyzer) {
+    assert(analyzer);
+
+    const auto& instruction_set = InstructionSet::get_instance();
+    const auto& named_gate_map = instruction_set.get_named_gate_map();
+    const auto& non_gate_map = instruction_set.get_non_gate_map();
+
+    // Named gates
+    for (const auto& [gate_name, pair_params_types_operands_types] : named_gate_map) {
+        const auto& operand_types = pair_params_types_operands_types.second;
+        analyzer->register_instruction(gate_name, operand_types);
+    };
+
+    // Single-qubit gates, e.g., inv.X, pow(2).X, or pow(2).inv.X
+    for (const auto& [named_gate_name, pair_params_types_operands_types] : named_gate_map) {
+        if (instruction_set.is_single_qubit_named_gate(named_gate_name)) {
+            const auto& operand_types = pair_params_types_operands_types.second;
+            const auto& single_qubit_gate_name =
+                fmt::format("{}_{}", instruction_set.single_qubit_gate_composition_prefix, named_gate_name);
+            analyzer->register_instruction(single_qubit_gate_name, operand_types);
+        }
+    };
+
+    // Two-qubit gates, e.g., ctrl.X, ctrl.inv.X, ctrl.pow(2).X, or ctrl.pow(2).inv.X
+    for (const auto& [named_gate_name, pair_params_types_operands_types] : named_gate_map) {
+        if (instruction_set.is_single_qubit_named_gate(named_gate_name)) {
+            // For a single qubit named gate such as Ry, the named gate map will hold two entries,
+            // namely "Ry(f) Q" and "Ry(f) V"
+            // For each of those entries, e.g., "Ry(f) Q", the code below registers 2 two-qubit gates "2q_Ry",
+            // each acting on a pair of qubit operands, one on "QQ" and the other on "VQ"
+            const auto& operand_types = pair_params_types_operands_types.second;
+            assert(operand_types.has_value());
+            const auto& operand_types_1 = fmt::format("{}{}", "Q", operand_types.value());
+            const auto& operand_types_2 = fmt::format("{}{}", "V", operand_types.value());
+            const auto& two_qubit_gate_name =
+                fmt::format("{}_{}", instruction_set.two_qubit_gate_composition_prefix, named_gate_name);
+            analyzer->register_instruction(two_qubit_gate_name, operand_types_1);
+            analyzer->register_instruction(two_qubit_gate_name, operand_types_2);
+        }
+    };
+
+    // Non-gate instructions
+    for (const auto& [non_gate_name, pair_params_types_operands_types] : non_gate_map) {
+        const auto& operand_types = pair_params_types_operands_types.second;
+        analyzer->register_instruction(non_gate_name, operand_types);
+    };
+}
+
+}  // namespace cqasm::v3x::instruction
diff --git a/src/v3x/cqasm-resolver.cpp b/src/v3x/resolver.cpp
similarity index 62%
rename from src/v3x/cqasm-resolver.cpp
rename to src/v3x/resolver.cpp
index 388943d8e..46da405ec 100644
--- a/src/v3x/cqasm-resolver.cpp
+++ b/src/v3x/resolver.cpp
@@ -1,24 +1,19 @@
 /** \file
- * Implementation for \ref include/v3x/cqasm-resolver.hpp "v3x/cqasm-resolver.hpp".
+ * Implementation for \ref include/libqasm/v3x/resolver.hpp "libqasm/v3x/resolver.hpp".
  */
 
-#include "v3x/cqasm-resolver.hpp"
+#include "libqasm/v3x/resolver.hpp"
 
 #include <fmt/format.h>
 
 #include <memory>
 #include <unordered_map>
 
-#include "cqasm-error.hpp"
-#include "cqasm-utils.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/utils.hpp"
 
 namespace cqasm::v3x::resolver {
 
-using Type = types::Type;
-using Types = types::Types;
-using Value = values::Value;
-using Values = values::Values;
-
 //---------------//
 // VariableTable //
 //---------------//
@@ -26,7 +21,7 @@ using Values = values::Values;
 /**
  * Adds a variable.
  */
-void VariableTable::add(const std::string &name, const Value &value) {
+void VariableTable::add(const std::string& name, const Value& value) {
     table.erase(name);
     table.insert(std::make_pair(name, value));
 }
@@ -35,7 +30,7 @@ void VariableTable::add(const std::string &name, const Value &value) {
  * Resolves a variable.
  * Throws NameResolutionFailure if no variable by the given name exists.
  */
-Value VariableTable::resolve(const std::string &name) const {
+[[nodiscard]] Value VariableTable::resolve(const std::string& name) const {
     if (auto entry = table.find(name); entry != table.end()) {
         return entry->second->clone();
     }
@@ -49,15 +44,15 @@ Value VariableTable::resolve(const std::string &name) const {
 ConstEvalCoreFunctionTable::ConstEvalCoreFunctionTable()
 : resolver{ std::make_unique<resolver_t>() } {}
 ConstEvalCoreFunctionTable::~ConstEvalCoreFunctionTable() = default;
-ConstEvalCoreFunctionTable::ConstEvalCoreFunctionTable(const ConstEvalCoreFunctionTable &t)
+ConstEvalCoreFunctionTable::ConstEvalCoreFunctionTable(const ConstEvalCoreFunctionTable& t)
 : resolver{ std::make_unique<resolver_t>(*t.resolver) } {}
-ConstEvalCoreFunctionTable::ConstEvalCoreFunctionTable(ConstEvalCoreFunctionTable &&t) noexcept
+ConstEvalCoreFunctionTable::ConstEvalCoreFunctionTable(ConstEvalCoreFunctionTable&& t) noexcept
 : resolver{ std::move(t.resolver) } {}
-ConstEvalCoreFunctionTable &ConstEvalCoreFunctionTable::operator=(const ConstEvalCoreFunctionTable &t) {
+ConstEvalCoreFunctionTable& ConstEvalCoreFunctionTable::operator=(const ConstEvalCoreFunctionTable& t) {
     resolver = std::make_unique<resolver_t>(resolver_t{ *t.resolver });
     return *this;
 }
-ConstEvalCoreFunctionTable &ConstEvalCoreFunctionTable::operator=(ConstEvalCoreFunctionTable &&t) noexcept {
+ConstEvalCoreFunctionTable& ConstEvalCoreFunctionTable::operator=(ConstEvalCoreFunctionTable&& t) noexcept {
     resolver = std::move(t.resolver);
     return *this;
 }
@@ -76,7 +71,7 @@ ConstEvalCoreFunctionTable &ConstEvalCoreFunctionTable::operator=(ConstEvalCoreF
  * so adding does have the effect of overriding.
  */
 void ConstEvalCoreFunctionTable::add(
-    const std::string &name, const Types &param_types, const ConstEvalCoreFunction &impl) {
+    const std::string& name, const Types& param_types, const ConstEvalCoreFunction& impl) {
     resolver->add_overload(name, impl, param_types);
 }
 
@@ -86,7 +81,7 @@ void ConstEvalCoreFunctionTable::add(
  * OverloadResolutionFailure if no overload of the function exists for the given arguments, or otherwise
  * returns the value returned by the function.
  */
-Value ConstEvalCoreFunctionTable::resolve(const std::string &name, const Values &args) const {
+Value ConstEvalCoreFunctionTable::resolve(const std::string& name, const Values& args) const {
     // Resolve the function and type-check/promote the argument list.
     auto resolution = resolver->resolve(name, args);
 
@@ -101,15 +96,15 @@ Value ConstEvalCoreFunctionTable::resolve(const std::string &name, const Values
 InstructionTable::InstructionTable()
 : resolver{ std::make_unique<resolver_t>() } {}
 InstructionTable::~InstructionTable() = default;
-InstructionTable::InstructionTable(const InstructionTable &t)
+InstructionTable::InstructionTable(const InstructionTable& t)
 : resolver{ std::make_unique<resolver_t>(*t.resolver) } {}
-InstructionTable::InstructionTable(InstructionTable &&t) noexcept
+InstructionTable::InstructionTable(InstructionTable&& t) noexcept
 : resolver{ std::move(t.resolver) } {}
-InstructionTable &InstructionTable::operator=(const InstructionTable &t) {
+InstructionTable& InstructionTable::operator=(const InstructionTable& t) {
     resolver = std::make_unique<resolver_t>(resolver_t{ *t.resolver });
     return *this;
 }
-InstructionTable &InstructionTable::operator=(InstructionTable &&t) noexcept {
+InstructionTable& InstructionTable::operator=(InstructionTable&& t) noexcept {
     resolver = std::move(t.resolver);
     return *this;
 }
@@ -117,20 +112,34 @@ InstructionTable &InstructionTable::operator=(InstructionTable &&t) noexcept {
 /**
  * Registers an instruction type.
  */
-void InstructionTable::add(const instruction::Instruction &type) {
-    resolver->add_overload(type.name, tree::make<instruction::Instruction>(type), type.param_types);
+void InstructionTable::add(const instruction::Instruction& type) {
+    resolver->add_overload(type.name, tree::make<instruction::Instruction>(type), type.operand_types);
+}
+
+/**
+ * Resolves an GateInstruction type.
+ * Throws NameResolutionFailure if no instruction by the given name exists,
+ * OverloadResolutionFailure if no overload exists for the given arguments, or otherwise
+ * returns the resolved instruction node.
+ * Annotation data, line number information, and the condition still need to be set by the caller.
+ */
+[[nodiscard]] tree::One<semantic::Instruction> InstructionTable::resolve(
+    const std::string& name, const tree::One<semantic::Gate>& gate, const Values& args) const {
+    auto [instruction_ref, promoted_args] = resolver->resolve(name, args);
+    return tree::make<semantic::GateInstruction>(instruction_ref, gate, promoted_args);
 }
 
 /**
- * Resolves an instruction.
+ * Resolves an NonGateInstruction type.
  * Throws NameResolutionFailure if no instruction by the given name exists,
  * OverloadResolutionFailure if no overload exists for the given arguments, or otherwise
  * returns the resolved instruction node.
  * Annotation data, line number information, and the condition still need to be set by the caller.
  */
-tree::One<semantic::Instruction> InstructionTable::resolve(const std::string &name, const Values &args) const {
+[[nodiscard]] tree::One<semantic::Instruction> InstructionTable::resolve(
+    const std::string& name, const Values& args) const {
     auto [instruction_ref, promoted_args] = resolver->resolve(name, args);
-    return tree::make<semantic::Instruction>(instruction_ref, name, promoted_args);
+    return tree::make<semantic::NonGateInstruction>(instruction_ref, name, promoted_args);
 }
 
 }  // namespace cqasm::v3x::resolver
diff --git a/src/v3x/cqasm-semantic.tree b/src/v3x/semantic.tree
similarity index 63%
rename from src/v3x/cqasm-semantic.tree
rename to src/v3x/semantic.tree
index f4b88aaf0..5e2b856ca 100644
--- a/src/v3x/cqasm-semantic.tree
+++ b/src/v3x/semantic.tree
@@ -2,15 +2,15 @@
 source
 
 # Header file for the semantic tree node classes.
-header "v3x/cqasm-semantic-gen.hpp"
+header "libqasm/v3x/semantic_generated.hpp"
 
-include "cqasm-tree.hpp"
+include "libqasm/tree.hpp"
 tree_namespace cqasm::tree
 
-include "v3x/cqasm-instruction.hpp"
-include "v3x/cqasm-primitives.hpp"
-include "v3x/cqasm-types.hpp"
-include "v3x/cqasm-values.hpp"
+include "libqasm/v3x/instruction.hpp"
+include "libqasm/v3x/primitives.hpp"
+include "libqasm/v3x/types.hpp"
+include "libqasm/v3x/values.hpp"
 import cqasm.v3x.instruction
 import cqasm.v3x.primitives
 import cqasm.v3x.types
@@ -44,17 +44,34 @@ annotation_data {
 annotated {
     annotations: Any<annotation_data>;
 
+    # A gate can be a named gate or a composition of gate modifiers acting on a gate.
+    # pow is the only gate modifier that has an operand.
+    gate {
+        name: cqasm::v3x::primitives::Str;
+        gate: Maybe<gate>;
+        parameter: external Maybe<cqasm::v3x::values::ValueBase>;
+    }
+
     variable {
         name: cqasm::v3x::primitives::Str;
         typ: external One<cqasm::v3x::types::TypeBase>;
     }
 
     statement {
-        # Regular instruction (a gate, or a measure instruction).
         instruction {
-            instruction_ref: cqasm::v3x::instruction::InstructionRef;
-            name: cqasm::v3x::primitives::Str;
-            operands: external Any<cqasm::v3x::values::ValueBase>;
+            # A gate, or a composition of gate modifiers and a gate
+            gate_instruction {
+                instruction_ref: cqasm::v3x::instruction::InstructionRef;
+                gate: One<gate>;
+                operands: external Any<cqasm::v3x::values::ValueBase>;
+            }
+
+            # A measure instruction or a reset instruction
+            non_gate_instruction {
+                instruction_ref: cqasm::v3x::instruction::InstructionRef;
+                name: cqasm::v3x::primitives::Str;
+                operands: external Any<cqasm::v3x::values::ValueBase>;
+            }
         }
     }
 }
diff --git a/src/v3x/AnalyzeTreeGenAstVisitor.cpp b/src/v3x/semantic_analyzer.cpp
similarity index 57%
rename from src/v3x/AnalyzeTreeGenAstVisitor.cpp
rename to src/v3x/semantic_analyzer.cpp
index 913c6a1ce..d7c151a51 100644
--- a/src/v3x/AnalyzeTreeGenAstVisitor.cpp
+++ b/src/v3x/semantic_analyzer.cpp
@@ -1,25 +1,27 @@
-#include "v3x/AnalyzeTreeGenAstVisitor.hpp"
+#include "libqasm/v3x/semantic_analyzer.hpp"
 
 #include <algorithm>  // any_of, for_each, transform
 #include <any>
-#include <cassert>  // assert
 #include <iterator>  // back_inserter
-#include <range/v3/view/tail.hpp>  // tail
 
-#include "v3x/cqasm-analyzer.hpp"
-#include "v3x/cqasm-ast-gen.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/ast_generated.hpp"
+#include "libqasm/v3x/instruction.hpp"
+#include "libqasm/v3x/instruction_set.hpp"
 
 namespace cqasm::v3x::analyzer {
 
-AnalyzeTreeGenAstVisitor::AnalyzeTreeGenAstVisitor(Analyzer &analyzer)
+using instruction::InstructionSet;
+
+SemanticAnalyzer::SemanticAnalyzer(Analyzer& analyzer)
 : analyzer_{ analyzer }
 , result_{} {}
 
-std::any AnalyzeTreeGenAstVisitor::visit_node(ast::Node & /* node */) {
+std::any SemanticAnalyzer::visit_node(ast::Node& /* node */) {
     throw error::AnalysisError{ "unimplemented" };
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_program(ast::Program &program_ast) {
+std::any SemanticAnalyzer::visit_program(ast::Program& program_ast) {
     result_.root = tree::make<semantic::Program>();
     result_.root->api_version = analyzer_.api_version;
     result_.root->version = std::any_cast<tree::One<semantic::Version>>(visit_version(*program_ast.version));
@@ -29,7 +31,7 @@ std::any AnalyzeTreeGenAstVisitor::visit_program(ast::Program &program_ast) {
     return result_;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_version(ast::Version &node) {
+std::any SemanticAnalyzer::visit_version(ast::Version& node) {
     auto ret = tree::make<semantic::Version>();
     try {
         // Check API version
@@ -39,14 +41,14 @@ std::any AnalyzeTreeGenAstVisitor::visit_version(ast::Version &node) {
             }
         }
         if (node.items != analyzer_.api_version) {
-            throw error::AnalysisError(
-                fmt::format("the only cQASM version supported is {}, but the cQASM file is version {}",
-                    analyzer_.api_version,
-                    node.items));
+            throw error::AnalysisError{ fmt::format(
+                "the only cQASM version supported is {}, but the cQASM file is version {}",
+                analyzer_.api_version,
+                node.items) };
         }
 
         ret->items = node.items;
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         err.context(node);
         result_.errors.push_back(std::move(err));
 
@@ -57,34 +59,34 @@ std::any AnalyzeTreeGenAstVisitor::visit_version(ast::Version &node) {
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_global_block(ast::GlobalBlock &node) {
+std::any SemanticAnalyzer::visit_global_block(ast::GlobalBlock& node) {
     visit_block(node);
     return GlobalBlockReturnT{ analyzer_.current_block(), analyzer_.current_variables() };
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_annotated(ast::Annotated &node) {
+std::any SemanticAnalyzer::visit_annotated(ast::Annotated& node) {
     auto ret = tree::Any<semantic::AnnotationData>();
-    for (const auto &annotation_data_ast : node.annotations) {
+    for (const auto& annotation_data_ast : node.annotations) {
         ret.add(std::any_cast<ast::One<semantic::AnnotationData>>(visit_annotation_data(*annotation_data_ast)));
     }
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_annotation_data(ast::AnnotationData &node) {
+std::any SemanticAnalyzer::visit_annotation_data(ast::AnnotationData& node) {
     auto ret = tree::make<semantic::AnnotationData>();
     try {
         ret->interface = node.interface->name;
         ret->operation = node.operation->name;
-        for (const auto &expression_ast : node.operands->items) {
+        for (const auto& expression_ast : node.operands->items) {
             try {
                 ret->operands.add(std::any_cast<values::Value>(expression_ast->visit(*this)));
-            } catch (error::AnalysisError &err) {
+            } catch (error::AnalysisError& err) {
                 err.context(node);
                 result_.errors.push_back(std::move(err));
             }
         }
         ret->copy_annotation<parser::SourceLocation>(node);
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         err.context(node);
         result_.errors.push_back(std::move(err));
         ret.reset();
@@ -92,7 +94,7 @@ std::any AnalyzeTreeGenAstVisitor::visit_annotation_data(ast::AnnotationData &no
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_variable(ast::Variable &node) {
+std::any SemanticAnalyzer::visit_variable(ast::Variable& node) {
     auto ret = tree::make<semantic::Variable>();
     try {
         // Build semantic type from syntactic type
@@ -100,7 +102,7 @@ std::any AnalyzeTreeGenAstVisitor::visit_variable(ast::Variable &node) {
 
         // Construct variable
         // Use the location tag of the identifier to record where the variable was defined
-        const auto identifier = node.name;
+        const auto& identifier = node.name;
         ret->name = identifier->name;
         ret->typ = type.clone();
         ret->annotations = std::any_cast<tree::Any<semantic::AnnotationData>>(visit_annotated(*node.as_annotated()));
@@ -111,7 +113,7 @@ std::any AnalyzeTreeGenAstVisitor::visit_variable(ast::Variable &node) {
 
         // Register the variable
         analyzer_.register_variable(identifier->name, tree::make<values::VariableRef>(ret));
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         err.context(node);
         result_.errors.push_back(std::move(err));
         ret.reset();
@@ -119,38 +121,44 @@ std::any AnalyzeTreeGenAstVisitor::visit_variable(ast::Variable &node) {
     return ret;
 }
 
-/**
- * Promote a value to a given type,
- * or error if the promotion is not valid
- */
-values::Value promote_or_error(const values::Value &rhs_value, const types::Type &lhs_type) {
-    if (auto rhs_promoted_value = values::promote(rhs_value, lhs_type); !rhs_promoted_value.empty()) {
-        return rhs_promoted_value;
-    }
-    throw error::AnalysisError{ fmt::format("type of right-hand side ({}) could not be coerced to left-hand side ({})",
-        values::type_of(rhs_value),
-        lhs_type) };
-}
-
 /**
  * Convenience function for extracting the types of a list of variables.
  */
-types::Types types_of(const tree::Any<semantic::Variable> &variables) {
+types::Types types_of(const tree::Any<semantic::Variable>& variables) {
     types::Types types{};
-    for (const auto &variable : variables) {
+    for (const auto& variable : variables) {
         types.add(variable->typ.clone());
     }
     return types;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_gate(ast::Gate &node) {
-    auto ret = tree::Maybe<semantic::Instruction>();
+/**
+ * For a named gate, such as X or Rz, the terminal name will be the name of the gate.
+ * For a composition of gate modifiers acting on a named gate, the terminal name will be the name of the named gate.
+ */
+std::string get_gate_terminal_name(const tree::One<semantic::Gate>& gate) {
+    return gate->gate.empty() ? gate->name : get_gate_terminal_name(gate->gate);
+}
+
+std::string get_gate_resolution_name(const tree::One<semantic::Gate>& gate) {
+    return gate->gate.empty() ? gate->name
+                              : fmt::format("{}_{}",
+                                    (InstructionSet::get_instance().is_single_qubit_gate_modifier(gate->name))
+                                        ? InstructionSet::get_instance().single_qubit_gate_composition_prefix
+                                        : InstructionSet::get_instance().two_qubit_gate_composition_prefix,
+                                    get_gate_terminal_name(gate->gate));
+}
+
+std::any SemanticAnalyzer::visit_gate_instruction(ast::GateInstruction& node) {
+    auto ret = tree::make<semantic::GateInstruction>();
     try {
-        // Set operand list
-        auto operands = std::any_cast<values::Values>(visit_expression_list(*node.operands));
+        // Set gate and operands
+        auto gate = std::any_cast<tree::One<semantic::Gate>>(node.gate->visit(*this));
+        auto operands = std::any_cast<values::Values>(node.operands->visit(*this));
 
         // Resolve the instruction
-        ret.set(analyzer_.resolve_instruction(node.name->name, operands));
+        const auto& resolution_name = get_gate_resolution_name(gate);
+        ret.set(analyzer_.resolve_instruction(resolution_name, gate, operands));
 
         // Copy annotation data
         ret->annotations = std::any_cast<tree::Any<semantic::AnnotationData>>(visit_annotated(*node.as_annotated()));
@@ -158,25 +166,75 @@ std::any AnalyzeTreeGenAstVisitor::visit_gate(ast::Gate &node) {
 
         // Add the statement to the current scope
         analyzer_.add_statement_to_current_scope(ret);
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         err.context(node);
         result_.errors.push_back(std::move(err));
         ret.reset();
     }
+    return ret;
+}
+
+bool is_two_qubit_gate(const tree::One<semantic::Gate>& gate) {
+    const auto& resolution_name = get_gate_resolution_name(gate);
+    return InstructionSet::get_instance().is_two_qubit_gate(resolution_name);
+}
+
+void check_gate(const tree::One<semantic::Gate>& gate) {
+    if (!gate->gate.empty() && is_two_qubit_gate(gate->gate)) {
+        throw error::AnalysisError{ "trying to apply a gate modifier to a multi-qubit gate" };
+    }
+}
+
+void resolve_gate(const tree::One<semantic::Gate>& gate) {
+    if (!gate->parameter.empty()) {
+        const auto& instruction_set = InstructionSet::get_instance();
+        const auto& param_type = instruction_set.get_instruction_param_type(gate->name);
+        if (!param_type.has_value() ||
+            !values::check_promote(values::type_of(gate->parameter), types::from_spec(param_type.value()))) {
+            throw error::AnalysisError{ fmt::format(
+                "failed to resolve '{}' with argument pack ({})", gate->name, values::type_of(gate->parameter)) };
+        }
+        gate->parameter = promote(gate->parameter, types::from_spec(param_type.value()));
+    }
+}
+
+std::any SemanticAnalyzer::visit_gate(ast::Gate& node) {
+    auto ret = tree::make<semantic::Gate>();
+    try {
+        ret->name = node.name->name;
+        if (!node.gate.empty()) {
+            ret->gate = std::any_cast<tree::One<semantic::Gate>>(visit_gate(*node.gate)).get_ptr();
+        }
+        if (!node.parameter.empty()) {
+            ret->parameter = std::any_cast<values::Value>(visit_expression(*node.parameter)).get_ptr();
+        }
 
+        // Resolve the gate
+        resolve_gate(ret);
+
+        // Specific checks
+        check_gate(ret);
+
+        // Copy annotation data
+        ret->annotations = std::any_cast<tree::Any<semantic::AnnotationData>>(visit_annotated(*node.as_annotated()));
+        ret->copy_annotation<parser::SourceLocation>(node);
+    } catch (error::AnalysisError& err) {
+        err.context(node);
+        throw;
+    }
     return ret;
 }
 
-bool check_qubit_and_bit_indices_have_same_size(const values::Values &operands) {
+void check_qubit_and_bit_indices_have_same_size(const values::Values& operands) {
     size_t qubit_indices_size{};
     size_t bit_indices_size{};
-    // Instruction operands can be, whether variables references or index references
+    // Instruction operands can be, either variable references or index references
     // Variables can be of type qubit, bit, qubit array, or bit array
     // Qubits and bits have a single index, arrays have a size
     // Index references point to a qubit array or bit array
-    for (const auto &operand : operands) {
+    for (const auto& operand : operands) {
         if (auto variable_ref = operand->as_variable_ref()) {
-            const auto &variable = *variable_ref->variable;
+            const auto& variable = *variable_ref->variable;
             if (variable.typ->as_qubit()) {
                 qubit_indices_size += 1;
             } else if (auto qubit_array = variable.typ->as_qubit_array()) {
@@ -187,7 +245,7 @@ bool check_qubit_and_bit_indices_have_same_size(const values::Values &operands)
                 bit_indices_size += bit_array->size;
             }
         } else if (auto index_ref = operand->as_index_ref()) {
-            const auto &variable = *index_ref->variable;
+            const auto& variable = *index_ref->variable;
             if (variable.typ->as_qubit() || variable.typ->as_qubit_array()) {
                 qubit_indices_size += index_ref->indices.size();
             } else if (variable.typ->as_bit() || variable.typ->as_bit_array()) {
@@ -195,56 +253,28 @@ bool check_qubit_and_bit_indices_have_same_size(const values::Values &operands)
             }
         }
     }
-    return qubit_indices_size == bit_indices_size;
+    if (qubit_indices_size != bit_indices_size) {
+        throw error::AnalysisError{ "qubit and bit indices have different sizes" };
+    }
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_measure_instruction(ast::MeasureInstruction &node) {
-    auto ret = tree::Maybe<semantic::Instruction>();
-    try {
-        // Set operand
-        // Notice operands have to be added in this order
-        // Otherwise instruction resolution would fail
-        auto operands = values::Values();
-        operands.add(std::any_cast<values::Value>(visit_expression(*node.lhs)));
-        operands.add(std::any_cast<values::Value>(visit_expression(*node.rhs)));
-
-        // Resolve the instruction
-        ret.set(analyzer_.resolve_instruction(node.name->name, operands));
-
-        // Check qubit and bit indices have the same size
-        if (!ret->instruction_ref.empty()) {
-            if (!check_qubit_and_bit_indices_have_same_size(operands)) {
-                throw error::AnalysisError{ "qubit and bit indices have different sizes" };
-            }
-        }
-
-        // Copy annotation data
-        ret->annotations = std::any_cast<tree::Any<semantic::AnnotationData>>(visit_annotated(*node.as_annotated()));
-        ret->copy_annotation<parser::SourceLocation>(node);
-
-        // Add the statement to the current scope
-        analyzer_.add_statement_to_current_scope(ret);
-    } catch (error::AnalysisError &err) {
-        err.context(node);
-        result_.errors.push_back(std::move(err));
-        ret.reset();
+void check_non_gate_instruction(const tree::One<semantic::NonGateInstruction>& instruction) {
+    if (InstructionSet::get_instance().is_measure(instruction->name)) {
+        check_qubit_and_bit_indices_have_same_size(instruction->operands);
     }
-    return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_reset_instruction(ast::ResetInstruction &node) {
-    auto ret = tree::Maybe<semantic::Instruction>();
+std::any SemanticAnalyzer::visit_non_gate_instruction(ast::NonGateInstruction& node) {
+    auto ret = tree::make<semantic::NonGateInstruction>();
     try {
-        // Set operand
-        // Notice operands have to be added in this order
-        // Otherwise instruction resolution would fail
-        auto operands = values::Values();
-        if (!node.operand.empty()) {
-            operands.add(std::any_cast<values::Value>(visit_expression(*node.operand)));
-        }
+        const auto& name = node.name->name;
+        const auto& operands = std::any_cast<values::Values>(visit_expression_list(*node.operands));
 
         // Resolve the instruction
-        ret.set(analyzer_.resolve_instruction(node.name->name, operands));
+        ret.set(analyzer_.resolve_instruction(name, operands));
+
+        // Specific checks
+        check_non_gate_instruction(ret);
 
         // Copy annotation data
         ret->annotations = std::any_cast<tree::Any<semantic::AnnotationData>>(visit_annotated(*node.as_annotated()));
@@ -252,7 +282,7 @@ std::any AnalyzeTreeGenAstVisitor::visit_reset_instruction(ast::ResetInstruction
 
         // Add the statement to the current scope
         analyzer_.add_statement_to_current_scope(ret);
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         err.context(node);
         result_.errors.push_back(std::move(err));
         ret.reset();
@@ -260,21 +290,21 @@ std::any AnalyzeTreeGenAstVisitor::visit_reset_instruction(ast::ResetInstruction
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_expression_list(ast::ExpressionList &node) {
+std::any SemanticAnalyzer::visit_expression_list(ast::ExpressionList& node) {
     auto ret = values::Values();
     std::transform(
-        node.items.begin(), node.items.end(), std::back_inserter(ret.get_vec()), [this](const auto &expression_ast) {
+        node.items.begin(), node.items.end(), std::back_inserter(ret.get_vec()), [this](const auto& expression_ast) {
             return std::any_cast<values::Value>(visit_expression(*expression_ast));
         });
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_expression(ast::Expression &node) {
+std::any SemanticAnalyzer::visit_expression(ast::Expression& node) {
     try {
         auto ret = std::any_cast<values::Value>(node.visit(*this));
         ret->copy_annotation<parser::SourceLocation>(node);
         return ret;
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         err.context(node);
         throw;
     }
@@ -283,8 +313,8 @@ std::any AnalyzeTreeGenAstVisitor::visit_expression(ast::Expression &node) {
 /**
  * Convenience function for visiting a function call given the function's name and arguments
  */
-values::Value AnalyzeTreeGenAstVisitor::visit_function_call(
-    const tree::One<ast::Identifier> &name, const tree::Maybe<ast::ExpressionList> &arguments) {
+values::Value SemanticAnalyzer::visit_function_call(
+    const tree::One<ast::Identifier>& name, const tree::Maybe<ast::ExpressionList>& arguments) {
     auto function_arguments = values::Values();
     if (!arguments.empty()) {
         std::for_each(
@@ -300,15 +330,14 @@ values::Value AnalyzeTreeGenAstVisitor::visit_function_call(
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_function_call(ast::FunctionCall &node) {
+std::any SemanticAnalyzer::visit_function_call(ast::FunctionCall& node) {
     return visit_function_call(node.name, node.arguments);
 }
 
 /**
  * Convenience function for visiting unary operators
  */
-std::any AnalyzeTreeGenAstVisitor::visit_unary_operator(
-    const std::string &name, const tree::One<ast::Expression> &expression) {
+std::any SemanticAnalyzer::visit_unary_operator(const std::string& name, const tree::One<ast::Expression>& expression) {
     return visit_function_call(tree::make<ast::Identifier>(std::string{ "operator" } + name),
         tree::Maybe<ast::ExpressionList>{
             tree::make<ast::ExpressionList>(tree::Any<ast::Expression>{ expression }).get_ptr() });
@@ -317,106 +346,106 @@ std::any AnalyzeTreeGenAstVisitor::visit_unary_operator(
 /**
  * Convenience function for visiting binary operators
  */
-std::any AnalyzeTreeGenAstVisitor::visit_binary_operator(
-    const std::string &name, const tree::One<ast::Expression> &lhs, const tree::One<ast::Expression> &rhs) {
+std::any SemanticAnalyzer::visit_binary_operator(
+    const std::string& name, const tree::One<ast::Expression>& lhs, const tree::One<ast::Expression>& rhs) {
     return visit_function_call(tree::make<ast::Identifier>(std::string{ "operator" } + name),
         tree::Maybe<ast::ExpressionList>{
             tree::make<ast::ExpressionList>(tree::Any<ast::Expression>{ lhs, rhs }).get_ptr() });
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_unary_minus_expression(ast::UnaryMinusExpression &node) {
+std::any SemanticAnalyzer::visit_unary_minus_expression(ast::UnaryMinusExpression& node) {
     return visit_unary_operator("-", node.expr);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_bitwise_not_expression(ast::BitwiseNotExpression &node) {
+std::any SemanticAnalyzer::visit_bitwise_not_expression(ast::BitwiseNotExpression& node) {
     return visit_unary_operator("~", node.expr);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_logical_not_expression(ast::LogicalNotExpression &node) {
+std::any SemanticAnalyzer::visit_logical_not_expression(ast::LogicalNotExpression& node) {
     return visit_unary_operator("!", node.expr);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_power_expression(ast::PowerExpression &node) {
+std::any SemanticAnalyzer::visit_power_expression(ast::PowerExpression& node) {
     return visit_binary_operator("**", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_product_expression(ast::ProductExpression &node) {
+std::any SemanticAnalyzer::visit_product_expression(ast::ProductExpression& node) {
     return visit_binary_operator("*", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_division_expression(ast::DivisionExpression &node) {
+std::any SemanticAnalyzer::visit_division_expression(ast::DivisionExpression& node) {
     return visit_binary_operator("/", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_modulo_expression(ast::ModuloExpression &node) {
+std::any SemanticAnalyzer::visit_modulo_expression(ast::ModuloExpression& node) {
     return visit_binary_operator("%", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_addition_expression(ast::AdditionExpression &node) {
+std::any SemanticAnalyzer::visit_addition_expression(ast::AdditionExpression& node) {
     return visit_binary_operator("+", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_subtraction_expression(ast::SubtractionExpression &node) {
+std::any SemanticAnalyzer::visit_subtraction_expression(ast::SubtractionExpression& node) {
     return visit_binary_operator("-", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_shift_left_expression(ast::ShiftLeftExpression &node) {
+std::any SemanticAnalyzer::visit_shift_left_expression(ast::ShiftLeftExpression& node) {
     return visit_binary_operator("<<", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_shift_right_expression(ast::ShiftRightExpression &node) {
+std::any SemanticAnalyzer::visit_shift_right_expression(ast::ShiftRightExpression& node) {
     return visit_binary_operator(">>", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_cmp_gt_expression(ast::CmpGtExpression &node) {
+std::any SemanticAnalyzer::visit_cmp_gt_expression(ast::CmpGtExpression& node) {
     return visit_binary_operator(">", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_cmp_lt_expression(ast::CmpLtExpression &node) {
+std::any SemanticAnalyzer::visit_cmp_lt_expression(ast::CmpLtExpression& node) {
     return visit_binary_operator("<", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_cmp_ge_expression(ast::CmpGeExpression &node) {
+std::any SemanticAnalyzer::visit_cmp_ge_expression(ast::CmpGeExpression& node) {
     return visit_binary_operator(">=", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_cmp_le_expression(ast::CmpLeExpression &node) {
+std::any SemanticAnalyzer::visit_cmp_le_expression(ast::CmpLeExpression& node) {
     return visit_binary_operator("<=", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_cmp_eq_expression(ast::CmpEqExpression &node) {
+std::any SemanticAnalyzer::visit_cmp_eq_expression(ast::CmpEqExpression& node) {
     return visit_binary_operator("==", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_cmp_ne_expression(ast::CmpNeExpression &node) {
+std::any SemanticAnalyzer::visit_cmp_ne_expression(ast::CmpNeExpression& node) {
     return visit_binary_operator("!=", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_bitwise_and_expression(ast::BitwiseAndExpression &node) {
+std::any SemanticAnalyzer::visit_bitwise_and_expression(ast::BitwiseAndExpression& node) {
     return visit_binary_operator("&", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_bitwise_xor_expression(ast::BitwiseXorExpression &node) {
+std::any SemanticAnalyzer::visit_bitwise_xor_expression(ast::BitwiseXorExpression& node) {
     return visit_binary_operator("^", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_bitwise_or_expression(ast::BitwiseOrExpression &node) {
+std::any SemanticAnalyzer::visit_bitwise_or_expression(ast::BitwiseOrExpression& node) {
     return visit_binary_operator("|", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_logical_and_expression(ast::LogicalAndExpression &node) {
+std::any SemanticAnalyzer::visit_logical_and_expression(ast::LogicalAndExpression& node) {
     return visit_binary_operator("&&", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_logical_xor_expression(ast::LogicalXorExpression &node) {
+std::any SemanticAnalyzer::visit_logical_xor_expression(ast::LogicalXorExpression& node) {
     return visit_binary_operator("^^", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_logical_or_expression(ast::LogicalOrExpression &node) {
+std::any SemanticAnalyzer::visit_logical_or_expression(ast::LogicalOrExpression& node) {
     return visit_binary_operator("||", node.lhs, node.rhs);
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_ternary_conditional_expression(ast::TernaryConditionalExpression &node) {
+std::any SemanticAnalyzer::visit_ternary_conditional_expression(ast::TernaryConditionalExpression& node) {
     return visit_function_call(tree::make<ast::Identifier>("operator?:"),
         tree::make<ast::ExpressionList>(tree::Any<ast::Expression>{ node.cond, node.if_true, node.if_false }));
 }
@@ -424,15 +453,15 @@ std::any AnalyzeTreeGenAstVisitor::visit_ternary_conditional_expression(ast::Ter
 /**
  * Check out of range accesses from any index in an input list to an array of a given size
  */
-void check_out_of_range(const IndexListT &indices, primitives::Int size) {
-    for (const auto &index_item : indices) {
+void check_out_of_range(const IndexListT& indices, primitives::Int size) {
+    for (const auto& index_item : indices) {
         if (index_item->value < 0 || index_item->value >= size) {
             throw error::AnalysisError{ fmt::format("index {} out of range (size {})", index_item->value, size) };
         }
     }
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_index(ast::Index &node) {
+std::any SemanticAnalyzer::visit_index(ast::Index& node) {
     try {
         auto expression = std::any_cast<values::Value>(visit_expression(*node.expr));
         auto variable_ref_ptr = expression->as_variable_ref();
@@ -447,15 +476,15 @@ std::any AnalyzeTreeGenAstVisitor::visit_index(ast::Index &node) {
             throw error::AnalysisError{ fmt::format(
                 "indexation is not supported for value of type '{}'", values::type_of(expression)) };
         }
-    } catch (error::AnalysisError &err) {
+    } catch (error::AnalysisError& err) {
         err.context(node);
         throw;
     }
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_index_list(ast::IndexList &index_list_ast) {
+std::any SemanticAnalyzer::visit_index_list(ast::IndexList& index_list_ast) {
     auto ret = IndexListT{};
-    for (const auto &index_entry : index_list_ast.items) {
+    for (const auto& index_entry : index_list_ast.items) {
         if (auto index_item = index_entry->as_index_item()) {
             // Single index
             ret.add(std::any_cast<tree::One<IndexT>>(visit_index_item(*index_item)));
@@ -469,14 +498,14 @@ std::any AnalyzeTreeGenAstVisitor::visit_index_list(ast::IndexList &index_list_a
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_index_item(ast::IndexItem &index_item_ast) {
+std::any SemanticAnalyzer::visit_index_item(ast::IndexItem& index_item_ast) {
     auto index_item = visit_const_int(*index_item_ast.index);
     auto index_value_sp = tree::make<IndexT>(index_item);
     index_value_sp->copy_annotation<parser::SourceLocation>(index_item_ast);
     return index_value_sp;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_index_range(ast::IndexRange &index_range_ast) {
+std::any SemanticAnalyzer::visit_index_range(ast::IndexRange& index_range_ast) {
     auto first = visit_const_int(*index_range_ast.first);
     auto last = visit_const_int(*index_range_ast.last);
     if (first > last) {
@@ -491,30 +520,30 @@ std::any AnalyzeTreeGenAstVisitor::visit_index_range(ast::IndexRange &index_rang
     return ret;
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_identifier(ast::Identifier &node) {
+std::any SemanticAnalyzer::visit_identifier(ast::Identifier& node) {
     return analyzer_.resolve_variable(node.name);
 }
 
 /**
  * If any element of the initialization list is not a const boolean, const int, or const float, throw an error
  */
-void check_initialization_list_element_type(const values::Value &value) {
+void check_initialization_list_element_type(const values::Value& value) {
     if (!(value->as_const_bool() || value->as_const_int() || value->as_const_float())) {
         throw error::AnalysisError{ "expecting a const bool, const int, or const float value" };
     }
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_boolean_literal(ast::BooleanLiteral &node) {
+std::any SemanticAnalyzer::visit_boolean_literal(ast::BooleanLiteral& node) {
     auto ret = tree::make<values::ConstBool>(node.value);
     return values::Value{ ret };
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_integer_literal(ast::IntegerLiteral &node) {
+std::any SemanticAnalyzer::visit_integer_literal(ast::IntegerLiteral& node) {
     auto ret = tree::make<values::ConstInt>(node.value);
     return values::Value{ ret };
 }
 
-std::any AnalyzeTreeGenAstVisitor::visit_float_literal(ast::FloatLiteral &node) {
+std::any SemanticAnalyzer::visit_float_literal(ast::FloatLiteral& node) {
     auto ret = tree::make<values::ConstFloat>(node.value);
     return values::Value{ ret };
 }
@@ -522,7 +551,7 @@ std::any AnalyzeTreeGenAstVisitor::visit_float_literal(ast::FloatLiteral &node)
 /**
  * Shorthand for parsing an expression to a constant integer.
  */
-primitives::Int AnalyzeTreeGenAstVisitor::visit_const_int(ast::Expression &expression) {
+primitives::Int SemanticAnalyzer::visit_const_int(ast::Expression& expression) {
     if (auto int_value = visit_as<types::Int>(expression); !int_value.empty()) {
         if (auto const_int_value = int_value->as_const_int()) {
             return const_int_value->value;
diff --git a/src/v3x/BuildTreeGenAstVisitor.cpp b/src/v3x/syntactic_analyzer.cpp
similarity index 57%
rename from src/v3x/BuildTreeGenAstVisitor.cpp
rename to src/v3x/syntactic_analyzer.cpp
index e64028acb..1b359b551 100644
--- a/src/v3x/BuildTreeGenAstVisitor.cpp
+++ b/src/v3x/syntactic_analyzer.cpp
@@ -1,4 +1,4 @@
-#include "v3x/BuildTreeGenAstVisitor.hpp"
+#include "libqasm/v3x/syntactic_analyzer.hpp"
 
 #include <antlr4-runtime.h>
 
@@ -9,18 +9,18 @@
 #include <stdexcept>  // runtime_error
 #include <string>  // stod, stoll
 
-#include "cqasm-annotations.hpp"
-#include "cqasm-tree.hpp"
-#include "v3x/CustomErrorListener.hpp"
-#include "v3x/cqasm-ast.hpp"
-#include "v3x/cqasm-types.hpp"
+#include "libqasm/annotations.hpp"
+#include "libqasm/tree.hpp"
+#include "libqasm/v3x/antlr_custom_error_listener.hpp"
+#include "libqasm/v3x/ast.hpp"
+#include "libqasm/v3x/types.hpp"
 
 namespace cqasm::v3x::parser {
 
 using namespace cqasm::v3x::ast;
 using namespace cqasm::error;
 
-BuildTreeGenAstVisitor::BuildTreeGenAstVisitor(const std::optional<std::string> &file_name)
+SyntacticAnalyzer::SyntacticAnalyzer(const std::optional<std::string>& file_name)
 : file_name_{ file_name }
 , error_listener_p_{ nullptr } {
     if (file_name_.has_value() && file_name_.value().empty()) {
@@ -28,11 +28,11 @@ BuildTreeGenAstVisitor::BuildTreeGenAstVisitor(const std::optional<std::string>
     }
 }
 
-void BuildTreeGenAstVisitor::addErrorListener(CustomErrorListener *errorListener) {
+void SyntacticAnalyzer::addErrorListener(AntlrCustomErrorListener* errorListener) {
     error_listener_p_ = errorListener;
 }
 
-void BuildTreeGenAstVisitor::syntaxError(size_t line, size_t char_position_in_line, const std::string &text) const {
+void SyntacticAnalyzer::syntaxError(size_t line, size_t char_position_in_line, const std::string& text) const {
     assert(error_listener_p_);
     error_listener_p_->syntaxError(line, char_position_in_line, text);
 }
@@ -41,7 +41,7 @@ void BuildTreeGenAstVisitor::syntaxError(size_t line, size_t char_position_in_li
  * ANTLR provides a zero-based character position in line
  * We change it here to a one-based index, which is the more human-readable, and the common option in text editors
  */
-void BuildTreeGenAstVisitor::setNodeAnnotation(const ast::One<ast::Node> &node, antlr4::Token *token) const {
+void SyntacticAnalyzer::setNodeAnnotation(const ast::One<ast::Node>& node, antlr4::Token* token) const {
     auto token_size = token->getStopIndex() - token->getStartIndex() + 1;
     node->set_annotation(annotations::SourceLocation{ file_name_,
         { { static_cast<std::uint32_t>(token->getLine()),
@@ -50,7 +50,7 @@ void BuildTreeGenAstVisitor::setNodeAnnotation(const ast::One<ast::Node> &node,
                 static_cast<std::uint32_t>(token->getCharPositionInLine() + 1 + token_size) } } });
 }
 
-void BuildTreeGenAstVisitor::expandNodeAnnotation(const One<Node> &node, antlr4::Token *token) const {
+void SyntacticAnalyzer::expandNodeAnnotation(const One<Node>& node, antlr4::Token* token) const {
     auto token_size = token->getStopIndex() - token->getStartIndex() + 1;
     if (auto source_location = node->get_annotation_ptr<annotations::SourceLocation>()) {
         source_location->expand_to_include(
@@ -59,45 +59,48 @@ void BuildTreeGenAstVisitor::expandNodeAnnotation(const One<Node> &node, antlr4:
     }
 }
 
-std::int64_t BuildTreeGenAstVisitor::get_int_value(
-    size_t line, size_t char_position_in_line, const std::string &text) const {
+void SyntacticAnalyzer::copyNodeAnnotation(const ast::One<ast::Node>& from, const ast::One<ast::Node>& to) const {
+    to->copy_annotation<annotations::SourceLocation>(*from);
+}
+
+std::int64_t SyntacticAnalyzer::get_int_value(
+    size_t line, size_t char_position_in_line, const std::string& text) const {
     try {
         return std::stoll(text);
-    } catch (std::out_of_range &) {
+    } catch (std::out_of_range&) {
         syntaxError(line, char_position_in_line, fmt::format("value '{}' is out of the INTEGER_LITERAL range", text));
     }
     return {};
 }
 
-double BuildTreeGenAstVisitor::get_float_value(
-    size_t line, size_t char_position_in_line, const std::string &text) const {
+double SyntacticAnalyzer::get_float_value(size_t line, size_t char_position_in_line, const std::string& text) const {
     try {
         return std::stod(text);
-    } catch (std::out_of_range &) {
+    } catch (std::out_of_range&) {
         syntaxError(line, char_position_in_line, fmt::format("value '{}' is out of the FLOAT_LITERAL range", text));
     }
     return {};
 }
 
-bool BuildTreeGenAstVisitor::get_bool_value(antlr4::tree::TerminalNode *node) const {
+bool SyntacticAnalyzer::get_bool_value(antlr4::tree::TerminalNode* node) const {
     assert(node->getSymbol()->getType() == CqasmParser::BOOLEAN_LITERAL);
     return node->getText() == "true";
 }
 
-std::int64_t BuildTreeGenAstVisitor::get_int_value(antlr4::tree::TerminalNode *node) const {
+std::int64_t SyntacticAnalyzer::get_int_value(antlr4::tree::TerminalNode* node) const {
     const auto token = node->getSymbol();
     assert(token->getType() == CqasmParser::INTEGER_LITERAL);
     return get_int_value(token->getLine(), token->getCharPositionInLine(), node->getText());
 }
 
-double BuildTreeGenAstVisitor::get_float_value(antlr4::tree::TerminalNode *node) const {
+double SyntacticAnalyzer::get_float_value(antlr4::tree::TerminalNode* node) const {
     const auto token = node->getSymbol();
-    const auto &text = node->getText();
+    const auto& text = node->getText();
     assert(token->getType() == CqasmParser::FLOAT_LITERAL);
     return get_float_value(token->getLine(), token->getCharPositionInLine(), text);
 }
 
-std::any BuildTreeGenAstVisitor::visitProgram(CqasmParser::ProgramContext *context) {
+std::any SyntacticAnalyzer::visitProgram(CqasmParser::ProgramContext* context) {
     auto ret = tree::make<Program>();
     ret->version = std::any_cast<One<Version>>(visitVersionSection(context->versionSection()));
     ret->block = context->globalBlockSection()
@@ -106,31 +109,31 @@ std::any BuildTreeGenAstVisitor::visitProgram(CqasmParser::ProgramContext *conte
     return ret;
 }
 
-std::any BuildTreeGenAstVisitor::visitVersionSection(CqasmParser::VersionSectionContext *context) {
+std::any SyntacticAnalyzer::visitVersionSection(CqasmParser::VersionSectionContext* context) {
     return visitVersion(context->version());
 }
 
-std::any BuildTreeGenAstVisitor::visitGlobalBlockSection(CqasmParser::GlobalBlockSectionContext *context) {
+std::any SyntacticAnalyzer::visitGlobalBlockSection(CqasmParser::GlobalBlockSectionContext* context) {
     auto ret = tree::make<GlobalBlock>();
-    const auto &statements = context->globalBlockStatement();
-    std::for_each(statements.begin(), statements.end(), [this, &ret](const auto &statement_ctx) {
+    const auto& statements = context->globalBlockStatement();
+    std::for_each(statements.begin(), statements.end(), [this, &ret](const auto& statement_ctx) {
         ret->statements.add(std::any_cast<One<Statement>>(statement_ctx->accept(this)));
     });
     return ret;
 }
 
-std::any BuildTreeGenAstVisitor::visitEofSection(CqasmParser::EofSectionContext *) {
+std::any SyntacticAnalyzer::visitEofSection(CqasmParser::EofSectionContext*) {
     return {};
 }
 
-std::any BuildTreeGenAstVisitor::visitStatementSeparator(CqasmParser::StatementSeparatorContext *) {
+std::any SyntacticAnalyzer::visitStatementSeparator(CqasmParser::StatementSeparatorContext*) {
     return {};
 }
 
-std::any BuildTreeGenAstVisitor::visitVersion(CqasmParser::VersionContext *context) {
+std::any SyntacticAnalyzer::visitVersion(CqasmParser::VersionContext* context) {
     auto ret = tree::make<Version>();
     const auto token = context->VERSION_NUMBER()->getSymbol();
-    const auto &text = context->VERSION_NUMBER()->getText();
+    const auto& text = context->VERSION_NUMBER()->getText();
     const std::regex pattern{ "([0-9]+)(?:\\.([0-9]+))?" };
     std::smatch matches{};
     std::regex_match(text, matches, pattern);
@@ -143,54 +146,98 @@ std::any BuildTreeGenAstVisitor::visitVersion(CqasmParser::VersionContext *conte
     return ret;
 }
 
-std::any BuildTreeGenAstVisitor::visitGlobalBlockStatement(CqasmParser::GlobalBlockStatementContext *context) {
+std::any SyntacticAnalyzer::visitGlobalBlockStatement(CqasmParser::GlobalBlockStatementContext* context) {
     if (auto variable_definition_ctx = context->variableDefinition(); variable_definition_ctx) {
         return variable_definition_ctx->accept(this);
     } else if (auto instruction_ctx = context->instruction(); instruction_ctx) {
         return instruction_ctx->accept(this);
     }
-    throw error::AnalysisError{ "unknown local block statement type" };
+    throw error::AnalysisError{ "unknown global block statement type" };
 }
 
-std::any BuildTreeGenAstVisitor::visitVariableDefinition(CqasmParser::VariableDefinitionContext *context) {
+std::any SyntacticAnalyzer::visitVariableDefinition(CqasmParser::VariableDefinitionContext* context) {
     return One<Statement>{ visitVariable(context) };
 }
 
-std::any BuildTreeGenAstVisitor::visitMeasureInstruction(CqasmParser::MeasureInstructionContext *context) {
-    auto ret = tree::make<MeasureInstruction>();
-    ret->name = tree::make<Identifier>(context->MEASURE()->getText());
-    ret->lhs = std::any_cast<One<Expression>>(context->expression(0)->accept(this));
-    ret->rhs = std::any_cast<One<Expression>>(context->expression(1)->accept(this));
-    setNodeAnnotation(ret, context->MEASURE()->getSymbol());
-    return One<Statement>{ ret };
+std::any SyntacticAnalyzer::visitInstruction(CqasmParser::InstructionContext* context) {
+    if (auto gate_instruction_ctx = context->gateInstruction(); gate_instruction_ctx) {
+        return gate_instruction_ctx->accept(this);
+    } else if (auto non_gate_instruction_ctx = context->nonGateInstruction(); non_gate_instruction_ctx) {
+        return non_gate_instruction_ctx->accept(this);
+    }
+    throw error::AnalysisError{ "unknown instruction type" };
 }
 
-std::any BuildTreeGenAstVisitor::visitResetInstruction(CqasmParser::ResetInstructionContext *context) {
-    auto ret = tree::make<ResetInstruction>();
-    ret->name = tree::make<Identifier>(context->RESET()->getText());
-    ret->operand = context->expression()
-        ? Maybe<Expression>{ std::any_cast<One<Expression>>(context->expression()->accept(this)) }
-        : Maybe<Expression>{};
-    setNodeAnnotation(ret, context->RESET()->getSymbol());
+std::any SyntacticAnalyzer::visitGateInstruction(CqasmParser::GateInstructionContext* context) {
+    auto ret = tree::make<GateInstruction>();
+    ret->gate = std::any_cast<One<Gate>>(context->gate()->accept(this));
+    ret->operands = std::any_cast<One<ExpressionList>>(visitExpressionList(context->expressionList()));
+    // Set the instruction annotation to the annotation of its gate
+    copyNodeAnnotation(ret->gate, ret);
     return One<Statement>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitGate(CqasmParser::GateContext *context) {
+std::any SyntacticAnalyzer::visitInvGate(CqasmParser::InvGateContext* context) {
+    auto ret = tree::make<Gate>();
+    ret->name = tree::make<Identifier>(context->INV()->getText());
+    ret->gate = std::any_cast<tree::One<Gate>>(context->gate()->accept(this)).get_ptr();
+    setNodeAnnotation(ret, context->INV()->getSymbol());
+    return ret;
+}
+
+std::any SyntacticAnalyzer::visitPowGate(CqasmParser::PowGateContext* context) {
+    auto ret = tree::make<Gate>();
+    ret->name = tree::make<Identifier>(context->POW()->getText());
+    ret->gate = std::any_cast<tree::One<Gate>>(context->gate()->accept(this)).get_ptr();
+    ret->parameter = std::any_cast<One<Expression>>(context->expression()->accept(this)).get_ptr();
+    setNodeAnnotation(ret, context->POW()->getSymbol());
+    return ret;
+}
+
+std::any SyntacticAnalyzer::visitCtrlGate(CqasmParser::CtrlGateContext* context) {
+    auto ret = tree::make<Gate>();
+    ret->name = tree::make<Identifier>(context->CTRL()->getText());
+    ret->gate = std::any_cast<tree::One<Gate>>(context->gate()->accept(this)).get_ptr();
+    setNodeAnnotation(ret, context->CTRL()->getSymbol());
+    return ret;
+}
+
+std::any SyntacticAnalyzer::visitNamedGate(CqasmParser::NamedGateContext* context) {
     auto ret = tree::make<Gate>();
     ret->name = tree::make<Identifier>(context->IDENTIFIER()->getText());
-    ret->operands = std::any_cast<One<ExpressionList>>(visitExpressionList(context->expressionList()));
     if (context->expression()) {
-        ret->operands->items.add(std::any_cast<One<Expression>>(context->expression()->accept(this)));
+        ret->parameter = std::any_cast<One<Expression>>(context->expression()->accept(this)).get_ptr();
     }
     setNodeAnnotation(ret, context->IDENTIFIER()->getSymbol());
+    return ret;
+}
+
+std::any SyntacticAnalyzer::visitMeasureInstruction(CqasmParser::MeasureInstructionContext* context) {
+    auto ret = tree::make<NonGateInstruction>();
+    ret->name = tree::make<Keyword>(context->MEASURE()->getText());
+    ret->operands = tree::make<ExpressionList>();
+    ret->operands->items.add(std::any_cast<One<Expression>>(context->expression(0)->accept(this)));
+    ret->operands->items.add(std::any_cast<One<Expression>>(context->expression(1)->accept(this)));
+    setNodeAnnotation(ret, context->MEASURE()->getSymbol());
+    return One<Statement>{ ret };
+}
+
+std::any SyntacticAnalyzer::visitResetInstruction(CqasmParser::ResetInstructionContext* context) {
+    auto ret = tree::make<NonGateInstruction>();
+    ret->name = tree::make<Keyword>(context->RESET()->getText());
+    ret->operands = tree::make<ExpressionList>();
+    if (context->expression()) {
+        ret->operands->items.add(std::any_cast<One<Expression>>(context->expression()->accept(this)));
+    }
+    setNodeAnnotation(ret, context->RESET()->getSymbol());
     return One<Statement>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitType(CqasmParser::TypeContext *context) {
+std::any SyntacticAnalyzer::visitType(CqasmParser::TypeContext* context) {
     return context->quantumType()->accept(this);
 }
 
-std::any BuildTreeGenAstVisitor::visitQubitType(CqasmParser::QubitTypeContext *context) {
+std::any SyntacticAnalyzer::visitQubitType(CqasmParser::QubitTypeContext* context) {
     auto ret =
         tree::make<Type>(tree::make<Keyword>(types::qubit_type_name), getArraySize(context->arraySizeDeclaration()));
     setNodeAnnotation(ret, context->QUBIT_TYPE()->getSymbol());
@@ -200,7 +247,7 @@ std::any BuildTreeGenAstVisitor::visitQubitType(CqasmParser::QubitTypeContext *c
     return ret;
 }
 
-std::any BuildTreeGenAstVisitor::visitBitType(CqasmParser::BitTypeContext *context) {
+std::any SyntacticAnalyzer::visitBitType(CqasmParser::BitTypeContext* context) {
     auto ret =
         tree::make<Type>(tree::make<Keyword>(types::bit_type_name), getArraySize(context->arraySizeDeclaration()));
     setNodeAnnotation(ret, context->BIT_TYPE()->getSymbol());
@@ -210,49 +257,49 @@ std::any BuildTreeGenAstVisitor::visitBitType(CqasmParser::BitTypeContext *conte
     return ret;
 }
 
-Maybe<IntegerLiteral> BuildTreeGenAstVisitor::getArraySize(CqasmParser::ArraySizeDeclarationContext *context) {
+Maybe<IntegerLiteral> SyntacticAnalyzer::getArraySize(CqasmParser::ArraySizeDeclarationContext* context) {
     return (context) ? Maybe<IntegerLiteral>{ std::any_cast<One<IntegerLiteral>>(context->accept(this)).get_ptr() }
                      : Maybe<IntegerLiteral>{};
 }
 
-std::any BuildTreeGenAstVisitor::visitArraySizeDeclaration(CqasmParser::ArraySizeDeclarationContext *context) {
+std::any SyntacticAnalyzer::visitArraySizeDeclaration(CqasmParser::ArraySizeDeclarationContext* context) {
     return tree::make<IntegerLiteral>(get_int_value(context->INTEGER_LITERAL()));
 }
 
-std::any BuildTreeGenAstVisitor::visitExpressionList(CqasmParser::ExpressionListContext *context) {
+std::any SyntacticAnalyzer::visitExpressionList(CqasmParser::ExpressionListContext* context) {
     auto ret = tree::make<ExpressionList>();
-    const auto &expressions = context->expression();
-    std::for_each(expressions.begin(), expressions.end(), [this, &ret](auto &expression_ctx) {
+    const auto& expressions = context->expression();
+    std::for_each(expressions.begin(), expressions.end(), [this, &ret](auto& expression_ctx) {
         ret->items.add(std::any_cast<One<Expression>>(expression_ctx->accept(this)));
     });
     return ret;
 }
 
-std::any BuildTreeGenAstVisitor::visitIndexList(CqasmParser::IndexListContext *context) {
+std::any SyntacticAnalyzer::visitIndexList(CqasmParser::IndexListContext* context) {
     auto ret = tree::make<IndexList>();
-    const auto &index_entries = context->indexEntry();
-    std::for_each(index_entries.begin(), index_entries.end(), [this, &ret](auto &index_entry_ctx) {
+    const auto& index_entries = context->indexEntry();
+    std::for_each(index_entries.begin(), index_entries.end(), [this, &ret](auto& index_entry_ctx) {
         auto index_entry = std::any_cast<One<IndexEntry>>(index_entry_ctx->accept(this));
         ret->items.add(index_entry);
     });
     return ret;
 }
 
-std::any BuildTreeGenAstVisitor::visitIndexItem(CqasmParser::IndexItemContext *context) {
+std::any SyntacticAnalyzer::visitIndexItem(CqasmParser::IndexItemContext* context) {
     return One<IndexEntry>{ tree::make<IndexItem>(
         std::any_cast<One<Expression>>(context->expression()->accept(this))) };
 }
 
-std::any BuildTreeGenAstVisitor::visitIndexRange(CqasmParser::IndexRangeContext *context) {
+std::any SyntacticAnalyzer::visitIndexRange(CqasmParser::IndexRangeContext* context) {
     return One<IndexEntry>{ tree::make<IndexRange>(std::any_cast<One<Expression>>(context->expression(0)->accept(this)),
         std::any_cast<One<Expression>>(context->expression(1)->accept(this))) };
 }
 
-std::any BuildTreeGenAstVisitor::visitParensExpression(CqasmParser::ParensExpressionContext *context) {
+std::any SyntacticAnalyzer::visitParensExpression(CqasmParser::ParensExpressionContext* context) {
     return context->expression()->accept(this);
 }
 
-std::any BuildTreeGenAstVisitor::visitUnaryPlusMinusExpression(CqasmParser::UnaryPlusMinusExpressionContext *context) {
+std::any SyntacticAnalyzer::visitUnaryPlusMinusExpression(CqasmParser::UnaryPlusMinusExpressionContext* context) {
     if (context->PLUS()) {
         return std::any_cast<One<Expression>>(context->expression()->accept(this));
     }
@@ -261,23 +308,23 @@ std::any BuildTreeGenAstVisitor::visitUnaryPlusMinusExpression(CqasmParser::Unar
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitBitwiseNotExpression(CqasmParser::BitwiseNotExpressionContext *context) {
+std::any SyntacticAnalyzer::visitBitwiseNotExpression(CqasmParser::BitwiseNotExpressionContext* context) {
     auto ret = tree::make<BitwiseNotExpression>(std::any_cast<One<Expression>>(context->expression()->accept(this)));
     setNodeAnnotation(ret, context->BITWISE_NOT_OP()->getSymbol());
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitLogicalNotExpression(CqasmParser::LogicalNotExpressionContext *context) {
+std::any SyntacticAnalyzer::visitLogicalNotExpression(CqasmParser::LogicalNotExpressionContext* context) {
     auto ret = tree::make<LogicalNotExpression>(std::any_cast<One<Expression>>(context->expression()->accept(this)));
     setNodeAnnotation(ret, context->LOGICAL_NOT_OP()->getSymbol());
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitPowerExpression(CqasmParser::PowerExpressionContext *context) {
+std::any SyntacticAnalyzer::visitPowerExpression(CqasmParser::PowerExpressionContext* context) {
     return visitBinaryExpression<PowerExpression>(context, context->POWER_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitProductExpression(CqasmParser::ProductExpressionContext *context) {
+std::any SyntacticAnalyzer::visitProductExpression(CqasmParser::ProductExpressionContext* context) {
     if (context->PRODUCT_OP()) {
         return visitBinaryExpression<ProductExpression>(context, context->PRODUCT_OP()->getSymbol());
     } else if (context->DIVISION_OP()) {
@@ -286,21 +333,21 @@ std::any BuildTreeGenAstVisitor::visitProductExpression(CqasmParser::ProductExpr
     return visitBinaryExpression<ModuloExpression>(context, context->MODULO_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitAdditionExpression(CqasmParser::AdditionExpressionContext *context) {
+std::any SyntacticAnalyzer::visitAdditionExpression(CqasmParser::AdditionExpressionContext* context) {
     if (context->PLUS()) {
         return visitBinaryExpression<AdditionExpression>(context, context->PLUS()->getSymbol());
     }
     return visitBinaryExpression<SubtractionExpression>(context, context->MINUS()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitShiftExpression(CqasmParser::ShiftExpressionContext *context) {
+std::any SyntacticAnalyzer::visitShiftExpression(CqasmParser::ShiftExpressionContext* context) {
     if (context->SHL_OP()) {
         return visitBinaryExpression<ShiftLeftExpression>(context, context->SHL_OP()->getSymbol());
     }
     return visitBinaryExpression<ShiftRightExpression>(context, context->SHR_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitComparisonExpression(CqasmParser::ComparisonExpressionContext *context) {
+std::any SyntacticAnalyzer::visitComparisonExpression(CqasmParser::ComparisonExpressionContext* context) {
     if (context->CMP_GT_OP()) {
         return visitBinaryExpression<CmpGtExpression>(context, context->CMP_GT_OP()->getSymbol());
     } else if (context->CMP_LT_OP()) {
@@ -311,39 +358,39 @@ std::any BuildTreeGenAstVisitor::visitComparisonExpression(CqasmParser::Comparis
     return visitBinaryExpression<CmpLeExpression>(context, context->CMP_LE_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitEqualityExpression(CqasmParser::EqualityExpressionContext *context) {
+std::any SyntacticAnalyzer::visitEqualityExpression(CqasmParser::EqualityExpressionContext* context) {
     if (context->CMP_EQ_OP()) {
         return visitBinaryExpression<CmpEqExpression>(context, context->CMP_EQ_OP()->getSymbol());
     }
     return visitBinaryExpression<CmpNeExpression>(context, context->CMP_NE_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitBitwiseAndExpression(CqasmParser::BitwiseAndExpressionContext *context) {
+std::any SyntacticAnalyzer::visitBitwiseAndExpression(CqasmParser::BitwiseAndExpressionContext* context) {
     return visitBinaryExpression<BitwiseAndExpression>(context, context->BITWISE_AND_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitBitwiseXorExpression(CqasmParser::BitwiseXorExpressionContext *context) {
+std::any SyntacticAnalyzer::visitBitwiseXorExpression(CqasmParser::BitwiseXorExpressionContext* context) {
     return visitBinaryExpression<BitwiseXorExpression>(context, context->BITWISE_XOR_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitBitwiseOrExpression(CqasmParser::BitwiseOrExpressionContext *context) {
+std::any SyntacticAnalyzer::visitBitwiseOrExpression(CqasmParser::BitwiseOrExpressionContext* context) {
     return visitBinaryExpression<BitwiseOrExpression>(context, context->BITWISE_OR_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitLogicalAndExpression(CqasmParser::LogicalAndExpressionContext *context) {
+std::any SyntacticAnalyzer::visitLogicalAndExpression(CqasmParser::LogicalAndExpressionContext* context) {
     return visitBinaryExpression<LogicalAndExpression>(context, context->LOGICAL_AND_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitLogicalXorExpression(CqasmParser::LogicalXorExpressionContext *context) {
+std::any SyntacticAnalyzer::visitLogicalXorExpression(CqasmParser::LogicalXorExpressionContext* context) {
     return visitBinaryExpression<LogicalXorExpression>(context, context->LOGICAL_XOR_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitLogicalOrExpression(CqasmParser::LogicalOrExpressionContext *context) {
+std::any SyntacticAnalyzer::visitLogicalOrExpression(CqasmParser::LogicalOrExpressionContext* context) {
     return visitBinaryExpression<LogicalOrExpression>(context, context->LOGICAL_OR_OP()->getSymbol());
 }
 
-std::any BuildTreeGenAstVisitor::visitTernaryConditionalExpression(
-    CqasmParser::TernaryConditionalExpressionContext *context) {
+std::any SyntacticAnalyzer::visitTernaryConditionalExpression(
+    CqasmParser::TernaryConditionalExpressionContext* context) {
     auto ret =
         tree::make<TernaryConditionalExpression>(std::any_cast<One<Expression>>(context->expression(0)->accept(this)),
             std::any_cast<One<Expression>>(context->expression(1)->accept(this)),
@@ -352,7 +399,7 @@ std::any BuildTreeGenAstVisitor::visitTernaryConditionalExpression(
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitFunctionCall(CqasmParser::FunctionCallContext *context) {
+std::any SyntacticAnalyzer::visitFunctionCall(CqasmParser::FunctionCallContext* context) {
     auto ret = tree::make<FunctionCall>();
     ret->name = tree::make<Identifier>(context->IDENTIFIER()->getText());
     ret->arguments = context->expressionList()
@@ -362,7 +409,7 @@ std::any BuildTreeGenAstVisitor::visitFunctionCall(CqasmParser::FunctionCallCont
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitIndex(CqasmParser::IndexContext *context) {
+std::any SyntacticAnalyzer::visitIndex(CqasmParser::IndexContext* context) {
     auto ret = tree::make<Index>();
     ret->expr = tree::make<Identifier>(context->IDENTIFIER()->getText());
     ret->indices = std::any_cast<One<IndexList>>(visitIndexList(context->indexList()));
@@ -370,27 +417,27 @@ std::any BuildTreeGenAstVisitor::visitIndex(CqasmParser::IndexContext *context)
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitIdentifier(CqasmParser::IdentifierContext *context) {
+std::any SyntacticAnalyzer::visitIdentifier(CqasmParser::IdentifierContext* context) {
     auto ret = tree::make<Identifier>(context->IDENTIFIER()->getText());
     setNodeAnnotation(ret, context->IDENTIFIER()->getSymbol());
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitBooleanLiteral(CqasmParser::BooleanLiteralContext *context) {
+std::any SyntacticAnalyzer::visitBooleanLiteral(CqasmParser::BooleanLiteralContext* context) {
     auto value = get_bool_value(context->BOOLEAN_LITERAL());
     auto ret = tree::make<BooleanLiteral>(value);
     setNodeAnnotation(ret, context->BOOLEAN_LITERAL()->getSymbol());
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitIntegerLiteral(CqasmParser::IntegerLiteralContext *context) {
+std::any SyntacticAnalyzer::visitIntegerLiteral(CqasmParser::IntegerLiteralContext* context) {
     auto value = get_int_value(context->INTEGER_LITERAL());
     auto ret = tree::make<IntegerLiteral>(value);
     setNodeAnnotation(ret, context->INTEGER_LITERAL()->getSymbol());
     return One<Expression>{ ret };
 }
 
-std::any BuildTreeGenAstVisitor::visitFloatLiteral(CqasmParser::FloatLiteralContext *context) {
+std::any SyntacticAnalyzer::visitFloatLiteral(CqasmParser::FloatLiteralContext* context) {
     auto value = get_float_value(context->FLOAT_LITERAL());
     auto ret = tree::make<FloatLiteral>(value);
     setNodeAnnotation(ret, context->FLOAT_LITERAL()->getSymbol());
diff --git a/src/v3x/cqasm-types.cpp b/src/v3x/types.cpp
similarity index 82%
rename from src/v3x/cqasm-types.cpp
rename to src/v3x/types.cpp
index 84c419119..144345d51 100644
--- a/src/v3x/cqasm-types.cpp
+++ b/src/v3x/types.cpp
@@ -1,13 +1,12 @@
 /** \file
- * Implementation for \ref include/v3x/cqasm-types.hpp "v3x/cqasm-types.hpp".
+ * Implementation for \ref include/libqasm/v3x/types.hpp "libqasm/v3x/types.hpp".
  */
 
-#include "v3x/cqasm-types.hpp"
+#include "libqasm/v3x/types.hpp"
 
 #include <fmt/ostream.h>
 #include <fmt/ranges.h>
 
-
 namespace cqasm::v3x::types {
 
 /**
@@ -35,7 +34,7 @@ Type from_spec(const char c) {
     }
 }
 
-Types from_spec(const std::string &spec) {
+Types from_spec(const std::string& spec) {
     Types types;
     for (auto c : spec) {
         types.add(from_spec(c));
@@ -46,21 +45,21 @@ Types from_spec(const std::string &spec) {
 /**
  * Returns whether the `actual` type matches the constraints of the `expected` type.
  */
-bool type_check(const Type &expected, const Type &actual) {
+bool type_check(const Type& expected, const Type& actual) {
     return actual->type() == expected->type();
 }
 
 /**
  * Returns the number of elements of the given type.
  */
-primitives::Int size_of(const Type &type) {
+primitives::Int size_of(const Type& type) {
     return type->as_type_base()->size;
 }
 
 /**
  * Stream << overload for a single type.
  */
-std::ostream &operator<<(std::ostream &os, const Type &type) {
+std::ostream& operator<<(std::ostream& os, const Type& type) {
     if (type.empty()) {
         os << "!EMPTY";
     } else if (type->as_qubit()) {
@@ -87,8 +86,8 @@ std::ostream &operator<<(std::ostream &os, const Type &type) {
 /**
  * Stream << overload for zero or more types.
  */
-std::ostream &operator<<(std::ostream &os, const Types &types) {
-    return os << fmt::format("({})", fmt::join(types, ", "));
+std::ostream& operator<<(std::ostream& os, const Types& types) {
+    return os << fmt::format("{}", fmt::join(types, ", "));
 }
 
 }  // namespace cqasm::v3x::types
diff --git a/src/v3x/cqasm-types.tree b/src/v3x/types.tree
similarity index 90%
rename from src/v3x/cqasm-types.tree
rename to src/v3x/types.tree
index 8d1a4f598..111dca37e 100644
--- a/src/v3x/cqasm-types.tree
+++ b/src/v3x/types.tree
@@ -2,14 +2,14 @@
 source
 
 # Header file for the various classes representing the types of values available in cQASM 3.0
-header "v3x/cqasm-types-gen.hpp"
+header "libqasm/v3x/types_generated.hpp"
 
 // Include tree base classes.
-include "cqasm-tree.hpp"
+include "libqasm/tree.hpp"
 tree_namespace cqasm::tree
 
 // Include primitive types.
-include "v3x/cqasm-primitives.hpp"
+include "libqasm/v3x/primitives.hpp"
 import cqasm.v3x.primitives
 
 // Initialization function to use to construct default values for the tree base classes and primitives
diff --git a/src/v3x/cqasm-values.cpp b/src/v3x/values.cpp
similarity index 81%
rename from src/v3x/cqasm-values.cpp
rename to src/v3x/values.cpp
index 7d96d5597..fe4aa460c 100644
--- a/src/v3x/cqasm-values.cpp
+++ b/src/v3x/values.cpp
@@ -1,18 +1,19 @@
 /** \file
- * Implementation for \ref include/v3x/cqasm-values.hpp "v3x/cqasm-values.hpp".
+ * Implementation for \ref include/libqasm/v3x/values.hpp "libqasm/v3x/values.hpp".
  */
 
-#include "v3x/cqasm-values.hpp"
+#include "libqasm/v3x/values.hpp"
 
 #include <fmt/format.h>
 #include <fmt/ranges.h>
+
 #include <ostream>
 #include <stdexcept>  // runtime_error
 
-#include "cqasm-error.hpp"
-#include "v3x/cqasm-parse-helper.hpp"
-#include "v3x/cqasm-semantic.hpp"
-#include "v3x/cqasm-types.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
+#include "libqasm/v3x/semantic.hpp"
+#include "libqasm/v3x/types.hpp"
 
 namespace cqasm::v3x::values {
 
@@ -25,8 +26,8 @@ namespace cqasm::v3x::values {
  *
  * For a variable or a return value, a promotion is just a check.
  * If the check is successful, the variable or return value is returned
-*/
-Value promote(const Value &value, const types::Type &type) {
+ */
+Value promote(const Value& value, const types::Type& type) {
     // If the types match exactly, just return the original value
     if (types::type_check(type, type_of(value))) {
         return value;
@@ -36,7 +37,7 @@ Value promote(const Value &value, const types::Type &type) {
 
     // Booleans promote to integer
     if (type->as_int()) {
-        if (const auto &const_bool = value->as_const_bool()) {
+        if (const auto& const_bool = value->as_const_bool()) {
             ret = tree::make<values::ConstInt>(static_cast<ConstInt>(const_bool->value));
         } else if (value->as_variable_ref()) {
             if (types::type_check(type_of(value), tree::make<types::Bool>())) {
@@ -47,9 +48,9 @@ Value promote(const Value &value, const types::Type &type) {
 
     // Booleans and integers promote to float
     if (type->as_float()) {
-        if (const auto &const_bool = value->as_const_bool()) {
+        if (const auto& const_bool = value->as_const_bool()) {
             ret = tree::make<values::ConstFloat>(static_cast<ConstFloat>(const_bool->value));
-        } else if (const auto &const_int = value->as_const_int()) {
+        } else if (const auto& const_int = value->as_const_int()) {
             ret = tree::make<values::ConstFloat>(static_cast<ConstFloat>(static_cast<double>(const_int->value)));
         }
     }
@@ -65,7 +66,7 @@ Value promote(const Value &value, const types::Type &type) {
 /**
  * Checks if a from_type can be promoted to a to_type.
  */
-bool check_promote(const types::Type &from_type, const types::Type &to_type) {
+bool check_promote(const types::Type& from_type, const types::Type& to_type) {
     if (types::type_check(from_type, to_type)) {
         return true;
     } else if (from_type->as_bool()) {
@@ -80,7 +81,7 @@ bool check_promote(const types::Type &from_type, const types::Type &to_type) {
  * Returns the element type of the given type.
  * Throws an error if the given type is not of array type.
  */
-types::Type element_type_of(const types::Type &type) {
+types::Type element_type_of(const types::Type& type) {
     if (types::type_check(type, tree::make<types::QubitArray>())) {
         return tree::make<types::Qubit>();
     } else if (types::type_check(type, tree::make<types::BitArray>())) {
@@ -93,7 +94,7 @@ types::Type element_type_of(const types::Type &type) {
 /**
  * Returns the type of the given value.
  */
-types::Type type_of(const Value &value) {
+types::Type type_of(const Value& value) {
     if (value->as_const_bool()) {
         return tree::make<types::Bool>();
     } else if (value->as_const_int()) {
@@ -118,9 +119,9 @@ types::Type type_of(const Value &value) {
 /**
  * Returns the types of the given values.
  */
-types::Types types_of(const Values &values) {
+types::Types types_of(const Values& values) {
     types::Types types;
-    for (const auto &value : values) {
+    for (const auto& value : values) {
         types.add(type_of(value));
     }
     return types;
@@ -129,7 +130,7 @@ types::Types types_of(const Values &values) {
 /**
  * Returns the number of elements of the given value.
  */
-primitives::Int size_of(const Value &value) {
+primitives::Int size_of(const Value& value) {
     if (value->as_const_bool() || value->as_const_int() || value->as_const_float()) {
         return 1;
     } else if (auto index = value->as_index_ref()) {
@@ -145,7 +146,7 @@ primitives::Int size_of(const Value &value) {
  * Throws an AnalysisError if the given value is not a constant,
  * i.e. if it doesn't have a known value at this time.
  */
-void check_const(const Value &value) {
+void check_const(const Value& value) {
     if (!value->as_constant()) {
         throw error::AnalysisError("dynamic values are not supported here", &*value);
     }
@@ -155,8 +156,8 @@ void check_const(const Value &value) {
  * Throws an AnalysisError if any of the given values are not a constant,
  * i.e. if it doesn't have a known value at this time.
  */
-void check_const(const Values &values) {
-    for (const auto &value : values) {
+void check_const(const Values& values) {
+    for (const auto& value : values) {
         check_const(value);
     }
 }
@@ -164,14 +165,14 @@ void check_const(const Values &values) {
 /**
  * Stream << overload for a single value.
  */
-std::ostream &operator<<(std::ostream &os, const Value &value) {
+std::ostream& operator<<(std::ostream& os, const Value& value) {
     return (value.empty()) ? os << "NULL" : os << *value;
 }
 
 /**
  * Stream << overload for zero or more values.
  */
-std::ostream &operator<<(std::ostream &os, const Values &values) {
+std::ostream& operator<<(std::ostream& os, const Values& values) {
     return os << fmt::format("[{}]", fmt::join(values, ", "));
 }
 
diff --git a/src/v3x/cqasm-values.tree b/src/v3x/values.tree
similarity index 86%
rename from src/v3x/cqasm-values.tree
rename to src/v3x/values.tree
index 94ddaf1ee..80e1bb6f6 100644
--- a/src/v3x/cqasm-values.tree
+++ b/src/v3x/values.tree
@@ -2,15 +2,15 @@
 source
 
 # Header file for the various classes representing constants, references, and dynamic expressions in cQASM 3.0
-header "v3x/cqasm-values-gen.hpp"
+header "libqasm/v3x/values_generated.hpp"
 
-include "cqasm-tree.hpp"
+include "libqasm/tree.hpp"
 tree_namespace cqasm::tree
 
-include "v3x/cqasm-core-function.hpp"
-include "v3x/cqasm-primitives.hpp"
-include "v3x/cqasm-semantic-helper.hpp"
-src_include "v3x/cqasm-semantic.hpp"
+include "libqasm/v3x/instruction.hpp"
+include "libqasm/v3x/primitives.hpp"
+include "libqasm/v3x/semantic_helper.hpp"
+src_include "libqasm/v3x/semantic.hpp"
 import cqasm.v3x.primitives
 import cqasm.v3x.semantic
 
diff --git a/src/cqasm-version.cpp b/src/version.cpp
similarity index 82%
rename from src/cqasm-version.cpp
rename to src/version.cpp
index fb34a5f9f..72c09eb79 100644
--- a/src/cqasm-version.cpp
+++ b/src/version.cpp
@@ -1,11 +1,12 @@
 /** \file
- * Implementation for \ref include/cqasm-version.hpp "cqasm-version.hpp".
+ * Implementation for \ref include/libqasm/libqasm/version.hpp "libqasm/version.hpp".
  */
 
-#include "cqasm-version.hpp"
+#include "libqasm/version.hpp"
 
 #include <fmt/format.h>
 #include <fmt/ranges.h>
+
 #include <memory>
 
 namespace cqasm::version {
@@ -13,7 +14,7 @@ namespace cqasm::version {
 /**
  * Constructs a version object from a string.
  */
-Version::Version(const std::string &version) {
+Version::Version(const std::string& version) {
     if (version.empty()) {
         return;
     }
@@ -34,7 +35,7 @@ Version::Version(const std::string &version) {
 /**
  * Constructs a version object from an array of chars.
  */
-Version::Version(const char *version)
+Version::Version(const char* version)
 : Version(std::string{ version }) {}
 
 /**
@@ -45,7 +46,7 @@ Version::Version(const char *version)
  *   0 if both versions are the same.
  * When there is a mismatch in the number of components between the versions, missing components are interpreted as 0.
  */
-int Version::compare(const Version &other) const {
+int Version::compare(const Version& other) const {
     for (size_t i = 0; i < this->size() || i < other.size(); i++) {
         auto lhs = (i < this->size()) ? (*this)[i] : 0;
         auto rhs = (i < other.size()) ? other[i] : 0;
@@ -62,7 +63,7 @@ int Version::compare(const Version &other) const {
 /**
  * Stream << overload for version nodes.
  */
-std::ostream &operator<<(std::ostream &os, const Version &object) {
+std::ostream& operator<<(std::ostream& os, const Version& object) {
     return os << fmt::format("{}", fmt::join(object, "."));
 }
 
diff --git a/version.py b/version.py
index c76c74eda..e6172c143 100644
--- a/version.py
+++ b/version.py
@@ -6,10 +6,10 @@ def get_version(verbose=False):
     """Extract version information from source code"""
 
     root_dir = os.getcwd()  # root of the repository
-    inc_dir = root_dir + os.sep + "include"  # C++ include directory
-    matcher = re.compile('static const char \*version\{ "(.*)" \}')
+    inc_dir = os.path.join(root_dir, "include", "libqasm")  # C++ include directory
+    matcher = re.compile('static const char\* version\{ "(.*)" \}')
     version = None
-    with open(os.path.join(inc_dir, "version.hpp"), "r") as f:
+    with open(os.path.join(inc_dir, "versioning.hpp"), "r") as f:
         for ln in f:
             m = matcher.match(ln)
             if m:


Test patch:
diff --git a/.github/actions/python-tests/action.yaml b/.github/actions/python-tests/action.yaml
index 9e517785a..899017eb6 100644
--- a/.github/actions/python-tests/action.yaml
+++ b/.github/actions/python-tests/action.yaml
@@ -18,10 +18,12 @@ runs:
         python -m pip install --upgrade pip conan pytest setuptools wheel
       shell: ${{ inputs.shell }}
     - name: Build
-      run: python -m pip install --verbose .
+      run: |
+        python -m pip install --verbose .
       shell: ${{ inputs.shell }}
     - name: Run unit tests
-      run: python -m pytest
+      run: |
+        python -m pytest
       shell: ${{ inputs.shell }}
     - name: Test libqasm
       working-directory: python
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yaml
similarity index 91%
rename from .github/workflows/test.yml
rename to .github/workflows/test.yaml
index 75bacd218..6fdff61ec 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yaml
@@ -3,8 +3,8 @@ name: Test
 on:
   push:
     branches:
-    - develop
-    - master
+      - develop
+      - master
   pull_request:
 
 jobs:
@@ -33,6 +33,9 @@ jobs:
     name: "C++ tests (gcc/Linux/ARM64)"
     runs-on: [self-hosted, ARM64, Linux]
     container: python:3.11
+    # Run only when merging to develop
+    # (until we have a GitHub-hosted runner for Linux/ARM64)
+    if: github.ref == 'refs/heads/develop'
     strategy:
       fail-fast: false
       matrix:
@@ -89,14 +92,6 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v4
-        # We are having problems when using zulu-opendjk Conan package on an armv8 architecture.
-        # zulu-openjdk provides the Java JRE required by the ANTLR generator.
-        # So, for the time being, we are installing Java manually for this platform
-      - name: Install dependencies
-        run: |
-          brew install java
-          echo "$(brew --prefix java)/bin" >> $GITHUB_PATH
-        shell: bash
       - uses: ./.github/actions/cpp-tests
         with:
           build_type: ${{ matrix.build_type }}
@@ -129,14 +124,15 @@ jobs:
       fail-fast: false
       matrix:
         build_type:
+          - Debug
           - Release
     steps:
       - name: Checkout
         uses: actions/checkout@v4
       - uses: ./.github/actions/cpp-tests
         with:
-          build_type: Release
-          conan_profile_host: conan/profiles/release-gcc-linux-x64-shared
+          build_type: ${{ matrix.build_type }}
+          conan_profile_host: conan/profiles/${{ matrix.build_type }}-gcc-linux-x64-shared
           shell: bash
 
   ts-emscripten-wasm:
@@ -228,8 +224,7 @@ jobs:
         uses: actions/checkout@v4
       - name: Install dependencies
         run: |
-          brew install java swig
-          echo "$(brew --prefix java)/bin" >> $GITHUB_PATH
+          brew install swig
         shell: bash
       - name: Export LIBQASM_BUILD_TYPE
         run: |
@@ -292,6 +287,15 @@ jobs:
           ${{
                contains(needs.*.result, 'failure')
             || contains(needs.*.result, 'cancelled')
-            || contains(needs.*.result, 'skipped')
+            || (
+                    github.ref != 'refs/heads/develop'
+                 && contains(needs.*.result, 'skipped')
+                 && !contains(needs.cpp-linux-arm64.result, 'skipped')
+               )
+            ||
+               (
+                    github.ref == 'refs/heads/develop'
+                 && contains(needs.*.result, 'skipped')
+               )
           }}
         run: exit 1
diff --git a/emscripten/test_libqasm.ts b/emscripten/test_libqasm.ts
index 9172131a6..2a19b40d0 100644
--- a/emscripten/test_libqasm.ts
+++ b/emscripten/test_libqasm.ts
@@ -8,7 +8,7 @@ wrapper().then(function(result: any) {
 
     try {
         let output = cqasm.get_version()
-        let expected_output = "0.6.7"
+        let expected_output = "0.6.8"
         console.log("\nThe version of libqasm compiled with emscripten is:", output);
         if (output !== expected_output) {
             console.log("\tExpected output:", expected_output)
@@ -32,7 +32,7 @@ wrapper().then(function(result: any) {
     try {
         let program_2 = "version 3;qubit[5] q;bit[5] b;H q[0:4];b = measure q"
         let output = cqasm.parse_string_to_json(program_2, "<unknown>")
-        let expected_output = String.raw`{"Program":{"version":{"Version":{"items":"3","source_location":"<unknown>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:11..19"}},"annotations":"[]","source_location":"<unknown>:1:20..21"}},{"Variable":{"name":{"Identifier":{"name":"b"}},"typ":{"Type":{"name":{"Keyword":{"name":"bit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:22..28"}},"annotations":"[]","source_location":"<unknown>:1:29..30"}},{"Gate":{"name":{"Identifier":{"name":"H"}},"operands":{"ExpressionList":{"items":[{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexRange":{"first":{"IntegerLiteral":{"value":"0","source_location":"<unknown>:1:35..36"}},"last":{"IntegerLiteral":{"value":"4","source_location":"<unknown>:1:37..38"}}}}]}},"source_location":"<unknown>:1:33..34"}}]}},"annotations":"[]","source_location":"<unknown>:1:31..32"}},{"MeasureInstruction":{"name":{"Identifier":{"name":"measure"}},"lhs":{"Identifier":{"name":"b","source_location":"<unknown>:1:40..41"}},"rhs":{"Identifier":{"name":"q","source_location":"<unknown>:1:52..53"}},"annotations":"[]","source_location":"<unknown>:1:44..51"}}]}}}}`
+        let expected_output = String.raw`{"Program":{"version":{"Version":{"items":"3","source_location":"<unknown>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:11..19"}},"annotations":[],"source_location":"<unknown>:1:20..21"}},{"Variable":{"name":{"Identifier":{"name":"b"}},"typ":{"Type":{"name":{"Keyword":{"name":"bit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:22..28"}},"annotations":[],"source_location":"<unknown>:1:29..30"}},{"GateInstruction":{"gate":{"Gate":{"name":{"Identifier":{"name":"H"}},"gate":"-","parameter":"-","annotations":[],"source_location":"<unknown>:1:31..32"}},"operands":{"ExpressionList":{"items":[{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexRange":{"first":{"IntegerLiteral":{"value":"0","source_location":"<unknown>:1:35..36"}},"last":{"IntegerLiteral":{"value":"4","source_location":"<unknown>:1:37..38"}}}}]}},"source_location":"<unknown>:1:33..34"}}]}},"annotations":[],"source_location":"<unknown>:1:31..32"}},{"NonGateInstruction":{"name":{"Keyword":{"name":"measure"}},"operands":{"ExpressionList":{"items":[{"Identifier":{"name":"b","source_location":"<unknown>:1:40..41"}},{"Identifier":{"name":"q","source_location":"<unknown>:1:52..53"}}]}},"annotations":[],"source_location":"<unknown>:1:44..51"}}]}}}}`
         console.log("\nExample 2:", program_2, "\n\tCalling parse_string_to_json...", "\n\tOutput:", output)
         if (output !== expected_output) {
             console.log("\tExpected output:", expected_output)
@@ -56,7 +56,7 @@ wrapper().then(function(result: any) {
     try {
         let program_4 = "version 3;qubit[5] q;bit[5] b;H q[0:4];b = measure q"
         let output = cqasm.analyze_string_to_json(program_4, "spin_q.cq")
-        let expected_output = String.raw`{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"Instruction":{"instruction_ref":"H(qubit array)","name":"H","operands":[{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":"[]"}},"indices":[{"ConstInt":{"value":"0"}},{"ConstInt":{"value":"1"}},{"ConstInt":{"value":"2"}},{"ConstInt":{"value":"3"}},{"ConstInt":{"value":"4"}}]}}],"annotations":"[]"}},{"Instruction":{"instruction_ref":"measure(bit array, qubit array)","name":"measure","operands":[{"VariableRef":{"variable":{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":"[]"}}}},{"VariableRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":"[]"}}}}],"annotations":"[]"}}]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":"[]"}},{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":"[]"}}]}}`
+        let expected_output = String.raw`{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"GateInstruction":{"instruction_ref":"H(qubit array)","gate":{"Gate":{"name":"H","gate":"-","parameter":"-","annotations":[]}},"operands":[{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":[]}},"indices":[{"ConstInt":{"value":"0"}},{"ConstInt":{"value":"1"}},{"ConstInt":{"value":"2"}},{"ConstInt":{"value":"3"}},{"ConstInt":{"value":"4"}}]}}],"annotations":[]}},{"NonGateInstruction":{"instruction_ref":"measure(bit array, qubit array)","name":"measure","operands":[{"VariableRef":{"variable":{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":[]}}}},{"VariableRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":[]}}}}],"annotations":[]}}]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":[]}},{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":[]}}]}}`
         console.log("\nExample 4:", program_4, "\n\tCalling analyze_string_to_json...", "\n\tOutput:", output)
         if (output !== expected_output) {
             console.log("\tExpected output:", expected_output)
diff --git a/python/test_libqasm.py b/python/test_libqasm.py
index a157c9cce..1c26d8149 100644
--- a/python/test_libqasm.py
+++ b/python/test_libqasm.py
@@ -22,7 +22,7 @@ def __init__(self, file_name: str, program: str, analyzer_api_name: str, expecte
         "<unknown>",
         "version 3;qubit[5] q;bit[5] b;H q[0:4];b = measure q",
         "parse_string_to_json",
-        '''{"Program":{"version":{"Version":{"items":"3","source_location":"<unknown>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:11..19"}},"annotations":"[]","source_location":"<unknown>:1:20..21"}},{"Variable":{"name":{"Identifier":{"name":"b"}},"typ":{"Type":{"name":{"Keyword":{"name":"bit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:22..28"}},"annotations":"[]","source_location":"<unknown>:1:29..30"}},{"Gate":{"name":{"Identifier":{"name":"H"}},"operands":{"ExpressionList":{"items":[{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexRange":{"first":{"IntegerLiteral":{"value":"0","source_location":"<unknown>:1:35..36"}},"last":{"IntegerLiteral":{"value":"4","source_location":"<unknown>:1:37..38"}}}}]}},"source_location":"<unknown>:1:33..34"}}]}},"annotations":"[]","source_location":"<unknown>:1:31..32"}},{"MeasureInstruction":{"name":{"Identifier":{"name":"measure"}},"lhs":{"Identifier":{"name":"b","source_location":"<unknown>:1:40..41"}},"rhs":{"Identifier":{"name":"q","source_location":"<unknown>:1:52..53"}},"annotations":"[]","source_location":"<unknown>:1:44..51"}}]}}}}'''
+        '''{"Program":{"version":{"Version":{"items":"3","source_location":"<unknown>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:11..19"}},"annotations":[],"source_location":"<unknown>:1:20..21"}},{"Variable":{"name":{"Identifier":{"name":"b"}},"typ":{"Type":{"name":{"Keyword":{"name":"bit"}},"size":{"IntegerLiteral":{"value":"5"}},"source_location":"<unknown>:1:22..28"}},"annotations":[],"source_location":"<unknown>:1:29..30"}},{"GateInstruction":{"gate":{"Gate":{"name":{"Identifier":{"name":"H"}},"gate":"-","parameter":"-","annotations":[],"source_location":"<unknown>:1:31..32"}},"operands":{"ExpressionList":{"items":[{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexRange":{"first":{"IntegerLiteral":{"value":"0","source_location":"<unknown>:1:35..36"}},"last":{"IntegerLiteral":{"value":"4","source_location":"<unknown>:1:37..38"}}}}]}},"source_location":"<unknown>:1:33..34"}}]}},"annotations":[],"source_location":"<unknown>:1:31..32"}},{"NonGateInstruction":{"name":{"Keyword":{"name":"measure"}},"operands":{"ExpressionList":{"items":[{"Identifier":{"name":"b","source_location":"<unknown>:1:40..41"}},{"Identifier":{"name":"q","source_location":"<unknown>:1:52..53"}}]}},"annotations":[],"source_location":"<unknown>:1:44..51"}}]}}}}'''
     ),
     TestDataEntry(
         "q_gym.cq",
@@ -34,7 +34,7 @@ def __init__(self, file_name: str, program: str, analyzer_api_name: str, expecte
         "spin_q.cq",
         "version 3;qubit[5] q;bit[5] b;H q[0:4];b = measure q",
         "analyze_string_to_json",
-        '''{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"Instruction":{"instruction_ref":"H(qubit array)","name":"H","operands":[{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":"[]"}},"indices":[{"ConstInt":{"value":"0"}},{"ConstInt":{"value":"1"}},{"ConstInt":{"value":"2"}},{"ConstInt":{"value":"3"}},{"ConstInt":{"value":"4"}}]}}],"annotations":"[]"}},{"Instruction":{"instruction_ref":"measure(bit array, qubit array)","name":"measure","operands":[{"VariableRef":{"variable":{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":"[]"}}}},{"VariableRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":"[]"}}}}],"annotations":"[]"}}]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":"[]"}},{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":"[]"}}]}}'''
+        '''{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"GateInstruction":{"instruction_ref":"H(qubit array)","gate":{"Gate":{"name":"H","gate":"-","parameter":"-","annotations":[]}},"operands":[{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":[]}},"indices":[{"ConstInt":{"value":"0"}},{"ConstInt":{"value":"1"}},{"ConstInt":{"value":"2"}},{"ConstInt":{"value":"3"}},{"ConstInt":{"value":"4"}}]}}],"annotations":[]}},{"NonGateInstruction":{"instruction_ref":"measure(bit array, qubit array)","name":"measure","operands":[{"VariableRef":{"variable":{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":[]}}}},{"VariableRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":[]}}}}],"annotations":[]}}]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"5"}},"annotations":[]}},{"Variable":{"name":"b","typ":{"BitArray":{"size":"5"}},"annotations":[]}}]}}'''
     )
 ]
 
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_0_b/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_0_b/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_0_b/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_0_b/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_0_b/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_0_b/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_0_b/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_0_b/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_0_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_0_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_0_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_0_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_123abc/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_123abc/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_123abc/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_123abc/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_123abc/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_123abc/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_123abc/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_123abc/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_1/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_1/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_1/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_1/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_1/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_1/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_1/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_3.14/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_3.14/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_3.14/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_3.14/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_3.14/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_3.14/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_3.14/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_3.14/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_b1/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_b1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_b1/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_b1/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_b1/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_b1/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_b1/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_b1/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_comma_b1/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_comma_b1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_comma_b1/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_comma_b1/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_comma_b1/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_comma_b1/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b0_comma_b1/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b0_comma_b1/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b__bit_array_of_17_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b_at_0/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b_at_0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b_at_0/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b_at_0/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_b_at_0/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b_at_0/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_b_at_0/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_b_at_0/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_equal/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_equal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_equal/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_equal/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_17_equal/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_17_equal/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_17_equal/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_17_equal/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_3.14/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_3.14/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_3.14/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_3.14/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_3.14/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_3.14/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_3.14/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_3.14/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_b/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_b/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_b/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_b/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_b/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_b/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_b/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_b/input.cq
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_equal/ast.golden.txt b/res/v3x/tests/integration/bit_array_definition/bit_array_of_equal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_equal/ast.golden.txt
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_equal/ast.golden.txt
diff --git a/res/v3x/parsing/bit_array_definition/bit_array_of_equal/input.cq b/res/v3x/tests/integration/bit_array_definition/bit_array_of_equal/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_array_definition/bit_array_of_equal/input.cq
rename to res/v3x/tests/integration/bit_array_definition/bit_array_of_equal/input.cq
diff --git a/res/v3x/parsing/bit_definition/bbit_b/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bbit_b/ast.golden.txt
similarity index 59%
rename from res/v3x/parsing/bit_definition/bbit_b/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bbit_b/ast.golden.txt
index ca1fd75d9..c12b1e7ba 100644
--- a/res/v3x/parsing/bit_definition/bbit_b/ast.golden.txt
+++ b/res/v3x/tests/integration/bit_definition/bbit_b/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..4
-          name: <
-            Identifier(
-              name: Bit
+        GateInstruction( # input.cq:3:1..4
+          gate: <
+            Gate( # input.cq:3:1..4
+              name: <
+                Identifier(
+                  name: Bit
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/bit_definition/bbit_b/input.cq b/res/v3x/tests/integration/bit_definition/bbit_b/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bbit_b/input.cq
rename to res/v3x/tests/integration/bit_definition/bbit_b/input.cq
diff --git a/res/v3x/parsing/bit_definition/bbit_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/bit_definition/bbit_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bbit_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/bit_definition/bbit_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit/input.cq b/res/v3x/tests/integration/bit_definition/bit/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit/input.cq
rename to res/v3x/tests/integration/bit_definition/bit/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_1/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_1/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_1/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_1/input.cq b/res/v3x/tests/integration/bit_definition/bit_1/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_1/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_1/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_123abc/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_123abc/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_123abc/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_123abc/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_123abc/input.cq b/res/v3x/tests/integration/bit_definition/bit_123abc/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_123abc/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_123abc/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_3.14/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_3.14/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_3.14/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_3.14/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_3.14/input.cq b/res/v3x/tests/integration/bit_definition/bit_3.14/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_3.14/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_3.14/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_b/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_b/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_b/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_b/input.cq b/res/v3x/tests/integration/bit_definition/bit_b/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_b/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/bit_definition/bit_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_b0_b1/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_b0_b1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b0_b1/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_b0_b1/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_b0_b1/input.cq b/res/v3x/tests/integration/bit_definition/bit_b0_b1/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b0_b1/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_b0_b1/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_b0_comma_b1/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_b0_comma_b1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b0_comma_b1/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_b0_comma_b1/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_b0_comma_b1/input.cq b/res/v3x/tests/integration/bit_definition/bit_b0_comma_b1/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b0_comma_b1/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_b0_comma_b1/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_b__bit_b/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_b__bit_b/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b__bit_b/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_b__bit_b/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_b__bit_b/input.cq b/res/v3x/tests/integration/bit_definition/bit_b__bit_b/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b__bit_b/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_b__bit_b/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_b__bit_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/bit_definition/bit_b__bit_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b__bit_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_b__bit_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_b_at_0/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_b_at_0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b_at_0/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_b_at_0/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_b_at_0/input.cq b/res/v3x/tests/integration/bit_definition/bit_b_at_0/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_b_at_0/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_b_at_0/input.cq
diff --git a/res/v3x/parsing/bit_definition/bit_equal/ast.golden.txt b/res/v3x/tests/integration/bit_definition/bit_equal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_equal/ast.golden.txt
rename to res/v3x/tests/integration/bit_definition/bit_equal/ast.golden.txt
diff --git a/res/v3x/parsing/bit_definition/bit_equal/input.cq b/res/v3x/tests/integration/bit_definition/bit_equal/input.cq
similarity index 100%
rename from res/v3x/parsing/bit_definition/bit_equal/input.cq
rename to res/v3x/tests/integration/bit_definition/bit_equal/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/eu_uppercase/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/eu_uppercase/ast.golden.txt
similarity index 74%
rename from res/v3x/parsing/case_sensitiveness/eu_uppercase/ast.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/eu_uppercase/ast.golden.txt
index 815279ab6..b44b30312 100644
--- a/res/v3x/parsing/case_sensitiveness/eu_uppercase/ast.golden.txt
+++ b/res/v3x/tests/integration/case_sensitiveness/eu_uppercase/ast.golden.txt
@@ -26,10 +26,21 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..3
-          name: <
-            Identifier(
-              name: Rx
+        GateInstruction( # input.cq:4:1..3
+          gate: <
+            Gate( # input.cq:4:1..3
+              name: <
+                Identifier(
+                  name: Rx
+                )
+              >
+              gate: -
+              parameter: <
+                Identifier( # input.cq:4:4..6
+                  name: EU
+                )
+              >
+              annotations: []
             )
           >
           operands: <
@@ -38,9 +49,6 @@ Program(
                 Identifier( # input.cq:4:8..9
                   name: q
                 )
-                Identifier( # input.cq:4:4..6
-                  name: EU
-                )
               ]
             )
           >
diff --git a/res/v3x/parsing/case_sensitiveness/eu_uppercase/input.cq b/res/v3x/tests/integration/case_sensitiveness/eu_uppercase/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/eu_uppercase/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/eu_uppercase/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/eu_uppercase/semantic.3.0.golden.txt b/res/v3x/tests/integration/case_sensitiveness/eu_uppercase/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/eu_uppercase/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/eu_uppercase/semantic.3.0.golden.txt
diff --git a/res/v3x/tests/integration/case_sensitiveness/pi_uppercase/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/pi_uppercase/ast.golden.txt
new file mode 100644
index 000000000..f98f9fd12
--- /dev/null
+++ b/res/v3x/tests/integration/case_sensitiveness/pi_uppercase/ast.golden.txt
@@ -0,0 +1,61 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:4:1..3
+          gate: <
+            Gate( # input.cq:4:1..3
+              name: <
+                Identifier(
+                  name: Rx
+                )
+              >
+              gate: -
+              parameter: <
+                Identifier( # input.cq:4:4..6
+                  name: PI
+                )
+              >
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:4:8..9
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/case_sensitiveness/pi_uppercase/input.cq b/res/v3x/tests/integration/case_sensitiveness/pi_uppercase/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/pi_uppercase/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/pi_uppercase/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/pi_uppercase/semantic.3.0.golden.txt b/res/v3x/tests/integration/case_sensitiveness/pi_uppercase/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/pi_uppercase/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/pi_uppercase/semantic.3.0.golden.txt
diff --git a/res/v3x/tests/integration/case_sensitiveness/q_uppercase/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/q_uppercase/ast.golden.txt
new file mode 100644
index 000000000..5eebd0493
--- /dev/null
+++ b/res/v3x/tests/integration/case_sensitiveness/q_uppercase/ast.golden.txt
@@ -0,0 +1,57 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:4:1..2
+          gate: <
+            Gate( # input.cq:4:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:4:3..4
+                  name: Q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/case_sensitiveness/q_uppercase/input.cq b/res/v3x/tests/integration/case_sensitiveness/q_uppercase/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/q_uppercase/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/q_uppercase/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/q_uppercase/semantic.3.0.golden.txt b/res/v3x/tests/integration/case_sensitiveness/q_uppercase/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/q_uppercase/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/q_uppercase/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qqubit_q/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/qubit_all_uppercase/ast.golden.txt
similarity index 59%
rename from res/v3x/parsing/qubit_definition/qqubit_q/ast.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/qubit_all_uppercase/ast.golden.txt
index 33c35bdb3..57dccf1ab 100644
--- a/res/v3x/parsing/qubit_definition/qqubit_q/ast.golden.txt
+++ b/res/v3x/tests/integration/case_sensitiveness/qubit_all_uppercase/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..6
-          name: <
-            Identifier(
-              name: Qubit
+        GateInstruction( # input.cq:3:1..6
+          gate: <
+            Gate( # input.cq:3:1..6
+              name: <
+                Identifier(
+                  name: QUBIT
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/case_sensitiveness/qubit_all_uppercase/input.cq b/res/v3x/tests/integration/case_sensitiveness/qubit_all_uppercase/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/qubit_all_uppercase/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/qubit_all_uppercase/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/qubit_all_uppercase/semantic.3.0.golden.txt b/res/v3x/tests/integration/case_sensitiveness/qubit_all_uppercase/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/qubit_all_uppercase/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/qubit_all_uppercase/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/case_sensitiveness/qubit_all_uppercase/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/qubit_camel_case/ast.golden.txt
similarity index 59%
rename from res/v3x/parsing/case_sensitiveness/qubit_all_uppercase/ast.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/qubit_camel_case/ast.golden.txt
index 332b2f049..b55a1c1b9 100644
--- a/res/v3x/parsing/case_sensitiveness/qubit_all_uppercase/ast.golden.txt
+++ b/res/v3x/tests/integration/case_sensitiveness/qubit_camel_case/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..6
-          name: <
-            Identifier(
-              name: QUBIT
+        GateInstruction( # input.cq:3:1..6
+          gate: <
+            Gate( # input.cq:3:1..6
+              name: <
+                Identifier(
+                  name: Qubit
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/case_sensitiveness/qubit_camel_case/input.cq b/res/v3x/tests/integration/case_sensitiveness/qubit_camel_case/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/qubit_camel_case/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/qubit_camel_case/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/qubit_camel_case/semantic.3.0.golden.txt b/res/v3x/tests/integration/case_sensitiveness/qubit_camel_case/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/qubit_camel_case/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/qubit_camel_case/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/case_sensitiveness/tau_uppercase/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/tau_uppercase/ast.golden.txt
similarity index 74%
rename from res/v3x/parsing/case_sensitiveness/tau_uppercase/ast.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/tau_uppercase/ast.golden.txt
index 7a3a2ed1d..5d1cd263e 100644
--- a/res/v3x/parsing/case_sensitiveness/tau_uppercase/ast.golden.txt
+++ b/res/v3x/tests/integration/case_sensitiveness/tau_uppercase/ast.golden.txt
@@ -26,10 +26,21 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..3
-          name: <
-            Identifier(
-              name: Rx
+        GateInstruction( # input.cq:4:1..3
+          gate: <
+            Gate( # input.cq:4:1..3
+              name: <
+                Identifier(
+                  name: Rx
+                )
+              >
+              gate: -
+              parameter: <
+                Identifier( # input.cq:4:4..7
+                  name: TAU
+                )
+              >
+              annotations: []
             )
           >
           operands: <
@@ -38,9 +49,6 @@ Program(
                 Identifier( # input.cq:4:9..10
                   name: q
                 )
-                Identifier( # input.cq:4:4..7
-                  name: TAU
-                )
               ]
             )
           >
diff --git a/res/v3x/parsing/case_sensitiveness/tau_uppercase/input.cq b/res/v3x/tests/integration/case_sensitiveness/tau_uppercase/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/tau_uppercase/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/tau_uppercase/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/tau_uppercase/semantic.3.0.golden.txt b/res/v3x/tests/integration/case_sensitiveness/tau_uppercase/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/tau_uppercase/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/tau_uppercase/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/case_sensitiveness/version_all_uppercase/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/version_all_uppercase/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/version_all_uppercase/ast.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/version_all_uppercase/ast.golden.txt
diff --git a/res/v3x/parsing/case_sensitiveness/version_all_uppercase/input.cq b/res/v3x/tests/integration/case_sensitiveness/version_all_uppercase/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/version_all_uppercase/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/version_all_uppercase/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/version_camel_case/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/version_camel_case/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/version_camel_case/ast.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/version_camel_case/ast.golden.txt
diff --git a/res/v3x/parsing/case_sensitiveness/version_camel_case/input.cq b/res/v3x/tests/integration/case_sensitiveness/version_camel_case/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/version_camel_case/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/version_camel_case/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/x_lowercase/ast.golden.txt b/res/v3x/tests/integration/case_sensitiveness/x_lowercase/ast.golden.txt
similarity index 73%
rename from res/v3x/parsing/case_sensitiveness/x_lowercase/ast.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/x_lowercase/ast.golden.txt
index 48d39ae0a..373da9db4 100644
--- a/res/v3x/parsing/case_sensitiveness/x_lowercase/ast.golden.txt
+++ b/res/v3x/tests/integration/case_sensitiveness/x_lowercase/ast.golden.txt
@@ -26,10 +26,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..2
-          name: <
-            Identifier(
-              name: x
+        GateInstruction( # input.cq:4:1..2
+          gate: <
+            Gate( # input.cq:4:1..2
+              name: <
+                Identifier(
+                  name: x
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/case_sensitiveness/x_lowercase/input.cq b/res/v3x/tests/integration/case_sensitiveness/x_lowercase/input.cq
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/x_lowercase/input.cq
rename to res/v3x/tests/integration/case_sensitiveness/x_lowercase/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/x_lowercase/semantic.3.0.golden.txt b/res/v3x/tests/integration/case_sensitiveness/x_lowercase/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/case_sensitiveness/x_lowercase/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/case_sensitiveness/x_lowercase/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/multi_line/ast.golden.txt b/res/v3x/tests/integration/comments/multi_line/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/multi_line/ast.golden.txt
rename to res/v3x/tests/integration/comments/multi_line/ast.golden.txt
diff --git a/res/v3x/parsing/comments/multi_line/input.cq b/res/v3x/tests/integration/comments/multi_line/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/multi_line/input.cq
rename to res/v3x/tests/integration/comments/multi_line/input.cq
diff --git a/res/v3x/parsing/comments/multi_line/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/multi_line/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/multi_line/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/multi_line/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/multi_line_then_error/ast.golden.txt b/res/v3x/tests/integration/comments/multi_line_then_error/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/multi_line_then_error/ast.golden.txt
rename to res/v3x/tests/integration/comments/multi_line_then_error/ast.golden.txt
diff --git a/res/v3x/parsing/comments/multi_line_then_error/input.cq b/res/v3x/tests/integration/comments/multi_line_then_error/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/multi_line_then_error/input.cq
rename to res/v3x/tests/integration/comments/multi_line_then_error/input.cq
diff --git a/res/v3x/parsing/comments/multi_line_then_error/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/multi_line_then_error/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/multi_line_then_error/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/multi_line_then_error/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/multi_line_within_single_line/ast.golden.txt b/res/v3x/tests/integration/comments/multi_line_within_single_line/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/multi_line_within_single_line/ast.golden.txt
rename to res/v3x/tests/integration/comments/multi_line_within_single_line/ast.golden.txt
diff --git a/res/v3x/parsing/comments/multi_line_within_single_line/input.cq b/res/v3x/tests/integration/comments/multi_line_within_single_line/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/multi_line_within_single_line/input.cq
rename to res/v3x/tests/integration/comments/multi_line_within_single_line/input.cq
diff --git a/res/v3x/parsing/comments/multi_line_within_single_line/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/multi_line_within_single_line/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/multi_line_within_single_line/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/multi_line_within_single_line/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/open_multi_line_within_single_line/ast.golden.txt b/res/v3x/tests/integration/comments/open_multi_line_within_single_line/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/open_multi_line_within_single_line/ast.golden.txt
rename to res/v3x/tests/integration/comments/open_multi_line_within_single_line/ast.golden.txt
diff --git a/res/v3x/parsing/comments/open_multi_line_within_single_line/input.cq b/res/v3x/tests/integration/comments/open_multi_line_within_single_line/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/open_multi_line_within_single_line/input.cq
rename to res/v3x/tests/integration/comments/open_multi_line_within_single_line/input.cq
diff --git a/res/v3x/parsing/comments/open_multi_line_within_single_line/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/open_multi_line_within_single_line/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/open_multi_line_within_single_line/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/open_multi_line_within_single_line/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/python_style_before_version/ast.golden.txt b/res/v3x/tests/integration/comments/python_style_before_version/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/python_style_before_version/ast.golden.txt
rename to res/v3x/tests/integration/comments/python_style_before_version/ast.golden.txt
diff --git a/res/v3x/parsing/comments/python_style_before_version/input.cq b/res/v3x/tests/integration/comments/python_style_before_version/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/python_style_before_version/input.cq
rename to res/v3x/tests/integration/comments/python_style_before_version/input.cq
diff --git a/res/v3x/parsing/comments/python_style_single_line/ast.golden.txt b/res/v3x/tests/integration/comments/python_style_single_line/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/python_style_single_line/ast.golden.txt
rename to res/v3x/tests/integration/comments/python_style_single_line/ast.golden.txt
diff --git a/res/v3x/parsing/comments/python_style_single_line/input.cq b/res/v3x/tests/integration/comments/python_style_single_line/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/python_style_single_line/input.cq
rename to res/v3x/tests/integration/comments/python_style_single_line/input.cq
diff --git a/res/v3x/parsing/comments/single_line/ast.golden.txt b/res/v3x/tests/integration/comments/single_line/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line/ast.golden.txt
rename to res/v3x/tests/integration/comments/single_line/ast.golden.txt
diff --git a/res/v3x/parsing/comments/single_line/input.cq b/res/v3x/tests/integration/comments/single_line/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/single_line/input.cq
rename to res/v3x/tests/integration/comments/single_line/input.cq
diff --git a/res/v3x/parsing/comments/single_line/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/single_line/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/single_line/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/single_line_and_multi_line_before_version/ast.golden.txt b/res/v3x/tests/integration/comments/single_line_and_multi_line_before_version/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line_and_multi_line_before_version/ast.golden.txt
rename to res/v3x/tests/integration/comments/single_line_and_multi_line_before_version/ast.golden.txt
diff --git a/res/v3x/parsing/comments/single_line_and_multi_line_before_version/input.cq b/res/v3x/tests/integration/comments/single_line_and_multi_line_before_version/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/single_line_and_multi_line_before_version/input.cq
rename to res/v3x/tests/integration/comments/single_line_and_multi_line_before_version/input.cq
diff --git a/res/v3x/parsing/comments/single_line_and_multi_line_before_version/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/single_line_and_multi_line_before_version/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line_and_multi_line_before_version/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/single_line_and_multi_line_before_version/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/single_line_then_error/ast.golden.txt b/res/v3x/tests/integration/comments/single_line_then_error/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line_then_error/ast.golden.txt
rename to res/v3x/tests/integration/comments/single_line_then_error/ast.golden.txt
diff --git a/res/v3x/parsing/comments/single_line_then_error/input.cq b/res/v3x/tests/integration/comments/single_line_then_error/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/single_line_then_error/input.cq
rename to res/v3x/tests/integration/comments/single_line_then_error/input.cq
diff --git a/res/v3x/parsing/comments/single_line_then_error/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/single_line_then_error/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line_then_error/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/single_line_then_error/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/comments/single_line_within_multi_line/ast.golden.txt b/res/v3x/tests/integration/comments/single_line_within_multi_line/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line_within_multi_line/ast.golden.txt
rename to res/v3x/tests/integration/comments/single_line_within_multi_line/ast.golden.txt
diff --git a/res/v3x/parsing/comments/single_line_within_multi_line/input.cq b/res/v3x/tests/integration/comments/single_line_within_multi_line/input.cq
similarity index 100%
rename from res/v3x/parsing/comments/single_line_within_multi_line/input.cq
rename to res/v3x/tests/integration/comments/single_line_within_multi_line/input.cq
diff --git a/res/v3x/parsing/comments/single_line_within_multi_line/semantic.3.0.golden.txt b/res/v3x/tests/integration/comments/single_line_within_multi_line/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/comments/single_line_within_multi_line/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/comments/single_line_within_multi_line/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/eof/error_right_before_eof/ast.golden.json b/res/v3x/tests/integration/eof/error_right_before_eof/ast.golden.json
similarity index 100%
rename from res/v3x/parsing/eof/error_right_before_eof/ast.golden.json
rename to res/v3x/tests/integration/eof/error_right_before_eof/ast.golden.json
diff --git a/res/v3x/parsing/eof/error_right_before_eof/ast.golden.txt b/res/v3x/tests/integration/eof/error_right_before_eof/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/eof/error_right_before_eof/ast.golden.txt
rename to res/v3x/tests/integration/eof/error_right_before_eof/ast.golden.txt
diff --git a/res/v3x/parsing/eof/error_right_before_eof/input.cq b/res/v3x/tests/integration/eof/error_right_before_eof/input.cq
similarity index 100%
rename from res/v3x/parsing/eof/error_right_before_eof/input.cq
rename to res/v3x/tests/integration/eof/error_right_before_eof/input.cq
diff --git a/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/ast.golden.txt b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/ast.golden.txt
new file mode 100644
index 000000000..ebd26fec0
--- /dev/null
+++ b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/ast.golden.txt
@@ -0,0 +1,61 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:4:1..3
+          gate: <
+            Gate( # input.cq:4:1..3
+              name: <
+                Identifier(
+                  name: Rx
+                )
+              >
+              gate: -
+              parameter: <
+                Identifier( # input.cq:4:4..6
+                  name: eu
+                )
+              >
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:4:8..9
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/input.cq b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/input.cq
similarity index 100%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/input.cq
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/input.cq
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/semantic.3.0.golden.txt
similarity index 69%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/semantic.3.0.golden.txt
index cf028725c..92fb6d9f5 100644
--- a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_eu_q/semantic.3.0.golden.txt
@@ -9,9 +9,20 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
-          instruction_ref: Rx(qubit, float)
-          name: Rx
+        GateInstruction(
+          instruction_ref: Rx(qubit)
+          gate: <
+            Gate(
+              name: Rx
+              gate: -
+              parameter: <
+                ConstFloat(
+                  value: 2.71828
+                )
+              >
+              annotations: []
+            )
+          >
           operands: [
             VariableRef(
               variable --> <
@@ -26,9 +37,6 @@ Program(
                 )
               >
             )
-            ConstFloat(
-              value: 2.71828
-            )
           ]
           annotations: []
         )
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/ast.golden.txt b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/ast.golden.txt
similarity index 74%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/ast.golden.txt
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/ast.golden.txt
index 82c3760df..f5fa42439 100644
--- a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/ast.golden.txt
+++ b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/ast.golden.txt
@@ -26,10 +26,21 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..3
-          name: <
-            Identifier(
-              name: Rx
+        GateInstruction( # input.cq:4:1..3
+          gate: <
+            Gate( # input.cq:4:1..3
+              name: <
+                Identifier(
+                  name: Rx
+                )
+              >
+              gate: -
+              parameter: <
+                Identifier( # input.cq:4:4..6
+                  name: pi
+                )
+              >
+              annotations: []
             )
           >
           operands: <
@@ -38,9 +49,6 @@ Program(
                 Identifier( # input.cq:4:8..9
                   name: q
                 )
-                Identifier( # input.cq:4:4..6
-                  name: pi
-                )
               ]
             )
           >
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/input.cq b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/input.cq
similarity index 100%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/input.cq
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/input.cq
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/semantic.3.0.golden.txt
similarity index 69%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/semantic.3.0.golden.txt
index 460d8b3d9..99f5f539f 100644
--- a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_pi_q/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_pi_q/semantic.3.0.golden.txt
@@ -9,9 +9,20 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
-          instruction_ref: Rx(qubit, float)
-          name: Rx
+        GateInstruction(
+          instruction_ref: Rx(qubit)
+          gate: <
+            Gate(
+              name: Rx
+              gate: -
+              parameter: <
+                ConstFloat(
+                  value: 3.14159
+                )
+              >
+              annotations: []
+            )
+          >
           operands: [
             VariableRef(
               variable --> <
@@ -26,9 +37,6 @@ Program(
                 )
               >
             )
-            ConstFloat(
-              value: 3.14159
-            )
           ]
           annotations: []
         )
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/ast.golden.txt b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/ast.golden.txt
similarity index 74%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/ast.golden.txt
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/ast.golden.txt
index ef7b32872..c44e78fc9 100644
--- a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/ast.golden.txt
+++ b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/ast.golden.txt
@@ -26,10 +26,21 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..3
-          name: <
-            Identifier(
-              name: Rx
+        GateInstruction( # input.cq:4:1..3
+          gate: <
+            Gate( # input.cq:4:1..3
+              name: <
+                Identifier(
+                  name: Rx
+                )
+              >
+              gate: -
+              parameter: <
+                Identifier( # input.cq:4:4..7
+                  name: tau
+                )
+              >
+              annotations: []
             )
           >
           operands: <
@@ -38,9 +49,6 @@ Program(
                 Identifier( # input.cq:4:9..10
                   name: q
                 )
-                Identifier( # input.cq:4:4..7
-                  name: tau
-                )
               ]
             )
           >
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/input.cq b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/input.cq
similarity index 100%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/input.cq
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/input.cq
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/semantic.3.0.golden.txt
similarity index 69%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/semantic.3.0.golden.txt
index d23d68789..66fa6b80d 100644
--- a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_tau_q/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/eu_pi_tau/qubit_q__Rx_tau_q/semantic.3.0.golden.txt
@@ -9,9 +9,20 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
-          instruction_ref: Rx(qubit, float)
-          name: Rx
+        GateInstruction(
+          instruction_ref: Rx(qubit)
+          gate: <
+            Gate(
+              name: Rx
+              gate: -
+              parameter: <
+                ConstFloat(
+                  value: 6.28319
+                )
+              >
+              annotations: []
+            )
+          >
           operands: [
             VariableRef(
               variable --> <
@@ -26,9 +37,6 @@ Program(
                 )
               >
             )
-            ConstFloat(
-              value: 6.28319
-            )
           ]
           annotations: []
         )
diff --git a/res/v3x/parsing/expression/out_of_bounds_index/ast.golden.txt b/res/v3x/tests/integration/expression/out_of_bounds_index/ast.golden.txt
similarity index 83%
rename from res/v3x/parsing/expression/out_of_bounds_index/ast.golden.txt
rename to res/v3x/tests/integration/expression/out_of_bounds_index/ast.golden.txt
index cefc76f56..0405c0527 100644
--- a/res/v3x/parsing/expression/out_of_bounds_index/ast.golden.txt
+++ b/res/v3x/tests/integration/expression/out_of_bounds_index/ast.golden.txt
@@ -30,10 +30,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:4:1..2
+          gate: <
+            Gate( # input.cq:4:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/expression/out_of_bounds_index/input.cq b/res/v3x/tests/integration/expression/out_of_bounds_index/input.cq
similarity index 100%
rename from res/v3x/parsing/expression/out_of_bounds_index/input.cq
rename to res/v3x/tests/integration/expression/out_of_bounds_index/input.cq
diff --git a/res/v3x/parsing/expression/out_of_bounds_index/semantic.3.0.golden.txt b/res/v3x/tests/integration/expression/out_of_bounds_index/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/expression/out_of_bounds_index/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/expression/out_of_bounds_index/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/expression/out_of_range_float_literal/ast.golden.txt b/res/v3x/tests/integration/expression/out_of_range_float_literal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/expression/out_of_range_float_literal/ast.golden.txt
rename to res/v3x/tests/integration/expression/out_of_range_float_literal/ast.golden.txt
diff --git a/res/v3x/parsing/expression/out_of_range_float_literal/input.cq b/res/v3x/tests/integration/expression/out_of_range_float_literal/input.cq
similarity index 100%
rename from res/v3x/parsing/expression/out_of_range_float_literal/input.cq
rename to res/v3x/tests/integration/expression/out_of_range_float_literal/input.cq
diff --git a/res/v3x/parsing/expression/out_of_range_integer_literal/ast.golden.txt b/res/v3x/tests/integration/expression/out_of_range_integer_literal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/expression/out_of_range_integer_literal/ast.golden.txt
rename to res/v3x/tests/integration/expression/out_of_range_integer_literal/ast.golden.txt
diff --git a/res/v3x/parsing/expression/out_of_range_integer_literal/input.cq b/res/v3x/tests/integration/expression/out_of_range_integer_literal/input.cq
similarity index 100%
rename from res/v3x/parsing/expression/out_of_range_integer_literal/input.cq
rename to res/v3x/tests/integration/expression/out_of_range_integer_literal/input.cq
diff --git a/res/v3x/parsing/expression_list/bad_first_part/ast.golden.txt b/res/v3x/tests/integration/expression_list/bad_first_part/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/expression_list/bad_first_part/ast.golden.txt
rename to res/v3x/tests/integration/expression_list/bad_first_part/ast.golden.txt
diff --git a/res/v3x/parsing/expression_list/bad_first_part/input.cq b/res/v3x/tests/integration/expression_list/bad_first_part/input.cq
similarity index 100%
rename from res/v3x/parsing/expression_list/bad_first_part/input.cq
rename to res/v3x/tests/integration/expression_list/bad_first_part/input.cq
diff --git a/res/v3x/parsing/expression_list/bad_second_part/ast.golden.txt b/res/v3x/tests/integration/expression_list/bad_second_part/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/expression_list/bad_second_part/ast.golden.txt
rename to res/v3x/tests/integration/expression_list/bad_second_part/ast.golden.txt
diff --git a/res/v3x/parsing/expression_list/bad_second_part/input.cq b/res/v3x/tests/integration/expression_list/bad_second_part/input.cq
similarity index 100%
rename from res/v3x/parsing/expression_list/bad_second_part/input.cq
rename to res/v3x/tests/integration/expression_list/bad_second_part/input.cq
diff --git a/res/v3x/parsing/expression_list/no_second_part/ast.golden.txt b/res/v3x/tests/integration/expression_list/no_second_part/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/expression_list/no_second_part/ast.golden.txt
rename to res/v3x/tests/integration/expression_list/no_second_part/ast.golden.txt
diff --git a/res/v3x/parsing/expression_list/no_second_part/input.cq b/res/v3x/tests/integration/expression_list/no_second_part/input.cq
similarity index 100%
rename from res/v3x/parsing/expression_list/no_second_part/input.cq
rename to res/v3x/tests/integration/expression_list/no_second_part/input.cq
diff --git a/res/v3x/tests/integration/gate_modifier/X_inv_q/ast.golden.json b/res/v3x/tests/integration/gate_modifier/X_inv_q/ast.golden.json
new file mode 100644
index 000000000..c2ae47547
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/X_inv_q/ast.golden.json
@@ -0,0 +1,1 @@
+{"errors":[{"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":3}},"message":"no viable alternative at input 'X.'","severity":1,"relatedInformation":[{"location":{"uri":"file:///input.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/X_inv_q/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/X_inv_q/ast.golden.txt
new file mode 100644
index 000000000..231f92b4d
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/X_inv_q/ast.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:5:2..3: no viable alternative at input 'X.'
diff --git a/res/v3x/tests/integration/gate_modifier/X_inv_q/input.cq b/res/v3x/tests/integration/gate_modifier/X_inv_q/input.cq
new file mode 100644
index 000000000..318c589b7
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/X_inv_q/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit q
+
+X.inv q
diff --git a/res/v3x/tests/integration/gate_modifier/adj_X_q/ast.golden.json b/res/v3x/tests/integration/gate_modifier/adj_X_q/ast.golden.json
new file mode 100644
index 000000000..8de0ff756
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/adj_X_q/ast.golden.json
@@ -0,0 +1,1 @@
+{"errors":[{"range":{"start":{"line":5,"character":4},"end":{"line":5,"character":5}},"message":"no viable alternative at input 'adj.'","severity":1,"relatedInformation":[{"location":{"uri":"file:///input.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/adj_X_q/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/adj_X_q/ast.golden.txt
new file mode 100644
index 000000000..4373fdc66
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/adj_X_q/ast.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:5:4..5: no viable alternative at input 'adj.'
diff --git a/res/v3x/tests/integration/gate_modifier/adj_X_q/input.cq b/res/v3x/tests/integration/gate_modifier/adj_X_q/input.cq
new file mode 100644
index 000000000..82de7018d
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/adj_X_q/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit q
+
+adj.X q
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q/ast.golden.json b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/ast.golden.json
new file mode 100644
index 000000000..0338797a6
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/ast.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"version":{"Version":{"items":"3","source_location":"input.cq:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":"-","source_location":"input.cq:3:1..6"}},"annotations":[],"source_location":"input.cq:3:7..8"}},{"GateInstruction":{"gate":{"Gate":{"name":{"Identifier":{"name":"ctrl"}},"gate":{"Gate":{"name":{"Identifier":{"name":"X"}},"gate":"-","parameter":"-","annotations":[],"source_location":"input.cq:5:6..7"}},"parameter":"-","annotations":[],"source_location":"input.cq:5:1..5"}},"operands":{"ExpressionList":{"items":[{"Identifier":{"name":"q","source_location":"input.cq:5:8..9"}}]}},"annotations":[],"source_location":"input.cq:5:1..5"}}]}}}}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/ast.golden.txt
new file mode 100644
index 000000000..65cd82787
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/ast.golden.txt
@@ -0,0 +1,68 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:5:1..5
+          gate: <
+            Gate( # input.cq:5:1..5
+              name: <
+                Identifier(
+                  name: ctrl
+                )
+              >
+              gate: <
+                Gate( # input.cq:5:6..7
+                  name: <
+                    Identifier(
+                      name: X
+                    )
+                  >
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:5:8..9
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q/input.cq b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/input.cq
new file mode 100644
index 000000000..2b1e84805
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit q
+
+ctrl.X q
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q/semantic.3.0.golden.json b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/semantic.3.0.golden.json
new file mode 100644
index 000000000..67f6f2e43
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"errors":[{"range":{"start":{"line":5,"character":1},"end":{"line":5,"character":5}},"message":"failed to resolve instruction '2q_X' with argument pack (qubit)","severity":1,"relatedInformation":[{"location":{"uri":"file:///input.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/semantic.3.0.golden.txt
new file mode 100644
index 000000000..379872e19
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q/semantic.3.0.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:5:1..5: failed to resolve instruction '2q_X' with argument pack (qubit)
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/ast.golden.txt
new file mode 100644
index 000000000..373609c27
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/ast.golden.txt
@@ -0,0 +1,109 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:10..11
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..9
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 2
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:5:1..5
+          gate: <
+            Gate( # input.cq:5:1..5
+              name: <
+                Identifier(
+                  name: ctrl
+                )
+              >
+              gate: <
+                Gate( # input.cq:5:6..7
+                  name: <
+                    Identifier(
+                      name: X
+                    )
+                  >
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:5:8..9
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:5:10..11
+                              value: 0
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+                Index( # input.cq:5:14..15
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:5:16..17
+                              value: 1
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/input.cq b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/input.cq
new file mode 100644
index 000000000..e9bf00995
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit[2] q
+
+ctrl.X q[0], q[1]
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/semantic.3.0.golden.json b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/semantic.3.0.golden.json
new file mode 100644
index 000000000..d6f5027e6
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"GateInstruction":{"instruction_ref":"2q_X(qubit, qubit)","gate":{"Gate":{"name":"ctrl","gate":{"Gate":{"name":"X","gate":"-","parameter":"-","annotations":[]}},"parameter":"-","annotations":[]}},"operands":[{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"2"}},"annotations":[]}},"indices":[{"ConstInt":{"value":"0"}}]}},{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"2"}},"annotations":[]}},"indices":[{"ConstInt":{"value":"1"}}]}}],"annotations":[]}}]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"2"}},"annotations":[]}}]}}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/semantic.3.0.golden.txt
new file mode 100644
index 000000000..c555ba25b
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_0_q_1/semantic.3.0.golden.txt
@@ -0,0 +1,84 @@
+SUCCESS
+Program(
+  api_version: 3.0
+  version: <
+    Version(
+      items: 3
+    )
+  >
+  block: <
+    Block(
+      statements: [
+        GateInstruction(
+          instruction_ref: 2q_X(qubit, qubit)
+          gate: <
+            Gate(
+              name: ctrl
+              gate: <
+                Gate(
+                  name: X
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: [
+            IndexRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    QubitArray(
+                      size: 2
+                    )
+                  >
+                  annotations: []
+                )
+              >
+              indices: [
+                ConstInt(
+                  value: 0
+                )
+              ]
+            )
+            IndexRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    QubitArray(
+                      size: 2
+                    )
+                  >
+                  annotations: []
+                )
+              >
+              indices: [
+                ConstInt(
+                  value: 1
+                )
+              ]
+            )
+          ]
+          annotations: []
+        )
+      ]
+    )
+  >
+  variables: [
+    Variable(
+      name: q
+      typ: <
+        QubitArray(
+          size: 2
+        )
+      >
+      annotations: []
+    )
+  ]
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/ast.golden.json b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/ast.golden.json
new file mode 100644
index 000000000..4889f2d7c
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/ast.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"version":{"Version":{"items":"3","source_location":"input.cq:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"2"}},"source_location":"input.cq:3:1..9"}},"annotations":[],"source_location":"input.cq:3:10..11"}},{"GateInstruction":{"gate":{"Gate":{"name":{"Identifier":{"name":"ctrl"}},"gate":{"Gate":{"name":{"Identifier":{"name":"X"}},"gate":"-","parameter":"-","annotations":[],"source_location":"input.cq:5:6..7"}},"parameter":"-","annotations":[],"source_location":"input.cq:5:1..5"}},"operands":{"ExpressionList":{"items":[{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexRange":{"first":{"IntegerLiteral":{"value":"0","source_location":"input.cq:5:10..11"}},"last":{"IntegerLiteral":{"value":"1","source_location":"input.cq:5:12..13"}}}}]}},"source_location":"input.cq:5:8..9"}}]}},"annotations":[],"source_location":"input.cq:5:1..5"}}]}}}}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/ast.golden.txt
new file mode 100644
index 000000000..058a7777b
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/ast.golden.txt
@@ -0,0 +1,94 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:10..11
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..9
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 2
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:5:1..5
+          gate: <
+            Gate( # input.cq:5:1..5
+              name: <
+                Identifier(
+                  name: ctrl
+                )
+              >
+              gate: <
+                Gate( # input.cq:5:6..7
+                  name: <
+                    Identifier(
+                      name: X
+                    )
+                  >
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:5:8..9
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:5:10..11
+                              value: 0
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:5:12..13
+                              value: 1
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/input.cq b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/input.cq
new file mode 100644
index 000000000..ac267cedd
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit[2] q
+
+ctrl.X q[0:1]
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/semantic.3.0.golden.json b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/semantic.3.0.golden.json
new file mode 100644
index 000000000..7d153a67b
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"errors":[{"range":{"start":{"line":5,"character":1},"end":{"line":5,"character":5}},"message":"failed to resolve instruction '2q_X' with argument pack (qubit array)","severity":1,"relatedInformation":[{"location":{"uri":"file:///input.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/semantic.3.0.golden.txt
new file mode 100644
index 000000000..230fd3bf7
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/ctrl_X_q_range_0_1/semantic.3.0.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:5:1..5: failed to resolve instruction '2q_X' with argument pack (qubit array)
diff --git a/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/ast.golden.txt
new file mode 100644
index 000000000..5d83676ec
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/ast.golden.txt
@@ -0,0 +1,79 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:5:1..4
+          gate: <
+            Gate( # input.cq:5:1..4
+              name: <
+                Identifier(
+                  name: inv
+                )
+              >
+              gate: <
+                Gate( # input.cq:5:5..8
+                  name: <
+                    Identifier(
+                      name: inv
+                    )
+                  >
+                  gate: <
+                    Gate( # input.cq:5:9..10
+                      name: <
+                        Identifier(
+                          name: X
+                        )
+                      >
+                      gate: -
+                      parameter: -
+                      annotations: []
+                    )
+                  >
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:5:11..12
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/input.cq b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/input.cq
new file mode 100644
index 000000000..c9e5543bf
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit q
+
+inv.inv.X q
diff --git a/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/semantic.3.0.golden.json b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/semantic.3.0.golden.json
new file mode 100644
index 000000000..5a07c9342
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"GateInstruction":{"instruction_ref":"1q_X(qubit)","gate":{"Gate":{"name":"inv","gate":{"Gate":{"name":"inv","gate":{"Gate":{"name":"X","gate":"-","parameter":"-","annotations":[]}},"parameter":"-","annotations":[]}},"parameter":"-","annotations":[]}},"operands":[{"VariableRef":{"variable":{"Variable":{"name":"q","typ":{"Qubit":{"size":"1"}},"annotations":[]}}}}],"annotations":[]}}]}},"variables":[{"Variable":{"name":"q","typ":{"Qubit":{"size":"1"}},"annotations":[]}}]}}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/semantic.3.0.golden.txt
new file mode 100644
index 000000000..c914cc51b
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_inv_X_q/semantic.3.0.golden.txt
@@ -0,0 +1,68 @@
+SUCCESS
+Program(
+  api_version: 3.0
+  version: <
+    Version(
+      items: 3
+    )
+  >
+  block: <
+    Block(
+      statements: [
+        GateInstruction(
+          instruction_ref: 1q_X(qubit)
+          gate: <
+            Gate(
+              name: inv
+              gate: <
+                Gate(
+                  name: inv
+                  gate: <
+                    Gate(
+                      name: X
+                      gate: -
+                      parameter: -
+                      annotations: []
+                    )
+                  >
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: [
+            VariableRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    Qubit(
+                      size: 1
+                    )
+                  >
+                  annotations: []
+                )
+              >
+            )
+          ]
+          annotations: []
+        )
+      ]
+    )
+  >
+  variables: [
+    Variable(
+      name: q
+      typ: <
+        Qubit(
+          size: 1
+        )
+      >
+      annotations: []
+    )
+  ]
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/inv_reset/ast.golden.json b/res/v3x/tests/integration/gate_modifier/inv_reset/ast.golden.json
new file mode 100644
index 000000000..7a496da8b
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_reset/ast.golden.json
@@ -0,0 +1,1 @@
+{"errors":[{"range":{"start":{"line":3,"character":5},"end":{"line":3,"character":10}},"message":"mismatched input 'reset' expecting {'inv', 'pow', 'ctrl', IDENTIFIER}","severity":1,"relatedInformation":[{"location":{"uri":"file:///input.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/inv_reset/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/inv_reset/ast.golden.txt
new file mode 100644
index 000000000..a6ddbc3c8
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_reset/ast.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:3:5..10: mismatched input 'reset' expecting {'inv', 'pow', 'ctrl', IDENTIFIER}
diff --git a/res/v3x/tests/integration/gate_modifier/inv_reset/input.cq b/res/v3x/tests/integration/gate_modifier/inv_reset/input.cq
new file mode 100644
index 000000000..4990a0efb
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_reset/input.cq
@@ -0,0 +1,3 @@
+version 3
+
+inv.reset
diff --git a/res/v3x/tests/integration/gate_modifier/inv_x_q/ast.golden.json b/res/v3x/tests/integration/gate_modifier/inv_x_q/ast.golden.json
new file mode 100644
index 000000000..260c5b1f8
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_x_q/ast.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"version":{"Version":{"items":"3","source_location":"input.cq:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":"-","source_location":"input.cq:3:1..6"}},"annotations":[],"source_location":"input.cq:3:7..8"}},{"GateInstruction":{"gate":{"Gate":{"name":{"Identifier":{"name":"inv"}},"gate":{"Gate":{"name":{"Identifier":{"name":"x"}},"gate":"-","parameter":"-","annotations":[],"source_location":"input.cq:5:5..6"}},"parameter":"-","annotations":[],"source_location":"input.cq:5:1..4"}},"operands":{"ExpressionList":{"items":[{"Identifier":{"name":"q","source_location":"input.cq:5:7..8"}}]}},"annotations":[],"source_location":"input.cq:5:1..4"}}]}}}}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/inv_x_q/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/inv_x_q/ast.golden.txt
new file mode 100644
index 000000000..8aa343014
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_x_q/ast.golden.txt
@@ -0,0 +1,68 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:5:1..4
+          gate: <
+            Gate( # input.cq:5:1..4
+              name: <
+                Identifier(
+                  name: inv
+                )
+              >
+              gate: <
+                Gate( # input.cq:5:5..6
+                  name: <
+                    Identifier(
+                      name: x
+                    )
+                  >
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:5:7..8
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/inv_x_q/input.cq b/res/v3x/tests/integration/gate_modifier/inv_x_q/input.cq
new file mode 100644
index 000000000..18474b7ef
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_x_q/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit q
+
+inv.x q
diff --git a/res/v3x/tests/integration/gate_modifier/inv_x_q/semantic.3.0.golden.json b/res/v3x/tests/integration/gate_modifier/inv_x_q/semantic.3.0.golden.json
new file mode 100644
index 000000000..76aee6238
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_x_q/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"errors":[{"range":{"start":{"line":5,"character":1},"end":{"line":5,"character":4}},"message":"failed to resolve instruction '1q_x' with argument pack (qubit)","severity":1,"relatedInformation":[{"location":{"uri":"file:///input.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/inv_x_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/inv_x_q/semantic.3.0.golden.txt
new file mode 100644
index 000000000..b2f2cf35a
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/inv_x_q/semantic.3.0.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:5:1..4: failed to resolve instruction '1q_x' with argument pack (qubit)
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_X_q/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/ast.golden.txt
new file mode 100644
index 000000000..8aff073e8
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/ast.golden.txt
@@ -0,0 +1,72 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:5:1..4
+          gate: <
+            Gate( # input.cq:5:1..4
+              name: <
+                Identifier(
+                  name: pow
+                )
+              >
+              gate: <
+                Gate( # input.cq:5:8..9
+                  name: <
+                    Identifier(
+                      name: X
+                    )
+                  >
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: <
+                IntegerLiteral( # input.cq:5:5..6
+                  value: 2
+                )
+              >
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:5:10..11
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_X_q/input.cq b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/input.cq
new file mode 100644
index 000000000..2785fcfe9
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit q
+
+pow(2).X q
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_X_q/semantic.3.0.golden.json b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/semantic.3.0.golden.json
new file mode 100644
index 000000000..ae00d44c9
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"GateInstruction":{"instruction_ref":"1q_X(qubit)","gate":{"Gate":{"name":"pow","gate":{"Gate":{"name":"X","gate":"-","parameter":"-","annotations":[]}},"parameter":{"ConstFloat":{"value":"2"}},"annotations":[]}},"operands":[{"VariableRef":{"variable":{"Variable":{"name":"q","typ":{"Qubit":{"size":"1"}},"annotations":[]}}}}],"annotations":[]}}]}},"variables":[{"Variable":{"name":"q","typ":{"Qubit":{"size":"1"}},"annotations":[]}}]}}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_X_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/semantic.3.0.golden.txt
new file mode 100644
index 000000000..402741efc
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_X_q/semantic.3.0.golden.txt
@@ -0,0 +1,65 @@
+SUCCESS
+Program(
+  api_version: 3.0
+  version: <
+    Version(
+      items: 3
+    )
+  >
+  block: <
+    Block(
+      statements: [
+        GateInstruction(
+          instruction_ref: 1q_X(qubit)
+          gate: <
+            Gate(
+              name: pow
+              gate: <
+                Gate(
+                  name: X
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: <
+                ConstFloat(
+                  value: 2
+                )
+              >
+              annotations: []
+            )
+          >
+          operands: [
+            VariableRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    Qubit(
+                      size: 1
+                    )
+                  >
+                  annotations: []
+                )
+              >
+            )
+          ]
+          annotations: []
+        )
+      ]
+    )
+  >
+  variables: [
+    Variable(
+      name: q
+      typ: <
+        Qubit(
+          size: 1
+        )
+      >
+      annotations: []
+    )
+  ]
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/ast.golden.txt
new file mode 100644
index 000000000..1ab9065e8
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/ast.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:5:7..10: extraneous input 'inv' expecting '.'
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/input.cq b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/input.cq
new file mode 100644
index 000000000..885d548ac
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/input.cq
@@ -0,0 +1,5 @@
+version 3
+
+qubit[2] q
+
+pow(2)inv.ctrl.X q[0], q[1]
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/semantic.3.0.golden.json b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/semantic.3.0.golden.json
new file mode 100644
index 000000000..eb0cf0dea
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"errors":[{"range":{"start":{"line":5,"character":5},"end":{"line":5,"character":8}},"message":"trying to apply a gate modifier to a multi-qubit gate","severity":1,"relatedInformation":[{"location":{"uri":"file:///input.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]},{"range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}},"message":"failed to resolve instruction 'pow' with argument pack (qubit, qubit)","severity":1}]}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/semantic.3.0.golden.txt
new file mode 100644
index 000000000..5a5a84618
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_2_inv_ctrl_X_q_0_q_1/semantic.3.0.golden.txt
@@ -0,0 +1,3 @@
+ERROR
+Error at input.cq:5:5..8: trying to apply a gate modifier to a multi-qubit gate
+Error: failed to resolve instruction 'pow' with argument pack (qubit, qubit)
diff --git a/res/v3x/tests/integration/gate_modifier/pow_b_X_q/ast.golden.json b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/ast.golden.json
new file mode 100644
index 000000000..ddd040df9
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/ast.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"version":{"Version":{"items":"3","source_location":"input.cq:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":"-","source_location":"input.cq:3:1..6"}},"annotations":[],"source_location":"input.cq:3:7..8"}},{"Variable":{"name":{"Identifier":{"name":"b"}},"typ":{"Type":{"name":{"Keyword":{"name":"bit"}},"size":"-","source_location":"input.cq:4:1..4"}},"annotations":[],"source_location":"input.cq:4:5..6"}},{"GateInstruction":{"gate":{"Gate":{"name":{"Identifier":{"name":"pow"}},"gate":{"Gate":{"name":{"Identifier":{"name":"X"}},"gate":"-","parameter":"-","annotations":[],"source_location":"input.cq:6:8..9"}},"parameter":{"Identifier":{"name":"b","source_location":"input.cq:6:5..6"}},"annotations":[],"source_location":"input.cq:6:1..4"}},"operands":{"ExpressionList":{"items":[{"Identifier":{"name":"q","source_location":"input.cq:6:10..11"}}]}},"annotations":[],"source_location":"input.cq:6:1..4"}}]}}}}
\ No newline at end of file
diff --git a/res/v3x/tests/integration/gate_modifier/pow_b_X_q/ast.golden.txt b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/ast.golden.txt
new file mode 100644
index 000000000..f8e7b564f
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/ast.golden.txt
@@ -0,0 +1,90 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        Variable( # input.cq:4:5..6
+          name: <
+            Identifier(
+              name: b
+            )
+          >
+          typ: <
+            Type( # input.cq:4:1..4
+              name: <
+                Keyword(
+                  name: bit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:6:1..4
+          gate: <
+            Gate( # input.cq:6:1..4
+              name: <
+                Identifier(
+                  name: pow
+                )
+              >
+              gate: <
+                Gate( # input.cq:6:8..9
+                  name: <
+                    Identifier(
+                      name: X
+                    )
+                  >
+                  gate: -
+                  parameter: -
+                  annotations: []
+                )
+              >
+              parameter: <
+                Identifier( # input.cq:6:5..6
+                  name: b
+                )
+              >
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:10..11
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/tests/integration/gate_modifier/pow_b_X_q/input.cq b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/input.cq
new file mode 100644
index 000000000..56f3c1857
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/input.cq
@@ -0,0 +1,6 @@
+version 3
+
+qubit q
+bit b
+
+pow(b).X q
diff --git a/res/v3x/tests/integration/gate_modifier/pow_b_X_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/semantic.3.0.golden.txt
new file mode 100644
index 000000000..9c039a142
--- /dev/null
+++ b/res/v3x/tests/integration/gate_modifier/pow_b_X_q/semantic.3.0.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:6:1..4: failed to resolve 'pow' with argument pack (bit)
diff --git a/res/v3x/parsing/instruction/X/ast.golden.txt b/res/v3x/tests/integration/instruction/X/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/X/ast.golden.txt
rename to res/v3x/tests/integration/instruction/X/ast.golden.txt
diff --git a/res/v3x/parsing/instruction/X/input.cq b/res/v3x/tests/integration/instruction/X/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/X/input.cq
rename to res/v3x/tests/integration/instruction/X/input.cq
diff --git a/res/v3x/parsing/instruction/X/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/X/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/X/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/X/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/instruction/X_1/ast.golden.txt b/res/v3x/tests/integration/instruction/X_1/ast.golden.txt
similarity index 59%
rename from res/v3x/parsing/instruction/X_1/ast.golden.txt
rename to res/v3x/tests/integration/instruction/X_1/ast.golden.txt
index af7f7e349..8e3a58d49 100644
--- a/res/v3x/parsing/instruction/X_1/ast.golden.txt
+++ b/res/v3x/tests/integration/instruction/X_1/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:3:1..2
+          gate: <
+            Gate( # input.cq:3:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/instruction/X_1/input.cq b/res/v3x/tests/integration/instruction/X_1/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/X_1/input.cq
rename to res/v3x/tests/integration/instruction/X_1/input.cq
diff --git a/res/v3x/parsing/instruction/X_1/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/X_1/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/X_1/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/X_1/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/instruction/X_123abc/ast.golden.txt b/res/v3x/tests/integration/instruction/X_123abc/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/X_123abc/ast.golden.txt
rename to res/v3x/tests/integration/instruction/X_123abc/ast.golden.txt
diff --git a/res/v3x/parsing/instruction/X_123abc/input.cq b/res/v3x/tests/integration/instruction/X_123abc/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/X_123abc/input.cq
rename to res/v3x/tests/integration/instruction/X_123abc/input.cq
diff --git a/res/v3x/parsing/instruction/X_3.14/ast.golden.txt b/res/v3x/tests/integration/instruction/X_3.14/ast.golden.txt
similarity index 60%
rename from res/v3x/parsing/instruction/X_3.14/ast.golden.txt
rename to res/v3x/tests/integration/instruction/X_3.14/ast.golden.txt
index 3461541b4..631d9bfd2 100644
--- a/res/v3x/parsing/instruction/X_3.14/ast.golden.txt
+++ b/res/v3x/tests/integration/instruction/X_3.14/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:3:1..2
+          gate: <
+            Gate( # input.cq:3:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/instruction/X_3.14/input.cq b/res/v3x/tests/integration/instruction/X_3.14/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/X_3.14/input.cq
rename to res/v3x/tests/integration/instruction/X_3.14/input.cq
diff --git a/res/v3x/parsing/instruction/X_3.14/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/X_3.14/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/X_3.14/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/X_3.14/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/instruction/X_q0/ast.golden.txt b/res/v3x/tests/integration/instruction/X_q0/ast.golden.txt
similarity index 76%
rename from res/v3x/parsing/instruction/X_q0/ast.golden.txt
rename to res/v3x/tests/integration/instruction/X_q0/ast.golden.txt
index c3f013f1c..d812ec6e9 100644
--- a/res/v3x/parsing/instruction/X_q0/ast.golden.txt
+++ b/res/v3x/tests/integration/instruction/X_q0/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:3:1..2
+          gate: <
+            Gate( # input.cq:3:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/instruction/X_q0/input.cq b/res/v3x/tests/integration/instruction/X_q0/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/X_q0/input.cq
rename to res/v3x/tests/integration/instruction/X_q0/input.cq
diff --git a/res/v3x/parsing/instruction/X_q0/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/X_q0/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/X_q0/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/X_q0/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/instruction/X_v/ast.golden.txt b/res/v3x/tests/integration/instruction/X_v/ast.golden.txt
similarity index 59%
rename from res/v3x/parsing/instruction/X_v/ast.golden.txt
rename to res/v3x/tests/integration/instruction/X_v/ast.golden.txt
index 78d1570f0..7650ccdb9 100644
--- a/res/v3x/parsing/instruction/X_v/ast.golden.txt
+++ b/res/v3x/tests/integration/instruction/X_v/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:3:1..2
+          gate: <
+            Gate( # input.cq:3:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/instruction/X_v/input.cq b/res/v3x/tests/integration/instruction/X_v/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/X_v/input.cq
rename to res/v3x/tests/integration/instruction/X_v/input.cq
diff --git a/res/v3x/parsing/instruction/X_v/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/X_v/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/X_v/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/X_v/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/ast.golden.txt b/res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/ast.golden.txt
similarity index 80%
rename from res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/ast.golden.txt
rename to res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/ast.golden.txt
index b254163d1..5822d7a1b 100644
--- a/res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/ast.golden.txt
+++ b/res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/ast.golden.txt
@@ -30,10 +30,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:5:1..2
-          name: <
-            Identifier(
-              name: H
+        GateInstruction( # input.cq:5:1..2
+          gate: <
+            Gate( # input.cq:5:1..2
+              name: <
+                Identifier(
+                  name: H
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
@@ -64,10 +71,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:6:1..2
-          name: <
-            Identifier(
-              name: H
+        GateInstruction( # input.cq:6:1..2
+          gate: <
+            Gate( # input.cq:6:1..2
+              name: <
+                Identifier(
+                  name: H
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
@@ -98,10 +112,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:7:1..5
-          name: <
-            Identifier(
-              name: CNOT
+        GateInstruction( # input.cq:7:1..5
+          gate: <
+            Gate( # input.cq:7:1..5
+              name: <
+                Identifier(
+                  name: CNOT
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/input.cq b/res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/input.cq
rename to res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/input.cq
diff --git a/res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/semantic.3.0.golden.txt
similarity index 80%
rename from res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/semantic.3.0.golden.txt
index 3df9db668..25aebaac5 100644
--- a/res/v3x/parsing/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/instruction/qubit_2__H_q_0__H_q_1__CNOT_q_0_q_1/semantic.3.0.golden.txt
@@ -9,9 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        GateInstruction(
           instruction_ref: H(qubit)
-          name: H
+          gate: <
+            Gate(
+              name: H
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             IndexRef(
               variable --> <
@@ -34,9 +41,16 @@ Program(
           ]
           annotations: []
         )
-        Instruction(
+        GateInstruction(
           instruction_ref: H(qubit)
-          name: H
+          gate: <
+            Gate(
+              name: H
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             IndexRef(
               variable --> <
@@ -59,9 +73,16 @@ Program(
           ]
           annotations: []
         )
-        Instruction(
+        GateInstruction(
           instruction_ref: CNOT(qubit, qubit)
-          name: CNOT
+          gate: <
+            Gate(
+              name: CNOT
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             IndexRef(
               variable --> <
diff --git a/res/v3x/tests/integration/instruction/qubit_q__X_q/ast.golden.txt b/res/v3x/tests/integration/instruction/qubit_q__X_q/ast.golden.txt
new file mode 100644
index 000000000..3726b2ae9
--- /dev/null
+++ b/res/v3x/tests/integration/instruction/qubit_q__X_q/ast.golden.txt
@@ -0,0 +1,57 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:7..8
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..6
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: -
+            )
+          >
+          annotations: []
+        )
+        GateInstruction( # input.cq:5:1..2
+          gate: <
+            Gate( # input.cq:5:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:5:3..4
+                  name: q
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/instruction/qubit_q__X_q/input.cq b/res/v3x/tests/integration/instruction/qubit_q__X_q/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/qubit_q__X_q/input.cq
rename to res/v3x/tests/integration/instruction/qubit_q__X_q/input.cq
diff --git a/res/v3x/parsing/instruction/qubit_q__X_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/qubit_q__X_q/semantic.3.0.golden.txt
similarity index 78%
rename from res/v3x/parsing/instruction/qubit_q__X_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/qubit_q__X_q/semantic.3.0.golden.txt
index 75869d063..bd7977639 100644
--- a/res/v3x/parsing/instruction/qubit_q__X_q/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/instruction/qubit_q__X_q/semantic.3.0.golden.txt
@@ -9,9 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        GateInstruction(
           instruction_ref: X(qubit)
-          name: X
+          gate: <
+            Gate(
+              name: X
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             VariableRef(
               variable --> <
diff --git a/res/v3x/parsing/instruction/v_1/ast.golden.txt b/res/v3x/tests/integration/instruction/v_1/ast.golden.txt
similarity index 59%
rename from res/v3x/parsing/instruction/v_1/ast.golden.txt
rename to res/v3x/tests/integration/instruction/v_1/ast.golden.txt
index 4d658a6d3..d81a4101f 100644
--- a/res/v3x/parsing/instruction/v_1/ast.golden.txt
+++ b/res/v3x/tests/integration/instruction/v_1/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..2
-          name: <
-            Identifier(
-              name: v
+        GateInstruction( # input.cq:3:1..2
+          gate: <
+            Gate( # input.cq:3:1..2
+              name: <
+                Identifier(
+                  name: v
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/instruction/v_1/input.cq b/res/v3x/tests/integration/instruction/v_1/input.cq
similarity index 100%
rename from res/v3x/parsing/instruction/v_1/input.cq
rename to res/v3x/tests/integration/instruction/v_1/input.cq
diff --git a/res/v3x/parsing/instruction/v_1/semantic.3.0.golden.txt b/res/v3x/tests/integration/instruction/v_1/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/instruction/v_1/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/instruction/v_1/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/ast.golden.txt
similarity index 72%
rename from res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/ast.golden.txt
index caac5e48d..fd9ce1153 100644
--- a/res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/ast.golden.txt
@@ -30,10 +30,10 @@ Program(
           >
           annotations: []
         )
-        Variable( # input.cq:4:5..7
+        Variable( # input.cq:4:5..6
           name: <
             Identifier(
-              name: b0
+              name: b
             )
           >
           typ: <
@@ -48,20 +48,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:6..13
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..3
-              name: b0
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:14..15
-              name: q
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: b
+                )
+                Identifier( # input.cq:6:13..14
+                  name: q
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/input.cq b/res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/input.cq
similarity index 51%
rename from res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/input.cq
rename to res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/input.cq
index d890b828c..7ae29a60e 100644
--- a/res/v3x/parsing/measure_instruction/array_of_3_q_to_b0/input.cq
+++ b/res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/input.cq
@@ -1,6 +1,6 @@
 version 3
 
 qubit[3] q
-bit b0
+bit b
 
-b0 = measure q
+b = measure q
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_9_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_9_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_3_q_to_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/ast.golden.txt
similarity index 78%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/ast.golden.txt
index a7666dd5e..a03a962f1 100644
--- a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/ast.golden.txt
@@ -52,20 +52,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:13..14
-              name: q
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: b
+                )
+                Identifier( # input.cq:6:13..14
+                  name: q
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/input.cq b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/input.cq
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/semantic.3.0.golden.txt
similarity index 97%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/semantic.3.0.golden.txt
index 91cd08af0..7569fe26c 100644
--- a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_5_b/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_5_b/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
diff --git a/res/v3x/parsing/measure_instruction/array_of_9_q_to_array_of_5_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_9_b/ast.golden.txt
similarity index 78%
rename from res/v3x/parsing/measure_instruction/array_of_9_q_to_array_of_5_b/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_9_b/ast.golden.txt
index be97db2d2..0226fa9f2 100644
--- a/res/v3x/parsing/measure_instruction/array_of_9_q_to_array_of_5_b/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_9_b/ast.golden.txt
@@ -52,20 +52,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:13..14
-              name: q
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: b
+                )
+                Identifier( # input.cq:6:13..14
+                  name: q
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_9_b/input.cq b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_9_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_9_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_9_b/input.cq
diff --git a/res/v3x/parsing/measure_instruction/array_of_9_q_to_array_of_5_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_9_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/array_of_9_q_to_array_of_5_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_array_of_9_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/ast.golden.txt
similarity index 58%
rename from res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/ast.golden.txt
index dd96ab461..84e2d52bb 100644
--- a/res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/ast.golden.txt
@@ -23,7 +23,7 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 9
+                  value: 5
                 )
               >
             )
@@ -45,49 +45,51 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 5
+                  value: 9
                 )
               >
             )
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:10..17
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Index( # input.cq:6:13..14
-              expr: <
-                Identifier(
-                  name: q
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexRange(
-                      first: <
-                        IntegerLiteral( # input.cq:6:15..16
-                          value: 0
-                        )
-                      >
-                      last: <
-                        IntegerLiteral( # input.cq:6:17..18
-                          value: 4
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:6:1..2
+                  expr: <
+                    Identifier(
+                      name: b
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:6:3..4
+                              value: 0
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:6:5..6
+                              value: 4
+                            )
+                          >
                         )
-                      >
+                      ]
                     )
-                  ]
+                  >
                 )
-              >
+                Identifier( # input.cq:6:18..19
+                  name: q
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/input.cq b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/input.cq
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/semantic.3.0.golden.txt
index 56523e286..3136ed2f6 100644
--- a/res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_0_4/semantic.3.0.golden.txt
@@ -9,29 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
-            VariableRef(
+            IndexRef(
               variable --> <
                 Variable(
                   name: b
                   typ: <
                     BitArray(
-                      size: 5
-                    )
-                  >
-                  annotations: []
-                )
-              >
-            )
-            IndexRef(
-              variable --> <
-                Variable(
-                  name: q
-                  typ: <
-                    QubitArray(
                       size: 9
                     )
                   >
@@ -56,6 +43,19 @@ Program(
                 )
               ]
             )
+            VariableRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    QubitArray(
+                      size: 5
+                    )
+                  >
+                  annotations: []
+                )
+              >
+            )
           ]
           annotations: []
         )
@@ -67,7 +67,7 @@ Program(
       name: q
       typ: <
         QubitArray(
-          size: 9
+          size: 5
         )
       >
       annotations: []
@@ -76,7 +76,7 @@ Program(
       name: b
       typ: <
         BitArray(
-          size: 5
+          size: 9
         )
       >
       annotations: []
diff --git a/res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/ast.golden.txt
similarity index 58%
rename from res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/ast.golden.txt
index 5e5752249..55c48230c 100644
--- a/res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/ast.golden.txt
@@ -23,7 +23,7 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 9
+                  value: 5
                 )
               >
             )
@@ -45,49 +45,51 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 5
+                  value: 9
                 )
               >
             )
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:10..17
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Index( # input.cq:6:13..14
-              expr: <
-                Identifier(
-                  name: q
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexRange(
-                      first: <
-                        IntegerLiteral( # input.cq:6:15..16
-                          value: 2
-                        )
-                      >
-                      last: <
-                        IntegerLiteral( # input.cq:6:17..18
-                          value: 6
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:6:1..2
+                  expr: <
+                    Identifier(
+                      name: b
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:6:3..4
+                              value: 2
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:6:5..6
+                              value: 6
+                            )
+                          >
                         )
-                      >
+                      ]
                     )
-                  ]
+                  >
                 )
-              >
+                Identifier( # input.cq:6:18..19
+                  name: q
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/input.cq b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/input.cq
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/semantic.3.0.golden.txt
index f2afacb22..dc854ac6d 100644
--- a/res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_5_q_to_b_range_2_6/semantic.3.0.golden.txt
@@ -9,29 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
-            VariableRef(
+            IndexRef(
               variable --> <
                 Variable(
                   name: b
                   typ: <
                     BitArray(
-                      size: 5
-                    )
-                  >
-                  annotations: []
-                )
-              >
-            )
-            IndexRef(
-              variable --> <
-                Variable(
-                  name: q
-                  typ: <
-                    QubitArray(
                       size: 9
                     )
                   >
@@ -56,6 +43,19 @@ Program(
                 )
               ]
             )
+            VariableRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    QubitArray(
+                      size: 5
+                    )
+                  >
+                  annotations: []
+                )
+              >
+            )
           ]
           annotations: []
         )
@@ -67,7 +67,7 @@ Program(
       name: q
       typ: <
         QubitArray(
-          size: 9
+          size: 5
         )
       >
       annotations: []
@@ -76,7 +76,7 @@ Program(
       name: b
       typ: <
         BitArray(
-          size: 5
+          size: 9
         )
       >
       annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_9_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_9_q_to_array_of_5_b/ast.golden.txt
similarity index 78%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_9_b/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_9_q_to_array_of_5_b/ast.golden.txt
index be97db2d2..0226fa9f2 100644
--- a/res/v3x/parsing/measure_instruction/array_of_5_q_to_array_of_9_b/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/array_of_9_q_to_array_of_5_b/ast.golden.txt
@@ -52,20 +52,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:13..14
-              name: q
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: b
+                )
+                Identifier( # input.cq:6:13..14
+                  name: q
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_9_q_to_array_of_5_b/input.cq b/res/v3x/tests/integration/measure_instruction/array_of_9_q_to_array_of_5_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/array_of_9_q_to_array_of_5_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/array_of_9_q_to_array_of_5_b/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q0_to_array_of_3_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/array_of_9_q_to_array_of_5_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q0_to_array_of_3_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/array_of_9_q_to_array_of_5_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/b_to_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/b_to_b/ast.golden.txt
similarity index 66%
rename from res/v3x/parsing/measure_instruction/b_to_b/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/b_to_b/ast.golden.txt
index 65ff9d579..4961be78e 100644
--- a/res/v3x/parsing/measure_instruction/b_to_b/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/b_to_b/ast.golden.txt
@@ -26,20 +26,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:5:5..12
+        NonGateInstruction( # input.cq:5:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:5:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:5:13..14
-              name: b
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:5:1..2
+                  name: b
+                )
+                Identifier( # input.cq:5:13..14
+                  name: b
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/b_to_b/input.cq b/res/v3x/tests/integration/measure_instruction/b_to_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/b_to_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/b_to_b/input.cq
diff --git a/res/v3x/parsing/measure_instruction/b_to_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/b_to_b/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/b_to_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/b_to_b/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/b_to_q/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/b_to_q/ast.golden.txt
similarity index 75%
rename from res/v3x/parsing/measure_instruction/b_to_q/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/b_to_q/ast.golden.txt
index 7d8667293..5a99f557a 100644
--- a/res/v3x/parsing/measure_instruction/b_to_q/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/b_to_q/ast.golden.txt
@@ -44,20 +44,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: q
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:13..14
-              name: b
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: q
+                )
+                Identifier( # input.cq:6:13..14
+                  name: b
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/b_to_q/input.cq b/res/v3x/tests/integration/measure_instruction/b_to_q/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/b_to_q/input.cq
rename to res/v3x/tests/integration/measure_instruction/b_to_q/input.cq
diff --git a/res/v3x/parsing/measure_instruction/b_to_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/b_to_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/b_to_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/b_to_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/bit_and_qubit/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/bit_and_qubit/ast.golden.txt
similarity index 75%
rename from res/v3x/parsing/measure_instruction/bit_and_qubit/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/bit_and_qubit/ast.golden.txt
index c4464593b..ff4e3f92f 100644
--- a/res/v3x/parsing/measure_instruction/bit_and_qubit/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/bit_and_qubit/ast.golden.txt
@@ -44,20 +44,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:13..14
-              name: q
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: b
+                )
+                Identifier( # input.cq:6:13..14
+                  name: q
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/bit_and_qubit/input.cq b/res/v3x/tests/integration/measure_instruction/bit_and_qubit/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/bit_and_qubit/input.cq
rename to res/v3x/tests/integration/measure_instruction/bit_and_qubit/input.cq
diff --git a/res/v3x/parsing/measure_instruction/bit_and_qubit/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/bit_and_qubit/semantic.3.0.golden.txt
similarity index 97%
rename from res/v3x/parsing/measure_instruction/bit_and_qubit/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/bit_and_qubit/semantic.3.0.golden.txt
index 7a74ae0ab..3d3eff9b5 100644
--- a/res/v3x/parsing/measure_instruction/bit_and_qubit/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/bit_and_qubit/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit, qubit)
           name: measure
           operands: [
diff --git a/res/v3x/parsing/measure_instruction/no_bit/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/no_bit/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/no_bit/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/no_bit/ast.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/no_bit/input.cq b/res/v3x/tests/integration/measure_instruction/no_bit/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/no_bit/input.cq
rename to res/v3x/tests/integration/measure_instruction/no_bit/input.cq
diff --git a/res/v3x/parsing/measure_instruction/no_bit_no_qubit/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/no_bit_no_qubit/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/no_bit_no_qubit/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/no_bit_no_qubit/ast.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/no_bit_no_qubit/input.cq b/res/v3x/tests/integration/measure_instruction/no_bit_no_qubit/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/no_bit_no_qubit/input.cq
rename to res/v3x/tests/integration/measure_instruction/no_bit_no_qubit/input.cq
diff --git a/res/v3x/parsing/measure_instruction/no_qubit/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/no_qubit/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/no_qubit/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/no_qubit/ast.golden.txt
diff --git a/res/v3x/parsing/measure_instruction/no_qubit/input.cq b/res/v3x/tests/integration/measure_instruction/no_qubit/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/no_qubit/input.cq
rename to res/v3x/tests/integration/measure_instruction/no_qubit/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q0_to_array_of_3_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/ast.golden.txt
similarity index 76%
rename from res/v3x/parsing/measure_instruction/q0_to_array_of_3_b/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/ast.golden.txt
index 684a5fe48..1defdd2c6 100644
--- a/res/v3x/parsing/measure_instruction/q0_to_array_of_3_b/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/ast.golden.txt
@@ -48,20 +48,22 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:5..12
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..2
-              name: b
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:13..15
-              name: q0
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: b
+                )
+                Identifier( # input.cq:6:13..15
+                  name: q0
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/q0_to_array_of_3_b/input.cq b/res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q0_to_array_of_3_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/input.cq
diff --git a/res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/semantic.3.0.golden.txt
new file mode 100644
index 000000000..628e3d7ec
--- /dev/null
+++ b/res/v3x/tests/integration/measure_instruction/q0_to_array_of_3_b/semantic.3.0.golden.txt
@@ -0,0 +1,2 @@
+ERROR
+Error at input.cq:6:5..12: qubit and bit indices have different sizes
diff --git a/res/v3x/parsing/measure_instruction/q0_to_b_0/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q0_to_b_0/ast.golden.txt
similarity index 61%
rename from res/v3x/parsing/measure_instruction/q0_to_b_0/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q0_to_b_0/ast.golden.txt
index 12b8c8746..363d304d6 100644
--- a/res/v3x/parsing/measure_instruction/q0_to_b_0/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q0_to_b_0/ast.golden.txt
@@ -48,37 +48,39 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:8..15
+        NonGateInstruction( # input.cq:6:8..15
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Index( # input.cq:6:1..2
-              expr: <
-                Identifier(
-                  name: b
-                )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:3..4
-                          value: 0
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:6:1..2
+                  expr: <
+                    Identifier(
+                      name: b
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:3..4
+                              value: 0
+                            )
+                          >
                         )
-                      >
+                      ]
                     )
-                  ]
+                  >
                 )
-              >
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:16..18
-              name: q0
+                Identifier( # input.cq:6:16..18
+                  name: q0
+                )
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/q0_to_b_0/input.cq b/res/v3x/tests/integration/measure_instruction/q0_to_b_0/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q0_to_b_0/input.cq
rename to res/v3x/tests/integration/measure_instruction/q0_to_b_0/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q0_to_b_0/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q0_to_b_0/semantic.3.0.golden.txt
similarity index 97%
rename from res/v3x/parsing/measure_instruction/q0_to_b_0/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q0_to_b_0/semantic.3.0.golden.txt
index 1adf77b83..83e851f31 100644
--- a/res/v3x/parsing/measure_instruction/q0_to_b_0/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q0_to_b_0/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit, qubit)
           name: measure
           operands: [
diff --git a/res/v3x/parsing/measure_instruction/q_0_to_b0/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_0_to_b0/ast.golden.txt
similarity index 61%
rename from res/v3x/parsing/measure_instruction/q_0_to_b0/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_0_to_b0/ast.golden.txt
index 4c5332d4f..f47bcd114 100644
--- a/res/v3x/parsing/measure_instruction/q_0_to_b0/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_0_to_b0/ast.golden.txt
@@ -48,37 +48,39 @@ Program(
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:6..13
+        NonGateInstruction( # input.cq:6:6..13
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Identifier( # input.cq:6:1..3
-              name: b0
-            )
-          >
-          rhs: <
-            Index( # input.cq:6:14..15
-              expr: <
-                Identifier(
-                  name: q
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..3
+                  name: b0
                 )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexItem(
-                      index: <
-                        IntegerLiteral( # input.cq:6:16..17
-                          value: 0
+                Index( # input.cq:6:14..15
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:16..17
+                              value: 0
+                            )
+                          >
                         )
-                      >
+                      ]
                     )
-                  ]
+                  >
                 )
-              >
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/q_0_to_b0/input.cq b/res/v3x/tests/integration/measure_instruction/q_0_to_b0/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_0_to_b0/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_0_to_b0/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_0_to_b0/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_0_to_b0/semantic.3.0.golden.txt
similarity index 97%
rename from res/v3x/parsing/measure_instruction/q_0_to_b0/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_0_to_b0/semantic.3.0.golden.txt
index f99a00894..d9bc69b10 100644
--- a/res/v3x/parsing/measure_instruction/q_0_to_b0/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_0_to_b0/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit, qubit)
           name: measure
           operands: [
diff --git a/res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/ast.golden.txt
new file mode 100644
index 000000000..acdb82ddd
--- /dev/null
+++ b/res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/ast.golden.txt
@@ -0,0 +1,124 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:10..11
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..9
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 9
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        Variable( # input.cq:4:8..9
+          name: <
+            Identifier(
+              name: b
+            )
+          >
+          typ: <
+            Type( # input.cq:4:1..7
+              name: <
+                Keyword(
+                  name: bit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 5
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        NonGateInstruction( # input.cq:6:5..12
+          name: <
+            Keyword(
+              name: measure
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
+                  name: b
+                )
+                Index( # input.cq:6:13..14
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:15..16
+                              value: 1
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:18..19
+                              value: 3
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:21..22
+                              value: 4
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:24..25
+                              value: 5
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:27..28
+                              value: 7
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/input.cq b/res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/semantic.3.0.golden.txt
index 959feb632..7dc466a12 100644
--- a/res/v3x/parsing/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_1_3_4_5_7_to_array_of_5_b/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
diff --git a/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/ast.golden.txt
new file mode 100644
index 000000000..fe7f17a62
--- /dev/null
+++ b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/ast.golden.txt
@@ -0,0 +1,127 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:10..11
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..9
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 5
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        Variable( # input.cq:4:8..9
+          name: <
+            Identifier(
+              name: b
+            )
+          >
+          typ: <
+            Type( # input.cq:4:1..7
+              name: <
+                Keyword(
+                  name: bit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 5
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        NonGateInstruction( # input.cq:6:11..18
+          name: <
+            Keyword(
+              name: measure
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:6:1..2
+                  expr: <
+                    Identifier(
+                      name: b
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:3..4
+                              value: 1
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:6..7
+                              value: 3
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+                Index( # input.cq:6:19..20
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:21..22
+                              value: 2
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:24..25
+                              value: 4
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/input.cq b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/semantic.3.0.golden.txt
index 9f3c2b079..fd4ef2f9d 100644
--- a/res/v3x/parsing/measure_instruction/q_2_4_to_b_1_3/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_1_3/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
diff --git a/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/ast.golden.txt
new file mode 100644
index 000000000..7b72c926c
--- /dev/null
+++ b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/ast.golden.txt
@@ -0,0 +1,125 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:10..11
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..9
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 5
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        Variable( # input.cq:4:8..9
+          name: <
+            Identifier(
+              name: b
+            )
+          >
+          typ: <
+            Type( # input.cq:4:1..7
+              name: <
+                Keyword(
+                  name: bit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 5
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        NonGateInstruction( # input.cq:6:10..17
+          name: <
+            Keyword(
+              name: measure
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:6:1..2
+                  expr: <
+                    Identifier(
+                      name: b
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:6:3..4
+                              value: 1
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:6:5..6
+                              value: 2
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+                Index( # input.cq:6:18..19
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:20..21
+                              value: 2
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:23..24
+                              value: 4
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/input.cq b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/semantic.3.0.golden.txt
index 55f69ea3e..7f9388b03 100644
--- a/res/v3x/parsing/measure_instruction/q_2_4_to_b_range_1_2/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_2_4_to_b_range_1_2/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/ast.golden.txt
similarity index 58%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/ast.golden.txt
index e136dc334..ab083d9fa 100644
--- a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/ast.golden.txt
@@ -23,7 +23,7 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 5
+                  value: 9
                 )
               >
             )
@@ -45,49 +45,51 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 9
+                  value: 5
                 )
               >
             )
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:10..17
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Index( # input.cq:6:1..2
-              expr: <
-                Identifier(
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
                   name: b
                 )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexRange(
-                      first: <
-                        IntegerLiteral( # input.cq:6:3..4
-                          value: 0
-                        )
-                      >
-                      last: <
-                        IntegerLiteral( # input.cq:6:5..6
-                          value: 4
+                Index( # input.cq:6:13..14
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:6:15..16
+                              value: 0
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:6:17..18
+                              value: 4
+                            )
+                          >
                         )
-                      >
+                      ]
                     )
-                  ]
+                  >
                 )
-              >
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:18..19
-              name: q
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/input.cq b/res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_range_0_4_to_array_of_5_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/input.cq
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/semantic.3.0.golden.txt
index f001937e4..2434743ce 100644
--- a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_0_4/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_range_0_4_to_array_of_5_b/semantic.3.0.golden.txt
@@ -9,16 +9,29 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
-            IndexRef(
+            VariableRef(
               variable --> <
                 Variable(
                   name: b
                   typ: <
                     BitArray(
+                      size: 5
+                    )
+                  >
+                  annotations: []
+                )
+              >
+            )
+            IndexRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    QubitArray(
                       size: 9
                     )
                   >
@@ -43,19 +56,6 @@ Program(
                 )
               ]
             )
-            VariableRef(
-              variable --> <
-                Variable(
-                  name: q
-                  typ: <
-                    QubitArray(
-                      size: 5
-                    )
-                  >
-                  annotations: []
-                )
-              >
-            )
           ]
           annotations: []
         )
@@ -67,7 +67,7 @@ Program(
       name: q
       typ: <
         QubitArray(
-          size: 5
+          size: 9
         )
       >
       annotations: []
@@ -76,7 +76,7 @@ Program(
       name: b
       typ: <
         BitArray(
-          size: 9
+          size: 5
         )
       >
       annotations: []
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/ast.golden.txt
similarity index 58%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/ast.golden.txt
index bcacc4cc5..b110ee8ce 100644
--- a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/ast.golden.txt
@@ -23,7 +23,7 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 5
+                  value: 9
                 )
               >
             )
@@ -45,49 +45,51 @@ Program(
               >
               size: <
                 IntegerLiteral(
-                  value: 9
+                  value: 5
                 )
               >
             )
           >
           annotations: []
         )
-        MeasureInstruction( # input.cq:6:10..17
+        NonGateInstruction( # input.cq:6:5..12
           name: <
-            Identifier(
+            Keyword(
               name: measure
             )
           >
-          lhs: <
-            Index( # input.cq:6:1..2
-              expr: <
-                Identifier(
+          operands: <
+            ExpressionList(
+              items: [
+                Identifier( # input.cq:6:1..2
                   name: b
                 )
-              >
-              indices: <
-                IndexList(
-                  items: [
-                    IndexRange(
-                      first: <
-                        IntegerLiteral( # input.cq:6:3..4
-                          value: 2
-                        )
-                      >
-                      last: <
-                        IntegerLiteral( # input.cq:6:5..6
-                          value: 6
+                Index( # input.cq:6:13..14
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:6:15..16
+                              value: 2
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:6:17..18
+                              value: 6
+                            )
+                          >
                         )
-                      >
+                      ]
                     )
-                  ]
+                  >
                 )
-              >
-            )
-          >
-          rhs: <
-            Identifier( # input.cq:6:18..19
-              name: q
+              ]
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/input.cq b/res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_range_2_6_to_array_of_5_b/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/input.cq
diff --git a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/semantic.3.0.golden.txt
index 378c7561a..d9ef8fed6 100644
--- a/res/v3x/parsing/measure_instruction/array_of_5_q_to_b_range_2_6/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_range_2_6_to_array_of_5_b/semantic.3.0.golden.txt
@@ -9,16 +9,29 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
-            IndexRef(
+            VariableRef(
               variable --> <
                 Variable(
                   name: b
                   typ: <
                     BitArray(
+                      size: 5
+                    )
+                  >
+                  annotations: []
+                )
+              >
+            )
+            IndexRef(
+              variable --> <
+                Variable(
+                  name: q
+                  typ: <
+                    QubitArray(
                       size: 9
                     )
                   >
@@ -43,19 +56,6 @@ Program(
                 )
               ]
             )
-            VariableRef(
-              variable --> <
-                Variable(
-                  name: q
-                  typ: <
-                    QubitArray(
-                      size: 5
-                    )
-                  >
-                  annotations: []
-                )
-              >
-            )
           ]
           annotations: []
         )
@@ -67,7 +67,7 @@ Program(
       name: q
       typ: <
         QubitArray(
-          size: 5
+          size: 9
         )
       >
       annotations: []
@@ -76,7 +76,7 @@ Program(
       name: b
       typ: <
         BitArray(
-          size: 9
+          size: 5
         )
       >
       annotations: []
diff --git a/res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/ast.golden.txt
new file mode 100644
index 000000000..c4e95b8ae
--- /dev/null
+++ b/res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/ast.golden.txt
@@ -0,0 +1,125 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:10..11
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..9
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 5
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        Variable( # input.cq:4:8..9
+          name: <
+            Identifier(
+              name: b
+            )
+          >
+          typ: <
+            Type( # input.cq:4:1..7
+              name: <
+                Keyword(
+                  name: bit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 5
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        NonGateInstruction( # input.cq:6:11..18
+          name: <
+            Keyword(
+              name: measure
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:6:1..2
+                  expr: <
+                    Identifier(
+                      name: b
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:3..4
+                              value: 1
+                            )
+                          >
+                        )
+                        IndexItem(
+                          index: <
+                            IntegerLiteral( # input.cq:6:6..7
+                              value: 3
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+                Index( # input.cq:6:19..20
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:6:21..22
+                              value: 3
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:6:23..24
+                              value: 4
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/input.cq b/res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/semantic.3.0.golden.txt
similarity index 98%
rename from res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/semantic.3.0.golden.txt
index 123116c0a..e841dc1a1 100644
--- a/res/v3x/parsing/measure_instruction/q_range_3_4_to_b_1_3/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_range_3_4_to_b_1_3/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: measure(bit array, qubit array)
           name: measure
           operands: [
diff --git a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_to_5/ast.golden.txt
similarity index 78%
rename from res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_to_5/ast.golden.txt
index 4a07c1f66..5f987819d 100644
--- a/res/v3x/parsing/eu_pi_tau/qubit_q__Rx_eu_q/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_to_5/ast.golden.txt
@@ -26,20 +26,20 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..3
+        NonGateInstruction( # input.cq:5:5..12
           name: <
-            Identifier(
-              name: Rx
+            Keyword(
+              name: measure
             )
           >
           operands: <
             ExpressionList(
               items: [
-                Identifier( # input.cq:4:8..9
-                  name: q
+                IntegerLiteral( # input.cq:5:1..2
+                  value: 5
                 )
-                Identifier( # input.cq:4:4..6
-                  name: eu
+                Identifier( # input.cq:5:13..14
+                  name: q
                 )
               ]
             )
diff --git a/res/v3x/parsing/measure_instruction/q_to_5/input.cq b/res/v3x/tests/integration/measure_instruction/q_to_5/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_to_5/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_to_5/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_to_5/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_to_5/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_to_5/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_to_5/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/case_sensitiveness/pi_uppercase/ast.golden.txt b/res/v3x/tests/integration/measure_instruction/q_to_q/ast.golden.txt
similarity index 78%
rename from res/v3x/parsing/case_sensitiveness/pi_uppercase/ast.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_to_q/ast.golden.txt
index 0d1894080..59479b14f 100644
--- a/res/v3x/parsing/case_sensitiveness/pi_uppercase/ast.golden.txt
+++ b/res/v3x/tests/integration/measure_instruction/q_to_q/ast.golden.txt
@@ -26,20 +26,20 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..3
+        NonGateInstruction( # input.cq:5:5..12
           name: <
-            Identifier(
-              name: Rx
+            Keyword(
+              name: measure
             )
           >
           operands: <
             ExpressionList(
               items: [
-                Identifier( # input.cq:4:8..9
+                Identifier( # input.cq:5:1..2
                   name: q
                 )
-                Identifier( # input.cq:4:4..6
-                  name: PI
+                Identifier( # input.cq:5:13..14
+                  name: q
                 )
               ]
             )
diff --git a/res/v3x/parsing/measure_instruction/q_to_q/input.cq b/res/v3x/tests/integration/measure_instruction/q_to_q/input.cq
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_to_q/input.cq
rename to res/v3x/tests/integration/measure_instruction/q_to_q/input.cq
diff --git a/res/v3x/parsing/measure_instruction/q_to_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/measure_instruction/q_to_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/measure_instruction/q_to_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/measure_instruction/q_to_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/newline_or_semicolon/qubit_newline_q/ast.golden.txt b/res/v3x/tests/integration/newline_or_semicolon/qubit_newline_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/qubit_newline_q/ast.golden.txt
rename to res/v3x/tests/integration/newline_or_semicolon/qubit_newline_q/ast.golden.txt
diff --git a/res/v3x/parsing/newline_or_semicolon/qubit_newline_q/input.cq b/res/v3x/tests/integration/newline_or_semicolon/qubit_newline_q/input.cq
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/qubit_newline_q/input.cq
rename to res/v3x/tests/integration/newline_or_semicolon/qubit_newline_q/input.cq
diff --git a/res/v3x/parsing/newline_or_semicolon/version_3_newline_qubit_q/ast.golden.txt b/res/v3x/tests/integration/newline_or_semicolon/version_3_newline_qubit_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/version_3_newline_qubit_q/ast.golden.txt
rename to res/v3x/tests/integration/newline_or_semicolon/version_3_newline_qubit_q/ast.golden.txt
diff --git a/res/v3x/parsing/newline_or_semicolon/version_3_newline_qubit_q/input.cq b/res/v3x/tests/integration/newline_or_semicolon/version_3_newline_qubit_q/input.cq
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/version_3_newline_qubit_q/input.cq
rename to res/v3x/tests/integration/newline_or_semicolon/version_3_newline_qubit_q/input.cq
diff --git a/res/v3x/parsing/newline_or_semicolon/version_3_newline_qubit_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/newline_or_semicolon/version_3_newline_qubit_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/version_3_newline_qubit_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/newline_or_semicolon/version_3_newline_qubit_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/newline_or_semicolon/version_3_semicolon_qubit_q/ast.golden.txt b/res/v3x/tests/integration/newline_or_semicolon/version_3_semicolon_qubit_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/version_3_semicolon_qubit_q/ast.golden.txt
rename to res/v3x/tests/integration/newline_or_semicolon/version_3_semicolon_qubit_q/ast.golden.txt
diff --git a/res/v3x/parsing/newline_or_semicolon/version_3_semicolon_qubit_q/input.cq b/res/v3x/tests/integration/newline_or_semicolon/version_3_semicolon_qubit_q/input.cq
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/version_3_semicolon_qubit_q/input.cq
rename to res/v3x/tests/integration/newline_or_semicolon/version_3_semicolon_qubit_q/input.cq
diff --git a/res/v3x/parsing/newline_or_semicolon/version_3_semicolon_qubit_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/newline_or_semicolon/version_3_semicolon_qubit_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/newline_or_semicolon/version_3_semicolon_qubit_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/newline_or_semicolon/version_3_semicolon_qubit_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/program/bad_token_after_version/ast.golden.txt b/res/v3x/tests/integration/program/bad_token_after_version/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/program/bad_token_after_version/ast.golden.txt
rename to res/v3x/tests/integration/program/bad_token_after_version/ast.golden.txt
diff --git a/res/v3x/parsing/program/bad_token_after_version/input.cq b/res/v3x/tests/integration/program/bad_token_after_version/input.cq
similarity index 100%
rename from res/v3x/parsing/program/bad_token_after_version/input.cq
rename to res/v3x/tests/integration/program/bad_token_after_version/input.cq
diff --git a/res/v3x/parsing/program/newlines_before_version/ast.golden.txt b/res/v3x/tests/integration/program/newlines_before_version/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/program/newlines_before_version/ast.golden.txt
rename to res/v3x/tests/integration/program/newlines_before_version/ast.golden.txt
diff --git a/res/v3x/parsing/program/newlines_before_version/input.cq b/res/v3x/tests/integration/program/newlines_before_version/input.cq
similarity index 100%
rename from res/v3x/parsing/program/newlines_before_version/input.cq
rename to res/v3x/tests/integration/program/newlines_before_version/input.cq
diff --git a/res/v3x/parsing/program/newlines_before_version/semantic.3.0.golden.txt b/res/v3x/tests/integration/program/newlines_before_version/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/program/newlines_before_version/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/program/newlines_before_version/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/program/no_statement_separators_after_last_statement/ast.golden.txt b/res/v3x/tests/integration/program/no_statement_separators_after_last_statement/ast.golden.txt
similarity index 73%
rename from res/v3x/parsing/program/no_statement_separators_after_last_statement/ast.golden.txt
rename to res/v3x/tests/integration/program/no_statement_separators_after_last_statement/ast.golden.txt
index 7e82c99b9..f5b4bd489 100644
--- a/res/v3x/parsing/program/no_statement_separators_after_last_statement/ast.golden.txt
+++ b/res/v3x/tests/integration/program/no_statement_separators_after_last_statement/ast.golden.txt
@@ -26,10 +26,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:3:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:3:1..2
+          gate: <
+            Gate( # input.cq:3:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/program/no_statement_separators_after_last_statement/input.cq b/res/v3x/tests/integration/program/no_statement_separators_after_last_statement/input.cq
similarity index 100%
rename from res/v3x/parsing/program/no_statement_separators_after_last_statement/input.cq
rename to res/v3x/tests/integration/program/no_statement_separators_after_last_statement/input.cq
diff --git a/res/v3x/parsing/program/no_statement_separators_after_last_statement/semantic.3.0.golden.txt b/res/v3x/tests/integration/program/no_statement_separators_after_last_statement/semantic.3.0.golden.txt
similarity index 79%
rename from res/v3x/parsing/program/no_statement_separators_after_last_statement/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/program/no_statement_separators_after_last_statement/semantic.3.0.golden.txt
index c1e59b817..cd35a7fa3 100644
--- a/res/v3x/parsing/program/no_statement_separators_after_last_statement/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/program/no_statement_separators_after_last_statement/semantic.3.0.golden.txt
@@ -9,9 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        GateInstruction(
           instruction_ref: X(qubit)
-          name: X
+          gate: <
+            Gate(
+              name: X
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             VariableRef(
               variable --> <
diff --git a/res/v3x/parsing/program/no_statement_separators_after_version/ast.golden.txt b/res/v3x/tests/integration/program/no_statement_separators_after_version/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/program/no_statement_separators_after_version/ast.golden.txt
rename to res/v3x/tests/integration/program/no_statement_separators_after_version/ast.golden.txt
diff --git a/res/v3x/parsing/program/no_statement_separators_after_version/input.cq b/res/v3x/tests/integration/program/no_statement_separators_after_version/input.cq
similarity index 100%
rename from res/v3x/parsing/program/no_statement_separators_after_version/input.cq
rename to res/v3x/tests/integration/program/no_statement_separators_after_version/input.cq
diff --git a/res/v3x/parsing/program/no_statement_separators_after_version/semantic.3.0.golden.txt b/res/v3x/tests/integration/program/no_statement_separators_after_version/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/program/no_statement_separators_after_version/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/program/no_statement_separators_after_version/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/program/statement_separators_after_statements/ast.golden.txt b/res/v3x/tests/integration/program/statement_separators_after_statements/ast.golden.txt
similarity index 73%
rename from res/v3x/parsing/program/statement_separators_after_statements/ast.golden.txt
rename to res/v3x/tests/integration/program/statement_separators_after_statements/ast.golden.txt
index 7e82c99b9..f5b4bd489 100644
--- a/res/v3x/parsing/program/statement_separators_after_statements/ast.golden.txt
+++ b/res/v3x/tests/integration/program/statement_separators_after_statements/ast.golden.txt
@@ -26,10 +26,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:3:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:3:1..2
+          gate: <
+            Gate( # input.cq:3:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/program/statement_separators_after_statements/input.cq b/res/v3x/tests/integration/program/statement_separators_after_statements/input.cq
similarity index 100%
rename from res/v3x/parsing/program/statement_separators_after_statements/input.cq
rename to res/v3x/tests/integration/program/statement_separators_after_statements/input.cq
diff --git a/res/v3x/parsing/program/statement_separators_after_statements/semantic.3.0.golden.txt b/res/v3x/tests/integration/program/statement_separators_after_statements/semantic.3.0.golden.txt
similarity index 79%
rename from res/v3x/parsing/program/statement_separators_after_statements/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/program/statement_separators_after_statements/semantic.3.0.golden.txt
index c1e59b817..cd35a7fa3 100644
--- a/res/v3x/parsing/program/statement_separators_after_statements/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/program/statement_separators_after_statements/semantic.3.0.golden.txt
@@ -9,9 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        GateInstruction(
           instruction_ref: X(qubit)
-          name: X
+          gate: <
+            Gate(
+              name: X
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             VariableRef(
               variable --> <
diff --git a/res/v3x/parsing/program/statement_separators_before_eof/ast.golden.txt b/res/v3x/tests/integration/program/statement_separators_before_eof/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/program/statement_separators_before_eof/ast.golden.txt
rename to res/v3x/tests/integration/program/statement_separators_before_eof/ast.golden.txt
diff --git a/res/v3x/parsing/program/statement_separators_before_eof/input.cq b/res/v3x/tests/integration/program/statement_separators_before_eof/input.cq
similarity index 100%
rename from res/v3x/parsing/program/statement_separators_before_eof/input.cq
rename to res/v3x/tests/integration/program/statement_separators_before_eof/input.cq
diff --git a/res/v3x/parsing/program/statement_separators_before_eof/semantic.3.0.golden.txt b/res/v3x/tests/integration/program/statement_separators_before_eof/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/program/statement_separators_before_eof/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/program/statement_separators_before_eof/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/program/statement_separators_between_statements/ast.golden.txt b/res/v3x/tests/integration/program/statement_separators_between_statements/ast.golden.txt
similarity index 73%
rename from res/v3x/parsing/program/statement_separators_between_statements/ast.golden.txt
rename to res/v3x/tests/integration/program/statement_separators_between_statements/ast.golden.txt
index 5fce185b3..4a21f49e3 100644
--- a/res/v3x/parsing/program/statement_separators_between_statements/ast.golden.txt
+++ b/res/v3x/tests/integration/program/statement_separators_between_statements/ast.golden.txt
@@ -26,10 +26,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..2
-          name: <
-            Identifier(
-              name: X
+        GateInstruction( # input.cq:4:1..2
+          gate: <
+            Gate( # input.cq:4:1..2
+              name: <
+                Identifier(
+                  name: X
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/program/statement_separators_between_statements/input.cq b/res/v3x/tests/integration/program/statement_separators_between_statements/input.cq
similarity index 100%
rename from res/v3x/parsing/program/statement_separators_between_statements/input.cq
rename to res/v3x/tests/integration/program/statement_separators_between_statements/input.cq
diff --git a/res/v3x/parsing/program/statement_separators_between_statements/semantic.3.0.golden.txt b/res/v3x/tests/integration/program/statement_separators_between_statements/semantic.3.0.golden.txt
similarity index 79%
rename from res/v3x/parsing/program/statement_separators_between_statements/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/program/statement_separators_between_statements/semantic.3.0.golden.txt
index c1e59b817..cd35a7fa3 100644
--- a/res/v3x/parsing/program/statement_separators_between_statements/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/program/statement_separators_between_statements/semantic.3.0.golden.txt
@@ -9,9 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        GateInstruction(
           instruction_ref: X(qubit)
-          name: X
+          gate: <
+            Gate(
+              name: X
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             VariableRef(
               variable --> <
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_0_q/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_0_q/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_q/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_0_q/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_0_q/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_q/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_0_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_0_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_123abc/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_123abc/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_123abc/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_123abc/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_123abc/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_123abc/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_123abc/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_123abc/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_1/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_1/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_1/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_1/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_1/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_1/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_1/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_3.14/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_3.14/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_3.14/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_3.14/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_3.14/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_3.14/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_3.14/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_3.14/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_equal/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_equal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_equal/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_equal/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_equal/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_equal/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_equal/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_equal/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_comma_q1/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_comma_q1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_comma_q1/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_comma_q1/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_comma_q1/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_comma_q1/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_comma_q1/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_comma_q1/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_q1/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_q1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_q1/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_q1/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_q1/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_q1/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q0_q1/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q0_q1/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q__qubit_array_of_17_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q_at_0/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q_at_0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q_at_0/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q_at_0/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q_at_0/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q_at_0/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q_at_0/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q_at_0/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_3.14_q/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_3.14_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_3.14_q/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_3.14_q/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_3.14_q/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_3.14_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_3.14_q/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_3.14_q/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_equal/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_equal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_equal/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_equal/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_equal/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_equal/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_equal/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_equal/input.cq
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_q/ast.golden.txt b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_q/ast.golden.txt
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_q/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_array_definition/qubit_array_of_q/input.cq b/res/v3x/tests/integration/qubit_array_definition/qubit_array_of_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_array_definition/qubit_array_of_q/input.cq
rename to res/v3x/tests/integration/qubit_array_definition/qubit_array_of_q/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/qubit_camel_case/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qqubit_q/ast.golden.txt
similarity index 59%
rename from res/v3x/parsing/case_sensitiveness/qubit_camel_case/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qqubit_q/ast.golden.txt
index 33c35bdb3..b55a1c1b9 100644
--- a/res/v3x/parsing/case_sensitiveness/qubit_camel_case/ast.golden.txt
+++ b/res/v3x/tests/integration/qubit_definition/qqubit_q/ast.golden.txt
@@ -8,10 +8,17 @@ Program(
   block: <
     GlobalBlock(
       statements: [
-        Gate( # input.cq:3:1..6
-          name: <
-            Identifier(
-              name: Qubit
+        GateInstruction( # input.cq:3:1..6
+          gate: <
+            Gate( # input.cq:3:1..6
+              name: <
+                Identifier(
+                  name: Qubit
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/qubit_definition/qqubit_q/input.cq b/res/v3x/tests/integration/qubit_definition/qqubit_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qqubit_q/input.cq
rename to res/v3x/tests/integration/qubit_definition/qqubit_q/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qqubit_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/qubit_definition/qqubit_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qqubit_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qqubit_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit/input.cq b/res/v3x/tests/integration/qubit_definition/qubit/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_1/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_1/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_1/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_1/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_1/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_1/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_1/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_123abc/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_123abc/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_123abc/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_123abc/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_123abc/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_123abc/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_123abc/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_123abc/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_equal/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_equal/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_equal/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_equal/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_equal/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_equal/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_equal/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_equal/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_q/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_q/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q0_comma_q1/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q0_comma_q1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q0_comma_q1/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q0_comma_q1/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q0_comma_q1/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_q0_comma_q1/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q0_comma_q1/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_q0_comma_q1/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_q0_q1/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q0_q1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q0_q1/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q0_q1/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q0_q1/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_q0_q1/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q0_q1/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_q0_q1/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_q1__qubit_q2/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q1__qubit_q2/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q1__qubit_q2/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q1__qubit_q2/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q1__qubit_q2/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_q1__qubit_q2/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q1__qubit_q2/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_q1__qubit_q2/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_q1__qubit_q2/semantic.3.0.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q1__qubit_q2/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q1__qubit_q2/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q1__qubit_q2/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q__qubit_q/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q__qubit_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q__qubit_q/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q__qubit_q/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q__qubit_q/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_q__qubit_q/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q__qubit_q/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_q__qubit_q/input.cq
diff --git a/res/v3x/parsing/qubit_definition/qubit_q__qubit_q/semantic.3.0.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q__qubit_q/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q__qubit_q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q__qubit_q/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q_at_0/ast.golden.txt b/res/v3x/tests/integration/qubit_definition/qubit_q_at_0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q_at_0/ast.golden.txt
rename to res/v3x/tests/integration/qubit_definition/qubit_q_at_0/ast.golden.txt
diff --git a/res/v3x/parsing/qubit_definition/qubit_q_at_0/input.cq b/res/v3x/tests/integration/qubit_definition/qubit_q_at_0/input.cq
similarity index 100%
rename from res/v3x/parsing/qubit_definition/qubit_q_at_0/input.cq
rename to res/v3x/tests/integration/qubit_definition/qubit_q_at_0/input.cq
diff --git a/res/v3x/parsing/case_sensitiveness/q_uppercase/ast.golden.txt b/res/v3x/tests/integration/reset_instruction/5/ast.golden.txt
similarity index 81%
rename from res/v3x/parsing/case_sensitiveness/q_uppercase/ast.golden.txt
rename to res/v3x/tests/integration/reset_instruction/5/ast.golden.txt
index 7eca5f98a..53293a8ea 100644
--- a/res/v3x/parsing/case_sensitiveness/q_uppercase/ast.golden.txt
+++ b/res/v3x/tests/integration/reset_instruction/5/ast.golden.txt
@@ -26,17 +26,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:4:1..2
+        NonGateInstruction( # input.cq:5:1..6
           name: <
-            Identifier(
-              name: X
+            Keyword(
+              name: reset
             )
           >
           operands: <
             ExpressionList(
               items: [
-                Identifier( # input.cq:4:3..4
-                  name: Q
+                IntegerLiteral( # input.cq:5:7..8
+                  value: 5
                 )
               ]
             )
diff --git a/res/v3x/parsing/reset_instruction/5/input.cq b/res/v3x/tests/integration/reset_instruction/5/input.cq
similarity index 100%
rename from res/v3x/parsing/reset_instruction/5/input.cq
rename to res/v3x/tests/integration/reset_instruction/5/input.cq
diff --git a/res/v3x/parsing/reset_instruction/5/semantic.3.0.golden.txt b/res/v3x/tests/integration/reset_instruction/5/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/reset_instruction/5/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/reset_instruction/5/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/reset_instruction/q/ast.golden.txt b/res/v3x/tests/integration/reset_instruction/no_operand/ast.golden.txt
similarity index 81%
rename from res/v3x/parsing/reset_instruction/q/ast.golden.txt
rename to res/v3x/tests/integration/reset_instruction/no_operand/ast.golden.txt
index 924a6cdb8..ed08c7221 100644
--- a/res/v3x/parsing/reset_instruction/q/ast.golden.txt
+++ b/res/v3x/tests/integration/reset_instruction/no_operand/ast.golden.txt
@@ -26,15 +26,15 @@ Program(
           >
           annotations: []
         )
-        ResetInstruction( # input.cq:5:1..6
+        NonGateInstruction( # input.cq:5:1..6
           name: <
-            Identifier(
+            Keyword(
               name: reset
             )
           >
-          operand: <
-            Identifier( # input.cq:5:7..8
-              name: q
+          operands: <
+            ExpressionList(
+              items: []
             )
           >
           annotations: []
diff --git a/res/v3x/parsing/reset_instruction/no_operand/input.cq b/res/v3x/tests/integration/reset_instruction/no_operand/input.cq
similarity index 100%
rename from res/v3x/parsing/reset_instruction/no_operand/input.cq
rename to res/v3x/tests/integration/reset_instruction/no_operand/input.cq
diff --git a/res/v3x/parsing/reset_instruction/no_operand/semantic.3.0.golden.txt b/res/v3x/tests/integration/reset_instruction/no_operand/semantic.3.0.golden.txt
similarity index 93%
rename from res/v3x/parsing/reset_instruction/no_operand/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/reset_instruction/no_operand/semantic.3.0.golden.txt
index 4a75cf368..c453454d4 100644
--- a/res/v3x/parsing/reset_instruction/no_operand/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/reset_instruction/no_operand/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: reset()
           name: reset
           operands: []
diff --git a/res/v3x/parsing/instruction/qubit_q__X_q/ast.golden.txt b/res/v3x/tests/integration/reset_instruction/q/ast.golden.txt
similarity index 84%
rename from res/v3x/parsing/instruction/qubit_q__X_q/ast.golden.txt
rename to res/v3x/tests/integration/reset_instruction/q/ast.golden.txt
index fdd3cc896..26a943b34 100644
--- a/res/v3x/parsing/instruction/qubit_q__X_q/ast.golden.txt
+++ b/res/v3x/tests/integration/reset_instruction/q/ast.golden.txt
@@ -26,16 +26,16 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:5:1..2
+        NonGateInstruction( # input.cq:5:1..6
           name: <
-            Identifier(
-              name: X
+            Keyword(
+              name: reset
             )
           >
           operands: <
             ExpressionList(
               items: [
-                Identifier( # input.cq:5:3..4
+                Identifier( # input.cq:5:7..8
                   name: q
                 )
               ]
diff --git a/res/v3x/parsing/reset_instruction/q/input.cq b/res/v3x/tests/integration/reset_instruction/q/input.cq
similarity index 100%
rename from res/v3x/parsing/reset_instruction/q/input.cq
rename to res/v3x/tests/integration/reset_instruction/q/input.cq
diff --git a/res/v3x/parsing/reset_instruction/q/semantic.3.0.golden.txt b/res/v3x/tests/integration/reset_instruction/q/semantic.3.0.golden.txt
similarity index 96%
rename from res/v3x/parsing/reset_instruction/q/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/reset_instruction/q/semantic.3.0.golden.txt
index ca1b79a91..4998e4ff9 100644
--- a/res/v3x/parsing/reset_instruction/q/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/reset_instruction/q/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: reset(qubit)
           name: reset
           operands: [
diff --git a/res/v3x/tests/integration/reset_instruction/q_range_0_4/ast.golden.txt b/res/v3x/tests/integration/reset_instruction/q_range_0_4/ast.golden.txt
new file mode 100644
index 000000000..e709ff33c
--- /dev/null
+++ b/res/v3x/tests/integration/reset_instruction/q_range_0_4/ast.golden.txt
@@ -0,0 +1,76 @@
+SUCCESS
+Program(
+  version: <
+    Version( # input.cq:1:9..10
+      items: 3
+    )
+  >
+  block: <
+    GlobalBlock(
+      statements: [
+        Variable( # input.cq:3:10..11
+          name: <
+            Identifier(
+              name: q
+            )
+          >
+          typ: <
+            Type( # input.cq:3:1..9
+              name: <
+                Keyword(
+                  name: qubit
+                )
+              >
+              size: <
+                IntegerLiteral(
+                  value: 9
+                )
+              >
+            )
+          >
+          annotations: []
+        )
+        NonGateInstruction( # input.cq:5:1..6
+          name: <
+            Keyword(
+              name: reset
+            )
+          >
+          operands: <
+            ExpressionList(
+              items: [
+                Index( # input.cq:5:7..8
+                  expr: <
+                    Identifier(
+                      name: q
+                    )
+                  >
+                  indices: <
+                    IndexList(
+                      items: [
+                        IndexRange(
+                          first: <
+                            IntegerLiteral( # input.cq:5:9..10
+                              value: 0
+                            )
+                          >
+                          last: <
+                            IntegerLiteral( # input.cq:5:11..12
+                              value: 4
+                            )
+                          >
+                        )
+                      ]
+                    )
+                  >
+                )
+              ]
+            )
+          >
+          annotations: []
+        )
+      ]
+    )
+  >
+)
+
diff --git a/res/v3x/parsing/reset_instruction/q_range_0_4/input.cq b/res/v3x/tests/integration/reset_instruction/q_range_0_4/input.cq
similarity index 100%
rename from res/v3x/parsing/reset_instruction/q_range_0_4/input.cq
rename to res/v3x/tests/integration/reset_instruction/q_range_0_4/input.cq
diff --git a/res/v3x/parsing/reset_instruction/q_range_0_4/semantic.3.0.golden.txt b/res/v3x/tests/integration/reset_instruction/q_range_0_4/semantic.3.0.golden.txt
similarity index 97%
rename from res/v3x/parsing/reset_instruction/q_range_0_4/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/reset_instruction/q_range_0_4/semantic.3.0.golden.txt
index 51634b7cc..82cee738f 100644
--- a/res/v3x/parsing/reset_instruction/q_range_0_4/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/reset_instruction/q_range_0_4/semantic.3.0.golden.txt
@@ -9,7 +9,7 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        NonGateInstruction(
           instruction_ref: reset(qubit array)
           name: reset
           operands: [
diff --git a/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/ast.golden.json b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/ast.golden.json
new file mode 100644
index 000000000..8b1e39470
--- /dev/null
+++ b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/ast.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"version":{"Version":{"items":"3","source_location":"input.cq:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"4"}},"source_location":"input.cq:3:1..9"}},"annotations":[],"source_location":"input.cq:3:10..11"}},{"GateInstruction":{"gate":{"Gate":{"name":{"Identifier":{"name":"CNOT"}},"gate":"-","parameter":"-","annotations":[],"source_location":"input.cq:5:1..5"}},"operands":{"ExpressionList":{"items":[{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexItem":{"index":{"IntegerLiteral":{"value":"0","source_location":"input.cq:5:8..9"}}}},{"IndexItem":{"index":{"IntegerLiteral":{"value":"1","source_location":"input.cq:5:11..12"}}}}]}},"source_location":"input.cq:5:6..7"}},{"Index":{"expr":{"Identifier":{"name":"q"}},"indices":{"IndexList":{"items":[{"IndexItem":{"index":{"IntegerLiteral":{"value":"3","source_location":"input.cq:5:17..18"}}}},{"IndexItem":{"index":{"IntegerLiteral":{"value":"2","source_location":"input.cq:5:20..21"}}}}]}},"source_location":"input.cq:5:15..16"}}]}},"annotations":[],"source_location":"input.cq:5:1..5"}}]}}}}
\ No newline at end of file
diff --git a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/ast.golden.txt b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/ast.golden.txt
similarity index 89%
rename from res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/ast.golden.txt
rename to res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/ast.golden.txt
index bec25619b..6b7b51707 100644
--- a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/ast.golden.txt
+++ b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/ast.golden.txt
@@ -30,10 +30,17 @@ Program(
           >
           annotations: []
         )
-        Gate( # input.cq:5:1..5
-          name: <
-            Identifier(
-              name: CNOT
+        GateInstruction( # input.cq:5:1..5
+          gate: <
+            Gate( # input.cq:5:1..5
+              name: <
+                Identifier(
+                  name: CNOT
+                )
+              >
+              gate: -
+              parameter: -
+              annotations: []
             )
           >
           operands: <
diff --git a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/input.cq b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/input.cq
similarity index 100%
rename from res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/input.cq
rename to res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/input.cq
diff --git a/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.json b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.json
new file mode 100644
index 000000000..77d536ef3
--- /dev/null
+++ b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.json
@@ -0,0 +1,1 @@
+{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[{"GateInstruction":{"instruction_ref":"CNOT(qubit array, qubit array)","gate":{"Gate":{"name":"CNOT","gate":"-","parameter":"-","annotations":[]}},"operands":[{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"4"}},"annotations":[]}},"indices":[{"ConstInt":{"value":"0"}},{"ConstInt":{"value":"1"}}]}},{"IndexRef":{"variable":{"Variable":{"name":"q","typ":{"QubitArray":{"size":"4"}},"annotations":[]}},"indices":[{"ConstInt":{"value":"3"}},{"ConstInt":{"value":"2"}}]}}],"annotations":[]}}]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"4"}},"annotations":[]}}]}}
\ No newline at end of file
diff --git a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.txt b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.txt
similarity index 88%
rename from res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.txt
index 49f3ed4c7..0e9520eae 100644
--- a/res/v3x/parsing/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.txt
+++ b/res/v3x/tests/integration/sgmq/cnot_q_0_1_q_3_2/semantic.3.0.golden.txt
@@ -9,9 +9,16 @@ Program(
   block: <
     Block(
       statements: [
-        Instruction(
+        GateInstruction(
           instruction_ref: CNOT(qubit array, qubit array)
-          name: CNOT
+          gate: <
+            Gate(
+              name: CNOT
+              gate: -
+              parameter: -
+              annotations: []
+            )
+          >
           operands: [
             IndexRef(
               variable --> <
diff --git a/res/v3x/parsing/version/empty/ast.golden.txt b/res/v3x/tests/integration/version/empty/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/empty/ast.golden.txt
rename to res/v3x/tests/integration/version/empty/ast.golden.txt
diff --git a/res/v3x/parsing/version/empty/input.cq b/res/v3x/tests/integration/version/empty/input.cq
similarity index 100%
rename from res/v3x/parsing/version/empty/input.cq
rename to res/v3x/tests/integration/version/empty/input.cq
diff --git a/res/v3x/parsing/version/instruction_and_version_3.0/ast.golden.txt b/res/v3x/tests/integration/version/instruction_and_version_3.0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/instruction_and_version_3.0/ast.golden.txt
rename to res/v3x/tests/integration/version/instruction_and_version_3.0/ast.golden.txt
diff --git a/res/v3x/parsing/version/instruction_and_version_3.0/input.cq b/res/v3x/tests/integration/version/instruction_and_version_3.0/input.cq
similarity index 100%
rename from res/v3x/parsing/version/instruction_and_version_3.0/input.cq
rename to res/v3x/tests/integration/version/instruction_and_version_3.0/input.cq
diff --git a/res/v3x/parsing/version/instruction_called_version/ast.golden.txt b/res/v3x/tests/integration/version/instruction_called_version/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/instruction_called_version/ast.golden.txt
rename to res/v3x/tests/integration/version/instruction_called_version/ast.golden.txt
diff --git a/res/v3x/parsing/version/instruction_called_version/input.cq b/res/v3x/tests/integration/version/instruction_called_version/input.cq
similarity index 100%
rename from res/v3x/parsing/version/instruction_called_version/input.cq
rename to res/v3x/tests/integration/version/instruction_called_version/input.cq
diff --git a/res/v3x/parsing/version/no_version_and_instruction/ast.golden.txt b/res/v3x/tests/integration/version/no_version_and_instruction/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/no_version_and_instruction/ast.golden.txt
rename to res/v3x/tests/integration/version/no_version_and_instruction/ast.golden.txt
diff --git a/res/v3x/parsing/version/no_version_and_instruction/input.cq b/res/v3x/tests/integration/version/no_version_and_instruction/input.cq
similarity index 100%
rename from res/v3x/parsing/version/no_version_and_instruction/input.cq
rename to res/v3x/tests/integration/version/no_version_and_instruction/input.cq
diff --git a/res/v3x/parsing/version/version_1.0/ast.golden.txt b/res/v3x/tests/integration/version/version_1.0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_1.0/ast.golden.txt
rename to res/v3x/tests/integration/version/version_1.0/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_1.0/input.cq b/res/v3x/tests/integration/version/version_1.0/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_1.0/input.cq
rename to res/v3x/tests/integration/version/version_1.0/input.cq
diff --git a/res/v3x/parsing/version/version_1.0/semantic.3.0.golden.txt b/res/v3x/tests/integration/version/version_1.0/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_1.0/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/version/version_1.0/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/version/version_2.0/ast.golden.txt b/res/v3x/tests/integration/version/version_2.0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_2.0/ast.golden.txt
rename to res/v3x/tests/integration/version/version_2.0/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_2.0/input.cq b/res/v3x/tests/integration/version/version_2.0/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_2.0/input.cq
rename to res/v3x/tests/integration/version/version_2.0/input.cq
diff --git a/res/v3x/parsing/version/version_2.0/semantic.3.0.golden.txt b/res/v3x/tests/integration/version/version_2.0/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_2.0/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/version/version_2.0/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/version/version_3.0/ast.golden.txt b/res/v3x/tests/integration/version/version_3.0/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_3.0/ast.golden.txt
rename to res/v3x/tests/integration/version/version_3.0/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_3.0/input.cq b/res/v3x/tests/integration/version/version_3.0/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_3.0/input.cq
rename to res/v3x/tests/integration/version/version_3.0/input.cq
diff --git a/res/v3x/parsing/version/version_3.0/semantic.3.0.golden.txt b/res/v3x/tests/integration/version/version_3.0/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_3.0/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/version/version_3.0/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/version/version_3.2.1/ast.golden.txt b/res/v3x/tests/integration/version/version_3.2.1/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_3.2.1/ast.golden.txt
rename to res/v3x/tests/integration/version/version_3.2.1/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_3.2.1/input.cq b/res/v3x/tests/integration/version/version_3.2.1/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_3.2.1/input.cq
rename to res/v3x/tests/integration/version/version_3.2.1/input.cq
diff --git a/res/v3x/parsing/version/version_3/ast.golden.txt b/res/v3x/tests/integration/version/version_3/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_3/ast.golden.txt
rename to res/v3x/tests/integration/version/version_3/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_3/input.cq b/res/v3x/tests/integration/version/version_3/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_3/input.cq
rename to res/v3x/tests/integration/version/version_3/input.cq
diff --git a/res/v3x/parsing/version/version_3/semantic.3.0.golden.txt b/res/v3x/tests/integration/version/version_3/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_3/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/version/version_3/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/version/version_30.20/ast.golden.txt b/res/v3x/tests/integration/version/version_30.20/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_30.20/ast.golden.txt
rename to res/v3x/tests/integration/version/version_30.20/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_30.20/input.cq b/res/v3x/tests/integration/version/version_30.20/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_30.20/input.cq
rename to res/v3x/tests/integration/version/version_30.20/input.cq
diff --git a/res/v3x/parsing/version/version_30.20/semantic.3.0.golden.txt b/res/v3x/tests/integration/version/version_30.20/semantic.3.0.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_30.20/semantic.3.0.golden.txt
rename to res/v3x/tests/integration/version/version_30.20/semantic.3.0.golden.txt
diff --git a/res/v3x/parsing/version/version_3__version_3__qubit_q/ast.golden.txt b/res/v3x/tests/integration/version/version_3__version_3__qubit_q/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_3__version_3__qubit_q/ast.golden.txt
rename to res/v3x/tests/integration/version/version_3__version_3__qubit_q/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_3__version_3__qubit_q/input.cq b/res/v3x/tests/integration/version/version_3__version_3__qubit_q/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_3__version_3__qubit_q/input.cq
rename to res/v3x/tests/integration/version/version_3__version_3__qubit_q/input.cq
diff --git a/res/v3x/parsing/version/version_3_dot/ast.golden.txt b/res/v3x/tests/integration/version/version_3_dot/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_3_dot/ast.golden.txt
rename to res/v3x/tests/integration/version/version_3_dot/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_3_dot/input.cq b/res/v3x/tests/integration/version/version_3_dot/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_3_dot/input.cq
rename to res/v3x/tests/integration/version/version_3_dot/input.cq
diff --git a/res/v3x/parsing/version/version_no_number/ast.golden.txt b/res/v3x/tests/integration/version/version_no_number/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/version_no_number/ast.golden.txt
rename to res/v3x/tests/integration/version/version_no_number/ast.golden.txt
diff --git a/res/v3x/parsing/version/version_no_number/input.cq b/res/v3x/tests/integration/version/version_no_number/input.cq
similarity index 100%
rename from res/v3x/parsing/version/version_no_number/input.cq
rename to res/v3x/tests/integration/version/version_no_number/input.cq
diff --git a/res/v3x/parsing/version/vrsion/ast.golden.txt b/res/v3x/tests/integration/version/vrsion/ast.golden.txt
similarity index 100%
rename from res/v3x/parsing/version/vrsion/ast.golden.txt
rename to res/v3x/tests/integration/version/vrsion/ast.golden.txt
diff --git a/res/v3x/parsing/version/vrsion/input.cq b/res/v3x/tests/integration/version/vrsion/input.cq
similarity index 100%
rename from res/v3x/parsing/version/vrsion/input.cq
rename to res/v3x/tests/integration/version/vrsion/input.cq
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 183253cb5..0375debe6 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -7,11 +7,11 @@ add_executable(${PROJECT_NAME}_test)
 add_subdirectory(v3x)
 
 target_sources(${PROJECT_NAME}_test PRIVATE
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-annotations.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-error.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-result.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-utils.cpp"
     "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_annotations.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_error.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_result.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_utils.cpp"
     "${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp"
 )
 
@@ -60,4 +60,5 @@ endif()
 
 gtest_discover_tests(${PROJECT_NAME}_test
     WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+    DISCOVERY_TIMEOUT 600
 )
diff --git a/test/main.cpp b/test/main.cpp
index 8a40a639b..7fb102edd 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -2,9 +2,9 @@
 
 #include <iostream>
 
-#include "v3x/cpp/parsing.hpp"
+#include "v3x/cpp/integration_test.hpp"
 
-int main_impl(int argc, char **argv, std::ostream &) {
+int main_impl(int argc, char** argv, std::ostream&) {
     ::testing::InitGoogleMock(&argc, argv);
 
     cqasm::v3x::test::register_tests();
@@ -12,6 +12,6 @@ int main_impl(int argc, char **argv, std::ostream &) {
     return RUN_ALL_TESTS();
 }
 
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
     return main_impl(argc, argv, std::cout);
 }
diff --git a/test/parsing-tools.py b/test/parsing-tools.py
deleted file mode 100644
index 1a93b9963..000000000
--- a/test/parsing-tools.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import sys
-import os
-
-if len(sys.argv) != 2:
-    print('invalid number of args')
-    print()
-    print('NOTE: this tool is unsupported and not at all portable.')
-    print('If you want to use it, you\'ll probably have to edit its sources.')
-    sys.exit(1)
-
-from plumbum import local, FG
-
-
-def diff():
-    is_test_dir = False
-    for t in ('ast', 'semantic.1.0', 'semantic.1.1', 'semantic.1.2'):
-        if os.path.isfile(t + '.actual.txt') and os.path.isfile(t + '.golden.txt'):
-            with open(t + '.actual.txt', 'r') as a:
-                a = a.read()
-            with open(t + '.golden.txt', 'r') as g:
-                g = g.read()
-            if a == g:
-                print(t, 'is as expected')
-            else:
-                print(t, 'is NOT as expected, opening meld')
-                local['meld'](t + '.actual.txt', t + '.golden.txt')
-            is_test_dir = True
-    if not is_test_dir:
-        for sub in os.listdir():
-            if os.path.isdir(sub):
-                print('Entering %s...' % sub)
-                with local.cwd(sub):
-                    diff()
-                print('Leaving %s...' % sub)
-
-
-def gild_all():
-    is_test_dir = False
-    for t in ('ast', 'semantic.1.0', 'semantic.1.1', 'semantic.1.2'):
-        if os.path.isfile(t + '.actual.txt'):
-            local['cp'](t + '.actual.txt', t + '.golden.txt')
-            is_test_dir = True
-    if not is_test_dir:
-        for sub in os.listdir():
-            if os.path.isdir(sub):
-                print('Entering %s...' % sub)
-                with local.cwd(sub):
-                    gild_all()
-                print('Leaving %s...' % sub)
-
-
-cmd = sys.argv[1]
-
-if cmd == 'run':
-    with local.cwd(os.path.dirname(__file__)):
-        local['make']['--no-print-directory']['-C']['../cmake-build-debug']['test-parsing'] & FG
-        output = local['../cmake-build-debug/test/test-parsing'](retcode=None)
-        print(output.split('[==========]')[2])
-    cmd = 'diff'
-
-if cmd == 'diff':
-    diff()
-
-elif cmd == 'gild':
-    if os.path.isfile('ast.actual.txt'):
-        print('copying ast result')
-        local['cp']('-f', 'ast.actual.txt', 'ast.golden.txt')
-        with open('ast.actual.txt', 'r') as f:
-            ast_errors = next(f).startswith('ERROR')
-        if ast_errors:
-            print('parse failed, deleting semantic result')
-            local['rm']('-f', 'semantic.golden.txt', 'semantic.actual.txt')
-        elif os.path.isfile('semantic.actual.txt'):
-            print('copying semantic result')
-            local['cp']('-f', 'semantic.actual.txt', 'semantic.golden.txt')
-
-elif cmd == 'gild-all':
-    gild_all()
-
-else:
-    print('unknown command')
diff --git a/test/test-register.hpp b/test/register_integration_tests.hpp
similarity index 88%
rename from test/test-register.hpp
rename to test/register_integration_tests.hpp
index 5432bd5c2..2c2b58843 100644
--- a/test/test-register.hpp
+++ b/test/register_integration_tests.hpp
@@ -8,7 +8,7 @@ namespace cqasm::test {
 namespace fs = std::filesystem;
 
 template <typename TestFactory>
-void register_tests(const fs::path &tests_root_path, TestFactory test_factory) {
+void register_tests(const fs::path& tests_root_path, TestFactory test_factory) {
     // Discover the tests.
     // They should live in a directory tree with the following structure:
     //
@@ -31,10 +31,10 @@ void register_tests(const fs::path &tests_root_path, TestFactory test_factory) {
     } else if (!fs::is_directory(tests_root_path)) {
         throw std::runtime_error(fmt::format("'{}' is not a directory", tests_root_path.generic_string()));
     }
-    for (const fs::directory_entry &suite : fs::directory_iterator(tests_root_path)) {
+    for (const fs::directory_entry& suite : fs::directory_iterator(tests_root_path)) {
         if (fs::is_directory(suite)) {
             auto suite_name = suite.path().filename().string();
-            for (const fs::directory_entry &test : fs::directory_iterator(suite.path())) {
+            for (const fs::directory_entry& test : fs::directory_iterator(suite.path())) {
                 auto test_name = test.path().filename().string();
                 if (fs::is_directory(test)) {
                     auto input_cq_path = test.path() / "input.cq";
@@ -45,7 +45,7 @@ void register_tests(const fs::path &tests_root_path, TestFactory test_factory) {
                             nullptr,
                             __FILE__,
                             __LINE__,
-                            [=]() -> ::testing::Test * { return test_factory(test.path()); });
+                            [=]() -> ::testing::Test* { return test_factory(test.path()); });
                     }
                 }
             }
diff --git a/test/cqasm-annotations.cpp b/test/test_annotations.cpp
similarity index 97%
rename from test/cqasm-annotations.cpp
rename to test/test_annotations.cpp
index 67d246635..1358d0e25 100644
--- a/test/cqasm-annotations.cpp
+++ b/test/test_annotations.cpp
@@ -1,10 +1,10 @@
-#include "cqasm-annotations.hpp"  // SourceLocation
-
 #include <fmt/format.h>
 #include <gmock/gmock.h>
 
 #include <optional>
 
+#include "libqasm/annotations.hpp"  // SourceLocation
+
 using namespace ::testing;
 using namespace cqasm::annotations;
 
diff --git a/test/cqasm-error.cpp b/test/test_error.cpp
similarity index 98%
rename from test/cqasm-error.cpp
rename to test/test_error.cpp
index b350c812a..6bc2018ae 100644
--- a/test/cqasm-error.cpp
+++ b/test/test_error.cpp
@@ -1,12 +1,11 @@
-#include "cqasm-error.hpp"
-
 #include <fmt/format.h>
 #include <gmock/gmock.h>
 
 #include <memory>  // make_shared
 
-#include "cqasm-annotations.hpp"  // SourceLocation
-#include "cqasm-tree.hpp"  // Annotatable
+#include "libqasm/annotations.hpp"  // SourceLocation
+#include "libqasm/error.hpp"
+#include "libqasm/tree.hpp"  // Annotatable
 
 using namespace ::testing;
 using namespace cqasm::error;
diff --git a/test/cqasm-result.cpp b/test/test_result.cpp
similarity index 59%
rename from test/cqasm-result.cpp
rename to test/test_result.cpp
index a7c74c24a..4bcae451f 100644
--- a/test/cqasm-result.cpp
+++ b/test/test_result.cpp
@@ -1,52 +1,51 @@
-#include "cqasm-result.hpp"
-
 #include <gtest/gtest.h>
 
 #include <filesystem>
 
-#include "v3x/cqasm.hpp"  // default_analyzer
+#include "libqasm/result.hpp"
+#include "libqasm/v3x/cqasm.hpp"  // default_analyzer
 
 namespace fs = std::filesystem;
 using namespace cqasm::result;
 
 TEST(to_json, v3x_parser_errors) {
-    auto input_file_path =
-        fs::path{ "res" } / "v3x" / "parsing" / "qubit_array_definition" / "qubit_array_of_17" / "input.cq";
+    auto input_file_path = fs::path{ "res" } / "v3x" / "tests" / "integration" / "qubit_array_definition" /
+        "qubit_array_of_17" / "input.cq";
     cqasm::v3x::analyzer::Analyzer analyzer{};
     auto semantic_ast_result = analyzer.analyze_file(input_file_path.generic_string());
     auto json_result = to_json(semantic_ast_result);
     auto expected_json_result = std::string{
-        R"delim({"errors":[{"range":{"start":{"line":3,"character":10},"end":{"line":3,"character":11}},"message":"missing IDENTIFIER at '\u005Cn'","severity":1,"relatedInformation":[{"location":{"uri":"file:///res%2Fv3x%2Fparsing%2Fqubit_array_definition%2Fqubit_array_of_17%2Finput.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]})delim"
+        R"delim({"errors":[{"range":{"start":{"line":3,"character":10},"end":{"line":3,"character":11}},"message":"missing IDENTIFIER at '\u005Cn'","severity":1,"relatedInformation":[{"location":{"uri":"file:///res%2Fv3x%2Ftests%2Fintegration%2Fqubit_array_definition%2Fqubit_array_of_17%2Finput.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]})delim"
     };
     EXPECT_EQ(json_result, expected_json_result);
 }
 TEST(to_json, v3x_parser_ast) {
-    auto input_file_path =
-        fs::path{ "res" } / "v3x" / "parsing" / "qubit_array_definition" / "qubit_array_of_0_q" / "input.cq";
+    auto input_file_path = fs::path{ "res" } / "v3x" / "tests" / "integration" / "qubit_array_definition" /
+        "qubit_array_of_0_q" / "input.cq";
     auto ast_result = cqasm::v3x::parser::parse_file(input_file_path.generic_string(), std::nullopt);
     auto json_result = to_json(ast_result);
     auto expected_json_result = std::string{
-        R"delim({"Program":{"version":{"Version":{"items":"3","source_location":"<unknown file name>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"0"}},"source_location":"<unknown file name>:3:1..9"}},"annotations":"[]","source_location":"<unknown file name>:3:10..11"}}]}}}})delim"
+        R"delim({"Program":{"version":{"Version":{"items":"3","source_location":"<unknown file name>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"0"}},"source_location":"<unknown file name>:3:1..9"}},"annotations":[],"source_location":"<unknown file name>:3:10..11"}}]}}}})delim"
     };
     EXPECT_EQ(json_result, expected_json_result);
 }
 TEST(to_json, v3x_analyzer_errors) {
-    auto input_file_path =
-        fs::path{ "res" } / "v3x" / "parsing" / "qubit_array_definition" / "qubit_array_of_0_q" / "input.cq";
+    auto input_file_path = fs::path{ "res" } / "v3x" / "tests" / "integration" / "qubit_array_definition" /
+        "qubit_array_of_0_q" / "input.cq";
     auto semantic_ast_result = cqasm::v3x::default_analyzer().analyze_file(input_file_path.generic_string());
     auto json_result = to_json(semantic_ast_result);
     auto expected_json_result = std::string{
-        R"delim({"errors":[{"range":{"start":{"line":3,"character":10},"end":{"line":3,"character":11}},"message":"found qubit array of size <= 0","severity":1,"relatedInformation":[{"location":{"uri":"file:///res%2Fv3x%2Fparsing%2Fqubit_array_definition%2Fqubit_array_of_0_q%2Finput.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]})delim"
+        R"delim({"errors":[{"range":{"start":{"line":3,"character":10},"end":{"line":3,"character":11}},"message":"found qubit array of size <= 0","severity":1,"relatedInformation":[{"location":{"uri":"file:///res%2Fv3x%2Ftests%2Fintegration%2Fqubit_array_definition%2Fqubit_array_of_0_q%2Finput.cq","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}},"message":"<unknown error message>"}]}]})delim"
     };
     EXPECT_EQ(json_result, expected_json_result);
 }
 TEST(to_json, v3x_analyzer_ast) {
-    auto input_file_path =
-        fs::path{ "res" } / "v3x" / "parsing" / "qubit_array_definition" / "qubit_array_of_17_q" / "input.cq";
+    auto input_file_path = fs::path{ "res" } / "v3x" / "tests" / "integration" / "qubit_array_definition" /
+        "qubit_array_of_17_q" / "input.cq";
     auto semantic_ast_result = cqasm::v3x::default_analyzer().analyze_file(input_file_path.generic_string());
     auto json_result = to_json(semantic_ast_result);
     auto expected_json_result = std::string{
-        R"delim({"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":"[]"}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"17"}},"annotations":"[]"}}]}})delim"
+        R"delim({"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"17"}},"annotations":[]}}]}})delim"
     };
     EXPECT_EQ(json_result, expected_json_result);
 }
diff --git a/test/cqasm-utils.cpp b/test/test_utils.cpp
similarity index 98%
rename from test/cqasm-utils.cpp
rename to test/test_utils.cpp
index 37c51a29e..bd8cc8036 100644
--- a/test/cqasm-utils.cpp
+++ b/test/test_utils.cpp
@@ -1,7 +1,7 @@
-#include "cqasm-utils.hpp"
-
 #include <gtest/gtest.h>
 
+#include "libqasm/utils.hpp"
+
 using namespace cqasm::utils;
 
 // clang-format off
diff --git a/test/utils.cpp b/test/utils.cpp
index 24a713bde..e49486e27 100644
--- a/test/utils.cpp
+++ b/test/utils.cpp
@@ -12,7 +12,7 @@ namespace fs = std::filesystem;
  * returns true if it exists,
  * otherwise do nothing with the buffer and return false.
  */
-bool read_file(const fs::path &file_path, std::string &output) {
+bool read_file(const fs::path& file_path, std::string& output) {
     std::ifstream ifs(file_path);
     if (!ifs.is_open()) {
         return false;
@@ -28,7 +28,7 @@ bool read_file(const fs::path &file_path, std::string &output) {
 /**
  * Overwrites or creates the given file with the given string.
  */
-void write_file(const fs::path &file_path, const std::string &input) {
+void write_file(const fs::path& file_path, const std::string& input) {
     std::ofstream stream(file_path, std::ios::binary | std::ios::out);  // always write LF, i.e., avoid CR+LF in Windows
     stream << input;
 }
diff --git a/test/utils.hpp b/test/utils.hpp
index f00e87d42..5f5337401 100644
--- a/test/utils.hpp
+++ b/test/utils.hpp
@@ -10,11 +10,11 @@ namespace fs = std::filesystem;
  * returns true if it exists,
  * otherwise do nothing with the buffer and return false.
  */
-bool read_file(const fs::path &file_path, std::string &output);
+bool read_file(const fs::path& file_path, std::string& output);
 
 /**
  * Overwrites or creates the given file with the given string.
  */
-void write_file(const fs::path &file_path, const std::string &input);
+void write_file(const fs::path& file_path, const std::string& input);
 
 }  // namespace cqasm::test
diff --git a/test/v3x/cpp/CMakeLists.txt b/test/v3x/cpp/CMakeLists.txt
index 5b6ae7f46..6d40a712e 100644
--- a/test/v3x/cpp/CMakeLists.txt
+++ b/test/v3x/cpp/CMakeLists.txt
@@ -1,9 +1,10 @@
 target_sources(${PROJECT_NAME}_test PRIVATE
-    "${CMAKE_CURRENT_SOURCE_DIR}/AnalyzeTreeGenAstVisitor.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-analyzer.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-functions.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-parse-helper.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/cqasm-values.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/integration_test.cpp"
     "${CMAKE_CURRENT_SOURCE_DIR}/matcher_values.cpp"
-    "${CMAKE_CURRENT_SOURCE_DIR}/parsing.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_analyzer.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_functions.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_instruction_set.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_parse_helper.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_semantic_analyzer.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/test_values.cpp"
 )
diff --git a/test/v3x/cpp/parsing.cpp b/test/v3x/cpp/integration_test.cpp
similarity index 57%
rename from test/v3x/cpp/parsing.cpp
rename to test/v3x/cpp/integration_test.cpp
index 1682c8ef6..d3cf730af 100644
--- a/test/v3x/cpp/parsing.cpp
+++ b/test/v3x/cpp/integration_test.cpp
@@ -1,15 +1,17 @@
-#include "parsing.hpp"
+#include "integration_test.hpp"
 
-#include <filesystem>
 #include <fmt/format.h>
+#include <fmt/ostream.h>
 #include <fmt/ranges.h>
 #include <gtest/gtest.h>
+
+#include <filesystem>
 #include <string>
 
-#include "test-register.hpp"
+#include "libqasm/v3x/cqasm.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
+#include "register_integration_tests.hpp"
 #include "utils.hpp"
-#include "v3x/cqasm-parse-helper.hpp"
-#include "v3x/cqasm.hpp"
 
 namespace cqasm::v3x::test {
 
@@ -20,11 +22,11 @@ namespace fs = std::filesystem;
  * Tests the default parsing pipeline, varying only the input files, and
  * matching debug dumps of the output against golden versions.
  */
-class ParsingTest : public ::testing::Test {
+class IntegrationTest : public ::testing::Test {
     fs::path path_{};
 
 public:
-    explicit ParsingTest(fs::path path)
+    explicit IntegrationTest(fs::path path)
     : path_{ std::move(path) } {}
 
     void TestBody() override {
@@ -46,19 +48,17 @@ class ParsingTest : public ::testing::Test {
         EXPECT_TRUE(ast_actual_file_contents == ast_golden_file_contents);
 
         // Check the JSON dump of the parse result
-        if (parse_result.errors.empty()) {
-            if (auto json_golden_file_path = path_ / "ast.golden.json"; fs::exists(json_golden_file_path)) {
-                auto json_actual_file_path = path_ / "ast.actual.json";
-                std::string json_actual_file_contents{};
-                std::string json_golden_file_contents{};
-                {
-                    std::ofstream json_actual_ofs{ json_actual_file_path };
-                    parse_result.root->dump_json(json_actual_ofs);
-                }
-                EXPECT_TRUE(cqasm::test::read_file(json_actual_file_path, json_actual_file_contents));
-                EXPECT_TRUE(cqasm::test::read_file(json_golden_file_path, json_golden_file_contents));
-                EXPECT_TRUE(json_actual_file_contents == json_golden_file_contents);
+        if (auto json_golden_file_path = path_ / "ast.golden.json"; fs::exists(json_golden_file_path)) {
+            auto json_actual_file_path = path_ / "ast.actual.json";
+            std::string json_actual_file_contents{};
+            std::string json_golden_file_contents{};
+            {
+                std::ofstream json_actual_ofs{ json_actual_file_path };
+                fmt::print(json_actual_ofs, "{}", parse_result.to_json());
             }
+            EXPECT_TRUE(cqasm::test::read_file(json_actual_file_path, json_actual_file_contents));
+            EXPECT_TRUE(cqasm::test::read_file(json_golden_file_path, json_golden_file_contents));
+            EXPECT_TRUE(json_actual_file_contents == json_golden_file_contents);
         }
 
         // Stop if parsing failed
@@ -67,7 +67,7 @@ class ParsingTest : public ::testing::Test {
         }
 
         // If there were no errors, try semantic analysis
-        for (const auto &api_version : std::vector<std::string>({ "3.0" })) {
+        for (const auto& api_version : std::vector<std::string>({ "3.0" })) {
             auto analyzer = cq3x::analyzer::Analyzer{ api_version };
 
             analyzer.register_default_constants();
@@ -89,22 +89,18 @@ class ParsingTest : public ::testing::Test {
             EXPECT_TRUE(semantic_actual_file_contents == semantic_golden_file_contents);
 
             // Check the JSON dump of the analysis result
-            if (analysis_result.errors.empty()) {
-                if (auto semantic_json_golden_file_path = path_ / fmt::format("semantic.{}.golden.json", api_version);
-                    fs::exists(semantic_json_golden_file_path)) {
-                    auto semantic_json_actual_file_path = path_ / fmt::format("semantic.{}.actual.json", api_version);
-                    std::string semantic_json_actual_file_contents{};
-                    std::string semantic_json_golden_file_contents{};
-                    {
-                        std::ofstream semantic_json_actual_ofs{ semantic_json_actual_file_path };
-                        analysis_result.root->dump_json(semantic_json_actual_ofs);
-                    }
-                    EXPECT_TRUE(
-                        cqasm::test::read_file(semantic_json_actual_file_path, semantic_json_actual_file_contents));
-                    EXPECT_TRUE(
-                        cqasm::test::read_file(semantic_json_golden_file_path, semantic_json_golden_file_contents));
-                    EXPECT_TRUE(semantic_json_actual_file_contents == semantic_json_golden_file_contents);
+            if (auto semantic_json_golden_file_path = path_ / fmt::format("semantic.{}.golden.json", api_version);
+                fs::exists(semantic_json_golden_file_path)) {
+                auto semantic_json_actual_file_path = path_ / fmt::format("semantic.{}.actual.json", api_version);
+                std::string semantic_json_actual_file_contents{};
+                std::string semantic_json_golden_file_contents{};
+                {
+                    std::ofstream semantic_json_actual_ofs{ semantic_json_actual_file_path };
+                    fmt::print(semantic_json_actual_ofs, "{}", analysis_result.to_json());
                 }
+                EXPECT_TRUE(cqasm::test::read_file(semantic_json_actual_file_path, semantic_json_actual_file_contents));
+                EXPECT_TRUE(cqasm::test::read_file(semantic_json_golden_file_path, semantic_json_golden_file_contents));
+                EXPECT_TRUE(semantic_json_actual_file_contents == semantic_json_golden_file_contents);
             }
 
             if (analysis_result.errors.empty()) {
@@ -115,8 +111,8 @@ class ParsingTest : public ::testing::Test {
 };
 
 void register_tests() {
-    cqasm::test::register_tests(fs::path{ "res" } / "v3x" / "parsing",
-        [=](fs::path test_path) -> ParsingTest * { return new ParsingTest(std::move(test_path)); });
+    cqasm::test::register_tests(fs::path{ "res" } / "v3x" / "tests" / "integration",
+        [=](fs::path test_path) -> IntegrationTest* { return new IntegrationTest(std::move(test_path)); });
 }
 
 }  // namespace cqasm::v3x::test
diff --git a/test/v3x/cpp/parsing.hpp b/test/v3x/cpp/integration_test.hpp
similarity index 100%
rename from test/v3x/cpp/parsing.hpp
rename to test/v3x/cpp/integration_test.hpp
diff --git a/test/v3x/cpp/matcher_values.cpp b/test/v3x/cpp/matcher_values.cpp
index a02aaee1e..f0eab1346 100644
--- a/test/v3x/cpp/matcher_values.cpp
+++ b/test/v3x/cpp/matcher_values.cpp
@@ -4,26 +4,26 @@
 
 #include <ostream>
 
-#include "v3x/cqasm-semantic-gen.hpp"
+#include "libqasm/v3x/semantic_generated.hpp"
 
 namespace cqasm::v3x::values {
 
-ValuesEqMatcher::ValuesEqMatcher(const Values &expectedValue)
+ValuesEqMatcher::ValuesEqMatcher(const Values& expectedValue)
 : expectedValue_(expectedValue) {}
 
-bool ValuesEqMatcher::MatchAndExplain(const Values &args, std::ostream * /* os */) const {
+bool ValuesEqMatcher::MatchAndExplain(const Values& args, std::ostream* /* os */) const {
     return args.equals(expectedValue_);
 }
 
-void ValuesEqMatcher::DescribeTo(std::ostream *os) const {
+void ValuesEqMatcher::DescribeTo(std::ostream* os) const {
     *os << "contains values equal to the expected";
 }
 
-void ValuesEqMatcher::DescribeNegationTo(std::ostream *os) const {
+void ValuesEqMatcher::DescribeNegationTo(std::ostream* os) const {
     *os << "does not contain values equal to the expected";
 }
 
-::testing::Matcher<const Values &> ValuesEq(const Values &expectedValue) {
+::testing::Matcher<const Values&> ValuesEq(const Values& expectedValue) {
     return ValuesEqMatcher(expectedValue);
 }
 
diff --git a/test/v3x/cpp/matcher_values.hpp b/test/v3x/cpp/matcher_values.hpp
index 03908cadb..4e3fb6904 100644
--- a/test/v3x/cpp/matcher_values.hpp
+++ b/test/v3x/cpp/matcher_values.hpp
@@ -4,7 +4,7 @@
 
 #include <iosfwd>  //ostream
 
-#include "v3x/cqasm-values.hpp"
+#include "libqasm/v3x/values.hpp"
 
 namespace cqasm::v3x::values {
 
@@ -12,15 +12,15 @@ class ValuesEqMatcher {
 public:
     using is_gtest_matcher = void;
 
-    ValuesEqMatcher(const values::Values &expectedValue);
-    bool MatchAndExplain(const values::Values &args, std::ostream *os) const;
-    void DescribeTo(std::ostream *os) const;
-    void DescribeNegationTo(std::ostream *os) const;
+    ValuesEqMatcher(const values::Values& expectedValue);
+    bool MatchAndExplain(const values::Values& args, std::ostream* os) const;
+    void DescribeTo(std::ostream* os) const;
+    void DescribeNegationTo(std::ostream* os) const;
 
 private:
-    const values::Values &expectedValue_;
+    const values::Values& expectedValue_;
 };
 
-::testing::Matcher<const values::Values &> ValuesEq(const values::Values &expectedValue);
+::testing::Matcher<const values::Values&> ValuesEq(const values::Values& expectedValue);
 
 }  // namespace cqasm::v3x::values
diff --git a/test/v3x/cpp/mock_analyze_tree_gen_ast_visitor.hpp b/test/v3x/cpp/mock_analyze_tree_gen_ast_visitor.hpp
deleted file mode 100644
index 63aa4cd1f..000000000
--- a/test/v3x/cpp/mock_analyze_tree_gen_ast_visitor.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "v3x/AnalyzeTreeGenAstVisitor.hpp"
-#include "v3x/cqasm-analysis-result.hpp"
-#include "v3x/cqasm-analyzer.hpp"
-
-namespace cqasm::v3x::analyzer {
-
-struct MockAnalyzeTreeGenAstVisitor : public AnalyzeTreeGenAstVisitor {
-    explicit MockAnalyzeTreeGenAstVisitor(Analyzer &analyzer)
-    : AnalyzeTreeGenAstVisitor{ analyzer } {}
-
-    [[nodiscard]] AnalysisResult &result() { return result_; }
-};
-
-}  // namespace cqasm::v3x::analyzer
diff --git a/test/v3x/cpp/mock_analyzer.hpp b/test/v3x/cpp/mock_analyzer.hpp
index bd321e21f..3f4fe84c6 100644
--- a/test/v3x/cpp/mock_analyzer.hpp
+++ b/test/v3x/cpp/mock_analyzer.hpp
@@ -4,25 +4,25 @@
 
 #include <string>
 
-#include "v3x/cqasm-analyzer.hpp"
-#include "v3x/cqasm-values.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/values.hpp"
 
 namespace cqasm::v3x::analyzer {
 
 struct MockAnalyzer : public Analyzer {
-    MOCK_METHOD((AnalysisResult), analyze, (const parser::ParseResult &parse_result));
-    MOCK_METHOD((values::Value), resolve_function, (const std::string &name, const values::Values &args), (const));
+    MOCK_METHOD((AnalysisResult), analyze, (const parser::ParseResult& parse_result));
+    MOCK_METHOD((values::Value), resolve_function, (const std::string& name, const values::Values& args), (const));
 
-    [[nodiscard]] std::list<Scope> &scope_stack() { return scope_stack_; }
+    [[nodiscard]] std::list<Scope>& scope_stack() { return scope_stack_; }
 
-    [[nodiscard]] Scope &global_scope() { return Analyzer::global_scope(); }
-    [[nodiscard]] Scope &current_scope() { return Analyzer::current_scope(); }
+    [[nodiscard]] Scope& global_scope() { return Analyzer::global_scope(); }
+    [[nodiscard]] Scope& current_scope() { return Analyzer::current_scope(); }
     [[nodiscard]] tree::One<semantic::Block> current_block() { return Analyzer::current_block(); }
-    [[nodiscard]] tree::Any<semantic::Variable> &current_variables() { return Analyzer::current_variables(); }
+    [[nodiscard]] tree::Any<semantic::Variable>& current_variables() { return Analyzer::current_variables(); }
 
-    [[nodiscard]] const Scope &global_scope() const { return Analyzer::global_scope(); }
-    [[nodiscard]] const Scope &current_scope() const { return Analyzer::current_scope(); }
-    [[nodiscard]] const tree::Any<semantic::Variable> &current_variables() const {
+    [[nodiscard]] const Scope& global_scope() const { return Analyzer::global_scope(); }
+    [[nodiscard]] const Scope& current_scope() const { return Analyzer::current_scope(); }
+    [[nodiscard]] const tree::Any<semantic::Variable>& current_variables() const {
         return Analyzer::current_variables();
     }
 };
diff --git a/test/v3x/cpp/mock_scanner_adaptor.hpp b/test/v3x/cpp/mock_scanner_adaptor.hpp
index e77fe5c2b..71dbe01ab 100644
--- a/test/v3x/cpp/mock_scanner_adaptor.hpp
+++ b/test/v3x/cpp/mock_scanner_adaptor.hpp
@@ -4,8 +4,8 @@
 
 #include <string>
 
-#include "v3x/cqasm-parse-helper.hpp"
-#include "v3x/cqasm-parse-result.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
+#include "libqasm/v3x/parse_result.hpp"
 
 namespace cqasm::v3x::parser {
 
diff --git a/test/v3x/cpp/mock_semantic_analyzer.hpp b/test/v3x/cpp/mock_semantic_analyzer.hpp
new file mode 100644
index 000000000..e214b9978
--- /dev/null
+++ b/test/v3x/cpp/mock_semantic_analyzer.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "libqasm/v3x/analysis_result.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/semantic_analyzer.hpp"
+
+namespace cqasm::v3x::analyzer {
+
+struct MockSemanticAnalyzer : public SemanticAnalyzer {
+    explicit MockSemanticAnalyzer(Analyzer& analyzer)
+    : SemanticAnalyzer{ analyzer } {}
+
+    [[nodiscard]] AnalysisResult& result() { return result_; }
+};
+
+}  // namespace cqasm::v3x::analyzer
diff --git a/test/v3x/cpp/cqasm-analyzer.cpp b/test/v3x/cpp/test_analyzer.cpp
similarity index 79%
rename from test/v3x/cpp/cqasm-analyzer.cpp
rename to test/v3x/cpp/test_analyzer.cpp
index c9d394b4c..0756f0560 100644
--- a/test/v3x/cpp/cqasm-analyzer.cpp
+++ b/test/v3x/cpp/test_analyzer.cpp
@@ -1,15 +1,14 @@
-#include "v3x/cqasm-analyzer.hpp"
-
 #include <gmock/gmock.h>
 
 #include <functional>
 
-#include "cqasm-error.hpp"
-#include "cqasm-tree.hpp"
-#include "cqasm-version.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/tree.hpp"
+#include "libqasm/v3x/analyzer.hpp"
+#include "libqasm/v3x/ast.hpp"
+#include "libqasm/v3x/parse_result.hpp"
+#include "libqasm/version.hpp"
 #include "mock_analyzer.hpp"
-#include "v3x/cqasm-ast.hpp"
-#include "v3x/cqasm-parse-result.hpp"
 
 using namespace ::testing;
 
@@ -37,15 +36,15 @@ class AnalyzerAnalyzeTest : public ::testing::Test {
 
 TEST_F(AnalyzerAnalyzeTest, parser_returns_parse_result) {
     auto analyzer = Analyzer{};
-    const auto &analysis_result = analyzer.analyze(parse_result_ok);
+    const auto& analysis_result = analyzer.analyze(parse_result_ok);
     auto program = analysis_result.root->as_program();
-    const auto &version = program->version->items;
+    const auto& version = program->version->items;
     EXPECT_EQ(version, version_3_0);
 }
 TEST_F(AnalyzerAnalyzeTest, parser_returns_errors) {
     auto analyzer = Analyzer{};
-    const auto &analysis_result = analyzer.analyze(parse_result_errors);
-    const auto &error = analysis_result.errors[0];
+    const auto& analysis_result = analyzer.analyze(parse_result_errors);
+    const auto& error = analysis_result.errors[0];
     EXPECT_THAT(error.what(), ::testing::HasSubstr(parse_error_message));
 }
 
@@ -57,12 +56,13 @@ class AnalyzerTest : public ::testing::Test {
 protected:
     void SetUp() override {}
 
-    tree::One<instruction::Instruction> instruction = tree::make<instruction::Instruction>("h", "Q");
+    instruction::InstructionRef instruction_ref = tree::make<instruction::Instruction>("h", "Q");
     tree::One<semantic::Variable> qubit = tree::make<semantic::Variable>("q", tree::make<types::Qubit>());
     tree::Link<semantic::Variable> qubit_link{ qubit };
     tree::One<values::VariableRef> qubit_variable_ref = tree::make<values::VariableRef>(qubit_link);
-    tree::Any<values::ValueBase> operands = tree::Any<values::ValueBase>{ qubit_variable_ref };
-    tree::One<semantic::Instruction> statement = tree::make<semantic::Instruction>(instruction, "H", operands);
+    tree::Any<values::ValueBase> operands{ qubit_variable_ref };
+    tree::One<semantic::Gate> gate = tree::make<semantic::Gate>(instruction_ref->name);
+    tree::One<semantic::Instruction> statement = tree::make<semantic::GateInstruction>(instruction_ref, gate, operands);
 };
 
 TEST_F(AnalyzerTest, constructor) {
@@ -88,11 +88,11 @@ TEST_F(AnalyzerTest, add_statement_to_current_scope) {
 }
 TEST_F(AnalyzerTest, add_statement_with_source_location_information_to_current_scope) {
     MockAnalyzer analyzer{};
-    const auto &statement_source_location = annotations::SourceLocation{ "input.cq", { { 10, 20 }, { 11, 10 } } };
+    const auto& statement_source_location = annotations::SourceLocation{ "input.cq", { { 10, 20 }, { 11, 10 } } };
     statement->set_annotation(statement_source_location);
     analyzer.add_statement_to_current_scope(statement);
     EXPECT_EQ(analyzer.current_block()->statements.size(), 1);
-    const auto &block_source_location = analyzer.current_block()->get_annotation<annotations::SourceLocation>();
+    const auto& block_source_location = analyzer.current_block()->get_annotation<annotations::SourceLocation>();
     EXPECT_EQ(block_source_location.file_name, statement_source_location.file_name);
     EXPECT_EQ(block_source_location.range, statement_source_location.range);
 }
@@ -102,19 +102,19 @@ TEST_F(AnalyzerTest,
     //     10 15 20 25 30
     //  5      <
     //  8               >
-    const auto &block_initial_source_location = annotations::SourceLocation{ "input.cq", { { 5, 15 }, { 8, 30 } } };
+    const auto& block_initial_source_location = annotations::SourceLocation{ "input.cq", { { 5, 15 }, { 8, 30 } } };
     analyzer.current_block()->set_annotation(block_initial_source_location);
     //     10 15 20 25 30
     // 10   <
     // 11         >
-    const auto &statement_source_location = annotations::SourceLocation{ "input.cq", { { 10, 10 }, { 11, 20 } } };
+    const auto& statement_source_location = annotations::SourceLocation{ "input.cq", { { 10, 10 }, { 11, 20 } } };
     statement->set_annotation(statement_source_location);
     analyzer.add_statement_to_current_scope(statement);
     EXPECT_EQ(analyzer.current_block()->statements.size(), 1);
     //     10 15 20 25 30
     //  5      <
     // 11         >
-    const auto &block_final_source_location = analyzer.current_block()->get_annotation<annotations::SourceLocation>();
+    const auto& block_final_source_location = analyzer.current_block()->get_annotation<annotations::SourceLocation>();
     EXPECT_EQ(block_final_source_location.file_name, "input.cq");
     EXPECT_EQ(block_final_source_location.range, (annotations::SourceLocation::Range{ { 5, 15 }, { 11, 20 } }));
 }
diff --git a/test/v3x/cpp/cqasm-functions.cpp b/test/v3x/cpp/test_functions.cpp
similarity index 98%
rename from test/v3x/cpp/cqasm-functions.cpp
rename to test/v3x/cpp/test_functions.cpp
index bc6e6ce71..d9ce9d4df 100644
--- a/test/v3x/cpp/cqasm-functions.cpp
+++ b/test/v3x/cpp/test_functions.cpp
@@ -1,9 +1,9 @@
-#include "cqasm-functions.hpp"
+#include "test_functions.hpp"
 
 #include <gmock/gmock.h>
 
-#include "v3x/cqasm-primitives.hpp"
-#include "v3x/register-consteval-core-functions.hpp"
+#include "libqasm/v3x/primitives.hpp"
+#include "libqasm/v3x/register_consteval_core_functions.hpp"
 
 namespace function = cqasm::v3x::function;
 namespace primitives = cqasm::v3x::primitives;
diff --git a/test/v3x/cpp/cqasm-functions.hpp b/test/v3x/cpp/test_functions.hpp
similarity index 89%
rename from test/v3x/cpp/cqasm-functions.hpp
rename to test/v3x/cpp/test_functions.hpp
index 525280180..ece1b294c 100644
--- a/test/v3x/cpp/cqasm-functions.hpp
+++ b/test/v3x/cpp/test_functions.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "v3x/cqasm-values.hpp"
+#include "libqasm/v3x/values.hpp"
 
 namespace values = cqasm::v3x::values;
 
@@ -21,7 +21,7 @@ auto invoke_unary(ParamType p) {
 
     auto values = values::Values{ { cqasm::tree::make<FParamValue>(p) } };
     auto ret = F(values);
-    return dynamic_cast<FReturnValue &>(*ret).value;
+    return dynamic_cast<FReturnValue&>(*ret).value;
 }
 
 template <auto F, typename ParamsType>
@@ -31,7 +31,7 @@ auto invoke_binary(ParamsType a, ParamsType b) {
 
     auto values = values::Values{ { cqasm::tree::make<FParamValue>(a), cqasm::tree::make<FParamValue>(b) } };
     auto ret = F(values);
-    return dynamic_cast<FReturnValue &>(*ret).value;
+    return dynamic_cast<FReturnValue&>(*ret).value;
 }
 
 template <auto F, typename ParamsType>
@@ -43,5 +43,5 @@ auto invoke_ternary(bool condition, ParamsType if_true, ParamsType if_false) {
         cqasm::tree::make<FParamValue>(if_true),
         cqasm::tree::make<FParamValue>(if_false) } };
     auto ret = F(values);
-    return dynamic_cast<FReturnValue &>(*ret).value;
+    return dynamic_cast<FReturnValue&>(*ret).value;
 }
diff --git a/test/v3x/cpp/test_instruction_set.cpp b/test/v3x/cpp/test_instruction_set.cpp
new file mode 100644
index 000000000..188df845c
--- /dev/null
+++ b/test/v3x/cpp/test_instruction_set.cpp
@@ -0,0 +1,170 @@
+#include <gmock/gmock.h>
+
+#include "libqasm/error.hpp"
+#include "libqasm/v3x/instruction_set.hpp"
+
+using namespace cqasm::error;
+using namespace cqasm::v3x::instruction;
+
+class InstructionSetTest : public ::testing::Test {
+protected:
+    void SetUp() override {}
+
+    const InstructionSet& instruction_set = InstructionSet::get_instance();
+    const InstructionSet& instruction_set_2 = InstructionSet::get_instance();
+    const InstructionMapT& named_gate_map = instruction_set.get_named_gate_map();
+    const InstructionMapT& non_gate_map = instruction_set.get_non_gate_map();
+    const GateModifierMapT& gate_modifier_map = instruction_set.get_gate_modifier_map();
+    const InstructionListT& single_qubit_named_gate_list = instruction_set.get_single_qubit_named_gate_list();
+    const InstructionListT& two_qubit_named_gate_list = instruction_set.get_two_qubit_named_gate_list();
+
+    const size_t number_of_gate_map_entries = 48;
+    const size_t number_of_non_gate_map_entries = 7;
+    const size_t number_of_gate_modifier_map_entries = 3;
+    const size_t number_of_single_qubit_named_gates = 16;
+    const size_t number_of_two_qubit_named_gates = 4;
+};
+
+TEST_F(InstructionSetTest, get_instance) {
+    EXPECT_EQ(&instruction_set, &instruction_set_2);
+}
+TEST_F(InstructionSetTest, get_named_gate_map) {
+    EXPECT_EQ(named_gate_map.size(), number_of_gate_map_entries);
+}
+TEST_F(InstructionSetTest, get_non_gate_map) {
+    EXPECT_EQ(non_gate_map.size(), number_of_non_gate_map_entries);
+}
+TEST_F(InstructionSetTest, get_gate_modifier_map) {
+    EXPECT_EQ(gate_modifier_map.size(), number_of_gate_modifier_map_entries);
+}
+TEST_F(InstructionSetTest, get_single_qubit_named_gate_list) {
+    EXPECT_EQ(single_qubit_named_gate_list.size(), number_of_single_qubit_named_gates);
+}
+TEST_F(InstructionSetTest, get_two_qubit_named_gate_list) {
+    EXPECT_EQ(two_qubit_named_gate_list.size(), number_of_two_qubit_named_gates);
+}
+TEST_F(InstructionSetTest, is_single_qubit_named_gate) {
+    EXPECT_TRUE(instruction_set.is_single_qubit_named_gate("H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_named_gate("h"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_named_gate("CNOT"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_named_gate("1q_H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_named_gate("inv"));
+}
+TEST_F(InstructionSetTest, is_two_qubit_named_gate) {
+    EXPECT_TRUE(instruction_set.is_two_qubit_named_gate("CNOT"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_named_gate("cnot"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_named_gate("H"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_named_gate("2q_H"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_named_gate("ctrl"));
+}
+TEST_F(InstructionSetTest, is_named_gate) {
+    EXPECT_TRUE(instruction_set.is_named_gate("H"));
+    EXPECT_TRUE(instruction_set.is_named_gate("CNOT"));
+    EXPECT_FALSE(instruction_set.is_named_gate("h"));
+    EXPECT_FALSE(instruction_set.is_named_gate("cnot"));
+    EXPECT_FALSE(instruction_set.is_named_gate("1q_H"));
+    EXPECT_FALSE(instruction_set.is_named_gate("2q_H"));
+    EXPECT_FALSE(instruction_set.is_named_gate("inv"));
+}
+TEST_F(InstructionSetTest, is_single_qubit_gate_composition) {
+    EXPECT_TRUE(instruction_set.is_single_qubit_gate_composition("1q_H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate_composition("H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate_composition("CNOT"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate_composition("2q_H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate_composition("inv"));
+}
+TEST_F(InstructionSetTest, is_two_qubit_gate_composition) {
+    EXPECT_TRUE(instruction_set.is_two_qubit_gate_composition("2q_H"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate_composition("H"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate_composition("CNOT"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate_composition("1q_H"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate_composition("inv"));
+}
+TEST_F(InstructionSetTest, is_single_qubit_gate) {
+    EXPECT_TRUE(instruction_set.is_single_qubit_gate("H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate("CNOT"));
+    EXPECT_TRUE(instruction_set.is_single_qubit_gate("1q_H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate("2q_H"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate("inv"));
+}
+TEST_F(InstructionSetTest, is_two_qubit_gate) {
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate("H"));
+    EXPECT_TRUE(instruction_set.is_two_qubit_gate("CNOT"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate("1q_H"));
+    EXPECT_TRUE(instruction_set.is_two_qubit_gate("2q_H"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate("inv"));
+}
+TEST_F(InstructionSetTest, is_gate) {
+    EXPECT_TRUE(instruction_set.is_gate("H"));
+    EXPECT_TRUE(instruction_set.is_gate("CNOT"));
+    EXPECT_TRUE(instruction_set.is_gate("1q_H"));
+    EXPECT_TRUE(instruction_set.is_gate("2q_H"));
+    EXPECT_FALSE(instruction_set.is_gate("inv"));
+    EXPECT_FALSE(instruction_set.is_gate("measure"));
+    EXPECT_FALSE(instruction_set.is_gate("reset"));
+}
+TEST_F(InstructionSetTest, is_measure) {
+    EXPECT_TRUE(instruction_set.is_measure("measure"));
+    EXPECT_FALSE(instruction_set.is_measure("MEASURE"));
+}
+TEST_F(InstructionSetTest, is_reset) {
+    EXPECT_TRUE(instruction_set.is_reset("reset"));
+    EXPECT_FALSE(instruction_set.is_reset("RESET"));
+}
+TEST_F(InstructionSetTest, is_non_gate) {
+    EXPECT_TRUE(instruction_set.is_non_gate("measure"));
+    EXPECT_TRUE(instruction_set.is_non_gate("reset"));
+    EXPECT_FALSE(instruction_set.is_non_gate("inv"));
+}
+TEST_F(InstructionSetTest, is_inv_gate_modifier) {
+    EXPECT_TRUE(instruction_set.is_inv_gate_modifier("inv"));
+    EXPECT_FALSE(instruction_set.is_inv_gate_modifier("INV"));
+}
+TEST_F(InstructionSetTest, is_pow_gate_modifier) {
+    EXPECT_TRUE(instruction_set.is_pow_gate_modifier("pow"));
+    EXPECT_FALSE(instruction_set.is_pow_gate_modifier("POW"));
+}
+TEST_F(InstructionSetTest, is_ctrl_gate_modifier) {
+    EXPECT_TRUE(instruction_set.is_ctrl_gate_modifier("ctrl"));
+    EXPECT_FALSE(instruction_set.is_ctrl_gate_modifier("CTRL"));
+}
+TEST_F(InstructionSetTest, is_single_qubit_gate_modifier) {
+    EXPECT_TRUE(instruction_set.is_single_qubit_gate_modifier("inv"));
+    EXPECT_TRUE(instruction_set.is_single_qubit_gate_modifier("pow"));
+    EXPECT_FALSE(instruction_set.is_single_qubit_gate_modifier("ctrl"));
+}
+TEST_F(InstructionSetTest, is_two_qubit_gate_modifier) {
+    EXPECT_TRUE(instruction_set.is_two_qubit_gate_modifier("ctrl"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate_modifier("inv"));
+    EXPECT_FALSE(instruction_set.is_two_qubit_gate_modifier("pow"));
+}
+TEST_F(InstructionSetTest, is_gate_modifier) {
+    EXPECT_TRUE(instruction_set.is_gate_modifier("inv"));
+    EXPECT_TRUE(instruction_set.is_gate_modifier("pow"));
+    EXPECT_TRUE(instruction_set.is_gate_modifier("ctrl"));
+    EXPECT_FALSE(instruction_set.is_gate_modifier("H"));
+    EXPECT_FALSE(instruction_set.is_gate_modifier("CNOT"));
+    EXPECT_FALSE(instruction_set.is_gate_modifier("1q_H"));
+    EXPECT_FALSE(instruction_set.is_gate_modifier("2q_H"));
+    EXPECT_FALSE(instruction_set.is_gate_modifier("measure"));
+    EXPECT_FALSE(instruction_set.is_gate_modifier("reset"));
+}
+TEST_F(InstructionSetTest, get_named_gate_param_type) {
+    EXPECT_EQ(instruction_set.get_named_gate_param_type("H"), std::nullopt);
+    EXPECT_EQ(instruction_set.get_named_gate_param_type("Rz"), 'f');
+    EXPECT_THAT([this]() { (void)instruction_set.get_named_gate_param_type("inv"); },
+        ThrowsMessage<AnalysisError>(::testing::HasSubstr("couldn't find gate")));
+}
+TEST_F(InstructionSetTest, get_gate_modifier_param_type) {
+    EXPECT_EQ(instruction_set.get_gate_modifier_param_type("inv"), std::nullopt);
+    EXPECT_EQ(instruction_set.get_gate_modifier_param_type("pow"), 'f');
+    EXPECT_THAT([this]() { (void)instruction_set.get_gate_modifier_param_type("H"); },
+        ThrowsMessage<AnalysisError>(::testing::HasSubstr("couldn't find gate modifier")));
+}
+TEST_F(InstructionSetTest, get_instruction_param_type) {
+    EXPECT_EQ(instruction_set.get_instruction_param_type("H"), std::nullopt);
+    EXPECT_EQ(instruction_set.get_instruction_param_type("measure"), std::nullopt);
+    EXPECT_EQ(instruction_set.get_instruction_param_type("pow"), 'f');
+    EXPECT_THAT([this]() { (void)instruction_set.get_instruction_param_type("1q_H"); },
+        ThrowsMessage<AnalysisError>(::testing::HasSubstr("couldn't find instruction")));
+}
diff --git a/test/v3x/cpp/cqasm-parse-helper.cpp b/test/v3x/cpp/test_parse_helper.cpp
similarity index 91%
rename from test/v3x/cpp/cqasm-parse-helper.cpp
rename to test/v3x/cpp/test_parse_helper.cpp
index ec5d8980a..2da4a7fa8 100644
--- a/test/v3x/cpp/cqasm-parse-helper.cpp
+++ b/test/v3x/cpp/test_parse_helper.cpp
@@ -1,5 +1,3 @@
-#include "v3x/cqasm-parse-helper.hpp"
-
 #include <fmt/format.h>
 #include <gmock/gmock.h>
 
@@ -8,12 +6,13 @@
 #include <stdexcept>  // runtime_error
 #include <string>
 
-#include "cqasm-error.hpp"
-#include "cqasm-tree.hpp"
-#include "cqasm-version.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/tree.hpp"
+#include "libqasm/v3x/ast.hpp"
+#include "libqasm/v3x/parse_helper.hpp"
+#include "libqasm/v3x/parse_result.hpp"
+#include "libqasm/version.hpp"
 #include "mock_scanner_adaptor.hpp"
-#include "v3x/cqasm-ast.hpp"
-#include "v3x/cqasm-parse-result.hpp"
 
 using namespace ::testing;
 using namespace cqasm::error;
@@ -60,7 +59,7 @@ TEST_F(ParseHelperParseTest, scanner_parse_throws_parse_error) {
     ExpectScannerParseThrowsParseError();
     auto parse_helper = ParseHelper{ std::move(scanner_up), file_name };
     auto parse_result = parse_helper.parse();
-    const auto &errors = parse_result.errors;
+    const auto& errors = parse_result.errors;
     EXPECT_TRUE(errors.size() == 1);
     EXPECT_EQ(fmt::format("{}", errors[0]),
         fmt::format("Error at {}:{}:{}..{}: {}",
@@ -74,7 +73,7 @@ TEST_F(ParseHelperParseTest, scanner_parse_throws_runtime_error) {
     ExpectScannerParseThrowsRuntimeError();
     auto parse_helper = ParseHelper{ std::move(scanner_up), file_name };
     auto parse_result = parse_helper.parse();
-    const auto &errors = parse_result.errors;
+    const auto& errors = parse_result.errors;
     EXPECT_TRUE(errors.size() == 1);
     EXPECT_EQ(fmt::format("{}", errors[0]), fmt::format("Error: {}", runtime_error_message));
 }
@@ -89,7 +88,7 @@ TEST_F(ParseHelperParseTest, parse_result_errors_is_empty_and_root_is_well_forme
     auto parse_helper = ParseHelper{ std::move(scanner_up), file_name };
     auto parse_result = parse_helper.parse();
     auto program = parse_result.root->as_program();
-    const auto &version = program->version->items;
+    const auto& version = program->version->items;
     EXPECT_EQ(version, version_3_0);
 }
 
diff --git a/test/v3x/cpp/AnalyzeTreeGenAstVisitor.cpp b/test/v3x/cpp/test_semantic_analyzer.cpp
similarity index 82%
rename from test/v3x/cpp/AnalyzeTreeGenAstVisitor.cpp
rename to test/v3x/cpp/test_semantic_analyzer.cpp
index fc6c3887e..7b12fff94 100644
--- a/test/v3x/cpp/AnalyzeTreeGenAstVisitor.cpp
+++ b/test/v3x/cpp/test_semantic_analyzer.cpp
@@ -1,12 +1,11 @@
-#include "v3x/AnalyzeTreeGenAstVisitor.hpp"
-
 #include <fmt/format.h>
 #include <gmock/gmock.h>
 
-#include "cqasm-error.hpp"
+#include "libqasm/error.hpp"
+#include "libqasm/v3x/ast_generated.hpp"
+#include "libqasm/v3x/semantic_analyzer.hpp"
+#include "libqasm/v3x/values.hpp"
 #include "mock_analyzer.hpp"
-#include "v3x/cqasm-ast-gen.hpp"
-#include "v3x/cqasm-values.hpp"
 
 namespace analyzer = cqasm::v3x::analyzer;
 namespace ast = cqasm::v3x::ast;
@@ -18,23 +17,23 @@ namespace cqasm::v3x::analyzer {
 class VisitFunctionResolveTest : public ::testing::Test {
 protected:
     void SetUp() override {}
-    void ExpectAnalyzerResolveFunctionOK(const values::Value &function_return_value) {
+    void ExpectAnalyzerResolveFunctionOK(const values::Value& function_return_value) {
         EXPECT_CALL(analyzer, resolve_function(::testing::_, ::testing::_))
             .WillOnce(::testing::Return(function_return_value));
     }
-    void ExpectAnalyzerResolveFunctionThrow(const std::string &error_message) {
+    void ExpectAnalyzerResolveFunctionThrow(const std::string& error_message) {
         EXPECT_CALL(analyzer, resolve_function(::testing::_, ::testing::_))
             .WillRepeatedly(::testing::Throw(std::runtime_error{ error_message.c_str() }));
     }
     analyzer::MockAnalyzer analyzer;
-    analyzer::AnalyzeTreeGenAstVisitor visitor{ analyzer };
+    analyzer::SemanticAnalyzer visitor{ analyzer };
 };
 
 /**
  * visit_function_call(name, arguments) is a private method, but we can test it through visit_function_call(node)
  */
 TEST_F(VisitFunctionResolveTest, analyzer_resolve_function_returns_a_non_empty_value) {
-    const auto &function_return_value = values::Value{ cqasm::tree::make<values::ConstFloat>(0) };
+    const auto& function_return_value = values::Value{ cqasm::tree::make<values::ConstFloat>(0) };
     ExpectAnalyzerResolveFunctionOK(function_return_value);
 
     auto name = cqasm::tree::make<ast::Identifier>("function_that_returns_a_non_empty_value");
@@ -45,7 +44,7 @@ TEST_F(VisitFunctionResolveTest, analyzer_resolve_function_returns_a_non_empty_v
 }
 
 TEST_F(VisitFunctionResolveTest, analyzer_resolve_function_returns_an_empty_value) {
-    const auto &function_return_value = values::Value{};
+    const auto& function_return_value = values::Value{};
     ExpectAnalyzerResolveFunctionOK(function_return_value);
 
     auto name = cqasm::tree::make<ast::Identifier>("function_that_returns_an_empty_value");
@@ -56,8 +55,8 @@ TEST_F(VisitFunctionResolveTest, analyzer_resolve_function_returns_an_empty_valu
 }
 
 TEST_F(VisitFunctionResolveTest, analyzer_resolve_function_throws) {
-    const auto &function_name = std::string{ "function_that_is_not_registered" };
-    const auto &error_message = fmt::format("failed to resolve '{}'", function_name);
+    const auto& function_name = std::string{ "function_that_is_not_registered" };
+    const auto& error_message = fmt::format("failed to resolve '{}'", function_name);
     ExpectAnalyzerResolveFunctionThrow(error_message);
 
     auto name = cqasm::tree::make<ast::Identifier>(function_name);
diff --git a/test/v3x/cpp/cqasm-values.cpp b/test/v3x/cpp/test_values.cpp
similarity index 96%
rename from test/v3x/cpp/cqasm-values.cpp
rename to test/v3x/cpp/test_values.cpp
index 714cfbd85..c517b09ef 100644
--- a/test/v3x/cpp/cqasm-values.cpp
+++ b/test/v3x/cpp/test_values.cpp
@@ -1,8 +1,7 @@
-#include "v3x/cqasm-values.hpp"
-
 #include <gtest/gtest.h>
 
-#include "v3x/cqasm-semantic-gen.hpp"
+#include "libqasm/v3x/semantic_generated.hpp"
+#include "libqasm/v3x/values.hpp"
 
 namespace cqasm::v3x::values {
 
diff --git a/test/v3x/python/test_analysis_result.py b/test/v3x/python/test_analysis_result.py
index d1e9dcf66..7620b5934 100644
--- a/test/v3x/python/test_analysis_result.py
+++ b/test/v3x/python/test_analysis_result.py
@@ -5,7 +5,7 @@
 
 class TestAnalysisResult(unittest.TestCase):
     def test_to_json_with_analyzer_errors(self):
-        # res/v3x/parsing/qubit_array_definition/qubit_array_of_0_q
+        # res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_q
         program_str = "version 3; qubit[0] q"
         v3x_analyzer = cq.Analyzer()
         actual_errors_json = v3x_analyzer.analyze_string_to_json(program_str)
@@ -13,9 +13,9 @@ def test_to_json_with_analyzer_errors(self):
         self.assertEqual(actual_errors_json, expected_errors_json)
 
     def test_to_json_with_analyzer_ast(self):
-        # res/v3x/parsing/qubit_array_definition/qubit_array_of_17_q
+        # res/v3x/tests/integration/qubit_array_definition/qubit_array_of_17_q
         program_str = "version 3; qubit[17] q"
         v3x_analyzer = cq.Analyzer()
         actual_ast_json = v3x_analyzer.analyze_string_to_json(program_str)
-        expected_ast_json = '''{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":"[]"}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"17"}},"annotations":"[]"}}]}}'''
+        expected_ast_json = '''{"Program":{"api_version":"3.0","version":{"Version":{"items":"3"}},"block":{"Block":{"statements":[]}},"variables":[{"Variable":{"name":"q","typ":{"QubitArray":{"size":"17"}},"annotations":[]}}]}}'''
         self.assertEqual(actual_ast_json, expected_ast_json)
diff --git a/test/v3x/python/test_parse_result.py b/test/v3x/python/test_parse_result.py
index 8d5cb7f7c..ee6ac284d 100644
--- a/test/v3x/python/test_parse_result.py
+++ b/test/v3x/python/test_parse_result.py
@@ -5,7 +5,7 @@
 
 class TestParseResult(unittest.TestCase):
     def test_to_json_with_parser_errors(self):
-        # res/v3x/parsing/qubit_array_definition/qubit_array_of_3.14
+        # res/v3x/tests/integration/qubit_array_definition/qubit_array_of_3.14
         program_str = "version 3; qubit[3.14]"
         v3x_analyzer = cq.Analyzer()
         actual_errors_json = v3x_analyzer.parse_string_to_json(program_str)
@@ -13,9 +13,9 @@ def test_to_json_with_parser_errors(self):
         self.assertEqual(actual_errors_json, expected_errors_json)
 
     def test_to_json_with_parser_ast(self):
-        # res/v3x/parsing/qubit_array_definition/qubit_array_of_0_b
+        # res/v3x/tests/integration/qubit_array_definition/qubit_array_of_0_b
         program_str = "version 3; qubit[0] q"
         v3x_analyzer = cq.Analyzer()
         actual_ast_json = v3x_analyzer.parse_string_to_json(program_str)
-        expected_ast_json = '''{"Program":{"version":{"Version":{"items":"3","source_location":"<unknown file name>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"0"}},"source_location":"<unknown file name>:1:12..20"}},"annotations":"[]","source_location":"<unknown file name>:1:21..22"}}]}}}}'''
+        expected_ast_json = '''{"Program":{"version":{"Version":{"items":"3","source_location":"<unknown file name>:1:9..10"}},"block":{"GlobalBlock":{"statements":[{"Variable":{"name":{"Identifier":{"name":"q"}},"typ":{"Type":{"name":{"Keyword":{"name":"qubit"}},"size":{"IntegerLiteral":{"value":"0"}},"source_location":"<unknown file name>:1:12..20"}},"annotations":[],"source_location":"<unknown file name>:1:21..22"}}]}}}}'''
         self.assertEqual(actual_ast_json, expected_ast_json)
diff --git a/test/v3x/python/test_v3x_analyzer.py b/test/v3x/python/test_v3x_analyzer.py
index 01d3ba2f9..10be7af55 100644
--- a/test/v3x/python/test_v3x_analyzer.py
+++ b/test/v3x/python/test_v3x_analyzer.py
@@ -22,7 +22,8 @@ def test_parse_string_returning_ast(self):
         self.assertEqual(bit_array.typ.size.value, 5)
 
         h_instruction = ast.block.statements[2]
-        self.assertEqual(h_instruction.name.name, "H")
+        h_gate = h_instruction.gate
+        self.assertEqual(h_gate.name.name, "H")
         h_operand = h_instruction.operands.items[0]
         self.assertEqual(h_operand.expr.name, "q")
         self.assertEqual(h_operand.indices.items[0].first.value, 0)
@@ -30,8 +31,8 @@ def test_parse_string_returning_ast(self):
 
         measure_instruction = ast.block.statements[3]
         self.assertEqual(measure_instruction.name.name, "measure")
-        self.assertEqual(measure_instruction.lhs.name, "b")
-        self.assertEqual(measure_instruction.rhs.name, "q")
+        self.assertEqual(measure_instruction.operands.items[0].name, "b")
+        self.assertEqual(measure_instruction.operands.items[1].name, "q")
 
     def test_parse_string_returning_errors(self):
         program_str = "version 3;qubit[5] q;bit[5] b;H q[0:4];b = measure"
@@ -49,7 +50,8 @@ def test_analyze_string_returning_ast(self):
         self.assertEqual(ast.version.items[0], 3)
 
         h_instruction = ast.block.statements[0]
-        self.assertEqual(h_instruction.name, "H")
+        h_gate = h_instruction.gate
+        self.assertEqual(h_gate.name, "H")
         h_operand = h_instruction.operands[0]
         self.assertEqual(h_operand.variable.name, "q")
         self.assertIsInstance(h_operand.variable.typ, cq.types.QubitArray)
@@ -88,7 +90,7 @@ def test_analyze_string_returning_errors(self):
     def test_to_json_vrsion(self):
         """This test is used to check that a program with a syntactic error
         produces the same error message both when parsing and analyzing."""
-        # res/v3x/parsing/version/vrsion
+        # res/v3x/tests/integration/version/vrsion
         program_str = "vrsion 3"
         v3x_analyzer = cq.Analyzer()
         actual_parse_json = v3x_analyzer.parse_string_to_json(program_str)